use crate::dff_setup;
use crate::spi::master::SPIWiresMaster;
use crate::synchronizer::BitSynchronizer;
use crate::{dff::DFF, dff_with_init::DFFWithInit, spi::master::SPIConfig, strobe::Strobe};
use rust_hdl_core::prelude::*;
#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
enum SPIState {
Idle,
SetMode,
Activate,
Dwell,
LoadBit,
MActive,
SampleMISO,
MIdle,
Finish,
}
#[derive(Copy, Clone)]
pub struct SPIConfigDynamicMode {
pub clock_speed: u64,
pub cs_off: bool,
pub mosi_off: bool,
pub speed_hz: u64,
}
impl From<SPIConfig> for SPIConfigDynamicMode {
fn from(x: SPIConfig) -> Self {
SPIConfigDynamicMode {
clock_speed: x.clock_speed,
cs_off: x.cs_off,
mosi_off: x.mosi_off,
speed_hz: x.speed_hz,
}
}
}
impl Into<SPIConfig> for SPIConfigDynamicMode {
fn into(self) -> SPIConfig {
SPIConfig {
clock_speed: self.clock_speed,
cs_off: self.cs_off,
mosi_off: self.mosi_off,
speed_hz: self.speed_hz,
cpha: false,
cpol: false,
}
}
}
#[derive(LogicBlock)]
pub struct SPIMasterDynamicMode<const N: usize> {
pub clock: Signal<In, Clock>,
pub bits_outbound: Signal<In, Bits<16>>,
pub data_outbound: Signal<In, Bits<N>>,
pub data_inbound: Signal<Out, Bits<N>>,
pub start_send: Signal<In, Bit>,
pub transfer_done: Signal<Out, Bit>,
pub continued_transaction: Signal<In, Bit>,
pub wires: SPIWiresMaster,
pub busy: Signal<Out, Bit>,
register_out: DFF<Bits<N>>,
register_in: DFF<Bits<N>>,
state: DFF<SPIState>,
strobe: Strobe<32>,
pointer: DFF<Bits<16>>,
pointerm1: Signal<Local, Bits<16>>,
clock_state: DFF<Bit>,
done_flop: DFF<Bit>,
msel_flop: DFFWithInit<Bit>,
mosi_flop: DFF<Bit>,
miso_synchronizer: BitSynchronizer,
continued_save: DFF<Bit>,
cs_off: Constant<Bit>,
mosi_off: Constant<Bit>,
cpha_flop: DFF<Bit>,
cpol_flop: DFF<Bit>,
}
impl<const N: usize> SPIMasterDynamicMode<N> {
pub fn new(config: SPIConfigDynamicMode) -> Self {
assert!(8 * config.speed_hz <= config.clock_speed);
Self {
clock: Default::default(),
bits_outbound: Default::default(),
data_outbound: Default::default(),
data_inbound: Default::default(),
start_send: Default::default(),
transfer_done: Default::default(),
continued_transaction: Default::default(),
wires: Default::default(),
busy: Default::default(),
register_out: Default::default(),
register_in: Default::default(),
state: Default::default(),
strobe: Strobe::new(config.clock_speed, 4.0 * config.speed_hz as f64),
pointer: Default::default(),
pointerm1: Default::default(),
clock_state: Default::default(),
done_flop: Default::default(),
msel_flop: DFFWithInit::new(config.cs_off),
mosi_flop: Default::default(),
miso_synchronizer: Default::default(),
continued_save: Default::default(),
cs_off: Constant::new(config.cs_off),
mosi_off: Constant::new(config.mosi_off),
cpha_flop: Default::default(),
cpol_flop: Default::default(),
}
}
}
impl<const N: usize> Logic for SPIMasterDynamicMode<N> {
#[hdl_gen]
fn update(&mut self) {
dff_setup!(
self,
clock,
register_out,
register_in,
state,
pointer,
clock_state,
done_flop,
msel_flop,
mosi_flop,
continued_save,
cpha_flop,
cpol_flop
);
clock!(self, clock, miso_synchronizer, strobe);
self.strobe.enable.next = true;
self.miso_synchronizer.sig_in.next = self.wires.miso.val();
self.wires.mclk.next = self.clock_state.q.val();
self.wires.mosi.next = self.mosi_flop.q.val();
self.wires.msel.next = self.msel_flop.q.val();
self.data_inbound.next = self.register_in.q.val();
self.transfer_done.next = self.done_flop.q.val();
self.done_flop.d.next = false;
self.pointerm1.next = self.pointer.q.val() - 1;
self.busy.next = self.state.q.val() != SPIState::Idle;
match self.state.q.val() {
SPIState::Idle => {
self.clock_state.d.next = self.cpol_flop.q.val();
if self.start_send.val() {
self.register_out.d.next = self.data_outbound.val();
self.state.d.next = SPIState::SetMode; self.pointer.d.next = self.bits_outbound.val() & 0x00FF; self.cpha_flop.d.next = self.bits_outbound.val().get_bit(9);
self.cpol_flop.d.next = self.bits_outbound.val().get_bit(8);
self.register_in.d.next = 0.into(); self.continued_save.d.next = self.continued_transaction.val();
} else {
if !self.continued_save.q.val() {
self.msel_flop.d.next = self.cs_off.val(); }
}
self.mosi_flop.d.next = self.mosi_off.val(); }
SPIState::SetMode => {
self.clock_state.d.next = self.cpol_flop.q.val();
if self.strobe.strobe.val() {
self.state.d.next = SPIState::Activate;
}
}
SPIState::Activate => {
if self.strobe.strobe.val() {
self.msel_flop.d.next = !self.cs_off.val(); self.state.d.next = SPIState::Dwell;
}
}
SPIState::Dwell => {
if self.strobe.strobe.val() {
self.state.d.next = SPIState::LoadBit; }
}
SPIState::LoadBit => {
if self.pointer.q.val().any() {
self.mosi_flop.d.next = self
.register_out
.q
.val()
.get_bit(self.pointerm1.val().index()); self.pointer.d.next = self.pointerm1.val(); self.state.d.next = SPIState::MActive; self.clock_state.d.next = self.cpol_flop.q.val() ^ self.cpha_flop.q.val();
} else {
self.mosi_flop.d.next = self.mosi_off.val(); self.clock_state.d.next = self.cpol_flop.q.val();
self.state.d.next = SPIState::Finish; }
}
SPIState::MActive => {
if self.strobe.strobe.val() {
self.state.d.next = SPIState::SampleMISO;
}
}
SPIState::SampleMISO => {
self.register_in.d.next = self.register_in.q.val().replace_bit(
self.pointer.q.val().index(),
self.miso_synchronizer.sig_out.val(),
);
self.clock_state.d.next = !self.clock_state.q.val();
self.state.d.next = SPIState::MIdle;
}
SPIState::MIdle => {
if self.strobe.strobe.val() {
self.state.d.next = SPIState::LoadBit;
}
}
SPIState::Finish => {
if self.strobe.strobe.val() {
self.done_flop.d.next = true; self.state.d.next = SPIState::Idle;
}
}
_ => {
self.state.d.next = SPIState::Idle;
}
}
}
}
#[test]
fn test_spi_master_dynamic_mode_is_synthesizable() {
let config = SPIConfigDynamicMode {
clock_speed: 48_000_000,
cs_off: true,
mosi_off: false,
speed_hz: 1_000_000,
};
let mut dev = SPIMasterDynamicMode::<64>::new(config);
dev.connect_all();
yosys_validate("spi_master_dyn_mode", &generate_verilog(&dev)).unwrap();
}