Simulator compatibility updates

This commit is contained in:
Alex Mykyta
2023-10-22 20:43:34 -07:00
parent d689bb7077
commit b5b1ba790e
29 changed files with 244 additions and 111 deletions

View File

@@ -49,6 +49,13 @@ class BaseTestCase(unittest.TestCase):
def _load_request(self, request):
self.request = request
@property
def rerun(self) -> bool:
"""
Re-run wothout deleting and re-generating prior output directory.
"""
return self.request.config.getoption("--rerun")
def get_testcase_dir(self) -> str:
class_dir = os.path.dirname(inspect.getfile(self.__class__))
return class_dir
@@ -114,6 +121,9 @@ class BaseTestCase(unittest.TestCase):
)
def setUp(self) -> None:
if self.rerun:
return
# Create fresh build dir
run_dir = self.get_run_dir()
if os.path.exists(run_dir):

View File

@@ -95,79 +95,148 @@ interface axi4lite_intf_driver #(
@cb;
end
semaphore txn_aw_mutex = new(1);
semaphore txn_w_mutex = new(1);
semaphore txn_b_mutex = new(1);
semaphore txn_ar_mutex = new(1);
semaphore txn_r_mutex = new(1);
//--------------------------------------------------------------------------
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);
bit w_before_aw;
w_before_aw = $urandom_range(1,0);
write_request_t req;
write_response_t resp;
fork
begin
txn_aw_mutex.get();
##0;
if(w_before_aw) repeat($urandom_range(2,0)) @cb;
cb.AWVALID <= '1;
cb.AWADDR <= addr;
cb.AWPROT <= '0;
@(cb);
while(cb.AWREADY !== 1'b1) @(cb);
cb.AWVALID <= '0;
txn_aw_mutex.put();
end
req = new();
req.addr = addr;
req.data = data;
req.strb = strb;
begin
txn_w_mutex.get();
##0;
if(!w_before_aw) repeat($urandom_range(2,0)) @cb;
cb.WVALID <= '1;
cb.WDATA <= data;
cb.WSTRB <= strb;
@(cb);
while(cb.WREADY !== 1'b1) @(cb);
cb.WVALID <= '0;
cb.WSTRB <= '0;
txn_w_mutex.put();
end
aw_mbx.put(req);
w_mbx.put(req);
write_queue.push_back(req);
begin
txn_b_mutex.get();
@cb;
while(!(cb.BREADY === 1'b1 && cb.BVALID === 1'b1)) @(cb);
assert(!$isunknown(cb.BRESP)) else $error("Read from 0x%0x returned X's on BRESP", addr);
txn_b_mutex.put();
end
join
// 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;
fork
begin
txn_ar_mutex.get();
##0;
cb.ARVALID <= '1;
cb.ARADDR <= addr;
cb.ARPROT <= '0;
@(cb);
while(cb.ARREADY !== 1'b1) @(cb);
cb.ARVALID <= '0;
txn_ar_mutex.put();
end
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;
begin
txn_r_mutex.get();
@cb;
while(!(cb.RREADY === 1'b1 && cb.RVALID === 1'b1)) @(cb);
assert(!$isunknown(cb.RDATA)) else $error("Read from 0x%0x returned X's on RDATA", addr);
assert(!$isunknown(cb.RRESP)) else $error("Read from 0x%0x returned X's on RRESP", addr);
data = cb.RDATA;
txn_r_mutex.put();
end
join
// 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);
@@ -177,6 +246,7 @@ interface axi4lite_intf_driver #(
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

View File

@@ -14,7 +14,7 @@ module external_block #(
output logic [WIDTH-1:0] rd_data,
output logic wr_ack
);
timeunit 1ns;
timeunit 1ps;
timeprecision 1ps;
localparam ADDR_SHIFT = $clog2(WIDTH/8);

View File

@@ -13,7 +13,7 @@ module external_reg #(
output logic [WIDTH-1:0] rd_data,
output logic wr_ack
);
timeunit 1ns;
timeunit 1ps;
timeprecision 1ps;
logic [SUBWORDS-1:0][WIDTH-1:0] value;
@@ -69,6 +69,7 @@ initial begin
#1ns;
if(!rst && req) begin
$info("got request");
if(req_is_wr) do_write(req, wr_data, wr_biten);
else do_read(req);
end

View File

@@ -76,7 +76,8 @@ class SimTestCase(BaseTestCase):
super().setUp()
# Create testbench from template
self._generate_tb()
if not self.rerun:
self._generate_tb()
simulator = simulator_cls(self)

View File

@@ -3,13 +3,13 @@ import functools
from .base import Simulator
from .questa import Questa
from .xilinx import Xilinx
from .xilinx import XilinxXSIM
from .stub import StubSimulator
ALL_SIMULATORS: List[Simulator]
ALL_SIMULATORS = [
Questa,
Xilinx,
XilinxXSIM,
StubSimulator,
]

View File

@@ -13,6 +13,10 @@ class Simulator:
def __init__(self, testcase: 'SimTestCase' = None) -> None:
self.testcase = testcase
@property
def gui_mode(self) -> bool:
return self.testcase.request.config.getoption("--gui")
@property
def tb_files(self) -> List[str]:
files = []

View File

@@ -48,14 +48,21 @@ class Questa(Simulator):
"vsim", "-quiet",
"-voptargs=+acc",
"-msgmode", "both",
"-do", "set WildcardFilter [lsearch -not -all -inline $WildcardFilter Memory]",
"-do", "log -r /*;",
"-do", "run -all; exit;",
"-c",
"-l", "%s.log" % test_name,
"-wlf", "%s.wlf" % test_name,
"tb",
"-do", "set WildcardFilter [lsearch -not -all -inline $WildcardFilter Memory]",
"-do", "log -r /*;",
]
if self.gui_mode:
cmd.append("-i")
else:
cmd.extend([
"-do", "run -all; exit;",
"-c",
])
for plusarg in plusargs:
cmd.append("+" + plusarg)
subprocess.run(cmd, check=True)

View File

@@ -5,17 +5,17 @@ import shutil
from .base import Simulator
class Xilinx(Simulator):
class XilinxXSIM(Simulator):
"""
Don't bother using the Xilinx simulator... Its buggy and extraordinarily slow.
Avoid using the Xilinx simulator... Its buggy and extraordinarily slow.
As observed in v2023.2:
- clocking block assignments do not seem to actually simulate correctly.
assignment statements get ignored or the values get mangled.
- Streaming operators have all sorts of limitations.
Keeping this here in case someday it works better...
- Clocking block assignments to struct members do not simulate correctly.
assignment statements get lost.
https://support.xilinx.com/s/question/0D54U00007ZIGfXSAX/xsim-bug-xsim-does-not-simulate-struct-assignments-in-clocking-blocks-correctly?language=en_US
- Streaming bit-swap within a conditional returns a corrupted value
https://support.xilinx.com/s/question/0D54U00007ZIIBPSA5/xsim-bug-xsim-corrupts-value-of-signal-that-is-bitswapped-within-a-conditional-operator?language=en_US
"""
name = "xilinx"
name = "xsim"
@classmethod
def is_installed(cls) -> bool:
@@ -30,7 +30,7 @@ class Xilinx(Simulator):
"xvlog", "--sv",
"--log", "compile.log",
"--include", os.path.join(os.path.dirname(__file__), ".."),
"--define", "XSIM",
"--define", "XILINX_XSIM",
]
cmd.extend(self.tb_files)
subprocess.run(cmd, check=True)
@@ -38,7 +38,7 @@ class Xilinx(Simulator):
cmd = [
"xelab",
"--log", "elaborate.log",
"--timescale", "1ns/1ps",
"--timescale", "1ps/1ps",
"--debug", "all",
"tb",
]
@@ -50,13 +50,17 @@ class Xilinx(Simulator):
test_name = self.testcase.request.node.name
# call vsim
cmd = [
"xsim",
"--R",
# call xsim
cmd = ["xsim"]
if self.gui_mode:
cmd.append("--gui")
else:
cmd.append("-R")
cmd.extend([
"--log", "%s.log" % test_name,
"tb",
]
])
for plusarg in plusargs:
cmd.append("--testplusarg")

View File

@@ -1,8 +1,10 @@
{% sv_line_anchor %}
module tb;
timeunit 1ns;
timeunit 10ps;
timeprecision 1ps;
`define bitswap(x) ($bits(x))'({<<{x}})
logic rst = '1;
logic clk = '0;
initial forever begin