1#![doc = "Reusable synthesis-aware RHDL primitives for the `HashSigs` datapaths."]
2#![forbid(unsafe_code)]
3
4use quote::format_ident;
12use rhdl::{
13 core::{ScopedName, circuit::descriptor::SyncKind},
14 prelude::*,
15};
16use syn::parse_quote;
17
18#[derive(PartialEq, Debug, Clone, Copy, Default)]
33pub struct NoResetDff<T: Digital> {
34 _marker: core::marker::PhantomData<T>,
35}
36
37impl<T: Digital> NoResetDff<T> {
38 #[must_use]
40 pub const fn new() -> Self {
41 Self {
42 _marker: core::marker::PhantomData,
43 }
44 }
45}
46
47impl<T: Digital> SynchronousIO for NoResetDff<T> {
48 type I = T;
49 type O = T;
50 type Kernel = NoSynchronousKernel<ClockReset, T, (), (T, ())>;
51}
52
53impl<T: Digital> SynchronousDQ for NoResetDff<T> {
54 type D = ();
55 type Q = ();
56}
57
58#[derive(PartialEq, Debug, Digital, Clone, Copy)]
59#[doc(hidden)]
60pub struct NoResetDffState<T: Digital> {
61 clock_reset: ClockReset,
62 current: T,
63 next: T,
64}
65
66impl<T: Digital> Synchronous for NoResetDff<T> {
67 type S = NoResetDffState<T>;
68
69 fn init(&self) -> Self::S {
70 Self::S::dont_care()
71 }
72
73 fn sim(&self, clock_reset: ClockReset, input: Self::I, state: &mut Self::S) -> Self::O {
74 trace_push_path("no_reset_dff");
75 trace("input", &input);
76 let clock = clock_reset.clock;
77 if !clock.raw() {
78 state.next = input;
79 }
80 if clock.raw() && !state.clock_reset.clock.raw() {
81 state.current = state.next;
82 }
83 state.clock_reset = clock_reset;
84 trace("output", &state.current);
85 trace_pop_path();
86 state.current
87 }
88
89 fn descriptor(&self, scoped_name: ScopedName) -> Result<Descriptor<SyncKind>, RHDLError> {
90 let name = scoped_name.to_string();
91 Descriptor::<SyncKind> {
92 name: scoped_name,
93 input_kind: Self::I::static_kind(),
94 output_kind: Self::O::static_kind(),
95 d_kind: Kind::Empty,
96 q_kind: Kind::Empty,
97 kernel: None,
98 hdl: Some(Self::hdl(&name)?),
99 netlist: None,
100 _phantom: core::marker::PhantomData,
101 }
102 .with_netlist_black_box()
103 }
104}
105
106impl<T: Digital> NoResetDff<T> {
107 fn hdl(name: &str) -> Result<HDLDescriptor, RHDLError> {
108 let module_name = format_ident!("{name}");
109 let data_width: vlog::BitRange = (0..T::static_kind().bits()).into();
110 let clock_reset = ClockReset::dont_care();
111 let clock_index = bit_range(ClockReset::static_kind(), &path!(clock_reset.clock))?;
112 let clock_index = syn::Index::from(clock_index.0.start);
113 let module: vlog::ModuleDef = parse_quote! {
114 module #module_name(
115 input wire [1:0] clock_reset,
116 input wire [#data_width] i,
117 output reg [#data_width] o
118 );
119 wire clock;
120 assign clock = clock_reset[#clock_index];
121 always @(posedge clock) begin
122 o <= i;
123 end
124 endmodule
125 };
126 Ok(HDLDescriptor {
127 name: name.into(),
128 modules: module.into(),
129 })
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn emitted_storage_has_no_reset_or_initialization_branch() -> Result<(), RHDLError> {
139 let hdl = NoResetDff::<b32>::hdl("datapath_register")?
140 .modules
141 .pretty();
142
143 assert!(hdl.contains("always @(posedge clock)"));
144 assert!(hdl.contains("o <= i;"));
145 assert!(!hdl.contains("wire reset"));
146 assert!(!hdl.contains("if (reset)"));
147 assert!(!hdl.contains("initial begin"));
148 assert_eq!(hdl.matches("always @").count(), 1);
149 Ok(())
150 }
151
152 #[test]
153 fn descriptor_is_a_native_rhdl_black_box() -> Result<(), RHDLError> {
154 let descriptor = NoResetDff::<b32>::new().descriptor("datapath_register".into())?;
155 let hdl = descriptor.hdl()?.modules.pretty();
156 assert!(hdl.contains("module datapath_register"));
157 assert!(!hdl.contains("if (reset)"));
158 Ok(())
159 }
160}