use rust_hdl_core::prelude::*; use crate::{dff::DFF, dff_setup}; /// A [BitSynchronizer] is used to move signals that are asynchronous to a clock into that /// clock domain using a pair of back-to-back flip-flops. While the first flip flop may /// become metastable, the second one is likely to be stable. #[derive(LogicBlock, Default)] pub struct BitSynchronizer { /// The input signal, which is asynchronous to the clock pub sig_in: Signal, /// The output signal, synchronized to the clock pub sig_out: Signal, /// The clock signal to synchronize the output to pub clock: Signal, dff0: DFF, dff1: DFF, } impl Logic for BitSynchronizer { #[hdl_gen] fn update(&mut self) { dff_setup!(self, clock, dff0, dff1); self.dff0.d.next = self.sig_in.val(); self.dff1.d.next = self.dff0.q.val(); self.sig_out.next = self.dff1.q.val(); } } #[test] fn sync_is_synthesizable() { let mut dev: BitSynchronizer = Default::default(); dev.connect_all(); yosys_validate("sync", &generate_verilog(&dev)).unwrap(); } #[derive(Copy, Clone, Debug, PartialEq, LogicState)] enum SyncSenderState { Idle, WaitAck, WaitDone, } /// When you need to send many bits between two clock domains, it is risky to use a vector /// of [BitSynchronizer] structs. That is because, you cannot guarantee at any given moment /// that all of the bits of your multi-bit signal will cross into the new clock domain at once. /// So to synchronize a multi-bit signal, use a [SyncSender] and [SyncReceiver] pair. These /// widgets will use a set of handshake signals to move a value from one clock domain to another /// safely. Note that while the state machine is executing, the synchronizer will indicate it /// is busy. Crossing clock domains with greater ease is best done with an [AsynchronousFIFO]. #[derive(LogicBlock, Default)] pub struct SyncSender { /// The input signal to synchronize across clock domains pub sig_in: Signal, /// The input signals are assumed to be synchronous to this clock pub clock: Signal, /// These are the wires used to send signals to the [SyncReceiver]. pub sig_cross: Signal, /// A protocol flag signal indicating that data is ready to be transferred to the second clock doamin. pub flag_out: Signal, /// A protocol flag signal indicating that the data has been transferred to the second clock domain. pub ack_in: Signal, /// A signal indicating that the [SyncSender] is busy transferring data to the second clock domain. pub busy: Signal, /// A protocol signal - raise this high for one cycle to latch [sig_in]. pub send: Signal, hold: DFF, state: DFF, sync: BitSynchronizer, } impl Logic for SyncSender { #[hdl_gen] fn update(&mut self) { dff_setup!(self, clock, hold, state); clock!(self, clock, sync); // By default, the hold DFF does not change self.sig_cross.next = self.hold.q.val(); self.flag_out.next = false.into(); self.sync.sig_in.next = self.ack_in.val(); // State machine self.busy.next = true; match self.state.q.val() { SyncSenderState::Idle => { self.busy.next = false.into(); if self.send.val() { // Sample the input signal self.hold.d.next = self.sig_in.val(); self.state.d.next = SyncSenderState::WaitAck.into(); // Indicate that the output is valid self.flag_out.next = true; } } SyncSenderState::WaitAck => { self.flag_out.next = true; if self.sync.sig_out.val() { self.state.d.next = SyncSenderState::WaitDone.into(); self.flag_out.next = false.into(); } } SyncSenderState::WaitDone => { if !self.sync.sig_out.val() { self.hold.d.next = self.sig_in.val(); // Indicate that the output is valid self.flag_out.next = true; self.state.d.next = SyncSenderState::Idle.into(); } } _ => { self.state.d.next = SyncSenderState::Idle; } } } } #[test] fn sync_sender_is_synthesizable() { let mut dev: SyncSender> = Default::default(); dev.connect_all(); yosys_validate("sync_send", &generate_verilog(&dev)).unwrap(); } #[derive(Copy, Clone, Debug, PartialEq, LogicState)] enum SyncReceiverState { WaitSteady, WaitDone, } /// A [SyncReceiver] works together with a [SyncSender] to transmit data from one clock domain /// to another (in one direction). To use a [SyncReceiver] wire up the [sig_cross], [flag_in] /// and [ack_out] signals between the two. #[derive(LogicBlock, Default)] pub struct SyncReceiver { /// The data output synchronized to the receiver's clock pub sig_out: Signal, /// The receivers clock signal. Data is synchronized to this clock. pub clock: Signal, /// The wires used to send data from the [SyncSender] to the [SyncReceiver]. pub sig_cross: Signal, /// This is wired up to the [SyncSender::flag_out], and carries the new-data flag. pub flag_in: Signal, /// This is wired up to the [SyncSender::ack_in], and carries the acknowledge flag. pub ack_out: Signal, /// This signal will strobe high for one clock when the output is valid and synchronized. pub update: Signal, hold: DFF, update_delay: DFF, state: DFF, sync: BitSynchronizer, } impl Logic for SyncReceiver { #[hdl_gen] fn update(&mut self) { dff_setup!(self, clock, hold, update_delay, state); clock!(self, clock, sync); self.sig_out.next = self.hold.q.val(); self.ack_out.next = false.into(); self.sync.sig_in.next = self.flag_in.val(); self.update.next = self.update_delay.q.val(); self.update_delay.d.next = false.into(); match self.state.q.val() { SyncReceiverState::WaitSteady => { if self.sync.sig_out.val() { self.ack_out.next = true; self.state.d.next = SyncReceiverState::WaitDone.into(); } } SyncReceiverState::WaitDone => { if !self.sync.sig_out.val() { self.ack_out.next = false.into(); self.update_delay.d.next = true; self.hold.d.next = self.sig_cross.val().into(); self.state.d.next = SyncReceiverState::WaitSteady.into(); } else { self.ack_out.next = true; } } _ => { self.state.d.next = SyncReceiverState::WaitSteady; } } } } #[test] fn sync_receiver_is_synthesizable() { let mut dev: SyncReceiver> = Default::default(); dev.connect_all(); yosys_validate("sync_recv", &generate_verilog(&dev)).unwrap(); } /// A [VectorSynchronizer] uses a [SyncSender] and [SyncReceiver] in a matched pair to /// transmit a vector of bits (or any [Synth] type from one clock domain to a second /// clock domain without metastability or data corruption. You can think of a [VectorSynchronizer] /// as a single-element asynchronous FIFO, and indeed [AsynchronousFIFO] uses the [VectorSynchronizer] /// internally. /// /// Note that the [VectorSynchronizer] can be used to reflect a value/register into a /// second clock domain by tying `self.send.next = !self.busy.val()`. In that case, the output /// signal will be always attempting to follow the [sig_in] input as quickly as possible. #[derive(LogicBlock, Default)] pub struct VectorSynchronizer { /// The input clock interface. Input data is clocked in using this clock. pub clock_in: Signal, /// The input data interface. Any synthesizable type can be used here. This is the data to send. pub sig_in: Signal, /// The busy signal is asserted as long as the synchronizer is, well, synchronizing. You must /// wait until this flag goes low before attempting to send more data. The [send] signal is /// only valid when [busy] is low. pub busy: Signal, /// Raise the [send] signal for a single clock cycle to indicate that the current data on /// [sig_in] should be sent across the synchronizer. pub send: Signal, /// The clock to use on the output side of the [VectorSynchronizer]. This is the output clock. pub clock_out: Signal, /// Data synchronized to the output clock [clock_out]. pub sig_out: Signal, /// The update flag is strobed whenever a new valid output is available on [sig_out]. pub update: Signal, sender: SyncSender, recv: SyncReceiver, } impl Logic for VectorSynchronizer { #[hdl_gen] fn update(&mut self) { clock!(self, clock_in, sender); clock!(self, clock_out, recv); // Wire the inputs.. self.sender.sig_in.next = self.sig_in.val(); self.busy.next = self.sender.busy.val(); self.sender.send.next = self.send.val(); // Wire the outputs.. self.sig_out.next = self.recv.sig_out.val(); self.update.next = self.recv.update.val(); // Cross wire the two parts self.recv.sig_cross.next = self.sender.sig_cross.val(); self.recv.flag_in.next = self.sender.flag_out.val(); self.sender.ack_in.next = self.recv.ack_out.val(); } } #[test] fn test_vec_sync_synthesizable() { let mut dev: VectorSynchronizer> = Default::default(); dev.connect_all(); yosys_validate("vsync", &generate_verilog(&dev)).unwrap(); }