rust_hdl_sim/
max31856_sim.rs

1use super::ad7193_sim::AD7193Config;
2use rust_hdl_core::prelude::*;
3use rust_hdl_widgets::prelude::*;
4
5#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
6enum MAX31856State {
7    Start,
8    Ready,
9    GettingCmd,
10    RegFetchRead,
11    ReadCmd,
12    WaitReadComplete,
13    WriteCmd,
14    DoWrite,
15}
16
17#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
18enum DAQState {
19    Idle,
20    Convert,
21    Copy0,
22    Copy1,
23}
24
25#[derive(LogicBlock)]
26pub struct MAX31856Simulator {
27    // Slave SPI bus
28    pub wires: SPIWiresSlave,
29    pub clock: Signal<In, Clock>,
30    // RAM that stores the memory contents
31    reg_ram: RAM<Bits<8>, 4>,
32    // Used to handle auto conversions
33    auto_conversions_enabled: DFF<Bit>,
34    auto_conversion_strobe: Strobe<32>,
35    auto_conversion_counter: DFF<Bits<19>>,
36    // Separate bits out of the SPI message
37    cmd: Signal<Local, Bits<8>>,
38    rw_flag: Signal<Local, Bit>,
39    reg_index: Signal<Local, Bits<4>>,
40    // The SPI slave device
41    spi_slave: SPISlave<64>,
42    // FSM state:
43    state: DFF<MAX31856State>,
44    reg_read_index: DFF<Bits<4>>,
45    reg_write_index: DFF<Bits<4>>,
46    // Boot timer
47    boot: DFF<Bits<4>>,
48    // DAQ state:
49    dstate: DFF<DAQState>,
50}
51
52const MAX31856_REG_INITS: [u8; 16] = [
53    0x00, 0x03, 0xFF, 0x7F, 0xC0, 0x7F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54];
55
56impl MAX31856Simulator {
57    pub fn new(config: SPIConfig) -> Self {
58        let reg_ram = MAX31856_REG_INITS.iter().map(|x| x.to_bits()).into();
59        Self {
60            wires: Default::default(),
61            clock: Default::default(),
62            reg_ram,
63            auto_conversions_enabled: Default::default(),
64            auto_conversion_strobe: Strobe::new(config.clock_speed, 100.0),
65            auto_conversion_counter: Default::default(),
66            cmd: Default::default(),
67            rw_flag: Default::default(),
68            spi_slave: SPISlave::new(config),
69            state: Default::default(),
70            reg_read_index: Default::default(),
71            reg_write_index: Default::default(),
72            boot: DFF::default(),
73            reg_index: Default::default(),
74            dstate: Default::default(),
75        }
76    }
77}
78
79impl Logic for MAX31856Simulator {
80    #[hdl_gen]
81    fn update(&mut self) {
82        // Connect the spi bus
83        SPIWiresSlave::link(&mut self.wires, &mut self.spi_slave.wires);
84        // Clock the internal logic
85        self.reg_ram.write_clock.next = self.clock.val();
86        self.reg_ram.read_clock.next = self.clock.val();
87        // Setup the DFF and internal widgets
88        dff_setup!(
89            self,
90            clock,
91            auto_conversions_enabled,
92            auto_conversion_counter,
93            state,
94            reg_read_index,
95            reg_write_index,
96            boot,
97            dstate
98        );
99        clock!(self, clock, auto_conversion_strobe, spi_slave);
100        // Set default values
101        self.spi_slave.start_send.next = false;
102        self.spi_slave.continued_transaction.next = false;
103        self.spi_slave.bits.next = 0.into();
104        self.spi_slave.data_outbound.next = 0.into();
105        self.reg_ram.write_enable.next = false;
106        self.spi_slave.disabled.next = false;
107        self.cmd.next = self.spi_slave.data_inbound.val().get_bits::<8>(0);
108        self.reg_index.next = self.cmd.val().get_bits::<4>(0);
109        self.rw_flag.next = self.cmd.val().get_bit(7);
110        self.reg_ram.read_address.next = self.reg_read_index.q.val();
111        self.reg_ram.write_address.next = self.reg_write_index.q.val();
112        self.reg_ram.write_data.next = self.spi_slave.data_inbound.val().get_bits::<8>(0);
113        self.auto_conversion_strobe.enable.next = self.auto_conversions_enabled.q.val();
114        match self.state.q.val() {
115            MAX31856State::Start => {
116                self.boot.d.next = self.boot.q.val() + 1;
117                if self.boot.q.val().all() {
118                    self.state.d.next = MAX31856State::Ready
119                }
120            }
121            MAX31856State::Ready => {
122                self.spi_slave.continued_transaction.next = true;
123                self.spi_slave.bits.next = 8.into();
124                self.spi_slave.data_outbound.next = 0xFF.into();
125                self.spi_slave.start_send.next = true;
126                self.state.d.next = MAX31856State::GettingCmd;
127            }
128            MAX31856State::GettingCmd => {
129                if self.spi_slave.transfer_done.val() {
130                    if !self.rw_flag.val() {
131                        self.reg_read_index.d.next = self.reg_index.val();
132                        self.state.d.next = MAX31856State::RegFetchRead;
133                    } else {
134                        self.reg_write_index.d.next = self.reg_index.val();
135                        self.state.d.next = MAX31856State::WriteCmd;
136                    }
137                }
138            }
139            MAX31856State::RegFetchRead => {
140                self.state.d.next = MAX31856State::ReadCmd;
141            }
142            MAX31856State::ReadCmd => {
143                self.spi_slave.continued_transaction.next = true;
144                self.spi_slave.bits.next = 8.into();
145                self.spi_slave.data_outbound.next = bit_cast::<64, 8>(self.reg_ram.read_data.val());
146                self.spi_slave.start_send.next = true;
147                self.state.d.next = MAX31856State::WaitReadComplete;
148            }
149            MAX31856State::WaitReadComplete => {
150                if !self.spi_slave.busy.val() & self.spi_slave.transfer_done.val() {
151                    self.state.d.next = MAX31856State::Ready;
152                }
153                if self.spi_slave.busy.val() & self.spi_slave.transfer_done.val() {
154                    self.reg_read_index.d.next = self.reg_read_index.q.val() + 1;
155                    self.state.d.next = MAX31856State::RegFetchRead;
156                }
157            }
158            MAX31856State::WriteCmd => {
159                self.spi_slave.continued_transaction.next = true;
160                self.spi_slave.bits.next = 8.into();
161                self.spi_slave.data_outbound.next = 0xFF.into();
162                self.spi_slave.start_send.next = true;
163                self.state.d.next = MAX31856State::DoWrite;
164            }
165            MAX31856State::DoWrite => {
166                if !self.spi_slave.busy.val() & self.spi_slave.transfer_done.val() {
167                    if !self.reg_write_index.q.val().any() {
168                        self.auto_conversions_enabled.d.next =
169                            self.spi_slave.data_inbound.val().get_bit(7);
170                    }
171                    self.reg_ram.write_enable.next = true;
172                    self.state.d.next = MAX31856State::Ready;
173                }
174                if self.spi_slave.busy.val() & self.spi_slave.transfer_done.val() {
175                    self.reg_ram.write_enable.next = true;
176                    self.reg_write_index.d.next = self.reg_write_index.q.val() + 1;
177                    self.state.d.next = MAX31856State::WriteCmd;
178                }
179            }
180            _ => {
181                self.state.d.next = MAX31856State::Start;
182            }
183        }
184        // Warning! There is a contention between writes from the SPI bus and
185        // writes from the DAQ...  A more sophisticated model would segment
186        // the register ram into 2 blocks, and limit SPI writes to the lower block.
187        match self.dstate.q.val() {
188            DAQState::Idle => {
189                if self.auto_conversion_strobe.strobe.val() {
190                    self.auto_conversion_counter.d.next = self.auto_conversion_counter.q.val() + 1;
191                    self.dstate.d.next = DAQState::Convert;
192                }
193            }
194            DAQState::Convert => {
195                self.reg_ram.write_address.next = 0x0E.into();
196                self.reg_ram.write_data.next =
197                    bit_cast::<8, 3>(self.auto_conversion_counter.q.val().get_bits::<3>(0)) << 5;
198                self.reg_ram.write_enable.next = true;
199                self.dstate.d.next = DAQState::Copy0;
200            }
201            DAQState::Copy0 => {
202                self.reg_ram.write_address.next = 0x0D.into();
203                self.reg_ram.write_data.next =
204                    self.auto_conversion_counter.q.val().get_bits::<8>(3);
205                self.reg_ram.write_enable.next = true;
206                self.dstate.d.next = DAQState::Copy1;
207            }
208            DAQState::Copy1 => {
209                self.reg_ram.write_address.next = 0x0C.into();
210                self.reg_ram.write_data.next =
211                    self.auto_conversion_counter.q.val().get_bits::<8>(11);
212                self.reg_ram.write_enable.next = true;
213                self.dstate.d.next = DAQState::Idle;
214            }
215            _ => {
216                self.dstate.d.next = DAQState::Idle;
217            }
218        }
219    }
220}
221
222#[test]
223fn test_max31856_synthesizes() {
224    let mut uut = MAX31856Simulator::new(SPIConfig {
225        clock_speed: 1_000_000,
226        cs_off: true,
227        mosi_off: true,
228        speed_hz: 10_000,
229        cpha: true,
230        cpol: true,
231    });
232    uut.connect_all();
233    yosys_validate("max31856", &generate_verilog(&uut)).unwrap();
234}
235
236#[derive(LogicBlock)]
237struct Test31856 {
238    clock: Signal<In, Clock>,
239    master: SPIMaster<64>,
240    uut: MAX31856Simulator,
241}
242
243impl Logic for Test31856 {
244    #[hdl_gen]
245    fn update(&mut self) {
246        clock!(self, clock, master, uut);
247        SPIWiresMaster::join(&mut self.master.wires, &mut self.uut.wires);
248    }
249}
250
251impl Default for Test31856 {
252    fn default() -> Self {
253        Self {
254            clock: Default::default(),
255            master: SPIMaster::new(AD7193Config::sw().spi),
256            uut: MAX31856Simulator::new(AD7193Config::sw().spi),
257        }
258    }
259}
260
261#[cfg(test)]
262fn reg_read(
263    reg_index: u32,
264    x: Box<Test31856>,
265    sim: &mut Sim<Test31856>,
266) -> Result<(Bits<64>, Box<Test31856>), SimError> {
267    let cmd = (reg_index << 8) as u64;
268    let result = do_spi_txn(16, cmd.into(), false, x, sim)?;
269    let reg_val = result.0 & 0xFF;
270    Ok((reg_val, result.1))
271}
272
273#[cfg(test)]
274fn reg_write(
275    reg_index: u32,
276    reg_value: u64,
277    x: Box<Test31856>,
278    sim: &mut Sim<Test31856>,
279) -> Result<Box<Test31856>, SimError> {
280    let mut cmd = (((1 << 7) | reg_index) << 8) as u64;
281    cmd = cmd | (reg_value & 0xFF);
282    let ret = do_spi_txn(16, cmd.into(), false, x, sim)?;
283    Ok(ret.1)
284}
285
286#[cfg(test)]
287fn do_spi_txn(
288    bits: u16,
289    value: u64,
290    continued: bool,
291    mut x: Box<Test31856>,
292    sim: &mut Sim<Test31856>,
293) -> Result<(Bits<64>, Box<Test31856>), SimError> {
294    wait_clock_true!(sim, clock, x);
295    wait_clock_cycles!(sim, clock, x, 10);
296    x.master.data_outbound.next = value.to_bits();
297    x.master.bits_outbound.next = bits.to_bits();
298    x.master.continued_transaction.next = continued;
299    x.master.start_send.next = true;
300    wait_clock_cycle!(sim, clock, x);
301    x.master.start_send.next = false;
302    x = sim.watch(
303        |x| x.clock.val().clk && x.master.transfer_done.val().into(),
304        x,
305    )?;
306    let ret = x.master.data_inbound.val();
307    wait_clock_true!(sim, clock, x);
308    wait_clock_cycles!(sim, clock, x, 50);
309    Ok((ret, x))
310}
311
312#[cfg(test)]
313fn mk_test31856() -> Test31856 {
314    let mut uut = Test31856::default();
315    uut.clock.connect();
316    uut.master.continued_transaction.connect();
317    uut.master.start_send.connect();
318    uut.master.data_outbound.connect();
319    uut.master.bits_outbound.connect();
320    uut.connect_all();
321    uut
322}
323
324#[test]
325fn test_yosys_validate_test_fixture() {
326    let uut = mk_test31856();
327    yosys_validate("31856_1", &generate_verilog(&uut)).unwrap();
328}
329
330#[test]
331fn test_multireg_reads() {
332    let uut = mk_test31856();
333    let mut sim = Simulation::new();
334    sim.add_clock(5, |x: &mut Box<Test31856>| x.clock.next = !x.clock.val());
335    sim.add_testbench(move |mut sim: Sim<Test31856>| {
336        let mut x = sim.init()?;
337
338        wait_clock_true!(sim, clock, x);
339        wait_clock_cycles!(sim, clock, x, 20);
340        let cmd = 1 << 32;
341        let result = do_spi_txn(40, cmd, false, x, &mut sim)?;
342        x = result.1;
343        sim_assert_eq!(
344            sim,
345            result.0 & 0xFF_FF_FF_FF,
346            Bits::<64>::from(0x03_FF_7F_C0),
347            x
348        );
349        sim.done(x)
350    });
351    sim.run(Box::new(uut), 100_000).unwrap();
352}
353
354#[test]
355fn test_multireg_write() {
356    let uut = mk_test31856();
357    let mut sim = Simulation::new();
358    sim.add_clock(5, |x: &mut Box<Test31856>| x.clock.next = !x.clock.val());
359    sim.add_testbench(move |mut sim: Sim<Test31856>| {
360        let mut x = sim.init()?;
361
362        wait_clock_true!(sim, clock, x);
363        wait_clock_cycles!(sim, clock, x, 20);
364        let cmd = 0x81 << 32 | 0xDEADBEEF;
365        println!("CMD = {:x}", cmd);
366        let result = do_spi_txn(40, cmd, false, x, &mut sim)?;
367        x = result.1;
368        let cmd = 0x1 << 32;
369        let result = do_spi_txn(40, cmd, false, x, &mut sim)?;
370        x = result.1;
371        sim_assert_eq!(
372            sim,
373            result.0 & 0xFF_FF_FF_FF,
374            0xDEADBEEF_u32.to_bits::<64>(),
375            x
376        );
377        sim.done(x)
378    });
379    sim.run(Box::new(uut), 100_000).unwrap();
380}
381
382#[test]
383fn test_reg_reads() {
384    let uut = mk_test31856();
385    let mut sim = Simulation::new();
386    sim.add_clock(5, |x: &mut Box<Test31856>| x.clock.next = !x.clock.val());
387    sim.add_testbench(move |mut sim: Sim<Test31856>| {
388        let mut x = sim.init()?;
389
390        wait_clock_true!(sim, clock, x);
391        wait_clock_cycles!(sim, clock, x, 20);
392        for ndx in 0..16 {
393            println!("Reading register index {}", ndx);
394            let result = reg_read(ndx, x, &mut sim)?;
395            x = result.1;
396            println!("Value {} -> {:x}", ndx, result.0);
397            sim_assert_eq!(
398                sim,
399                result.0,
400                MAX31856_REG_INITS[ndx as usize].to_bits::<64>(),
401                x
402            );
403            wait_clock_true!(sim, clock, x);
404        }
405        sim.done(x)
406    });
407    //    sim.run_traced(Box::new(uut), 1_000_000, std::fs::File::create("max3.vcd").unwrap()).unwrap();
408    sim.run(Box::new(uut), 1_000_000).unwrap();
409}
410
411#[test]
412fn test_reg_writes() {
413    use std::num::Wrapping;
414    let uut = mk_test31856();
415    let mut sim = Simulation::new();
416    sim.add_clock(5, |x: &mut Box<Test31856>| x.clock.next = !x.clock.val());
417    sim.add_testbench(move |mut sim: Sim<Test31856>| {
418        let mut x = sim.init()?;
419
420        // Initialize the chip...
421        wait_clock_true!(sim, clock, x);
422        wait_clock_cycles!(sim, clock, x, 20);
423        for ndx in 0..16 {
424            let result = reg_read(ndx, x, &mut sim)?;
425            x = result.1;
426            sim_assert_eq!(
427                sim,
428                result.0,
429                MAX31856_REG_INITS[ndx as usize].to_bits::<64>(),
430                x
431            );
432            println!("Read of register {} -> {:x}", ndx, result.0);
433            x = reg_write(
434                ndx,
435                (MAX31856_REG_INITS[ndx as usize] as u64 + 1) as u64,
436                x,
437                &mut sim,
438            )?;
439            let result = reg_read(ndx, x, &mut sim)?;
440            x = result.1;
441            sim_assert_eq!(
442                sim,
443                result.0,
444                (Wrapping(MAX31856_REG_INITS[ndx as usize]) + Wrapping(1))
445                    .0
446                    .to_bits::<64>(),
447                x
448            );
449            println!("Re-read of register {} -> {:x}", ndx, result.0);
450        }
451        sim.done(x)
452    });
453    sim.run(Box::new(uut), 1_000_000).unwrap();
454}
455
456#[test]
457fn test_single_conversion() {
458    let uut = mk_test31856();
459    let mut sim = Simulation::new();
460    sim.add_clock(5, |x: &mut Box<Test31856>| x.clock.next = !x.clock.val());
461    sim.add_testbench(move |mut sim: Sim<Test31856>| {
462        let mut x = sim.init()?;
463
464        wait_clock_true!(sim, clock, x);
465        wait_clock_cycles!(sim, clock, x, 50);
466        x = reg_write(0, 0x80, x, &mut sim)?;
467        x = sim.wait(200_000, x)?;
468        let result = reg_read(0x0E, x, &mut sim)?;
469        x = result.1;
470        sim_assert_eq!(sim, result.0 & 0xFF, 0x40, x);
471        let cmd = 0xC << 24;
472        let result = do_spi_txn(32, cmd, false, x, &mut sim)?;
473        x = result.1;
474        sim_assert_eq!(sim, result.0 & 0xFFFFFF, 0x40, x);
475        sim.done(x)
476    });
477    //    sim.run(Box::new(uut), 1_000_000).unwrap();
478    sim.run_to_file(Box::new(uut), 1_000_000, "/tmp/mread.vcd")
479        .unwrap();
480}