Files
b210-k7-fpga/lib/rfnoc/core/backend_iface.v
T
Martin Braun 8af1711952 fpga: rfnoc: Enable blocks with no inputs/outputs
The following modifications are applied to allow RFNoC blocks with zero
input- or output ports (or none at all):

- The backend interface will no longer synthesize any flushing logic if
  there are no streaming ports
- The Mako templates are modified to allow generating code for blocks
  without streaming ports
- The BFM for RFNoC blocks (RfnocBlockCtrlBfm) is partially factored out
  into a parent class (RfnocBlockCtrlBfmCtrlOnly) which allows the
  simulation of such RFNoC blocks


Original-commit: 878cfcf48f125826e9d48b7c61a4ee7fd19e9a94
2024-05-27 17:05:06 +02:00

198 lines
8.0 KiB
Verilog

//
// 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