rust_hdl_hls/
spi.rs

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// HLS ports
11// 0 - data in
12// 1 - data out
13// 2 - width in
14// 3 - start/type
15#[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
107// Assert D >= 8 + 2
108
109impl<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}