Skip to content

Commit

Permalink
Merge 78d3b1d into bd4687b
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed May 24, 2019
2 parents bd4687b + 78d3b1d commit b7ed849
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 11 deletions.
12 changes: 12 additions & 0 deletions fault/expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Expression:
pass


class BinaryOp(Expression):
def __init__(self, left, right):
self.left = left
self.right = right


class And(BinaryOp):
pass
12 changes: 12 additions & 0 deletions fault/system_verilog_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import subprocess
from fault.wrapper import PortWrapper
import fault
import fault.expression as expression


src_tpl = """\
Expand Down Expand Up @@ -106,8 +107,19 @@ def process_value(self, port, value):
# Assume that the user didn't want an array 1 byte, so unpack
new_value += "[0]"
value = new_value
elif isinstance(value, expression.Expression):
value = f"({self.compile_expression(port, value)})"
return value

def compile_expression(self, port, value):
if isinstance(value, expression.And):
left = self.compile_expression(port, value.left)
right = self.compile_expression(port, value.right)
return f"{left} & {right}"
elif isinstance(value, PortWrapper):
return f"dut.{value.select_path.system_verilog_path}"
raise NotImplementedError(value)

def make_poke(self, i, action):
name = self.make_name(action.port)
# For now we assume that verilog can handle big ints
Expand Down
4 changes: 3 additions & 1 deletion fault/tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from fault.circuit_utils import check_interface_is_subset
from fault.wrapper import CircuitWrapper, PortWrapper, InstanceWrapper
from fault.file import File
import fault.expression as expression
import copy
import os
import inspect
Expand Down Expand Up @@ -113,7 +114,8 @@ def expect(self, port, value):
"""
Expect the current value of `port` to be `value`
"""
if not isinstance(value, (actions.Peek, PortWrapper, LoopIndex)):
if not isinstance(value, (actions.Peek, PortWrapper,
LoopIndex, expression.Expression)):
value = make_value(port, value)
self.actions.append(actions.Expect(port, value))

Expand Down
37 changes: 27 additions & 10 deletions fault/verilator_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import fault.utils as utils
import platform
import os
import fault.expression as expression


src_tpl = """\
Expand Down Expand Up @@ -112,16 +113,32 @@ def __init__(self, circuit, directory="build/",
# works
self.verilator_version = float(verilator_version.split()[1])

def process_signed_values(self, port, value):
if isinstance(value, (int, BitVector)) and value < 0:
# Handle sign extension for verilator since it expects and unsigned
# c type
if isinstance(port, SelectPath):
port = port[-1]
port_len = len(port)
value = BitVector[port_len](value).as_uint()
def process_value(self, port, value):
if isinstance(value, expression.Expression):
return self.compile_expression(port, value)
elif isinstance(value, (int, BitVector)) and value < 0:
return self.process_signed_values(port, value)
elif isinstance(value, (int, BitVector)):
return value
return value

def process_signed_values(self, port, value):
# Handle sign extension for verilator since it expects and unsigned
# c type
if isinstance(port, SelectPath):
port = port[-1]
port_len = len(port)
return BitVector[port_len](value).as_uint()

def compile_expression(self, port, value):
if isinstance(value, expression.And):
left = self.compile_expression(port, value.left)
right = self.compile_expression(port, value.right)
return f"{left} & {right}"
elif isinstance(value, PortWrapper):
return f"top->{value.select_path.verilator_path}"
raise NotImplementedError(value)

def make_poke(self, i, action):
if self.verilator_version > 3.874:
prefix = f"{self.circuit_name}"
Expand Down Expand Up @@ -180,7 +197,7 @@ def make_poke(self, i, action):
if isinstance(value, actions.FileRead):
mask = "FF" * value.file.chunk_size
value = f"(*{value.file.name_without_ext}_in) & 0x{mask}"
value = self.process_signed_values(action.port, value)
value = self.process_value(action.port, value)
result = [f"top->{name} = {value};"]
# Hack to support verilator's semantics, need to set the register
# mux values for expected behavior
Expand Down Expand Up @@ -245,7 +262,7 @@ def make_expect(self, i, action):
circuit_name = type(item.instance).name
self.debug_includes.add(f"{circuit_name}")
value = f"top->{prefix}->" + value.select_path.verilator_path
value = self.process_signed_values(action.port, value)
value = self.process_value(action.port, value)

return [f"my_assert(top->{name}, {value}, "
f"{i}, \"{debug_name}\");"]
Expand Down
4 changes: 4 additions & 0 deletions fault/wrapper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fault
from fault.select_path import SelectPath
import fault.expression as expression
import magma as m


Expand Down Expand Up @@ -103,6 +104,9 @@ def select_path(self):
select_path.tester = parent
return select_path

def __and__(self, other):
return expression.And(self, other)


class InstanceWrapper(Wrapper):
def __init__(self, instance, parent):
Expand Down
64 changes: 64 additions & 0 deletions tests/test_expressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Test the construction of expression trees from Peeked values
"""
import shutil
import tempfile

import fault
import magma as m
import mantle
import hwtypes


def pytest_generate_tests(metafunc):
"""
Parametrize tests over targets
"""
if "target" in metafunc.fixturenames:
targets = [("verilator", None)]
if shutil.which("irun"):
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)


def test_and_two_signals(target, simulator):
"""
Test that we can and two output signals for an expect
"""
class ANDCircuit(m.Circuit):
"""
Pass through I0 and I1 as outputs so we can assert a function
of I0 and I1 as the result
"""
IO = ["I0", m.In(m.Bits[5]), "I1", m.In(m.Bits[5]),
"I0_out", m.Out(m.Bits[5]), "I1_out", m.Out(m.Bits[5]),
"O", m.Out(m.Bits[5])]

@classmethod
def definition(io):
io.I0_out <= io.I0
io.I1_out <= io.I1
io.O <= io.I0 & io.I1

tester = fault.Tester(ANDCircuit)
for _ in range(5):
tester.circuit.I0 = hwtypes.BitVector.random(5)
tester.circuit.I1 = hwtypes.BitVector.random(5)
tester.eval()
tester.circuit.O.expect(tester.circuit.I0_out & tester.circuit.I1_out)

with tempfile.TemporaryDirectory() as _dir:
kwargs = {
"target": target,
"directory": _dir,
}
if target == "system-verilog":
kwargs["simulator"] = simulator
tester.compile_and_run(**kwargs)

0 comments on commit b7ed849

Please sign in to comment.