rust_hdl_hls/
miso_wide_port.rs

1use crate::bus::SoCPortResponder;
2use rust_hdl_core::prelude::*;
3use rust_hdl_widgets::prelude::*;
4
5#[derive(LogicBlock)]
6pub struct MISOWidePort<const W: usize, const D: usize> {
7    pub bus: SoCPortResponder<D>,
8    pub port_in: Signal<In, Bits<W>>,
9    pub strobe_in: Signal<In, Bit>,
10    pub clock_out: Signal<Out, Clock>,
11    accum: DFF<Bits<W>>,
12    address_active: DFF<Bit>,
13    offset: Constant<Bits<16>>,
14    shift: Constant<Bits<16>>,
15    modulo: Constant<Bits<8>>,
16    count: DFF<Bits<8>>,
17    ready: DFF<Bit>,
18}
19
20impl<const W: usize, const D: usize> Default for MISOWidePort<W, D> {
21    fn default() -> Self {
22        assert!(W > D);
23        assert_eq!(W % D, 0);
24        assert!(W / D < 256);
25        assert!(W < 65536);
26        Self {
27            bus: Default::default(),
28            port_in: Default::default(),
29            strobe_in: Default::default(),
30            clock_out: Default::default(),
31            accum: Default::default(),
32            address_active: Default::default(),
33            offset: Constant::new(D.to_bits()),
34            shift: Constant::new((W - D).to_bits()),
35            modulo: Constant::new((W / D).to_bits()),
36            count: Default::default(),
37            ready: Default::default(),
38        }
39    }
40}
41
42impl<const W: usize, const D: usize> Logic for MISOWidePort<W, D> {
43    #[hdl_gen]
44    fn update(&mut self) {
45        self.clock_out.next = self.bus.clock.val();
46        dff_setup!(self, clock_out, accum, address_active, count, ready);
47        // Latch prevention
48        self.address_active.d.next = self.bus.select.val();
49        self.bus.ready.next = false;
50        // On the strobe in, load the new value into our accumulator
51        if self.strobe_in.val() {
52            self.accum.d.next = self.port_in.val();
53            self.count.d.next = self.modulo.val();
54        }
55        self.bus.to_controller.next = 0.into();
56        self.ready.d.next = self.count.q.val().any() & self.address_active.q.val();
57        if self.address_active.q.val() {
58            self.bus.to_controller.next =
59                self.accum.q.val().get_bits::<D>(self.shift.val().index());
60            self.bus.ready.next = self.ready.q.val() & self.count.q.val().any();
61            if self.bus.strobe.val() {
62                self.accum.d.next = self.accum.q.val() << bit_cast::<W, 16>(self.offset.val());
63                self.count.d.next = self.count.q.val() - 1;
64            }
65        }
66    }
67}
68
69#[test]
70fn test_local_in_wide_port_is_synthesizable() {
71    let mut dev = MISOWidePort::<64, 16>::default();
72    dev.connect_all();
73    let vlog = generate_verilog(&dev);
74    yosys_validate("local_wide_in", &vlog).unwrap();
75}