aboutsummaryrefslogtreecommitdiffstats
path: root/fpga
diff options
context:
space:
mode:
authorMax Köhler <max.koehler@ni.com>2020-08-10 23:19:29 +0200
committerWade Fife <wade.fife@ettus.com>2020-08-13 07:46:14 -0500
commit2c2bb3a0b7269dcbd8b5933ce1c0c29ca17f599d (patch)
tree1682858e1035527085656e219c51335e5785f4a0 /fpga
parent31cafbb3ed9020acb82c3ab29645154f25e90172 (diff)
downloaduhd-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')
-rw-r--r--fpga/usrp3/lib/control/Makefile.srcs1
-rw-r--r--fpga/usrp3/lib/control/handshake.v82
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/ctrlport_clk_cross.v108
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