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