1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: ctrlport_reg_ro
//
// Description:
//
// Implements a read-only register on a CTRL Port bus. The actual register
// bits are driven from outside of this module and passed in through the
// "value_in" input port. 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 COHERENT is true and the WIDTH is larger than a single CTRL Port word
// (32 bits), reading the least-significant word of the register causes the
// other words of the register to be read and saved in a cache register on
// the same clock cycle. Reading the upper words of the register will always
// read from the cached copy. This allows reads 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 which could change or roll over between 32-bit reads. The
// least-significant word MUST always be read first when COHERENT 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_in" 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 reads
// maintain coherency. Setting to 0 removes this logic, so that
// each 32-bit word of the register is treated independently.
//
// Ports:
//
// *ctrlport* : CTRL Port interface.
// value_in : The current value of the register.
//
module ctrlport_reg_ro #(
parameter [ 19:0] ADDR = 0,
parameter WIDTH = 32,
parameter COHERENT = 0
) (
input wire ctrlport_clk,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
output reg s_ctrlport_resp_ack,
output wire [ 1:0] s_ctrlport_resp_status,
output reg [31:0] s_ctrlport_resp_data,
input wire [WIDTH-1:0] value_in
);
//---------------------------------------------------------------------------
// 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
//---------------------------------------------------------------------------
// Resize Input Value
//---------------------------------------------------------------------------
// Use full size to simplify indexing. Unused bits will be optimized away.
reg [NUM_BYTES*8-1:0] reg_val = 0;
always @(*) begin
reg_val <= 0;
reg_val[WIDTH-1:0] <= value_in;
end
//---------------------------------------------------------------------------
// Read Logic
//---------------------------------------------------------------------------
reg [WIDTH-1:0] cache_reg;
assign s_ctrlport_resp_status = 0; // Status is always "OK" (0)
//
// Coherent implementation
//
if (WIDTH > 32 && COHERENT) begin : gen_coherent
// In this case we want the upper bits, when read separately, to be
// coherent with the lower bits. So we register the upper bits when the
// least-significant word is read.
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 <= 1'b1;
// Check if we're reading the least-significant word
if (s_ctrlport_req_addr[BYTE_ADDR_W-1 : 2] == 0) begin
s_ctrlport_resp_data <= reg_val[31:0];
cache_reg <= reg_val; // Unused bits will be optimized away
// Otherwise, grab the word that's being addressed from the cached value
end else begin
s_ctrlport_resp_data <= cache_reg[s_ctrlport_req_addr[2 +: WORD_ADDR_W]*32 +: 32];
end
end else begin
s_ctrlport_resp_ack <= 1'b0;
end
end
//
// Non-coherent implementation
//
end else begin : gen_no_coherent
// In this case, 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 <= 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;
end
end else begin
s_ctrlport_resp_ack <= 1'b0;
end
end
end
endmodule
|