Simplify test suite to pytest unit scaffolding

This commit is contained in:
Arnav Sacheti
2025-10-19 16:48:25 -07:00
parent bb1ac6bde9
commit cca98098da
176 changed files with 71 additions and 7599 deletions

View File

@@ -1,119 +1,27 @@
# Unit tests
# Test Dependencies
The bus decoder exporter now ships with a small unit test suite built around
`pytest`. The tests exercise the Python implementation directly and use the
[`systemrdl-compiler`](https://github.com/SystemRDL/systemrdl-compiler)
package to elaborate inline SystemRDL snippets.
## Questa
## Install dependencies
Testcases require an installation of the Questa simulator, and for `vlog` & `vsim`
commands to be visible via the PATH environment variable.
*Questa - Intel FPGA Starter Edition* can be downloaded for free from Intel:
* Go to https://www.intel.com/content/www/us/en/collections/products/fpga/software/downloads.html?edition=pro&q=questa&s=Relevancy
* Select latest version of Questa
* Download Questa files.
* Install
* Be sure to choose "Starter Edition" for the free version.
* Create an account on https://licensing.intel.com
* press "Enroll" to register
* After you confirm your email, go back to this page and press "Enroll" again to finish enrollment
* Go to https://licensing.intel.com/psg/s/sales-signup-evaluationlicenses
* Generate a free *Starter Edition* license file for Questa
* Easiest to use a *fixed* license using your NIC ID (MAC address of your network card via `ifconfig`)
* Download the license file and point the `LM_LICENSE_FILE` environment variable to the folder which contains it.
* (optional) Delete Intel libraries to save some disk space
* Delete `<install_dir>/questa_fse/intel`
* Edit `<install_dir>/questa_fse/modelsim.ini` and remove lines that reference the `intel` libraries
## Vivado (optional)
To run synthesis tests, Vivado needs to be installed and visible via the PATH environment variable.
Vivado can be downloaded for free from: https://www.xilinx.com/support/download.html
## Python Packages
Install dependencies required for running tests
Create an isolated environment if desired and install the minimal requirements:
```bash
python3 -m pip install -r tests/requirements.txt
python -m pip install -r tests/requirements.txt
```
## Running the suite
Invoke `pytest` from the repository root (or the `tests` directory) and point it
at the unit tests:
# Running tests
Tests can be launched from the test directory using `pytest`.
Use `pytest --workers auto` to run tests in parallel.
To run all tests:
```bash
python3 setup.py install
pytest tests
pytest tests/unit
```
You can also run a specific testcase. For example:
```bash
pytest tests/test_hw_access
```
Command-line arguments can be used to explicitly select which simulator/synthesis tools are used
If unspecified, the tool will be selected automatically based on what you have installed.
```bash
pytest --sim-tool questa --synth-tool vivado
```
Alternatively, launch tests using the helper script. This handles installing
dependencies into a virtual environment automatically.
```bash
cd tests
./run.sh
```
# Test organization
The goal for this test infrastructure is to make it easy to add small-standalone
testcases, with minimal repetition/boilerplate code that is usually present in
SystemVerilog testbenches.
To accomplish this, Jinja templates are used extensively to generate the
resulting `tb.sv` file, as well as assist in dynamic testcase parameterization.
## CPU Interfaces
Each CPU interface type is described in its own folder as follows:
`lib/cpuifs/<type>/__init__.py`
: Definitions for CPU Interface test mode classes.
`lib/cpuifs/<type>/tb_inst.sv`
: Jinja template that defines how the CPU interface is declared & instantiated in the testbench file.
`lib/cpuifs/<type>/*.sv`
: Any other files required for compilation.
## Testcase
Each testcase group has its own folder and contains the following:
`test_*/__init__.py`
: Empty file required for test discovery.
`test_*/busdecoder.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the busdecoder output SystemVerilog.
`test_*/tb_template.sv`
: Jinja template that defines the testcase-specific sequence.
`test_*/testcase.py`
: Defines Python unittest testcase entry point.
## Parameterization
Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of busdecoder export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations.
Pytest will automatically discover tests that follow the `test_*.py` naming
pattern and can make use of the `compile_rdl` fixture defined in
`tests/unit/conftest.py` to compile inline SystemRDL sources.

View File

@@ -1,47 +0,0 @@
def pytest_addoption(parser):
parser.addoption(
"--sim-tool",
choices=["questa", "xsim", "stub", "skip", "auto"],
default="auto",
help="""
Select the simulator to use.
stub: run the testcase using a no-op simulator stub
skip: skip all the simulation tests
auto: choose the best simulator based on what is installed
""",
)
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 busdecoder
Useful if hand-editing a testcase interactively.
""",
)
parser.addoption(
"--synth-tool",
choices=["vivado", "skip", "auto"],
default="auto",
help="""
Select the synthesis tool to use.
skip: skip all the simulation tests
auto: choose the best tool based on what is installed
""",
)

View File

View File

@@ -1,139 +0,0 @@
from typing import Optional
import unittest
import os
import glob
import shutil
import inspect
import pathlib
import pytest
from systemrdl import RDLCompiler
from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_busdecoder.udps import ALL_UDPS
from .cpuifs.base import CpuifTestMode
from .cpuifs.apb4 import APB4
class BaseTestCase(unittest.TestCase):
#: Path to the testcase's RDL file.
#: Relative to the testcase's dir. If unset, the first RDL file found in the
#: testcase dir will be used
rdl_file = None # type: Optional[str]
#: RDL type name to elaborate. If unset, compiler will automatically choose
#: the top.
rdl_elab_target = None # type: Optional[str]
#: Parameters to pass into RDL elaboration
rdl_elab_params = {}
#: Define what CPUIF to use for this testcase
cpuif = APB4() # type: CpuifTestMode
# Other exporter args:
retime_read_fanin = False
retime_read_response = False
reuse_hwif_typedefs = True
retime_external = False
default_reset_activelow = False
default_reset_async = False
#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
exporter = BusDecoderExporter()
@pytest.fixture(autouse=True)
def _load_request(self, request):
self.request = request
@property
def rerun(self) -> bool:
"""
Re-run without 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
def get_run_dir(self) -> str:
this_dir = self.get_testcase_dir()
run_dir = os.path.join(this_dir, "run.out", self.__class__.__name__)
return run_dir
def _write_params(self) -> None:
"""
Write out the class parameters to a file so that it is easier to debug
how a testcase was parameterized
"""
path = os.path.join(self.get_run_dir(), "params.txt")
with open(path, "w") as f:
for k, v in self.__class__.__dict__.items():
if k.startswith("_") or callable(v):
continue
f.write(f"{k}: {repr(v)}\n")
def export_busdecoder(self):
"""
Call the peakrdl_busdecoder exporter to generate the DUT
"""
this_dir = self.get_testcase_dir()
if self.rdl_file:
rdl_file = os.path.join(this_dir, self.rdl_file)
else:
# Find any *.rdl file in testcase dir
rdl_file = glob.glob(os.path.join(this_dir, "*.rdl"))[0]
rdlc = RDLCompiler()
# Load the UDPs
for udp in ALL_UDPS:
rdlc.register_udp(udp)
# ... including the definition
udp_file = os.path.join(this_dir, "../../hdl-src/busdecoder_udps.rdl")
rdlc.compile_file(udp_file)
rdlc.compile_file(rdl_file)
root = rdlc.elaborate(self.rdl_elab_target, "busdecoder", self.rdl_elab_params)
self.exporter.export(
root,
self.get_run_dir(),
module_name="busdecoder",
package_name="busdecoder_pkg",
cpuif_cls=self.cpuif.cpuif_cls,
retime_read_fanin=self.retime_read_fanin,
retime_read_response=self.retime_read_response,
reuse_hwif_typedefs=self.reuse_hwif_typedefs,
retime_external_reg=self.retime_external,
retime_external_regfile=self.retime_external,
retime_external_mem=self.retime_external,
retime_external_addrmap=self.retime_external,
default_reset_activelow=self.default_reset_activelow,
default_reset_async=self.default_reset_async,
)
def delete_run_dir(self) -> None:
run_dir = self.get_run_dir()
if os.path.exists(run_dir):
shutil.rmtree(run_dir)
def setUp(self) -> None:
if self.rerun:
return
# Create fresh build dir
run_dir = self.get_run_dir()
self.delete_run_dir()
pathlib.Path(run_dir).mkdir(parents=True, exist_ok=True)
self._write_params()
# Convert testcase RDL file --> SV
self.export_busdecoder()

View File

@@ -1,17 +0,0 @@
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

@@ -1,20 +0,0 @@
from ..base import CpuifTestMode
from peakrdl_busdecoder.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

@@ -1,116 +0,0 @@
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

@@ -1,32 +0,0 @@
{% 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

@@ -1,20 +0,0 @@
from ..base import CpuifTestMode
from peakrdl_busdecoder.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

@@ -1,128 +0,0 @@
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

@@ -1,36 +0,0 @@
{% 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

@@ -1,20 +0,0 @@
from ..base import CpuifTestMode
from peakrdl_busdecoder.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

@@ -1,138 +0,0 @@
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

@@ -1,36 +0,0 @@
{% 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

@@ -1,20 +0,0 @@
from ..base import CpuifTestMode
from peakrdl_busdecoder.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

@@ -1,263 +0,0 @@
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

@@ -1,54 +0,0 @@
{% 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 %}

View File

@@ -1,82 +0,0 @@
from typing import List, TYPE_CHECKING
import os
import inspect
import jinja2 as jj
from peakrdl_busdecoder.cpuif.base_cpuif import CpuifBase
from ..sv_line_anchor import SVLineAnchor
if TYPE_CHECKING:
from peakrdl_busdecoder import BusDecoderExporter
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: "BusDecoderExporter"
) -> 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

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

View File

@@ -1,123 +0,0 @@
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

@@ -1,32 +0,0 @@
{% 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)
);

View File

@@ -1,73 +0,0 @@
module external_block #(
parameter WIDTH = 32,
parameter ADDR_WIDTH = 8
)(
input wire clk,
input wire rst,
input wire req,
input wire req_is_wr,
input wire [ADDR_WIDTH-1:0] addr,
input wire [WIDTH-1:0] wr_data,
input wire [WIDTH-1:0] wr_biten,
output logic rd_ack,
output logic [WIDTH-1:0] rd_data,
output logic wr_ack
);
timeunit 1ps;
timeprecision 1ps;
localparam ADDR_SHIFT = $clog2(WIDTH/8);
localparam N_ENTRIES = 2**(ADDR_WIDTH - ADDR_SHIFT);
logic [WIDTH-1:0] mem[N_ENTRIES];
task do_write(int idx, logic [WIDTH-1:0] data, logic [WIDTH-1:0] biten);
automatic int delay;
// Random delay
delay = $urandom_range(3,0);
repeat(delay) @(posedge clk)
$info("Write delay: %d", delay);
for(int b=0; b<WIDTH; b++) begin
if(biten[b]) mem[idx][b] <= data[b];
end
wr_ack <= '1;
endtask
task do_read(int idx);
automatic int delay;
// Random delay
delay = $urandom_range(3,0);
repeat(delay) @(posedge clk)
$info("Read delay: %d", delay);
rd_data <= mem[idx];
rd_ack <= '1;
endtask;
initial begin
rd_ack <= '0;
rd_data <= '0;
wr_ack <= '0;
for(int i=0; i<N_ENTRIES; i++) mem[i] <= '0;
forever begin
// Wait for next clock edge
@(posedge clk);
rd_ack <= '0;
rd_data <= '0;
wr_ack <= '0;
// wait slightly longer to "peek" at the current cycle's state
#1ns;
if(!rst && req) begin
if(req_is_wr) do_write(addr >> ADDR_SHIFT, wr_data, wr_biten);
else do_read(addr >> ADDR_SHIFT);
end
end
end
endmodule

View File

@@ -1,79 +0,0 @@
module external_reg #(
parameter WIDTH = 32,
parameter SUBWORDS = 1
)(
input wire clk,
input wire rst,
input wire [SUBWORDS-1:0] req,
input wire req_is_wr,
input wire [WIDTH-1:0] wr_data,
input wire [WIDTH-1:0] wr_biten,
output logic rd_ack,
output logic [WIDTH-1:0] rd_data,
output logic wr_ack
);
timeunit 1ps;
timeprecision 1ps;
logic [SUBWORDS-1:0][WIDTH-1:0] value;
task do_write(logic [SUBWORDS-1:0] strb, logic [WIDTH-1:0] data, logic [WIDTH-1:0] biten);
automatic int delay;
// Random delay
delay = $urandom_range(3,0);
repeat(delay) @(posedge clk)
$info("Write delay: %d", delay);
for(int i=0; i<SUBWORDS; i++) begin
if(strb[i]) begin
for(int b=0; b<WIDTH; b++) begin
if(biten[b]) value[i][b] <= data[b];
end
end
end
wr_ack <= '1;
endtask
task do_read(logic [SUBWORDS-1:0] strb);
automatic int delay;
// Random delay
delay = $urandom_range(3,0);
repeat(delay) @(posedge clk)
$info("Read delay: %d", delay);
for(int i=0; i<SUBWORDS; i++) begin
if(strb[i]) begin
rd_data <= value[i];
end
end
rd_ack <= '1;
endtask;
initial begin
rd_ack <= '0;
rd_data <= '0;
wr_ack <= '0;
value <= '0;
forever begin
// Wait for next clock edge
@(posedge clk);
rd_ack <= '0;
rd_data <= '0;
wr_ack <= '0;
// wait slightly longer to "peek" at the current cycle's state
#1ns;
if(!rst && req) begin
$info("got request");
if(req_is_wr) do_write(req, wr_data, wr_biten);
else do_read(req);
end
end
end
endmodule

View File

@@ -1,107 +0,0 @@
from typing import List
import os
import jinja2 as jj
import pytest
from .sv_line_anchor import SVLineAnchor
from .simulators import get_simulator_cls
from .base_testcase import BaseTestCase
class SimTestCase(BaseTestCase):
#: Abort test if it exceeds this number of clock cycles
timeout_clk_cycles = 5000
incompatible_sim_tools = set()
tb_template_file = "tb_template.sv"
# Paths are relative to the testcase dir
extra_tb_files = [] # type: List[str]
# Whether to initialize the hwif_in struct at test startup
init_hwif_in = True
# Control whether to include in clocking block
clocking_hwif_in = True
clocking_hwif_out = True
def get_extra_tb_files(self) -> List[str]:
paths = []
for path in self.extra_tb_files:
path = os.path.join(self.get_testcase_dir(), path)
paths.append(path)
return paths
def _generate_tb(self):
"""
Render the testbench template into actual tb.sv
"""
template_root_path = os.path.join(os.path.dirname(__file__), "..")
loader = jj.FileSystemLoader(
template_root_path
)
jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
extensions=[SVLineAnchor],
)
context = {
"testcase": self,
"exporter": self.exporter,
}
# template path needs to be relative to the Jinja loader root
template_path = os.path.join(self.get_testcase_dir(), self.tb_template_file)
template_path = os.path.relpath(template_path, template_root_path)
template = jj_env.get_template(template_path)
output_path = os.path.join(self.get_run_dir(), "tb.sv")
stream = template.stream(context)
stream.dump(output_path)
def setUp(self):
name = self.request.config.getoption("--sim-tool")
if name in self.incompatible_sim_tools:
pytest.skip()
simulator_cls = get_simulator_cls(name)
if simulator_cls is None:
pytest.skip()
super().setUp()
# Create testbench from template
if not self.rerun:
self._generate_tb()
simulator = simulator_cls(self)
# cd into the build directory
cwd = os.getcwd()
os.chdir(self.get_run_dir())
try:
simulator.compile()
finally:
# cd back
os.chdir(cwd)
def run_test(self, plusargs:List[str] = None) -> None:
name = self.request.config.getoption("--sim-tool")
simulator_cls = get_simulator_cls(name)
simulator = simulator_cls(self)
# cd into the build directory
cwd = os.getcwd()
os.chdir(self.get_run_dir())
try:
simulator.run(plusargs)
finally:
# cd back
os.chdir(cwd)

View File

@@ -1,39 +0,0 @@
from typing import Type, Optional, List
import functools
from .base import Simulator
from .questa import Questa
from .xilinx import XilinxXSIM
from .xcelium import Xcelium
from .stub import StubSimulator
ALL_SIMULATORS: List[Simulator]
ALL_SIMULATORS = [
Questa,
XilinxXSIM,
Xcelium,
StubSimulator,
]
@functools.lru_cache()
def get_simulator_cls(name: str) -> Optional[Type[Simulator]]:
if name == "skip":
return None
if name == "auto":
# Find the first simulator that is installed
for sim_cls in ALL_SIMULATORS:
if sim_cls is StubSimulator:
# Never offer the stub as an automatic option
continue
if sim_cls.is_installed():
return sim_cls
raise ValueError("Could not find any installed simulators")
# Look up which explicit simulator name was specified
for sim_cls in ALL_SIMULATORS:
if sim_cls.name == name:
if not sim_cls.is_installed():
raise ValueError("Simulator '%s' is not installed" % sim_cls.name)
return sim_cls
raise RuntimeError

View File

@@ -1,36 +0,0 @@
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from ..sim_testcase import SimTestCase
class Simulator:
name = ""
@classmethod
def is_installed(cls) -> bool:
raise NotImplementedError
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 = []
files.extend(self.testcase.cpuif.get_sim_files())
files.extend(self.testcase.get_extra_tb_files())
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
files.append("tb.sv")
return files
def compile(self) -> None:
raise NotImplementedError
def run(self, plusargs: List[str] = None) -> None:
raise NotImplementedError

View File

@@ -1,84 +0,0 @@
from typing import List
import subprocess
import os
import shutil
from .base import Simulator
class Questa(Simulator):
name = "questa"
@classmethod
def is_installed(cls) -> bool:
return (
shutil.which("vlog") is not None
and shutil.which("vsim") is not None
)
def compile(self) -> None:
cmd = [
"vlog", "-sv", "-quiet", "-l", "build.log",
"+incdir+%s" % os.path.join(os.path.dirname(__file__), ".."),
# Use strict LRM conformance
"-svinputport=net",
# all warnings are errors
"-warning", "error",
# Ignore noisy warning about vopt-time checking of always_comb/always_latch
"-suppress", "2583",
# Suppress "error" about use of the `line directive
"-suppress", "13465",
]
# Add source files
cmd.extend(self.tb_files)
# Run command!
subprocess.run(cmd, check=True)
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase.request.node.name
# call vsim
cmd = [
"vsim", "-quiet",
"-voptargs=+acc",
"-msgmode", "both",
"-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)
self.assertSimLogPass("%s.log" % test_name)
def assertSimLogPass(self, path: str):
self.testcase.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("# ** Error"):
self.testcase.fail(line)
elif line.startswith("# ** Fatal"):
self.testcase.fail(line)

View File

@@ -1,17 +0,0 @@
from typing import List
from .base import Simulator
class StubSimulator(Simulator):
name = "stub"
@classmethod
def is_installed(cls) -> bool:
# Always available!
return True
def compile(self) -> None:
pass
def run(self, plusargs: List[str] = None) -> None:
pass

View File

@@ -1,86 +0,0 @@
from typing import List
import subprocess
import os
import shutil
from .base import Simulator
class Xcelium(Simulator):
"""
Don't use the Xcelium simulator, it is unable to compile & run any testcases.
As observed in 25.03.006:
- Using unpacked structs with more than 2 levels of nesting in clocking blocks is not
supported.
"""
name = "xcelium"
@classmethod
def is_installed(cls) -> bool:
return (
shutil.which("xrun") is not None
)
def compile(self) -> None:
# Compile and elaborate into a snapshot
cmd = [
"xrun",
"-64bit",
"-elaborate",
"-sv",
"-log build.log",
"-timescale 10ps/1ps",
"-ENABLE_DS_UNPS", # Allow ".*" DUT connection with unpacked structs
"-nowarn LNDER6", # Suppress warning about the `line 2 "lib/tb_base.sv" 0 file location
"-nowarn SPDUSD", # Suppress warning about unused include directory
"-incdir %s" % os.path.join(os.path.dirname(__file__), "..")
]
if self.gui_mode:
cmd.append("-access +rwc")
# Add source files
cmd.extend(self.tb_files)
# Run command!
subprocess.run(cmd, check=True)
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase.request.node.name
# Call xrun on the elaborated snapshot
cmd = [
"xrun",
"-64bit",
"-log %s.log" % test_name,
"-r worklib.tb:sv"
]
if self.gui_mode:
cmd.append("-gui")
cmd.append('-input "@database -open waves -into waves.shm -shm -default -event"')
cmd.append('-input "@probe -create tb -depth all -tasks -functions -all -packed 4k \
-unpacked 16k -memories -dynamic -variables -database waves"')
else:
cmd.extend([
"-input", "@run",
])
for plusarg in plusargs:
cmd.append("+" + plusarg)
subprocess.run(cmd, check=True)
self.assertSimLogPass("%s.log" % test_name)
def assertSimLogPass(self, path: str):
self.testcase.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("xmsim: *E"):
self.testcase.fail(line)
elif line.startswith("xmsim: *F"):
self.testcase.fail(line)

View File

@@ -1,74 +0,0 @@
from typing import List
import subprocess
import os
import shutil
from .base import Simulator
class XilinxXSIM(Simulator):
name = "xsim"
@classmethod
def is_installed(cls) -> bool:
return (
shutil.which("xvlog") is not None
and shutil.which("xelab") is not None
and shutil.which("xsim") is not None
)
def compile(self) -> None:
cmd = [
"xvlog", "--sv",
"--log", "compile.log",
"--include", os.path.join(os.path.dirname(__file__), ".."),
"--define", "XILINX_XSIM",
]
cmd.extend(self.tb_files)
subprocess.run(cmd, check=True)
cmd = [
"xelab",
"--log", "elaborate.log",
"--timescale", "1ps/1ps",
"--debug", "all",
"tb",
]
subprocess.run(cmd, check=True)
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase.request.node.name
# 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")
cmd.append(plusarg)
subprocess.run(cmd, check=True)
self.assertSimLogPass("%s.log" % test_name)
def assertSimLogPass(self, path: str):
self.testcase.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("Error:"):
self.testcase.fail(line)
elif line.startswith("Fatal:"):
self.testcase.fail(line)
elif line.startswith("FATAL_ERROR:"):
self.testcase.fail(line)

View File

@@ -1,10 +0,0 @@
from jinja2_simple_tags import StandaloneTag
class SVLineAnchor(StandaloneTag):
"""
Define a custom Jinja tag that emits a SystemVerilog `line directive so that
assertion messages can get properly back-annotated
"""
tags = {"sv_line_anchor"}
def render(self):
return f'`line {self.lineno + 1} "{self.template}" 0'

View File

@@ -1,39 +0,0 @@
from typing import List
import os
import pytest
from .base_testcase import BaseTestCase
from .synthesizers import get_synthesizer_cls
class SynthTestCase(BaseTestCase):
def _get_synth_files(self) -> List[str]:
files = []
files.extend(self.cpuif.get_synth_files())
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
return files
def setUp(self) -> None:
name = self.request.config.getoption("--synth-tool")
synth_cls = get_synthesizer_cls(name)
if synth_cls is None:
pytest.skip()
super().setUp()
def run_synth(self) -> None:
name = self.request.config.getoption("--synth-tool")
synth_cls = get_synthesizer_cls(name)
synth = synth_cls(self)
# cd into the build directory
cwd = os.getcwd()
os.chdir(self.get_run_dir())
try:
synth.run()
finally:
# cd back
os.chdir(cwd)

View File

@@ -1,30 +0,0 @@
from typing import List, Optional, Type
import functools
from .base import Synthesizer
from .vivado import Vivado
ALL_SYNTHESIZERS: List[Synthesizer]
ALL_SYNTHESIZERS = [
Vivado,
]
@functools.lru_cache()
def get_synthesizer_cls(name: str) -> Optional[Type[Synthesizer]]:
if name == "skip":
return None
if name == "auto":
# Find the first tool that is installed
for synth_cls in ALL_SYNTHESIZERS:
if synth_cls.is_installed():
return synth_cls
raise ValueError("Could not find any installed synthesis tools")
# Look up which explicit synth tool name was specified
for synth_cls in ALL_SYNTHESIZERS:
if synth_cls.name == name:
if not synth_cls.is_installed():
raise ValueError("Synthesis tool '%s' is not installed" % synth_cls.name)
return synth_cls
raise RuntimeError

View File

@@ -1,17 +0,0 @@
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from ..synth_testcase import SynthTestCase
class Synthesizer:
name = ""
@classmethod
def is_installed(cls) -> bool:
raise NotImplementedError
def __init__(self, testcase: 'SynthTestCase' = None) -> None:
self.testcase = testcase
def run(self) -> None:
raise NotImplementedError

View File

@@ -1,29 +0,0 @@
import os
import subprocess
import shutil
from .base import Synthesizer
class Vivado(Synthesizer):
name = "vivado"
@classmethod
def is_installed(cls) -> bool:
return shutil.which("vivado") is not None
def run(self) -> None:
script = os.path.join(
os.path.dirname(__file__),
"vivado_scripts/run.tcl"
)
cmd = [
"vivado", "-nojournal", "-notrace",
"-mode", "batch",
"-log", "out.log",
"-source", script,
"-tclargs"
]
cmd.extend(self.testcase._get_synth_files())
subprocess.run(cmd, check=True)

View File

@@ -1,8 +0,0 @@
create_clock -period 10.000 -name clk [get_ports clk]
set_input_delay -clock [get_clocks clk] -min -add_delay 0.000 [get_ports -filter {(DIRECTION == IN) && (NAME != clk)}]
set_input_delay -clock [get_clocks clk] -max -add_delay 0.000 [get_ports -filter {(DIRECTION == IN) && (NAME != clk)}]
set_output_delay -clock [get_clocks clk] -min -add_delay 0.000 [get_ports -filter {DIRECTION == OUT}]
set_output_delay -clock [get_clocks clk] -max -add_delay 0.000 [get_ports -filter {DIRECTION == OUT}]

View File

@@ -1,34 +0,0 @@
set this_dir [file dirname [file normalize [info script]]]
set files $argv
# Multi-driven
set_msg_config -id {[Synth 8-6858]} -new_severity "ERROR"
set_msg_config -id {[Synth 8-6859]} -new_severity "ERROR"
# Implicit net
set_msg_config -id {[Synth 8-992]} -new_severity "ERROR"
# Non-combo always_comb
set_msg_config -id {[Synth 8-87]} -new_severity "ERROR"
# Latch
set_msg_config -id {[Synth 8-327]} -new_severity "ERROR"
# Timing loop
set_msg_config -id {[Synth 8-295]} -new_severity "ERROR"
# Promote all critical warnings to errors
set_msg_config -severity {CRITICAL WARNING} -new_severity "ERROR"
set_part [lindex [get_parts] 0]
read_verilog -sv $files
read_xdc $this_dir/constr.xdc
synth_design -top busdecoder -mode out_of_context
#write_checkpoint -force synth.dcp
if {[get_msg_config -count -severity {CRITICAL WARNING}] || [get_msg_config -count -severity ERROR]} {
error "Encountered errors"
}

View File

@@ -1,128 +0,0 @@
{% sv_line_anchor %}
module tb;
timeunit 10ps;
timeprecision 1ps;
class bitswap_cls #(W=1);
static function logic [W-1:0] bitswap(logic [W-1:0] x);
logic [W-1:0] result;
result = {<<{x}};
return result;
endfunction
endclass
`define bitswap(x) (bitswap_cls#($bits(x))::bitswap(x))
logic rst = '1;
logic clk = '0;
initial forever begin
#5ns;
clk = ~clk;
end
logic rst_n, arst, arst_n;
assign rst_n = ~rst;
assign arst = rst;
assign arst_n = ~rst;
//--------------------------------------------------------------------------
// DUT Signal declarations
//--------------------------------------------------------------------------
{%- if exporter.hwif.has_input_struct %}
busdecoder_pkg::busdecoder__in_t hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct %}
busdecoder_pkg::busdecoder__out_t hwif_out;
{%- endif %}
{%- if exporter.ds.has_paritycheck %}
logic parity_error;
{%- endif %}
{%- block declarations %}
{%- endblock %}
//--------------------------------------------------------------------------
// Clocking
//--------------------------------------------------------------------------
default clocking cb @(posedge clk);
default input #1step output #1;
output rst;
{%- if exporter.hwif.has_input_struct and testcase.clocking_hwif_in %}
output hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct and testcase.clocking_hwif_out %}
input hwif_out;
{%- endif %}
{%- if exporter.ds.has_paritycheck %}
input parity_error;
{%- endif %}
{%- filter indent %}
{%- block clocking_dirs %}
{%- endblock %}
{%- endfilter %}
endclocking
//--------------------------------------------------------------------------
// CPUIF
//--------------------------------------------------------------------------
{{testcase.cpuif.get_tb_inst(testcase, exporter)|indent}}
//--------------------------------------------------------------------------
// DUT
//--------------------------------------------------------------------------
{% sv_line_anchor %}
busdecoder dut (.*);
{%- if exporter.hwif.has_output_struct %}
{% sv_line_anchor %}
initial begin
logic [$bits(hwif_out)-1:0] tmp;
forever begin
##1;
tmp = {>>{hwif_out}}; // Workaround for Xilinx's xsim - assign to tmp variable
if(!rst) assert(!$isunknown(tmp)) else $error("hwif_out has X's!");
{%- if exporter.ds.has_paritycheck %}
if(!rst) assert(!$isunknown(parity_error)) else $error("parity_error has X's!");
{%- endif %}
end
end
{%- endif %}
{% sv_line_anchor %}
{%- block dut_support %}
{%- endblock %}
//--------------------------------------------------------------------------
// Test Sequence
//--------------------------------------------------------------------------
initial begin
cb.rst <= '1;
{%- if exporter.hwif.has_input_struct and testcase.init_hwif_in %}
cb.hwif_in <= '{default: '0};
{%- endif %}
begin
{%- filter indent(8) %}
{%- block seq %}
{%- endblock %}
{%- endfilter %}
end
{% sv_line_anchor %}
##5;
$finish();
end
//--------------------------------------------------------------------------
// Monitor for timeout
//--------------------------------------------------------------------------
initial begin
##{{testcase.timeout_clk_cycles}};
$fatal(1, "Test timed out after {{testcase.timeout_clk_cycles}} clock cycles");
end
endmodule

View File

@@ -1,7 +0,0 @@
from itertools import product
def get_permutations(spec):
param_list = []
for v in product(*spec.values()):
param_list.append(dict(zip(spec, v)))
return param_list

View File

@@ -1,2 +1,3 @@
[pytest]
testpaths = unit
python_files = test_*.py testcase.py

View File

@@ -1,11 +1,3 @@
# hold back: https://github.com/kevlened/pytest-parallel/issues/118
pytest<7.2
parameterized
pytest-parallel # TODO: deprecated. migrate to pytest-xdist
jinja2-simple-tags
pylint
mypy
pytest-cov
coveralls>=3.0.0
types-setuptools
pytest
systemrdl-compiler
jinja2

View File

@@ -1,28 +0,0 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
# Initialize venv
rm -rf .venv
python3 -m venv .venv
source .venv/bin/activate
# Install test dependencies
pip install -r requirements.txt
# Install dut
pip install -e "../[cli]"
# Run lint
pylint --rcfile pylint.rc ../src/peakrdl_busdecoder
# Run static type checking
mypy ../src/peakrdl_busdecoder
# Run unit tests
pytest --workers auto --cov=peakrdl_busdecoder --synth-tool skip
# Generate coverage report
coverage html -i -d htmlcov

View File

@@ -1,8 +0,0 @@
addrmap top {
reg {
field {
sw=rw; hw=r;
anded; ored; xored;
} f[7:0] = 0;
} r1;
};

View File

@@ -1,44 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
cpuif.write('h0, 'h00);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b0);
assert(cb.hwif_out.r1.f.ored == 1'b0);
assert(cb.hwif_out.r1.f.xored == 1'b0);
cpuif.write('h0, 'h01);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b0);
assert(cb.hwif_out.r1.f.ored == 1'b1);
assert(cb.hwif_out.r1.f.xored == 1'b1);
cpuif.write('h0, 'h02);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b0);
assert(cb.hwif_out.r1.f.ored == 1'b1);
assert(cb.hwif_out.r1.f.xored == 1'b1);
cpuif.write('h0, 'h03);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b0);
assert(cb.hwif_out.r1.f.ored == 1'b1);
assert(cb.hwif_out.r1.f.xored == 1'b0);
cpuif.write('h0, 'hFE);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b0);
assert(cb.hwif_out.r1.f.ored == 1'b1);
assert(cb.hwif_out.r1.f.xored == 1'b1);
cpuif.write('h0, 'hFF);
@cb;
assert(cb.hwif_out.r1.f.anded == 1'b1);
assert(cb.hwif_out.r1.f.ored == 1'b1);
assert(cb.hwif_out.r1.f.xored == 1'b0);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,60 +0,0 @@
addrmap top {
default regwidth = 16;
default accesswidth = 8;
reg {
buffer_reads;
field {
sw=r; hw=w;
swacc;
} f[16];
} r1;
reg {
buffer_reads;
buffer_writes;
field {
sw=rw; hw=r;
swmod;
} f[16] = 0x4020;
} r2;
reg {
buffer_reads;
buffer_writes;
field {
sw=rw; hw=r;
swmod;
rclr;
} f[16] = 0x1030;
} r3;
reg {
buffer_reads;
buffer_writes;
field {
sw=rw; hw=r;
swacc;
swmod;
} f[16] = 0x1234;
} r4;
reg {
buffer_writes;
field {
sw=rw; hw=r;
swacc;
swmod;
} f[16] = 0xABCD;
} r5;
reg {
buffer_reads;
field {
sw=r; hw=rw;
we;
swmod;
rclr;
} f[16] = 0x1030;
} r6;
};

View File

@@ -1,197 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
logic [15:0] counter;
logic [7:0] rd_data_l;
logic [7:0] rd_data_h;
logic [15:0] latched_data;
int event_count;
bit fired;
latched_data = 'x;
##1;
cb.rst <= '0;
##1;
// Verify that hwif gets sampled at the same cycle as swacc strobe
counter = 'h10;
cb.hwif_in.r1.f.next <= counter;
@cb;
event_count = 0;
fork
begin
##0;
forever begin
counter++;
cb.hwif_in.r1.f.next <= counter;
@cb;
if(cb.hwif_out.r1.f.swacc) begin
latched_data = counter;
event_count++;
end
end
end
begin
cpuif.read('h0, rd_data_l);
cpuif.read('h1, rd_data_h);
repeat(3) @cb;
end
join_any
disable fork;
assert({rd_data_h, rd_data_l} == latched_data) else $error("Read returned 0x%0x but swacc strobed during 0x%0x", {rd_data_h, rd_data_l}, latched_data);
assert(event_count == 1) else $error("Observed excess swacc events: %0d", event_count);
// Verify that hwif changes 1 cycle after swmod
fired = 0;
fork
begin
##0;
forever begin
assert(cb.hwif_out.r2.f.value == 'h4020);
if(cb.hwif_out.r2.f.swmod) break;
@cb;
end
fired = 1;
@cb;
forever begin
assert(cb.hwif_out.r2.f.value == 'h4221);
assert(cb.hwif_out.r2.f.swmod == 0);
@cb;
end
end
begin
cpuif.write('h2, 'h21);
cpuif.write('h3, 'h42);
repeat(3) @cb;
end
join_any
disable fork;
assert(fired);
// Verify that hwif changes 1 cycle after swmod
fired = 0;
fork
begin
##0;
forever begin
assert(cb.hwif_out.r3.f.value == 'h1030);
if(cb.hwif_out.r3.f.swmod) break;
@cb;
end
fired = 1;
@cb;
forever begin
assert(cb.hwif_out.r3.f.value == 0);
assert(cb.hwif_out.r3.f.swmod == 0);
@cb;
end
end
begin
cpuif.assert_read('h4, 'h30);
cpuif.assert_read('h5, 'h10);
repeat(3) @cb;
end
join_any
disable fork;
assert(fired);
// Verify swacc and swmod assert when written
fired = 0;
fork
begin
##0;
forever begin
assert(cb.hwif_out.r4.f.value == 'h1234);
if(cb.hwif_out.r4.f.swmod || cb.hwif_out.r4.f.swacc) begin
assert(cb.hwif_out.r4.f.swmod == 1);
assert(cb.hwif_out.r4.f.swacc == 1);
break;
end
@cb;
end
fired = 1;
@cb;
forever begin
assert(cb.hwif_out.r4.f.value == 'h4567);
assert(cb.hwif_out.r4.f.swmod == 0);
assert(cb.hwif_out.r4.f.swacc == 0);
@cb;
end
end
begin
cpuif.write('h6, 'h67);
cpuif.write('h7, 'h45);
repeat(3) @cb;
end
join_any
disable fork;
assert(fired);
// Verify swacc and swmod assert when written
fired = 0;
fork
begin
##0;
forever begin
assert(cb.hwif_out.r5.f.value == 'hABCD);
if(cb.hwif_out.r5.f.swmod || cb.hwif_out.r5.f.swacc) begin
assert(cb.hwif_out.r5.f.swmod == 1);
assert(cb.hwif_out.r5.f.swacc == 1);
break;
end
@cb;
end
fired = 1;
@cb;
forever begin
assert(cb.hwif_out.r5.f.value == 'hEF12);
assert(cb.hwif_out.r5.f.swmod == 0);
assert(cb.hwif_out.r5.f.swacc == 0);
@cb;
end
end
begin
cpuif.write('h8, 'h12);
cpuif.write('h9, 'hEF);
repeat(3) @cb;
end
join_any
disable fork;
assert(fired);
// Verify that hwif changes 1 cycle after swmod
fired = 0;
fork
begin
##0;
forever begin
assert(cb.hwif_out.r6.f.value == 'h1030);
if(cb.hwif_out.r6.f.swmod) break;
@cb;
end
fired = 1;
@cb;
forever begin
assert(cb.hwif_out.r6.f.value == 0);
assert(cb.hwif_out.r6.f.swmod == 0);
@cb;
end
end
begin
cpuif.assert_read('ha, 'h30);
cpuif.assert_read('hb, 'h10);
repeat(3) @cb;
end
join_any
disable fork;
assert(fired);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,69 +0,0 @@
addrmap top {
reg {
field {
sw=r; hw=na; counter;
} implied_up[3:0] = 0xD;
field {
sw=r; hw=na; counter;
incrvalue=1;
} up[7:4] = 0xD;
field {
sw=r; hw=na; counter;
decrvalue=1;
} down[11:8] = 0x4;
field {
sw=r; hw=r; counter;
incrvalue=1;
decrvalue=1;
overflow; underflow;
} updown[15:12] = 0;
field {
sw=r; hw=na; counter;
incrvalue=3;
decrvalue=3;
} updown2[19:16] = 0;
field {
sw=r; hw=na; counter;
incrwidth=2;
decrwidth=2;
} updown3[23:20] = 0;
field {
sw=r; hw=na; counter;
} updown4[27:24] = 0;
field {
sw=rw; hw=na;
} step[29:28] = 0;
updown4->incrvalue = step;
updown4->decrvalue = step;
field {
sw=w; hw=r; singlepulse;
} do_count_up[30:30] = 0;
field {
sw=w; hw=r; singlepulse;
} do_count_down[31:31] = 0;
updown2->incr = do_count_up;
updown2->decr = do_count_down;
updown3->incr = do_count_up;
updown3->decr = do_count_down;
updown4->incr = updown3->incr;
updown4->decr = updown3->decr;
} simple @ 0x0;
reg {
field {
sw=r; hw=na; rclr; counter;
} overflow_count[8] = 0;
field {
sw=r; hw=na; rclr; counter;
} underflow_count[8] = 0;
} wrap_counter @ 0x4;
wrap_counter.overflow_count->incr = simple.updown3->overflow;
wrap_counter.underflow_count->incr = simple.updown3->underflow;
};

View File

@@ -1,147 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
//--------------------------------------------------------------------------
// Test simple counters
//--------------------------------------------------------------------------
// up
cpuif.assert_read('h0, 'hD, 'h000F);
cb.hwif_in.simple.implied_up.incr <= '1;
repeat(4) @cb;
cb.hwif_in.simple.implied_up.incr <= '0;
cpuif.assert_read('h0, 'h1, 'h000F);
// up
cpuif.assert_read('h0, 'hD0, 'h00F0);
cb.hwif_in.simple.up.incr <= '1;
repeat(4) @cb;
cb.hwif_in.simple.up.incr <= '0;
cpuif.assert_read('h0, 'h10, 'h00F0);
// down
cpuif.assert_read('h0, 'h400, 'h0F00);
cb.hwif_in.simple.down.decr <= '1;
repeat(6) @cb;
cb.hwif_in.simple.down.decr <= '0;
cpuif.assert_read('h0, 'hE00, 'h0F00);
// up/down via hw
cpuif.assert_read('h0, 'h0000, 'hF000);
cb.hwif_in.simple.updown.incr <= '1;
repeat(6) @cb;
cb.hwif_in.simple.updown.incr <= '0;
cpuif.assert_read('h0, 'h6000, 'hF000);
cb.hwif_in.simple.updown.decr <= '1;
repeat(6) @cb;
cb.hwif_in.simple.updown.decr <= '0;
cpuif.assert_read('h0, 'h0000, 'hF000);
cb.hwif_in.simple.updown.decr <= '1;
repeat(6) @cb;
cb.hwif_in.simple.updown.decr <= '0;
cpuif.assert_read('h0, 'hA000, 'hF000);
cb.hwif_in.simple.updown.incr <= '1;
repeat(6) @cb;
cb.hwif_in.simple.updown.incr <= '0;
cpuif.assert_read('h0, 'h0000, 'hF000);
// up/down external underflow
fork
begin
##0;
forever begin
assert(cb.hwif_out.simple.updown.value == 0);
if(cb.hwif_out.simple.updown.underflow) break;
@cb;
end
@cb;
forever begin
assert(cb.hwif_out.simple.updown.value == 15);
assert(cb.hwif_out.simple.updown.underflow == 0);
@cb;
end
end
begin
repeat(2) @cb;
cb.hwif_in.simple.updown.decr <= '1;
@cb;
cb.hwif_in.simple.updown.decr <= '0;
repeat(2) @cb;
end
join_any
disable fork;
// up/down external overflow
fork
begin
##0;
forever begin
assert(cb.hwif_out.simple.updown.value == 15);
if(cb.hwif_out.simple.updown.overflow) break;
@cb;
end
@cb;
forever begin
assert(cb.hwif_out.simple.updown.value == 0);
assert(cb.hwif_out.simple.updown.overflow == 0);
@cb;
end
end
begin
repeat(2) @cb;
cb.hwif_in.simple.updown.incr <= '1;
@cb;
cb.hwif_in.simple.updown.incr <= '0;
repeat(2) @cb;
end
join_any
disable fork;
// up/down via sw
cpuif.assert_read('h0, 'h00000, 'hF0000);
repeat(3) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'h90000, 'hF0000);
repeat(3) cpuif.write('h0, 'h8000_0000); // decr
cpuif.assert_read('h0, 'h00000, 'hF0000);
repeat(3) cpuif.write('h0, 'h8000_0000); // decr
cpuif.assert_read('h0, 'h70000, 'hF0000);
repeat(3) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'h00000, 'hF0000);
// up/down via hw + external dynamic stepsize
cpuif.assert_read('h0, 'h000000, 'hF00000);
cb.hwif_in.simple.updown3.incrvalue <= 'h2;
repeat(3) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'h600000, 'hF00000);
cpuif.assert_read('h4, 'h00_00); // no overflows or underflows
cb.hwif_in.simple.updown3.decrvalue <= 'h3;
repeat(3) cpuif.write('h0, 'h8000_0000); // decr
cpuif.assert_read('h0, 'hD00000, 'hF00000);
cpuif.assert_read('h4, 'h01_00); // one underflow
cb.hwif_in.simple.updown3.incrvalue <= 'h1;
repeat(2) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'hF00000, 'hF00000);
cpuif.assert_read('h4, 'h00_00); // no overflows or underflows
repeat(1) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'h000000, 'hF00000);
cpuif.assert_read('h4, 'h00_01); // one overflow
repeat(32) cpuif.write('h0, 'h4000_0000); // incr
cpuif.assert_read('h0, 'h000000, 'hF00000);
cpuif.assert_read('h4, 'h00_02); // overflow
// up/down via hw + referenced dynamic stepsize
cpuif.assert_read('h0, 'h0000000, 'hF000000);
repeat(4) cpuif.write('h0, 'h4000_0000 + (2'h3 << 28)); // + 3
cpuif.assert_read('h0, 'hC000000, 'hF000000);
repeat(4) cpuif.write('h0, 'h8000_0000 + (2'h1 << 28)); // - 1
cpuif.assert_read('h0, 'h8000000, 'hF000000);
repeat(2) cpuif.write('h0, 'h8000_0000 + (2'h3 << 28)); // - 3
cpuif.assert_read('h0, 'h2000000, 'hF000000);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,88 +0,0 @@
addrmap top {
field strobe_t {
sw=w; hw=r; singlepulse;
};
reg {
field {
sw=r; hw=r; counter;
incrsaturate;
decrsaturate;
} count[8] = 0;
strobe_t increment[9:9] = 0;
strobe_t decrement[10:10] = 0;
strobe_t clear[11:11] = 0;
strobe_t set[12:12] = 0;
field {
sw=rw; hw=na;
} step[23:16] = 1;
count->incr = increment;
count->decr = decrement;
count->hwclr = clear;
count->hwset = set;
count->incrvalue = step;
count->decrvalue = step;
} saturate_via_bool @ 0x0;
reg {
field {
sw=r; hw=r; counter;
incrsaturate = 250;
decrsaturate = 5;
} count[8] = 0;
strobe_t increment[9:9] = 0;
strobe_t decrement[10:10] = 0;
strobe_t clear[11:11] = 0;
strobe_t set[12:12] = 0;
field {
sw=rw; hw=na;
} step[23:16] = 1;
count->incr = increment;
count->decr = decrement;
count->hwclr = clear;
count->hwset = set;
count->decrvalue = step;
count->incrvalue = count->decrvalue;
} saturate_via_const @ 0x4;
reg {
field {
sw=r; hw=r; counter;
} count[8] = 0;
strobe_t increment[9:9] = 0;
strobe_t decrement[10:10] = 0;
strobe_t clear[11:11] = 0;
strobe_t set[12:12] = 0;
field {
sw=rw; hw=na;
} step[23:16] = 1;
count->incr = increment;
count->decr = decrement;
count->hwclr = clear;
count->hwset = set;
count->incrvalue = step;
count->decrvalue = count->incrvalue;
} saturate_via_ref @ 0x8;
reg {
field {
sw=rw; hw=na;
} min[8] = 0x00;
field {
sw=rw; hw=na;
} max[8] = 0xFF;
} saturate_control @ 0xC;
saturate_via_ref.count -> decrsaturate = saturate_control.min;
saturate_via_ref.count -> incrsaturate = saturate_control.max;
};

View File

@@ -1,214 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// counter controls are the same for each sub-test
`define incr (1<<9)
`define decr (1<<10)
`define clr (1<<11)
`define set (1<<12)
`define step(n) (n<<16)
//--------------------------------------------------------------------------
// Test incrsaturate = true; decrsaturate = true;
//--------------------------------------------------------------------------
cpuif.assert_read('h0, 'h00, 'hFF);
// incrsaturate via +1
cpuif.write('h0, `set);
cpuif.assert_read('h0, 'hFF, 'hFF);
cpuif.write('h0, `decr + `step(1));
cpuif.assert_read('h0, 'hFE, 'hFF);
cpuif.write('h0, `incr + `step(1));
cpuif.assert_read('h0, 'hFF, 'hFF);
cpuif.write('h0, `incr + `step(1));
cpuif.assert_read('h0, 'hFF, 'hFF);
// decrsaturate via +1
cpuif.write('h0, `clr);
cpuif.assert_read('h0, 'h00, 'hFF);
cpuif.write('h0, `incr + `step(1));
cpuif.assert_read('h0, 'h01, 'hFF);
cpuif.write('h0, `decr + `step(1));
cpuif.assert_read('h0, 'h00, 'hFF);
cpuif.write('h0, `decr + `step(1));
cpuif.assert_read('h0, 'h00, 'hFF);
// incrsaturate via larger steps
cpuif.write('h0, `set);
cpuif.assert_read('h0, 'hFF, 'hFF);
cpuif.write('h0, `decr + `step(1));
cpuif.assert_read('h0, 'hFE, 'hFF);
cpuif.write('h0, `incr + `step(2));
cpuif.assert_read('h0, 'hFF, 'hFF);
cpuif.write('h0, `incr + `step(3));
cpuif.assert_read('h0, 'hFF, 'hFF);
cpuif.write('h0, `incr + `step(255));
cpuif.assert_read('h0, 'hFF, 'hFF);
// decrsaturate via larger steps
cpuif.write('h0, `clr);
cpuif.assert_read('h0, 'h00, 'hFF);
cpuif.write('h0, `incr + `step(1));
cpuif.assert_read('h0, 'h01, 'hFF);
cpuif.write('h0, `decr + `step(2));
cpuif.assert_read('h0, 'h00, 'hFF);
cpuif.write('h0, `decr + `step(3));
cpuif.assert_read('h0, 'h00, 'hFF);
cpuif.write('h0, `decr + `step(255));
cpuif.assert_read('h0, 'h00, 'hFF);
//--------------------------------------------------------------------------
// Test incrsaturate = 250; decrsaturate = 5;
//--------------------------------------------------------------------------
cpuif.assert_read('h4, 'h00, 'hFF);
// incrsaturate via +1
cpuif.write('h4, `set);
cpuif.assert_read('h4, 'hFF, 'hFF);
cpuif.write('h4, `decr + `step(1));
cpuif.assert_read('h4, 'hFE, 'hFF);
cpuif.write('h4, `incr + `step(1));
cpuif.assert_read('h4, 'hFA, 'hFF);
cpuif.write('h4, `incr + `step(1));
cpuif.assert_read('h4, 'hFA, 'hFF);
// decrsaturate via +1
cpuif.write('h4, `clr);
cpuif.assert_read('h4, 'h00, 'hFF);
cpuif.write('h4, `incr + `step(1));
cpuif.assert_read('h4, 'h01, 'hFF);
cpuif.write('h4, `decr + `step(1));
cpuif.assert_read('h4, 'h05, 'hFF);
cpuif.write('h4, `decr + `step(1));
cpuif.assert_read('h4, 'h05, 'hFF);
// incrsaturate via larger steps
cpuif.write('h4, `set);
cpuif.assert_read('h4, 'hFF, 'hFF);
cpuif.write('h4, `decr + `step(1));
cpuif.assert_read('h4, 'hFE, 'hFF);
cpuif.write('h4, `incr + `step(2));
cpuif.assert_read('h4, 'hFA, 'hFF);
cpuif.write('h4, `incr + `step(3));
cpuif.assert_read('h4, 'hFA, 'hFF);
cpuif.write('h4, `incr + `step(255));
cpuif.assert_read('h4, 'hFA, 'hFF);
// decrsaturate via larger steps
cpuif.write('h4, `clr);
cpuif.assert_read('h4, 'h00, 'hFF);
cpuif.write('h4, `incr + `step(1));
cpuif.assert_read('h4, 'h01, 'hFF);
cpuif.write('h4, `decr + `step(2));
cpuif.assert_read('h4, 'h05, 'hFF);
cpuif.write('h4, `decr + `step(3));
cpuif.assert_read('h4, 'h05, 'hFF);
cpuif.write('h4, `decr + `step(255));
cpuif.assert_read('h4, 'h05, 'hFF);
//--------------------------------------------------------------------------
// Test incrsaturate = <ref 255>; decrsaturate = <ref 0>;
//--------------------------------------------------------------------------
cpuif.assert_read('h8, 'h00, 'hFF);
// incrsaturate via +1
cpuif.write('h8, `set);
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'hFE, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'hFF, 'hFF);
// decrsaturate via +1
cpuif.write('h8, `clr);
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'h01, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'h00, 'hFF);
// incrsaturate via larger steps
cpuif.write('h8, `set);
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'hFE, 'hFF);
cpuif.write('h8, `incr + `step(2));
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `incr + `step(3));
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `incr + `step(255));
cpuif.assert_read('h8, 'hFF, 'hFF);
// decrsaturate via larger steps
cpuif.write('h8, `clr);
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'h01, 'hFF);
cpuif.write('h8, `decr + `step(2));
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `decr + `step(3));
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `decr + `step(255));
cpuif.assert_read('h8, 'h00, 'hFF);
//--------------------------------------------------------------------------
// Test incrsaturate = <ref 250>; decrsaturate = <ref 5>;
//--------------------------------------------------------------------------
cpuif.write('hc, 'hFA_05);
cpuif.assert_read('h4, 'h05, 'hFF);
// incrsaturate via +1
cpuif.write('h8, `set);
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'hFE, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'hFA, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'hFA, 'hFF);
// decrsaturate via +1
cpuif.write('h8, `clr);
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'h01, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'h05, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'h05, 'hFF);
// incrsaturate via larger steps
cpuif.write('h8, `set);
cpuif.assert_read('h8, 'hFF, 'hFF);
cpuif.write('h8, `decr + `step(1));
cpuif.assert_read('h8, 'hFE, 'hFF);
cpuif.write('h8, `incr + `step(2));
cpuif.assert_read('h8, 'hFA, 'hFF);
cpuif.write('h8, `incr + `step(3));
cpuif.assert_read('h8, 'hFA, 'hFF);
cpuif.write('h8, `incr + `step(255));
cpuif.assert_read('h8, 'hFA, 'hFF);
// decrsaturate via larger steps
cpuif.write('h8, `clr);
cpuif.assert_read('h8, 'h00, 'hFF);
cpuif.write('h8, `incr + `step(1));
cpuif.assert_read('h8, 'h01, 'hFF);
cpuif.write('h8, `decr + `step(2));
cpuif.assert_read('h8, 'h05, 'hFF);
cpuif.write('h8, `decr + `step(3));
cpuif.assert_read('h8, 'h05, 'hFF);
cpuif.write('h8, `decr + `step(255));
cpuif.assert_read('h8, 'h05, 'hFF);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,36 +0,0 @@
addrmap top {
reg {
field {
sw=r; hw=r; counter;
incrthreshold;
decrthreshold;
} count[4] = 0;
} threshold_via_bool @ 0x0;
reg {
field {
sw=r; hw=r; counter;
incrthreshold = 10;
decrthreshold = 5;
} count[4] = 0;
} threshold_via_const @ 0x4;
reg {
field {
sw=r; hw=r; counter;
} count[4] = 0;
} threshold_via_ref @ 0x8;
reg {
field {
sw=rw; hw=na;
} min[4] = 0x4;
field {
sw=rw; hw=na;
} max[4] = 0xB;
} threshold_control @ 0xC;
threshold_via_ref.count -> decrthreshold = threshold_control.min;
threshold_via_ref.count -> incrthreshold = threshold_control.max;
};

View File

@@ -1,115 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
//--------------------------------------------------------------------------
// Test incrthreshold = true; incrthreshold = true;
//--------------------------------------------------------------------------
cpuif.assert_read('h0, 'h0);
fork
begin
forever begin
@cb;
if(cb.hwif_out.threshold_via_bool.count.value == 0)
assert(cb.hwif_out.threshold_via_bool.count.decrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_bool.count.decrthreshold == 1'b0);
if(cb.hwif_out.threshold_via_bool.count.value == 15)
assert(cb.hwif_out.threshold_via_bool.count.incrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_bool.count.incrthreshold == 1'b0);
end
end
begin
@cb;
cb.hwif_in.threshold_via_bool.count.incr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_bool.count.incr <= '0;
cb.hwif_in.threshold_via_bool.count.decr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_bool.count.decr <= '0;
@cb;
@cb;
end
join_any
disable fork;
//--------------------------------------------------------------------------
// Test incrthreshold = 10; incrthreshold = 5;
//--------------------------------------------------------------------------
cpuif.assert_read('h4, 'h0);
fork
begin
forever begin
@cb;
if(cb.hwif_out.threshold_via_const.count.value <= 5)
assert(cb.hwif_out.threshold_via_const.count.decrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_const.count.decrthreshold == 1'b0);
if(cb.hwif_out.threshold_via_const.count.value >= 10)
assert(cb.hwif_out.threshold_via_const.count.incrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_const.count.incrthreshold == 1'b0);
end
end
begin
@cb;
cb.hwif_in.threshold_via_const.count.incr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_const.count.incr <= '0;
cb.hwif_in.threshold_via_const.count.decr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_const.count.decr <= '0;
@cb;
@cb;
end
join_any
disable fork;
//--------------------------------------------------------------------------
// Test incrthreshold = ref; incrthreshold =ref;
//--------------------------------------------------------------------------
cpuif.assert_read('h8, 'h0);
fork
begin
forever begin
@cb;
if(cb.hwif_out.threshold_via_ref.count.value <= 4)
assert(cb.hwif_out.threshold_via_ref.count.decrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_ref.count.decrthreshold == 1'b0);
if(cb.hwif_out.threshold_via_ref.count.value >= 11)
assert(cb.hwif_out.threshold_via_ref.count.incrthreshold == 1'b1);
else
assert(cb.hwif_out.threshold_via_ref.count.incrthreshold == 1'b0);
end
end
begin
@cb;
cb.hwif_in.threshold_via_ref.count.incr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_ref.count.incr <= '0;
cb.hwif_in.threshold_via_ref.count.decr <= '1;
repeat(32) @cb;
cb.hwif_in.threshold_via_ref.count.decr <= '0;
@cb;
@cb;
end
join_any
disable fork;
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,12 +0,0 @@
addrmap top {
enum my_enum {
val_1 = 3 {name = "Value 1";};
val_2 = 4 {desc = "Second value";};
};
reg {
field {
encode = my_enum;
sw=rw; hw=na;
} f[2:0] = my_enum::val_2;
} r0;
};

View File

@@ -1,22 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// check enum values
assert(busdecoder_pkg::top__my_enum__val_1 == 'd3);
assert(busdecoder_pkg::top__my_enum__val_2 == 'd4);
// check initial conditions
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_2);
//---------------------------------
// set r0 = val_1
cpuif.write('h0, busdecoder_pkg::top__my_enum__val_1);
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_1);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,17 +0,0 @@
addrmap top {
default regwidth = 8;
reg {
field {
sw=r; hw=w;
rd_swacc;
} f[8];
} r1;
reg {
field {
sw=rw; hw=r;
wr_swacc;
} f[8] = 20;
} r2;
};

View File

@@ -1,68 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
logic [7:0] counter;
logic [7:0] rd_data;
logic [7:0] latched_data;
int event_count;
latched_data = 'x;
##1;
cb.rst <= '0;
##1;
// Verify that hwif gets sampled at the same cycle as rd_swacc strobe
counter = 'h10;
cb.hwif_in.r1.f.next <= counter;
@cb;
event_count = 0;
fork
begin
##0;
forever begin
counter++;
cb.hwif_in.r1.f.next <= counter;
@cb;
if(cb.hwif_out.r1.f.rd_swacc) begin
latched_data = counter;
event_count++;
end
end
end
begin
cpuif.read('h0, rd_data);
@cb;
end
join_any
disable fork;
assert(rd_data == latched_data) else $error("Read returned 0x%0x but rd_swacc strobed during 0x%0x", rd_data, latched_data);
assert(event_count == 1) else $error("Observed excess rd_swacc events: %0d", event_count);
// Verify that hwif changes 1 cycle after wr_swacc
fork
begin
##0;
forever begin
assert(cb.hwif_out.r2.f.value == 20);
if(cb.hwif_out.r2.f.wr_swacc) break;
@cb;
end
@cb;
forever begin
assert(cb.hwif_out.r2.f.value == 21);
assert(cb.hwif_out.r2.f.wr_swacc == 0);
@cb;
end
end
begin
cpuif.write('h1, 21);
@cb;
end
join_any
disable fork;
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,56 +0,0 @@
addrmap top {
reg my_reg {
field {sw=rw; hw=r;} whatever[32] = 0;
};
reg my_reg_alt {
field {sw=r; hw=w;} whatever_a[3:2] = 0;
field {sw=w; hw=r;} whatever_b[4:4] = 0;
field {sw=rw; hw=r;} whatever_c[15:8] = 0;
};
reg my_wide_reg {
regwidth = 64;
accesswidth = 32;
field {sw=rw; hw=r;} whatever = 0;
};
external my_reg_alt ext_reg @ 0x00;
my_reg int_reg @ 0x04;
external my_wide_reg wide_ext_reg @ 0x10;
external my_reg ext_reg_array[32] @ 0x100 += 4;
external regfile {
my_reg placeholder @ 8*4-4;
} rf @ 0x1000;
addrmap {
my_reg placeholder @ 8*4-4;
} am @ 0x2000;
external mem {
memwidth = 32;
mementries = 8;
} mm @ 0x3000;
reg my_ro_reg {
field {sw=r; hw=w;} whatever[32] = 0;
};
reg my_wo_reg {
field {sw=w; hw=r;} whatever[32] = 0;
};
external my_ro_reg ro_reg @ 0x4000;
external my_wo_reg wo_reg @ 0x4004;
reg my_wide_ro_reg {
regwidth = 64;
accesswidth = 32;
field {sw=r; hw=w;} whatever[32] = 0;
};
reg my_wide_wo_reg {
regwidth = 64;
accesswidth = 32;
field {sw=w; hw=r;} whatever[32] = 0;
};
external my_wide_ro_reg wide_ro_reg @ 0x4010;
external my_wide_wo_reg wide_wo_reg @ 0x4018;
};

View File

@@ -1,311 +0,0 @@
{% extends "lib/tb_base.sv" %}
{%- block dut_support %}
{% sv_line_anchor %}
external_reg ext_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.ext_reg.req),
.req_is_wr(hwif_out.ext_reg.req_is_wr),
.wr_data(hwif_out.ext_reg.wr_data),
.wr_biten(hwif_out.ext_reg.wr_biten),
.rd_ack(hwif_in.ext_reg.rd_ack),
.rd_data(hwif_in.ext_reg.rd_data),
.wr_ack(hwif_in.ext_reg.wr_ack)
);
external_reg #(
.SUBWORDS(2)
) wide_ext_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.wide_ext_reg.req),
.req_is_wr(hwif_out.wide_ext_reg.req_is_wr),
.wr_data(hwif_out.wide_ext_reg.wr_data),
.wr_biten(hwif_out.wide_ext_reg.wr_biten),
.rd_ack(hwif_in.wide_ext_reg.rd_ack),
.rd_data(hwif_in.wide_ext_reg.rd_data),
.wr_ack(hwif_in.wide_ext_reg.wr_ack)
);
for(genvar i=0; i<32; i++) begin : array
external_reg ext_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.ext_reg_array[i].req),
.req_is_wr(hwif_out.ext_reg_array[i].req_is_wr),
.wr_data(hwif_out.ext_reg_array[i].wr_data),
.wr_biten(hwif_out.ext_reg_array[i].wr_biten),
.rd_ack(hwif_in.ext_reg_array[i].rd_ack),
.rd_data(hwif_in.ext_reg_array[i].rd_data),
.wr_ack(hwif_in.ext_reg_array[i].wr_ack)
);
end
external_block #(
.ADDR_WIDTH(5)
) rf_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.rf.req),
.req_is_wr(hwif_out.rf.req_is_wr),
.addr(hwif_out.rf.addr),
.wr_data(hwif_out.rf.wr_data),
.wr_biten(hwif_out.rf.wr_biten),
.rd_ack(hwif_in.rf.rd_ack),
.rd_data(hwif_in.rf.rd_data),
.wr_ack(hwif_in.rf.wr_ack)
);
external_block #(
.ADDR_WIDTH(5)
) am_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.am.req),
.req_is_wr(hwif_out.am.req_is_wr),
.addr(hwif_out.am.addr),
.wr_data(hwif_out.am.wr_data),
.wr_biten(hwif_out.am.wr_biten),
.rd_ack(hwif_in.am.rd_ack),
.rd_data(hwif_in.am.rd_data),
.wr_ack(hwif_in.am.wr_ack)
);
external_block #(
.ADDR_WIDTH(5)
) mm_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.mm.req),
.req_is_wr(hwif_out.mm.req_is_wr),
.addr(hwif_out.mm.addr),
.wr_data(hwif_out.mm.wr_data),
.wr_biten(hwif_out.mm.wr_biten),
.rd_ack(hwif_in.mm.rd_ack),
.rd_data(hwif_in.mm.rd_data),
.wr_ack(hwif_in.mm.wr_ack)
);
external_reg wo_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.wo_reg.req),
.req_is_wr(hwif_out.wo_reg.req_is_wr),
.wr_data(hwif_out.wo_reg.wr_data),
.wr_biten(hwif_out.wo_reg.wr_biten),
.rd_ack(),
.rd_data(),
.wr_ack(hwif_in.wo_reg.wr_ack)
);
external_reg ro_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.ro_reg.req),
.req_is_wr(hwif_out.ro_reg.req_is_wr),
.wr_data(32'b0),
.wr_biten(32'b0),
.rd_ack(hwif_in.ro_reg.rd_ack),
.rd_data(hwif_in.ro_reg.rd_data),
.wr_ack()
);
external_reg #(
.SUBWORDS(2)
) wide_wo_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.wide_wo_reg.req),
.req_is_wr(hwif_out.wide_wo_reg.req_is_wr),
.wr_data(hwif_out.wide_wo_reg.wr_data),
.wr_biten(hwif_out.wide_wo_reg.wr_biten),
.rd_ack(),
.rd_data(),
.wr_ack(hwif_in.wide_wo_reg.wr_ack)
);
external_reg #(
.SUBWORDS(2)
) wide_ro_reg_inst (
.clk(clk),
.rst(rst),
.req(hwif_out.wide_ro_reg.req),
.req_is_wr(hwif_out.wide_ro_reg.req_is_wr),
.wr_data(32'b0),
.wr_biten(32'b0),
.rd_ack(hwif_in.wide_ro_reg.rd_ack),
.rd_data(hwif_in.wide_ro_reg.rd_data),
.wr_ack()
);
{%- endblock %}
{% block seq %}
logic [31:0] x;
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
//--------------------------------------------------------------------------
// Simple read/write tests
//--------------------------------------------------------------------------
repeat(20) begin
x = $urandom();
cpuif.write('h00, x);
cpuif.assert_read('h00, x);
assert(ext_reg_inst.value == x);
end
for(int i=0; i<2; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h10 + i*4, x);
cpuif.assert_read('h10 + i*4, x);
assert(wide_ext_reg_inst.value[i] == x);
end
end
for(int i=0; i<32; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h100 + i*4, x);
cpuif.assert_read('h100 + i*4, x);
end
end
for(int i=0; i<8; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h1000 + i*4, x);
cpuif.assert_read('h1000 + i*4, x);
assert(rf_inst.mem[i] == x);
end
end
for(int i=0; i<8; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h2000 + i*4, x);
cpuif.assert_read('h2000 + i*4, x);
assert(am_inst.mem[i] == x);
end
end
for(int i=0; i<8; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h3000 + i*4, x);
cpuif.assert_read('h3000 + i*4, x);
assert(mm_inst.mem[i] == x);
end
end
repeat(20) begin
x = $urandom();
ro_reg_inst.value <= x;
cpuif.write('h4000, ~x);
cpuif.assert_read('h4000, x);
assert(ro_reg_inst.value == x);
end
repeat(20) begin
x = $urandom();
cpuif.write('h4004, x);
cpuif.assert_read('h4004, 0);
assert(wo_reg_inst.value == x);
end
for(int i=0; i<2; i++) begin
repeat(20) begin
x = $urandom();
wide_ro_reg_inst.value[i] <= x;
cpuif.write('h4010 + i*4, ~x);
cpuif.assert_read('h4010 + i*4, x);
assert(wide_ro_reg_inst.value[i] == x);
end
end
for(int i=0; i<2; i++) begin
repeat(20) begin
x = $urandom();
cpuif.write('h4018 + i*4, x);
cpuif.assert_read('h4018 + i*4, 0);
assert(wide_wo_reg_inst.value[i] == x);
end
end
//--------------------------------------------------------------------------
// Pipelined access
//--------------------------------------------------------------------------
// init array with unique known value
cpuif.write('h4, 'h1234);
for(int i=0; i<32; i++) begin
cpuif.write('h100 + i*4, 'h100 + i);
end
for(int i=0; i<8; i++) begin
cpuif.write('h1000 + i*4, 'h1000 + i);
cpuif.write('h2000 + i*4, 'h2000 + i);
cpuif.write('h3000 + i*4, 'h3000 + i);
end
// random pipelined read/writes
repeat(256) begin
fork
begin
automatic int i, j;
i = $urandom_range(31, 0);
j = $urandom_range(7, 0);
case($urandom_range(9,0))
// external reg
0: cpuif.write('h100 + i*4, 'h100 + i);
1: cpuif.assert_read('h100 + i*4, 'h100 + i);
// internal reg
2: cpuif.write('h4, 'h1234);
3: cpuif.assert_read('h4, 'h1234);
// external regfile
4: cpuif.write('h1000 + j*4, 'h1000 + j);
5: cpuif.assert_read('h1000 + j*4, 'h1000 + j);
// external addrmap
6: cpuif.write('h2000 + j*4, 'h2000 + j);
7: cpuif.assert_read('h2000 + j*4, 'h2000 + j);
// external mem
8: cpuif.write('h3000 + j*4, 'h3000 + j);
9: cpuif.assert_read('h3000 + j*4, 'h3000 + j);
endcase
end
join_none
end
wait fork;
// Check register struct bit-order
repeat(32) begin
busdecoder_pkg::top__my_reg_alt__external__fields__in_t fields_in;
busdecoder_pkg::top__my_reg_alt__external__fields__out_t fields_out;
fields_in = $urandom();
fields_out = $urandom();
assert(fields_in.whatever_a == fields_in[3:2]);
assert(fields_in.whatever_c == fields_in[15:8]);
assert(fields_out.whatever_b == fields_out[4]);
assert(fields_out.whatever_c == fields_out[15:8]);
end
{% endblock %}

View File

@@ -1,29 +0,0 @@
from parameterized import parameterized_class
from ..lib.sim_testcase import SimTestCase
from ..lib.test_params import get_permutations
from ..lib.cpuifs.apb4 import APB4
from ..lib.cpuifs.axi4lite import AXI4Lite
from ..lib.cpuifs.passthrough import Passthrough
@parameterized_class(get_permutations({
"cpuif": [
APB4(),
AXI4Lite(),
Passthrough(),
],
"retime_read_fanin": [True, False],
"retime_read_response": [True, False],
"retime_external": [True, False],
}))
class Test(SimTestCase):
extra_tb_files = [
"../lib/external_reg.sv",
"../lib/external_block.sv",
]
init_hwif_in = False
clocking_hwif_in = False
timeout_clk_cycles = 30000
def test_dut(self):
self.run_test()

View File

@@ -1,64 +0,0 @@
addrmap top {
default regwidth = 8;
// All the valid combinations from Table 12
reg {
field {
sw=rw; hw=rw; we; // Storage element
} f[8] = 10;
} r1;
reg {
field {
sw=rw; hw=r; // Storage element
} f[8] = 20;
} r2;
reg {
field {
sw=rw; hw=w; wel; // Storage element
} f[8] = 30;
} r3;
reg {
field {
sw=rw; hw=na; // Storage element
} f[8] = 40;
} r4;
reg {
field {
sw=r; hw=rw; we; // Storage element
} f[8] = 50;
} r5;
reg {
field {
sw=r; hw=r; // Wire/Bus - constant value
} f[8] = 60;
} r6;
reg {
field {
sw=r; hw=w; // Wire/Bus - hardware assigns value
} f[8];
} r7;
reg {
field {
sw=r; hw=na; // Wire/Bus - constant value
} f[8] = 80;
} r8;
reg {
field {
sw=w; hw=rw; we; // Storage element
} f[8] = 90;
} r9;
reg {
field {
sw=w; hw=r; // Storage element
} f[8] = 100;
} r10;
};

View File

@@ -1,131 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
cb.hwif_in.r3.f.wel <= 1;
##1;
cb.rst <= '0;
##1;
// r1 - sw=rw; hw=rw; we; // Storage element
cpuif.assert_read('h0, 10);
assert(cb.hwif_out.r1.f.value == 10);
cpuif.write('h0, 11);
cpuif.assert_read('h0, 11);
assert(cb.hwif_out.r1.f.value == 11);
cb.hwif_in.r1.f.next <= 9;
cpuif.assert_read('h0, 11);
assert(cb.hwif_out.r1.f.value == 11);
cb.hwif_in.r1.f.next <= 12;
cb.hwif_in.r1.f.we <= 1;
@cb;
cb.hwif_in.r1.f.next <= 0;
cb.hwif_in.r1.f.we <= 0;
cpuif.assert_read('h0, 12);
assert(cb.hwif_out.r1.f.value == 12);
// r2 - sw=rw; hw=r; // Storage element
cpuif.assert_read('h1, 20);
assert(cb.hwif_out.r2.f.value == 20);
cpuif.write('h1, 21);
cpuif.assert_read('h1, 21);
assert(cb.hwif_out.r2.f.value == 21);
// r3 - sw=rw; hw=w; wel; // Storage element
cpuif.assert_read('h2, 30);
cpuif.write('h2, 31);
cpuif.assert_read('h2, 31);
cb.hwif_in.r3.f.next <= 29;
cpuif.assert_read('h2, 31);
cb.hwif_in.r3.f.next <= 32;
cb.hwif_in.r3.f.wel <= 0;
@cb;
cb.hwif_in.r3.f.next <= 0;
cb.hwif_in.r3.f.wel <= 1;
cpuif.assert_read('h2, 32);
// r4 - sw=rw; hw=na; // Storage element
cpuif.assert_read('h3, 40);
cpuif.write('h3, 41);
cpuif.assert_read('h3, 41);
// r5 - sw=r; hw=rw; we; // Storage element
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cpuif.write('h4, 51);
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cb.hwif_in.r5.f.next <= 9;
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cb.hwif_in.r5.f.next <= 52;
cb.hwif_in.r5.f.we <= 1;
@cb;
cb.hwif_in.r5.f.next <= 0;
cb.hwif_in.r5.f.we <= 0;
cpuif.assert_read('h4, 52);
assert(cb.hwif_out.r5.f.value == 52);
// r6 - sw=r; hw=r; // Wire/Bus - constant value
cpuif.assert_read('h5, 60);
assert(cb.hwif_out.r6.f.value == 60);
cpuif.write('h5, 61);
cpuif.assert_read('h5, 60);
assert(cb.hwif_out.r6.f.value == 60);
// r7 - sw=r; hw=w; // Wire/Bus - hardware assigns value
cpuif.assert_read('h6, 0);
cb.hwif_in.r7.f.next <= 70;
cpuif.assert_read('h6, 70);
cpuif.write('h6, 71);
cpuif.assert_read('h6, 70);
// r8 - sw=r; hw=na; // Wire/Bus - constant value
cpuif.assert_read('h7, 80);
cpuif.write('h7, 81);
cpuif.assert_read('h7, 80);
// r9 - sw=w; hw=rw; we; // Storage element
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 90);
cpuif.write('h8, 91);
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 91);
cb.hwif_in.r9.f.next <= 89;
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 91);
cb.hwif_in.r9.f.next <= 92;
cb.hwif_in.r9.f.we <= 1;
@cb;
cb.hwif_in.r9.f.next <= 0;
cb.hwif_in.r9.f.we <= 0;
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 92);
// r10 - sw=w; hw=r; // Storage element
cpuif.assert_read('h9, 0);
assert(cb.hwif_out.r10.f.value == 100);
cpuif.write('h9, 101);
cpuif.assert_read('h9, 0);
assert(cb.hwif_out.r10.f.value == 101);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,39 +0,0 @@
addrmap top {
default accesswidth = 64;
default regwidth = 64;
reg {
field {
sw = rw; hw = r;
intwidth = 8;
fracwidth = 8;
} f_Q8_8[16] = 0;
field {
sw = r; hw = w;
intwidth = 32;
} f_Q32_n12[20];
field {
sw = rw; hw = r;
fracwidth = 32;
is_signed;
} f_SQn8_32[24] = 0;
field {
sw = rw; hw = r;
fracwidth = 7;
is_signed;
} f_SQn6_7 = 0;
} r1 @ 0x0;
reg {
field {
sw = r; hw = w;
is_signed;
} f_signed[16];
field {
sw = rw; hw = r;
is_signed = false;
} f_unsigned[16] = 0;
field {
sw = r; hw = w;
} f_no_sign[16];
} r2 @ 0x8;
};

View File

@@ -1,77 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// set all fields to all 1s
cb.hwif_in.r1.f_Q32_n12.next <= '1;
cb.hwif_in.r2.f_signed.next <= '1;
cb.hwif_in.r2.f_no_sign.next <= '1;
cpuif.write('h0, 64'hFFFF_FFFF_FFFF_FFFF);
cpuif.write('h8, 64'hFFFF_FFFF_FFFF_FFFF);
@cb;
// Q8.8
// verify bit range
assert(cb.hwif_out.r1.f_Q8_8.value[7:-8] == '1);
// verify bit width
assert($size(hwif_out.r1.f_Q8_8.value) == 16);
// verify unsigned
assert(cb.hwif_out.r1.f_Q8_8.value > 0);
// Q32.-12
// verify bit range
assert(hwif_in.r1.f_Q32_n12.next[31:12] == '1);
// verify bit width
assert($size(hwif_in.r1.f_Q32_n12.next) == 20);
// verify unsigned
assert(hwif_in.r1.f_Q32_n12.next > 0);
// SQ-8.32
// verify bit range
assert(cb.hwif_out.r1.f_SQn8_32.value[-9:-32] == '1);
// verify bit width
assert($size(hwif_out.r1.f_SQn8_32.value) == 24);
// verify signed
assert(cb.hwif_out.r1.f_SQn8_32.value < 0);
// SQ-6.7
// verify bit range
assert(cb.hwif_out.r1.f_SQn6_7.value[-7:-7] == '1);
// verify bit width
assert($size(hwif_out.r1.f_SQn6_7.value) == 1);
// verify signed
assert(cb.hwif_out.r1.f_SQn6_7.value < 0);
// 16-bit signed integer
// verify bit range
assert(hwif_in.r2.f_signed.next[15:0] == '1);
// verify bit width
assert($size(hwif_in.r2.f_signed.next) == 16);
// verify signed
assert(hwif_in.r2.f_signed.next < 0);
// 16-bit unsigned integer
// verify bit range
assert(cb.hwif_out.r2.f_unsigned.value[15:0] == '1);
// verify bit width
assert($size(hwif_out.r2.f_unsigned.value) == 16);
// verify unsigned
assert(cb.hwif_out.r2.f_unsigned.value > 0);
// 16-bit field (no sign)
// verify bit range
assert(hwif_in.r2.f_no_sign.next[15:0] == '1);
// verify bit width
assert($size(hwif_in.r2.f_no_sign.next) == 16);
// verify unsigned (logic is unsigned in SV)
assert(hwif_in.r2.f_no_sign.next > 0);
// verify readback
cpuif.assert_read('h0, 64'h1FFF_FFFF_FFFF_FFFF);
cpuif.assert_read('h8, 64'h0000_FFFF_FFFF_FFFF);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,73 +0,0 @@
addrmap top {
reg {
field {
sw=rw; hw=na;
} hw_enable[7:0] = 0xFF;
field {
sw=rw; hw=na;
} hw_mask[15:8] = 0x00;
field {
sw=rw; hw=na;
} hw_clr[16:16] = 0;
field {
sw=rw; hw=na;
} hw_set[17:17] = 0;
field {
sw=rw; hw=na;
} hw_we[18:18] = 0;
field {
sw=rw; hw=na;
} hw_wel[20:20] = 1;
} hw_ctrl;
reg {
field {
sw=r; hw=w;
we; hwclr; hwset;
} f[7:0] = 0x11;
} r1;
r1.f->hwenable = hw_ctrl.hw_enable;
reg {
field {
sw=r; hw=w;
we; hwclr; hwset;
} f[7:0] = 0x22;
} r2;
r2.f->hwmask = hw_ctrl.hw_mask;
reg {
field {
sw=rw; hw=w;
} f[7:0] = 0x33;
} r3;
r3.f->hwenable = hw_ctrl.hw_enable;
r3.f->hwclr = hw_ctrl.hw_clr;
r3.f->hwset = hw_ctrl.hw_set;
r3.f->we = hw_ctrl.hw_we;
reg {
field {
sw=rw; hw=w;
} f[7:0] = 0x44;
} r4;
r4.f->wel = hw_ctrl.hw_wel;
reg {
signal {} f_next_value[8];
signal {} f_we;
field {
sw=rw; hw=w;
next = f_next_value;
we = f_we;
} f[7:0] = 0x55;
} r5;
};

View File

@@ -1,105 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// check initial conditions
cpuif.assert_read('h4, 'h11);
cpuif.assert_read('h8, 'h22);
cpuif.assert_read('hC, 'h33);
//---------------------------------
// set hwenable = F0
cpuif.write('h0, 'h00_F0);
// test hwenable + we
cb.hwif_in.r1.f.next <= 'hAB;
cb.hwif_in.r1.f.we <= '1;
@cb;
cb.hwif_in.r1.f.we <= '0;
cpuif.assert_read('h4, 'hA1);
// test hwenable + hwclr
cb.hwif_in.r1.f.hwclr <= '1;
@cb;
cb.hwif_in.r1.f.hwclr <= '0;
cpuif.assert_read('h4, 'h01);
// test hwenable + hwset
cb.hwif_in.r1.f.hwset <= '1;
@cb;
cb.hwif_in.r1.f.hwset <= '0;
cpuif.assert_read('h4, 'hF1);
//---------------------------------
// set hwmask = F0
cpuif.write('h0, 'hF0_00);
// test hwmask + we
cb.hwif_in.r2.f.next <= 'hAB;
cb.hwif_in.r2.f.we <= '1;
@cb;
cb.hwif_in.r2.f.we <= '0;
cpuif.assert_read('h8, 'h2B);
// test hwmask + hwclr
cb.hwif_in.r2.f.hwclr <= '1;
@cb;
cb.hwif_in.r2.f.hwclr <= '0;
cpuif.assert_read('h8, 'h20);
// test hwmask + hwset
cb.hwif_in.r2.f.hwset <= '1;
@cb;
cb.hwif_in.r2.f.hwset <= '0;
cpuif.assert_read('h8, 'h2F);
//---------------------------------
// test hwenable + hwclr via reference
// toggle hwenable = F0, hwclr=1
cpuif.write('h0, 'h1_00_F0);
cpuif.write('h0, 'h0_00_00);
cpuif.assert_read('hC, 'h03);
// test hwenable + hwset via reference
// toggle hwenable = 0F, hwset=1
cpuif.write('h0, 'h2_00_0F);
cpuif.write('h0, 'h0_00_00);
cpuif.assert_read('hC, 'h0F);
// test hwenable + we via reference
cb.hwif_in.r3.f.next <= 'hAA;
// toggle hwenable = 0F, we=1
cpuif.write('h0, 'h4_00_0F);
cpuif.write('h0, 'h0_00_00);
cpuif.assert_read('hC, 'h0A);
//---------------------------------
// test wel via reference
cb.hwif_in.r4.f.next <= 'hBB;
// toggle wel
cpuif.write('h0, 'h10_00_00);
cpuif.write('h0, 'h00_00_00);
cpuif.assert_read('h10, 'hBB);
cb.hwif_in.r4.f.next <= 'hCC;
// toggle wel
cpuif.write('h0, 'h10_00_00);
cpuif.write('h0, 'h00_00_00);
cpuif.assert_read('h10, 'hCC);
//---------------------------------
// test we and next via reference
cb.hwif_in.r5.f_next_value <= 'h54;
cpuif.assert_read('h14, 'h55);
cb.hwif_in.r5.f_next_value <= 'h56;
cb.hwif_in.r5.f_we <= '1;
@cb;
cb.hwif_in.r5.f_next_value <= '0;
cb.hwif_in.r5.f_we <= '0;
cpuif.assert_read('h14, 'h56);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,229 +0,0 @@
addrmap top {
//---------------------------------
reg {
field ctrl_t {
sw=rw; hw=na;
};
ctrl_t irq0[8] = 0;
ctrl_t irq1[1] = 0;
}
ctrl_enable @ 0x100,
ctrl_mask @ 0x104,
ctrl_haltenable @ 0x108,
ctrl_haltmask @ 0x10c;
reg {
field ctrl_t {
sw=rw; hw=na;
};
ctrl_t irq0[1] = 0;
ctrl_t irq1[1] = 0;
}
ctrl_we @ 0x110,
ctrl_wel @ 0x114;
//---------------------------------
reg {
field intr_t {
sw=rw; hw=w;
level intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
}
level_irqs_1 @ 0x0,
level_irqs_2 @ 0x4,
level_irqs_3 @ 0x8;
level_irqs_2.irq0->enable = ctrl_enable.irq0;
level_irqs_2.irq1->enable = ctrl_enable.irq1;
level_irqs_2.irq0->haltenable = ctrl_haltenable.irq0;
level_irqs_2.irq1->haltenable = ctrl_haltenable.irq1;
level_irqs_3.irq0->mask = ctrl_mask.irq0;
level_irqs_3.irq1->mask = ctrl_mask.irq1;
level_irqs_3.irq0->haltmask = ctrl_haltmask.irq0;
level_irqs_3.irq1->haltmask = ctrl_haltmask.irq1;
reg {
field intr_t {
sw=rw; hw=w;
level intr;
woclr;
we;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} level_irqs_we @ 0x10;
reg {
field intr_t {
sw=rw; hw=w;
level intr;
woclr;
wel;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} level_irqs_wel @ 0x14;
level_irqs_we.irq0->we = ctrl_we.irq0;
level_irqs_we.irq1->we = ctrl_we.irq1;
level_irqs_wel.irq0->wel = ctrl_wel.irq0;
level_irqs_wel.irq1->wel = ctrl_wel.irq1;
//---------------------------------
reg {
field intr_t {
sw=rw; hw=w;
posedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} posedge_irqs @ 0x20;
reg {
field intr_t {
sw=rw; hw=w;
posedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} posedge_we_irqs @ 0x24;
posedge_we_irqs.irq0->we = ctrl_we.irq0;
posedge_we_irqs.irq1->we = ctrl_we.irq1;
reg {
field intr_t {
sw=rw; hw=w;
posedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} posedge_wel_irqs @ 0x28;
posedge_wel_irqs.irq0->wel = ctrl_wel.irq0;
posedge_wel_irqs.irq1->wel = ctrl_wel.irq1;
//---------------------------------
reg {
field intr_t {
sw=rw; hw=w;
negedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} negedge_irqs @ 0x30;
reg {
field intr_t {
sw=rw; hw=w;
negedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} negedge_we_irqs @ 0x34;
negedge_we_irqs.irq0->we = ctrl_we.irq0;
negedge_we_irqs.irq1->we = ctrl_we.irq1;
reg {
field intr_t {
sw=rw; hw=w;
negedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} negedge_wel_irqs @ 0x38;
negedge_wel_irqs.irq0->wel = ctrl_wel.irq0;
negedge_wel_irqs.irq1->wel = ctrl_wel.irq1;
//---------------------------------
reg {
field intr_t {
sw=rw; hw=w;
bothedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} bothedge_irqs @ 0x40;
reg {
field intr_t {
sw=rw; hw=w;
bothedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} bothedge_we_irqs @ 0x44;
bothedge_we_irqs.irq0->we = ctrl_we.irq0;
bothedge_we_irqs.irq1->we = ctrl_we.irq1;
reg {
field intr_t {
sw=rw; hw=w;
bothedge intr;
woclr;
};
intr_t irq0[8] = 0;
intr_t irq1[1] = 0;
} bothedge_wel_irqs @ 0x48;
bothedge_wel_irqs.irq0->wel = ctrl_wel.irq0;
bothedge_wel_irqs.irq1->wel = ctrl_wel.irq1;
//---------------------------------
reg {
field intr_t {
sw=r; hw=w;
nonsticky intr;
};
intr_t level_active[1];
intr_t posedge_active[1];
intr_t negedge_active[1];
intr_t bothedge_active[1];
intr_t level_halt_active[1];
} top_irq @ 0x50;
top_irq.level_active->next = level_irqs_1->intr;
top_irq.posedge_active->next = posedge_irqs->intr;
top_irq.negedge_active->next = negedge_irqs->intr;
top_irq.bothedge_active->next = bothedge_irqs->intr;
top_irq.level_halt_active->next = level_irqs_2->halt;
//---------------------------------
reg {
field {
sw=rw; hw=w;
sticky;
} stickyfield[8] = 0;
} stickyreg @ 0x60;
};

View File

@@ -1,288 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// Enable all interrupts
cpuif.write('h100, 'h1FF); // ctrl_enable
cpuif.write('h104, 'h000); // ctrl_mask
cpuif.write('h108, 'h1FF); // ctrl_haltenable
cpuif.write('h10C, 'h000); // ctrl_haltmask
cpuif.write('h110, 'h0); // ctrl_we
cpuif.write('h114, 'h3); // ctrl_wel
//--------------------------------------------------------------------------
// Test level_irqs_1
cpuif.assert_read('h0, 'h000);
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
cb.hwif_in.level_irqs_1.irq0.next <= 'h0F;
@cb;
cb.hwif_in.level_irqs_1.irq0.next <= 'h00;
cpuif.assert_read('h0, 'h00F);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cpuif.write('h0, 'h3);
cpuif.assert_read('h0, 'h00C);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cpuif.write('h0, 'hC);
cpuif.assert_read('h0, 'h000);
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
cb.hwif_in.level_irqs_1.irq1.next <= 'b1;
@cb;
cb.hwif_in.level_irqs_1.irq1.next <= 'b0;
cpuif.assert_read('h0, 'h100);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cpuif.write('h0, 'h100);
@cb;
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
cpuif.assert_read('h0, 'h0);
cb.hwif_in.level_irqs_1.irq1.next <= 'b1;
cpuif.assert_read('h0, 'h100);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cpuif.write('h0, 'h100);
cpuif.assert_read('h0, 'h100);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cb.hwif_in.level_irqs_1.irq1.next <= 'b0;
cpuif.assert_read('h0, 'h100);
assert(cb.hwif_out.level_irqs_1.intr == 1'b1);
cpuif.write('h0, 'h100);
cpuif.assert_read('h0, 'h000);
assert(cb.hwif_out.level_irqs_1.intr == 1'b0);
//--------------------------------------------------------------------------
// Test level_irqs_2
cpuif.assert_read('h4, 'h000);
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
cb.hwif_in.level_irqs_2.irq0.next <= 'h0F;
@cb;
cb.hwif_in.level_irqs_2.irq0.next <= 'h00;
cpuif.assert_read('h4, 'h00F);
assert(cb.hwif_out.level_irqs_2.intr == 1'b1);
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
cpuif.write('h100, 'h0); // ctrl_enable
@cb;
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
cpuif.write('h108, 'h0); // ctrl_haltenable
@cb;
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
cpuif.write('h100, 'h1FF); // ctrl_enable
cpuif.write('h108, 'h1FF); // ctrl_haltenable
@cb;
assert(cb.hwif_out.level_irqs_2.intr == 1'b1);
assert(cb.hwif_out.level_irqs_2.halt == 1'b1);
cpuif.write('h4, 'h1FF);
@cb;
assert(cb.hwif_out.level_irqs_2.intr == 1'b0);
assert(cb.hwif_out.level_irqs_2.halt == 1'b0);
//--------------------------------------------------------------------------
// Test level_irqs_3
cpuif.assert_read('h8, 'h000);
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
assert(cb.hwif_out.level_irqs_3.halt == 1'b0);
cb.hwif_in.level_irqs_3.irq0.next <= 'h0F;
@cb;
cb.hwif_in.level_irqs_3.irq0.next <= 'h00;
cpuif.assert_read('h8, 'h00F);
assert(cb.hwif_out.level_irqs_3.intr == 1'b1);
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
cpuif.write('h104, 'h0F); // ctrl_mask
@cb;
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
cpuif.write('h10C, 'hF); // ctrl_haltmask
@cb;
assert(cb.hwif_out.level_irqs_3.intr == 1'b0);
assert(cb.hwif_out.level_irqs_3.halt == 1'b0);
cpuif.write('h104, 'h0); // ctrl_mask
cpuif.write('h10C, 'h0); // ctrl_haltmask
@cb;
assert(cb.hwif_out.level_irqs_3.intr == 1'b1);
assert(cb.hwif_out.level_irqs_3.halt == 1'b1);
//--------------------------------------------------------------------------
// Test level_irqs with we
cpuif.assert_read('h10, 'h000);
assert(cb.hwif_out.level_irqs_we.intr == 1'b0);
cb.hwif_in.level_irqs_we.irq0.next <= 'h0F;
@cb;
cb.hwif_in.level_irqs_we.irq0.next <= 'h00;
assert(cb.hwif_out.level_irqs_we.intr == 1'b0);
cpuif.assert_read('h10, 'h000);
cpuif.write('h110, 'h1); // enable ctrl_we
@cb;
cpuif.assert_read('h110, 'h1);
assert(cb.hwif_out.level_irqs_we.intr == 1'b0);
cb.hwif_in.level_irqs_we.irq0.next <= 'h0F;
@cb;
cpuif.assert_read('h10, 'h00F);
assert(cb.hwif_out.level_irqs_we.intr == 1'b1);
cpuif.write('h110, 'h0); // disable ctrl_we
cpuif.write('h10, 'h1FF);
@cb;
assert(cb.hwif_out.level_irqs_we.intr == 1'b0);
cpuif.assert_read('h10, 'h000);
cb.hwif_in.level_irqs_we.irq0.next <= 'h00;
//--------------------------------------------------------------------------
// Test level_irqs with wel
cpuif.assert_read('h14, 'h000);
assert(cb.hwif_out.level_irqs_wel.intr == 1'b0);
cb.hwif_in.level_irqs_wel.irq0.next <= 'h0F;
@cb;
cb.hwif_in.level_irqs_wel.irq0.next <= 'h00;
cpuif.assert_read('h14, 'h000);
assert(cb.hwif_out.level_irqs_wel.intr == 1'b0);
cpuif.write('h114, 'h2); // enable ctrl_we
@cb;
cpuif.assert_read('h14, 'h000);
assert(cb.hwif_out.level_irqs_wel.intr == 1'b0);
cb.hwif_in.level_irqs_wel.irq0.next <= 'h0F;
@cb;
cpuif.assert_read('h14, 'h00F);
assert(cb.hwif_out.level_irqs_wel.intr == 1'b1);
cpuif.write('h114, 'h3); // disable ctrl_we
cpuif.write('h14, 'h1FF);
@cb;
assert(cb.hwif_out.level_irqs_wel.intr == 1'b0);
cpuif.assert_read('h14, 'h000);
cb.hwif_in.level_irqs_wel.irq0.next <= 'h00;
//--------------------------------------------------------------------------
// Test posedge_irqs
cpuif.assert_read('h20, 'h000);
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
cb.hwif_in.posedge_irqs.irq1.next <= 1'b1;
@cb;
cpuif.assert_read('h20, 'h100);
assert(cb.hwif_out.posedge_irqs.intr == 1'b1);
cpuif.write('h20, 'h100);
cpuif.assert_read('h20, 'h000);
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
cpuif.assert_read('h20, 'h000);
cb.hwif_in.posedge_irqs.irq1.next <= 1'b0;
cpuif.assert_read('h20, 'h000);
assert(cb.hwif_out.posedge_irqs.intr == 1'b0);
//--------------------------------------------------------------------------
// Test negedge_irqs
cpuif.assert_read('h30, 'h000);
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
cb.hwif_in.negedge_irqs.irq1.next <= 1'b1;
@cb;
cpuif.assert_read('h30, 'h000);
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
cb.hwif_in.negedge_irqs.irq1.next <= 1'b0;
cpuif.assert_read('h30, 'h100);
assert(cb.hwif_out.negedge_irqs.intr == 1'b1);
cpuif.write('h30, 'h100);
cpuif.assert_read('h30, 'h000);
assert(cb.hwif_out.negedge_irqs.intr == 1'b0);
cpuif.assert_read('h30, 'h000);
//--------------------------------------------------------------------------
// Test bothedge_irqs
cpuif.assert_read('h40, 'h000);
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
cb.hwif_in.bothedge_irqs.irq1.next <= 1'b1;
cpuif.assert_read('h40, 'h100);
assert(cb.hwif_out.bothedge_irqs.intr == 1'b1);
cpuif.write('h40, 'h100);
cpuif.assert_read('h40, 'h000);
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
cpuif.assert_read('h40, 'h000);
cb.hwif_in.bothedge_irqs.irq1.next <= 1'b0;
cpuif.assert_read('h40, 'h100);
assert(cb.hwif_out.bothedge_irqs.intr == 1'b1);
cpuif.write('h40, 'h100);
cpuif.assert_read('h40, 'h000);
assert(cb.hwif_out.bothedge_irqs.intr == 1'b0);
cpuif.assert_read('h40, 'h000);
//--------------------------------------------------------------------------
// Test top_irq
cpuif.assert_read('h50, 'h000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cb.hwif_in.level_irqs_1.irq0.next <= 'h01;
@cb;
cb.hwif_in.level_irqs_1.irq0.next <= 'h00;
cpuif.assert_read('h50, 'b0001);
assert(cb.hwif_out.top_irq.intr == 1'b1);
cpuif.write('h0, 'h01);
cpuif.assert_read('h50, 'b0000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cb.hwif_in.posedge_irqs.irq0.next <= 'h01;
@cb;
cb.hwif_in.posedge_irqs.irq0.next <= 'h00;
cpuif.assert_read('h50, 'b0010);
assert(cb.hwif_out.top_irq.intr == 1'b1);
cpuif.write('h20, 'h01);
cpuif.assert_read('h50, 'b0000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cb.hwif_in.negedge_irqs.irq0.next <= 'h01;
@cb;
cb.hwif_in.negedge_irqs.irq0.next <= 'h00;
@cb;
cpuif.assert_read('h50, 'b0100);
assert(cb.hwif_out.top_irq.intr == 1'b1);
cpuif.write('h30, 'h01);
cpuif.assert_read('h50, 'b0000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cb.hwif_in.bothedge_irqs.irq0.next <= 'h01;
@cb;
cb.hwif_in.bothedge_irqs.irq0.next <= 'h00;
cpuif.assert_read('h50, 'b1000);
assert(cb.hwif_out.top_irq.intr == 1'b1);
cpuif.write('h40, 'h01);
cpuif.assert_read('h50, 'b0000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cpuif.write('h108, 'h000); // ctrl_haltenable
cb.hwif_in.level_irqs_2.irq0.next <= 'h01;
@cb;
cb.hwif_in.level_irqs_2.irq0.next <= 'h00;
@cb;
cpuif.assert_read('h50, 'b00000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
cpuif.write('h108, 'h001); // ctrl_haltenable
cpuif.assert_read('h50, 'b10000);
assert(cb.hwif_out.top_irq.intr == 1'b1);
cpuif.write('h4, 'h01);
cpuif.assert_read('h50, 'b00000);
assert(cb.hwif_out.top_irq.intr == 1'b0);
//--------------------------------------------------------------------------
// Test multi-bit sticky reg
cpuif.assert_read('h60, 'h00);
cb.hwif_in.stickyreg.stickyfield.next <= 'h12;
@cb;
cb.hwif_in.stickyreg.stickyfield.next <= 'h34;
@cb;
cb.hwif_in.stickyreg.stickyfield.next <= 'h56;
@cb;
cpuif.assert_read('h60, 'h12);
cpuif.write('h60, 'h00);
@cb;
cb.hwif_in.stickyreg.stickyfield.next <= 'h78;
@cb;
cpuif.assert_read('h60, 'h56);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,5 +0,0 @@
addrmap top {
reg {
field {} f1[32] = 0;
} my_reg;
};

View File

@@ -1,12 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
// check block size
assert(busdecoder_pkg::REGBLOCK_SIZE == {{exporter.ds.top_node.size}});
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,61 +0,0 @@
addrmap top {
reg {
field {
sw=rw; hw=na;
onread = rclr;
} f1[7:0] = 0xF0;
field {
sw=rw; hw=na;
onread = rset;
} f2[15:8] = 0x0F;
} r1;
reg {
field {
sw=rw; hw=na;
onwrite = woset;
} f1[3:0] = 0x0;
field {
sw=rw; hw=na;
onwrite = woclr;
} f2[7:4] = 0xF;
field {
sw=rw; hw=na;
onwrite = wot;
} f3[11:8] = 0x0;
} r2;
reg {
field {
sw=rw; hw=na;
onwrite = wzs;
} f1[3:0] = 0x0;
field {
sw=rw; hw=na;
onwrite = wzc;
} f2[7:4] = 0xF;
field {
sw=rw; hw=na;
onwrite = wzt;
} f3[11:8] = 0x0;
} r3;
reg {
field {
sw=rw; hw=na;
onwrite = wclr;
} f1[7:0] = 0xF0;
field {
sw=rw; hw=na;
onwrite = wset;
} f2[15:8] = 0x0F;
} r4;
};

View File

@@ -1,30 +0,0 @@
{% extends "lib/tb_base.sv" %}
{% block seq %}
{% sv_line_anchor %}
##1;
cb.rst <= '0;
##1;
cpuif.assert_read('h0, 'h0F_F0);
cpuif.assert_read('h0, 'hFF_00);
cpuif.write ('h0, 'h00_FF);
cpuif.assert_read('h0, 'h00_FF);
cpuif.assert_read('h0, 'hFF_00);
cpuif.assert_read('h4, 'h0_F_0);
cpuif.write ('h4, 'h1_1_1);
cpuif.assert_read('h4, 'h1_E_1);
cpuif.write ('h4, 'h1_2_2);
cpuif.assert_read('h4, 'h0_C_3);
cpuif.assert_read('h8, 'h0_F_0);
cpuif.write ('h8, 'hE_E_E);
cpuif.assert_read('h8, 'h1_E_1);
cpuif.write ('h8, 'hE_D_D);
cpuif.assert_read('h8, 'h0_C_3);
cpuif.assert_read('hC, 'h0F_F0);
cpuif.write ('hC, 'h12_34);
cpuif.assert_read('hC, 'hFF_00);
{% endblock %}

View File

@@ -1,5 +0,0 @@
from ..lib.sim_testcase import SimTestCase
class Test(SimTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,14 +0,0 @@
addrmap top {
default paritycheck;
default sw=rw;
default hw=na;
reg my_reg {
field {} f1[16] = 0;
field {} f2[8] = 0;
field {} f3 = 0;
};
my_reg r1 @ 0x000;
my_reg r2[8] @ 0x1000;
};

Some files were not shown because too many files have changed in this diff Show More