Skip to content

Commit

Permalink
Reduced hardware usage with nested for loops (#182)
Browse files Browse the repository at this point in the history
- Improved errors
  • Loading branch information
WorldofKerry authored Oct 23, 2023
1 parent 509d861 commit b52262d
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 70 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "python2verilog"
version = "0.4.1"
version = "0.4.2"
authors = [{ name = "Kerry Wang", email = "[email protected]" }]
description = "Converts a subset of python generator functions into synthesizable sequential SystemVerilog"
readme = "README.md"
Expand Down
10 changes: 5 additions & 5 deletions python2verilog/api/verilogify.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ def generator_wrapper(*args, **kwargs):
if not context.input_types:
context.input_types = [type(arg) for arg in args]
for val in context.input_types:
assert (
val == int
), f"Unexpected {val} as a input type {list(map(type, args))}"
assert val == int, f"Unexpected {val} as a input type"
else:
context.check_input_types(args)

Expand All @@ -110,7 +108,9 @@ def tuplefy(either: Union[int, tuple[int]]) -> tuple[int]:
elif isinstance(either, tuple):
ret = either
else:
raise StaticTypingError(f"Unexpected yielded value {either}")
raise StaticTypingError(
f"Unexpected yielded value `{either}` from `{func.__name__}`"
)
return ret

# Always get output one-ahead of what func user sees
Expand Down Expand Up @@ -186,7 +186,7 @@ def tuplefy(either: Union[int, tuple[int]]) -> tuple[int]:
assert guard(value, int)
except Exception as e:
raise StaticTypingError(
"Expected `int` type inputs and outputs"
f"Expected `int` type inputs and outputs for `{func.__name__}`"
) from e
return ret

Expand Down
21 changes: 13 additions & 8 deletions python2verilog/exceptions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ class UnsupportedSyntaxError(Exception):
Python syntax was not within the supported subset
"""

def __init__(self, *args: object) -> None:
def __init__(self, msg: object) -> None:
super().__init__(
"Python syntax was not within the supported subset",
*args,
msg,
)

@classmethod
def from_pyast(cls, node: ast.AST):
def from_pyast(cls, node: ast.AST, name: str):
"""
Based on AST error
"""
inst = cls(f"Unsupported Python syntax {ast.dump(node)}")
inst = cls(
f"Unsupported Python syntax `{ast.unparse(node)}` found in function "
f"`{name}` as {ast.dump(node)}"
)
return inst


Expand All @@ -49,10 +51,13 @@ class TypeInferenceError(Exception):
Type inferrence failed, either use the function in code or provide type hints
"""

def __init__(self, *args: object) -> None:
def __init__(self, name: str) -> None:
"""
:param name: function name
"""
msg = (
"Input/output type inferrence failed, "
f"Input/output type inferrence failed for function `{name}`, "
"either use the function in Python code or provide type hints",
)

super().__init__(" ".join(map(str, (*msg, *args))))
super().__init__(msg)
92 changes: 41 additions & 51 deletions python2verilog/frontend/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def _parse_stmt(
assert guard(dummy.child, ir.Edge)
return dummy, [dummy.child]

raise TypeError(f"Unexpected statement {pyast.dump(stmt)}")
raise UnsupportedSyntaxError.from_pyast(stmt, self.__context.name)

def _parse_return(self, ret: pyast.Return, prefix: str) -> ParseResult:
"""
Expand All @@ -195,12 +195,15 @@ def _parse_return(self, ret: pyast.Return, prefix: str) -> ParseResult:
return done, []
assert not self.__context.is_generator

if isinstance(ret.value, pyast.Tuple):
stmts = [self._parse_expression(c) for c in ret.value.elts]
elif isinstance(ret.value, pyast.expr):
stmts = [self._parse_expression(ret.value)]
else:
raise TypeError(f"Expected tuple {type(ret.value)} {pyast.dump(ret)}")
try:
if isinstance(ret.value, pyast.Tuple):
stmts = [self._parse_expression(c) for c in ret.value.elts]
elif isinstance(ret.value, pyast.expr):
stmts = [self._parse_expression(ret.value)]
else:
raise UnsupportedSyntaxError.from_pyast(ret, self.__context.name)
except Exception as e:
raise UnsupportedSyntaxError.from_pyast(ret, self.__context.name) from e

self.__context.validate()
head, tail = self._weave_nonclocked_edges(
Expand Down Expand Up @@ -256,7 +259,7 @@ def _parse_assign_to_call(self, assign: pyast.Assign, prefix: str) -> ParseResul
target = assign.targets[0]
assert isinstance(target, pyast.Name)

def get_func_call_names(caller_cxt: ir.Context):
def get_func_call_names():
"""
:return: target_name, func_name
"""
Expand All @@ -269,21 +272,9 @@ def get_func_call_names(caller_cxt: ir.Context):
assert guard(func, pyast.Name)
func_name = func.id

if target_name in map(
lambda x: x.py_name,
(
*caller_cxt.local_vars,
*caller_cxt.input_vars,
*caller_cxt.output_vars,
),
):
raise StaticTypingError(
f"{target_name} changed type from another type to generator instance"
)

return target_name, func_name

target_name, func_name = get_func_call_names(self.__context)
target_name, func_name = get_func_call_names()

# Get context of generator function being called
callee_cxt = self.__context.namespace[func_name]
Expand All @@ -299,7 +290,7 @@ def get_func_call_names(caller_cxt: ir.Context):
call_args=assign.value.args,
callee_cxt=callee_cxt,
targets=assign.targets,
target_name=target_name,
_target_name=target_name,
prefix=prefix,
)

Expand All @@ -308,7 +299,7 @@ def _parse_func_call(
call_args: list[pyast.expr],
targets: list[pyast.expr],
callee_cxt: ir.Context,
target_name: str,
_target_name: str,
prefix: str,
) -> ParseResult:
"""
Expand All @@ -317,7 +308,7 @@ def _parse_func_call(
Implemented as an inline (no external unit).
"""
callee_cxt, body_head, prev_tails = Function(
callee_cxt, prefix=f"{prefix}_{target_name}_"
callee_cxt, prefix=f"{prefix}_"
).parse_inline()

arguments = list(map(self._parse_expression, call_args))
Expand Down Expand Up @@ -474,7 +465,7 @@ def _parse_for_gen_call(self, stmt: pyast.For, prefix: str) -> ParseResult:
assert guard(stmt.iter, pyast.Call)
gen_cxt = self.__context.namespace[self._get_func_call_name(stmt.iter)]

mangled_name = f"{prefix}_offset{stmt.col_offset}" # consider nested for loops
mangled_name = f"nested{stmt.col_offset}" # consider nested for loops

call_head, call_tails = self._parse_gen_call(
call_args=stmt.iter.args,
Expand Down Expand Up @@ -849,30 +840,27 @@ def _parse_expression(self, expr: pyast.expr) -> ir.Expression:
"""
<expression> (e.g. constant, name, subscript, etc., those that return a value)
"""
try:
if isinstance(expr, pyast.Constant):
return ir.Int(expr.value)
if isinstance(expr, pyast.Name):
return self.__context.make_var(expr.id)
if isinstance(expr, pyast.Subscript):
return self._parse_subscript(expr)
if isinstance(expr, pyast.BinOp):
return self._parse_binop(expr)
if isinstance(expr, pyast.UnaryOp):
if isinstance(expr.op, pyast.USub):
return ir.UnaryOp("-", self._parse_expression(expr.operand))
if isinstance(expr, pyast.Compare):
return self._parse_compare(expr)
if isinstance(expr, pyast.BoolOp):
if isinstance(expr.op, pyast.And):
return ir.UBinOp(
self._parse_expression(expr.values[0]),
"&&",
self._parse_expression(expr.values[1]),
)
except Exception as e:
raise UnsupportedSyntaxError.from_pyast(expr) from e
raise UnsupportedSyntaxError.from_pyast(expr)
if isinstance(expr, pyast.Constant):
return ir.Int(expr.value)
if isinstance(expr, pyast.Name):
return self.__context.make_var(expr.id)
if isinstance(expr, pyast.Subscript):
return self._parse_subscript(expr)
if isinstance(expr, pyast.BinOp):
return self._parse_binop(expr)
if isinstance(expr, pyast.UnaryOp):
if isinstance(expr.op, pyast.USub):
return ir.UnaryOp("-", self._parse_expression(expr.operand))
if isinstance(expr, pyast.Compare):
return self._parse_compare(expr)
if isinstance(expr, pyast.BoolOp):
if isinstance(expr.op, pyast.And):
return ir.UBinOp(
self._parse_expression(expr.values[0]),
"&&",
self._parse_expression(expr.values[1]),
)
raise UnsupportedSyntaxError.from_pyast(expr, self.__context.name)

def _parse_subscript(self, node: pyast.Subscript) -> ir.Expression:
"""
Expand Down Expand Up @@ -907,7 +895,9 @@ def _parse_compare(self, node: pyast.Compare) -> ir.UBinOp:
elif isinstance(node.ops[0], pyast.Eq):
operator = "==="
else:
raise UnsupportedSyntaxError(f"Unknown operator {pyast.dump(node.ops[0])}")
raise UnsupportedSyntaxError(
f"Unsupported operator {pyast.dump(node.ops[0])}"
)
return ir.UBinOp(
left=self._parse_expression(node.left),
oper=operator,
Expand Down Expand Up @@ -942,4 +932,4 @@ def _parse_binop(self, expr: pyast.BinOp) -> ir.Expression:
left = self._parse_expression(expr.left)
right = self._parse_expression(expr.right)
return ir.Mod(left, right)
raise UnsupportedSyntaxError(f"Unexpected binop type {pyast.dump(expr.op)}")
raise UnsupportedSyntaxError(f"Unsupported binop `{pyast.dump(expr.op)}")
9 changes: 5 additions & 4 deletions python2verilog/ir/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def input_mapper(arg: ast.arg) -> type[Any]:
try:
self.input_types = list(map(input_mapper, input_args))
except Exception as e:
raise TypeInferenceError() from e
raise TypeInferenceError(self.name) from e

def _use_output_type_hints(self):
"""
Expand Down Expand Up @@ -150,7 +150,7 @@ def output_mapper(arg: ast.Name) -> type[Any]:
try:
self.output_types = list(map(output_mapper, output_args))
except Exception as e:
raise TypeInferenceError(f"in function `{self.name}`") from e
raise TypeInferenceError(self.name) from e
self.default_output_vars()

def validate(self):
Expand Down Expand Up @@ -296,7 +296,8 @@ def add_special_local_var(self, var: Var):
"""
if var.py_name in self.generator_instances:
raise StaticTypingError(
f"{var.py_name} changed type from generator instance to another type"
f"Variable `{var.py_name}` changed type from generator"
f" instance to another type in {self.name}"
)
if var not in (*self._local_vars, *self.input_vars, *self.output_vars):
self._local_vars.append(typed_strict(var, Var))
Expand Down Expand Up @@ -367,7 +368,7 @@ def create_generator_instance(self, name: str) -> Instance:
)
)

signals = ProtocolSignals(prefix=f"{self.prefix}{name}_{self.name}__")
signals = ProtocolSignals(prefix=f"{self.prefix}{self.name}_{name}__")

return Instance(
self.name,
Expand Down
1 change: 0 additions & 1 deletion python2verilog/optimizer/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ def backwards_replace(
"""
expr = copy.deepcopy(expr)
if isinstance(expr, ir.Var):
# if not isinstance(expr, ir.ExclusiveVar):
for key in mapping:
if key.to_string() == expr.to_string():
return mapping[key]
Expand Down
1 change: 1 addition & 0 deletions python2verilog/utils/generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ class GenericReprAndStr(GenericRepr):
Implements a generic __repr__ and __str__ based on self.__dict__
"""

@reprlib.recursive_repr()
def __str__(self):
return f"{self.__class__.__name__}\n{pretty_dict(self.__dict__)}"
18 changes: 18 additions & 0 deletions tests/integration/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,21 @@ def fib_product(n):
for num in fib(n):
prod = multiplier(num, num)
yield prod


def multi_funcs(a, b):
"""
Testing multiple function calls and tested function calls
"""
temp = multiplier(a, b)
yield temp
temp = multiplier(a + 10, b)
yield temp
for i in p2vrange(0, 2, 1):
yield i
for i in p2vrange(0, 2, 1):
yield i
for i in p2vrange(0, 2, 1):
yield i
for i in p2vrange(0, 2, 1):
yield i
2 changes: 2 additions & 0 deletions tests/integration/test_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from .utils import name_func

PARAMETERS = [
([multi_funcs, multiplier, p2vrange], [(13, 17)]),
([fib_product, multiplier, fib, p2vrange], [10, 20]),
([fib_product, multiplier, fib, p2vrange], [10, 20]),
([fib, p2vrange], range(10, 31, 10)),
([quad_multiply, multiplier_generator], [(3, 7), (31, 43)]),
Expand Down

0 comments on commit b52262d

Please sign in to comment.