NOTE: This is a work in progress, please let us know via issue/gitter/email if you'd like to see anything added to this.
This is inspired by the chisel cheatsheet and will be rendered in a similar single page layout soon.
Types
Bit
m.Bit: boolean valuem.VCC, m.GND: boolean literals
Bits and Integers
m.Bits[N]: lengthNbit vector with bitwise logical operators definedm.UInt[N]: lengthNunsigned integer that includes Bits operators and unsigned arithmetic (e.g.+,-, ...) and comparison operators (e.g.<,<=, ...)m.SInt[N]: lengthNsigned integer that includes Bits operators and signed arithmetic (e.g.+,-, ...) and comparison operators (e.g.<,<=, ...)
Array
m.Array[N, T]: fixed length array of lengthNcontaining values of typeTwith equality operator (==) defined
Tuples and Products
m.Tuple[t0, t1, ...]: heterogenous compound data type containing members of typet0,t1, ... Values of such types can be indexed using integer keys like Python tuples (e.g. for a tuple valuex,type(x[0]) == t0).m.Product.from_fields(name, dict(k0=t0, k1=t1, ...)): heterogenous compound data type containing members of typet0,t1, ... Values of such types can be indexed using attributes (e.g. for a product valuex,type(x.k0) == t0).name="anon"makes this type anonymous.- Alternate syntax:
python class <name>(m.Product): k0 = t0 k1 = t1 ...
Enum
Statically typed enumerated type:
class <name>(m.Enum):
k0 = v0
k1 = v1
...
Type qualifiers
m.In(T), m.Out(T), m.InOut(T) qualify type T to be an input, output, and
inout respectively.
Type conversions
m.bits(value, n=None): convertvalueto anBits(same rules asm.arrayexcept if the input is a magma type or list, the members must be convertible to a Bit (e.g.Bit,bool,0,1))m.array(value, n=None): convertvalueto an array.valuemust be a magma type (Bit,Tuple,Array), a Python integer (e.g. literal), or a Python sequence (e.g. list). IfnisNone, the width of the array is inferred fromvalue(e.g. length of the list, bit length of the integer, length of the magma type)m.uint(value, n=None): convertvalueto anUInt(same rules asm.bits, except will not convertm.SInttom.UInt)m.sint(value, n=None): convertvalueto anSInt(same rules asm.bits, except will not convertm.UInttom.SInt)
Math Helpers
Contained in the module magma.math
* m.math.log2_ceil(x: int) -> int: log2(x) rounded up
* m.math.log2_floor(x: int) -> int: log2(x) rounded down
* m.math.is_pow2(x: int) -> bool: True if x is a power of 2
Registers
Retain state until updated:
my_reg = mantle.Register(32)
my_reg_init_7 = mantle.Register(32, init=7)
my_reg_clock_enable = mantle.Register(32, init=7, has_ce=True)
my_reg_uint = mantle.Register(32, init=7, has_ce=True, T=m.UInt)
Define update value by wiring to the input I port
my_reg.I @= next_val
Set N to None for a Register of a single Bit (setting N to 1 will
produce a register of Bits[1])
bit_reg = mantle.Register(None)
Memories
MyMemCircuit = mantle.DefineMemory(height, width, readonly=False, read_latency=0)
my_mem_inst = MyMemCircuit()
height: number of elementswidth: width of each elementreadonly: ROM if True else RAMread_latency: number of registers to append to read out port
Memory ports (where addr_width = max(clog2(height - 1), 1)):
RADDR:m.In(m.Bits[addr_width])RDATA:m.Out(m.Bits[width])WADDR:m.In(m.Bits[addr_width])WDATA:m.In(m.Bits[width])CLK:m.In(m.Clock)WE:m.In(m.Bit)
Circuits
Defining: subclass m.Circuit
class Accum16(m.Circuit):
io = m.IO(I=m.In(m.UInt[16]), O=m.Out(m.UInt[16])) + m.ClockIO()
sum_ = mantle.Register(16)
io.O @= sum_(m.uint(sum_.O) + io.I)
Usage: circuits are used by instancing them inside another definitions and their ports are accessed using dot notation
my_module = Accum16()
my_module.I @= some_data
sum_ = my_module.O
Wiring: wire an output to an input using @= operator (statically typed)
Metaprogramming: abstract over parameters by generating a circuit definition inside a closure
def define_accum(width: int):
class Accum(m.Circuit):
name = f"Accum{width}"
io = m.IO(I=m.In(m.UInt[width]), O=m.Out(m.UInt[width])) + m.ClockIO()
sum_ = mantle.Register(width)
io.O @= sum_(m.uint(sum_.O) + io.I)
return Accum
Operators
Infix operators
All types support the following operators:
- Equal ==
- Not Equal !=
The Bit type supports the following logical operators.
- And &
- Or |
- Exclusive or ^
- Not ~
The Array type family supports the following operator.
- Dynamic bit selection my_arry[add.O] (select a bit dynamically using a magma value).
The Bits type family supports the following logical operators.
- And & (element-wise)
- Or | (element-wise)
- Exclusive or ^ (element-wise)
- Not ~ (element-wise)
- Logical right shift (with zeros) >>
- Logical left shift (with zeros) <<
The UInt and SInt types support all the logical operators
as well as arithmetic and comparison operators.
- Add +
- Subtract/Negate -
- Multiply *
- Divide /
- Less than <
- Less than or equal <=
- Greater than >
- Greater than or equal >=
Note that the the right shift operator when applied to an SInt becomes
an arithmetic shift right operator (which replicates the sign bit as it shifts right).
Functional operators
mantle.mux(*I, S)(constraint:len(S) == log2_ceil(len(I))): selectI[S].m.zext(v, n): zero extend arrayvbynm.sext(v, n): sign extend arrayvbynm.concat(*arrays): concat arrays togetherm.repeat(value, n): create an array repeatingvaluentimes
Combinational
@m.circuit.combinational
def basic_if(I: m.Bits[2], S: m.Bit) -> m.Bit:
if S:
return I[0]
else:
return I[1]
produces
basic_if = DefineCircuit("basic_if", "I", In(Bits[2]), "S", In(Bit), "O", Out(Bit))
Mux2xOutBit_inst0 = Mux2xOutBit()
wire(basic_if.I[1], Mux2xOutBit_inst0.I0)
wire(basic_if.I[0], Mux2xOutBit_inst0.I1)
wire(basic_if.S, Mux2xOutBit_inst0.S)
wire(Mux2xOutBit_inst0.O, basic_if.O)
EndCircuit()
See here for more details.
Sequential
@m.circuit.sequential(async_reset=True)
class DelayBy2:
def __init__(self):
self.x: m.Bits[2] = m.bits(0, 2)
self.y: m.Bits[2] = m.bits(0, 2)
def __call__(self, I: m.Bits[2]) -> m.Bits[2]:
O = self.y
self.y = self.x
self.x = I
return O
See here for more details.