@@ -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 = {
0 commit comments