rust_hdl_widgets/synchronizer.rs
1use rust_hdl_core::prelude::*;
2
3use crate::{dff::DFF, dff_setup};
4
5/// A [BitSynchronizer] is used to move signals that are asynchronous to a clock into that
6/// clock domain using a pair of back-to-back flip-flops. While the first flip flop may
7/// become metastable, the second one is likely to be stable.
8#[derive(LogicBlock, Default)]
9pub struct BitSynchronizer {
10 /// The input signal, which is asynchronous to the clock
11 pub sig_in: Signal<In, Bit>,
12 /// The output signal, synchronized to the clock
13 pub sig_out: Signal<Out, Bit>,
14 /// The clock signal to synchronize the output to
15 pub clock: Signal<In, Clock>,
16 dff0: DFF<Bit>,
17 dff1: DFF<Bit>,
18}
19
20impl Logic for BitSynchronizer {
21 #[hdl_gen]
22 fn update(&mut self) {
23 dff_setup!(self, clock, dff0, dff1);
24 self.dff0.d.next = self.sig_in.val();
25 self.dff1.d.next = self.dff0.q.val();
26 self.sig_out.next = self.dff1.q.val();
27 }
28}
29
30#[test]
31fn sync_is_synthesizable() {
32 let mut dev: BitSynchronizer = Default::default();
33 dev.connect_all();
34 yosys_validate("sync", &generate_verilog(&dev)).unwrap();
35}
36
37#[derive(Copy, Clone, Debug, PartialEq, LogicState)]
38enum SyncSenderState {
39 Idle,
40 WaitAck,
41 WaitDone,
42}
43
44/// When you need to send many bits between two clock domains, it is risky to use a vector
45/// of [BitSynchronizer] structs. That is because, you cannot guarantee at any given moment
46/// that all of the bits of your multi-bit signal will cross into the new clock domain at once.
47/// So to synchronize a multi-bit signal, use a [SyncSender] and [SyncReceiver] pair. These
48/// widgets will use a set of handshake signals to move a value from one clock domain to another
49/// safely. Note that while the state machine is executing, the synchronizer will indicate it
50/// is busy. Crossing clock domains with greater ease is best done with an [AsynchronousFIFO].
51#[derive(LogicBlock, Default)]
52pub struct SyncSender<T: Synth> {
53 /// The input signal to synchronize across clock domains
54 pub sig_in: Signal<In, T>,
55 /// The input signals are assumed to be synchronous to this clock
56 pub clock: Signal<In, Clock>,
57 /// These are the wires used to send signals to the [SyncReceiver].
58 pub sig_cross: Signal<Out, T>,
59 /// A protocol flag signal indicating that data is ready to be transferred to the second clock doamin.
60 pub flag_out: Signal<Out, Bit>,
61 /// A protocol flag signal indicating that the data has been transferred to the second clock domain.
62 pub ack_in: Signal<In, Bit>,
63 /// A signal indicating that the [SyncSender] is busy transferring data to the second clock domain.
64 pub busy: Signal<Out, Bit>,
65 /// A protocol signal - raise this high for one cycle to latch [sig_in].
66 pub send: Signal<In, Bit>,
67 hold: DFF<T>,
68 state: DFF<SyncSenderState>,
69 sync: BitSynchronizer,
70}
71
72impl<T: Synth> Logic for SyncSender<T> {
73 #[hdl_gen]
74 fn update(&mut self) {
75 dff_setup!(self, clock, hold, state);
76 clock!(self, clock, sync);
77 // By default, the hold DFF does not change
78 self.sig_cross.next = self.hold.q.val();
79 self.flag_out.next = false.into();
80 self.sync.sig_in.next = self.ack_in.val();
81 // State machine
82 self.busy.next = true;
83 match self.state.q.val() {
84 SyncSenderState::Idle => {
85 self.busy.next = false.into();
86 if self.send.val() {
87 // Sample the input signal
88 self.hold.d.next = self.sig_in.val();
89 self.state.d.next = SyncSenderState::WaitAck.into();
90 // Indicate that the output is valid
91 self.flag_out.next = true;
92 }
93 }
94 SyncSenderState::WaitAck => {
95 self.flag_out.next = true;
96 if self.sync.sig_out.val() {
97 self.state.d.next = SyncSenderState::WaitDone.into();
98 self.flag_out.next = false.into();
99 }
100 }
101 SyncSenderState::WaitDone => {
102 if !self.sync.sig_out.val() {
103 self.hold.d.next = self.sig_in.val();
104 // Indicate that the output is valid
105 self.flag_out.next = true;
106 self.state.d.next = SyncSenderState::Idle.into();
107 }
108 }
109 _ => {
110 self.state.d.next = SyncSenderState::Idle;
111 }
112 }
113 }
114}
115
116#[test]
117fn sync_sender_is_synthesizable() {
118 let mut dev: SyncSender<Bits<8>> = Default::default();
119 dev.connect_all();
120 yosys_validate("sync_send", &generate_verilog(&dev)).unwrap();
121}
122
123#[derive(Copy, Clone, Debug, PartialEq, LogicState)]
124enum SyncReceiverState {
125 WaitSteady,
126 WaitDone,
127}
128
129/// A [SyncReceiver] works together with a [SyncSender] to transmit data from one clock domain
130/// to another (in one direction). To use a [SyncReceiver] wire up the [sig_cross], [flag_in]
131/// and [ack_out] signals between the two.
132#[derive(LogicBlock, Default)]
133pub struct SyncReceiver<T: Synth> {
134 /// The data output synchronized to the receiver's clock
135 pub sig_out: Signal<Out, T>,
136 /// The receivers clock signal. Data is synchronized to this clock.
137 pub clock: Signal<In, Clock>,
138 /// The wires used to send data from the [SyncSender] to the [SyncReceiver].
139 pub sig_cross: Signal<In, T>,
140 /// This is wired up to the [SyncSender::flag_out], and carries the new-data flag.
141 pub flag_in: Signal<In, Bit>,
142 /// This is wired up to the [SyncSender::ack_in], and carries the acknowledge flag.
143 pub ack_out: Signal<Out, Bit>,
144 /// This signal will strobe high for one clock when the output is valid and synchronized.
145 pub update: Signal<Out, Bit>,
146 hold: DFF<T>,
147 update_delay: DFF<Bit>,
148 state: DFF<SyncReceiverState>,
149 sync: BitSynchronizer,
150}
151
152impl<T: Synth> Logic for SyncReceiver<T> {
153 #[hdl_gen]
154 fn update(&mut self) {
155 dff_setup!(self, clock, hold, update_delay, state);
156 clock!(self, clock, sync);
157 self.sig_out.next = self.hold.q.val();
158 self.ack_out.next = false.into();
159 self.sync.sig_in.next = self.flag_in.val();
160 self.update.next = self.update_delay.q.val();
161 self.update_delay.d.next = false.into();
162 match self.state.q.val() {
163 SyncReceiverState::WaitSteady => {
164 if self.sync.sig_out.val() {
165 self.ack_out.next = true;
166 self.state.d.next = SyncReceiverState::WaitDone.into();
167 }
168 }
169 SyncReceiverState::WaitDone => {
170 if !self.sync.sig_out.val() {
171 self.ack_out.next = false.into();
172 self.update_delay.d.next = true;
173 self.hold.d.next = self.sig_cross.val().into();
174 self.state.d.next = SyncReceiverState::WaitSteady.into();
175 } else {
176 self.ack_out.next = true;
177 }
178 }
179 _ => {
180 self.state.d.next = SyncReceiverState::WaitSteady;
181 }
182 }
183 }
184}
185
186#[test]
187fn sync_receiver_is_synthesizable() {
188 let mut dev: SyncReceiver<Bits<8>> = Default::default();
189 dev.connect_all();
190 yosys_validate("sync_recv", &generate_verilog(&dev)).unwrap();
191}
192
193/// A [VectorSynchronizer] uses a [SyncSender] and [SyncReceiver] in a matched pair to
194/// transmit a vector of bits (or any [Synth] type from one clock domain to a second
195/// clock domain without metastability or data corruption. You can think of a [VectorSynchronizer]
196/// as a single-element asynchronous FIFO, and indeed [AsynchronousFIFO] uses the [VectorSynchronizer]
197/// internally.
198///
199/// Note that the [VectorSynchronizer] can be used to reflect a value/register into a
200/// second clock domain by tying `self.send.next = !self.busy.val()`. In that case, the output
201/// signal will be always attempting to follow the [sig_in] input as quickly as possible.
202#[derive(LogicBlock, Default)]
203pub struct VectorSynchronizer<T: Synth> {
204 /// The input clock interface. Input data is clocked in using this clock.
205 pub clock_in: Signal<In, Clock>,
206 /// The input data interface. Any synthesizable type can be used here. This is the data to send.
207 pub sig_in: Signal<In, T>,
208 /// The busy signal is asserted as long as the synchronizer is, well, synchronizing. You must
209 /// wait until this flag goes low before attempting to send more data. The [send] signal is
210 /// only valid when [busy] is low.
211 pub busy: Signal<Out, Bit>,
212 /// Raise the [send] signal for a single clock cycle to indicate that the current data on
213 /// [sig_in] should be sent across the synchronizer.
214 pub send: Signal<In, Bit>,
215 /// The clock to use on the output side of the [VectorSynchronizer]. This is the output clock.
216 pub clock_out: Signal<In, Clock>,
217 /// Data synchronized to the output clock [clock_out].
218 pub sig_out: Signal<Out, T>,
219 /// The update flag is strobed whenever a new valid output is available on [sig_out].
220 pub update: Signal<Out, Bit>,
221 sender: SyncSender<T>,
222 recv: SyncReceiver<T>,
223}
224
225impl<T: Synth> Logic for VectorSynchronizer<T> {
226 #[hdl_gen]
227 fn update(&mut self) {
228 clock!(self, clock_in, sender);
229 clock!(self, clock_out, recv);
230 // Wire the inputs..
231 self.sender.sig_in.next = self.sig_in.val();
232 self.busy.next = self.sender.busy.val();
233 self.sender.send.next = self.send.val();
234 // Wire the outputs..
235 self.sig_out.next = self.recv.sig_out.val();
236 self.update.next = self.recv.update.val();
237 // Cross wire the two parts
238 self.recv.sig_cross.next = self.sender.sig_cross.val();
239 self.recv.flag_in.next = self.sender.flag_out.val();
240 self.sender.ack_in.next = self.recv.ack_out.val();
241 }
242}
243
244#[test]
245fn test_vec_sync_synthesizable() {
246 let mut dev: VectorSynchronizer<Bits<8>> = Default::default();
247 dev.connect_all();
248 yosys_validate("vsync", &generate_verilog(&dev)).unwrap();
249}