"""
Intermediate Representation Expressions
Represents the subset of Python expressions
that are synthesizable
"""
from __future__ import annotations
from typing import Optional
from python2verilog.utils.generics import GenericRepr
from python2verilog.utils.typed import guard, typed, typed_list, typed_strict
[docs]
class Expression(GenericRepr):
"""
An expression that can be equated
"""
def __init__(self, string: str):
assert isinstance(string, str)
if "_state" in string and self.__class__ == Expression:
raise RuntimeError()
self.string = string
[docs]
def to_string(self) -> str:
"""
To String
"""
return self.string
def __str__(self):
return self.to_string()
def __eq__(self, other: object):
if isinstance(other, Expression):
return self.verilog() == other.verilog()
return False
def __hash__(self):
return hash(self.to_string())
[docs]
def verilog(self) -> str:
"""
In Verilog syntax
"""
return self.to_string()
[docs]
class Int(Expression):
"""
Signed integer literal
"""
def __init__(self, value: int):
self.value = int(typed_strict(value, int)) # Cast bool to int
super().__init__(str(self.__class__))
[docs]
def verilog(self) -> str:
"""
In Verilog
"""
return f"$signed({str(self.value)})"
[docs]
def to_string(self) -> str:
"""
String
"""
return str(self.value)
def __repr__(self):
return f"{self.value}"
[docs]
class UInt(Expression):
"""
Unsigned integer literal
"""
def __init__(self, value: int):
assert isinstance(value, int)
super().__init__(str(value))
def __repr__(self):
return str(self)
[docs]
class Unknown(Expression):
"""
Unknown or "don't care" value
"""
def __init__(self):
super().__init__("'x")
[docs]
class Var(Expression):
"""
Named-variable
"""
def __init__(
self,
py_name: str,
ver_name: str = "",
width: int = 32,
is_signed: bool = True,
initial_value: str = "0",
**_,
):
if ver_name == "":
ver_name = "_" + py_name
self.ver_name = typed_strict(ver_name, str)
self.py_name = typed_strict(py_name, str)
self.width = typed_strict(width, int)
self.is_signed = typed_strict(is_signed, bool)
self.initial_value = initial_value
super().__init__(ver_name)
def __repr__(self):
return f"{self.ver_name}"
[docs]
class State(Var):
"""
State constant
"""
def __init__(
self, name, width: int = 32, isSigned: bool = True, initial_value: str = "0"
):
super().__init__(
name, name, width=width, is_signed=isSigned, initial_value=initial_value
)
[docs]
class ExclusiveVar(Var):
"""
Exclusive Variable
Can only be set once before a clock cycle must occur,
used by the optimizer to determine if it needs to make
a edge clocked or not
"""
def __init__(
self,
py_name: str,
ver_name: str = "",
width: int = 32,
is_signed: bool = True,
initial_value: str = "0",
exclusive_group: Optional[str] = None,
**_,
):
super().__init__(py_name, ver_name, width, is_signed, initial_value, **_)
# Default exclusive group is it's Verilog variable name
if exclusive_group is None:
exclusive_group = self.ver_name
self.exclusive_group = exclusive_group
[docs]
class Ternary(Expression):
"""
<condition> ? <left> : <right>
"""
def __init__(self, condition: Expression, left: Expression, right: Expression):
self.condition = condition
self.left = left
self.right = right
super().__init__(self.to_string())
[docs]
def to_string(self):
return (
f"({self.condition.to_string()} ? {self.left.to_string()}"
f" : {self.right.to_string()})"
)
[docs]
def verilog(self):
return f"({self.condition.verilog()} ? {self.left.verilog()} : {self.right.verilog()})"
[docs]
class UBinOp(Expression):
"""
Unsigned BinOp
Is usually better for comparators
"""
def __init__(self, left: Expression, oper: str, right: Expression):
self.left = typed_strict(left, Expression)
self.right = typed_strict(right, Expression)
self.oper = typed_strict(oper, str)
super().__init__(self.__class__.__name__)
[docs]
def to_string(self):
return f"({self.left.to_string()} {self.oper} {self.right.to_string()})"
[docs]
def verilog(self):
"""
To Verilog
"""
return f"({self.left.verilog()} {self.oper} {self.right.verilog()})"
[docs]
class BinOp(UBinOp):
"""
<left> <op> <right>
In verilog the signed specifier is used.
For mixed unsigned and signed operations, the following page explains well
https://www.01signal.com/verilog-design/arithmetic/signed-wire-reg/
"""
[docs]
def verilog(self):
return "$signed" + super().verilog()
def __repr__(self):
return f"{self.left} {self.oper} {self.right}"
[docs]
class Add(BinOp):
"""
<left> + <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "+", right)
[docs]
class Sub(BinOp):
"""
<left> - <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "-", right)
[docs]
class Mul(BinOp):
"""
<left> * <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "*", right)
[docs]
class Div(BinOp):
"""
<left> / <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "/", right)
[docs]
class LessThan(UBinOp):
"""
<left> < <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "<", right)
[docs]
class Pow(UBinOp):
"""
<left> ** <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "**", right)
class _Mod(UBinOp):
"""
<left> % <right>
"""
def __init__(self, left: Expression, right: Expression):
super().__init__(left, "%", right)
[docs]
class UnaryOp(Expression):
"""
<op>(<expr>)
"""
def __init__(self, oper: str, expr: Expression):
self.oper = typed_strict(oper, str)
self.expr = typed_strict(expr, Expression)
super().__init__(self.__class__.__name__)
[docs]
def to_string(self):
"""
string
"""
return f"{self.oper}({self.expr.to_string()})"
[docs]
def verilog(self):
"""
Verilog
"""
return f"{self.oper}({self.expr.verilog()})"
[docs]
class Mod(BinOp):
"""
<left> % <right>
"""
def __init__(self, left: Expression, right: Expression):
self.left = typed_strict(left, Expression)
self.right = typed_strict(right, Expression)
super().__init__(left, "%", right)
[docs]
def verilog(self):
"""
Verilog
"""
return BinOp(
BinOp(BinOp(self.left, "%", self.right), "+", self.right),
"%",
self.right,
).verilog()
[docs]
def to_string(self):
"""
String
"""
return f"({self.left.to_string()} % {self.right.to_string()})"
[docs]
class FloorDiv(BinOp):
"""
<left> // <right>
Follows Python conventions
"""
def __init__(self, left: Expression, right: Expression):
self.left = typed_strict(left, Expression)
self.right = typed_strict(right, Expression)
super().__init__(left, "//", right)
[docs]
def verilog(self):
"""
Verilog
"""
return Ternary(
condition=BinOp(
BinOp(left=self.left, right=self.right, oper="%"),
"===",
Int(0),
),
left=BinOp(self.left, "/", self.right),
right=BinOp(
BinOp(self.left, "/", self.right),
"-",
BinOp(
UBinOp(
BinOp(self.left, "<", Int(0)),
"^",
BinOp(self.right, "<", Int(0)),
),
"&",
Int(1),
),
),
).verilog()