hdk/common/verif/models/sh_bfm/axis_bfm.sv (349 lines of code) (raw):

// ============================================================================ // Amazon FPGA Hardware Development Kit // // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Amazon Software License (the "License"). You may not use // this file except in compliance with the License. A copy of the License is // located at // // http://aws.amazon.com/asl/ // // or in the "license" file accompanying this file. This file is distributed on // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or // implied. See the License for the specific language governing permissions and // limitations under the License. // ============================================================================ `include "anp_base_macros.svh" //------------------------------------------------------------------------------ // Module: axis_bfm // Main AXIS BFM module. Both of Master and Slave mode are implemented. // Shall set the IS_MASTER parameter as below to selet the mode during // instantiation. // Master mode -> IS_MASTER=1 // Slave mode -> IS_MASTER=0 //------------------------------------------------------------------------------ module axis_bfm #( parameter MSG_ID = "AXIS_BFM", parameter FILE_NAME = "", parameter IS_MASTER = 1, parameter DATA_WIDTH = 64, parameter USER_WIDTH = 1, parameter KEEP_WIDTH = (DATA_WIDTH/8) )( input wire axis_clk, input wire axis_rst_n, inout wire axis_valid, inout wire [DATA_WIDTH-1:0] axis_data, inout wire axis_last, inout wire [KEEP_WIDTH-1:0] axis_keep, inout wire [USER_WIDTH-1:0] axis_user, inout wire axis_ready ); import axis_bfm_pkg::*; //--------------------------------------------------------------------------- // Local variables //--------------------------------------------------------------------------- logic m_axis_valid; logic [DATA_WIDTH-1:0] m_axis_data; logic m_axis_last; logic [KEEP_WIDTH-1:0] m_axis_keep; logic [USER_WIDTH-1:0] m_axis_user; logic m_axis_ready; // FIFO Queue to store the stimulus packet being read from the text stimulus file axis_mbx_t stim_mbx; // FIFO Queue to store the actual packet being driven axis_mbx_t drive_mbx; // FIFO Queue to store the monitored sampled packets axis_mbx_t monitor_mbx; // Event to indicate the mailboxes are created event mbx_created_e; // File pointer to the input file to read the user stimulus int fp_i; // File pointer to the output file to dump the sampled packet int fp_o; // Knob indicate the output file dump is enabled bit dump_file; // Knob indicate the input stimulus file read is enabled bit read_file; // HDL path of the hierarchy of this BFM's instance string hdl_path = $sformatf("%m"); //--------------------------------------------------------------------------- // Task: reset // Reset the output signals //--------------------------------------------------------------------------- task automatic reset(); if (IS_MASTER) begin m_axis_valid <= 0; m_axis_data <= 0; m_axis_last <= 0; m_axis_keep <= 0; m_axis_user <= 0; end else begin m_axis_ready = 0; end endtask : reset always @ (posedge axis_clk, negedge axis_rst_n) begin if (axis_rst_n === 1'b0) begin while (monitor_mbx.num() > 0) begin axis_bfm_pkt np; monitor_mbx.get(np); end reset(); end else if (IS_MASTER == 0) begin `ifdef VCS randcase 1 : m_axis_ready <= ($test$plusargs("AXIS_RAND_READY") ? 0: 1); 20 : m_axis_ready <= 1; endcase `else m_axis_ready <= 1; `endif end end //--------------------------------------------------------------------------- // Create the instance of the mailboxes //--------------------------------------------------------------------------- initial begin stim_mbx = new(1); monitor_mbx = new(); drive_mbx = new(); ->mbx_created_e; end //--------------------------------------------------------------------------- // Drive Output signals //--------------------------------------------------------------------------- generate if (IS_MASTER == 1) begin : MASTER_MODE assign axis_valid = m_axis_valid; assign axis_data = m_axis_data; assign axis_last = m_axis_last; assign axis_keep = m_axis_keep; assign axis_user = m_axis_user; end : MASTER_MODE else begin : SLAVE_MODE assign axis_ready = m_axis_ready; end : SLAVE_MODE endgenerate //--------------------------------------------------------------------------- // Open the input stimulus file for data generation and drive the packets // based on the provided stimulus //--------------------------------------------------------------------------- bit stim_done; generate if (IS_MASTER) begin : MASTER initial begin : READ_TEXT_STIMULUS string fname; wait (mbx_created_e.triggered); if (FILE_NAME == "") begin fname = {hdl_path, ".in"}; end else begin fname = {FILE_NAME, ".in"}; end fp_i = $fopen(fname, "r"); if (fp_i != 0) begin : OPENED read_file = 1; `anp_info( {"read_stim: Driving master packet using stimulus file ", fname}, MSG_ID) axis_bfm_pkg::read_stim( .mbx_h (stim_mbx), .fp (fp_i), .msg_id (MSG_ID)); end : OPENED stim_done = 1; `anp_info("drive_stim: Done", MSG_ID) end : READ_TEXT_STIMULUS initial begin : DRIVE_TEXT_STIMULUS string fname; wait_out_of_reset($sformatf("drive_stim(%0d)", `__LINE__)); forever begin axis_bfm_pkt pkt; stim_mbx.get(pkt); wait_out_of_reset($sformatf("drive_stim(%0d)", `__LINE__)); wait_clock(pkt.delay); drive_packet(pkt.data); end end : DRIVE_TEXT_STIMULUS end : MASTER endgenerate //--------------------------------------------------------------------------- // Open the output file to dump the monitored packet //--------------------------------------------------------------------------- initial begin string fname; if ($test$plusargs("AXIS_DUMP_PACKET")) begin if (FILE_NAME == "") begin fname = {hdl_path, ".out"}; end else begin fname = {FILE_NAME, ".out"}; end fp_o = $fopen(fname, "w"); if (fp_o != 0) begin dump_file = 1; end else begin `anp_fatal({"Failed to open ", fname}, MSG_ID) end end end //--------------------------------------------------------------------------- // Close the opened files //--------------------------------------------------------------------------- final begin if (dump_file) $fclose(fp_o); if (read_file) $fclose(fp_i); end //--------------------------------------------------------------------------- // Task: drive_packet // Task to send a complete single packet based on the input data array. // // Parameters: // - data : Queue of data array in bytes // - user : User data value to be used at the end of packet //--------------------------------------------------------------------------- task automatic drive_packet(input bit [7:0] data[$], input logic user = 0); int total_beats = (data.size() + (KEEP_WIDTH - 1)) / KEEP_WIDTH; if (IS_MASTER == 0) begin `anp_fatal("Calling send task is not supported in slave mode", MSG_ID) return; end wait_out_of_reset($sformatf("drive_packet(%0d)", `__LINE__)); `anp_info({"drive_packet: ", axis_bfm_pkg::print_array(data)}, MSG_ID) if ($test$plusargs("AXIS_CHECK_DRIVE_AND_MONITOR")) begin axis_bfm_pkt pkt = new(); pkt.data = data; drive_mbx.put(pkt); end for (int i = 0; i < total_beats; i++) begin bit [DATA_WIDTH-1:0] data_tmp; bit [KEEP_WIDTH-1:0] keep_tmp; for (int j = 0; j < KEEP_WIDTH; j++) begin if (data.size() > 0) begin data_tmp[j*8+:8] = data.pop_front(); keep_tmp[j] = 1'b1; end end @ (posedge axis_clk); m_axis_valid <= 1'b1; m_axis_data <= data_tmp; m_axis_keep <= keep_tmp; if (data.size() == 0) begin m_axis_last <= 1'b1; m_axis_user <= 1'b0; end `anp_debug($sformatf("drive_packet: %0d / %0d", i, total_beats), MSG_ID) #1ns; while (axis_ready !== 1'b1) begin @ (posedge axis_clk); #1ns; end end @ (posedge axis_clk); reset(); endtask : drive_packet //--------------------------------------------------------------------------- // Task: monitor // Task to monitor the AXI4 Streaming data, sample them and store them // into the monitor FIFO //--------------------------------------------------------------------------- int delay; bit sampling; int packets; task automatic monitor_packet(); wait (mbx_created_e.triggered); wait_out_of_reset($sformatf("monitor_packet(%0d)", `__LINE__)); forever begin // Create the new handle of the packet that will be assembled axis_bfm_pkt pkt = new(); wait_out_of_reset($sformatf("monitor_packet(%0d)", `__LINE__)); @ (posedge axis_clk); #1ns; if (!sampling) begin delay++; end `anp_debug($sformatf("monitor_packet: Looking for next packet#%0d", packets), MSG_ID) while ((axis_valid & axis_ready & axis_last) !== 1'b1) begin : SAMPLE if ((axis_valid & axis_ready) === 1'b1) begin sampling <= 1; for (int i = 0; i < KEEP_WIDTH; i++) begin if (axis_keep[i] === 1'b1) begin pkt.data.push_back(axis_data[i*8+:8]); end end `anp_debug($sformatf("monitor_packet: Sampling packet#%0d", packets), MSG_ID) @ (posedge axis_clk); #1ns; end else begin @ (posedge axis_clk); #1ns; if (!sampling) begin delay++; end end end : SAMPLE // Capture the last beat for (int i = 0; i < KEEP_WIDTH; i++) begin if (axis_keep[i] === 1'b1) begin pkt.data.push_back(axis_data[i*8+:8]); end end // Broadcast the sampled packet into the FIFO monitor_mbx.put(pkt); // If a dump to text file is selected then DUMP the assembled // packet into the text file if (dump_file) begin pkt.dump(fp_o, delay); end `anp_info($sformatf("monitor_packet: Complete Sampling packet#%0d. %0s", packets, pkt.print), MSG_ID) packets++; delay = 0; sampling = 0; end endtask : monitor_packet initial monitor_packet(); //--------------------------------------------------------------------------- // Wait for number of clock cycles //--------------------------------------------------------------------------- task automatic wait_clock(int clocks); repeat (clocks) @ (posedge axis_clk); endtask : wait_clock //--------------------------------------------------------------------------- // Wait for number of clock cycles //--------------------------------------------------------------------------- task automatic wait_out_of_reset(string id); wait (axis_rst_n === 1'b1); wait_clock(1); `anp_info({id, ": Out-of-reset"}, MSG_ID) endtask : wait_out_of_reset //--------------------------------------------------------------------------- // Self drive and monitor checker //--------------------------------------------------------------------------- initial begin if ($test$plusargs("AXIS_CHECK_DRIVE_AND_MONITOR") && IS_MASTER) begin int packets; forever begin axis_bfm_pkt exp, act; drive_mbx .get(exp); monitor_mbx.get(act); void'(exp.compare( .rhs (act), .message ($sformatf("%0s-SELF-CHECK Pkt-%0d", MSG_ID, packets)))); packets++; end end end endmodule : axis_bfm