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

Experimental Version #54

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ check:
flake8 .

test:
pytest -v --cov

pytest -v --cov --doctest-modules

cov: test
codecov

format:
yapf --recursive -i py_hcl tests examples

clean:
rm -rf .eggs dist py_hcl.egg-info .coverage .pytest_cache */__pycache__


upload: test
python setup.py sdist && twine upload dist/*
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
[![codecov](https://codecov.io/gh/scutdig/py-hcl/branch/master/graph/badge.svg)](https://codecov.io/gh/scutdig/py-hcl)
[![PyPI](https://img.shields.io/pypi/v/py-hcl.svg)](https://pypi.python.org/pypi)

PyHCL is a hardware construct language like [Chisel](https://github.com/freechipsproject/chisel3) but more lightweight and more relaxed to use.
As a novel hardware construction framework embedded in Python, PyHCL supports several useful features include object-oriented, functional programming,
PyHCL is a hardware construct language similar to [Chisel](https://github.com/freechipsproject/chisel3) but more lightweight and more relaxed to use.
As a novel hardware construction framework embedded in Python, PyHCL supports several useful features including object-oriented, functional programming,
and dynamically typed objects.

The goal of PyHCL is providing a complete design and verification tool flow for heterogeneous computing systems flexibly using the same design methodology.
The goal of PyHCL is providing a complete design and verification toolchain for heterogeneous computing systems.

PyHCL is powered by [FIRRTL](https://github.com/freechipsproject/firrtl), an intermediate representation for digital circuit design. With the FIRRTL
PyHCL is powered by [FIRRTL](https://github.com/freechipsproject/firrtl), an intermediate representation for digital circuit design. Leveraging the FIRRTL
compiler framework, PyHCL-generated circuits can be compiled to the widely-used HDL Verilog.


## Getting Started

#### Installing PyHCL
#### Install PyHCL

```shell script
$ pip install py-hcl
```

#### Writing A Full Adder
#### Write a Full Adder
PyHCL defines modules using only simple Python syntax that looks like this:
```python
from py_hcl import *
Expand All @@ -42,14 +42,16 @@ class FullAdder(Module):
io.cout <<= io.a & io.b | io.b & io.cin | io.a & io.cin
```

#### Compiling To FIRRTL
#### Compile To FIRRTL

Compiling module by calling `compile_to_firrtl`:
Compile module via `compile_to_firrtl`:
```python
from py_hcl import compile_to_firrtl

compile_to_firrtl(FullAdder, 'full_adder.fir')
```

Will generate the following FIRRTL codes:
Will generate the following FIRRTL code:
```
circuit FullAdder :
module FullAdder :
Expand All @@ -72,7 +74,7 @@ circuit FullAdder :
FullAdder_io_cout <= _T_6
```

#### Compiling To Verilog
#### Compile To Verilog

While FIRRTL is generated, PyHCL's job is complete. To further compile to Verilog, the [FIRRTL compiler framework](
https://github.com/freechipsproject/firrtl) is required:
Expand Down
File renamed without changes.
37 changes: 37 additions & 0 deletions examples/full_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import tempfile
import os

from py_hcl import Module, IO, Input, Bool, Output


class FullAdder(Module):
io = IO(
a=Input(Bool),
b=Input(Bool),
cin=Input(Bool),
sum=Output(Bool),
cout=Output(Bool),
)

# Generate the sum
io.sum <<= io.a ^ io.b ^ io.cin

# Generate the carry
io.cout <<= io.a & io.b | io.b & io.cin | io.a & io.cin


def main():
tmp_dir = tempfile.mkdtemp()
path = os.path.join(tmp_dir, "full_adder.fir")

FullAdder.compile_to_firrtl(path)

with open(path) as f:
print(f.read())

os.remove(path)
os.removedirs(tmp_dir)


if __name__ == '__main__':
main()
42 changes: 42 additions & 0 deletions py_hcl/compile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,52 @@


def compile_to_firrtl(module_class, path=None):
"""
Compiles PyHCL Module `module_class` to FIRRTL source code file.

Examples
--------

Define a PyHCL module:

>>> from py_hcl import *
>>> class N(Module):
... io = IO(
... i=Input(U.w(8)),
... o=Output(U.w(8)),
... )
... io.o <<= io.i

Compile to FIRRTL:

>>> from tempfile import mktemp
>>> tmp_file = mktemp()
>>> compile_to_firrtl(N, tmp_file)

Read the content:

>>> with open(tmp_file) as f:
... print(f.read())
circuit N :
module N :
input clock : Clock
input reset : UInt<1>
input N_io_i : UInt<8>
output N_io_o : UInt<8>
<BLANKLINE>
N_io_o <= N_io_i
<BLANKLINE>
<BLANKLINE>

>>> from os import remove
>>> remove(tmp_file)
"""

m = convert(module_class.packed_module)

if path is None:
path = module_class.packed_module.name + ".fir"

with open(path, 'wb') as f:
m.serialize_stmt(f)
f.flush()
4 changes: 2 additions & 2 deletions py_hcl/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
def install_ops():
import py_hcl.core.expr.add # noqa: F401
import py_hcl.core.expr.and_ # noqa: F401
import py_hcl.core.expr.ands # noqa: F401
import py_hcl.core.expr.xor # noqa: F401
import py_hcl.core.expr.or_ # noqa: F401
import py_hcl.core.expr.ors # noqa: F401
import py_hcl.core.stmt.connect # noqa: F401
import py_hcl.core.expr.field # noqa: F401
import py_hcl.core.expr.slice # noqa: F401
Expand Down
2 changes: 1 addition & 1 deletion py_hcl/core/error/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from py_hcl.error import PyHclError
from py_hcl.utils.error import PyHclError


class CoreError(PyHclError):
Expand Down
14 changes: 8 additions & 6 deletions py_hcl/core/expr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from multipledispatch.dispatcher import MethodDispatcher

from py_hcl.core.hcl_ops import op_apply
from py_hcl.core.stmt.connect import ConnSide
from py_hcl.core.stmt.connect import VariableType
from py_hcl.core.type import UnknownType, HclType
from py_hcl.utils import json_serialize
from py_hcl.utils.serialization import json_serialize


class ExprIdGen:
Expand All @@ -30,7 +30,7 @@ def get(cls, i):
@json_serialize
class HclExpr(object):
hcl_type = UnknownType()
conn_side = ConnSide.UNKNOWN
variable_type = VariableType.UNKNOWN

def __new__(cls, *args):
obj = super().__new__(cls)
Expand Down Expand Up @@ -94,10 +94,12 @@ def to_bool(self):
return op_apply('to_bool')(self)


@json_serialize(json_fields=['id', 'type', 'hcl_type', 'conn_side', 'op_node'])
@json_serialize(
json_fields=['id', 'type', 'hcl_type', 'variable_type', 'op_node'])
class ExprHolder(HclExpr):
def __init__(self, hcl_type: HclType, conn_side: ConnSide, op_node):
def __init__(self, hcl_type: HclType, variable_type: VariableType,
op_node):
self.type = 'expr_holder'
self.hcl_type = hcl_type
self.conn_side = conn_side
self.variable_type = variable_type
self.op_node = op_node
72 changes: 57 additions & 15 deletions py_hcl/core/expr/add.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,54 @@
"""
Implement addition operation for pyhcl values.

Examples
--------

>>> from py_hcl import U, S, Wire, Bundle


Add two literals of Uint type:

>>> res = U(1) + U(2)


Add two literals of Sint type:

>>> res = S(1) + S(2)


Add two wires of Uint type:

>>> w1 = Wire(U.w(8)); w2 = Wire(U.w(9))
>>> res = w1 + w2


Add two wires of Vector type:

>>> w1 = Wire(U.w(8)[8]); w2 = Wire(U.w(9)[8])
>>> res = w1 + w2


Add two wires of Bundle type:

>>> w1 = Wire(Bundle(a=U.w(2), b=~S.w(3)))
>>> w2 = Wire(Bundle(a=U.w(3), b=~S.w(4)))
>>> res = w1 + w2
"""

from py_hcl.core.expr import ExprHolder
from py_hcl.core.expr.bundle_holder import BundleHolder
from py_hcl.core.expr.error import ExprError
from py_hcl.core.expr.utils import assert_right_side
from py_hcl.core.expr.utils import ensure_all_args_are_readable
from py_hcl.core.expr.vec_holder import VecHolder
from py_hcl.core.hcl_ops import op_register
from py_hcl.core.stmt.connect import ConnSide
from py_hcl.core.stmt.connect import VariableType
from py_hcl.core.type import HclType
from py_hcl.core.type.bundle import BundleT, Dir
from py_hcl.core.type.bundle import BundleT, BundleDirection
from py_hcl.core.type.sint import SIntT
from py_hcl.core.type.uint import UIntT
from py_hcl.core.type.vector import VectorT
from py_hcl.utils import json_serialize
from py_hcl.utils.serialization import json_serialize


@json_serialize
Expand All @@ -25,34 +63,34 @@ def __init__(self, left, right):


@adder(UIntT, UIntT)
@assert_right_side
@ensure_all_args_are_readable
def _(lf, rt):
w = max(lf.hcl_type.width, rt.hcl_type.width) + 1
t = UIntT(w)
return ExprHolder(t, ConnSide.RT, Add(lf, rt))
return ExprHolder(t, VariableType.ReadOnly, Add(lf, rt))


@adder(SIntT, SIntT)
@assert_right_side
@ensure_all_args_are_readable
def _(lf, rt):
w = max(lf.hcl_type.width, rt.hcl_type.width) + 1
t = SIntT(w)
return ExprHolder(t, ConnSide.RT, Add(lf, rt))
return ExprHolder(t, VariableType.ReadOnly, Add(lf, rt))


@adder(VectorT, VectorT)
@assert_right_side
@ensure_all_args_are_readable
def _(lf, rt):
# TODO: Accurate Error Message
assert lf.hcl_type.size == rt.hcl_type.size
if lf.hcl_type.size != rt.hcl_type.size:
raise ExprError.unmatched_vec_size(lf.hcl_type.size, rt.hcl_type.size)

values = [lf[i] + rt[i] for i in range(lf.hcl_type.size)]
v_type = VectorT(values[0].hcl_type, len(values))
return VecHolder(v_type, ConnSide.RT, values)
return VecHolder(v_type, VariableType.ReadOnly, values)


@adder(BundleT, BundleT)
@assert_right_side
@ensure_all_args_are_readable
def _(lf, rt):
# TODO: Accurate Error Message
assert set(lf.hcl_type.fields.keys()) == set(rt.hcl_type.fields.keys())
Expand All @@ -61,10 +99,14 @@ def _(lf, rt):
bd_values = {}
for k in lf.hcl_type.fields.keys():
res = getattr(lf, k) + getattr(rt, k)
bd_type_fields[k] = {"dir": Dir.SRC, "hcl_type": res.hcl_type}
bd_type_fields[k] = {
"dir": BundleDirection.SOURCE,
"hcl_type": res.hcl_type
}
bd_values[k] = res

return BundleHolder(BundleT(bd_type_fields), ConnSide.RT, bd_values)
return BundleHolder(BundleT(bd_type_fields), VariableType.ReadOnly,
bd_values)


@adder(HclType, HclType)
Expand Down
Loading