Skip to content

Commit

Permalink
Simulator compatibility updates
Browse files Browse the repository at this point in the history
  • Loading branch information
amykyta3 committed Oct 23, 2023
1 parent d689bb7 commit b5b1ba7
Show file tree
Hide file tree
Showing 29 changed files with 248 additions and 115 deletions.
25 changes: 24 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
def pytest_addoption(parser):
parser.addoption(
"--sim-tool",
choices=["questa", "xilinx", "stub", "skip", "auto"],
choices=["questa", "xsim", "stub", "skip", "auto"],
default="auto",
help="""
Select the simulator to use.
Expand All @@ -12,6 +12,29 @@ def pytest_addoption(parser):
"""
)

parser.addoption(
"--gui",
default=False,
action="store_true",
help=""",
Launch sim tool in GUI mode
Only use this option when running a single test
"""
)


parser.addoption(
"--rerun",
default=False,
action="store_true",
help=""",
Re-run simulation in-place without re-exporting regblock
Useful if hand-editing a testcase interactively.
"""
)

parser.addoption(
"--synth-tool",
choices=["vivado", "skip", "auto"],
Expand Down
10 changes: 10 additions & 0 deletions tests/lib/base_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -114,6 +121,9 @@ def _export_regblock(self):
)

def setUp(self) -> None:
if self.rerun:
return

# Create fresh build dir
run_dir = self.get_run_dir()
if os.path.exists(run_dir):
Expand Down
204 changes: 137 additions & 67 deletions tests/lib/cpuifs/axi4lite/axi4lite_intf_driver.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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

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

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
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);

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

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
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);
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/external_block.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion tests/lib/external_reg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/lib/sim_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ def setUp(self):
super().setUp()

# Create testbench from template
self._generate_tb()
if not self.rerun:
self._generate_tb()

simulator = simulator_cls(self)

Expand Down
4 changes: 2 additions & 2 deletions tests/lib/simulators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

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,
]

Expand Down
4 changes: 4 additions & 0 deletions tests/lib/simulators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def is_installed(cls) -> bool:
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 = []
Expand Down
15 changes: 11 additions & 4 deletions tests/lib/simulators/questa.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,21 @@ def run(self, plusargs:List[str] = None) -> None:
"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)
Expand Down
Loading

0 comments on commit b5b1ba7

Please sign in to comment.