Skip to content

Commit d3c5b95

Browse files
wanda-phiwhitequark
authored andcommitted
back.rtlil: Opportunistically trim zero and sign extension on operands.
Fixes #1148.
1 parent 2d59242 commit d3c5b95

File tree

3 files changed

+231
-83
lines changed

3 files changed

+231
-83
lines changed

amaranth/back/rtlil.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,16 @@ def emit_assignments(case, cond):
744744
emit_assignments(proc, _nir.Net.from_const(1))
745745
assert pos == len(cell.assignments)
746746

747+
def shorten_operand(self, value, *, signed):
748+
value = list(value)
749+
if signed:
750+
while len(value) > 1 and value[-1] == value[-2]:
751+
value.pop()
752+
else:
753+
while len(value) > 0 and value[-1] == _nir.Net.from_const(0):
754+
value.pop()
755+
return _nir.Value(value)
756+
747757
def emit_operator(self, cell_idx, cell):
748758
UNARY_OPERATORS = {
749759
"-": "$neg",
@@ -782,17 +792,75 @@ def emit_operator(self, cell_idx, cell):
782792
if len(cell.inputs) == 1:
783793
cell_type = UNARY_OPERATORS[cell.operator]
784794
operand, = cell.inputs
795+
signed = False
796+
if cell.operator == "-":
797+
# For arithmetic operands, we trim the extra sign or zero extension on the operands
798+
# to make the output prettier, and to fix inference problems in some not very smart
799+
# synthesis tools.
800+
operand_u = self.shorten_operand(operand, signed=False)
801+
operand_s = self.shorten_operand(operand, signed=True)
802+
# The operator will work when lowered with either signedness. Pick whichever
803+
# is prettier.
804+
if len(operand_s) < len(operand_u):
805+
signed = True
806+
operand = operand_s
807+
else:
808+
signed = False
809+
operand = operand_u
785810
self.builder.cell(cell_type, ports={
786811
"A": self.sigspec(operand),
787812
"Y": self.cell_wires[cell_idx].name
788813
}, parameters={
789-
"A_SIGNED": False,
814+
"A_SIGNED": signed,
790815
"A_WIDTH": len(operand),
791816
"Y_WIDTH": cell.width,
792817
}, src_loc=cell.src_loc)
793818
elif len(cell.inputs) == 2:
794819
cell_type, a_signed, b_signed = BINARY_OPERATORS[cell.operator]
795820
operand_a, operand_b = cell.inputs
821+
if cell.operator in ("+", "-", "*", "==", "!="):
822+
# Arithmetic operators that will work with any signedness, but we have to choose
823+
# a common one for both operands. Prefer signed in case of mixed signedness.
824+
operand_a_u = self.shorten_operand(operand_a, signed=False)
825+
operand_b_u = self.shorten_operand(operand_b, signed=False)
826+
operand_a_s = self.shorten_operand(operand_a, signed=True)
827+
operand_b_s = self.shorten_operand(operand_b, signed=True)
828+
if operand_a.is_const:
829+
# In case of constant operand, choose whichever shortens the other one better.
830+
signed = len(operand_b_s) < len(operand_b_u)
831+
elif operand_b.is_const:
832+
signed = len(operand_a_s) < len(operand_a_u)
833+
elif (len(operand_a_s) < len(operand_a) and len(operand_a_u) == len(operand_a)):
834+
# Operand A can only be shortened by signed. Pick it.
835+
signed = True
836+
elif (len(operand_b_s) < len(operand_b) and len(operand_b_u) == len(operand_b)):
837+
# Operand B can only be shortened by signed. Pick it.
838+
signed = True
839+
else:
840+
# Otherwise, use unsigned shortening.
841+
signed = False
842+
if signed:
843+
operand_a = operand_a_s
844+
operand_b = operand_b_s
845+
else:
846+
operand_a = operand_a_u
847+
operand_b = operand_b_u
848+
a_signed = b_signed = signed
849+
if cell.operator[0] in "us":
850+
# Signedness forced, just shorten.
851+
operand_a = self.shorten_operand(operand_a, signed=a_signed)
852+
operand_b = self.shorten_operand(operand_b, signed=b_signed)
853+
if cell.operator == "<<":
854+
# We can pick the signedness for left operand, but right is fixed.
855+
operand_a_u = self.shorten_operand(operand_a, signed=False)
856+
operand_a_s = self.shorten_operand(operand_a, signed=True)
857+
if len(operand_a_s) < len(operand_a_u):
858+
a_signed = True
859+
operand_a = operand_a_s
860+
else:
861+
a_signed = False
862+
operand_a = operand_a_u
863+
operand_b = self.shorten_operand(operand_b, signed=b_signed)
796864
if cell.operator in ("u//", "s//", "u%", "s%"):
797865
result = self.builder.wire(cell.width)
798866
self.builder.cell(cell_type, ports={

amaranth/hdl/_nir.py

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

164+
@property
165+
def is_const(self):
166+
return all(net.is_const for net in self)
167+
164168
__str__ = __repr__
165169

166170

0 commit comments

Comments
 (0)