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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: backend_iface
// Description:
// A noc_shell interface to the backend infrastructure
//
// Parameters:
// - CTRL_CLK_IDX: The index of the clock that is used for the control interface.
// UHD will query this to generate a clock interface object for
// the register interface. Set to 0 to let UHD figure this out
// itself.
// - TB_CLK_IDX: The index of the clock that is used as the timebase for this
// block. UHD will query this to generate a clock interface object
// for the register interface. This allows converting real-valued
// timestamps into tick counts.
module backend_iface #(
parameter [31:0] NOC_ID = 32'h0,
parameter [5:0] NUM_DATA_I = 0,
parameter [5:0] NUM_DATA_O = 0,
parameter [5:0] CTRL_FIFOSIZE = 0,
parameter [7:0] CTRL_MAX_ASYNC_MSGS = 0,
parameter [5:0] CTRL_CLK_IDX = 6'h3F,
parameter [5:0] TB_CLK_IDX = 6'h3F,
parameter [5:0] MTU = 0
)(
// Input clock
input wire rfnoc_chdr_clk,
input wire rfnoc_ctrl_clk,
// Output reset
output wire rfnoc_chdr_rst,
output wire rfnoc_ctrl_rst,
// Flush interface (sync. to rfnoc_chdr_clk)
output wire data_i_flush_en,
output wire [31:0] data_i_flush_timeout,
input wire [63:0] data_i_flush_active,
input wire [63:0] data_i_flush_done,
output wire data_o_flush_en,
output wire [31:0] data_o_flush_timeout,
input wire [63:0] data_o_flush_active,
input wire [63:0] data_o_flush_done,
// Backend interface (sync. to rfnoc_ctrl_clk)
input wire [511:0] rfnoc_core_config,
output wire [511:0] rfnoc_core_status
);
localparam RESET_LENGTH = 32;
`include "rfnoc_backend_iface.vh"
// -----------------------------------
// CONFIG: Infrastructure => Block
// -----------------------------------
wire [BEC_TOTAL_WIDTH-1:0] rfnoc_core_config_trim = rfnoc_core_config[BEC_TOTAL_WIDTH-1:0];
// Synchronize flush signals to the CHDR clock domain. Note this is only
// necessary if we have data ports.
generate
if (NUM_DATA_I > 0 || NUM_DATA_O > 0) begin
reg [31:0] flush_timeout_ctclk = 32'd0;
reg flush_en_ctclk = 1'b0;
// Register logic before synchronizer
always @(posedge rfnoc_ctrl_clk) begin
flush_timeout_ctclk <= rfnoc_core_config_trim[BEC_FLUSH_TIMEOUT_OFFSET +: BEC_FLUSH_TIMEOUT_WIDTH];
flush_en_ctclk <= rfnoc_core_config_trim[BEC_FLUSH_EN_OFFSET +: BEC_FLUSH_EN_WIDTH ];
end
// Synchronizer
wire [31:0] flush_timeout_chclk;
wire flush_en_chclk;
// Note: We are using a synchronizer to cross the 32-bit timeout bus
// into a different clock domain. Typically we would use a 2clk FIFO
// but it's OK to have the bits unsynchronized here because the value
// is static and is set from SW long before it is actually used.
synchronizer #(.WIDTH(33), .INITIAL_VAL(33'd0)) sync_ctrl_i (
.clk(rfnoc_chdr_clk), .rst(1'b0),
.in({flush_en_ctclk, flush_timeout_ctclk}),
.out({flush_en_chclk, flush_timeout_chclk})
);
assign data_i_flush_timeout = flush_timeout_chclk;
assign data_o_flush_timeout = flush_timeout_chclk;
assign data_i_flush_en = flush_en_chclk;
assign data_o_flush_en = flush_en_chclk;
end else begin
assign data_i_flush_timeout = 32'h0;
assign data_o_flush_timeout = 32'h0;
assign data_i_flush_en = 1'b0;
assign data_o_flush_en = 1'b0;
end
endgenerate
// Synchronize the reset to the CHDR and CTRL clock domains, and extend the
// reset pulse to make it long enough for most IP to reset correctly.
reg soft_ctrl_rst_ctclk = 1'b0;
reg soft_chdr_rst_ctclk = 1'b0;
wire rfnoc_ctrl_rst_pulse;
wire rfnoc_chdr_rst_pulse;
// Register logic before synchronizer
always @(posedge rfnoc_ctrl_clk) begin
soft_ctrl_rst_ctclk <= rfnoc_core_config_trim[BEC_SOFT_CTRL_RST_OFFSET +: BEC_SOFT_CTRL_RST_WIDTH];
soft_chdr_rst_ctclk <= rfnoc_core_config_trim[BEC_SOFT_CHDR_RST_OFFSET +: BEC_SOFT_CHDR_RST_WIDTH];
end
pulse_synchronizer #(.MODE("POSEDGE")) soft_ctrl_rst_sync_i (
.clk_a(rfnoc_ctrl_clk), .rst_a(1'b0), .pulse_a(soft_ctrl_rst_ctclk), .busy_a(),
.clk_b(rfnoc_ctrl_clk), .pulse_b(rfnoc_ctrl_rst_pulse)
);
pulse_synchronizer #(.MODE("POSEDGE")) soft_chdr_rst_sync_i (
.clk_a(rfnoc_ctrl_clk), .rst_a(1'b0), .pulse_a(soft_chdr_rst_ctclk), .busy_a(),
.clk_b(rfnoc_chdr_clk), .pulse_b(rfnoc_chdr_rst_pulse)
);
pulse_stretch_min #(.LENGTH(RESET_LENGTH)) soft_ctrl_rst_stretch_i (
.clk(rfnoc_ctrl_clk), .rst(1'b0),
.pulse_in(rfnoc_ctrl_rst_pulse), .pulse_out(rfnoc_ctrl_rst)
);
pulse_stretch_min #(.LENGTH(RESET_LENGTH)) soft_chdr_rst_stretch_i (
.clk(rfnoc_chdr_clk), .rst(1'b0),
.pulse_in(rfnoc_chdr_rst_pulse), .pulse_out(rfnoc_chdr_rst)
);
// -----------------------------------
// STATUS: Block => Infrastructure
// -----------------------------------
generate
if (NUM_DATA_I > 0 || NUM_DATA_O > 0) begin
reg flush_active_chclk = 1'b0;
reg flush_done_chclk = 1'b0;
// Register logic before synchronizer
wire flush_active_ctclk;
wire flush_done_ctclk;
if (NUM_DATA_I > 0 && NUM_DATA_O > 0) begin
always @(posedge rfnoc_chdr_clk) begin
flush_active_chclk <= (|data_i_flush_active[NUM_DATA_I-1:0]) | (|data_o_flush_active[NUM_DATA_O-1:0]);
flush_done_chclk <= (&data_i_flush_done [NUM_DATA_I-1:0]) & (&data_o_flush_done [NUM_DATA_O-1:0]);
end
end else if (NUM_DATA_I > 0 && NUM_DATA_O == 0) begin
always @(posedge rfnoc_chdr_clk) begin
flush_active_chclk <= (|data_i_flush_active[NUM_DATA_I-1:0]);
flush_done_chclk <= (&data_i_flush_done [NUM_DATA_I-1:0]);
end
end else if (NUM_DATA_I == 0 && NUM_DATA_O > 0) begin
always @(posedge rfnoc_chdr_clk) begin
flush_active_chclk <= (|data_o_flush_active[NUM_DATA_O-1:0]);
flush_done_chclk <= (&data_o_flush_done [NUM_DATA_O-1:0]);
end
end
// Synchronizer
synchronizer #(.WIDTH(2), .INITIAL_VAL(2'd0)) sync_status_i (
.clk(rfnoc_ctrl_clk), .rst(1'b0),
.in({flush_active_chclk, flush_done_chclk}),
.out({flush_active_ctclk, flush_done_ctclk})
);
assign rfnoc_core_status[BES_FLUSH_ACTIVE_OFFSET+:BES_FLUSH_ACTIVE_WIDTH] = flush_active_ctclk;
assign rfnoc_core_status[BES_FLUSH_DONE_OFFSET +:BES_FLUSH_DONE_WIDTH ] = flush_done_ctclk;
end else begin
assign rfnoc_core_status[BES_FLUSH_ACTIVE_OFFSET+:BES_FLUSH_ACTIVE_WIDTH] = {BES_FLUSH_ACTIVE_WIDTH{1'b0}};
assign rfnoc_core_status[BES_FLUSH_DONE_OFFSET +:BES_FLUSH_DONE_WIDTH ] = {BES_FLUSH_DONE_WIDTH{1'b1}};
end
endgenerate
assign rfnoc_core_status[BES_PROTO_VER_OFFSET +:BES_PROTO_VER_WIDTH ] = BACKEND_PROTO_VER;
assign rfnoc_core_status[BES_NUM_DATA_I_OFFSET +:BES_NUM_DATA_I_WIDTH ] = NUM_DATA_I;
assign rfnoc_core_status[BES_NUM_DATA_O_OFFSET +:BES_NUM_DATA_O_WIDTH ] = NUM_DATA_O;
assign rfnoc_core_status[BES_CTRL_FIFOSIZE_OFFSET +:BES_CTRL_FIFOSIZE_WIDTH ] = CTRL_FIFOSIZE;
assign rfnoc_core_status[BES_CTRL_MAX_ASYNC_MSGS_OFFSET+:BES_CTRL_MAX_ASYNC_MSGS_WIDTH] = CTRL_MAX_ASYNC_MSGS;
assign rfnoc_core_status[BES_NOC_ID_OFFSET +:BES_NOC_ID_WIDTH ] = NOC_ID;
assign rfnoc_core_status[BES_DATA_MTU_OFFSET +:BES_DATA_MTU_WIDTH ] = MTU;
assign rfnoc_core_status[BES_CTRL_CLK_IDX_OFFSET +:BES_CTRL_CLK_IDX_WIDTH ] = CTRL_CLK_IDX;
assign rfnoc_core_status[BES_TB_CLK_IDX_OFFSET +:BES_TB_CLK_IDX_WIDTH ] = TB_CLK_IDX;
// Assign the rest to 0
assign rfnoc_core_status[511:BES_TOTAL_WIDTH] = {(512-BES_TOTAL_WIDTH){1'b0}};
endmodule // backend_iface
|