Initial Commit - Forked from PeakRDL-regblock @ a440cc19769069be831d267505da4f3789a26695

This commit is contained in:
Arnav Sacheti
2025-10-10 22:28:36 -07:00
commit 9bf5cd1e68
308 changed files with 19414 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
from .passthrough import Passthrough
from .apb3 import APB3, FlatAPB3
from .apb4 import APB4, FlatAPB4
from .axi4lite import AXI4Lite, FlatAXI4Lite
from .avalon import Avalon, FlatAvalon
ALL_CPUIF = [
Passthrough(),
APB3(),
FlatAPB3(),
APB4(),
FlatAPB4(),
AXI4Lite(),
FlatAXI4Lite(),
Avalon(),
FlatAvalon(),
]

View File

@@ -0,0 +1,18 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
class APB3(CpuifTestMode):
cpuif_cls = APB3_Cpuif
rtl_files = [
"../../../../hdl-src/apb3_intf.sv",
]
tb_files = [
"../../../../hdl-src/apb3_intf.sv",
"apb3_intf_driver.sv",
]
tb_template = "tb_inst.sv"
class FlatAPB3(APB3):
cpuif_cls = APB3_Cpuif_flattened
rtl_files = []

View File

@@ -0,0 +1,116 @@
interface apb3_intf_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
apb3_intf.master m_apb
);
timeunit 1ps;
timeprecision 1ps;
logic PSEL;
logic PENABLE;
logic PWRITE;
logic [ADDR_WIDTH-1:0] PADDR;
logic [DATA_WIDTH-1:0] PWDATA;
logic [DATA_WIDTH-1:0] PRDATA;
logic PREADY;
logic PSLVERR;
assign m_apb.PSEL = PSEL;
assign m_apb.PENABLE = PENABLE;
assign m_apb.PWRITE = PWRITE;
assign m_apb.PADDR = PADDR;
assign m_apb.PWDATA = PWDATA;
assign PRDATA = m_apb.PRDATA;
assign PREADY = m_apb.PREADY;
assign PSLVERR = m_apb.PSLVERR;
default clocking cb @(posedge clk);
default input #1step output #1;
output PSEL;
output PENABLE;
output PWRITE;
output PADDR;
output PWDATA;
input PRDATA;
input PREADY;
input PSLVERR;
endclocking
task automatic reset();
cb.PSEL <= '0;
cb.PENABLE <= '0;
cb.PWRITE <= '0;
cb.PADDR <= '0;
cb.PWDATA <= '0;
endtask
semaphore txn_mutex = new(1);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data);
txn_mutex.get();
##0;
// Initiate transfer
cb.PSEL <= '1;
cb.PENABLE <= '0;
cb.PWRITE <= '1;
cb.PADDR <= addr;
cb.PWDATA <= data;
@(cb);
// active phase
cb.PENABLE <= '1;
@(cb);
// Wait for response
while(cb.PREADY !== 1'b1) @(cb);
reset();
txn_mutex.put();
endtask
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
txn_mutex.get();
##0;
// Initiate transfer
cb.PSEL <= '1;
cb.PENABLE <= '0;
cb.PWRITE <= '0;
cb.PADDR <= addr;
cb.PWDATA <= '0;
@(cb);
// active phase
cb.PENABLE <= '1;
@(cb);
// Wait for response
while(cb.PREADY !== 1'b1) @(cb);
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
data = cb.PRDATA;
reset();
txn_mutex.put();
endtask
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.PREADY)) else $error("Saw X on PREADY!");
end
endinterface

View File

@@ -0,0 +1,32 @@
{% sv_line_anchor %}
apb3_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) s_apb();
apb3_intf_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif (
.clk(clk),
.rst(rst),
.m_apb(s_apb)
);
{% if type(cpuif).__name__.startswith("Flat") %}
{% sv_line_anchor %}
wire s_apb_psel;
wire s_apb_penable;
wire s_apb_pwrite;
wire [{{exporter.cpuif.addr_width - 1}}:0] s_apb_paddr;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_pwdata;
wire s_apb_pready;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_prdata;
wire s_apb_pslverr;
assign s_apb_psel = s_apb.PSEL;
assign s_apb_penable = s_apb.PENABLE;
assign s_apb_pwrite = s_apb.PWRITE;
assign s_apb_paddr = s_apb.PADDR;
assign s_apb_pwdata = s_apb.PWDATA;
assign s_apb.PREADY = s_apb_pready;
assign s_apb.PRDATA = s_apb_prdata;
assign s_apb.PSLVERR = s_apb_pslverr;
{% endif %}

View File

@@ -0,0 +1,18 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
class APB4(CpuifTestMode):
cpuif_cls = APB4_Cpuif
rtl_files = [
"../../../../hdl-src/apb4_intf.sv",
]
tb_files = [
"../../../../hdl-src/apb4_intf.sv",
"apb4_intf_driver.sv",
]
tb_template = "tb_inst.sv"
class FlatAPB4(APB4):
cpuif_cls = APB4_Cpuif_flattened
rtl_files = []

View File

@@ -0,0 +1,128 @@
interface apb4_intf_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
apb4_intf.master m_apb
);
timeunit 1ps;
timeprecision 1ps;
logic PSEL;
logic PENABLE;
logic PWRITE;
logic [2:0] PPROT;
logic [ADDR_WIDTH-1:0] PADDR;
logic [DATA_WIDTH-1:0] PWDATA;
logic [DATA_WIDTH/8-1:0] PSTRB;
logic [DATA_WIDTH-1:0] PRDATA;
logic PREADY;
logic PSLVERR;
assign m_apb.PSEL = PSEL;
assign m_apb.PENABLE = PENABLE;
assign m_apb.PWRITE = PWRITE;
assign m_apb.PPROT = PPROT;
assign m_apb.PADDR = PADDR;
assign m_apb.PWDATA = PWDATA;
assign m_apb.PSTRB = PSTRB;
assign PRDATA = m_apb.PRDATA;
assign PREADY = m_apb.PREADY;
assign PSLVERR = m_apb.PSLVERR;
default clocking cb @(posedge clk);
default input #1step output #1;
output PSEL;
output PENABLE;
output PWRITE;
output PPROT;
output PADDR;
output PWDATA;
output PSTRB;
input PRDATA;
input PREADY;
input PSLVERR;
endclocking
task automatic reset();
cb.PSEL <= '0;
cb.PENABLE <= '0;
cb.PWRITE <= '0;
cb.PPROT <= '0;
cb.PADDR <= '0;
cb.PWDATA <= '0;
cb.PSTRB <= '0;
endtask
semaphore txn_mutex = new(1);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
txn_mutex.get();
##0;
// Initiate transfer
cb.PSEL <= '1;
cb.PENABLE <= '0;
cb.PWRITE <= '1;
cb.PPROT <= '0;
cb.PADDR <= addr;
cb.PWDATA <= data;
cb.PSTRB <= strb;
@(cb);
// active phase
cb.PENABLE <= '1;
@(cb);
// Wait for response
while(cb.PREADY !== 1'b1) @(cb);
reset();
txn_mutex.put();
endtask
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
txn_mutex.get();
##0;
// Initiate transfer
cb.PSEL <= '1;
cb.PENABLE <= '0;
cb.PWRITE <= '0;
cb.PPROT <= '0;
cb.PADDR <= addr;
cb.PWDATA <= '0;
cb.PSTRB <= '0;
@(cb);
// active phase
cb.PENABLE <= '1;
@(cb);
// Wait for response
while(cb.PREADY !== 1'b1) @(cb);
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
data = cb.PRDATA;
reset();
txn_mutex.put();
endtask
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.PREADY)) else $error("Saw X on PREADY!");
end
endinterface

View File

@@ -0,0 +1,36 @@
{% sv_line_anchor %}
apb4_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) s_apb();
apb4_intf_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif (
.clk(clk),
.rst(rst),
.m_apb(s_apb)
);
{% if type(cpuif).__name__.startswith("Flat") %}
{% sv_line_anchor %}
wire s_apb_psel;
wire s_apb_penable;
wire s_apb_pwrite;
wire [2:0] s_apb_pprot;
wire [{{exporter.cpuif.addr_width - 1}}:0] s_apb_paddr;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_pwdata;
wire [{{exporter.cpuif.data_width_bytes - 1}}:0] s_apb_pstrb;
wire s_apb_pready;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_prdata;
wire s_apb_pslverr;
assign s_apb_psel = s_apb.PSEL;
assign s_apb_penable = s_apb.PENABLE;
assign s_apb_pwrite = s_apb.PWRITE;
assign s_apb_pprot = s_apb.PPROT;
assign s_apb_paddr = s_apb.PADDR;
assign s_apb_pwdata = s_apb.PWDATA;
assign s_apb_pstrb = s_apb.PSTRB;
assign s_apb.PREADY = s_apb_pready;
assign s_apb.PRDATA = s_apb_prdata;
assign s_apb.PSLVERR = s_apb_pslverr;
{% endif %}

View File

@@ -0,0 +1,18 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened
class Avalon(CpuifTestMode):
cpuif_cls = Avalon_Cpuif
rtl_files = [
"../../../../hdl-src/avalon_mm_intf.sv",
]
tb_files = [
"../../../../hdl-src/avalon_mm_intf.sv",
"avalon_mm_intf_driver.sv",
]
tb_template = "tb_inst.sv"
class FlatAvalon(Avalon):
cpuif_cls = Avalon_Cpuif_flattened
rtl_files = []

View File

@@ -0,0 +1,138 @@
interface avalon_mm_intf_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
avalon_mm_intf.host avalon
);
timeunit 1ps;
timeprecision 1ps;
localparam ADDR_PAD = $clog2(DATA_WIDTH/8);
localparam WORD_ADDR_WIDTH = ADDR_WIDTH - ADDR_PAD;
logic av_read;
logic av_write;
logic av_waitrequest;
logic [WORD_ADDR_WIDTH-1:0] av_address;
logic [DATA_WIDTH-1:0] av_writedata;
logic [DATA_WIDTH/8-1:0] av_byteenable;
logic av_readdatavalid;
logic av_writeresponsevalid;
logic [DATA_WIDTH-1:0] av_readdata;
logic [1:0] av_response;
assign avalon.read = av_read;
assign avalon.write = av_write;
assign av_waitrequest = avalon.waitrequest;
assign avalon.address = av_address;
assign avalon.writedata = av_writedata;
assign avalon.byteenable = av_byteenable;
assign av_readdatavalid = avalon.readdatavalid;
assign av_writeresponsevalid = avalon.writeresponsevalid;
assign av_readdata = avalon.readdata;
assign av_response = avalon.response;
default clocking cb @(posedge clk);
default input #1step output #1;
output av_read;
output av_write;
input av_waitrequest;
output av_address;
output av_writedata;
output av_byteenable;
input av_readdatavalid;
input av_writeresponsevalid;
input av_readdata;
input av_response;
endclocking
task automatic reset();
cb.av_read <= '0;
cb.av_write <= '0;
cb.av_address <= '0;
cb.av_writedata <= '0;
cb.av_byteenable <= '0;
endtask
semaphore req_mutex = new(1);
semaphore resp_mutex = new(1);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
fork
begin
req_mutex.get();
##0;
// Initiate transfer
cb.av_write <= '1;
cb.av_address <= (addr >> ADDR_PAD);
cb.av_writedata <= data;
cb.av_byteenable <= strb;
@(cb);
// Wait for transfer to be accepted
while(cb.av_waitrequest == 1'b1) @(cb);
reset();
req_mutex.put();
end
begin
resp_mutex.get();
@cb;
// Wait for response
while(cb.av_writeresponsevalid !== 1'b1) @(cb);
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
resp_mutex.put();
end
join
endtask
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
fork
begin
req_mutex.get();
##0;
// Initiate transfer
cb.av_read <= '1;
cb.av_address <= (addr >> ADDR_PAD);
@(cb);
// Wait for transfer to be accepted
while(cb.av_waitrequest == 1'b1) @(cb);
reset();
req_mutex.put();
end
begin
resp_mutex.get();
@cb;
// Wait for response
while(cb.av_readdatavalid !== 1'b1) @(cb);
assert(!$isunknown(cb.av_readdata)) else $error("Read from 0x%0x returned X's on av_response", av_readdata);
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
data = cb.av_readdata;
resp_mutex.put();
end
join
endtask
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.av_waitrequest)) else $error("Saw X on av_waitrequest!");
if(!rst) assert(!$isunknown(cb.av_readdatavalid)) else $error("Saw X on av_readdatavalid!");
if(!rst) assert(!$isunknown(cb.av_writeresponsevalid)) else $error("Saw X on av_writeresponsevalid!");
end
endinterface

View File

@@ -0,0 +1,36 @@
{% sv_line_anchor %}
avalon_mm_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.word_addr_width}})
) avalon();
avalon_mm_intf_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif (
.clk(clk),
.rst(rst),
.avalon(avalon)
);
{% if type(cpuif).__name__.startswith("Flat") %}
{% sv_line_anchor %}
wire avalon_read;
wire avalon_write;
wire avalon_waitrequest;
wire [{{exporter.cpuif.word_addr_width - 1}}:0] avalon_address;
wire [{{exporter.cpuif.data_width - 1}}:0] avalon_writedata;
wire [{{exporter.cpuif.data_width_bytes - 1}}:0] avalon_byteenable;
wire avalon_readdatavalid;
wire avalon_writeresponsevalid;
wire [{{exporter.cpuif.data_width - 1}}:0] avalon_readdata;
wire [1:0] avalon_response;
assign avalon_read = avalon.read;
assign avalon_write = avalon.write;
assign avalon.waitrequest = avalon_waitrequest;
assign avalon_address = avalon.address;
assign avalon_writedata = avalon.writedata;
assign avalon_byteenable = avalon.byteenable;
assign avalon.readdatavalid = avalon_readdatavalid;
assign avalon.writeresponsevalid = avalon_writeresponsevalid;
assign avalon.readdata = avalon_readdata;
assign avalon.response = avalon_response;
{% endif %}

View File

@@ -0,0 +1,18 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened
class AXI4Lite(CpuifTestMode):
cpuif_cls = AXI4Lite_Cpuif
rtl_files = [
"../../../../hdl-src/axi4lite_intf.sv",
]
tb_files = [
"../../../../hdl-src/axi4lite_intf.sv",
"axi4lite_intf_driver.sv",
]
tb_template = "tb_inst.sv"
class FlatAXI4Lite(AXI4Lite):
cpuif_cls = AXI4Lite_Cpuif_flattened
rtl_files = []

View File

@@ -0,0 +1,263 @@
interface axi4lite_intf_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
axi4lite_intf.master m_axil
);
timeunit 1ps;
timeprecision 1ps;
logic AWREADY;
logic AWVALID;
logic [ADDR_WIDTH-1:0] AWADDR;
logic [2:0] AWPROT;
logic WREADY;
logic WVALID;
logic [DATA_WIDTH-1:0] WDATA;
logic [DATA_WIDTH/8-1:0] WSTRB;
logic BREADY;
logic BVALID;
logic [1:0] BRESP;
logic ARREADY;
logic ARVALID;
logic [ADDR_WIDTH-1:0] ARADDR;
logic [2:0] ARPROT;
logic RREADY;
logic RVALID;
logic [DATA_WIDTH-1:0] RDATA;
logic [1:0] RRESP;
assign AWREADY = m_axil.AWREADY;
assign m_axil.AWVALID = AWVALID;
assign m_axil.AWADDR = AWADDR;
assign m_axil.AWPROT = AWPROT;
assign WREADY = m_axil.WREADY;
assign m_axil.WVALID = WVALID;
assign m_axil.WDATA = WDATA;
assign m_axil.WSTRB = WSTRB;
assign m_axil.BREADY = BREADY;
assign BVALID = m_axil.BVALID;
assign BRESP = m_axil.BRESP;
assign ARREADY = m_axil.ARREADY;
assign m_axil.ARVALID = ARVALID;
assign m_axil.ARADDR = ARADDR;
assign m_axil.ARPROT = ARPROT;
assign m_axil.RREADY = RREADY;
assign RVALID = m_axil.RVALID;
assign RDATA = m_axil.RDATA;
assign RRESP = m_axil.RRESP;
default clocking cb @(posedge clk);
default input #1step output #1;
input AWREADY;
output AWVALID;
output AWADDR;
output AWPROT;
input WREADY;
output WVALID;
output WDATA;
output WSTRB;
inout BREADY;
input BVALID;
input BRESP;
input ARREADY;
output ARVALID;
output ARADDR;
output ARPROT;
inout RREADY;
input RVALID;
input RDATA;
input RRESP;
endclocking
task automatic reset();
cb.AWVALID <= '0;
cb.AWADDR <= '0;
cb.AWPROT <= '0;
cb.WVALID <= '0;
cb.WDATA <= '0;
cb.WSTRB <= '0;
cb.ARVALID <= '0;
cb.ARADDR <= '0;
cb.ARPROT <= '0;
endtask
initial forever begin
cb.RREADY <= $urandom_range(1, 0);
cb.BREADY <= $urandom_range(1, 0);
@cb;
end
//--------------------------------------------------------------------------
typedef struct {
logic [1:0] bresp;
} write_response_t;
class write_request_t;
mailbox #(write_response_t) response_mbx;
logic [ADDR_WIDTH-1:0] addr;
logic [DATA_WIDTH-1:0] data;
logic [DATA_WIDTH/8-1:0] strb;
function new();
this.response_mbx = new();
endfunction
endclass
mailbox #(write_request_t) aw_mbx = new();
mailbox #(write_request_t) w_mbx = new();
write_request_t write_queue[$];
// Issue AW transfers
initial forever begin
write_request_t req;
aw_mbx.get(req);
##0;
repeat($urandom_range(2,0)) @cb;
cb.AWVALID <= '1;
cb.AWADDR <= req.addr;
cb.AWPROT <= '0;
@(cb);
while(cb.AWREADY !== 1'b1) @(cb);
cb.AWVALID <= '0;
end
// Issue W transfers
initial forever begin
write_request_t req;
w_mbx.get(req);
##0;
repeat($urandom_range(2,0)) @cb;
cb.WVALID <= '1;
cb.WDATA <= req.data;
cb.WSTRB <= req.strb;
@(cb);
while(cb.WREADY !== 1'b1) @(cb);
cb.WVALID <= '0;
cb.WSTRB <= '0;
end
// Listen for R responses
initial forever begin
@cb;
while(rst || !(cb.BREADY === 1'b1 && cb.BVALID === 1'b1)) @cb;
if(write_queue.size() != 0) begin
// Can match this response with an existing request.
// Send response to requestor
write_request_t req;
write_response_t resp;
req = write_queue.pop_front();
resp.bresp = cb.BRESP;
req.response_mbx.put(resp);
end else begin
$error("Got unmatched write response");
end
end
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
write_request_t req;
write_response_t resp;
req = new();
req.addr = addr;
req.data = data;
req.strb = strb;
aw_mbx.put(req);
w_mbx.put(req);
write_queue.push_back(req);
// Wait for response
req.response_mbx.get(resp);
assert(!$isunknown(resp.bresp)) else $error("Read from 0x%0x returned X's on BRESP", addr);
endtask
//--------------------------------------------------------------------------
typedef struct {
logic [DATA_WIDTH-1: 0] rdata;
logic [1:0] rresp;
} read_response_t;
class read_request_t;
mailbox #(read_response_t) response_mbx;
function new();
this.response_mbx = new();
endfunction
endclass
semaphore txn_ar_mutex = new(1);
read_request_t read_queue[$];
// Listen for R responses
initial forever begin
@cb;
while(rst || !(cb.RREADY === 1'b1 && cb.RVALID === 1'b1)) @cb;
if(read_queue.size() != 0) begin
// Can match this response with an existing request.
// Send response to requestor
read_request_t req;
read_response_t resp;
req = read_queue.pop_front();
resp.rdata = cb.RDATA;
resp.rresp = cb.RRESP;
req.response_mbx.put(resp);
end else begin
$error("Got unmatched read response");
end
end
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
read_request_t req;
read_response_t resp;
txn_ar_mutex.get();
// Issue read request
##0;
cb.ARVALID <= '1;
cb.ARADDR <= addr;
cb.ARPROT <= '0;
@(cb);
while(cb.ARREADY !== 1'b1) @(cb);
cb.ARVALID <= '0;
// Push new request into queue
req = new();
read_queue.push_back(req);
txn_ar_mutex.put();
// Wait for response
req.response_mbx.get(resp);
assert(!$isunknown(resp.rdata)) else $error("Read from 0x%0x returned X's on RDATA", addr);
assert(!$isunknown(resp.rresp)) else $error("Read from 0x%0x returned X's on RRESP", addr);
data = resp.rdata;
endtask
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
//--------------------------------------------------------------------------
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.AWREADY)) else $error("Saw X on AWREADY!");
if(!rst) assert(!$isunknown(cb.WREADY)) else $error("Saw X on WREADY!");
if(!rst) assert(!$isunknown(cb.BVALID)) else $error("Saw X on BVALID!");
if(!rst) assert(!$isunknown(cb.ARREADY)) else $error("Saw X on ARREADY!");
if(!rst) assert(!$isunknown(cb.RVALID)) else $error("Saw X on RVALID!");
end
endinterface

View File

@@ -0,0 +1,54 @@
{% sv_line_anchor %}
axi4lite_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) s_axil();
axi4lite_intf_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif (
.clk(clk),
.rst(rst),
.m_axil(s_axil)
);
{% if type(cpuif).__name__.startswith("Flat") %}
{% sv_line_anchor %}
wire s_axil_awready;
wire s_axil_awvalid;
wire [{{exporter.cpuif.addr_width - 1}}:0] s_axil_awaddr;
wire [2:0] s_axil_awprot;
wire s_axil_wready;
wire s_axil_wvalid;
wire [{{exporter.cpuif.data_width - 1}}:0] s_axil_wdata;
wire [{{exporter.cpuif.data_width_bytes - 1}}:0] s_axil_wstrb;
wire s_axil_bready;
wire s_axil_bvalid;
wire [1:0] s_axil_bresp;
wire s_axil_arready;
wire s_axil_arvalid;
wire [{{exporter.cpuif.addr_width - 1}}:0] s_axil_araddr;
wire [2:0] s_axil_arprot;
wire s_axil_rready;
wire s_axil_rvalid;
wire [{{exporter.cpuif.data_width - 1}}:0] s_axil_rdata;
wire [1:0] s_axil_rresp;
assign s_axil.AWREADY = s_axil_awready;
assign s_axil_awvalid = s_axil.AWVALID;
assign s_axil_awaddr = s_axil.AWADDR;
assign s_axil_awprot = s_axil.AWPROT;
assign s_axil.WREADY = s_axil_wready;
assign s_axil_wvalid = s_axil.WVALID;
assign s_axil_wdata = s_axil.WDATA;
assign s_axil_wstrb = s_axil.WSTRB;
assign s_axil_bready = s_axil.BREADY;
assign s_axil.BVALID = s_axil_bvalid;
assign s_axil.BRESP = s_axil_bresp;
assign s_axil.ARREADY = s_axil_arready;
assign s_axil_arvalid = s_axil.ARVALID;
assign s_axil_araddr = s_axil.ARADDR;
assign s_axil_arprot = s_axil.ARPROT;
assign s_axil_rready = s_axil.RREADY;
assign s_axil.RVALID = s_axil_rvalid;
assign s_axil.RDATA = s_axil_rdata;
assign s_axil.RRESP = s_axil_rresp;
{% endif %}

87
tests/lib/cpuifs/base.py Normal file
View File

@@ -0,0 +1,87 @@
from typing import List, TYPE_CHECKING
import os
import inspect
import jinja2 as jj
from peakrdl_regblock.cpuif.base import CpuifBase
from ..sv_line_anchor import SVLineAnchor
if TYPE_CHECKING:
from peakrdl_regblock import RegblockExporter
from ..sim_testcase import SimTestCase
class CpuifTestMode:
cpuif_cls = None # type: CpuifBase
# Files required by the DUT
# Paths are relative to the class that assigns this
rtl_files = [] # type: List[str]
# Files required by the sim testbench
# Paths are relative to the class that assigns this
tb_files = [] # type: List[str]
# Path is relative to the class that assigns this
tb_template = ""
def _get_class_dir_of_variable(self, varname:str) -> str:
"""
Traverse up the MRO and find the first class that explicitly assigns
the variable of name varname. Returns the directory that contains the
class definition.
"""
for cls in inspect.getmro(self.__class__):
if varname in cls.__dict__:
class_dir = os.path.dirname(inspect.getfile(cls))
return class_dir
raise RuntimeError
def _get_file_paths(self, varname:str) -> List[str]:
class_dir = self._get_class_dir_of_variable(varname)
files = getattr(self, varname)
cwd = os.getcwd()
new_files = []
for file in files:
relpath = os.path.relpath(
os.path.join(class_dir, file),
cwd
)
new_files.append(relpath)
return new_files
def get_sim_files(self) -> List[str]:
files = self._get_file_paths("rtl_files") + self._get_file_paths("tb_files")
unique_files = []
[unique_files.append(f) for f in files if f not in unique_files]
return unique_files
def get_synth_files(self) -> List[str]:
return self._get_file_paths("rtl_files")
def get_tb_inst(self, testcase: 'SimTestCase', exporter: 'RegblockExporter') -> str:
class_dir = self._get_class_dir_of_variable("tb_template")
loader = jj.FileSystemLoader(class_dir)
jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
extensions=[SVLineAnchor],
)
context = {
"cpuif": self,
"testcase": testcase,
"exporter": exporter,
"type": type,
}
template = jj_env.get_template(self.tb_template)
return template.render(context)

View File

@@ -0,0 +1,11 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.passthrough import PassthroughCpuif
class Passthrough(CpuifTestMode):
cpuif_cls = PassthroughCpuif
rtl_files = []
tb_files = [
"passthrough_driver.sv",
]
tb_template = "tb_inst.sv"

View File

@@ -0,0 +1,123 @@
interface passthrough_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
output logic m_cpuif_req,
output logic m_cpuif_req_is_wr,
output logic [ADDR_WIDTH-1:0] m_cpuif_addr,
output logic [DATA_WIDTH-1:0] m_cpuif_wr_data,
output logic [DATA_WIDTH-1:0] m_cpuif_wr_biten,
input wire m_cpuif_req_stall_wr,
input wire m_cpuif_req_stall_rd,
input wire m_cpuif_rd_ack,
input wire m_cpuif_rd_err,
input wire [DATA_WIDTH-1:0] m_cpuif_rd_data,
input wire m_cpuif_wr_ack,
input wire m_cpuif_wr_err
);
timeunit 1ps;
timeprecision 1ps;
default clocking cb @(posedge clk);
default input #1step output #1;
output m_cpuif_req;
output m_cpuif_req_is_wr;
output m_cpuif_addr;
output m_cpuif_wr_data;
output m_cpuif_wr_biten;
input m_cpuif_req_stall_wr;
input m_cpuif_req_stall_rd;
input m_cpuif_rd_ack;
input m_cpuif_rd_err;
input m_cpuif_rd_data;
input m_cpuif_wr_ack;
input m_cpuif_wr_err;
endclocking
task automatic reset();
cb.m_cpuif_req <= '0;
cb.m_cpuif_req_is_wr <= '0;
cb.m_cpuif_addr <= '0;
cb.m_cpuif_wr_data <= '0;
cb.m_cpuif_wr_biten <= '0;
endtask
semaphore txn_req_mutex = new(1);
semaphore txn_resp_mutex = new(1);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH-1:0] biten = '1);
fork
begin
// Initiate transfer
txn_req_mutex.get();
##0;
cb.m_cpuif_req <= '1;
cb.m_cpuif_req_is_wr <= '1;
cb.m_cpuif_addr <= addr;
cb.m_cpuif_wr_data <= data;
cb.m_cpuif_wr_biten <= biten;
@(cb);
while(cb.m_cpuif_req_stall_wr !== 1'b0) @(cb);
reset();
txn_req_mutex.put();
end
begin
// Wait for response
txn_resp_mutex.get();
@cb;
while(cb.m_cpuif_wr_ack !== 1'b1) @(cb);
txn_resp_mutex.put();
end
join
endtask
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
fork
begin
// Initiate transfer
txn_req_mutex.get();
##0;
cb.m_cpuif_req <= '1;
cb.m_cpuif_req_is_wr <= '0;
cb.m_cpuif_addr <= addr;
@(cb);
while(cb.m_cpuif_req_stall_rd !== 1'b0) @(cb);
reset();
txn_req_mutex.put();
end
begin
// Wait for response
txn_resp_mutex.get();
@cb;
while(cb.m_cpuif_rd_ack !== 1'b1) @(cb);
assert(!$isunknown(cb.m_cpuif_rd_data)) else $error("Read from 0x%0x returned X's on m_cpuif_rd_data", addr);
data = cb.m_cpuif_rd_data;
txn_resp_mutex.put();
end
join
endtask
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.m_cpuif_rd_ack)) else $error("Saw X on m_cpuif_rd_ack!");
if(!rst) assert(!$isunknown(cb.m_cpuif_wr_ack)) else $error("Saw X on m_cpuif_wr_ack!");
end
endinterface

View File

@@ -0,0 +1,32 @@
{% sv_line_anchor %}
wire s_cpuif_req;
wire s_cpuif_req_is_wr;
wire [{{exporter.cpuif.addr_width-1}}:0] s_cpuif_addr;
wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_data;
wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_wr_biten;
wire s_cpuif_req_stall_wr;
wire s_cpuif_req_stall_rd;
wire s_cpuif_rd_ack;
wire s_cpuif_rd_err;
wire [{{exporter.cpuif.data_width-1}}:0] s_cpuif_rd_data;
wire s_cpuif_wr_ack;
wire s_cpuif_wr_err;
passthrough_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif (
.clk(clk),
.rst(rst),
.m_cpuif_req(s_cpuif_req),
.m_cpuif_req_is_wr(s_cpuif_req_is_wr),
.m_cpuif_addr(s_cpuif_addr),
.m_cpuif_wr_data(s_cpuif_wr_data),
.m_cpuif_wr_biten(s_cpuif_wr_biten),
.m_cpuif_req_stall_wr(s_cpuif_req_stall_wr),
.m_cpuif_req_stall_rd(s_cpuif_req_stall_rd),
.m_cpuif_rd_ack(s_cpuif_rd_ack),
.m_cpuif_rd_err(s_cpuif_rd_err),
.m_cpuif_rd_data(s_cpuif_rd_data),
.m_cpuif_wr_ack(s_cpuif_wr_ack),
.m_cpuif_wr_err(s_cpuif_wr_err)
);