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

Assume/Assert Cell Support #18

Closed
wants to merge 4 commits into from
Closed
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
2 changes: 1 addition & 1 deletion nmigen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal, Assert, Assume
from .hdl.dsl import Module
from .hdl.cd import ClockDomain
from .hdl.ir import Fragment, Instance
Expand Down
6 changes: 6 additions & 0 deletions nmigen/back/pysim.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ def run(state):
lhs(state, normalize(rhs(state), shape))
return run

def on_Assert(self, stmt):
raise NotImplementedError("Asserts not yet implemented for Simulator backend.") # :nocov:

def on_Assume(self, stmt):
pass # :nocov:

def on_Switch(self, stmt):
test = self.rrhs_compiler(stmt.test)
cases = []
Expand Down
20 changes: 20 additions & 0 deletions nmigen/back/rtlil.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,26 @@ def on_Assign(self, stmt):
stmt.rhs, lhs_bits, lhs_sign)
self._case.assign(self.lhs_compiler(stmt.lhs), rhs_sigspec)

def on_Assert(self, stmt):
self(stmt._en.eq(1))
self(stmt._check.eq(stmt.test))
en_wire = self.rhs_compiler(stmt._en)
check_wire = self.rhs_compiler(stmt._check)
self.state.rtlil.cell("$assert", ports={
"\\A": check_wire,
"\\EN": en_wire,
}, params={ }, src=src(stmt.test.src_loc))

def on_Assume(self, stmt):
self(stmt._en.eq(1))
self(stmt._check.eq(stmt.test))
en_wire = self.rhs_compiler(stmt._en)
check_wire = self.rhs_compiler(stmt._check)
self.state.rtlil.cell("$assume", ports={
"\\A": check_wire,
"\\EN": en_wire,
}, params={ }, src=src(stmt.test.src_loc))

def on_Switch(self, stmt):
self._check_rhs(stmt.test)

Expand Down
64 changes: 62 additions & 2 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"Value", "Const", "C", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
"Array", "ArrayProxy",
"Signal", "ClockSignal", "ResetSignal",
"Statement", "Assign", "Switch", "Delay", "Tick", "Passive",
"ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet",
"Statement", "Assign", "Assert", "Assume", "Switch", "Delay", "Tick",
"Passive", "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict",
"SignalSet",
]


Expand Down Expand Up @@ -826,6 +827,65 @@ def __repr__(self):
return "(eq {!r} {!r})".format(self.lhs, self.rhs)


class Assert(Statement):
def __init__(self, test, _en=None, _check=None):
self.test = Value.wrap(test)
if _en is None:
self._en = Signal(1, reset_less=True,
name="assert${}$en".format(src(self.test.src_loc)))
self._en.src_loc = self.test.src_loc
else:
self._en = _en

if _check is None:
self._check = Signal(1, reset_less=True,
name="assert${}$check".format(src(self.test.src_loc)))
self._check.src_loc = self.test.src_loc
else:
self._check = _check

def _lhs_signals(self):
return ValueSet((self._en, self._check))

def _rhs_signals(self):
return self.test._rhs_signals()

def __repr__(self):
return "(assert {!r})".format(self.test)


def src(src_loc):
file, line = src_loc
return "{}:{}".format(file, line)


class Assume(Statement):
def __init__(self, test, _en=None, _check=None):
self.test = Value.wrap(test)
if _en is None:
self._en = Signal(1, reset_less=True,
name="assume${}$en".format(src(self.test.src_loc)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This produces utterly gigantic IL statements like connect \A \assert$/home/whitequark/Projects/Boneless-CPU/boneless/gateware/formal.py:25$check. I think it's sufficient to call them something like $assume$en`

self._en.src_loc = self.test.src_loc
else:
self._en = _en

if _check is None:
self._check = Signal(1, reset_less=True,
name="assume${}$check".format(src(self.test.src_loc)))
else:
self._check = _check
self._check.src_loc = self.test.src_loc

def _lhs_signals(self):
return ValueSet((self._en, self._check))

def _rhs_signals(self):
return self.test._rhs_signals()

def __repr__(self):
return "(assume {!r})".format(self.test)


class Switch(Statement):
def __init__(self, test, cases):
self.test = Value.wrap(test)
Expand Down
4 changes: 2 additions & 2 deletions nmigen/hdl/dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,9 @@ def domain_name(domain):
self._pop_ctrl()

for assign in Statement.wrap(assigns):
if not compat_mode and not isinstance(assign, Assign):
if not compat_mode and not isinstance(assign, (Assign, Assert, Assume)):
raise SyntaxError(
"Only assignments may be appended to d.{}"
"Only assignments, asserts, and assumes may be appended to d.{}"
.format(domain_name(domain)))

for signal in assign._lhs_signals():
Expand Down
33 changes: 33 additions & 0 deletions nmigen/hdl/xfrm.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ class StatementVisitor(metaclass=ABCMeta):
def on_Assign(self, stmt):
pass # :nocov:

@abstractmethod
def on_Assert(self, stmt):
pass # :nocov:

@abstractmethod
def on_Assume(self, stmt):
pass # :nocov:

@abstractmethod
def on_Switch(self, stmt):
pass # :nocov:
Expand All @@ -155,6 +163,10 @@ def on_unknown_statement(self, stmt):
def on_statement(self, stmt):
if type(stmt) is Assign:
return self.on_Assign(stmt)
elif type(stmt) is Assert:
return self.on_Assert(stmt)
elif type(stmt) is Assume:
return self.on_Assume(stmt)
elif isinstance(stmt, Switch):
# Uses `isinstance()` and not `type() is` because nmigen.compat requires it.
return self.on_Switch(stmt)
Expand All @@ -174,6 +186,12 @@ def on_value(self, value):
def on_Assign(self, stmt):
return Assign(self.on_value(stmt.lhs), self.on_value(stmt.rhs))

def on_Assert(self, stmt):
return Assert(self.on_value(stmt.test), _en=stmt._en, _check=stmt._check)

def on_Assume(self, stmt):
return Assume(self.on_value(stmt.test), _en=stmt._en, _check=stmt._check)

def on_Switch(self, stmt):
cases = OrderedDict((k, self.on_statement(s)) for k, s in stmt.cases.items())
return Switch(self.on_value(stmt.test), cases)
Expand Down Expand Up @@ -294,6 +312,10 @@ class SwitchCleaner(StatementVisitor):
def on_Assign(self, stmt):
return stmt

on_Assert = on_Assign

on_Assume = on_Assign

def on_Switch(self, stmt):
cases = OrderedDict((k, self.on_statement(s)) for k, s in stmt.cases.items())
if any(len(s) for s in cases.values()):
Expand Down Expand Up @@ -338,6 +360,10 @@ def groups(self):
def on_Assign(self, stmt):
self.unify(*stmt._lhs_signals())

on_Assert = on_Assign

on_Assume = on_Assign

def on_Switch(self, stmt):
for case_stmts in stmt.cases.values():
self.on_statements(case_stmts)
Expand All @@ -362,6 +388,13 @@ def on_Assign(self, stmt):
if any_lhs_signal in self.signals:
return stmt

def on_Assert(self, stmt):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

any_lhs_signal = next(iter(stmt._lhs_signals()))
if any_lhs_signal in self.signals:
return stmt

on_Assume = on_Assert


class _ControlInserter(FragmentTransformer):
def __init__(self, controls):
Expand Down
2 changes: 1 addition & 1 deletion nmigen/test/test_hdl_dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_d_wrong(self):
def test_d_asgn_wrong(self):
m = Module()
with self.assertRaises(SyntaxError,
msg="Only assignments may be appended to d.sync"):
msg="Only assignments, asserts, and assumes may be appended to d.sync"):
m.d.sync += Switch(self.s1, {})

def test_comb_wrong(self):
Expand Down