rust_hdl_widgets/fifo/
fifo_expander_n.rs1use rust_hdl_core::prelude::*;
2
3use crate::{dff::DFF, dff_setup};
4
5#[derive(Copy, Clone, Debug, PartialEq)]
6pub enum WordOrder {
7 LeastSignificantFirst,
8 MostSignificantFirst,
9}
10
11#[derive(LogicBlock)]
12pub struct FIFOExpanderN<const DN: usize, const DW: usize> {
13 pub data_in: Signal<In, Bits<DN>>,
15 pub read: Signal<Out, Bit>,
16 pub empty: Signal<In, Bit>,
17 pub data_out: Signal<Out, Bits<DW>>,
19 pub write: Signal<Out, Bit>,
20 pub full: Signal<In, Bit>,
21 pub clock: Signal<In, Clock>,
24 load_count: DFF<Bits<8>>,
25 loaded: Signal<Local, Bit>,
26 complete_data_available: Signal<Local, Bit>,
27 will_write: Signal<Local, Bit>,
28 will_consume: Signal<Local, Bit>,
29 data_store: DFF<Bits<DW>>,
30 offset: Constant<Bits<DW>>,
31 ratio: Constant<Bits<8>>,
32 placement: Constant<Bits<DW>>,
33 msw_first: Constant<bool>,
34}
35
36impl<const DN: usize, const DW: usize> Logic for FIFOExpanderN<DN, DW> {
37 #[hdl_gen]
38 fn update(&mut self) {
39 dff_setup!(self, clock, load_count, data_store);
41 self.loaded.next = self.load_count.q.val() == self.ratio.val();
43 self.complete_data_available.next = self.loaded.val() & !self.empty.val();
46 self.will_write.next = self.complete_data_available.val() & !self.full.val();
48 self.will_consume.next = !self.empty.val() & (self.will_write.val() | !self.loaded.val());
50 if self.will_consume.val() & !self.loaded.val() {
52 if self.msw_first.val() {
53 self.data_store.d.next = (self.data_store.q.val() << self.offset.val())
54 | bit_cast::<DW, DN>(self.data_in.val());
55 } else {
56 self.data_store.d.next = (self.data_store.q.val() >> self.offset.val())
57 | (bit_cast::<DW, DN>(self.data_in.val()) << self.placement.val());
58 }
59 self.load_count.d.next = self.load_count.q.val() + 1;
60 }
61 if self.msw_first.val() {
63 self.data_out.next = bit_cast::<DW, DN>(self.data_in.val())
64 | (self.data_store.q.val() << self.offset.val());
65 } else {
66 self.data_out.next = (bit_cast::<DW, DN>(self.data_in.val()) << self.placement.val())
67 | (self.data_store.q.val() >> self.offset.val());
68 }
69 self.write.next = self.will_write.val();
70 self.read.next = self.will_consume.val();
71 if self.will_write.val() {
72 self.load_count.d.next = 0.into();
73 }
74 }
75}
76
77impl<const DN: usize, const DW: usize> FIFOExpanderN<DN, DW> {
78 pub fn new(order: WordOrder) -> Self {
79 assert!(DW > DN);
80 assert_eq!(DW % DN, 0);
81 Self {
82 data_in: Default::default(),
83 read: Default::default(),
84 empty: Default::default(),
85 data_out: Default::default(),
86 write: Default::default(),
87 full: Default::default(),
88 clock: Default::default(),
89 load_count: Default::default(),
90 loaded: Default::default(),
91 complete_data_available: Default::default(),
92 will_write: Default::default(),
93 will_consume: Default::default(),
94 data_store: Default::default(),
95 offset: Constant::new(DN.to_bits()),
96 ratio: Constant::new((DW / DN - 1).to_bits()),
97 placement: Constant::new((DN * (DW / DN - 1)).to_bits()),
98 msw_first: Constant::new(match order {
99 WordOrder::LeastSignificantFirst => false,
100 WordOrder::MostSignificantFirst => true,
101 }),
102 }
103 }
104}
105
106#[test]
107fn fifo_expandern_is_synthesizable() {
108 let mut dev = FIFOExpanderN::<4, 32>::new(WordOrder::MostSignificantFirst);
109 dev.connect_all();
110 yosys_validate("fifo_expandern", &generate_verilog(&dev)).unwrap();
111}