diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-02-13 16:45:43 -0600 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-03 11:26:54 -0500 |
commit | 7f36cced81fb05d3cc107a0a0773fcdfc26f8d64 (patch) | |
tree | 44a1759c5432ab746e4d12177a541edc7b3c72be /fpga/usrp3/lib/control/gearbox_2x1.v | |
parent | f6f43baa0012a24ed5a8cdf49240e0cb4a1f7d04 (diff) | |
download | uhd-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.v | 180 |
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 |