Skip to content

Commit

Permalink
Merge ba0a381 into 7e117e5
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed May 20, 2019
2 parents 7e117e5 + ba0a381 commit 22560d7
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 63 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ addons:
# python managed by conda until 3.7 available
# python:
# - '3.6'

install:
# install conda for py 3.7
- wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
Expand Down
165 changes: 107 additions & 58 deletions fault/system_verilog_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

src_tpl = """\
module {circuit_name}_tb;
integer __error_occurred = 0;
{declarations}
{circuit_name} dut (
Expand All @@ -20,25 +21,24 @@
initial begin
{initial_body}
#20 $finish;
#20 begin
if (__error_occurred)
$stop;
else
$finish;
end;
end
endmodule
"""

ncsim_cmd_string = """\
database -open -vcd vcddb -into verilog.vcd -default -timescale ps
probe -create -all -vcd -depth all
run 10000ns
quit
"""


class SystemVerilogTarget(VerilogTarget):
def __init__(self, circuit, circuit_name=None, directory="build/",
skip_compile=False, magma_output="coreir-verilog",
magma_opts={}, include_verilog_libraries=[], simulator=None,
timescale="1ns/1ns", clock_step_delay=5):
timescale="1ns/1ns", clock_step_delay=5, num_cycles=10000,
dump_vcd=True, no_warning=False):
"""
circuit: a magma circuit
Expand Down Expand Up @@ -67,11 +67,15 @@ def __init__(self, circuit, circuit_name=None, directory="build/",
if simulator is None:
raise ValueError("Must specify simulator when using system-verilog"
" target")
if simulator not in ["vcs", "ncsim"]:
if simulator not in {"vcs", "ncsim", "iverilog"}:
raise ValueError(f"Unsupported simulator {simulator}")
self.simulator = simulator
self.timescale = timescale
self.clock_step_delay = clock_step_delay
self.num_cycles = num_cycles
self.dump_vcd = dump_vcd
self.no_warning = no_warning
self.declarations = []

def make_name(self, port):
if isinstance(port, SelectPath):
Expand All @@ -86,15 +90,33 @@ def make_name(self, port):
name = verilog_name(port.name)
return name

def make_poke(self, i, action):
name = self.make_name(action.port)
# For now we assume that verilog can handle big ints
value = action.value
if isinstance(action.port, m.SIntType) and value < 0:
def process_value(self, port, value):
if isinstance(port, m.SIntType) and value < 0:
# Handle sign extension for verilator since it expects and
# unsigned c type
port_len = len(action.port)
port_len = len(port)
value = BitVector(value, port_len).as_uint()
elif value is fault.UnknownValue:
value = "'X"
elif isinstance(value, actions.Peek):
if isinstance(value.port, fault.WrappedVerilogInternalPort):
value = f"dut.{value.port.path}"
else:
value = f"{value.port.name}"
elif isinstance(value, PortWrapper):
value = f"dut.{value.select_path.system_verilog_path}"
elif isinstance(value, actions.FileRead):
new_value = f"{value.file.name_without_ext}_in"
if value.file.chunk_size == 1:
# Assume that the user didn't want an array 1 byte, so unpack
new_value += "[0]"
value = new_value
return value

def make_poke(self, i, action):
name = self.make_name(action.port)
# For now we assume that verilog can handle big ints
value = self.process_value(action.port, action.value)
return [f"{name} = {value};", f"#{self.clock_step_delay}"]

def make_print(self, i, action):
Expand All @@ -104,8 +126,9 @@ def make_print(self, i, action):
return [f'$write("{action.format_str}"{ports});']

def make_loop(self, i, action):
self.declarations.append(f"integer {action.loop_var};")
code = []
code.append(f"for (int {action.loop_var} = 0;"
code.append(f"for ({action.loop_var} = 0;"
f" {action.loop_var} < {action.n_iter};"
f" {action.loop_var}++) begin")

Expand All @@ -118,16 +141,36 @@ def make_loop(self, i, action):
return code

def make_file_open(self, i, action):
raise NotImplementedError()
if action.file.mode not in {"r", "w"}:
raise NotImplementedError(action.file.mode)
name = action.file.name_without_ext
self.declarations.append(
f"reg [7:0] {name}_in[0:{action.file.chunk_size - 1}];")
self.declarations.append(f"integer {name}_file;")
code = f"""\
{name}_file = $fopen(\"{action.file.name}\", \"{action.file.mode}\");
if (!{name}_file) $error("Could not open file {action.file.name}: %0d", {name}_file);
""" # noqa
return code.splitlines()

def make_file_close(self, i, action):
raise NotImplementedError()
return [f"$fclose({action.file.name_without_ext}_file);"]

def make_file_read(self, i, action):
raise NotImplementedError()
decl = f"integer __i;"
if decl not in self.declarations:
self.declarations.append(decl)
code = f"""\
for (__i = 0; __i < {action.file.chunk_size}; __i++) begin
{action.file.name_without_ext}_in[__i] = $fgetc({action.file.name_without_ext}_file);
end
""" # noqa
return code.splitlines()

def make_file_write(self, i, action):
raise NotImplementedError()
value = self.make_name(action.value)
return [f"$fwrite({action.file.name_without_ext}_file, \"%c\", "
f"{value});"]

def make_expect(self, i, action):
if value_utils.is_any(action.value):
Expand All @@ -139,23 +182,14 @@ def make_expect(self, i, action):
debug_name = name
else:
debug_name = action.port.name
value = action.value
if isinstance(value, actions.Peek):
if isinstance(value.port, fault.WrappedVerilogInternalPort):
value = f"dut.{value.port.path}"
else:
value = f"{value.port.name}"
elif isinstance(value, PortWrapper):
value = f"dut.{value.select_path.system_verilog_path}"
elif isinstance(action.port, m.SIntType) and value < 0:
# Handle sign extension for verilator since it expects and
# unsigned c type
port_len = len(action.port)
value = BitVector(value, port_len).as_uint()
value = self.process_value(action.port, action.value)

return [f"if ({name} != {value}) $error(\"Failed on action={i}"
f" checking port {debug_name}. Expected %x, got %x\""
f", {value}, {name});"]
return f"""
if ({name} != {value}) begin
$error(\"Failed on action={i} checking port {debug_name}. Expected %x, got %x\" , {value}, {name});
__error_occurred |= 1;
end;
""".splitlines() # noqa

def make_eval(self, i, action):
# Eval implicit in SV simulations
Expand All @@ -168,32 +202,27 @@ def make_step(self, i, action):
code.append(f"#5 {name} ^= 1;")
return code

@staticmethod
def generate_recursive_port_code(name, type_):
declarations = ""
def generate_recursive_port_code(self, name, type_):
port_list = []
if isinstance(type_, m.ArrayKind):
for j in range(type_.N):
result = SystemVerilogTarget.generate_port_code(
result = self.generate_port_code(
name + "_" + str(j), type_.T
)
declarations += result[0]
port_list.extend(result[1])
port_list.extend(result)
elif isinstance(type_, m.TupleKind):
for k, t in zip(type_.Ks, type_.Ts):
result = SystemVerilogTarget.generate_port_code(
result = self.generate_port_code(
name + "_" + str(k), t
)
declarations += result[0]
port_list.extend(result[1])
return declarations, port_list
port_list.extend(result)
return port_list

@staticmethod
def generate_port_code(name, type_):
def generate_port_code(self, name, type_):
is_array_of_bits = isinstance(type_, m.ArrayKind) and \
not isinstance(type_.T, m.BitKind)
if is_array_of_bits or isinstance(type_, m.TupleKind):
return SystemVerilogTarget.generate_recursive_port_code(name, type_)
return self.generate_recursive_port_code(name, type_)
else:
width_str = ""
if isinstance(type_, m.ArrayKind) and \
Expand All @@ -205,24 +234,23 @@ def generate_port_code(name, type_):
t = "reg"
else:
raise NotImplementedError()
return f" {t} {width_str}{name};\n", [f".{name}({name})"]
self.declarations.append(f" {t} {width_str}{name};\n")
return [f".{name}({name})"]

def generate_code(self, actions):
initial_body = ""
declarations = ""
port_list = []
for name, type_ in self.circuit.IO.ports.items():
result = SystemVerilogTarget.generate_port_code(name, type_)
declarations += result[0]
port_list.extend(result[1])
result = self.generate_port_code(name, type_)
port_list.extend(result)

for i, action in enumerate(actions):
code = self.generate_action_code(i, action)
for line in code:
initial_body += f" {line}\n"

src = src_tpl.format(
declarations=declarations,
declarations="\n".join(self.declarations),
initial_body=initial_body,
port_list=",\n ".join(port_list),
circuit_name=self.circuit_name,
Expand All @@ -241,18 +269,39 @@ def run(self, actions):
self.include_verilog_libraries)
cmd_file = Path(f"{self.circuit_name}_cmd.tcl")
if self.simulator == "ncsim":
if self.dump_vcd:
vcd_command = """
database -open -vcd vcddb -into verilog.vcd -default -timescale ps
probe -create -all -vcd -depth all"""
else:
vcd_command = ""
ncsim_cmd_string = f"""\
{vcd_command}
run {self.num_cycles}ns
quit"""
if self.no_warning:
warning = "-neverwarn"
else:
warning = ""
with open(self.directory / cmd_file, "w") as f:
f.write(ncsim_cmd_string)
cmd = f"""\
irun -top {self.circuit_name}_tb -timescale {self.timescale} -access +rwc -notimingchecks -input {cmd_file} {test_bench_file} {self.verilog_file} {verilog_libraries}
irun -top {self.circuit_name}_tb -timescale {self.timescale} -access +rwc -notimingchecks {warning} -input {cmd_file} {test_bench_file} {self.verilog_file} {verilog_libraries}
""" # nopep8
else:
elif self.simulator == "vcs":
cmd = f"""\
vcs -sverilog -full64 +v2k -timescale={self.timescale} -LDFLAGS -Wl,--no-as-needed {test_bench_file} {self.verilog_file} {verilog_libraries}
""" # nopep8
elif self.simulator == "iverilog":
cmd = f"iverilog -o {self.circuit_name}_tb {test_bench_file} {self.verilog_file}" # noqa
else:
raise NotImplementedError(self.simulator)

print(f"Running command: {cmd}")
assert not subprocess.call(cmd, cwd=self.directory, shell=True)
if self.simulator == "vcs":
print(f"Running command: {cmd}")
assert not subprocess.call("./simv", cwd=self.directory, shell=True)
elif self.simulator == "iverilog":
assert not subprocess.call(f"vvp -N {self.circuit_name}_tb",
cwd=self.directory, shell=True)
8 changes: 8 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,11 @@ def definition(io):
# [io.a + io.b, io.a - io.b, io.a * io.b, io.a / io.b], opcode)
# use arbitrary fourth op
[io.a + io.b, io.a - io.b, io.a * io.b, io.b - io.a], opcode)


class AndCircuit(m.Circuit):
IO = ["I0", m.In(m.Bit), "I1", m.In(m.Bit), "O", m.Out(m.Bit)]

@classmethod
def definition(io):
io.O <= io.I0 & io.I1
24 changes: 24 additions & 0 deletions tests/test_setattr_interface.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import common
import tempfile
import fault
from fault import Tester
import shutil
import random
import pytest


def pytest_generate_tests(metafunc):
Expand All @@ -12,6 +14,8 @@ def pytest_generate_tests(metafunc):
targets.append(("system-verilog", "ncsim"))
if shutil.which("vcs"):
targets.append(("system-verilog", "vcs"))
if shutil.which("iverilog"):
targets.append(("system-verilog", "iverilog"))
metafunc.parametrize("target,simulator", targets)


Expand Down Expand Up @@ -113,3 +117,23 @@ def test_setattr_tuple(target, simulator):
tester.circuit.O.a.expect(5)
tester.circuit.O.b.expect(11)
run_test(target, simulator, tester)


def test_setattr_x(target, simulator):
if target == "verilator":
pytest.skip("X not support with Verilator")
circ = common.AndCircuit
tester = Tester(circ)
tester.circuit.I0 = 0
tester.circuit.I1 = 1
tester.eval()
tester.circuit.O.expect(0)
tester.circuit.I0 = fault.UnknownValue
tester.circuit.I1 = 1
tester.eval()
tester.circuit.O.expect(0)
tester.circuit.I0 = fault.UnknownValue
tester.circuit.I1 = fault.UnknownValue
tester.eval()
tester.circuit.O.expect(fault.UnknownValue)
run_test(target, simulator, tester)

0 comments on commit 22560d7

Please sign in to comment.