1use rust_hdl_core::prelude::*;
2use rust_hdl_widgets::prelude::*;
3
4#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
5enum State {
6 Init,
7 Ready,
8 GettingCmd,
9 WaitSlaveIdle,
10 DoWrite,
11 DoRead,
12 DoNoop,
13 DoCommand,
14 ReadBack,
15 DoConvert,
16}
17
18#[derive(LogicBlock)]
19pub struct ADS8688Simulator {
20 pub wires: SPIWiresSlave,
21 pub clock: Signal<In, Clock>,
22 reg_ram: RAM<Bits<8>, 6>,
24 reg_address: Signal<Local, Bits<6>>,
27 rw_flag: Signal<Local, Bit>,
28 cmd_flag: Signal<Local, Bit>,
29 noop_flag: Signal<Local, Bit>,
30 spi_slave: SPISlave<64>,
32 state: DFF<State>,
34 reg_address_flop: DFF<Bits<6>>,
36 readback_flop: DFF<Bits<8>>,
38 conversion_counter: DFF<Bits<12>>,
40 command_register: DFF<Bits<8>>,
42 output_register: DFF<Bits<16>>,
44}
45
46fn ram_init_vec() -> Vec<u8> {
47 let mut ram_init = vec![0_u8; 0x40];
51 ram_init[1] = 0xFF;
52 for ch in 0..8 {
53 ram_init[0x15 + ch * 5 + 1] = 0xFF;
54 ram_init[0x15 + ch * 5 + 2] = 0xFF;
55 }
56 ram_init
57}
58
59#[test]
60fn test_ram_init_vec_is_correct() {
61 let vec = ram_init_vec();
62 assert_eq!(vec[1], 0xFF);
63 assert_eq!(vec[0x16], 0xFF);
64 assert_eq!(vec[0x17], 0xFF);
65 assert_eq!(vec[0x39], 0xFF);
66 assert_eq!(vec[0x3A], 0xFF);
67}
68
69impl ADS8688Simulator {
70 pub fn new(config: SPIConfig) -> Self {
71 assert!(config.clock_speed > 10 * config.speed_hz);
72 let reg_ram = ram_init_vec().iter().map(|x| x.to_bits()).into();
73 Self {
74 wires: Default::default(),
75 clock: Default::default(),
76 reg_ram,
77 reg_address: Default::default(),
78 rw_flag: Default::default(),
79 cmd_flag: Default::default(),
80 noop_flag: Default::default(),
81 spi_slave: SPISlave::new(config),
82 state: Default::default(),
83 reg_address_flop: Default::default(),
84 readback_flop: Default::default(),
85 conversion_counter: Default::default(),
86 command_register: Default::default(),
87 output_register: Default::default(),
88 }
89 }
90}
91
92impl Logic for ADS8688Simulator {
93 #[hdl_gen]
94 fn update(&mut self) {
95 SPIWiresSlave::link(&mut self.wires, &mut self.spi_slave.wires);
97 self.reg_ram.read_clock.next = self.clock.val();
99 self.reg_ram.write_clock.next = self.clock.val();
100 clock!(self, clock, spi_slave);
101 dff_setup!(
102 self,
103 clock,
104 state,
105 readback_flop,
106 reg_address_flop,
107 conversion_counter,
108 output_register,
109 command_register
110 );
111 self.spi_slave.start_send.next = false;
113 self.cmd_flag.next = self.spi_slave.data_inbound.val().get_bit(7);
114 self.reg_address.next = self.spi_slave.data_inbound.val().get_bits::<6>(1);
115 self.rw_flag.next = self.spi_slave.data_inbound.val().get_bit(0);
116 self.noop_flag.next = !self.spi_slave.data_inbound.val().get_bits::<8>(0).any();
117 self.reg_ram.read_address.next = self.reg_address_flop.q.val();
118 self.reg_ram.write_address.next = self.reg_address_flop.q.val();
119 self.spi_slave.continued_transaction.next = false;
120 self.spi_slave.bits.next = 0.into();
121 self.spi_slave.data_outbound.next = 0.into();
122 self.reg_ram.write_enable.next = false;
123 self.reg_ram.write_data.next = 0.into();
124 self.spi_slave.disabled.next = false;
125 match self.state.q.val() {
126 State::Init => {
127 if !self.spi_slave.busy.val() {
128 self.state.d.next = State::Ready;
129 }
130 }
131 State::Ready => {
132 self.spi_slave.continued_transaction.next = true;
133 self.spi_slave.bits.next = 8.into();
134 self.spi_slave.data_outbound.next = 0x00.into();
135 self.spi_slave.start_send.next = true;
136 self.state.d.next = State::GettingCmd;
137 }
138 State::GettingCmd => {
139 self.reg_address_flop.d.next = self.reg_address.val();
140 if self.spi_slave.transfer_done.val() {
141 self.state.d.next = State::WaitSlaveIdle;
142 if self.noop_flag.val() {
143 self.state.d.next = State::DoNoop;
144 } else {
145 if self.cmd_flag.val() {
146 self.state.d.next = State::DoCommand;
147 } else {
148 if self.rw_flag.val() {
149 self.spi_slave.continued_transaction.next = true;
150 self.spi_slave.bits.next = 8.into();
151 self.spi_slave.start_send.next = true;
152 self.state.d.next = State::DoWrite;
153 } else {
154 self.spi_slave.continued_transaction.next = true;
155 self.spi_slave.bits.next = 8.into();
156 self.spi_slave.start_send.next = true;
157 self.state.d.next = State::DoRead;
158 }
159 }
160 }
161 }
162 }
163 State::WaitSlaveIdle => {
164 if !self.spi_slave.busy.val() {
165 self.state.d.next = State::Ready;
166 }
167 }
168 State::DoRead => {
169 if self.spi_slave.transfer_done.val() {
170 self.spi_slave.continued_transaction.next = true;
171 self.spi_slave.bits.next = 8.into();
172 self.spi_slave.data_outbound.next =
173 bit_cast::<64, 8>(self.reg_ram.read_data.val());
174 self.spi_slave.start_send.next = true;
175 self.state.d.next = State::WaitSlaveIdle;
176 }
177 }
178 State::DoWrite => {
179 if self.spi_slave.transfer_done.val() {
180 self.reg_ram.write_data.next =
181 self.spi_slave.data_inbound.val().get_bits::<8>(0);
182 self.readback_flop.d.next = self.spi_slave.data_inbound.val().get_bits::<8>(0);
183 self.reg_ram.write_enable.next = true;
184 self.state.d.next = State::ReadBack;
185 }
186 }
187 State::ReadBack => {
188 self.spi_slave.continued_transaction.next = true;
189 self.spi_slave.bits.next = 8.into();
190 self.spi_slave.data_outbound.next = bit_cast::<64, 8>(self.readback_flop.q.val());
191 self.spi_slave.start_send.next = true;
192 self.state.d.next = State::WaitSlaveIdle;
193 }
194 State::DoNoop => {
195 self.state.d.next = State::DoConvert;
196 self.output_register.d.next =
197 bit_cast::<16, 3>(self.command_register.q.val().get_bits::<3>(2)) << 12
198 | bit_cast::<16, 12>(self.conversion_counter.q.val());
199 self.conversion_counter.d.next = self.conversion_counter.q.val() + 1;
200 }
201 State::DoCommand => {
202 self.command_register.d.next = self.spi_slave.data_inbound.val().get_bits::<8>(0);
203 self.reg_ram.write_address.next = 0x3F.into();
204 self.reg_ram.write_data.next = self.spi_slave.data_inbound.val().get_bits::<8>(0);
205 self.reg_ram.write_enable.next = true;
206 self.state.d.next = State::DoConvert;
207 self.output_register.d.next =
208 bit_cast::<16, 3>(self.command_register.q.val().get_bits::<3>(2)) << 12
209 | bit_cast::<16, 12>(self.conversion_counter.q.val());
210 self.conversion_counter.d.next = self.conversion_counter.q.val() + 1;
211 }
212 State::DoConvert => {
213 self.spi_slave.data_outbound.next =
214 bit_cast::<64, 16>(self.output_register.q.val());
215 self.spi_slave.continued_transaction.next = true;
216 self.spi_slave.bits.next = 16.into();
217 self.spi_slave.start_send.next = true;
218 self.state.d.next = State::WaitSlaveIdle;
219 }
220 _ => {
221 self.state.d.next = State::Init;
222 }
223 }
224 }
225}
226
227fn basic_spi_config() -> SPIConfig {
228 SPIConfig {
229 clock_speed: 1_000_000,
230 cs_off: true,
231 mosi_off: false,
232 speed_hz: 10_000,
233 cpha: false,
234 cpol: true,
235 }
236}
237
238#[test]
239fn test_ads8688_synthesizes() {
240 let mut uut = ADS8688Simulator::new(basic_spi_config());
241 uut.connect_all();
242 yosys_validate("ads8688", &generate_verilog(&uut)).unwrap();
243}
244
245#[derive(LogicBlock)]
246struct Test8688 {
247 clock: Signal<In, Clock>,
248 master: SPIMaster<64>,
249 adc: ADS8688Simulator,
250}
251
252impl Logic for Test8688 {
253 #[hdl_gen]
254 fn update(&mut self) {
255 clock!(self, clock, master, adc);
256 SPIWiresMaster::join(&mut self.master.wires, &mut self.adc.wires);
257 }
258}
259
260impl Default for Test8688 {
261 fn default() -> Self {
262 Self {
263 clock: Default::default(),
264 master: SPIMaster::new(basic_spi_config()),
265 adc: ADS8688Simulator::new(basic_spi_config()),
266 }
267 }
268}
269
270#[cfg(test)]
271fn mk_test8688() -> Test8688 {
272 let mut uut = Test8688::default();
273 uut.master.bits_outbound.connect();
274 uut.master.start_send.connect();
275 uut.master.continued_transaction.connect();
276 uut.master.data_outbound.connect();
277 uut.connect_all();
278 uut
279}
280
281#[test]
282fn test_yosys_validate_test_fixture() {
283 let uut = mk_test8688();
284 yosys_validate("ads8688_test_1", &generate_verilog(&uut)).unwrap();
285}
286
287#[cfg(test)]
288fn do_spi_txn(
289 bits: u16,
290 value: u64,
291 continued: bool,
292 mut x: Box<Test8688>,
293 sim: &mut Sim<Test8688>,
294) -> Result<(Bits<64>, Box<Test8688>), SimError> {
295 wait_clock_true!(sim, clock, x);
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
303 .watch(|x| x.master.transfer_done.val().into(), x)
304 .unwrap();
305 let ret = x.master.data_inbound.val();
306 for _ in 0..50 {
307 wait_clock_cycle!(sim, clock, x);
308 }
309 Ok((ret, x))
310}
311
312#[cfg(test)]
313fn reg_read(
314 reg_index: u32,
315 x: Box<Test8688>,
316 sim: &mut Sim<Test8688>,
317) -> Result<(u8, Box<Test8688>), SimError> {
318 let cmd = (reg_index << 17).into();
320 let result = do_spi_txn(24, cmd, false, x, sim)?;
321 let reg_val = (result.0 & 0xFF).get_bits::<8>(0).to_u8();
322 Ok((reg_val, result.1))
323}
324
325#[cfg(test)]
326fn reg_write(
327 reg_index: u32,
328 reg_value: u32,
329 x: Box<Test8688>,
330 sim: &mut Sim<Test8688>,
331) -> Result<(u8, Box<Test8688>), SimError> {
332 let cmd = ((reg_index << 17) | (1 << 16) | (reg_value << 8)).into();
334 let ret = do_spi_txn(24, cmd, false, x, sim)?;
335 Ok((ret.0.get_bits::<8>(0).to_u8(), ret.1))
336}
337
338#[cfg(test)]
339fn cmd_write(
340 cmd_value: u32,
341 x: Box<Test8688>,
342 sim: &mut Sim<Test8688>,
343) -> Result<Box<Test8688>, SimError> {
344 let cmd = (cmd_value << 8).into();
345 let ret = do_spi_txn(16, cmd, false, x, sim)?;
346 Ok(ret.1)
347}
348
349#[test]
350fn test_reg_reads() {
351 let uut = mk_test8688();
352 let mut sim = Simulation::new();
353 sim.add_clock(5, |x: &mut Box<Test8688>| x.clock.next = !x.clock.val());
354 sim.add_testbench(move |mut sim: Sim<Test8688>| {
355 let mut x = sim.init()?;
356 wait_clock_cycles!(sim, clock, x, 20);
358 let expected = ram_init_vec()
359 .into_iter()
360 .map(|x| x as LiteralType)
361 .collect::<Vec<_>>();
362 let mut reg_val;
363 for ndx in 0..0x3F {
364 println!("Reading register index {}", ndx);
365 (reg_val, x) = reg_read(ndx, x, &mut sim)?;
366 println!("Value {} -> {:x}", ndx, reg_val);
367 sim_assert_eq!(sim, u64::from(reg_val), expected[ndx as usize], x);
368 wait_clock_true!(sim, clock, x);
369 }
370 sim.done(x)
371 });
372 sim.run(Box::new(uut), 1_000_000).unwrap();
373}
374
375#[test]
376fn test_reg_writes() {
377 let uut = mk_test8688();
378 let mut sim = Simulation::new();
379 sim.add_clock(5, |x: &mut Box<Test8688>| x.clock.next = !x.clock.val());
380 sim.add_testbench(move |mut sim: Sim<Test8688>| {
381 let mut x = sim.init()?;
382 wait_clock_cycles!(sim, clock, x, 20);
384 let result = do_spi_txn(32, 0x00, false, x, &mut sim)?;
386 x = result.1;
387 let result = reg_write(5, 0xAF, x, &mut sim)?;
388 x = result.1;
389 println!("Write is {}", result.0);
390 sim_assert_eq!(sim, result.0, 0xAF, x);
391 let reg_val;
392 (reg_val, x) = reg_read(5, x, &mut sim)?;
394 sim_assert_eq!(sim, reg_val, 0xAF, x);
395 sim.done(x)
396 });
397 sim.run(Box::new(uut), 1_000_000).unwrap();
398 }
400
401#[test]
402fn test_cmd_write() {
403 let uut = mk_test8688();
404 let mut sim = Simulation::new();
405 sim.add_clock(5, |x: &mut Box<Test8688>| x.clock.next = !x.clock.val());
406 sim.add_testbench(move |mut sim: Sim<Test8688>| {
407 let mut x = sim.init()?;
408 wait_clock_cycles!(sim, clock, x, 20);
410 let result = do_spi_txn(32, 0x0, false, x, &mut sim)?;
412 x = result.1;
413 x = cmd_write(0xC4, x, &mut sim)?;
414 let mut reg_val;
416 (reg_val, x) = reg_read(0x3F, x, &mut sim)?;
417 sim_assert_eq!(sim, reg_val, 0xC4, x);
418 x = cmd_write(0x00, x, &mut sim)?;
420 (reg_val, x) = reg_read(0x3F, x, &mut sim)?;
422 sim_assert_eq!(sim, reg_val, 0xC4, x);
423 sim.done(x)
424 });
425 sim.run_to_file(
427 Box::new(uut),
428 1_000_000,
429 &vcd_path!("ads8688_cmd_write.vcd"),
430 )
431 .unwrap();
432}
433
434#[test]
435fn test_conversion() {
436 let uut = mk_test8688();
437 let mut sim = Simulation::new();
438 sim.add_clock(5, |x: &mut Box<Test8688>| x.clock.next = !x.clock.val());
439 sim.add_testbench(move |mut sim: Sim<Test8688>| {
440 let mut x = sim.init()?;
441 wait_clock_cycles!(sim, clock, x, 20);
443 let result = do_spi_txn(32, 0x0, false, x, &mut sim)?;
445 x = result.1;
446 x = cmd_write(0xC8, x, &mut sim)?;
447 let reg_val;
449 (reg_val, x) = reg_read(0x3F, x, &mut sim)?;
450 sim_assert_eq!(sim, reg_val, 0xC8, x);
451 let mut conversion;
452 for ndx in 0..4 {
453 (conversion, x) = do_spi_txn(24, 0x0, false, x, &mut sim)?;
454 println!("Conversion value {:x}", conversion);
455 sim_assert_eq!(sim, conversion, 0x2002 + ndx, x);
456 }
457 sim.done(x)
458 });
459 sim.run(Box::new(uut), 1_000_000).unwrap();
460 }
462
463#[test]
464fn test_pipelined_conversion() {
465 let uut = mk_test8688();
466 let mut sim = simple_sim!(Test8688, clock, 200_000_000_000, sim, {
467 let mut x = sim.init()?;
468 wait_clock_cycles!(sim, clock, x, 20);
470 let result = do_spi_txn(32, 0x0, false, x, &mut sim)?;
472 x = result.1;
473 x = cmd_write(0xC0, x, &mut sim)?;
475 let mut conversion;
479 for ndx in 1..8 {
480 let cmd = (0xC0 + (ndx << 2)) << 16;
481 (conversion, x) = do_spi_txn(24, cmd, false, x, &mut sim)?;
482 println!("Conversion value [{}] -> {:x}", ndx, conversion);
483 sim_assert_eq!(sim, conversion & 0xFFFF, ((ndx - 1) << 12) + ndx + 1, x);
484 }
485 (conversion, x) = do_spi_txn(24, 0, false, x, &mut sim)?;
487 println!("Conversion tail -> {:x}", conversion);
488 sim_assert_eq!(sim, conversion & 0xFFFF, 0x7009, x);
489 sim.done(x)
490 });
491 sim.run_to_file(Box::new(uut), 1_000_000, &vcd_path!("ads8688_pipeline.vcd"))
493 .unwrap();
494}