1use crate::{dff::DFF, dff_setup, dff_with_init::DFFWithInit, strobe::Strobe};
2use rust_hdl_core::prelude::*;
3
4#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
5enum SPIState {
6 Idle,
7 Dwell,
8 LoadBit,
9 MActive,
10 SampleMISO,
11 MIdle,
12 Finish,
13}
14
15#[derive(Copy, Clone)]
16pub struct SPIConfig {
17 pub clock_speed: u64,
18 pub cs_off: bool,
19 pub mosi_off: bool,
20 pub speed_hz: u64,
21 pub cpha: bool,
22 pub cpol: bool,
23}
24
25#[derive(LogicInterface, Default)]
26#[join = "SPIWiresSlave"]
27pub struct SPIWiresMaster {
28 pub mosi: Signal<Out, Bit>,
29 pub miso: Signal<In, Bit>,
30 pub msel: Signal<Out, Bit>,
31 pub mclk: Signal<Out, Bit>,
32}
33
34#[derive(LogicInterface, Default)]
35#[join = "SPIWiresMaster"]
36pub struct SPIWiresSlave {
37 pub mosi: Signal<In, Bit>,
38 pub miso: Signal<Out, Bit>,
39 pub msel: Signal<In, Bit>,
40 pub mclk: Signal<In, Bit>,
41}
42
43#[derive(LogicBlock)]
44pub struct SPIMaster<const N: usize> {
45 pub clock: Signal<In, Clock>,
46 pub bits_outbound: Signal<In, Bits<16>>,
47 pub data_outbound: Signal<In, Bits<N>>,
48 pub data_inbound: Signal<Out, Bits<N>>,
49 pub start_send: Signal<In, Bit>,
50 pub transfer_done: Signal<Out, Bit>,
51 pub continued_transaction: Signal<In, Bit>,
52 pub busy: Signal<Out, Bit>,
53 pub wires: SPIWiresMaster,
54 register_out: DFF<Bits<N>>,
55 register_in: DFF<Bits<N>>,
56 state: DFF<SPIState>,
57 strobe: Strobe<32>,
58 pointer: DFF<Bits<16>>,
59 pointerm1: Signal<Local, Bits<16>>,
60 clock_state: DFF<Bit>,
61 done_flop: DFF<Bit>,
62 msel_flop: DFFWithInit<Bit>,
63 mosi_flop: DFF<Bit>,
64 continued_save: DFF<Bit>,
65 cs_off: Constant<Bit>,
66 mosi_off: Constant<Bit>,
67 cpha: Constant<Bit>,
68 cpol: Constant<Bit>,
69}
70
71impl<const N: usize> SPIMaster<N> {
72 pub fn new(config: SPIConfig) -> Self {
73 assert!(8 * config.speed_hz <= config.clock_speed);
74 Self {
75 clock: Default::default(),
76 bits_outbound: Default::default(),
77 data_outbound: Default::default(),
78 data_inbound: Default::default(),
79 start_send: Default::default(),
80 transfer_done: Default::default(),
81 continued_transaction: Default::default(),
82 busy: Default::default(),
83 wires: Default::default(),
84 register_out: Default::default(),
85 register_in: Default::default(),
86 state: Default::default(),
87 strobe: Strobe::new(config.clock_speed, 4.0 * config.speed_hz as f64),
88 pointer: Default::default(),
89 pointerm1: Default::default(),
90 clock_state: Default::default(),
91 done_flop: Default::default(),
92 msel_flop: DFFWithInit::new(config.cs_off),
93 mosi_flop: Default::default(),
94 continued_save: Default::default(),
95 cs_off: Constant::new(config.cs_off),
96 mosi_off: Constant::new(config.mosi_off),
97 cpha: Constant::new(config.cpha),
98 cpol: Constant::new(config.cpol),
99 }
100 }
101}
102
103impl<const N: usize> Logic for SPIMaster<N> {
104 #[hdl_gen]
105 fn update(&mut self) {
106 dff_setup!(
108 self,
109 clock,
110 register_out,
111 register_in,
112 state,
113 pointer,
114 clock_state,
115 done_flop,
116 msel_flop,
117 mosi_flop,
118 continued_save
119 );
120 clock!(self, clock, strobe);
121 self.strobe.enable.next = true;
123 self.wires.mclk.next = self.clock_state.q.val();
125 self.wires.mosi.next = self.mosi_flop.q.val();
126 self.wires.msel.next = self.msel_flop.q.val();
127 self.data_inbound.next = self.register_in.q.val();
129 self.transfer_done.next = self.done_flop.q.val();
130 self.done_flop.d.next = false;
131 self.pointerm1.next = self.pointer.q.val() - 1;
132 self.busy.next = true;
133 match self.state.q.val() {
135 SPIState::Idle => {
136 self.busy.next = false;
137 self.clock_state.d.next = self.cpol.val();
138 if self.start_send.val() {
139 self.register_out.d.next = self.data_outbound.val();
141 self.state.d.next = SPIState::Dwell; self.pointer.d.next = self.bits_outbound.val(); self.register_in.d.next = 0.into(); self.msel_flop.d.next = !self.cs_off.val(); self.continued_save.d.next = self.continued_transaction.val();
146 } else if !self.continued_save.q.val() {
147 self.msel_flop.d.next = self.cs_off.val(); }
149 self.mosi_flop.d.next = self.mosi_off.val(); }
151 SPIState::Dwell => {
152 if self.strobe.strobe.val() {
153 self.state.d.next = SPIState::LoadBit; }
156 }
157 SPIState::LoadBit => {
158 if self.pointer.q.val().any() {
159 self.mosi_flop.d.next = self
161 .register_out
162 .q
163 .val()
164 .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.val() ^ self.cpha.val();
168 } else {
169 self.mosi_flop.d.next = self.mosi_off.val(); self.clock_state.d.next = self.cpol.val();
171 self.state.d.next = SPIState::Finish; }
173 }
174 SPIState::MActive => {
175 if self.strobe.strobe.val() {
176 self.state.d.next = SPIState::SampleMISO;
177 }
178 }
179 SPIState::SampleMISO => {
180 self.register_in.d.next = self
181 .register_in
182 .q
183 .val()
184 .replace_bit(self.pointer.q.val().index(), self.wires.miso.val());
185 self.clock_state.d.next = !self.clock_state.q.val();
186 self.state.d.next = SPIState::MIdle;
187 }
188 SPIState::MIdle => {
189 if self.strobe.strobe.val() {
190 self.state.d.next = SPIState::LoadBit;
191 }
192 }
193 SPIState::Finish => {
194 if self.strobe.strobe.val() {
195 self.state.d.next = SPIState::Idle;
196 self.done_flop.d.next = true;
197 }
198 }
199 _ => {
200 self.state.d.next = SPIState::Idle;
201 }
202 }
203 }
204}
205
206#[test]
207fn test_spi_master_is_synthesizable() {
208 let config = SPIConfig {
209 clock_speed: 48_000_000,
210 cs_off: true,
211 mosi_off: false,
212 speed_hz: 1_000_000,
213 cpha: true,
214 cpol: false,
215 };
216 let mut dev = SPIMaster::<64>::new(config);
217 dev.connect_all();
218 yosys_validate("spi_master", &generate_verilog(&dev)).unwrap();
219}