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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
//
// 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("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 begin
$error("Unexpected status received on interface. Expected: 0b%2b Received: 0b%2b",
transfer.resp.status, iface.resp.status);
end
// 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 begin
$error("Unexpected data received on interface. Expected: 0x%h Received: 0x%h",
transfer.resp.data, iface.resp.data);
end
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();
if (iface.resp.ack != '0) begin
$error("Unexpected response received on interface: %p", iface.resp);
end
endtask
endclass
endpackage
|