Skip to content

Commit 54dd41d

Browse files
wanda-phiwhitequark
authored andcommitted
amaranth._nir: implement new Match cell.
Both `Matches` and `PriorityMatch` cells are obsoleted by the new cell. This isn't really a functional change, as the validity rules on the `PriorityMatch` inputs imposed by RTLIL backend forced the netlist to conform to the same rules as the new composite cell.
1 parent 4a242ff commit 54dd41d

File tree

5 files changed

+331
-497
lines changed

5 files changed

+331
-497
lines changed

amaranth/back/rtlil.py

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ def emit_cell_wires(self):
585585
wire = self.emit_driven_wire(_nir.Value(nets))
586586
self.instance_wires[cell_idx, name] = wire
587587
continue # Instances use one wire per output, not per cell.
588-
elif isinstance(cell, (_nir.PriorityMatch, _nir.Matches)):
588+
elif isinstance(cell, _nir.Match):
589589
continue # Inlined into assignment lists.
590590
elif isinstance(cell, (_nir.SyncPrint, _nir.AsyncPrint, _nir.SyncProperty,
591591
_nir.AsyncProperty, _nir.Memory, _nir.SyncWritePort)):
@@ -737,41 +737,25 @@ def emit_assignments(case, cond):
737737
search_cond = assign.cond
738738
while True:
739739
if search_cond == cond:
740-
# We have found the PriorityMatch cell that we should enter.
740+
# We have found the Match cell that we should enter.
741741
break
742742
if search_cond == _nir.Net.from_const(1):
743743
# If this isn't nested condition, go back to parent invocation.
744744
return
745-
# Grab the PriorityMatch cell that is on the next level of nesting.
746-
priority_cell_idx = search_cond.cell
747-
priority_cell = self.netlist.cells[priority_cell_idx]
748-
assert isinstance(priority_cell, _nir.PriorityMatch)
749-
search_cond = priority_cell.en
750-
# We assume that:
751-
# 1. PriorityMatch inputs can only be Match cell outputs, or constant 1.
752-
# 2. All Match cells driving a given PriorityMatch cell test the same value.
753-
# Grab the tested value from a random Match cell.
754-
test = _nir.Value()
755-
for net in priority_cell.inputs:
756-
if net != _nir.Net.from_const(1):
757-
matches_cell = self.netlist.cells[net.cell]
758-
assert isinstance(matches_cell, _nir.Matches)
759-
test = matches_cell.value
760-
break
745+
# Grab the Match cell that is on the next level of nesting.
746+
match_cell_idx = search_cond.cell
747+
match_cell = self.netlist.cells[match_cell_idx]
748+
assert isinstance(match_cell, _nir.Match)
749+
search_cond = match_cell.en
761750
# Now emit cases for all PriorityMatch inputs, in sequence. Consume as many
762751
# assignments as possible along the way.
763-
switch = case.switch(self.sigspec(test))
764-
for bit, net in enumerate(priority_cell.inputs):
765-
subcond = _nir.Net.from_cell(priority_cell_idx, bit)
766-
if net == _nir.Net.from_const(1):
752+
switch = case.switch(self.sigspec(match_cell.value))
753+
for bit, pattern_list in enumerate(match_cell.patterns):
754+
subcond = _nir.Net.from_cell(match_cell_idx, bit)
755+
if pattern_list == ("-" * len(match_cell.value),):
767756
emit_assignments(switch.default(), subcond)
768757
else:
769-
# Validate the above assumptions.
770-
matches_cell = self.netlist.cells[net.cell]
771-
assert isinstance(matches_cell, _nir.Matches)
772-
assert test == matches_cell.value
773-
patterns = matches_cell.patterns
774-
emit_assignments(switch.case(patterns), subcond)
758+
emit_assignments(switch.case(pattern_list), subcond)
775759

776760
lhs = _nir.Value(_nir.Net.from_cell(cell_idx, bit) for bit in range(len(cell.default)))
777761
proc = self.builder.process(src_loc=cell.src_loc)
@@ -1235,10 +1219,8 @@ def emit_cells(self):
12351219
cell = self.netlist.cells[cell_idx]
12361220
if isinstance(cell, _nir.Top):
12371221
pass
1238-
elif isinstance(cell, _nir.Matches):
1239-
pass # Matches is only referenced from PriorityMatch cells and inlined there
1240-
elif isinstance(cell, _nir.PriorityMatch):
1241-
pass # PriorityMatch is only referenced from AssignmentList cells and inlined there
1222+
elif isinstance(cell, _nir.Match):
1223+
pass # Match is only referenced from AssignmentList cells and inlined there
12421224
elif isinstance(cell, _nir.AssignmentList):
12431225
self.emit_assignment_list(cell_idx, cell)
12441226
elif isinstance(cell, _nir.Operator):

amaranth/hdl/_ir.py

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,7 @@ def __init__(self, netlist: _nir.Netlist, design: Design, *, all_undef_to_ff=Fal
707707
self.drivers = _ast.SignalDict()
708708
self.io_ports: dict[_ast.IOPort, int] = {}
709709
self.rhs_cache: dict[int, tuple[_nir.Value, bool, _ast.Value]] = {}
710-
self.matches_cache = {}
711-
self.priority_match_cache = {}
710+
self.match_cache = {}
712711
self.fragment_module_idx: dict[Fragment, int] = {}
713712

714713
# Collected for driver conflict diagnostics only.
@@ -787,24 +786,14 @@ def emit_operator(self, module_idx: int, operator: str, *inputs: _nir.Value, src
787786
op = _nir.Operator(module_idx, operator=operator, inputs=inputs, src_loc=src_loc)
788787
return self.netlist.add_value_cell(op.width, op)
789788

790-
def emit_matches(self, module_idx: int, value: _nir.Value, patterns, *, src_loc):
791-
key = module_idx, value, patterns, src_loc
789+
def emit_match(self, module_idx: int, en: _nir.Net, value: _nir.Value, patterns, *, src_loc):
790+
key = module_idx, en, value, patterns, src_loc
792791
try:
793-
return self.matches_cache[key]
792+
return self.match_cache[key]
794793
except KeyError:
795-
cell = _nir.Matches(module_idx, value=value, patterns=patterns, src_loc=src_loc)
796-
net, = self.netlist.add_value_cell(1, cell)
797-
self.matches_cache[key] = net
798-
return net
799-
800-
def emit_priority_match(self, module_idx: int, en: _nir.Net, inputs: _nir.Value, *, src_loc):
801-
key = module_idx, en, inputs, src_loc
802-
try:
803-
return self.priority_match_cache[key]
804-
except KeyError:
805-
cell = _nir.PriorityMatch(module_idx, en=en, inputs=inputs, src_loc=src_loc)
806-
res = self.netlist.add_value_cell(len(inputs), cell)
807-
self.priority_match_cache[key] = res
794+
cell = _nir.Match(module_idx, en=en, value=value, patterns=patterns, src_loc=src_loc)
795+
res = self.netlist.add_value_cell(len(patterns), cell)
796+
self.match_cache[key] = res
808797
return res
809798

810799
def unify_shapes_bitwise(self,
@@ -956,17 +945,16 @@ def emit_rhs(self, module_idx: int, value: _ast.Value) -> tuple[_nir.Value, bool
956945
result = self.emit_operator(module_idx, 'm', test, operand_a, operand_b,
957946
src_loc=value.src_loc)
958947
else:
959-
conds = []
960948
elems = []
961-
for patterns, elem, in value.cases:
962-
if patterns is not None:
963-
net = self.emit_matches(module_idx, test, patterns, src_loc=value.src_loc)
964-
conds.append(net)
949+
patterns = []
950+
for pattern_list, elem, in value.cases:
951+
if pattern_list is not None:
952+
patterns.append(pattern_list)
965953
else:
966-
conds.append(_nir.Net.from_const(1))
954+
patterns.append(("-" * len(test),))
967955
elems.append(self.emit_rhs(module_idx, elem))
968-
conds = self.emit_priority_match(module_idx, _nir.Net.from_const(1),
969-
_nir.Value(conds), src_loc=value.src_loc)
956+
conds = self.emit_match(module_idx, _nir.Net.from_const(1), test, tuple(patterns),
957+
src_loc=value.src_loc)
970958
shape = _ast.Shape._unify(
971959
_ast.Shape(len(value), signed)
972960
for value, signed in elems
@@ -1056,14 +1044,10 @@ def emit_assign(self, module_idx: int, cd: "_cd.ClockDomain | None", lhs: _ast.V
10561044
offset, _signed = self.emit_rhs(module_idx, lhs.offset)
10571045
width = len(lhs.value)
10581046
num_cases = min((width + lhs.stride - 1) // lhs.stride, 1 << len(offset))
1059-
conds = []
1047+
patterns = []
10601048
for case_index in range(num_cases):
1061-
subcond = self.emit_matches(module_idx, offset,
1062-
(to_binary(case_index, len(offset)),),
1063-
src_loc=lhs.src_loc)
1064-
conds.append(subcond)
1065-
conds = self.emit_priority_match(module_idx, cond, _nir.Value(conds),
1066-
src_loc=lhs.src_loc)
1049+
patterns.append((to_binary(case_index, len(offset)),))
1050+
conds = self.emit_match(module_idx, cond, offset, tuple(patterns), src_loc=lhs.src_loc)
10671051
for idx, subcond in enumerate(conds):
10681052
start = lhs_start + idx * lhs.stride
10691053
if start >= width:
@@ -1075,17 +1059,15 @@ def emit_assign(self, module_idx: int, cd: "_cd.ClockDomain | None", lhs: _ast.V
10751059
self.emit_assign(module_idx, cd, lhs.value, start, subrhs, subcond, src_loc=src_loc)
10761060
elif isinstance(lhs, _ast.SwitchValue):
10771061
test, _signed = self.emit_rhs(module_idx, lhs.test)
1078-
conds = []
1062+
patterns = []
10791063
elems = []
1080-
for patterns, elem in lhs.cases:
1081-
if patterns is not None:
1082-
net = self.emit_matches(module_idx, test, patterns, src_loc=lhs.src_loc)
1083-
conds.append(net)
1064+
for pattern_list, elem in lhs.cases:
1065+
if pattern_list is not None:
1066+
patterns.append(pattern_list)
10841067
else:
1085-
conds.append(_nir.Net.from_const(1))
1068+
patterns.append(("-" * len(test),))
10861069
elems.append(elem)
1087-
conds = self.emit_priority_match(module_idx, cond, _nir.Value(conds),
1088-
src_loc=lhs.src_loc)
1070+
conds = self.emit_match(module_idx, cond, test, tuple(patterns), src_loc=lhs.src_loc)
10891071
for subcond, val in zip(conds, elems):
10901072
self.emit_assign(module_idx, cd, val, lhs_start, rhs[:len(val)], subcond, src_loc=src_loc)
10911073
elif isinstance(lhs, _ast.Operator):
@@ -1166,17 +1148,15 @@ def emit_stmt(self, module_idx: int, fragment: _ir.Fragment, domain: str,
11661148
self.netlist.add_cell(cell)
11671149
elif isinstance(stmt, _ast.Switch):
11681150
test, _signed = self.emit_rhs(module_idx, stmt.test)
1169-
conds = []
1151+
patterns = []
11701152
case_stmts = []
1171-
for patterns, stmts, case_src_loc in stmt.cases:
1172-
if patterns is not None:
1173-
net = self.emit_matches(module_idx, test, patterns, src_loc=case_src_loc)
1174-
conds.append(net)
1153+
for pattern_list, stmts, case_src_loc in stmt.cases:
1154+
if pattern_list is not None:
1155+
patterns.append(pattern_list)
11751156
else:
1176-
conds.append(_nir.Net.from_const(1))
1157+
patterns.append(("-" * len(test),))
11771158
case_stmts.append(stmts)
1178-
conds = self.emit_priority_match(module_idx, cond, _nir.Value(conds),
1179-
src_loc=stmt.src_loc)
1159+
conds = self.emit_match(module_idx, cond, test, tuple(patterns), src_loc=stmt.src_loc)
11801160
for subcond, substmts in zip(conds, case_stmts):
11811161
for substmt in substmts:
11821162
self.emit_stmt(module_idx, fragment, domain, substmt, subcond)
@@ -1430,13 +1410,10 @@ def emit_drivers(self):
14301410
driver.domain.rst is not None and
14311411
not driver.domain.async_reset and
14321412
not driver.signal.reset_less):
1433-
cond = self.emit_matches(driver.module_idx,
1413+
cond, = self.emit_match(driver.module_idx, _nir.Net.from_const(1),
14341414
self.emit_signal(driver.domain.rst),
1435-
("1",),
1415+
(("1",),),
14361416
src_loc=driver.domain.rst.src_loc)
1437-
cond, = self.emit_priority_match(driver.module_idx, _nir.Net.from_const(1),
1438-
_nir.Value(cond),
1439-
src_loc=driver.domain.rst.src_loc)
14401417
init = _nir.Value.from_const(driver.signal.init, len(driver.signal))
14411418
driver.assignments.append(_nir.Assignment(cond=cond, start=0,
14421419
value=init, src_loc=driver.signal.src_loc))

amaranth/hdl/_nir.py

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Computation cells
1616
"Operator", "Part",
1717
# Decision tree cells
18-
"Matches", "PriorityMatch", "Assignment", "AssignmentList",
18+
"Match", "Assignment", "AssignmentList",
1919
# Storage cells
2020
"FlipFlop", "Memory", "SyncWritePort", "AsyncReadPort", "SyncReadPort",
2121
# Print cells
@@ -768,79 +768,48 @@ def comb_edges_to(self, bit):
768768
yield (net, self.src_loc)
769769

770770

771-
class Matches(Cell):
772-
"""A combinational cell performing a comparison like ``Value.matches``
773-
(or, equivalently, a case condition).
774-
775-
Attributes
776-
----------
777-
778-
value: Value
779-
patterns: tuple of str, each str contains '0', '1', '-'
780-
"""
781-
def __init__(self, module_idx, *, value, patterns, src_loc):
782-
super().__init__(module_idx, src_loc=src_loc)
783-
784-
for pattern in patterns:
785-
assert len(pattern) == len(value)
786-
self.value = Value(value)
787-
self.patterns = tuple(patterns)
788-
789-
def input_nets(self):
790-
return set(self.value)
791-
792-
def output_nets(self, self_idx: int):
793-
return {Net.from_cell(self_idx, 0)}
794-
795-
def resolve_nets(self, netlist: Netlist):
796-
self.value = netlist.resolve_value(self.value)
797-
798-
def __repr__(self):
799-
patterns = " ".join(self.patterns)
800-
return f"(matches {self.value} {patterns})"
801-
802-
def comb_edges_to(self, bit):
803-
for net in self.value:
804-
yield (net, self.src_loc)
805-
806-
807-
class PriorityMatch(Cell):
771+
class Match(Cell):
808772
"""Used to represent a single switch on the control plane of processes.
809773
810-
The output is the same length as ``inputs``. If ``en`` is ``0``, the output
811-
is all-0. Otherwise, output keeps the lowest-numbered ``1`` bit in the input
812-
(if any) and masks all other bits to ``0``.
813-
814-
Note: the RTLIL backend requires all bits of ``inputs`` to be driven
815-
by a ``Match`` cell within the same module.
774+
The output is the same length as ``patterns``. If ``en`` is ``0``, the output
775+
is all-0. Otherwise, the ``value`` is matched against all pattern sets
776+
in ``patterns``. The output has a ``1`` bit for the first pattern set that
777+
matches ``value``, and ``0`` for all other bits. If no pattern set matches
778+
the value, the output is all-``0``.
816779
817780
Attributes
818781
----------
819782
en: Net
820-
inputs: Value
783+
value: Value
784+
patterns: tuple of tuple of str, each str contains '0', '1', '-'
821785
"""
822-
def __init__(self, module_idx, *, en, inputs, src_loc):
786+
def __init__(self, module_idx, *, en, value, patterns, src_loc):
823787
super().__init__(module_idx, src_loc=src_loc)
824788

789+
for pattern_list in patterns:
790+
for pattern in pattern_list:
791+
assert len(pattern) == len(value)
825792
self.en = Net.ensure(en)
826-
self.inputs = Value(inputs)
793+
self.value = Value(value)
794+
self.patterns = patterns
827795

828796
def input_nets(self):
829-
return set(self.inputs) | {self.en}
797+
return set(self.value) | {self.en}
830798

831799
def output_nets(self, self_idx: int):
832-
return {Net.from_cell(self_idx, bit) for bit in range(len(self.inputs))}
800+
return {Net.from_cell(self_idx, bit) for bit in range(len(self.patterns))}
833801

834802
def resolve_nets(self, netlist: Netlist):
835803
self.en = netlist.resolve_net(self.en)
836-
self.inputs = netlist.resolve_value(self.inputs)
804+
self.value = netlist.resolve_value(self.value)
837805

838806
def __repr__(self):
839-
return f"(priority_match {self.en} {self.inputs})"
807+
patterns = " ".join("{" + " ".join(pattern_list) + "}" if len(pattern_list) != 1 else pattern_list[0] for pattern_list in self.patterns)
808+
return f"(match {self.en} {self.value} {patterns})"
840809

841810
def comb_edges_to(self, bit):
842811
yield (self.en, self.src_loc)
843-
for net in self.inputs[:bit + 1]:
812+
for net in self.value:
844813
yield (net, self.src_loc)
845814

846815

@@ -883,7 +852,7 @@ class AssignmentList(Cell):
883852
then executing each assignment in sequence.
884853
885854
Note: the RTLIL backend requires all ``cond`` inputs of assignments to be driven
886-
by a ``PriorityMatch`` cell within the same module.
855+
by a ``Match`` cell within the same module.
887856
888857
Attributes
889858
----------

tests/test_back_rtlil.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,7 +1413,7 @@ def test_trivial(self):
14131413
wire width 4 output 1 \out
14141414
process $1
14151415
assign \out [3:0] 4'0000
1416-
switch { }
1416+
switch \sel [3:0]
14171417
case
14181418
assign \out [3:0] 4'0001
14191419
end
@@ -1838,7 +1838,7 @@ def test_assert_simple(self):
18381838
cell $check $3
18391839
parameter \FORMAT ""
18401840
parameter \ARGS_WIDTH 0
1841-
parameter signed \PRIORITY 32'11111111111111111111111111111100
1841+
parameter signed \PRIORITY 32'11111111111111111111111111111101
18421842
parameter \TRG_ENABLE 0
18431843
parameter \TRG_WIDTH 0
18441844
parameter \TRG_POLARITY 0

0 commit comments

Comments
 (0)