aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/gearbox_2x1.v
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-02-13 16:45:43 -0600
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-03 11:26:54 -0500
commit7f36cced81fb05d3cc107a0a0773fcdfc26f8d64 (patch)
tree44a1759c5432ab746e4d12177a541edc7b3c72be /fpga/usrp3/lib/control/gearbox_2x1.v
parentf6f43baa0012a24ed5a8cdf49240e0cb4a1f7d04 (diff)
downloaduhd-7f36cced81fb05d3cc107a0a0773fcdfc26f8d64.tar.gz
uhd-7f36cced81fb05d3cc107a0a0773fcdfc26f8d64.tar.bz2
uhd-7f36cced81fb05d3cc107a0a0773fcdfc26f8d64.zip
fpga: lib: Add 2 to 1 gearbox module
Diffstat (limited to 'fpga/usrp3/lib/control/gearbox_2x1.v')
-rw-r--r--fpga/usrp3/lib/control/gearbox_2x1.v180
1 files changed, 180 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/gearbox_2x1.v b/fpga/usrp3/lib/control/gearbox_2x1.v
new file mode 100644
index 000000000..1b3eccc9a
--- /dev/null
+++ b/fpga/usrp3/lib/control/gearbox_2x1.v
@@ -0,0 +1,180 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: gearbox_2x1
+//
+// Description:
+//
+// Moves data between two clock domains at a constant data rate. This module
+// requires that the clocks be related and that there is a 2:1 ratio between
+// the two clocks. Static timing analysis is assumed for the clock domain
+// crossing. The module supports going from a slow clock to a fast clock (2
+// words per 1x clock cycle to 1 word per 2x clock cycle) or from a fast
+// clock to a slow clock (1 word per 1x clock cycle to 2 words per 2x clock
+// cycles) depending on the parameters provided.
+//
+// Note that there are no tready signals, so downstream logic must always be
+// ready.
+//
+// Parameters:
+//
+// WORD_W : Bits per word
+// IN_WORDS : Number of input words per clock cycle
+// OUT_WORDS : Number of output words per clock cycle
+// BIG_ENDIAN : Order in which to input/output words when multiple words per
+// clock cycle are needed. Little endian means the first word is
+// in the least-significant position. Big endian means the first
+// word is in the most-significant position.
+//
+
+
+module gearbox_2x1 #(
+ parameter WORD_W = 8,
+ parameter IN_WORDS = 2,
+ parameter OUT_WORDS = 1,
+ parameter BIG_ENDIAN = 0
+) (
+ input wire i_clk,
+ input wire i_rst,
+ input wire [IN_WORDS*WORD_W-1:0] i_tdata,
+ input wire i_tvalid,
+
+ input wire o_clk,
+ input wire o_rst,
+ output reg [OUT_WORDS*WORD_W-1:0] o_tdata,
+ output reg o_tvalid = 1'b0
+);
+
+ localparam IN_W = WORD_W * IN_WORDS;
+ localparam OUT_W = WORD_W * OUT_WORDS;
+
+ generate
+
+ // Make sure the ratios are supported
+ if (IN_WORDS != 2*OUT_WORDS && OUT_WORDS != 2*IN_WORDS) begin : gen_ERROR
+ IN_WORDS_and_OUT_WORDS_must_have_a_2_to_1_ratio();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // 2 words to 1 word (slow clock to fast clock)
+ //-------------------------------------------------------------------------
+
+ if (IN_WORDS > OUT_WORDS) begin : gen_slow_to_fast
+ reg [IN_W-1:0] i_tdata_reg;
+ reg i_tvalid_reg;
+ reg i_toggle = 1'b0;
+
+ always @(posedge i_clk) begin
+ if (i_rst) begin
+ i_tdata_reg <= 'bX;
+ i_tvalid_reg <= 1'b0;
+ i_toggle <= 1'b0;
+ end else begin
+ i_tdata_reg <= i_tdata;
+ i_tvalid_reg <= i_tvalid;
+ if (i_tvalid) begin
+ i_toggle <= ~i_toggle;
+ end
+ end
+ end
+
+ reg [IN_W-1:0] o_tdata_reg;
+ reg o_tvalid_reg = 1'b0;
+ reg o_toggle;
+ reg o_toggle_dly;
+ reg o_data_sel;
+
+ always @(posedge o_clk) begin
+ if (o_rst) begin
+ o_tdata_reg <= 'bX;
+ o_tvalid <= 1'b0;
+ o_tvalid_reg <= 1'b0;
+ o_toggle <= 1'bX;
+ o_toggle_dly <= 1'bX;
+ o_data_sel <= 1'bX;
+ end else begin
+ // Clock crossing
+ o_tvalid_reg <= i_tvalid_reg;
+ o_toggle <= i_toggle;
+ o_tdata_reg <= i_tdata_reg;
+
+ // Determine which output to select
+ o_toggle_dly <= o_toggle;
+ o_data_sel <= BIG_ENDIAN ^ (o_toggle == o_toggle_dly);
+
+ // Select the correct output for this clock cycle
+ o_tvalid <= o_tvalid_reg;
+ o_tdata <= o_data_sel ?
+ o_tdata_reg[0 +: OUT_W] : o_tdata_reg[IN_W/2 +: OUT_W];
+ end
+ end
+
+
+ //-------------------------------------------------------------------------
+ // 1 word to 2 words (fast clock to slow clock)
+ //-------------------------------------------------------------------------
+
+ end else begin : gen_fast_to_slow
+ reg [IN_W-1:0] i_gear_reg;
+ reg [OUT_W-1:0] i_gear_tdata;
+ reg i_gear_one_word = 1'b0;
+ reg i_gear_tvalid = 1'b0;
+ reg i_gear_tvalid_dly = 1'b0;
+
+ always @(posedge i_clk) begin
+ if (i_rst) begin
+ i_gear_reg <= 'bX;
+ i_gear_tdata <= 'bX;
+ i_gear_one_word <= 1'b0;
+ i_gear_tvalid <= 1'b0;
+ i_gear_tvalid_dly <= 1'b0;
+ end else begin
+ // Default assignments
+ i_gear_tvalid <= 1'b0;
+ i_gear_tvalid_dly <= i_gear_tvalid;
+
+ if (i_tvalid) begin
+ // Track if the gearbox has one word saved
+ i_gear_one_word <= ~i_gear_one_word;
+ i_gear_reg <= i_tdata;
+ if (i_gear_one_word) begin
+ // This is the second word, so output the new word on i_gear_reg_t*
+ i_gear_tdata <= BIG_ENDIAN ?
+ { i_gear_reg, i_tdata } : { i_tdata, i_gear_reg };
+ i_gear_tvalid <= 1'b1;
+ end
+ end
+ end
+ end
+
+ reg [OUT_W-1:0] o_gear_tdata;
+ reg o_gear_tvalid = 1'b0;
+ reg o_gear_tvalid_dly = 1'b0;
+ reg o_tvalid_reg = 1'b0;
+
+ always @(posedge o_clk) begin
+ if (o_rst) begin
+ o_gear_tvalid <= 1'b0;
+ o_gear_tvalid_dly <= 1'b0;
+ o_gear_tdata <= 'bX;
+ o_tvalid <= 1'b0;
+ o_tdata <= 'bX;
+ end else begin
+ // Clock crossing
+ o_gear_tvalid <= i_gear_tvalid;
+ o_gear_tvalid_dly <= i_gear_tvalid_dly;
+ o_gear_tdata <= i_gear_tdata;
+
+ // Control tvalid
+ o_tvalid <= o_gear_tvalid | o_gear_tvalid_dly;
+ o_tdata <= o_gear_tdata;
+ end
+ end
+ end
+
+ endgenerate
+
+endmodule