In [1]:
import magma as m
In the last example, we defined a function that created a
toggle flip-flop (TFF) from a DFF and an XOR gate.
Let's convert the TFF to a Circuit
.
In Magma
a Circuit
is equivalent to a verilog module.
Circuits can be instanced and then wired to other circuits.
m.ClockIO()
appends Magma
's standard clock interface ports to the interface. When no parameters are specified, this just adds the port CLK
with type In(Clock)
.
In [2]:
from mantle import DFF
class TFF(m.Circuit):
io = m.IO(O=m.Out(m.Bit)) + m.ClockIO()
ff = DFF()
m.wire( ff(~ff.O), io.O )
Let's inspect the interface to see the result of appending m.ClockIO()
.
In [3]:
print(TFF)
Now we'll define a generator for our RippleCounter that accepts a single argument width
. A generator in magma
is a subclass of m.Generator
that defines a static method generate
which returns Magma
Circuit.
In [4]:
class RippleCounter(m.Generator):
@staticmethod
def generate(width: int):
class _RippleCounter(m.Circuit):
name = f'Ripple{width}'
io = m.IO(O=m.Out(m.Bits[width])) + m.ClockIO()
tffs = [TFF(name=f"tff{i}") for i in range(width)]
O = io.CLK
for i in range(width):
m.wire(m.clock(O), tffs[i].CLK)
O = tffs[i].O
m.wire(O, io.O[i])
return _RippleCounter
Now we can generate a 4-bit RippleCounter
by calling the generate
function directly.
In [5]:
Ripple4 = RippleCounter.generate(4)
print(repr(Ripple4))
Let's test our circuit using fault. Magma's Python simulator does not support asynchronous logic, so we'll use verilator
.
In [6]:
import fault
tester = fault.Tester(Ripple4, Ripple4.CLK)
for i in range(1 << 4):
tester.step(2)
tester.print("O=%x\n", Ripple4.O)
tester.compile_and_run(target="verilator", disp_type="realtime")
We can also look at the generated verilog
In [7]:
m.compile("build/ripple", Ripple4, inline=True)
In [8]:
%%bash
cat build/ripple.v
In [ ]: