diff options
| author | Max Köhler <max.koehler@ni.com> | 2020-08-10 23:19:29 +0200 | 
|---|---|---|
| committer | Wade Fife <wade.fife@ettus.com> | 2020-08-13 07:46:14 -0500 | 
| commit | 2c2bb3a0b7269dcbd8b5933ce1c0c29ca17f599d (patch) | |
| tree | 1682858e1035527085656e219c51335e5785f4a0 /fpga/usrp3/lib | |
| parent | 31cafbb3ed9020acb82c3ab29645154f25e90172 (diff) | |
| download | uhd-2c2bb3a0b7269dcbd8b5933ce1c0c29ca17f599d.tar.gz uhd-2c2bb3a0b7269dcbd8b5933ce1c0c29ca17f599d.tar.bz2 uhd-2c2bb3a0b7269dcbd8b5933ce1c0c29ca17f599d.zip | |
fpga: lib: add handshake to replace FIFO for ctrlport CDC
The clock crossing of the ctrlport used FIFOs to transfer requests and responses
between clock domains. This commit adds a handshake based on the pulse
synchronizer to reduce the resource usage for ctrlport clock domain crossing.
Data is stored in a single register while the pulse synchronizer handles the
signaling of valid flags.
Diffstat (limited to 'fpga/usrp3/lib')
| -rw-r--r-- | fpga/usrp3/lib/control/Makefile.srcs | 1 | ||||
| -rw-r--r-- | fpga/usrp3/lib/control/handshake.v | 82 | ||||
| -rw-r--r-- | fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v | 108 | 
3 files changed, 143 insertions, 48 deletions
| diff --git a/fpga/usrp3/lib/control/Makefile.srcs b/fpga/usrp3/lib/control/Makefile.srcs index 21f986a50..74ba3b48a 100644 --- a/fpga/usrp3/lib/control/Makefile.srcs +++ b/fpga/usrp3/lib/control/Makefile.srcs @@ -59,4 +59,5 @@ map/cam.v \  map/kv_map.v \  map/axis_muxed_kv_map.v \  axil_ctrlport_master.v\ +handshake.v\  )) 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 diff --git a/fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v b/fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v index 6cd45b86b..3747481ed 100644 --- a/fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v +++ b/fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v @@ -11,9 +11,7 @@  // -module ctrlport_clk_cross #( -  parameter DEVICE = "7SERIES" // FPGA technology identifier (for optimal FIFO inference) -)( +module ctrlport_clk_cross (    input wire rst, // Can be either clock domain, but must be glitch-free    //--------------------------------------------------------------------------- @@ -54,6 +52,16 @@ module ctrlport_clk_cross #(    input  wire [ 1:0] m_ctrlport_resp_status,    input  wire [31:0] m_ctrlport_resp_data  ); +  //--------------------------------------------------------------------------- +  // Reset sync to both clock domains +  //--------------------------------------------------------------------------- +  wire m_rst, s_rst; +  reset_sync master_reset_sync_inst ( +    .clk(m_ctrlport_clk), .reset_in(rst), .reset_out(m_rst) +  ); +  reset_sync slave_reset_sync_inst ( +    .clk(s_ctrlport_clk), .reset_in(rst), .reset_out(s_rst) +  );    //---------------------------------------------------------------------------    // Slave to Master Clock Crossing (Request) @@ -77,52 +85,53 @@ module ctrlport_clk_cross #(    wire              m_ctrlport_req_wr_tmp;    wire              m_ctrlport_req_rd_tmp; -  // Sort by descreasing order of usage possibility. -  // This way instance, which do not need port IDs, time etc. can save the MSBs.    assign s_req_flat = { +    s_ctrlport_req_wr, +    s_ctrlport_req_rd, +    s_ctrlport_req_addr,      s_ctrlport_req_portid,      s_ctrlport_req_rem_epid,      s_ctrlport_req_rem_portid, -    s_ctrlport_req_has_time, -    s_ctrlport_req_time, -    s_ctrlport_req_byte_en,      s_ctrlport_req_data, -    s_ctrlport_req_addr, -    s_ctrlport_req_wr, -    s_ctrlport_req_rd +    s_ctrlport_req_byte_en, +    s_ctrlport_req_has_time, +    s_ctrlport_req_time    }; -  axi_fifo_2clk #( -    .WIDTH  (REQ_W), -    .SIZE   (3), -    .DEVICE (DEVICE) -  ) req_fifo ( -    .reset    (rst), -    .i_aclk   (s_ctrlport_clk), -    .i_tdata  (s_req_flat), -    .i_tvalid (s_ctrlport_req_wr | s_ctrlport_req_rd), -    .i_tready (), -    .o_aclk   (m_ctrlport_clk), -    .o_tdata  (m_req_flat), -    .o_tready (1'b1), -    .o_tvalid (m_req_flat_valid) +  // Busy flag can be ignored as the response handshake takes at least the same +  // amount of cycles to transfer the response as this handshake instance needs +  // to release the busy flag as they are configured with the same amount of +  // synchronization stages. Furthermore the ctrlport protocol just allows for +  // one transaction to be active at the same time. A request can only be issued +  // once the response is provided. +  handshake #( +    .WIDTH(REQ_W) +  ) req_handshake_inst ( +    .clk_a(s_ctrlport_clk), +    .rst_a(s_rst), +    .valid_a((s_ctrlport_req_wr | s_ctrlport_req_rd) & ~s_rst), +    .data_a(s_req_flat), +    .busy_a(), +    .clk_b(m_ctrlport_clk), +    .valid_b(m_req_flat_valid), +    .data_b(m_req_flat)    );    assign { +    m_ctrlport_req_wr_tmp, +    m_ctrlport_req_rd_tmp, +    m_ctrlport_req_addr,      m_ctrlport_req_portid,      m_ctrlport_req_rem_epid,      m_ctrlport_req_rem_portid, -    m_ctrlport_req_has_time, -    m_ctrlport_req_time, -    m_ctrlport_req_byte_en,      m_ctrlport_req_data, -    m_ctrlport_req_addr, -    m_ctrlport_req_wr_tmp, -    m_ctrlport_req_rd_tmp +    m_ctrlport_req_byte_en, +    m_ctrlport_req_has_time, +    m_ctrlport_req_time    } = m_req_flat; -  assign m_ctrlport_req_wr = m_ctrlport_req_wr_tmp & m_req_flat_valid; -  assign m_ctrlport_req_rd = m_ctrlport_req_rd_tmp & m_req_flat_valid; +  assign m_ctrlport_req_wr = m_ctrlport_req_wr_tmp & m_req_flat_valid & ~m_rst; +  assign m_ctrlport_req_rd = m_ctrlport_req_rd_tmp & m_req_flat_valid & ~m_rst;    //--------------------------------------------------------------------------- @@ -145,20 +154,23 @@ module ctrlport_clk_cross #(      m_ctrlport_resp_data    }; -  axi_fifo_2clk #( -    .WIDTH  (RESP_W), -    .SIZE   (3), -    .DEVICE (DEVICE) -  ) resp_fifo ( -    .reset    (rst), -    .i_aclk   (m_ctrlport_clk), -    .i_tdata  (m_resp_flat), -    .i_tvalid (m_ctrlport_resp_ack), -    .i_tready (), -    .o_aclk   (s_ctrlport_clk), -    .o_tdata  (s_resp_flat), -    .o_tready (1'b1), -    .o_tvalid (s_resp_flat_valid) +  // Busy flag can be ignored as the request handshake takes at least the same +  // amount of cycles to transfer the request as this handshake instance needs +  // to release the busy flag as they are configured with the same amount of +  // synchronization stages. Furthermore the ctrlport protocol just allows for +  // one transaction to be active at the same time. A response can only be +  // issued once the request is available. +  handshake #( +    .WIDTH(RESP_W) +  ) resp_handshake_inst ( +    .clk_a(m_ctrlport_clk), +    .rst_a(m_rst), +    .valid_a(m_ctrlport_resp_ack & ~m_rst), +    .data_a(m_resp_flat), +    .busy_a(), +    .clk_b(s_ctrlport_clk), +    .valid_b(s_resp_flat_valid), +    .data_b(s_resp_flat)    );    assign { @@ -167,6 +179,6 @@ module ctrlport_clk_cross #(      s_ctrlport_resp_data    } = s_resp_flat; -  assign s_ctrlport_resp_ack = s_ctrlport_resp_ack_tmp & s_resp_flat_valid; +  assign s_ctrlport_resp_ack = s_ctrlport_resp_ack_tmp & s_resp_flat_valid & ~s_rst;  endmodule | 
