Types
December 6, 2023 ยท View on GitHub
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:
class <name>(m.Product): k0 = t0 k1 = t1 ...
Enum
Statically typed enumerated type:
class <name>(m.Enum):
k0 = v0
k1 = v1
...
Example
class State(m.Enum):
IDLE = 0
CONFIG = 1
LOOP = 2
FLAG_RESET = 3
class LoopState(m.Enum):
LOOP_READ = 0
LOOP_STIM = 1
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)
Registers
Retain state until updated:
# Note that m.Register is a generator, which returns a circuit, which is then
# instanced
my_reg = m.Regester(m.Bits[32])()
my_reg_init_7 = m.Register(m.Bits[32], init=7)()
my_reg_clock_enable = m.Register(m.Bits[32], init=7, has_enable=True)()
my_reg_uint = m.Register(m.UInt[32], init=7, has_enable=True)()
Define update value by wiring to the input I port
my_reg.I @= next_val
Memories
MyMemCircuit = m.Memory(height, T, read_latency=0, readonly=False, init=None,
has_read_enable=False)
my_mem_inst = MyMemCircuit()
height: number of elementsT: type of each elementreadonly: ROM if True else RAMinit:tupleof initial values of each elementread_latency: number of registers to append to read out porthas_read_enabe: add a read enable port
Memory ports (where addr_width = max(clog2(height - 1), 1)):
RADDR:m.In(m.Bits[addr_width])RDATA:m.Out(T)WADDR:m.In(m.Bits[addr_width])WDATA:m.In(mTCLK:m.In(m.Clock)WE:m.In(m.Bit)RE(optional):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_ = m.Register(m.UInt[16])()
io.O @= sum_(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 using a Generator
class Accum(m.Generator):
def __init__(self, width: int):
self.io = m.IO(I=m.In(m.UInt[width]), O=m.Out(m.UInt[width])) + m.ClockIO()
sum_ = m.Register(m.UInt[width])()
io.O @= sum_(sum_.O + io.I)
Accum8 = Accum(8)
accum8_inst = Accum8()
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
m.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
when
class BasicWhen(m.Circuit):
io = m.IO(I=m.In(m.Bits[2]), S=m.In(m.Bit), O=m.Out(m.Bit))
with m.when(io.S):
io.O @= io.I[0]
with m.otherwise():
io.O @= io.I[1]
when with registers
class RegDefault(m.Circuit):
name = "test_when_reg_ce"
io = m.IO(I=m.In(m.Bits[8]), O=m.Out(m.Bits[8]),
CE=m.In(m.Bit))
x = m.Register(m.Bits[8])()
# Register implicit default is x.I @= x.O
with m.when(io.CE):
x.I @= io.I
io.O @= x.O
when with registers with enable
class RegDefault(m.Circuit):
name = "test_when_reg_ce"
io = m.IO(I=m.In(m.Bits[8]), O=m.Out(m.Bits[8]),
CE=m.In(m.Bit))
x = m.Register(m.Bits[8], has_enable=True)()
with m.when(io.CE):
x.I @= io.I
# x.CE will implicitly be 1 when written
io.O @= x.O