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}