Files
b210-k7-fpga/sim/rfnoc/ctrlport_bfm_pkg.sv
T
Max Köhler f961936a43 rfnoc: lib: introduce ctrl-port interface and BFM
Original-commit: 886cf97ad5d494b20c6fff0a53113f6096d458c7
2025-02-14 08:04:26 -06:00

232 lines
6.8 KiB
Systemverilog

//
// Copyright 2024 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Package: ctrlport_bfm_pkg
//
// Description:
//
// Package defining a Control Port bus functional model (BFM).
// See the RFNoC Specification for a protocol description.
//
package ctrlport_bfm_pkg;
import ctrlport_pkg::*;
class ctrlport_bfm;
// create combined request and response struct for mailbox
typedef struct {
ctrlport_request_t req;
ctrlport_response_t resp;
bit skip_data_check;
} ctrlport_transfer_t;
local mailbox #(ctrlport_transfer_t) transactions;
// link to interface
local virtual ctrlport_if iface;
// data copy for response data
local logic [CTRLPORT_DATA_W-1:0] reponse_data;
// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------
// Class constructor. This must be given an interface for the master
// connection and an interface for the slave connection.
function new(virtual ctrlport_if iface);
this.iface = iface;
transactions = new;
endfunction
// ------------------------------------------------------------------
// Asynchronous transaction handling
// ------------------------------------------------------------------
// Queue an asynchronous transaction (read or write with full request)
task async_request(
ctrlport_request_t request,
ctrlport_response_t expected_response,
bit skip_data_check = '0
);
assert (request.rd || request.wr) else $error(1, "Transaction must be read or write");
transactions.put('{request, expected_response, skip_data_check});
endtask
// Queue a simple write transaction
task async_write(
logic[CTRLPORT_ADDR_W-1:0] addr,
logic[CTRLPORT_DATA_W-1:0] data,
ctrlport_status_t expected_status = STS_OKAY
);
ctrlport_request_t request;
ctrlport_response_t response;
request = '0;
request.wr = '1;
request.addr = addr;
request.data = data;
response = '0;
response.status = expected_status;
async_request(request, response);
endtask;
// Queue a simple read transaction with automated response checking
task async_read(
logic [CTRLPORT_ADDR_W-1:0] addr,
logic [CTRLPORT_DATA_W-1:0] expected_data,
ctrlport_status_t expected_status = STS_OKAY
);
ctrlport_request_t request;
ctrlport_response_t response;
request = '0;
request.rd = '1;
request.addr = addr;
response = '0;
response.status = expected_status;
response.data = expected_data;
async_request(request, response);
endtask;
// wait for all transactions to be processed
task wait_complete();
while(transactions.num() > 0) begin
@(posedge iface.clk);
end
endtask
// ------------------------------------------------------------------
// Synchronous transaction handling
// ------------------------------------------------------------------
// Perform a request and get response
task request(
input ctrlport_request_t request,
input ctrlport_response_t expected_response,
output logic[CTRLPORT_DATA_W-1:0] data
);
async_request(request, expected_response);
wait_complete();
data = reponse_data;
endtask;
// Perform a simple write transaction
task write(
logic[CTRLPORT_ADDR_W-1:0] addr,
logic[CTRLPORT_DATA_W-1:0] data,
ctrlport_status_t expected_status = STS_OKAY
);
async_write(addr, data, expected_status);
wait_complete();
endtask;
// Perform a simple read transaction, no response data check, return data from interface
task read(
input logic[CTRLPORT_ADDR_W-1:0] addr,
output logic[CTRLPORT_DATA_W-1:0] data,
input ctrlport_status_t expected_status = STS_OKAY
);
ctrlport_request_t request;
ctrlport_response_t response;
request = '0;
request.rd = '1;
request.addr = addr;
response = '0;
response.status = expected_status;
async_request(request, response, '1);
wait_complete();
data = reponse_data;
endtask;
// ------------------------------------------------------------------
// Signal handling
// ------------------------------------------------------------------
// Start the BFM.
// This will join a separate thread for the interface handling.
task run();
fork
request_body();
join_none
endtask
// Control the master request interface
local task request_body();
ctrlport_transfer_t transfer;
ctrlport_request_t request_idle;
// initialize request interface
request_idle = 'x;
request_idle.wr = '0;
request_idle.rd = '0;
iface.req = request_idle;
// ignore first clock cycle to let response settle
@(posedge iface.clk);
forever begin
// handle any available transaction (but leave it in the mailbox for now)
if (transactions.try_peek(transfer)) begin
// wait for reset to be deasserted
while(iface.rst) begin
@(posedge iface.clk);
check_no_response();
end
// send request
iface.req = transfer.req;
// check minimum time for response on interface
@(negedge iface.clk);
check_no_response();
// reset request interface
@(posedge iface.clk);
iface.req = request_idle;
// wait for response
while(!iface.resp.ack) begin
@(posedge iface.clk);
end
// status is always expected to match
assert (iface.resp.status == transfer.resp.status) else
$error(1, "Unexpected status received on interface. Expected: %2b Received: %2b", transfer.resp.status, iface.resp.status);
// check data if read and data check is not skipped
if (transfer.req.rd && !transfer.skip_data_check) begin
assert (iface.resp.data == transfer.resp.data) else
$error(1, "Unexpected data received on interface. Expected: %h Received: %h", transfer.resp.data, iface.resp.data);
end
// save data for read operations
reponse_data = iface.resp.data;
// request is done -> remove from queue
transactions.get(transfer);
// wait at least one clock cycle for next check of mailbox
end else begin
@(posedge iface.clk);
check_no_response();
end
end
endtask
// Check for no response in this clock cycle
local task check_no_response();
assert (iface.resp.ack == '0) else $error(1, "Unexpected response received on interface: %p", iface.resp);
endtask
endclass
endpackage