use rust_hdl_core::prelude::*;
use rust_hdl_widgets::prelude::*;
#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
enum ADS868XState {
Ready,
Waiting,
Dispatch,
ReadWordCmd,
ReadByteCmd,
WriteWordCmd,
WriteMSBCmd,
WriteLSBCmd,
WriteDone,
Nop,
}
#[derive(LogicBlock)]
pub struct ADS868XSimulator {
pub wires: SPIWiresSlave,
pub clock: Signal<In, Clock>,
reg_ram: RAM<Bits<16>, 5>,
spi_slave: SPISlave<32>,
state: DFF<ADS868XState>,
conversion_counter: DFF<Bits<16>>,
inbound: DFF<Bits<32>>,
read_cmd: Signal<Local, Bits<5>>,
write_cmd: Signal<Local, Bits<7>>,
address: Signal<Local, Bits<9>>,
data_parity: Signal<Local, Bit>,
id_parity: Signal<Local, Bit>,
}
impl ADS868XSimulator {
pub fn spi_hw() -> SPIConfig {
SPIConfig {
clock_speed: 48_000_000,
cs_off: true,
mosi_off: true,
speed_hz: 400_000,
cpha: false,
cpol: false,
}
}
pub fn spi_sw() -> SPIConfig {
SPIConfig {
clock_speed: 1_000_000,
cs_off: true,
mosi_off: true,
speed_hz: 10_000,
cpha: false,
cpol: false,
}
}
pub fn new(spi_config: SPIConfig) -> Self {
assert!(spi_config.clock_speed > 10 * spi_config.speed_hz);
Self {
wires: Default::default(),
clock: Default::default(),
reg_ram: Default::default(),
spi_slave: SPISlave::new(spi_config),
state: Default::default(),
conversion_counter: Default::default(),
inbound: Default::default(),
read_cmd: Default::default(),
write_cmd: Default::default(),
address: Default::default(),
data_parity: Default::default(),
id_parity: Default::default(),
}
}
}
#[test]
fn test_indexing() {
let val: Bits<32> = 0b11000_00_101_001_100_00000000_00000000.into();
assert_eq!(val.get_bits::<5>(27).index(), 0b11000);
assert_eq!(val.get_bits::<9>(16).index(), 0b101_001_100);
}
impl Logic for ADS868XSimulator {
#[hdl_gen]
fn update(&mut self) {
SPIWiresSlave::link(&mut self.wires, &mut self.spi_slave.wires);
self.reg_ram.read_clock.next = self.clock.val();
self.reg_ram.write_clock.next = self.clock.val();
clock!(self, clock, spi_slave);
dff_setup!(self, clock, state, conversion_counter, inbound);
self.spi_slave.start_send.next = false;
self.spi_slave.continued_transaction.next = false;
self.spi_slave.bits.next = 0.into();
self.spi_slave.data_outbound.next = 0.into();
self.reg_ram.write_enable.next = false;
self.reg_ram.write_data.next = 0.into();
self.spi_slave.disabled.next = false;
self.read_cmd.next = self.inbound.q.val().get_bits::<5>(27);
self.write_cmd.next = self.inbound.q.val().get_bits::<7>(25);
self.address.next = self.inbound.q.val().get_bits::<9>(16);
self.reg_ram.write_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
self.reg_ram.read_address.next = 0.into();
self.data_parity.next = self.conversion_counter.q.val().xor();
self.id_parity.next = (self.reg_ram.read_data.val() & 0x0FF).xor();
match self.state.q.val() {
ADS868XState::Ready => {
if !self.spi_slave.busy.val() {
self.state.d.next = ADS868XState::Nop;
}
}
ADS868XState::Waiting => {
if self.spi_slave.transfer_done.val() {
self.inbound.d.next = self.spi_slave.data_inbound.val();
self.state.d.next = ADS868XState::Dispatch;
}
}
ADS868XState::Dispatch => {
if self.read_cmd.val() == 0b11001 {
self.state.d.next = ADS868XState::ReadWordCmd;
self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
} else if self.read_cmd.val() == 0b01001 {
self.state.d.next = ADS868XState::ReadByteCmd;
self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
} else if self.write_cmd.val() == 0b11010_00 {
self.state.d.next = ADS868XState::WriteWordCmd;
} else if self.write_cmd.val() == 0b11010_01 {
self.state.d.next = ADS868XState::WriteMSBCmd;
self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
} else if self.write_cmd.val() == 0b11010_10 {
self.state.d.next = ADS868XState::WriteLSBCmd;
self.reg_ram.read_address.next = bit_cast::<5, 9>(self.address.val() >> 1);
} else {
self.reg_ram.read_address.next = 0x02.into();
self.state.d.next = ADS868XState::Nop;
}
}
ADS868XState::ReadWordCmd => {
self.spi_slave.data_outbound.next =
bit_cast::<32, 16>(self.reg_ram.read_data.val());
self.spi_slave.bits.next = 16.into();
self.spi_slave.start_send.next = true;
self.state.d.next = ADS868XState::Waiting;
}
ADS868XState::ReadByteCmd => {
if self.address.val().get_bit(0) {
self.spi_slave.data_outbound.next =
bit_cast::<32, 16>(self.reg_ram.read_data.val() >> 8);
} else {
self.spi_slave.data_outbound.next =
bit_cast::<32, 16>(self.reg_ram.read_data.val() & 0xFF);
}
self.spi_slave.bits.next = 8.into();
self.spi_slave.start_send.next = true;
self.state.d.next = ADS868XState::Waiting;
}
ADS868XState::WriteWordCmd => {
self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0xFFFF);
self.reg_ram.write_enable.next = true;
self.state.d.next = ADS868XState::WriteDone;
}
ADS868XState::WriteLSBCmd => {
self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0x00FF)
| (self.reg_ram.read_data.val() & 0xFF00);
self.reg_ram.write_enable.next = true;
self.state.d.next = ADS868XState::WriteDone;
}
ADS868XState::WriteMSBCmd => {
self.reg_ram.write_data.next = bit_cast::<16, 32>(self.inbound.q.val() & 0xFF00)
| (self.reg_ram.read_data.val() & 0x00FF);
self.reg_ram.write_enable.next = true;
self.state.d.next = ADS868XState::WriteDone;
}
ADS868XState::WriteDone => {
self.spi_slave.bits.next = 32.into();
self.spi_slave.data_outbound.next = self.inbound.q.val();
self.spi_slave.start_send.next = true;
self.state.d.next = ADS868XState::Waiting;
}
ADS868XState::Nop => {
self.spi_slave.bits.next = 32.into();
self.spi_slave.data_outbound.next =
(bit_cast::<32, 16>(self.conversion_counter.q.val()) << 16)
| (bit_cast::<32, 16>(self.reg_ram.read_data.val() & 0x0FF) << 12)
| (bit_cast::<32, 1>(self.data_parity.val().into()) << 8)
| (bit_cast::<32, 1>(
(self.data_parity.val() ^ self.id_parity.val()).into(),
) << 9);
self.spi_slave.start_send.next = true;
self.state.d.next = ADS868XState::Waiting;
self.conversion_counter.d.next = self.conversion_counter.q.val() + 1;
}
_ => {
self.state.d.next = ADS868XState::Ready;
}
}
}
}
#[test]
fn test_ads8689_synthesizes() {
let mut uut = ADS868XSimulator::new(ADS868XSimulator::spi_sw());
uut.connect_all();
yosys_validate("ads8689", &generate_verilog(&uut)).unwrap();
}
#[derive(LogicBlock)]
struct Test8689 {
clock: Signal<In, Clock>,
master: SPIMaster<32>,
adc: ADS868XSimulator,
}
impl Logic for Test8689 {
#[hdl_gen]
fn update(&mut self) {
clock!(self, clock, master, adc);
SPIWiresMaster::join(&mut self.master.wires, &mut self.adc.wires);
}
}
impl Default for Test8689 {
fn default() -> Self {
Self {
clock: Default::default(),
master: SPIMaster::new(ADS868XSimulator::spi_sw()),
adc: ADS868XSimulator::new(ADS868XSimulator::spi_sw()),
}
}
}
#[cfg(test)]
fn do_spi_txn(
bits: u16,
value: u64,
continued: bool,
mut x: Box<Test8689>,
sim: &mut Sim<Test8689>,
) -> Result<(Bits<32>, Box<Test8689>), SimError> {
wait_clock_true!(sim, clock, x);
x.master.data_outbound.next = value.to_bits();
x.master.bits_outbound.next = bits.to_bits();
x.master.continued_transaction.next = continued;
x.master.start_send.next = true;
wait_clock_cycle!(sim, clock, x);
x.master.start_send.next = false;
x = sim
.watch(|x| x.master.transfer_done.val().into(), x)
.unwrap();
let ret = x.master.data_inbound.val();
for _ in 0..50 {
wait_clock_cycle!(sim, clock, x);
}
Ok((ret, x))
}
#[cfg(test)]
fn mk_test8689() -> Test8689 {
let mut uut = Test8689::default();
uut.clock.connect();
uut.master.continued_transaction.connect();
uut.master.start_send.connect();
uut.master.data_outbound.connect();
uut.master.bits_outbound.connect();
uut.connect_all();
uut
}
#[test]
fn test_yosys_validate_test_fixture() {
let uut = mk_test8689();
yosys_validate("8689_1", &generate_verilog(&uut)).unwrap();
}
#[test]
fn test_reg_writes() {
let uut = mk_test8689();
let mut sim = Simulation::new();
sim.add_clock(5, |x: &mut Box<Test8689>| x.clock.next = !x.clock.val());
sim.add_testbench(move |mut sim: Sim<Test8689>| {
let mut x = sim.init()?;
wait_clock_cycles!(sim, clock, x, 50);
wait_clock_true!(sim, clock, x);
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(32, 0xd0_02_00_02, false, x, &mut sim)?;
x = result.1;
wait_clock_cycle!(sim, clock, x);
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(32, 0x48_02_00_00, false, x, &mut sim)?;
x = result.1;
let result = do_spi_txn(8, 0x00, false, x, &mut sim)?;
println!("ID Register read {:x}", result.0);
x = result.1;
sim_assert_eq!(sim, result.0.index(), 2, x);
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(32, 0xd0_10_40_08, false, x, &mut sim)?;
x = result.1;
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(32, 0xc8_10_00_00, false, x, &mut sim)?;
x = result.1;
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(16, 0x00, false, x, &mut sim)?;
x = result.1;
sim_assert_eq!(sim, result.0.index(), 0x40_08, x);
for i in 0..5 {
wait_clock_cycle!(sim, clock, x);
let result = do_spi_txn(32, 0x00_00_00_00, false, x, &mut sim)?;
x = result.1;
println!("Reading is {:x}", result.0);
sim_assert_eq!(sim, (result.0 & 0xFFFF0000), ((i + 2) << 16), x);
let parity_bit = result.0 & 0x100 != 0;
let data: Bits<32> = (result.0 & 0xFFFF0000) >> 16;
sim_assert_eq!(sim, data.xor(), parity_bit, x);
}
sim.done(x)
});
sim.run_to_file(Box::new(uut), 1_000_000, &vcd_path!("ad868x.vcd"))
.unwrap();
}
#[test]
fn test_parity_calculations() {
for sample in [
0x00020C00,
0x92ab1400_u32,
0x734b1800,
0x4fc81400,
0x7bee1400,
0x94821800_u32,
0x5eb31400,
0x4eaa1400,
0x8ac91800_u32,
0x95321800_u32,
0x54c01800,
0x561a1800,
0x91601800_u32,
0x7e401800,
0x50961400,
] {
let mut data = (sample & 0xFFFF_0000_u32) >> 16;
let mut parity = false;
for _ in 0..16 {
parity = parity ^ (data & 0x1 != 0);
data = data >> 1;
}
let adc_flag = (sample & 0x800) != 0;
assert_eq!(adc_flag, parity);
}
}