diff --git a/lib/rfnoc/core/backend_iface.v b/lib/rfnoc/core/backend_iface.v index 80d64c3..2220a56 100644 --- a/lib/rfnoc/core/backend_iface.v +++ b/lib/rfnoc/core/backend_iface.v @@ -30,9 +30,6 @@ module backend_iface #( // Input clock input wire rfnoc_chdr_clk, input wire rfnoc_ctrl_clk, - // Backend interface (sync. to rfnoc_ctrl_clk) - input wire [511:0] rfnoc_core_config, - output wire [511:0] rfnoc_core_status, // Output reset output wire rfnoc_chdr_rst, output wire rfnoc_ctrl_rst, @@ -44,7 +41,10 @@ module backend_iface #( 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 + 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; @@ -54,41 +54,64 @@ module backend_iface #( // 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; - reg soft_ctrl_rst_ctclk = 1'b0; - reg soft_chdr_rst_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 ]; - 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 + reg [31:0] flush_timeout_ctclk = 32'd0; + reg flush_en_ctclk = 1'b0; - // Synchronizer - wire [31:0] flush_timeout_chclk; - wire flush_en_chclk; + // 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 - // 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 + wire [31:0] flush_timeout_chclk; + wire flush_en_chclk; - 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}) - ); + // 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. - // Synchronize the reset to the CHDR and CTRL clock domains, and extend the + 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) @@ -109,33 +132,54 @@ module backend_iface #( .pulse_in(rfnoc_chdr_rst_pulse), .pulse_out(rfnoc_chdr_rst) ); - 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; - // ----------------------------------- // STATUS: Block => Infrastructure // ----------------------------------- - reg flush_active_chclk = 1'b0; - reg flush_done_chclk = 1'b0; + generate + if (NUM_DATA_I > 0 || NUM_DATA_O > 0) begin - // Register logic before synchronizer - wire flush_active_ctclk; - wire flush_done_ctclk; + reg flush_active_chclk = 1'b0; + reg flush_done_chclk = 1'b0; - 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 + // Register logic before synchronizer + wire flush_active_ctclk; + wire flush_done_ctclk; - // 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}) - ); + 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; @@ -143,8 +187,6 @@ module backend_iface #( 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_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; 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; diff --git a/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv b/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv index 2e1b6a6..e4d207d 100644 --- a/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv +++ b/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv @@ -97,19 +97,15 @@ package PkgRfnocBlockCtrlBfm; // //--------------------------------------------------------------------------- - class RfnocBlockCtrlBfm #(CHDR_W = 64, ITEM_W = 32); + // Control functionality only + class RfnocBlockCtrlBfmCtrlOnly; - local virtual RfnocBackendIf.master backend; - local CtrlIfaceBfm ctrl; - local ChdrIfaceBfm #(CHDR_W, ITEM_W) m_data[$]; - local ChdrIfaceBfm #(CHDR_W, ITEM_W) s_data[$]; - local bit running; + protected virtual RfnocBackendIf.master backend; + protected CtrlIfaceBfm ctrl; + protected bit running; localparam CMD_PROP_CYC = 5; - typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; - typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; - // Class constructor to create a new BFM instance. // // backend: Interface for the backend signals of a block @@ -130,6 +126,123 @@ package PkgRfnocBlockCtrlBfm; this.running = 0; endfunction : new + // Start the data and control BFM's processes running. + task run(); + assert (backend.sts.v1.proto_ver == 1) else begin + $fatal(1, "The connected block has an incompatible backend interface"); + end + if (!running) begin + ctrl.run(); + running = 1; + end + endtask : run + + // Get static info about the block + function logic [7:0] get_proto_ver(); + return backend.sts.v1.proto_ver; + endfunction : get_proto_ver + + function logic [31:0] get_noc_id(); + return backend.sts.v1.noc_id; + endfunction : get_noc_id + + function logic [5:0] get_num_data_i(); + return backend.sts.v1.num_data_i; + endfunction : get_num_data_i + + function logic [5:0] get_num_data_o(); + return backend.sts.v1.num_data_o; + endfunction : get_num_data_o + + function logic [5:0] get_ctrl_fifosize(); + return backend.sts.v1.ctrl_fifosize; + endfunction : get_ctrl_fifosize + + function logic [5:0] get_mtu(); + return backend.sts.v1.mtu; + endfunction : get_mtu + + // Soft-Reset the Control path + // + // rst_cyc: Number of cycles to wait for reset completion + // + task reset_ctrl(input int rst_cyc = 100); + assert (running) else begin + $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running"); + end + + // Assert soft_ctrl_rst then wait + @(posedge backend.ctrl_clk); + backend.cfg.v1.soft_ctrl_rst = 1; + repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); + backend.cfg.v1.soft_ctrl_rst = 0; + repeat (rst_cyc) @(posedge backend.ctrl_clk); + endtask : reset_ctrl + + // Send a read request packet on the AXIS-Ctrl interface and get the + // response. + // + // addr: Address for the read request + // word: Data word that was returned in response to the read + // + task reg_read( + input ctrl_address_t addr, + output ctrl_word_t word + ); + assert (running) else begin + $fatal(1, "Cannot call reg_read until RfnocBlockCtrlBfm is running"); + end + + ctrl.reg_read(addr, word); + endtask : reg_read + + + // Send a a write request packet on the AXIS-Ctrl interface and get the + // response. + // + // addr: Address for the write request + // word: Data word to write + // + task reg_write( + ctrl_address_t addr, + ctrl_word_t word + ); + assert (running) else begin + $fatal(1, "Cannot call reg_write until RfnocBlockCtrlBfm is running"); + end + + ctrl.reg_write(addr, word); + endtask : reg_write + + endclass : RfnocBlockCtrlBfmCtrlOnly + + // Control functionality plus streaming (the normal/default case) + class RfnocBlockCtrlBfm #(CHDR_W = 64, ITEM_W = 32) extends RfnocBlockCtrlBfmCtrlOnly; + + local ChdrIfaceBfm #(CHDR_W, ITEM_W) m_data[$]; + local ChdrIfaceBfm #(CHDR_W, ITEM_W) s_data[$]; + + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; + + // Class constructor to create a new BFM instance. + // + // backend: Interface for the backend signals of a block + // m_ctrl: Interface for the CTRL master connection (EP's AXIS-Ctrl output) + // s_ctrl: Interface for the CTRL slave connection (EP's AXIS-Ctrl input) + // dst_port: Destination port to use in generated control packets + // src_port: Source port to use in generated control packets + // + function new( + virtual RfnocBackendIf.master backend, + virtual AxiStreamIf #(32).master m_ctrl, + virtual AxiStreamIf #(32).slave s_ctrl, + input ctrl_port_t dst_port = 10'd2, + input ctrl_port_t src_port = 10'd1 + ); + super.new(backend, m_ctrl, s_ctrl, dst_port, src_port); + endfunction : new + // Add a master data port. This should connect to a DUT slave input. // // m_chdr: Virtual master interface to connect new port to. @@ -202,7 +315,7 @@ package PkgRfnocBlockCtrlBfm; // Start the data and control BFM's processes running. task run(); - assert (backend.sts.v1.proto_ver == 1) else begin + assert (super.backend.sts.v1.proto_ver == 1) else begin $fatal(1, "The connected block has an incompatible backend interface"); end if (!running) begin @@ -284,31 +397,6 @@ package PkgRfnocBlockCtrlBfm; return m_data[port].get_ticks_per_word(); endfunction - // Get static info about the block - function logic [7:0] get_proto_ver(); - return backend.sts.v1.proto_ver; - endfunction : get_proto_ver - - function logic [31:0] get_noc_id(); - return backend.sts.v1.noc_id; - endfunction : get_noc_id - - function logic [5:0] get_num_data_i(); - return backend.sts.v1.num_data_i; - endfunction : get_num_data_i - - function logic [5:0] get_num_data_o(); - return backend.sts.v1.num_data_o; - endfunction : get_num_data_o - - function logic [5:0] get_ctrl_fifosize(); - return backend.sts.v1.ctrl_fifosize; - endfunction : get_ctrl_fifosize - - function logic [5:0] get_mtu(); - return backend.sts.v1.mtu; - endfunction : get_mtu - // Soft-Reset the CHDR path // // rst_cyc: Number of cycles to wait for reset completion @@ -328,23 +416,6 @@ package PkgRfnocBlockCtrlBfm; repeat (rst_cyc) @(posedge backend.ctrl_clk); endtask : reset_chdr - // Soft-Reset the Control path - // - // rst_cyc: Number of cycles to wait for reset completion - // - task reset_ctrl(input int rst_cyc = 100); - assert (running) else begin - $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running"); - end - - // Assert soft_ctrl_rst then wait - @(posedge backend.ctrl_clk); - backend.cfg.v1.soft_ctrl_rst = 1; - repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); - backend.cfg.v1.soft_ctrl_rst = 0; - repeat (rst_cyc) @(posedge backend.ctrl_clk); - endtask : reset_ctrl - // Flush the data ports of the block // // idle_cyc: Number of idle cycles before done is asserted @@ -822,42 +893,6 @@ package PkgRfnocBlockCtrlBfm; m_data[port].set_master_stall_prob(stall_prob); endfunction - - // Send a read request packet on the AXIS-Ctrl interface and get the - // response. - // - // addr: Address for the read request - // word: Data word that was returned in response to the read - // - task reg_read( - input ctrl_address_t addr, - output ctrl_word_t word - ); - assert (running) else begin - $fatal(1, "Cannot call reg_read until RfnocBlockCtrlBfm is running"); - end - - ctrl.reg_read(addr, word); - endtask : reg_read - - - // Send a a write request packet on the AXIS-Ctrl interface and get the - // response. - // - // addr: Address for the write request - // word: Data word to write - // - task reg_write( - ctrl_address_t addr, - ctrl_word_t word - ); - assert (running) else begin - $fatal(1, "Cannot call reg_write until RfnocBlockCtrlBfm is running"); - end - - ctrl.reg_write(addr, word); - endtask : reg_write - // Compare data vectors static function bit compare_data( input chdr_word_t lhs[$],