1use crate::bridge::Bridge;
2use crate::bus::{SoCBusResponder, SoCPortController};
3use crate::miso_wide_port::MISOWidePort;
4use crate::mosi_port::MOSIPort;
5use crate::mosi_wide_port::MOSIWidePort;
6use crate::HLSNamedPorts;
7use rust_hdl_core::prelude::*;
8use rust_hdl_widgets::prelude::*;
9
10#[derive(LogicBlock)]
16pub struct HLSSPIMaster<const D: usize, const A: usize, const W: usize> {
17 pub spi: SPIWiresMaster,
18 pub upstream: SoCBusResponder<D, A>,
19 bridge: Bridge<D, A, 4>,
20 data_outbound: MOSIWidePort<W, D>,
21 data_inbound: MISOWidePort<W, D>,
22 num_bits: MOSIPort<D>,
23 start: MOSIPort<D>,
24 core: SPIMaster<W>,
25}
26
27impl<const D: usize, const A: usize, const W: usize> HLSNamedPorts for HLSSPIMaster<D, A, W> {
28 fn ports(&self) -> Vec<String> {
29 self.bridge.ports()
30 }
31}
32
33impl<const D: usize, const A: usize, const W: usize> Logic for HLSSPIMaster<D, A, W> {
34 #[hdl_gen]
35 fn update(&mut self) {
36 self.core.clock.next = self.bridge.clock_out.val();
37 self.core.data_outbound.next = self.data_outbound.port_out.val();
38 self.data_inbound.port_in.next = self.core.data_inbound.val();
39 self.data_inbound.strobe_in.next = self.core.transfer_done.val();
40 self.core.bits_outbound.next = bit_cast::<16, D>(self.num_bits.port_out.val());
41 self.core.continued_transaction.next = self.start.port_out.val().get_bit(0);
42 self.core.start_send.next = self.start.strobe_out.val();
43 SoCBusResponder::<D, A>::link(&mut self.upstream, &mut self.bridge.upstream);
44 SoCPortController::<D>::join(&mut self.bridge.nodes[0], &mut self.data_outbound.bus);
45 SoCPortController::<D>::join(&mut self.bridge.nodes[1], &mut self.data_inbound.bus);
46 SoCPortController::<D>::join(&mut self.bridge.nodes[2], &mut self.num_bits.bus);
47 SoCPortController::<D>::join(&mut self.bridge.nodes[3], &mut self.start.bus);
48 SPIWiresMaster::link(&mut self.spi, &mut self.core.wires);
49 self.num_bits.ready.next = true;
50 self.start.ready.next = !self.core.busy.val();
51 }
52}
53
54impl<const D: usize, const A: usize, const W: usize> HLSSPIMaster<D, A, W> {
55 pub fn new(config: SPIConfig) -> Self {
56 Self {
57 spi: Default::default(),
58 upstream: Default::default(),
59 bridge: Bridge::new(["data_outbound", "data_inbound", "num_bits", "start_flag"]),
60 data_outbound: Default::default(),
61 data_inbound: Default::default(),
62 num_bits: Default::default(),
63 start: Default::default(),
64 core: SPIMaster::new(config),
65 }
66 }
67}
68
69#[test]
70fn test_hls_spi_master_is_synthesizable() {
71 let spi_config = SPIConfig {
72 clock_speed: 48_000_000,
73 cs_off: true,
74 mosi_off: true,
75 speed_hz: 1_000_000,
76 cpha: true,
77 cpol: true,
78 };
79 let mut uut = HLSSPIMaster::<16, 8, 64>::new(spi_config);
80 uut.upstream.link_connect_dest();
81 uut.spi.link_connect_dest();
82 uut.connect_all();
83 let vlog = generate_verilog(&uut);
84 yosys_validate("hls_spi", &vlog).unwrap();
85}
86
87#[derive(LogicBlock)]
88pub struct HLSSPIMasterDynamicMode<const D: usize, const A: usize, const W: usize> {
89 pub spi: SPIWiresMaster,
90 pub upstream: SoCBusResponder<D, A>,
91 bridge: Bridge<D, A, 4>,
92 data_outbound: MOSIWidePort<W, D>,
93 data_inbound: MISOWidePort<W, D>,
94 num_bits_mode: MOSIPort<D>,
95 start: MOSIPort<D>,
96 core: SPIMasterDynamicMode<W>,
97}
98
99impl<const D: usize, const A: usize, const W: usize> HLSNamedPorts
100 for HLSSPIMasterDynamicMode<D, A, W>
101{
102 fn ports(&self) -> Vec<String> {
103 self.bridge.ports()
104 }
105}
106
107impl<const D: usize, const A: usize, const W: usize> Logic for HLSSPIMasterDynamicMode<D, A, W> {
110 #[hdl_gen]
111 fn update(&mut self) {
112 self.core.clock.next = self.bridge.clock_out.val();
113 self.core.data_outbound.next = self.data_outbound.port_out.val();
114 self.data_inbound.port_in.next = self.core.data_inbound.val();
115 self.data_inbound.strobe_in.next = self.core.transfer_done.val();
116 self.core.bits_outbound.next = bit_cast::<16, D>(self.num_bits_mode.port_out.val());
117 self.core.continued_transaction.next = self.start.port_out.val().get_bit(0);
118 self.core.start_send.next = self.start.strobe_out.val();
119 SoCBusResponder::<D, A>::link(&mut self.upstream, &mut self.bridge.upstream);
120 SoCPortController::<D>::join(&mut self.bridge.nodes[0], &mut self.data_outbound.bus);
121 SoCPortController::<D>::join(&mut self.bridge.nodes[1], &mut self.data_inbound.bus);
122 SoCPortController::<D>::join(&mut self.bridge.nodes[2], &mut self.num_bits_mode.bus);
123 SoCPortController::<D>::join(&mut self.bridge.nodes[3], &mut self.start.bus);
124 SPIWiresMaster::link(&mut self.spi, &mut self.core.wires);
125 self.num_bits_mode.ready.next = true;
126 self.start.ready.next = !self.core.busy.val();
127 }
128}
129
130impl<const D: usize, const A: usize, const W: usize> HLSSPIMasterDynamicMode<D, A, W> {
131 pub fn new(config: SPIConfigDynamicMode) -> Self {
132 Self {
133 spi: Default::default(),
134 upstream: Default::default(),
135 bridge: Bridge::new([
136 "data_outbound",
137 "data_inbound",
138 "num_bits_mode",
139 "start_flag",
140 ]),
141 data_outbound: Default::default(),
142 data_inbound: Default::default(),
143 num_bits_mode: Default::default(),
144 start: Default::default(),
145 core: SPIMasterDynamicMode::new(config),
146 }
147 }
148}
149
150#[test]
151fn test_hls_spi_master_dynamic_mode_is_synthesizable() {
152 let spi_config = SPIConfigDynamicMode {
153 clock_speed: 48_000_000,
154 cs_off: true,
155 mosi_off: true,
156 speed_hz: 1_000_000,
157 };
158 let mut uut = HLSSPIMasterDynamicMode::<16, 8, 64>::new(spi_config);
159 uut.upstream.link_connect_dest();
160 uut.spi.link_connect_dest();
161 uut.connect_all();
162 let vlog = generate_verilog(&uut);
163 yosys_validate("hsl_spi_dm", &vlog).unwrap();
164}
165
166#[derive(LogicBlock)]
167pub struct HLSSPIMuxSlaves<const D: usize, const A: usize, const N: usize> {
168 pub to_slaves: [SPIWiresMaster; N],
169 pub from_bus: SPIWiresSlave,
170 pub upstream: SoCBusResponder<D, A>,
171 mux: MuxSlaves<N, D>,
172 bridge: Bridge<D, A, 1>,
173 select: MOSIPort<D>,
174}
175
176impl<const D: usize, const A: usize, const N: usize> HLSNamedPorts for HLSSPIMuxSlaves<D, A, N> {
177 fn ports(&self) -> Vec<String> {
178 self.bridge.ports()
179 }
180}
181
182impl<const D: usize, const A: usize, const N: usize> Logic for HLSSPIMuxSlaves<D, A, N> {
183 #[hdl_gen]
184 fn update(&mut self) {
185 SoCBusResponder::<D, A>::link(&mut self.upstream, &mut self.bridge.upstream);
186 SoCPortController::<D>::join(&mut self.bridge.nodes[0], &mut self.select.bus);
187 for i in 0..N {
188 SPIWiresMaster::link(&mut self.to_slaves[i], &mut self.mux.to_slaves[i]);
189 }
190 self.mux.sel.next = self.select.port_out.val();
191 self.select.ready.next = true;
192 SPIWiresSlave::link(&mut self.from_bus, &mut self.mux.from_master);
193 }
194}
195
196impl<const D: usize, const A: usize, const N: usize> Default for HLSSPIMuxSlaves<D, A, N> {
197 fn default() -> Self {
198 assert!((1 << D) > N);
199 Self {
200 to_slaves: array_init::array_init(|_| Default::default()),
201 upstream: Default::default(),
202 from_bus: Default::default(),
203 mux: Default::default(),
204 bridge: Bridge::new(["select"]),
205 select: Default::default(),
206 }
207 }
208}
209
210#[test]
211fn test_hls_spi_mux_slaves_is_synthesizable() {
212 let mut uut = HLSSPIMuxSlaves::<8, 16, 4>::default();
213 uut.connect_all();
214 yosys_validate("hls_spi_mux_slaves", &generate_verilog(&uut)).unwrap()
215}
216
217#[derive(LogicBlock)]
218pub struct HLSSPIMuxMasters<const D: usize, const A: usize, const N: usize> {
219 pub from_masters: [SPIWiresSlave; N],
220 pub upstream: SoCBusResponder<D, A>,
221 pub to_bus: SPIWiresMaster,
222 mux: MuxMasters<N, D>,
223 bridge: Bridge<D, A, 1>,
224 select: MOSIPort<D>,
225}
226
227impl<const D: usize, const A: usize, const N: usize> HLSNamedPorts for HLSSPIMuxMasters<D, A, N> {
228 fn ports(&self) -> Vec<String> {
229 self.bridge.ports()
230 }
231}
232
233impl<const D: usize, const A: usize, const N: usize> Logic for HLSSPIMuxMasters<D, A, N> {
234 #[hdl_gen]
235 fn update(&mut self) {
236 SoCBusResponder::<D, A>::link(&mut self.upstream, &mut self.bridge.upstream);
237 SoCPortController::<D>::join(&mut self.bridge.nodes[0], &mut self.select.bus);
238 for i in 0..N {
239 SPIWiresSlave::link(&mut self.from_masters[i], &mut self.mux.from_masters[i]);
240 }
241 self.mux.sel.next = self.select.port_out.val();
242 self.select.ready.next = true;
243 SPIWiresMaster::link(&mut self.to_bus, &mut self.mux.to_bus);
244 }
245}
246
247impl<const D: usize, const A: usize, const N: usize> Default for HLSSPIMuxMasters<D, A, N> {
248 fn default() -> Self {
249 assert!((1 << D) > N);
250 Self {
251 from_masters: array_init::array_init(|_| Default::default()),
252 upstream: Default::default(),
253 to_bus: Default::default(),
254 mux: Default::default(),
255 bridge: Bridge::new(["select"]),
256 select: Default::default(),
257 }
258 }
259}
260
261#[test]
262fn test_hls_spi_mux_is_synthesizable() {
263 let mut uut = HLSSPIMuxMasters::<8, 16, 4>::default();
264 uut.connect_all();
265 yosys_validate("hls_spi_mux", &generate_verilog(&uut)).unwrap()
266}