def test_regression(): io_core = IOCore() io_core_circuit = io_core.circuit() tester = Tester(io_core_circuit) for _glb2io_16, _f2io_16 in \ [(random_bv(16), random_bv(16)) for _ in range(100)]: tester.poke(io_core_circuit.glb2io_16, _glb2io_16) tester.poke(io_core_circuit.f2io_16, _f2io_16) tester.eval() tester.expect(io_core_circuit.io2glb_16, _f2io_16) tester.expect(io_core_circuit.io2f_16, _glb2io_16) for _glb2io_1, _f2io_1 in \ [(random_bv(1), random_bv(1)) for _ in range(100)]: tester.poke(io_core_circuit.glb2io_1, _glb2io_1) tester.poke(io_core_circuit.f2io_1, _f2io_1) tester.eval() tester.expect(io_core_circuit.io2glb_1, _f2io_1) tester.expect(io_core_circuit.io2f_1, _glb2io_1) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def test_valid_generation(): io_core = IOCoreValid() io_core_circuit = io_core.circuit() tester = BasicTester(io_core_circuit, io_core_circuit.clk, io_core_circuit.reset) max_cycle = 16 # mode set to 1 # max cycle set to 16 config_data = [ io_core.get_config_data("mode", 1), io_core.get_config_data("max_cycle", max_cycle) ] config_data = compress_config_data(config_data) tester.zero_inputs() tester.poke(io_core_circuit.stall, 1) for addr, data in config_data: tester.configure(addr, data) tester.config_read(addr) tester.eval() tester.expect(io_core_circuit.read_config_data, data) # un-stall tester.poke(io_core_circuit.stall, 0) # it should stay low regardless of the IO as long as the reset signal is never high for _f2io_16 in [random_bv(16) for _ in range(10)]: tester.poke(io_core_circuit.f2io_16, _f2io_16) tester.eval() tester.expect(io_core_circuit.io2glb_16, _f2io_16) tester.expect(io_core_circuit.io2glb_1, 0) # hit the start signal tester.poke(io_core_circuit.glb2io_1, 1) tester.eval() # rising clock edge tester.step(2) # de-assert the reset/start signal tester.poke(io_core_circuit.glb2io_1, 0) tester.eval() # from now on it should output 1 until it reaches max cycle for _ in range(max_cycle): tester.expect(io_core_circuit.io2glb_1, 1) tester.step(2) # then stay low for _ in range(max_cycle): tester.expect(io_core_circuit.io2glb_1, 0) tester.step(2) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def test_magma_simulator_target_nested_arrays_bulk(backend): circ = common.TestNestedArraysCircuit expected = [random_bv(4) for i in range(3)] actions = [] actions.append(Poke(circ.I, expected)) actions.append(Eval()) actions.append(Expect(circ.O, expected)) run(circ, actions, None, backend)
def test_target_peek(target, simulator): circ = TestPeekCircuit actions = [] for i in range(3): x = random_bv(3) actions.append(Poke(circ.I, x)) actions.append(Eval()) actions.append(Expect(circ.O0, Peek(circ.O1))) run(circ, actions, target, simulator)
def test_magma_simulator_target_peek(backend): circ = common.TestPeekCircuit actions = [] for i in range(3): x = random_bv(3) actions.append(Poke(circ.I, x)) actions.append(Eval()) actions.append(Expect(circ.O0, Peek(circ.O1))) run(circ, actions, None, backend)
def test_magma_simulator_target_nested_arrays_by_element(backend): circ = common.TestNestedArraysCircuit expected = [random_bv(4) for i in range(3)] actions = [] for i, val in enumerate(expected): actions.append(Poke(circ.I[i], val)) actions.append(Eval()) for i, val in enumerate(expected): actions.append(Expect(circ.O[i], val)) run(circ, actions, None, backend)
def test_regression(default_value, num_tracks, has_constant): feedthrough_outputs = "1111101111" params = { "width": 16, "num_tracks": num_tracks, "feedthrough_outputs": feedthrough_outputs, "has_constant": has_constant, "default_value": default_value.as_uint() } magma_cb = define_cb(**params) m.compile(f"test_cb/build/{magma_cb.name}", magma_cb, output="coreir-verilog") genesis_cb = cb_wrapper.generator()(**params, input_files=["cb/genesis/cb.vp"]) genesis_verilog = "genesis_verif/cb.v" check_interfaces(magma_cb, genesis_cb) shutil.copy(genesis_verilog, "test_cb/build") config_addr = BitVector(0, 32) cb_functional_model = gen_cb(**params)() class CBTester(ResetTester, ConfigurationTester): pass tester = CBTester(genesis_cb, genesis_cb.clk, cb_functional_model) for config_data in [BitVector(x, 32) for x in range(0, num_tracks)]: tester.zero_inputs() tester.reset() tester.configure(config_addr, config_data) tester.actions += \ generate_actions_from_streams( # Interesting example of Python's dynamic scoping, observe how # the following code is incorrect because of when the string # argument to getattr is evaluated # genesis_cb, cb_functional_model, dict(**{ # f"in_{i}": lambda name, port: random_bv( # len(getattr(genesis_cb, f"in_i{i}"))) # for i in range(num_tracks) if feedthrough_outputs[i] == "1" # }, **{ genesis_cb, cb_functional_model, { f"in_{i}": lambda name, port: random_bv( len(port)) for i in range(num_tracks) if feedthrough_outputs[i] == "1" }) for cb, output in [(genesis_cb, "verilog"), (magma_cb, "coreir-verilog")]: tester.retarget(cb, cb.clk) \ .compile_and_run(directory="test_cb/build", target="verilator", flags=["-Wno-fatal"], magma_output=output)
def test_valid_generation(run_tb): io_core = IOCoreValid() io_core_circuit = io_core.circuit() tester = BasicTester(io_core_circuit, io_core_circuit.clk, io_core_circuit.reset) max_cycle = 16 # mode set to 1 # max cycle set to 16 config_data = [io_core.get_config_data("mode", 1), io_core.get_config_data("max_cycle", max_cycle)] config_data = compress_config_data(config_data) tester.zero_inputs() tester.reset() tester.poke(io_core_circuit.stall, 1) for addr, data in config_data: tester.configure(addr, data) tester.config_read(addr) tester.eval() tester.expect(io_core_circuit.read_config_data, data) # un-stall tester.poke(io_core_circuit.stall, 0) # it should stay low regardless of the IO as long as the reset signal is never high for _f2io_16 in [random_bv(16) for _ in range(10)]: tester.poke(io_core_circuit.f2io_16, _f2io_16) tester.eval() tester.expect(io_core_circuit.io2glb_16, _f2io_16) tester.expect(io_core_circuit.io2glb_1, 0) # hit the start signal tester.poke(io_core_circuit.glb2io_1, 1) tester.eval() # rising clock edge tester.step(2) # de-assert the reset/start signal tester.poke(io_core_circuit.glb2io_1, 0) tester.eval() # from now on it should output 1 until it reaches max cycle for _ in range(max_cycle): tester.expect(io_core_circuit.io2glb_1, 1) tester.step(2) # then stay low for _ in range(max_cycle): tester.expect(io_core_circuit.io2glb_1, 0) tester.step(2) run_tb(tester)
def test_regression(run_tb): io_core = IOCore() io_core_circuit = io_core.circuit() tester = Tester(io_core_circuit) for _glb2io_16, _f2io_16 in \ [(random_bv(16), random_bv(16)) for _ in range(100)]: tester.poke(io_core_circuit.glb2io_16, _glb2io_16) tester.poke(io_core_circuit.f2io_16, _f2io_16) tester.eval() tester.expect(io_core_circuit.io2glb_16, _f2io_16) tester.expect(io_core_circuit.io2f_16, _glb2io_16) for _glb2io_1, _f2io_1 in \ [(random_bv(1), random_bv(1)) for _ in range(100)]: tester.poke(io_core_circuit.glb2io_1, _glb2io_1) tester.poke(io_core_circuit.f2io_1, _f2io_1) tester.eval() tester.expect(io_core_circuit.io2glb_1, _f2io_1) tester.expect(io_core_circuit.io2f_1, _glb2io_1) run_tb(tester)
def get_random_arr(name, port): if issubclass(port, m.Bits) or issubclass(port.T, m.Digital): # TODO: Hack, check the name and don't twiddle config ports, we # should add a config type if "config_" in name: return BitVector[len(port)](0) else: return random_bv(len(port)) else: if issubclass(port.T, m.Array): return fault.array.Array([ get_random_arr(name + f"_{i}", port.T) for i in range(len(port)) ], len(port)) raise NotImplementedError() # pragma: nocover
def add_assumptions(self, circuit, actions, i): main_body = "" for port in circuit.interface.ports.values(): if port.isoutput(): for assumption in self.assumptions: # TODO: Chained assumptions? assume_port = assumption.port if isinstance(assume_port, SelectPath): assume_port = assume_port[-1] if assume_port is port: pred = assumption.value while True: randval = random_bv(len(assume_port)) if pred(randval): break code = self.make_poke( len(actions) + i, Poke(port, randval)) for line in code: main_body += f" {line}\n" break return main_body
import pytest from fault.action_generators import generate_actions_from_streams from common.testers import ResetTester, ConfigurationTester from common.regression_test import check_interfaces from fault.random import random_bv def teardown_function(): for item in glob.glob('genesis_*'): os.system(f"rm -r {item}") # Test 10 random default values with has_constant default_params = [(random_bv(16), 1) for _ in range(2)] # Include one test with no constant default_params += [(random_bv(16), 0)] @pytest.mark.parametrize('default_value,has_constant', default_params) # FIXME: this fails # @pytest.mark.parametrize('num_tracks', range(2,10)) @pytest.mark.parametrize('num_tracks', [10]) def test_regression(default_value, num_tracks, has_constant): feedthrough_outputs = "1111101111" params = { "width": 16, "num_tracks": num_tracks, "feedthrough_outputs": feedthrough_outputs, "has_constant": has_constant,
def test_tile(): core = DummyCore() tile = Tile(core) tile_circ = tile.circuit() # No functional model for tile yet, so we have to use the # standard fault tester for now tester = BasicTester(tile_circ, tile_circ.clk, tile_circ.reset) # assign the tile a random ID for configuration tile_id = random_bv(16) tester.poke(tile_circ.tile_id, tile_id) tester.reset() # Connect random vals to all tile inputs inputs_applied = {} for side_in in (tile_circ.north.I, tile_circ.south.I, tile_circ.east.I, tile_circ.west.I): for i in range(len(side_in.layer1)): port = side_in.layer1[i] rand_input = random_bv(1) inputs_applied[port] = rand_input tester.poke(port, rand_input) for j in range(len(side_in.layer16)): port = side_in.layer16[j] rand_input = random_bv(16) inputs_applied[port] = rand_input tester.poke(port, rand_input) # Write to all configuration registers in the tile # This test should be applicapable to any tile, regardless # of the core it's using data_written = {} for i, feat in enumerate(tile.features()): feat_addr = BitVector(i, 8) for reg in feat.registers.values(): reg_addr = BitVector(reg.addr, 8) upper_config_addr = BitVector.concat(reg_addr, feat_addr) config_addr = BitVector.concat(upper_config_addr, tile_id) # Ensure the register is wide enough to contain the random value rand_data = random_bv(reg.width) # Further restrict random config data values based on feature # Only 0-3 valid for SB config_data if (feat == tile.sb): if ((reg_addr % 2) == 0): rand_data = rand_data % 4 # Only 0-1 valid for SB regs else: rand_data = rand_data % 2 # Only 0-9 valid for CB config_data elif (feat in tile.cbs): rand_data = rand_data % 10 # Make sure we pass 32 bits of config data to configure config_data = BitVector(rand_data, 32) tester.configure(config_addr, config_data) # Keep track of data written so we know what to expect to read back data_written[config_addr] = config_data # Now, read back all the configuration we just wrote for addr in data_written: tester.config_read(addr) expected_data = data_written[addr] tester.expect(tile_circ.read_config_data, expected_data) feat_addr = addr[16:24] reg_addr = addr[24:32] check_all_config(tester, tile_circ, tile, data_written, inputs_applied) # Try writing to tile with wrong tile id for config_addr in data_written: new_tile_id = config_addr[0:16] + 1 upper_config_addr = config_addr[16:32] new_config_addr = BitVector.concat(upper_config_addr, new_tile_id) random_data = random_bv(32) tester.configure(new_config_addr, random_data) # Read all the config back again to make sure nothing changed check_all_config(tester, tile_circ, tile, data_written, inputs_applied) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(target="verilator", magma_output="coreir-verilog", directory=tempdir, flags=["-Wno-fatal"])
def test_regression(num_tracks): params = { "width": 16, "num_tracks": num_tracks, } # Create magma circuit. magma_simple_cb = define_simple_cb(**params) m.compile(f"test_simple_cb/build/{magma_simple_cb.name}", magma_simple_cb, output="coreir-verilog") # Create genesis circuit. genesis_simple_cb = simple_cb_wrapper.generator()( **params, input_files=["simple_cb/genesis/simple_cb.vp"]) genesis_verilog = "genesis_verif/simple_cb.v" shutil.copy(genesis_verilog, "test_simple_cb/build/") # TODO: Do we need this extra instantiation, could the function do it for # us? simple_cb_functional_model = gen_simple_cb(**params)() class MappedCB: def __init__(self, circuit): self.circuit = circuit self.renamed_ports = { "clk": "CLK", "reset": "ASYNCRESET", "out": "O" } def __getattr__(self, field): if self.circuit is magma_simple_cb: if field in self.renamed_ports: field = self.renamed_ports[field] elif "in_" in field: return self.circuit.I[int(field.split("_")[-1])] return getattr(self.circuit, field) check_interfaces(MappedCB(magma_simple_cb), genesis_simple_cb) class SimpleCBTester(ResetTester, ConfigurationTester): pass for simple_cb, clock, reset, output, streams in [ (genesis_simple_cb, genesis_simple_cb.clk, genesis_simple_cb.reset, "verilog", { f"in_{i}": lambda name, port: random_bv(len(port)) for i in range(num_tracks) }), (magma_simple_cb, magma_simple_cb.CLK, magma_simple_cb.ASYNCRESET, "coreir-verilog", { f"I": lambda name, port: [random_bv(len(port.T)) for i in range(num_tracks)] }) ]: input_mapping = None if simple_cb is genesis_simple_cb else ( lambda *args: args[0]) tester = SimpleCBTester(simple_cb, clock, simple_cb_functional_model, reset_port=reset) tester.zero_inputs() for config_data in [BitVector(x, 32) for x in range(0, 1)]: tester.reset() tester.configure(BitVector(0, 32), config_data) tester.actions += \ generate_actions_from_streams( simple_cb, simple_cb_functional_model, streams, input_mapping=input_mapping) tester.compile_and_run(target="verilator", directory="test_simple_cb/build", flags=["-Wno-fatal"], magma_output=output)
def test_simple_pe(ops): pe = define_pe(ops, T=m.UInt, data_width=16) class SimplePETester(ResetTester, ConfigurationTester): pass pe_functional_model = gen_simple_pe(ops, 16)() tester = SimplePETester(pe, pe.clk, pe_functional_model) m.compile("test_simple_pe/build/pe", pe, output="coreir", passes=["rungenerators", "flatten", "cullgraph"]) # For verilator test m.compile(f"test_simple_pe/build/{pe.name}", pe, output="coreir-verilog") tester.zero_inputs() opcode_width = m.bitutils.clog2(len(ops)) config_addr_width = 1 for config_data in [ BitVector(x, opcode_width) for x in range(0, len(ops)) ]: tester.reset() tester.configure(BitVector(0, config_addr_width), config_data) tester.actions += \ generate_actions_from_streams( pe, pe_functional_model, { f"I{i}": lambda name, port: random_bv(len(port)) for i in range(2) }) tester.compile_and_run(directory="test_simple_pe/build", target="verilator", flags=["-Wno-fatal"], magma_output="coreir-verilog") opcode_width = m.bitutils.clog2(len(ops)) op_strs = { operator.add: "+", operator.sub: "-", operator.and_: "&", operator.or_: "|" } for i, op in enumerate(ops): with open(f"test_simple_pe/build/conf_{op.__name__}.ets", "w") as ets: ets.write( config_ets_template.format(config_addr=0, config_addr_width=1, config_data=i, config_data_width=opcode_width)) with open(f"test_simple_pe/build/conf_{op.__name__}.ets", "r") as f: print(f.read()) problem = f"""\ [GENERAL] model_file: pe.json,conf_{op.__name__}.ets add_clock: True assume_if_true: True [DEFAULT] bmc_length: 40 verification: safety [PE check {op.__name__} configuration] description: "Check configuring to opcode={i} results in read_data={i}" formula: (conf_done = 1_1) -> (self.read_data = {i}_{opcode_width}) prove: TRUE expected: TRUE [PE check {op.__name__} functionality] description: "Check configuring to opcode={i} corresponds to {op.__name__}" formula: (conf_done = 1_1) -> ((self.I0 {op_strs[op]} self.I1) = self.O) prove: TRUE expected: TRUE """ # noqa problem_file = f"test_simple_pe/build/problem_pe_{op.__name__}.txt" with open(problem_file, "w") as f: f.write(problem) assert not os.system(f"CoSA --problem {problem_file} --solver z3")