Skip to content

Commit

Permalink
Merge fd60b40 into 1b8c00b
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed May 6, 2019
2 parents 1b8c00b + fd60b40 commit dfb8791
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 12 deletions.
20 changes: 20 additions & 0 deletions fault/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ def retarget(self, new_circuit, clock):
"""
raise NotImplementedError()

def __repr__(self):
return str(self)


class PortAction(Action):
def __init__(self, port, value):
Expand Down Expand Up @@ -133,3 +136,20 @@ def __str__(self):

def retarget(self, new_circuit, clock):
return Step(clock, self.steps)


class Loop(Action):
def __init__(self, n_iter, loop_var, actions):
self.n_iter = n_iter
self.actions = actions
self.loop_var = loop_var

def __str__(self):
# TODO: Might be nice to format this print output over multiple lines
# for actions
return f"Loop({self.n_iter}, {self.loop_var}, {self.actions})"

def retarget(self, new_circuit, clock):
actions = [action.retarget(new_circuit, clock) for action in
self.actions]
return Loop(self.n_iter, actions)
9 changes: 6 additions & 3 deletions fault/cosa_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ def __init__(self, circuit, directory="build/", skip_compile=False,
self.solver = solver

def make_eval(self, i, action):
return
raise NotImplementedError()

def make_expect(self, i, action):
return
raise NotImplementedError()

def make_poke(self, i, action):
name = verilog_name(action.port.name)
Expand All @@ -64,7 +64,10 @@ def make_poke(self, i, action):
f"self.{name} = {value}_{width}")

def make_print(self, i, action):
return
raise NotImplementedError()

def make_loop(self, i, action):
raise NotImplementedError()

def make_step(self, i, action):
self.step_offset += action.steps
Expand Down
14 changes: 14 additions & 0 deletions fault/system_verilog_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ def make_print(self, i, action):
return [f'$display("{action.port.debug_name} = '
f'{action.format_str}", {name});']

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

for inner_action in action.actions:
# TODO: Handle relative offset of sub-actions
inner_code = self.generate_action_code(i, inner_action)
code += [" " + x for x in inner_code]

code.append("end")
return code

def make_expect(self, i, action):
if value_utils.is_any(action.value):
return []
Expand Down
40 changes: 35 additions & 5 deletions fault/tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fault.value_utils import make_value
from fault.verilator_target import VerilatorTarget
from fault.system_verilog_target import SystemVerilogTarget
from fault.actions import Poke, Expect, Step, Print
from fault.actions import Poke, Expect, Step, Print, Loop
from fault.circuit_utils import check_interface_is_subset
from fault.wrapper import CircuitWrapper, PortWrapper, InstanceWrapper
import copy
Expand Down Expand Up @@ -90,7 +90,8 @@ def poke(self, port, value):
for p, v in zip(port, value):
self.poke(p, v)
else:
value = make_value(port, value)
if not isinstance(value, LoopIndex):
value = make_value(port, value)
self.actions.append(actions.Poke(port, value))

def peek(self, port):
Expand All @@ -114,9 +115,7 @@ def expect(self, port, value):
"""
Expect the current value of `port` to be `value`
"""
is_peek = isinstance(value, actions.Peek)
is_port_wrapper = isinstance(value, PortWrapper)
if not (is_peek or is_port_wrapper):
if not isinstance(value, (actions.Peek, PortWrapper, LoopIndex)):
value = make_value(port, value)
self.actions.append(actions.Expect(port, value))

Expand Down Expand Up @@ -225,3 +224,34 @@ def verilator_include(self, module_name):
@property
def circuit(self):
return CircuitWrapper(self._circuit, self)

def loop(self, n_iter):
"""
Returns a new tester to record actions inside the loop. The created
loop action object maintains a references to the return Tester's
`actions` list.
"""
loop_tester = LoopTester(self.circuit, self.clock,
self.default_print_format_str)
self.actions.append(Loop(n_iter, loop_tester.index,
loop_tester.actions))
return loop_tester


class LoopIndex:
def __init__(self, name):
self.name = name

def __str__(self):
return self.name


class LoopTester(Tester):
__unique_index_id = -1

def __init__(self, circuit: m.Circuit, clock: m.ClockType = None,
default_print_format_str: str = "%x"):
super().__init__(circuit, clock, default_print_format_str)
LoopTester.__unique_index_id += 1
self.index = LoopIndex(
f"__fault_loop_var_action_{LoopTester.__unique_index_id}")
14 changes: 14 additions & 0 deletions fault/verilator_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,20 @@ def make_step(self, i, action):
code.append("#endif")
return code

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

for inner_action in action.actions:
# TODO: Handle relative offset of sub-actions
inner_code = self.generate_action_code(i, inner_action)
code += [" " + x for x in inner_code]

code.append("}")
return code

def generate_code(self, actions, verilator_includes, num_tests, circuit):
if verilator_includes:
# Include the top circuit by default
Expand Down
6 changes: 6 additions & 0 deletions fault/verilog_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def generate_action_code(self, i, action):
return self.make_assume(i, action)
if isinstance(action, actions.Guarantee):
return self.make_guarantee(i, action)
if isinstance(action, actions.Loop):
return self.make_loop(i, action)
raise NotImplementedError(action)

@abstractmethod
Expand All @@ -120,3 +122,7 @@ def make_eval(self, i, action):
@abstractmethod
def make_step(self, i, action):
pass

@abstractmethod
def make_loop(self, i, action):
pass
6 changes: 5 additions & 1 deletion tests/test_action.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fault.actions import Poke, Expect, Eval, Step, Print, Peek
from fault.actions import Poke, Expect, Eval, Step, Print, Peek, Loop
import common


Expand All @@ -10,3 +10,7 @@ def test_action_strs():
assert str(Step(circ.CLK, 1)) == 'Step(BasicClkCircuit.CLK, steps=1)'
assert str(Print(circ.O, "%08x")) == 'Print(BasicClkCircuit.O, "%08x")'
assert str(Peek(circ.O)) == 'Peek(BasicClkCircuit.O)'
index = f"__fault_loop_var_action_0"
assert str(Loop(12, index, [Peek(circ.O), Poke(circ.I, 1)])) == \
f'Loop(12, {index}, ' \
f'[Peek(BasicClkCircuit.O), Poke(BasicClkCircuit.I, 1)])'
4 changes: 3 additions & 1 deletion tests/test_symbolic_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def test_tester_magma_internal_signals_verilator(target):
# become assumptions
tester.circuit.config_en = 0
tester.step(2)
tester.circuit.config_reg.Q.expect(0)
if target == "verilator":
# TODO: We could turn this expect into a CoSA assert
tester.circuit.config_reg.Q.expect(0)
tester.circuit.a.assume(lambda a: a < BitVector(32768, 16))
tester.circuit.b.assume(lambda b: b < BitVector(32768, 16))
# tester.circuit.b.assume(lambda b: b >= BitVector(32768, 16))
Expand Down
23 changes: 22 additions & 1 deletion tests/test_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import random
from hwtypes import BitVector
import fault
from fault.actions import Poke, Expect, Eval, Step, Print, Peek
from fault.actions import Poke, Expect, Eval, Step, Print, Peek, Loop
import common
import tempfile
import os
Expand Down Expand Up @@ -270,3 +270,24 @@ def test_tester_verilog_wrapped(target, simulator):
tester.compile_and_run(target, directory=_dir, flags=["-Wno-fatal"])
else:
tester.compile_and_run(target, directory=_dir, simulator=simulator)


def test_tester_loop(target, simulator):
circ = common.TestArrayCircuit
tester = fault.Tester(circ)
tester.zero_inputs()
loop = tester.loop(7)
loop.poke(circ.I, loop.index)
loop.eval()
loop.expect(circ.O, loop.index)
assert tester.actions[1].n_iter == 7
for actual, expected in zip(tester.actions[1].actions,
[Poke(circ.I, loop.index),
Eval(),
Expect(circ.O, loop.index)]):
check(actual, expected)
with tempfile.TemporaryDirectory() as _dir:
if target == "verilator":
tester.compile_and_run(target, directory=_dir, flags=["-Wno-fatal"])
else:
tester.compile_and_run(target, directory=_dir, simulator=simulator)
2 changes: 1 addition & 1 deletion tests/test_verilog_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from hwtypes import BitVector
import common
import random
from fault.actions import Poke, Expect, Eval, Step, Print, Peek
from fault.actions import Poke, Expect, Eval, Step, Print, Peek, Loop
from fault.random import random_bv
import copy
import os.path
Expand Down

0 comments on commit dfb8791

Please sign in to comment.