rust_hdl_sim/
ads868x_sim.rs

1use rust_hdl_core::prelude::*;
2use rust_hdl_widgets::prelude::*;
3
4#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
5enum ADS868XState {
6    Ready,
7    Waiting,
8    Dispatch,
9    ReadWordCmd,
10    ReadByteCmd,
11    WriteWordCmd,
12    WriteMSBCmd,
13    WriteLSBCmd,
14    WriteDone,
15    Nop,
16}
17
18#[derive(LogicBlock)]
19pub struct ADS868XSimulator {
20    pub wires: SPIWiresSlave,
21    pub clock: Signal<In, Clock>,
22    // RAM to store register values
23    reg_ram: RAM<Bits<16>, 5>,
24    // SPI slave device
25    spi_slave: SPISlave<32>,
26    // FSM State
27    state: DFF<ADS868XState>,
28    // Rolling counter to emulate conversions
29    conversion_counter: DFF<Bits<16>>,
30    // Inbound register
31    inbound: DFF<Bits<32>>,
32    // Local signal to store the command bits
33    read_cmd: Signal<Local, Bits<5>>,
34    write_cmd: Signal<Local, Bits<7>>,
35    address: Signal<Local, Bits<9>>,
36    data_parity: Signal<Local, Bit>,
37    id_parity: Signal<Local, Bit>,
38}
39
40impl ADS868XSimulator {
41    pub fn spi_hw() -> SPIConfig {
42        SPIConfig {
43            clock_speed: 48_000_000,
44            cs_off: true,
45            mosi_off: true,
46            speed_hz: 400_000,
47            cpha: false,
48            cpol: false,
49        }
50    }
51    pub fn spi_sw() -> SPIConfig {
52        SPIConfig {
53            clock_speed: 1_000_000,
54            cs_off: true,
55            mosi_off: true,
56            speed_hz: 10_000,
57            cpha: false,
58            cpol: false,
59        }
60    }
61
62    pub fn new(spi_config: SPIConfig) -> Self {
63        assert!(spi_config.clock_speed > 10 * spi_config.speed_hz);
64        Self {
65            wires: Default::default(),
66            clock: Default::default(),
67            reg_ram: Default::default(),
68            spi_slave: SPISlave::new(spi_config),
69            state: Default::default(),
70            conversion_counter: Default::default(),
71            inbound: Default::default(),
72            read_cmd: Default::default(),
73            write_cmd: Default::default(),
74            address: Default::default(),
75            data_parity: Default::default(),
76            id_parity: Default::default(),
77        }
78    }
79}
80
81#[test]
82fn test_indexing() {
83    let val: Bits<32> = 0b11000_00_101_001_100_00000000_00000000.into();
84    assert_eq!(val.get_bits::<5>(27).index(), 0b11000);
85    assert_eq!(val.get_bits::<9>(16).index(), 0b101_001_100);
86}
87
88impl Logic for ADS868XSimulator {
89    #[hdl_gen]
90    fn update(&mut self) {
91        // Connect the spi bus
92        SPIWiresSlave::link(&mut self.wires, &mut self.spi_slave.wires);
93        // Clock internal components
94        self.reg_ram.read_clock.next = self.clock.val();
95        self.reg_ram.write_clock.next = self.clock.val();
96        clock!(self, clock, spi_slave);
97        dff_setup!(self, clock, state, conversion_counter, inbound);
98        // Set default values
99        self.spi_slave.start_send.next = false;
100        self.spi_slave.continued_transaction.next = false;
101        self.spi_slave.bits.next = 0.into();
102        self.spi_slave.data_outbound.next = 0.into();
103        self.reg_ram.write_enable.next = false;
104        self.reg_ram.write_data.next = 0.into();
105        self.spi_slave.disabled.next = false;
106        self.read_cmd.next = self.inbound.q.val().get_bits::<5>(27);
107        self.write_cmd.next = self.inbound.q.val().get_bits::<7>(25);
108        self.address.next = self.inbound.q.val().get_bits::<9>(16);
109        self.reg_ram.write_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
110        self.reg_ram.read_address.next = 0.into();
111        self.data_parity.next = self.conversion_counter.q.val().xor();
112        self.id_parity.next = (self.reg_ram.read_data.val() & 0x0FF).xor();
113        match self.state.q.val() {
114            ADS868XState::Ready => {
115                if !self.spi_slave.busy.val() {
116                    self.state.d.next = ADS868XState::Nop;
117                }
118            }
119            ADS868XState::Waiting => {
120                if self.spi_slave.transfer_done.val() {
121                    self.inbound.d.next = self.spi_slave.data_inbound.val();
122                    self.state.d.next = ADS868XState::Dispatch;
123                }
124            }
125            ADS868XState::Dispatch => {
126                if self.read_cmd.val() == 0b11001 {
127                    self.state.d.next = ADS868XState::ReadWordCmd;
128                    self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
129                } else if self.read_cmd.val() == 0b01001 {
130                    self.state.d.next = ADS868XState::ReadByteCmd;
131                    self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
132                } else if self.write_cmd.val() == 0b11010_00 {
133                    self.state.d.next = ADS868XState::WriteWordCmd;
134                } else if self.write_cmd.val() == 0b11010_01 {
135                    self.state.d.next = ADS868XState::WriteMSBCmd;
136                    self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
137                } else if self.write_cmd.val() == 0b11010_10 {
138                    self.state.d.next = ADS868XState::WriteLSBCmd;
139                    self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
140                } else {
141                    self.reg_ram.read_address.next = 0x02.into();
142                    self.state.d.next = ADS868XState::Nop;
143                }
144            }
145            ADS868XState::ReadWordCmd => {
146                self.spi_slave.data_outbound.next =
147                    bit_cast::<32, 16>(self.reg_ram.read_data.val());
148                self.spi_slave.bits.next = 16.into();
149                self.spi_slave.start_send.next = true;
150                self.state.d.next = ADS868XState::Waiting;
151            }
152            ADS868XState::ReadByteCmd => {
153                if self.address.val().get_bit(0) {
154                    self.spi_slave.data_outbound.next =
155                        bit_cast::<32, 16>(self.reg_ram.read_data.val() >> 8);
156                } else {
157                    self.spi_slave.data_outbound.next =
158                        bit_cast::<32, 16>(self.reg_ram.read_data.val() & 0xFF);
159                }
160                self.spi_slave.bits.next = 8.into();
161                self.spi_slave.start_send.next = true;
162                self.state.d.next = ADS868XState::Waiting;
163            }
164            ADS868XState::WriteWordCmd => {
165                self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0xFFFF);
166                self.reg_ram.write_enable.next = true;
167                self.state.d.next = ADS868XState::WriteDone;
168            }
169            ADS868XState::WriteLSBCmd => {
170                self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0x00FF)
171                    | (self.reg_ram.read_data.val() & 0xFF00);
172                self.reg_ram.write_enable.next = true;
173                self.state.d.next = ADS868XState::WriteDone;
174            }
175            ADS868XState::WriteMSBCmd => {
176                self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0xFF00)
177                    | (self.reg_ram.read_data.val() & 0x00FF);
178                self.reg_ram.write_enable.next = true;
179                self.state.d.next = ADS868XState::WriteDone;
180            }
181            ADS868XState::WriteDone => {
182                self.spi_slave.bits.next = 32.into();
183                self.spi_slave.data_outbound.next = self.inbound.q.val();
184                self.spi_slave.start_send.next = true;
185                self.state.d.next = ADS868XState::Waiting;
186            }
187            ADS868XState::Nop => {
188                self.spi_slave.bits.next = 32.into();
189                // TODO - make this more accurate based on how
190                // the output register is programmed.
191                /*  self.spi_slave.data_outbound.next =
192                (bit_cast::<32, 16>(self.conversion_counter.q.val()) << 16)
193                    | bit_cast::<32, 16>(self.reg_ram.read_data.val() & 0x0FF) << 12
194                    | bit_cast::<32, 1>(self.data_parity.val().into()) << 11
195                    | bit_cast::<32, 1>((self.data_parity.val() ^ self.id_parity.val()).into())
196                    << 10;
197                    */
198                self.spi_slave.data_outbound.next =
199                    (bit_cast::<32, 16>(self.conversion_counter.q.val()) << 16)
200                        | (bit_cast::<32, 16>(self.reg_ram.read_data.val() & 0x0FF) << 12)
201                        | (bit_cast::<32, 1>(self.data_parity.val().into()) << 8)
202                        | (bit_cast::<32, 1>(
203                            (self.data_parity.val() ^ self.id_parity.val()).into(),
204                        ) << 9);
205                self.spi_slave.start_send.next = true;
206                self.state.d.next = ADS868XState::Waiting;
207                self.conversion_counter.d.next = self.conversion_counter.q.val() + 1;
208            }
209            _ => {
210                self.state.d.next = ADS868XState::Ready;
211            }
212        }
213    }
214}
215
216#[test]
217fn test_ads8689_synthesizes() {
218    let mut uut = ADS868XSimulator::new(ADS868XSimulator::spi_sw());
219    uut.connect_all();
220    yosys_validate("ads8689", &generate_verilog(&uut)).unwrap();
221}
222
223#[derive(LogicBlock)]
224struct Test8689 {
225    clock: Signal<In, Clock>,
226    master: SPIMaster<32>,
227    adc: ADS868XSimulator,
228}
229
230impl Logic for Test8689 {
231    #[hdl_gen]
232    fn update(&mut self) {
233        clock!(self, clock, master, adc);
234        SPIWiresMaster::join(&mut self.master.wires, &mut self.adc.wires);
235    }
236}
237
238impl Default for Test8689 {
239    fn default() -> Self {
240        Self {
241            clock: Default::default(),
242            master: SPIMaster::new(ADS868XSimulator::spi_sw()),
243            adc: ADS868XSimulator::new(ADS868XSimulator::spi_sw()),
244        }
245    }
246}
247
248#[cfg(test)]
249fn do_spi_txn(
250    bits: u16,
251    value: u64,
252    continued: bool,
253    mut x: Box<Test8689>,
254    sim: &mut Sim<Test8689>,
255) -> Result<(Bits<32>, Box<Test8689>), SimError> {
256    wait_clock_true!(sim, clock, x);
257    x.master.data_outbound.next = value.to_bits();
258    x.master.bits_outbound.next = bits.to_bits();
259    x.master.continued_transaction.next = continued;
260    x.master.start_send.next = true;
261    wait_clock_cycle!(sim, clock, x);
262    x.master.start_send.next = false;
263    x = sim
264        .watch(|x| x.master.transfer_done.val().into(), x)
265        .unwrap();
266    let ret = x.master.data_inbound.val();
267    for _ in 0..50 {
268        wait_clock_cycle!(sim, clock, x);
269    }
270    Ok((ret, x))
271}
272
273#[cfg(test)]
274fn mk_test8689() -> Test8689 {
275    let mut uut = Test8689::default();
276    uut.clock.connect();
277    uut.master.continued_transaction.connect();
278    uut.master.start_send.connect();
279    uut.master.data_outbound.connect();
280    uut.master.bits_outbound.connect();
281    uut.connect_all();
282    uut
283}
284
285#[test]
286fn test_yosys_validate_test_fixture() {
287    let uut = mk_test8689();
288    yosys_validate("8689_1", &generate_verilog(&uut)).unwrap();
289}
290
291#[test]
292fn test_reg_writes() {
293    let uut = mk_test8689();
294    let mut sim = Simulation::new();
295    sim.add_clock(5, |x: &mut Box<Test8689>| x.clock.next = !x.clock.val());
296    sim.add_testbench(move |mut sim: Sim<Test8689>| {
297        let mut x = sim.init()?;
298
299        wait_clock_cycles!(sim, clock, x, 50);
300        wait_clock_true!(sim, clock, x);
301        wait_clock_cycle!(sim, clock, x);
302        // Write an ID to register 2...
303        let result = do_spi_txn(32, 0xd0_02_00_02, false, x, &mut sim)?;
304        x = result.1;
305        wait_clock_cycle!(sim, clock, x);
306        wait_clock_cycle!(sim, clock, x);
307        let result = do_spi_txn(32, 0x48_02_00_00, false, x, &mut sim)?;
308        x = result.1;
309        let result = do_spi_txn(8, 0x00, false, x, &mut sim)?;
310        println!("ID Register read {:x}", result.0);
311        x = result.1;
312        sim_assert_eq!(sim, result.0.index(), 2, x);
313        /*
314        # Output should be 0x40 0x08
315        [ 0xd0 0x10 0x40 0x08 ] % [ 0xc8 0x10 0x00 0x00 ] % { 0x00 0x00 ]
316         */
317        wait_clock_cycle!(sim, clock, x);
318        let result = do_spi_txn(32, 0xd0_10_40_08, false, x, &mut sim)?;
319        x = result.1;
320        wait_clock_cycle!(sim, clock, x);
321        let result = do_spi_txn(32, 0xc8_10_00_00, false, x, &mut sim)?;
322        x = result.1;
323        wait_clock_cycle!(sim, clock, x);
324        let result = do_spi_txn(16, 0x00, false, x, &mut sim)?;
325        x = result.1;
326        sim_assert_eq!(sim, result.0.index(), 0x40_08, x);
327        for i in 0..5 {
328            wait_clock_cycle!(sim, clock, x);
329            let result = do_spi_txn(32, 0x00_00_00_00, false, x, &mut sim)?;
330            x = result.1;
331            println!("Reading is {:x}", result.0);
332            sim_assert_eq!(sim, (result.0 & 0xFFFF0000), ((i + 2) << 16), x);
333            let parity_bit = result.0 & 0x100 != 0;
334            let data: Bits<32> = (result.0 & 0xFFFF0000) >> 16;
335            sim_assert_eq!(sim, data.xor(), parity_bit, x);
336        }
337        sim.done(x)
338    });
339    //    sim.run(Box::new(uut), 1_000_000).unwrap();
340    sim.run_to_file(Box::new(uut), 1_000_000, &vcd_path!("ad868x.vcd"))
341        .unwrap();
342}
343
344#[test]
345fn test_parity_calculations() {
346    for sample in [
347        0x00020C00,
348        0x92ab1400_u32,
349        0x734b1800,
350        0x4fc81400,
351        0x7bee1400,
352        0x94821800_u32,
353        0x5eb31400,
354        0x4eaa1400,
355        0x8ac91800_u32,
356        0x95321800_u32,
357        0x54c01800,
358        0x561a1800,
359        0x91601800_u32,
360        0x7e401800,
361        0x50961400,
362    ] {
363        let mut data = (sample & 0xFFFF_0000_u32) >> 16;
364        let mut parity = false;
365        for _ in 0..16 {
366            parity = parity ^ (data & 0x1 != 0);
367            data = data >> 1;
368        }
369        let adc_flag = (sample & 0x800) != 0;
370        assert_eq!(adc_flag, parity);
371    }
372}