Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into nd-set-index
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed Jan 29, 2024
2 parents 44bd8ad + 818ad9b commit 3e7bc6f
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 18 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/linux-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ jobs:
python -c "import magma"
popd
- name: Test examples
run: |
cd examples
# TODO(leonardt): Merge with top-level coverage
pytest --pycodestyle tests/ . --ignore=riscv_mini
cd -
- name: Test riscv_mini
run: |
cd examples/riscv_mini
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ $ brew install verilator gmp mpfr libmpc

### Ubuntu
```
$ sudo apt-get install make gcc g++ verilator libgmp-dev libmpfr-dev libmpc-dev
$ sudo apt-get install make gcc g++ verilator libgmp-dev libmpfr-dev libmpc-dev cmake
```

## Arch
Expand Down Expand Up @@ -150,9 +150,7 @@ $ pip install -e .

Install testing infrastructure and run tests to validate the setup
```
$ pip install pytest # note that magma requires pytest version 3.3 or later
$ pip install fault
$ pip install kratos
$ pip install pytest fault # you may need to add $HOME/.local/bin to your $PATH
$ pytest tests
```

Expand Down
10 changes: 0 additions & 10 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import pytest
from magma.config import config as magma_config
from magma.util import reset_global_context


def pytest_configure(config):
magma_config.compile_dir = 'callee_file_dir'
# TODO(leonardt): Enable this globally for testing
# Revert a3a2452168f2251277a14548d053a9ca7a45bff7 for golds.
#
# magma_config.use_namer_dict = True
magma_config.use_uinspect = True


@pytest.fixture(autouse=True)
def magma_test():
reset_global_context()
11 changes: 7 additions & 4 deletions docs/primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,23 +249,26 @@ Slice function interfaces:
* width: constant slice width


### set_bit
### set_index
Similar to set_slice/get_slice, this function allows you to dynamically set the
value of a bit in a Bits value. Here's an example:
value of an index in an Array value. Here's an example:

```python
class SetBit(m.Circuit):
io = m.IO(I=m.In(m.Bits[4]),
val=m.In(m.Bit),
idx=m.In(m.Bits[2]),
O=m.Out(m.Bits[4]))
io.O @= m.set_bit(io.I, io.val, io.idx)
io.O @= m.set_index(io.I, io.val, io.idx)
```

Interface:
```python
def set_bit(target: Bits, value: Bit, idx: UInt):
def set_index(target: Array, value: Type, idx: UInt):
"""
Returns a new value where index `idx` of value `target` is set to `value`
* `target` - a value of type `Array[N, T]`
* `value` - a value of type `T`
* `idx` - a value of type `UInt[clog2(N)]`
"""
```
Empty file added examples/__init__.py
Empty file.
42 changes: 42 additions & 0 deletions examples/adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import magma as m
from .full_adder import FullAdder


class Adder(m.Generator):
"""
n-bit adder with a carry-in and carry-out
"""
def __init__(self, n: int):
self.io = io = m.IO(
A=m.In(m.UInt[n]),
B=m.In(m.UInt[n]),
CIN=m.In(m.Bit),
SUM=m.Out(m.UInt[n]),
COUT=m.Out(m.Bit)
)
curr_cin = io.CIN
for i in range(n):
next_sum, curr_cin = FullAdder()(io.A[i], io.B[i], curr_cin)
io.SUM[i] @= next_sum
io.COUT @= curr_cin


class Adder2(m.Generator):
"""
As higher-order circuit
"""
def __init__(self, n: int):
self.io = io = m.IO(
A=m.In(m.UInt[n]),
B=m.In(m.UInt[n]),
CIN=m.In(m.Bit),
SUM=m.Out(m.UInt[n]),
COUT=m.Out(m.Bit)
)
cout, _sum = m.braid(
(FullAdder() for _ in range(n)),
joinargs=["A", "B", "C"],
foldargs={"cin": "cout"}
)(io.A, io.B, io.CIN)
io.SUM @= _sum
io.COUT @= cout
35 changes: 35 additions & 0 deletions examples/full_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import magma as m


class FullAdder(m.Circuit):
# Interface ports are declared using the m.IO object
# A port has a name (appears before the =) and a type (appears after
# the =)
# The type must have a direction (In, Out, or InOut)
io = m.IO(
a=m.In(m.Bit),
b=m.In(m.Bit),

cin=m.In(m.Bit),
sum_=m.Out(m.Bit),
cout=m.Out(m.Bit)
)
# We can assign references to output wires to temporary python
# variables (in this case, a_xor_b refers to the output of the XOr

# circuit instanced by using the `^` (xor) operator)

a_xor_b = io.a ^ io.b
# To wire the output of an instance (e.g. the output of the instance
# created by a_xor_b ^ cin) to an output port (sum_), we use the `@=`
# operator
io.sum_ @= a_xor_b ^ io.cin

# Here's another example of assignment (for python variables) and
# wiring (for magma ports). We assign three temporaries for the
# outputs of various & (and) operators. Then we wire the result of
# "or"ing (|) these together to the `cout` output port
a_and_b = io.a & io.b
b_and_cin = io.b & io.cin
a_and_cin = io.a & io.cin
io.cout @= a_and_b | b_and_cin | a_and_cin
83 changes: 83 additions & 0 deletions examples/register_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import magma as m


class RegisterFile(m.Generator):
def __init__(self, n: int, T: m.Type, has_read_enable: bool = False):
addr_len = m.bitutils.clog2(n)
self.io = m.IO(
raddr=m.In(m.Bits[addr_len]),
rdata=m.Out(T),
wen=m.In(m.Enable),
waddr=m.In(m.Bits[addr_len]),
wdata=m.In(T)
) + m.ClockIO()
if has_read_enable:
self.io += m.IO(ren=m.In(m.Enable))


class RegisterFileWhen(RegisterFile):
def __init__(self, n: int, T: m.Type, has_read_enable: bool = False):
super().__init__(n, T, has_read_enable)

regs = [m.Register(T)() for _ in range(n)]

rdata = T()
rdata @= 0
for i, reg in enumerate(regs):
with m.when((self.io.waddr == i) & self.io.wen):
reg.I @= self.io.wdata

with m.when(self.io.raddr == i):
rdata @= reg.O

if has_read_enable:
rdata = m.Register(T, has_enable=True)()(rdata, self.io.ren)

self.io.rdata @= rdata


class RegisterFileComprehension(RegisterFile):
def __init__(self, n: int, T: m.Type, has_read_enable: bool = False):
super().__init__(n, T, has_read_enable)

regs = [
m.Register(T, has_enable=True)()(
self.io.wdata, (self.io.waddr == i) & self.io.wen
)
for i in range(n)
]

rdata = T()
rdata @= 0
for i, reg in enumerate(regs):
with m.when(self.io.raddr == i):
rdata @= reg

if has_read_enable:
rdata = m.Register(T, has_enable=True)()(rdata, self.io.ren)

self.io.rdata @= rdata


class RegisterFileBraid(RegisterFile):
def __init__(self, n: int, T: m.Type, has_read_enable: bool = False):
super().__init__(n, T, has_read_enable)

enables = m.array(
[(self.io.waddr == i) & self.io.wen for i in range(n)]
)
regs = m.braid(
[m.Register(T, has_enable=True)() for _ in range(n)],
forkargs=['I']
)(self.io.wdata, enables)

rdata = T()
rdata @= 0
for i, reg in enumerate(regs):
with m.when(self.io.raddr == i):
rdata @= reg

if has_read_enable:
rdata = m.Register(T, has_enable=True)()(rdata, self.io.ren)

self.io.rdata @= rdata
Empty file added examples/tests/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions examples/tests/test_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import random
import pytest

import fault as f
from hwtypes import BitVector


from ..adder import Adder, Adder2


@pytest.mark.parametrize("N", [random.randint(1, 16) for _ in range(4)])
@pytest.mark.parametrize("_Adder", [Adder, Adder2])
def test_adder(N, _Adder):
tester = f.Tester(_Adder(N))
tester.circuit.A = A = BitVector.random(N)
tester.circuit.B = B = BitVector.random(N)
tester.circuit.CIN = CIN = BitVector.random(1)

tester.eval()
tester.circuit.SUM.expect(A + B + CIN.zext(N - 1))
tester.circuit.COUT.expect(
(A.zext(1) + B.zext(1) + CIN.zext(N))[-1]
)
tester.compile_and_run("verilator", magma_output="mlir-verilog")
17 changes: 17 additions & 0 deletions examples/tests/test_full_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ..full_adder import FullAdder
import fault as f
from hwtypes import BitVector


def test_full_adder():
tester = f.Tester(FullAdder)
for _ in range(4):
tester.circuit.a = a = BitVector.random(1)
tester.circuit.b = b = BitVector.random(1)

tester.circuit.cin = cin = BitVector.random(1)
tester.eval()
tester.circuit.sum_.expect(a ^ b ^ cin)
tester.circuit.cout.expect((a & b) | (b & cin) | (a & cin))

tester.compile_and_run("verilator", magma_output="mlir-verilog")
45 changes: 45 additions & 0 deletions examples/tests/test_register_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import tempfile
import pytest
import random

from ..register_file import (
RegisterFileWhen, RegisterFileComprehension, RegisterFileBraid
)

import fault as f
from hwtypes import BitVector
import magma as m


@pytest.mark.parametrize('has_read_enable', [True, False])
@pytest.mark.parametrize('RegisterFile', [RegisterFileWhen,
RegisterFileComprehension,
RegisterFileBraid])
def test_register_file(RegisterFile, has_read_enable):
tester = f.SynchronousTester(RegisterFile(4, m.Bits[4], has_read_enable))
data = [BitVector.random(4) for _ in range(4)]
for i in random.sample(range(4), 4):
tester.circuit.waddr = i
tester.circuit.wdata = data[i]
tester.circuit.wen = 1
tester.advance_cycle()
tester.circuit.wen = 0
for i in random.sample(range(4), 4):
tester.circuit.raddr = i
if has_read_enable:
tester.circuit.ren = 1
tester.advance_cycle()
tester.circuit.rdata.expect(data[i])
if has_read_enable:
last = data[i]
for i in range(3):
tester.circuit.raddr = BitVector.random(2)
tester.circuit.ren = 0
tester.advance_cycle()
tester.circuit.rdata.expect(last)
with tempfile.TemporaryDirectory() as tmp_dir:
tester.compile_and_run(
"verilator",
directory=tmp_dir,
magma_output="mlir-verilog"
)
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from magma.config import config as magma_config


def pytest_configure(config):
magma_config.compile_dir = 'callee_file_dir'
# TODO(leonardt): Enable this globally for testing
# Revert a3a2452168f2251277a14548d053a9ca7a45bff7 for golds.
#
# magma_config.use_namer_dict = True
magma_config.use_uinspect = True

0 comments on commit 3e7bc6f

Please sign in to comment.