Skip to content

Commit

Permalink
Initial commit for generator exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajsekhar Setaluri committed Nov 8, 2019
1 parent e059d4c commit 2c4b9af
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 0 deletions.
1 change: 1 addition & 0 deletions generator_exercise/.#versions.py
153 changes: 153 additions & 0 deletions generator_exercise/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import abc
import collections
import dataclasses
import functools
import operator
import magma as m
import mantle


class IO:
def __init__(self, **kwargs):
self.interface = {}
for k, v in kwargs.items():
port = v.flip()()
self.interface[k] = type(port)
setattr(self, k, port)

def __repr__(self):
return repr(self.interface)


class CircuitMeta(type):
def __new__(metacls, name, bases, namespace):
namespace.setdefault("name", name)
return super().__new__(metacls, name, bases, namespace)


class Circuit(metaclass=CircuitMeta):
pass


class Generator1Meta(abc.ABCMeta):
def __new__(metacls, name, bases, namespace):
cls = super().__new__(metacls, name, bases, namespace)
return dataclasses.dataclass(cls)


class Generator1(metaclass=Generator1Meta):
@abc.abstractmethod
def generate(self):
raise NotImplementedError()


class Generator2Meta(abc.ABCMeta):
def __new__(metacls, name, bases, namespace):
cls = super().__new__(metacls, name, bases, namespace)
return dataclasses.dataclass(cls)


class Generator2(metaclass=Generator2Meta):
@abc.abstractmethod
def elaborate(self):
raise NotImplementedError()

def __post_init__(self):
self.elaborate()
if not hasattr(self, "IO"):
raise Exception("Must set IO")


class Optional:
def __init__(self, param, typ):
self.param = param
self.typ = typ


class IOTemplate:
def __init__(self, **kwargs):
self.ports = kwargs


class Symbolic:
def __init__(self, name, typ):
self.name = name
self.typ = typ


class CustomNamespace(collections.UserDict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.___annotations = None

def __setitem__(self, key, value):
if key is "__annotations__":
value = collections.UserDict()
self.___annotations = value
super().__setitem__(key, value)

def __getitem__(self, key):
if not self.___annotations or key not in self.___annotations:
return super().__getitem__(key)
return Symbolic(key, self.___annotations[key])


def elaborate_type(value, params):
if isinstance(value, Symbolic):
return getattr(params, value.name)
if isinstance(value, Optional):
option = elaborate_type(value.param, params)
if not option:
return None
return elaborate_type(value.typ, params)
# hacky...
if isinstance(value, m.UIntKind):
if not isinstance(value.N, Symbolic):
return value
N = elaborate_type(value.N, params)
return m.UInt[N]
if isinstance(value, m.BitKind):
return value
raise NotImplementedError(value, params)


class Generator3Meta(abc.ABCMeta):
def __new__(metacls, name, bases, namespace):
namespace = dict(namespace)
cls = super().__new__(metacls, name, bases, namespace)
return dataclasses.dataclass(cls)

def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)

@classmethod
def __prepare__(metacls, name, bases, **kwargs):
return CustomNamespace()


class Generator3(metaclass=Generator3Meta):
def interface(self):
ports = {}
for k, v in self.template.ports.items():
typ = elaborate_type(v, self)
if typ is not None:
ports[k] = typ
return IO(**ports)

@abc.abstractmethod
def elaborate(self):
raise NotImplementedError()

def __post_init__(self):
self.elaborate()
if not hasattr(self, "IO"):
raise Exception("Must set IO")


m.IO = IO
m.Circuit = Circuit
m.Optional = Optional
m.IOTemplate = IOTemplate
m.Generator1 = Generator1
m.Generator2 = Generator2
m.Generator3 = Generator3
25 changes: 25 additions & 0 deletions generator_exercise/current.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import magma as m
import common


def generate_add(bits, has_ce):
ports = dict(I0=m.In(m.UInt[bits]),
I1=m.In(m.UInt[bits]),
O=m.Out(m.UInt[bits]))
if has_ce:
ports.update(dict(CE=m.In(m.Bit)))

class _Add(m.Circuit):
name = f"Add_{bits}_{has_ce}"
IO = m.IO(**ports)

out = IO.I0 + IO.I1
if has_ce:
out += m.zext(m.uint(IO.CE), bits - 1)
m.wire(out, IO.O)

return _Add


Add16 = generate_add(16, False)
Add8_CE = generate_add(8, True)
25 changes: 25 additions & 0 deletions generator_exercise/ergonomic_class_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import magma as m
import common

class Add(m.Generator2):
bits: int
has_ce: bool

def elaborate(self):
ports = dict(I0=m.In(m.UInt[self.bits]),
I1=m.In(m.UInt[self.bits]),
O=m.Out(m.UInt[self.bits]))
if self.has_ce:
ports.update(dict(CE=m.In(m.Bit)))

self.name = f"Add_{self.bits}_{self.has_ce}"
self.IO = m.IO(**ports)

out = self.IO.I0 + self.IO.I1
if self.has_ce:
out += m.zext(m.uint(self.IO.CE), self.bits - 1)
m.wire(out, self.IO.O)


Add16 = Add(16, False)
Add8_CE = Add(8, True)
33 changes: 33 additions & 0 deletions generator_exercise/holy_grail_class_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import magma as m
import common


class Add(m.Generator3):
bits: int
has_ce: bool

template = m.IOTemplate(
I0=m.In(m.UInt[bits]),
I1=m.In(m.UInt[bits]),
O=m.Out(m.UInt[bits]),
CE=m.Optional(has_ce, m.In(m.Bit))
)

def elaborate(self):
self.name = f"Add_{self.bits}_{self.has_ce}"
self.IO = self.interface()

out = self.IO.I0 + self.IO.I1
if self.has_ce:
out += m.zext(m.uint(self.IO.CE), self.bits - 1)
m.wire(out, self.IO.O)


Add16 = Add(16, False)
Add8_CE = Add(8, True)


# Want to be able to do this!
# x = m.UInt[24]()
# y = m.UInt[24]()
# out = Add(x, y)
31 changes: 31 additions & 0 deletions generator_exercise/simple_class_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import magma as m
import common


class Add(m.Generator1):
bits: int
has_ce: bool

def generate(self):
ports = dict(I0=m.In(m.UInt[self.bits]),
I1=m.In(m.UInt[self.bits]),
O=m.Out(m.UInt[self.bits]))
if self.has_ce:
ports.update(dict(CE=m.In(m.Bit)))

class _Add(m.Circuit):
name = f"Add_{self.bits}_{self.has_ce}"
IO = m.IO(**ports)

out = IO.I0 + IO.I1
if self.has_ce:
out += m.zext(m.uint(IO.CE), self.bits - 1)
m.wire(out, IO.O)

return _Add


Add16Gen = Add(16, False)
Add16 = Add16Gen.generate()
Add8_CEGen = Add(8, True)
Add8_CE = Add8_CEGen.generate()
67 changes: 67 additions & 0 deletions generator_exercise/versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import magma as m
import mantle




## Current version
## End Current version ##


## Trivial class version
## End Trivial class version ##


## Slightly better class version
class Add(m.Generator2):
bits: int
has_ce: bool

def elaborate(self):
ports = dict(I0=m.In(m.UInt[self.bits]),
I1=m.In(m.UInt[self.bits]),
O=m.Out(m.UInt[self.bits]))
if self.has_ce:
ports.update(dict(CE=m.In(m.Bit)))

self.name = f"Add_{self.bits}_{self.has_ce}"
self.IO = IO(**ports)

out = self.IO.I0 + self.IO.I1
if self.has_ce:
out += m.zext(m.uint(self.IO.CE), self.bits - 1)
m.wire(out, self.IO.O)

Add16 = Add(16, False)
Add8_CE = Add(8, True)
## End Slightly better class ##


## Another variation version ##
class Add(m.Generator3):
bits: int
has_ce: bool

template = m.IOTemplate(
I0=m.In(m.UInt[bits]),
I1=m.In(m.UInt[bits]),
O=m.Out(m.UInt[bits]),
CE=m.Optional(has_ce, m.In(m.Bit))
)

def elaborate(self):
self.name = f"Add_{self.bits}_{self.has_ce}"
self.IO = self.interface()

out = self.IO.I0 + self.IO.I1
if self.has_ce:
out += m.zext(m.uint(self.IO.CE), self.bits - 1)
m.wire(out, self.IO.O)

Add16 = Add(16, False)
Add8_CE = Add(8, True)

x = m.UInt[24]()
y = m.UInt[24]()
#out = Add(x, y)
## End Another variation version ##

0 comments on commit 2c4b9af

Please sign in to comment.