Add option to use xilinx simulator in tests. Not recommended - simulator is pretty awful

This commit is contained in:
Alex Mykyta
2021-12-05 18:17:14 -08:00
parent 3adf7e1328
commit 027ac99ead
10 changed files with 209 additions and 100 deletions

2
.gitignore vendored
View File

@@ -7,6 +7,8 @@
**/_build
**/*.out
**/transcript
**/*.log
**/*.pb
build/
dist/

View File

@@ -1,6 +1,2 @@
begin
{%- filter indent %}
{%- block body %}
{%- endblock %}
{%- endfilter %}
end

View File

@@ -3,7 +3,6 @@ import unittest
import os
import glob
import shutil
import subprocess
import inspect
import pathlib
@@ -17,6 +16,8 @@ from peakrdl.regblock import RegblockExporter
from .cpuifs.base import CpuifTestMode
from .cpuifs.apb3 import APB3
from .simulators.modelsim import ModelSim
class RegblockTestCase(unittest.TestCase):
#: Path to the testcase's RDL file.
@@ -41,6 +42,8 @@ class RegblockTestCase(unittest.TestCase):
#: Abort test if it exceeds this number of clock cycles
timeout_clk_cycles = 1000
simulator_cls = ModelSim
#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
@@ -134,43 +137,6 @@ class RegblockTestCase(unittest.TestCase):
stream.dump(output_path)
@classmethod
def _compile_tb(cls):
# CD into the build directory
cwd = os.getcwd()
os.chdir(cls.get_build_dir())
cmd = [
"vlog", "-sv", "-quiet", "-l", "build.log",
# Free version of ModelSim throws errors if generate/endgenerate
# blocks are not used.
# These have been made optional long ago. Modern versions of SystemVerilog do
# not require them and I prefer not to add them.
"-suppress", "2720",
# Ignore noisy warning about vopt-time checking of always_comb/always_latch
"-suppress", "2583",
]
# Add CPUIF sources
cmd.extend(cls.cpuif.get_tb_files())
# Add DUT sources
cmd.append("regblock_pkg.sv")
cmd.append("regblock.sv")
# Add TB
cmd.append("tb.sv")
# Run command!
try:
subprocess.run(cmd, check=True)
finally:
# cd back
os.chdir(cwd)
@classmethod
def setUpClass(cls):
# Create fresh build dir
@@ -187,7 +153,16 @@ class RegblockTestCase(unittest.TestCase):
# Create testbench from template
cls._generate_tb(exporter)
cls._compile_tb()
simulator = cls.simulator_cls(testcase_cls=cls)
# cd into the build directory
cwd = os.getcwd()
os.chdir(cls.get_build_dir())
try:
simulator.compile()
finally:
# cd back
os.chdir(cwd)
def setUp(self) -> None:
@@ -197,27 +172,8 @@ class RegblockTestCase(unittest.TestCase):
def run_test(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.request.node.name
# call vsim
cmd = [
"vsim", "-quiet",
"-msgmode", "both",
"-do", "set WildcardFilter [lsearch -not -all -inline $WildcardFilter Memory]",
"-do", "log -r /*;",
"-do", "run -all; exit;",
"-c",
"-l", "%s.log" % test_name,
"-wlf", "%s.wlf" % test_name,
"tb",
]
for plusarg in plusargs:
cmd.append("+" + plusarg)
subprocess.run(cmd, check=True)
self.assertSimLogPass("%s.log" % test_name)
simulator = self.simulator_cls(testcase_cls_inst=self)
simulator.run(plusargs)
def tearDown(self) -> None:

View File

@@ -0,0 +1,26 @@
from typing import Type, TYPE_CHECKING, List
if TYPE_CHECKING:
from ..regblock_testcase import RegblockTestCase
class Simulator:
def __init__(self, testcase_cls: 'Type[RegblockTestCase]' = None, testcase_cls_inst: 'RegblockTestCase' = None) -> None:
self.testcase_cls = testcase_cls
self.testcase_cls_inst = testcase_cls_inst
@property
def tb_files(self) -> List[str]:
files = []
files.extend(self.testcase_cls.cpuif.get_tb_files())
files.append("regblock_pkg.sv")
files.append("regblock.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

@@ -0,0 +1,61 @@
from typing import List
import subprocess
import os
from . import Simulator
class ModelSim(Simulator):
def compile(self) -> None:
cmd = [
"vlog", "-sv", "-quiet", "-l", "build.log",
# Free version of ModelSim throws errors if generate/endgenerate
# blocks are not used.
# These have been made optional long ago. Modern versions of SystemVerilog do
# not require them and I prefer not to add them.
"-suppress", "2720",
# Ignore noisy warning about vopt-time checking of always_comb/always_latch
"-suppress", "2583",
]
# 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_cls_inst.request.node.name
# call vsim
cmd = [
"vsim", "-quiet",
"-msgmode", "both",
"-do", "set WildcardFilter [lsearch -not -all -inline $WildcardFilter Memory]",
"-do", "log -r /*;",
"-do", "run -all; exit;",
"-c",
"-l", "%s.log" % test_name,
"-wlf", "%s.wlf" % test_name,
"tb",
]
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_cls_inst.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("# ** Error"):
self.testcase_cls_inst.fail(line)
elif line.startswith("# ** Fatal"):
self.testcase_cls_inst.fail(line)

View File

@@ -0,0 +1,60 @@
from typing import List
import subprocess
import os
from . import Simulator
class Xilinx(Simulator):
"""
Don't bother using the Xilinx simulator... Its buggy and extraordinarily slow.
As observed in v2021.1, clocking block assignments do not seem to actually simulate
correctly - assignemnt statements get ignored or the values get mangled.
Keeping this here in case someday it works better...
"""
def compile(self) -> None:
cmd = [
"xvlog", "--sv"
]
cmd.extend(self.tb_files)
subprocess.run(cmd, check=True)
cmd = [
"xelab",
"--timescale", "1ns/1ps",
"--debug", "all",
"tb",
]
subprocess.run(cmd, check=True)
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase_cls_inst.request.node.name
# call vsim
cmd = [
"xsim",
"--R",
"--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_cls_inst.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("Error:"):
self.testcase_cls_inst.fail(line)
elif line.startswith("Fatal:"):
self.testcase_cls_inst.fail(line)

View File

@@ -57,8 +57,13 @@ module tb;
{%- if exporter.hwif.has_output_struct %}
{% sv_line_anchor %}
initial forever begin
##1; if(!rst) assert(!$isunknown({>>{hwif_out}})) else $error("hwif_out has X's!");
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!");
end
end
{%- endif %}

View File

@@ -88,7 +88,7 @@
// r7 - sw=r; hw=w; // Wire/Bus - hardware assigns value
cpuif.assert_read('h6, 0);
cb.hwif_in.r7.f.value = 70;
cb.hwif_in.r7.f.value <= 70;
cpuif.assert_read('h6, 70);
cpuif.write('h6, 71);
cpuif.assert_read('h6, 70);

View File

@@ -17,17 +17,17 @@
end
// Assert via hwif
assert(hwif_out.r0.a.value == 'h42);
assert(hwif_out.r0.b.value == 'h0);
assert(hwif_out.r0.c.value == 'h1);
foreach(hwif_out.r1[x, y, z]) begin
assert(hwif_out.r1[x][y][z].a.value == 'h23);
assert(hwif_out.r1[x][y][z].b.value == 'h0);
assert(hwif_out.r1[x][y][z].c.value == 'h1);
assert(cb.hwif_out.r0.a.value == 'h42);
assert(cb.hwif_out.r0.b.value == 'h0);
assert(cb.hwif_out.r0.c.value == 'h1);
foreach(cb.hwif_out.r1[x, y, z]) begin
assert(cb.hwif_out.r1[x][y][z].a.value == 'h23);
assert(cb.hwif_out.r1[x][y][z].b.value == 'h0);
assert(cb.hwif_out.r1[x][y][z].c.value == 'h1);
end
assert(hwif_out.r2.a.value == 'h11);
assert(hwif_out.r2.b.value == 'h0);
assert(hwif_out.r2.c.value == 'h1);
assert(cb.hwif_out.r2.a.value == 'h11);
assert(cb.hwif_out.r2.b.value == 'h0);
assert(cb.hwif_out.r2.c.value == 'h1);
// Write values
cpuif.write(0, 32'h8000_0002);
@@ -50,15 +50,15 @@
end
// Assert via hwif
assert(hwif_out.r0.a.value == 'h02);
assert(hwif_out.r0.b.value == 'h0);
assert(hwif_out.r0.c.value == 'h1);
foreach(hwif_out.r1[x, y, z]) begin
assert(hwif_out.r1[x][y][z].a.value == x*12+y*4+z+10);
assert(hwif_out.r1[x][y][z].b.value == 'h1);
assert(hwif_out.r1[x][y][z].c.value == 'h0);
assert(cb.hwif_out.r0.a.value == 'h02);
assert(cb.hwif_out.r0.b.value == 'h0);
assert(cb.hwif_out.r0.c.value == 'h1);
foreach(cb.hwif_out.r1[x, y, z]) begin
assert(cb.hwif_out.r1[x][y][z].a.value == x*12+y*4+z+10);
assert(cb.hwif_out.r1[x][y][z].b.value == 'h1);
assert(cb.hwif_out.r1[x][y][z].c.value == 'h0);
end
assert(hwif_out.r2.a.value == 'h0);
assert(hwif_out.r2.b.value == 'h0);
assert(hwif_out.r2.c.value == 'h0);
assert(cb.hwif_out.r2.a.value == 'h0);
assert(cb.hwif_out.r2.b.value == 'h0);
assert(cb.hwif_out.r2.c.value == 'h0);
{% endblock %}

View File

@@ -2,6 +2,7 @@
{% block seq %}
{% sv_line_anchor %}
logic [7:0] counter;
logic [7:0] rd_data;
logic [7:0] latched_data;
int event_count;
@@ -12,17 +13,19 @@
##1;
// Verify that hwif gets sampled at the same cycle as swacc strobe
cb.hwif_in.r1.f.value <= 'h10;
counter = 'h10;
cb.hwif_in.r1.f.value <= counter;
@cb;
event_count = 0;
fork
begin
##0;
forever begin
cb.hwif_in.r1.f.value <= cb.hwif_in.r1.f.value + 1;
counter++;
cb.hwif_in.r1.f.value <= counter;
@cb;
if(cb.hwif_out.r1.f.swacc) begin
latched_data = cb.hwif_in.r1.f.value;
latched_data = counter;
event_count++;
end
end
@@ -43,14 +46,14 @@
begin
##0;
forever begin
assert(hwif_out.r2.f.value == 20);
if(hwif_out.r2.f.swmod) break;
assert(cb.hwif_out.r2.f.value == 20);
if(cb.hwif_out.r2.f.swmod) break;
@cb;
end
@cb;
forever begin
assert(hwif_out.r2.f.value == 21);
assert(hwif_out.r2.f.swmod == 0);
assert(cb.hwif_out.r2.f.value == 21);
assert(cb.hwif_out.r2.f.swmod == 0);
@cb;
end
end
@@ -67,14 +70,14 @@
begin
##0;
forever begin
assert(hwif_out.r3.f.value == 30);
if(hwif_out.r3.f.swmod) break;
assert(cb.hwif_out.r3.f.value == 30);
if(cb.hwif_out.r3.f.swmod) break;
@cb;
end
@cb;
forever begin
assert(hwif_out.r3.f.value == 0);
assert(hwif_out.r3.f.swmod == 0);
assert(cb.hwif_out.r3.f.value == 0);
assert(cb.hwif_out.r3.f.swmod == 0);
@cb;
end
end