aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/handshake.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control/handshake.v')
-rw-r--r--fpga/usrp3/lib/control/handshake.v82
1 files changed, 82 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/handshake.v b/fpga/usrp3/lib/control/handshake.v
new file mode 100644
index 000000000..47c05b7df
--- /dev/null
+++ b/fpga/usrp3/lib/control/handshake.v
@@ -0,0 +1,82 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2020 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: handshake
+// Description:
+// Implements clock domain crossing for data from one clock domain
+// to another independent of the relative clock frequencies or phases of
+// clk_a and clk_b.
+//
+// Once a handshake is triggered it cannot be aborted. A proper design needs to
+// apply reset to the target clock domain downstream module to make sure a
+// valid_b pulse does not cause any issues.
+//
+// Input Behavior:
+// ┌┐┌┐┌┐┌┐┋ ┋┌┐┌┐┌┐┌┐┌┐┋ ┋┌┐┌┐┌┐┌┐┌┐┌┐┌┐
+// clk_a ┘└┘└┘└┘└┋ ┋┘└┘└┘└┘└┘└┋ ┋┘└┘└┘└┘└┘└┘└┘└
+// ┌─┐ ┋ ┋ ┌─┐ ┋ ┋
+// valid_a ──┘ └───┋ ┋────┘ └───┋ ┋──────────────
+// ▄▄┬─┬▄▄▄┋ ┋▄▄▄▄┬─┬▄▄▄┋ ┋▄▄▄▄▄▄▄▄▄▄▄▄▄▄
+// data_a ▀▀┴─┴▀▀▀┋ ┋▀▀▀▀┴─┴▀▀▀┋ ┋▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+// ┌───┋ ┋────┐ ┌───┋ ┋────────┐
+// busy_a ────┘ ┋ ┋ └─┘ ┋ ┋ └─────
+//
+//
+// Output Behavior:
+// ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┋ ┋ ┌─┐ ┌─┐ ┌─┐ ┌─┐
+// clk_b ─┘ └─┘ └─┘ └─┘ └─┋ ┋─┘ └─┘ └─┘ └─┘ └─
+// ┌───┐ ┋ ┋ ┌───┐
+// valid_b ─────────┘ └───┋ ┋─────┘ └───────
+// ▄▄▄▄▄▄▄▄▄┬───┬▄▄▄┋ ┋▄▄▄▄▄┬───┬▄▄▄▄▄▄▄
+// data_b ▀▀▀▀▀▀▀▀▀┴───┴▀▀▀┋ ┋▀▀▀▀▀┴───┴▀▀▀▀▀▀▀
+///////////////////////////////////////////////////////////////////////////////
+
+module handshake #(
+ parameter WIDTH = 32 // data width
+) (
+ // source clock domain
+ input wire clk_a,
+ input wire rst_a,
+ input wire valid_a, // trigger handshake on rising edge
+ input wire [WIDTH-1:0] data_a,
+ output wire busy_a,
+
+ // target clock domain
+ input wire clk_b,
+ output wire valid_b,
+ output wire [WIDTH-1:0] data_b
+);
+
+ // Handling of the handshaking between the two clock domains. The reset does
+ // not delete a pulse, which is already triggered!
+ pulse_synchronizer #(
+ .MODE("PULSE"), .STAGES(4)
+ ) push_sync_inst (
+ .clk_a(clk_a), .rst_a(rst_a), .pulse_a(valid_a), .busy_a(busy_a),
+ .clk_b(clk_b), .pulse_b(valid_b)
+ );
+
+ // Capture the data aligned with triggering the handshake.
+ reg [WIDTH-1:0] data_a_lcl;
+ always @(posedge clk_a) begin
+ if (valid_a & ~busy_a) begin
+ data_a_lcl <= data_a;
+ end
+ end
+
+ // Transfer data with timing exception. Data is captured upfront and kept
+ // stable in clk_a domain. As there are more synchronizer stages in the
+ // pulse_synchronizer the data in these 2 stage synchronizer is stable once
+ // the valid_b pulse arrives in clk_b domain. 2 stages are used to resolve
+ // meta-stability. Reset is not needed on the data path as it is controlled by
+ // the valid signals.
+ synchronizer #(
+ .WIDTH(WIDTH), .STAGES(2), .INITIAL_VAL({WIDTH {1'b0}}), .FALSE_PATH_TO_IN(1)
+ ) data_sync_inst (
+ .clk(clk_b), .rst(1'b0), .in(data_a_lcl), .out(data_b)
+ );
+
+endmodule