Skip to content

Commit a0ab64a

Browse files
committed
back.rtlil: Opportunistically trim zero and sign extension on operands.
Fixes #1148.
1 parent 188eb8d commit a0ab64a

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

amaranth/back/rtlil.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,16 @@ def emit_assignments(case, cond):
600600
emit_assignments(root_case, _nir.Net.from_const(1))
601601
assert pos == len(cell.assignments)
602602

603+
def shorten_operand(self, value, *, signed):
604+
value = list(value)
605+
if signed:
606+
while len(value) > 1 and value[-1] == value[-2]:
607+
value.pop()
608+
else:
609+
while len(value) > 0 and value[-1] == _nir.Net.from_const(0):
610+
value.pop()
611+
return _nir.Value(value)
612+
603613
def emit_operator(self, cell_idx, cell):
604614
UNARY_OPERATORS = {
605615
"-": "$neg",
@@ -638,17 +648,63 @@ def emit_operator(self, cell_idx, cell):
638648
if len(cell.inputs) == 1:
639649
cell_type = UNARY_OPERATORS[cell.operator]
640650
operand, = cell.inputs
651+
signed = False
652+
if cell.operator == "-":
653+
# For arithmetic operands, we trim the extra sign or zero extension on the operands
654+
# to make the output prettier, and to fix inference problems in some not very smart
655+
# synthesis tools.
656+
operand_u = self.shorten_operand(operand, signed=False)
657+
operand_s = self.shorten_operand(operand, signed=True)
658+
# The operator will work when lowered with either signedness. Pick whichever
659+
# is prettier.
660+
if len(operand_s) < len(operand_u):
661+
signed = True
662+
operand = operand_s
663+
else:
664+
signed = False
665+
operand = operand_u
641666
self.builder.cell(cell_type, ports={
642667
"A": self.sigspec(operand),
643668
"Y": self.cell_wires[cell_idx]
644669
}, params={
645-
"A_SIGNED": False,
670+
"A_SIGNED": signed,
646671
"A_WIDTH": len(operand),
647672
"Y_WIDTH": cell.width,
648673
}, src=_src(cell.src_loc))
649674
elif len(cell.inputs) == 2:
650675
cell_type, a_signed, b_signed = BINARY_OPERATORS[cell.operator]
651676
operand_a, operand_b = cell.inputs
677+
if cell.operator in ("+", "-", "*", "==", "!="):
678+
# Arithmetic operators that will work with any signedness, but we have to choose
679+
# a common one for both operands. Choose whichever results in shortening
680+
# a non-constant operand. In case of ties or no shortening at all, do whatever.
681+
operand_a_s = self.shorten_operand(operand_a, signed=True)
682+
operand_b_s = self.shorten_operand(operand_b, signed=True)
683+
if ((len(operand_a_s) < len(operand_a) and not operand_a.is_const) or
684+
(len(operand_b_s) < len(operand_b) and not operand_b.is_const)):
685+
signed = True
686+
operand_a = operand_a_s
687+
operand_b = operand_b_s
688+
else:
689+
signed = False
690+
operand_a = self.shorten_operand(operand_a, signed=False)
691+
operand_b = self.shorten_operand(operand_b, signed=False)
692+
a_signed = b_signed = signed
693+
if cell.operator[0] in "us":
694+
# Signedness forced, just shorten.
695+
operand_a = self.shorten_operand(operand_a, signed=a_signed)
696+
operand_b = self.shorten_operand(operand_b, signed=b_signed)
697+
if cell.operator == "<<":
698+
# We can pick the signedness for left operand, but right is fixed.
699+
operand_a_u = self.shorten_operand(operand_a, signed=False)
700+
operand_a_s = self.shorten_operand(operand_a, signed=True)
701+
if len(operand_a_s) < len(operand_a_u):
702+
a_signed = True
703+
operand_a = operand_a_s
704+
else:
705+
a_signed = False
706+
operand_a = operand_a_u
707+
operand_b = self.shorten_operand(operand_b, signed=b_signed)
652708
if cell.operator in ("u//", "s//", "u%", "s%"):
653709
result = self.builder.wire(cell.width)
654710
self.builder.cell(cell_type, ports={

amaranth/hdl/_nir.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ def __repr__(self):
142142
else:
143143
return f"(cat {' '.join(chunks)})"
144144

145+
@property
146+
def is_const(self):
147+
return all(net.is_const for net in self)
148+
145149
__str__ = __repr__
146150

147151

0 commit comments

Comments
 (0)