1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
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<In, Bit>,
/// The output signal, synchronized to the clock
pub sig_out: Signal<Out, Bit>,
/// The clock signal to synchronize the output to
pub clock: Signal<In, Clock>,
dff0: DFF<Bit>,
dff1: DFF<Bit>,
}
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<T: Synth> {
/// The input signal to synchronize across clock domains
pub sig_in: Signal<In, T>,
/// The input signals are assumed to be synchronous to this clock
pub clock: Signal<In, Clock>,
/// These are the wires used to send signals to the [SyncReceiver].
pub sig_cross: Signal<Out, T>,
/// A protocol flag signal indicating that data is ready to be transferred to the second clock doamin.
pub flag_out: Signal<Out, Bit>,
/// A protocol flag signal indicating that the data has been transferred to the second clock domain.
pub ack_in: Signal<In, Bit>,
/// A signal indicating that the [SyncSender] is busy transferring data to the second clock domain.
pub busy: Signal<Out, Bit>,
/// A protocol signal - raise this high for one cycle to latch [sig_in].
pub send: Signal<In, Bit>,
hold: DFF<T>,
state: DFF<SyncSenderState>,
sync: BitSynchronizer,
}
impl<T: Synth> Logic for SyncSender<T> {
#[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<Bits<8>> = 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<T: Synth> {
/// The data output synchronized to the receiver's clock
pub sig_out: Signal<Out, T>,
/// The receivers clock signal. Data is synchronized to this clock.
pub clock: Signal<In, Clock>,
/// The wires used to send data from the [SyncSender] to the [SyncReceiver].
pub sig_cross: Signal<In, T>,
/// This is wired up to the [SyncSender::flag_out], and carries the new-data flag.
pub flag_in: Signal<In, Bit>,
/// This is wired up to the [SyncSender::ack_in], and carries the acknowledge flag.
pub ack_out: Signal<Out, Bit>,
/// This signal will strobe high for one clock when the output is valid and synchronized.
pub update: Signal<Out, Bit>,
hold: DFF<T>,
update_delay: DFF<Bit>,
state: DFF<SyncReceiverState>,
sync: BitSynchronizer,
}
impl<T: Synth> Logic for SyncReceiver<T> {
#[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<Bits<8>> = 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<T: Synth> {
/// The input clock interface. Input data is clocked in using this clock.
pub clock_in: Signal<In, Clock>,
/// The input data interface. Any synthesizable type can be used here. This is the data to send.
pub sig_in: Signal<In, T>,
/// 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<Out, Bit>,
/// 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<In, Bit>,
/// The clock to use on the output side of the [VectorSynchronizer]. This is the output clock.
pub clock_out: Signal<In, Clock>,
/// Data synchronized to the output clock [clock_out].
pub sig_out: Signal<Out, T>,
/// The update flag is strobed whenever a new valid output is available on [sig_out].
pub update: Signal<Out, Bit>,
sender: SyncSender<T>,
recv: SyncReceiver<T>,
}
impl<T: Synth> Logic for VectorSynchronizer<T> {
#[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<Bits<8>> = Default::default();
dev.connect_all();
yosys_validate("vsync", &generate_verilog(&dev)).unwrap();
}