Skip to main content

strat9_bus_drivers/
ts_nbus.rs

1use crate::{BusChild, BusDriver, BusError, PowerState};
2use alloc::{string::String, vec::Vec};
3
4const TS_NBUS_DIRECTION_IN: bool = false;
5const TS_NBUS_DIRECTION_OUT: bool = true;
6const TS_NBUS_WRITE_ADR: bool = false;
7const TS_NBUS_WRITE_VAL: bool = true;
8
9const MAX_POLL_RDY: u32 = 10000;
10
11const COMPATIBLE: &[&str] = &["technologic,ts-nbus"];
12
13/// A single memory-mapped GPIO line of the FPGA GPIO controller that
14/// backs the NBUS bit-banging protocol.
15///
16/// # Register layout
17///
18/// The controller is expected to expose two 32-bit registers relative to
19/// `base`:
20///
21/// - `base + 0x00`: **data** register, bit `offset` drives/reflects the line;
22/// - `base + 0x04`: **direction** register, bit `offset` set = output,
23///   cleared = input.
24///
25/// All accesses are volatile read-modify-write so concurrent lines of the
26/// same controller are preserved.
27///
28/// An unconfigured pin (`base == 0`) reports [`Self::is_configured`] ==
29/// false; transactions that need it are rejected instead of silently
30/// doing nothing.
31pub struct GpioPin {
32    /// Physical base address of the GPIO controller register block.
33    pub base: usize,
34    /// Bit index of this line within the controller's 32-bit registers.
35    pub offset: u32,
36    /// When true, logical high/low are inverted relative to the electrical level.
37    pub active_low: bool,
38}
39
40impl GpioPin {
41    /// Offset of the data (set/clear via read-modify-write) register.
42    pub const DATA_REG_OFFSET: usize = 0x00;
43    /// Offset of the direction register (bit set = output).
44    pub const DIR_REG_OFFSET: usize = 0x04;
45
46    /// Returns true when this pin points at a real controller (`base != 0`).
47    pub fn is_configured(&self) -> bool {
48        self.base != 0 && self.offset < 32
49    }
50
51    /// Reads the data register.
52    fn read_data(&self) -> u32 {
53        // SAFETY: callers guarantee `base` is the mapped base of a GPIO
54        // controller register block and `DATA_REG_OFFSET + 4` stays within it.
55        unsafe { core::ptr::read_volatile((self.base + Self::DATA_REG_OFFSET) as *const u32) }
56    }
57
58    /// Writes the data register.
59    fn write_data(&self, val: u32) {
60        // SAFETY: see `read_data`.
61        unsafe { core::ptr::write_volatile((self.base + Self::DATA_REG_OFFSET) as *mut u32, val) }
62    }
63
64    /// Sets or clears bit `offset` of a register via volatile read-modify-write.
65    fn rmw_bit(&self, reg_offset: usize, set: bool) {
66        let addr = (self.base + reg_offset) as *const u32;
67        // SAFETY: see `read_data`; read-modify-write keeps sibling lines intact.
68        let mut val = unsafe { core::ptr::read_volatile(addr) };
69        let mask = 1u32 << self.offset;
70        if set {
71            val |= mask;
72        } else {
73            val &= !mask;
74        }
75        // SAFETY: same mapping, write back the updated word.
76        unsafe { core::ptr::write_volatile(addr as *mut u32, val) };
77    }
78
79    /// Drives the line to its logical-high level.
80    pub fn set_high(&self) {
81        let bit = !self.active_low;
82        self.rmw_bit(Self::DATA_REG_OFFSET, bit);
83    }
84
85    /// Drives the line to its logical-low level.
86    pub fn set_low(&self) {
87        let bit = self.active_low;
88        self.rmw_bit(Self::DATA_REG_OFFSET, bit);
89    }
90
91    /// Samples the current logical level of the line.
92    pub fn get_value(&self) -> bool {
93        let raw = self.read_data() & (1u32 << self.offset) != 0;
94        raw != self.active_low
95    }
96
97    /// Configures the line as input (direction bit cleared).
98    pub fn set_direction_input(&self) {
99        self.rmw_bit(Self::DIR_REG_OFFSET, false);
100    }
101
102    /// Configures the line as output (direction bit set).
103    pub fn set_direction_output(&self) {
104        self.rmw_bit(Self::DIR_REG_OFFSET, true);
105    }
106}
107
108pub struct TsNbus {
109    data_pins: [Option<GpioPin>; 8],
110    csn: Option<GpioPin>,
111    txrx: Option<GpioPin>,
112    strobe: Option<GpioPin>,
113    ale: Option<GpioPin>,
114    rdy: Option<GpioPin>,
115    power_state: PowerState,
116    children: Vec<BusChild>,
117}
118
119/// Control line of the NBUS protocol.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum NbusControlPin {
122    /// Chip select.
123    Csn,
124    /// TX/RX direction selector.
125    TxRx,
126    /// Strobe.
127    Strobe,
128    /// Address latch enable.
129    Ale,
130    /// Ready (input).
131    Rdy,
132}
133
134impl TsNbus {
135    /// Creates a new instance.
136    pub fn new() -> Self {
137        Self {
138            data_pins: [const { None }; 8],
139            csn: None,
140            txrx: None,
141            strobe: None,
142            ale: None,
143            rdy: None,
144            power_state: PowerState::Off,
145            children: Vec::new(),
146        }
147    }
148
149    /// Wires one of the 8 data lines (`index` 0..8) to a GPIO pin.
150    /// Returns [`BusError::InvalidArgument`] when `index >= 8`.
151    pub fn set_data_pin(&mut self, index: usize, pin: GpioPin) -> Result<(), BusError> {
152        if index >= 8 {
153            return Err(BusError::InvalidArgument);
154        }
155        self.data_pins[index] = Some(pin);
156        Ok(())
157    }
158
159    /// Wires a control line to a GPIO pin.
160    pub fn set_control_pin(&mut self, which: NbusControlPin, pin: GpioPin) {
161        let slot = match which {
162            NbusControlPin::Csn => &mut self.csn,
163            NbusControlPin::TxRx => &mut self.txrx,
164            NbusControlPin::Strobe => &mut self.strobe,
165            NbusControlPin::Ale => &mut self.ale,
166            NbusControlPin::Rdy => &mut self.rdy,
167        };
168        *slot = Some(pin);
169    }
170
171    /// Verifies that every pin required by the bit-banging protocol is
172    /// configured, so transactions fail loudly instead of silently no-op'ing.
173    fn validate_pins(&self) -> Result<(), BusError> {
174        for (i, p) in self.data_pins.iter().enumerate() {
175            match p {
176                Some(p) if p.is_configured() => {}
177                _ => return Err(BusError::InvalidArgument),
178            }
179        }
180        for p in [&self.csn, &self.txrx, &self.strobe, &self.ale, &self.rdy] {
181            match p {
182                Some(p) if p.is_configured() => {}
183                _ => return Err(BusError::InvalidArgument),
184            }
185        }
186        Ok(())
187    }
188
189    /// Sets data direction.
190    fn set_data_direction(&self, output: bool) {
191        for p in self.data_pins.iter().flatten() {
192            if output {
193                p.set_direction_output();
194            } else {
195                p.set_direction_input();
196            }
197        }
198    }
199
200    /// Writes byte.
201    fn write_byte(&self, val: u8) {
202        for i in 0..8 {
203            if let Some(ref p) = self.data_pins[i] {
204                if (val >> i) & 1 != 0 {
205                    p.set_high();
206                } else {
207                    p.set_low();
208                }
209            }
210        }
211    }
212
213    /// Reads byte.
214    fn read_byte(&self) -> u8 {
215        let mut val = 0u8;
216        for i in 0..8 {
217            if let Some(ref p) = self.data_pins[i]
218                && p.get_value()
219            {
220                val |= 1 << i;
221            }
222        }
223        val
224    }
225
226    /// Starts transaction.
227    fn start_transaction(&self) {
228        if let Some(ref s) = self.strobe {
229            s.set_high();
230        }
231    }
232
233    /// Performs the end transaction operation.
234    fn end_transaction(&self) {
235        if let Some(ref s) = self.strobe {
236            s.set_low();
237        }
238    }
239
240    /// Performs the wait rdy operation.
241    fn wait_rdy(&self) -> Result<(), BusError> {
242        for _ in 0..MAX_POLL_RDY {
243            if let Some(ref r) = self.rdy
244                && r.get_value()
245            {
246                return Ok(());
247            }
248        }
249        Err(BusError::Timeout)
250    }
251
252    /// Performs the reset bus operation.
253    fn reset_bus(&self) {
254        self.write_byte(0);
255        if let Some(ref c) = self.csn {
256            c.set_low();
257        }
258        if let Some(ref s) = self.strobe {
259            s.set_low();
260        }
261        if let Some(ref a) = self.ale {
262            a.set_low();
263        }
264    }
265
266    /// Performs the bus read operation.
267    pub fn bus_read(&self, address: u16) -> Result<u16, BusError> {
268        self.set_data_direction(true);
269        if let Some(ref t) = self.txrx {
270            t.set_low();
271        }
272        if let Some(ref a) = self.ale {
273            a.set_high();
274        }
275
276        self.write_byte((address >> 8) as u8);
277        self.start_transaction();
278        self.end_transaction();
279
280        self.write_byte(address as u8);
281        self.start_transaction();
282        self.end_transaction();
283
284        if let Some(ref a) = self.ale {
285            a.set_low();
286        }
287        self.set_data_direction(false);
288
289        if let Some(ref c) = self.csn {
290            c.set_high();
291        }
292        self.start_transaction();
293        self.wait_rdy()?;
294        let msb = self.read_byte();
295        self.end_transaction();
296
297        self.start_transaction();
298        self.wait_rdy()?;
299        let lsb = self.read_byte();
300        self.end_transaction();
301
302        if let Some(ref c) = self.csn {
303            c.set_low();
304        }
305
306        Ok(((msb as u16) << 8) | (lsb as u16))
307    }
308
309    /// Performs the bus write operation.
310    pub fn bus_write(&self, address: u16, value: u16) -> Result<(), BusError> {
311        self.set_data_direction(true);
312        if let Some(ref t) = self.txrx {
313            t.set_high();
314        }
315        if let Some(ref a) = self.ale {
316            a.set_high();
317        }
318
319        self.write_byte((address >> 8) as u8);
320        self.start_transaction();
321        self.end_transaction();
322
323        self.write_byte(address as u8);
324        self.start_transaction();
325        self.end_transaction();
326
327        if let Some(ref a) = self.ale {
328            a.set_low();
329        }
330        if let Some(ref c) = self.csn {
331            c.set_high();
332        }
333
334        self.write_byte((value >> 8) as u8);
335        self.start_transaction();
336        self.wait_rdy()?;
337        self.end_transaction();
338
339        self.write_byte(value as u8);
340        self.start_transaction();
341        self.wait_rdy()?;
342        self.end_transaction();
343
344        if let Some(ref c) = self.csn {
345            c.set_low();
346        }
347
348        Ok(())
349    }
350
351    /// Performs the add child operation.
352    pub fn add_child(&mut self, child: BusChild) {
353        self.children.push(child);
354    }
355}
356
357impl BusDriver for TsNbus {
358    /// Performs the name operation.
359    fn name(&self) -> &str {
360        "ts-nbus"
361    }
362
363    /// Performs the compatible operation.
364    fn compatible(&self) -> &[&str] {
365        COMPATIBLE
366    }
367
368    /// Requires explicit GPIO pin configuration; no auto-detect.
369    fn probe(&self) -> bool {
370        false
371    }
372
373    /// Performs the init operation.
374    fn init(&mut self, _base: usize) -> Result<(), BusError> {
375        self.validate_pins()?;
376        self.reset_bus();
377        self.power_state = PowerState::On;
378        Ok(())
379    }
380
381    /// Performs the shutdown operation.
382    fn shutdown(&mut self) -> Result<(), BusError> {
383        self.reset_bus();
384        self.power_state = PowerState::Off;
385        Ok(())
386    }
387
388    /// Reads reg.
389    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
390        // bus_read indexes a u16 register space: reject out-of-range
391        // offsets instead of silently truncating to the low 16 bits.
392        let off = u16::try_from(offset).map_err(|_| BusError::InvalidAddress)?;
393        let val = self.bus_read(off)?;
394        Ok(val as u32)
395    }
396
397    /// Writes reg.
398    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
399        let off = u16::try_from(offset).map_err(|_| BusError::InvalidAddress)?;
400        self.bus_write(off, value as u16)
401    }
402
403    /// Performs the children operation.
404    fn children(&self) -> Vec<BusChild> {
405        self.children.clone()
406    }
407}