Start by defining a Python function that we want to compute.
In [1]:
def f(a, b, c):
return (a & b) ^ c
Generate a circuit that computes this function. To implement the logical operations we use standard verilog gates, which are available in mantle.verilog.gates.
In [2]:
import magma as m
import mantle
class VerilatorExample(m.Circuit):
io = m.IO(a=m.In(m.Bit), b=m.In(m.Bit), c=m.In(m.Bit), d=m.Out(m.Bit))
io.d <= f(io.a, io.b, io.c)
m.compile("build/VerilatorExample", VerilatorExample, "coreir-verilog", inline=True)
%cat build/VerilatorExample.v
Next, generate a verilator test harness in C++ for the circuit. The test vectors are generated using the python function f. The verilator test bench compares the output of the simulator to those test vectors.
In [3]:
from itertools import product
from fault import Tester
tester = Tester(VerilatorExample)
for a, b, c in product([0, 1], [0, 1], [0, 1]):
tester.poke(VerilatorExample.a, a)
tester.poke(VerilatorExample.b, b)
tester.poke(VerilatorExample.c, c)
tester.eval()
tester.expect(VerilatorExample.d, f(a, b, c))
tester.print("done!!")
tester.compile_and_run("verilator", directory="build")
%cat build/VerilatorExample_driver.cpp
Using fault, we can use the same tester and (with the same testbench inputs/expectations) and use a different backend, like the python simulator.
In [4]:
tester.compile_and_run("python")