aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v')
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v247
1 files changed, 247 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v b/fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v
new file mode 100644
index 000000000..7e74b1422
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/utils/ctrlport_reg_rw.v
@@ -0,0 +1,247 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: ctrlport_reg_rw
+//
+// Description:
+//
+// Implements a read/write register on a CTRL Port bus. CTRL Port byte
+// enables are supported on writes. All input addresses are assumed to be
+// 32-bit word aligned.
+//
+// The width of the register is configurable. The register will take up the
+// full power-of-2 address region, with a minimum of a 4-byte region. For
+// example:
+//
+// WIDTH (Bits) │ Address Space (Bytes)
+// ──────────────┼───────────────────────
+// 1 to 32 │ 4
+// 33 to 64 │ 8
+// 64 to 128 │ 16
+// etc. │ etc.
+//
+// When COHERENCY is true and the WIDTH is larger than a single CTRL Port
+// word (32 bits), writing the least-significant words of the register causes
+// them to be saved in a cache register and does not immediately update those
+// words in the register. Writing the most-significant word of the register
+// causes all the words to be simultaneously written to the register. This
+// allows writes of large, multi-word registers to be coherent. This is very
+// important for registers in which there is a relationship between the upper
+// and lower bits, such as in a counter value in which changing only part of
+// the word at a time could be seen as a large change when in fact the final
+// change is small. The most-significant word MUST always be written last
+// when COHERENCY is true.
+//
+// Parameters:
+//
+// ADDR : Byte address to use for this register. This address must be
+// aligned to the size of the register.
+// WIDTH : Width of register to implement in bits. This determines the
+// width of the "value_out" input and the amount of address space
+// used by the register, which is always a power of 2.
+// COHERENT : Setting to 1 implements additional logic so that register
+// writes maintain coherency. Setting to 0 removes this logic, so
+// that each 32-bit word of the register is treated independently.
+// RESET_VAL : Value to give the register at power-on and at reset.
+//
+// Ports:
+//
+// *ctrlport* : CTRL Port interface.
+// value_out : The current value of the register.
+// written : A strobe (single-cycle pulse) that indicates when the
+// register was written. The new value may or may not be the
+// same as the old value.
+//
+
+
+module ctrlport_reg_rw #(
+ parameter [ 19:0] ADDR = 0,
+ parameter WIDTH = 32,
+ parameter COHERENT = 0,
+ parameter [WIDTH-1:0] RESET_VAL = 'h0
+) (
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ output wire [WIDTH-1:0] value_out,
+ output reg written
+);
+
+ //---------------------------------------------------------------------------
+ // Functions
+ //---------------------------------------------------------------------------
+
+ function automatic integer max(input integer a, b);
+ max = a > b ? a : b;
+ endfunction
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Calculate the number of bytes of address space this register will take up.
+ // The minimum size is a 32-bit register (4 bytes).
+ localparam NUM_BYTES = max(4, 2**$clog2(WIDTH)/8);
+
+ // Calculate the number of bits needed to index each byte of this register.
+ localparam BYTE_ADDR_W = $clog2(NUM_BYTES);
+
+ // Calculate the number of bits needed to index each 32-bit word of this
+ // register.
+ localparam WORD_ADDR_W = BYTE_ADDR_W-2;
+
+
+ //---------------------------------------------------------------------------
+ // Parameter Checking
+ //---------------------------------------------------------------------------
+
+ // Make sure WIDTH is valid
+ if (WIDTH < 1) begin
+ WIDTH_must_be_at_least_1();
+ end
+
+ // Make sure the address is word-aligned to the size of the register
+ if (ADDR[BYTE_ADDR_W-1:0] != 0) begin
+ ADDR_must_be_aligned_to_the_size_of_the_register();
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Write Logic
+ //---------------------------------------------------------------------------
+
+ // Use full size to simplify indexing. Unused bits will be optimized away.
+ reg [8*NUM_BYTES-1:0] reg_val = 0;
+
+ reg [8*NUM_BYTES-1:0] write_cache_reg;
+ reg [ NUM_BYTES-1:0] write_en_cache_reg;
+
+ reg s_ctrlport_resp_ack_wr;
+
+ integer b, w;
+
+ //
+ // Coherent implementation
+ //
+ if (WIDTH > 32 && COHERENT) begin : gen_coherent
+ always @(posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ reg_val <= RESET_VAL;
+ written <= 1'b0;
+ end else begin
+ // Check if any part of this register is being written to
+ if (s_ctrlport_req_addr[19 : BYTE_ADDR_W] == ADDR[19 : BYTE_ADDR_W] && s_ctrlport_req_wr) begin
+ s_ctrlport_resp_ack_wr <= 1'b1;
+
+ // Check if we're writing the most-significant word
+ if (s_ctrlport_req_addr[BYTE_ADDR_W-1 : 2] == {BYTE_ADDR_W-2{1'b1}}) begin
+ written <= 1'b1;
+
+ // Iterate over the 4 bytes, updating each based on byte_en
+ for (b = 0; b < 4; b = b+1) begin
+ // Update the most-significant word from the input
+ if(s_ctrlport_req_byte_en[b]) begin
+ reg_val[32*(NUM_BYTES/4-1)+b*8 +: 8] <= s_ctrlport_req_data[8*b +: 8];
+ end
+
+ // Update the least-significant words from the cache
+ for (w = 0; w < NUM_BYTES/4; w = w+1) begin
+ if (write_en_cache_reg[b]) begin
+ reg_val[32*w+b*8 +: 8] <= write_cache_reg[32*w+b*8 +: 8];
+ end
+ end
+ end
+
+ // We're writing one of the least-significant words, so just cache
+ // the values written.
+ end else begin
+ w = s_ctrlport_req_addr[2 +: WORD_ADDR_W];
+ write_cache_reg[w*32 +: 32] <= s_ctrlport_req_data;
+ write_en_cache_reg[w*4 +: 4] <= s_ctrlport_req_byte_en;
+ end
+
+ end else begin
+ s_ctrlport_resp_ack_wr <= 1'b0;
+ written <= 1'b0;
+ end
+ end
+ end
+
+ //
+ // Non-coherent implementation
+ //
+ end else begin : gen_no_coherent
+ always @(posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ reg_val <= RESET_VAL;
+ written <= 1'b0;
+ end else begin
+ // Check if any part of the word is begin written to
+ if (s_ctrlport_req_addr[19 : BYTE_ADDR_W] == ADDR[19 : BYTE_ADDR_W] && s_ctrlport_req_wr) begin
+ for (b = 0; b < 4; b = b + 1) begin
+ if (s_ctrlport_req_byte_en[b]) begin
+ if (WORD_ADDR_W > 0) begin
+ // Update only the word of the register being addressed. "max"
+ // is needed by Vivado here to elaborate when WORD_ADDR_W is 0.
+ w = s_ctrlport_req_addr[2 +: max(1, WORD_ADDR_W)];
+ reg_val[w*32+b*8 +: 8] <= s_ctrlport_req_data[8*b +: 8];
+ end else begin
+ reg_val[b*8 +: 8] <= s_ctrlport_req_data[8*b +: 8];
+ end
+ end
+ end
+ s_ctrlport_resp_ack_wr <= 1'b1;
+ written <= 1'b1;
+ end else begin
+ s_ctrlport_resp_ack_wr <= 1'b0;
+ written <= 1'b0;
+ end
+ end
+ end
+
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Read Logic
+ //---------------------------------------------------------------------------
+
+ reg s_ctrlport_resp_ack_rd;
+
+ assign s_ctrlport_resp_status = 0; // Status is always "OK" (0)
+
+ assign value_out = reg_val[WIDTH-1:0];
+
+ // Because the register is only changed by software, read coherency is not
+ // required, so we just return the word that's being addressed.
+ always @(posedge ctrlport_clk) begin
+ // Check if any part of this register is being addressed
+ if (s_ctrlport_req_addr[19 : BYTE_ADDR_W] == ADDR[19 : BYTE_ADDR_W] && s_ctrlport_req_rd) begin
+ s_ctrlport_resp_ack_rd <= 1'b1;
+ if (WORD_ADDR_W > 0) begin
+ // Read back only the word of the register being addressed
+ s_ctrlport_resp_data <= reg_val[s_ctrlport_req_addr[2 +: WORD_ADDR_W]*32 +: 32];
+ end else begin
+ s_ctrlport_resp_data <= reg_val[31:0];
+ end
+ end else begin
+ s_ctrlport_resp_ack_rd <= 1'b0;
+ end
+ end
+
+ // Combine read/write ack
+ assign s_ctrlport_resp_ack = s_ctrlport_resp_ack_wr | s_ctrlport_resp_ack_rd;
+
+endmodule