Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mantle] Init ice40 primitives #1302

Draft
wants to merge 9 commits into
base: deprecate-coreir
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions magma/backend/mlir/hardware_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from typing import Any, List, Mapping, Optional, Tuple, Union
import weakref

import hwtypes as ht

from magma.array import Array, ArrayMeta
from magma.backend.mlir.build_magma_graph import (
BuildMagmaGrahOpts, build_magma_graph
Expand Down Expand Up @@ -151,17 +153,23 @@ def magma_type_to_mlir_type(type: Kind) -> MlirType:
@wrap_with_not_implemented_error
@functools.lru_cache()
def python_type_to_mlir_type(type_: type) -> MlirType:
# NOTE(rsetaluri): We only support integer attribtue types right now. All
# integer parameter types are assumed to be int32's.
# NOTE(rsetaluri): All integer parameter types are assumed to be int32's.
if type_ is int:
return builtin.IntegerType(32)
if issubclass(type_, ht.BitVector):
return builtin.IntegerType(len(type_))
if type_ is str:
return hw.StringType()


@wrap_with_not_implemented_error
def python_value_to_mlir_attribtue(value: Any) -> MlirAttribute:
# NOTE(rsetaluri): We only support integer attribute types right now.
def python_value_to_mlir_attribute(value: Any) -> MlirAttribute:
if isinstance(value, int):
return builtin.IntegerAttr(value)
if isinstance(value, ht.BitVector):
return builtin.IntegerAttr(int(value))
if isinstance(value, str):
return builtin.StringAttr(str(value))


def get_module_interface(
Expand Down Expand Up @@ -219,7 +227,7 @@ def _visit_input(i):

def make_hw_param_decl(name: str, value: Any):
type_ = python_type_to_mlir_type(type(value))
value = python_value_to_mlir_attribtue(value)
value = python_value_to_mlir_attribute(value)
return hw.ParamDeclAttr(name, type_, value)


Expand Down Expand Up @@ -1343,9 +1351,8 @@ def new_values(fn, ports):

def _add_module_parameters(self, hw_module: hw.ModuleOpBase):
defn_or_decl = self._magma_defn_or_decl
try:
param_types = defn_or_decl.coreir_config_param_types
except AttributeError:
param_types = defn_or_decl.get_param_types()
if param_types is None:
return
for name, type in param_types.items():
type = python_type_to_mlir_type(type)
Expand Down
6 changes: 6 additions & 0 deletions magma/backend/mlir/hw.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def emit(self) -> str:
return f"!hw.inout<{self.T.emit()}>"


@dataclasses.dataclass(frozen=True)
class StringType(MlirType):
def emit(self) -> str:
return f"!hw.string"


@dataclasses.dataclass(frozen=True)
class InnerRefAttr(MlirAttribute):
module: MlirSymbol
Expand Down
10 changes: 10 additions & 0 deletions magma/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,16 @@ def inline_verilog(cls, inline_str, **kwargs):
with definition_context_manager(cls._context_):
m_inline_verilog(inline_str, **kwargs)

def get_param_types(self):
try:
return self.param_types
except AttributeError:
try:
# try old API
return self.coreir_config_param_types
except AttributeError:
return None


class AnonymousCircuitType(object, metaclass=CircuitKind):
"""Abstract base class for circuits"""
Expand Down
Empty file.
Empty file.
41 changes: 41 additions & 0 deletions magma/mantle/lattice/ice40/adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from magma.bit import Bit
from magma.circuit import Circuit
from magma.interface import IO
from magma.mantle.lattice.ice40.plb import SB_CARRY, A0, A1, A2
from magma.mantle.lattice.ice40.lut import LUT2, LUT3
from magma.t import In, Out


def half_adder(I0, I1):
"""Returns (sum, carry)"""
return LUT2(A0 ^ A1)()(I0, I1), SB_CARRY()(I0, I1, 0)


class HalfAdder(Circuit):
io = IO(
I0=In(Bit),
I1=In(Bit),
O=Out(Bit),
COUT=Out(Bit),
)
sum, carry = half_adder(io.I0, io.I1)
io.O @= sum
io.COUT @= carry


def full_adder(I0, I1, CIN):
"""Returns (sum, carry)"""
return LUT3(A0 ^ A1 ^ A2)()(I0, I1, CIN), SB_CARRY()(I0, I1, CIN)


class FullAdder(Circuit):
io = IO(
I0=In(Bit),
I1=In(Bit),
CIN=In(Bit),
O=Out(Bit),
COUT=Out(Bit),
)
sum, carry = full_adder(io.I0, io.I1, io.CIN)
io.O @= sum
io.COUT @= carry
39 changes: 39 additions & 0 deletions magma/mantle/lattice/ice40/lut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
Convenience wrappers around SB_LUT4
"""
from magma.bit import Bit
from magma.interface import IO
from magma.generator import Generator2
from magma.mantle.lattice.ice40.plb import SB_LUT4
from magma.t import In, Out


class LUT2(Generator2):
def __init__(self, contents):
self.io = IO(
I0=In(Bit),
I1=In(Bit),
O=Out(Bit),
)
self.io.O @= SB_LUT4(LUT_INIT=contents)(
self.io.I0,
self.io.I1,
0,
0
)


class LUT3(Generator2):
def __init__(self, contents):
self.io = IO(
I0=In(Bit),
I1=In(Bit),
I2=In(Bit),
O=Out(Bit),
)
self.io.O @= SB_LUT4(LUT_INIT=contents)(
self.io.I0,
self.io.I1,
self.io.I2,
0
)
60 changes: 60 additions & 0 deletions magma/mantle/lattice/ice40/plb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import hwtypes as ht

from magma.bit import Bit
from magma.circuit import Circuit
from magma.clock import Clock
from magma.interface import IO
from magma.t import In, Out


# Useful variables for programming LUTs
LUTS_PER_LOGICBLOCK = 2
LOG_BITS_PER_LUT = 4
BITS_PER_LUT = 1 << LOG_BITS_PER_LUT

ZERO = 0
ONE = (1 << BITS_PER_LUT) - 1

A0 = ht.BitVector[16](0xAAAA)
A1 = ht.BitVector[16](0xCCCC)
A2 = ht.BitVector[16](0xF0F0)
A3 = ht.BitVector[16](0xFF00)

I0 = A0
I1 = A1
I2 = A2
I3 = A3

ALL = A0 & A1 & A2 & A3
ANY = A0 | A1 | A2 | A3
PARITY = A0 ^ A1 ^ A2 ^ A3


class SB_LUT4(Circuit):
io = IO(
I0=In(Bit),
I1=In(Bit),
I2=In(Bit),
I3=In(Bit),
O=Out(Bit)
)
param_types = {"LUT_INIT": ht.BitVector[16]}


class SB_CARRY(Circuit):
"""Implements (I0&I1)|(I1&I2)|(I2&I0)"""
io = IO(
I0=In(Bit), # must be the same as SB_LUT4 I1 to pack
I1=In(Bit), # must be the same as SB_LUT4 I2 to pack
CI=In(Bit), # must be from previous SB_LUT4 to pack
CO=Out(Bit)
)


class SB_DFF(Circuit):
"""D Flip-Flop"""
io = IO(
C=In(Clock),
D=In(Bit),
Q=Out(Bit)
)
112 changes: 112 additions & 0 deletions magma/mantle/lattice/ice40/pll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import hwtypes as ht

from magma.bit import Bit
from magma.circuit import Circuit
from magma.clock import Clock
from magma.interface import IO
from magma.t import In, Out
from magma.generator import Generator2


def _filter_range(freq_in, divr, divf):
pfd = freq_in / (divr + 1)
vco = pfd * (divf + 1)

if pfd < 17:
return 1
if pfd < 26:
return 2
if pfd < 44:
return 3
if pfd < 66:
return 4
if pfd < 101:
return 5
return 6


def _compute_params(freq_in, freq_out):
freq_in /= 1000000
freq_out /= 1000000

best_freq_out = 0.
for divr in range(16):
pfd = freq_in / (divr + 1)
if pfd < 10 or pfd > 133:
continue

for divf in range(64):
vco = pfd * (divf + 1)
if vco < 533 or vco > 1066:
continue

# The valid range for DIVQ is 1..6.
#
# This is correctly documented in the ICE Technology Library
# Document, but unfortunately incorrectly documented as 0..7
# in the iCE40 sysCLOCK PLL Design and Usage Guide.
for divq in range(1, 7):
fout = vco / (1 << divq)

# simple mode
if abs(fout - freq_out) < abs(best_freq_out - freq_out):
best_div_r = divr
best_div_f = divf
best_div_q = divq
best_freq_out = fout
return (
best_div_r,
best_div_f,
best_div_q,
_filter_range(freq_in, best_div_r, best_div_f)
)


def _make_pll_io():
return IO(
REFERENCECLK=In(Clock),
RESETB=In(Bit),
BYPASS=In(Bit),
PLLOUTCORE=Out(Bit),
PLLOUTGLOBAL=Out(Clock)
)


class SB_PLL40_CORE(Circuit):
io = _make_pll_io()

param_types = {
"FEEDBACK_PATH": str,
"PLLOUT_SELECT": str,
"DIVR": ht.BitVector[4], # Reference clock divider (div+1) [0, ..., 15]
"DIVF": ht.BitVector[7], # Feedback divider (div+1) [0, ..., 63]
"DIVQ": ht.BitVector[3], # VCO divider (divq+1) [0, ..., 6]
"FILTER_RANGE": ht.BitVector[3]
}


class SB_PLL(Generator2):
def __init__(self, freq_out, freq_in=12000000):
"""
TODO(leonardt): This is a param wrapper, so shouldn't necessarily be a
generator
"""
self.name = "SB_PLL"
self.io = _make_pll_io()

divr, divf, divq, filter = _compute_params(freq_in, freq_out)

pll = SB_PLL40_CORE(
FEEDBACK_PATH="SIMPLE",
PLLOUT_SELECT="GENCLK",
DIVR=ht.BitVector[4](divr),
DIVF=ht.BitVector[7](divf),
DIVQ=ht.BitVector[3](divf),
FILTER_RANGE=ht.BitVector[3](filter)
)

pll.REFERENCECLK @= self.io.REFERENCECLK
pll.RESETB @= self.io.RESETB
pll.BYPASS @= self.io.BYPASS
self.io.PLLOUTCORE @= pll.PLLOUTCORE
self.io.PLLOUTGLOBAL @= pll.PLLOUTGLOBAL
2 changes: 2 additions & 0 deletions tests/test_mantle/test_lattice/test_ice40/build/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
Loading
Loading