@@ -600,6 +600,16 @@ def emit_assignments(case, cond):
600
600
emit_assignments (root_case , _nir .Net .from_const (1 ))
601
601
assert pos == len (cell .assignments )
602
602
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
+
603
613
def emit_operator (self , cell_idx , cell ):
604
614
UNARY_OPERATORS = {
605
615
"-" : "$neg" ,
@@ -638,17 +648,63 @@ def emit_operator(self, cell_idx, cell):
638
648
if len (cell .inputs ) == 1 :
639
649
cell_type = UNARY_OPERATORS [cell .operator ]
640
650
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
641
666
self .builder .cell (cell_type , ports = {
642
667
"A" : self .sigspec (operand ),
643
668
"Y" : self .cell_wires [cell_idx ]
644
669
}, params = {
645
- "A_SIGNED" : False ,
670
+ "A_SIGNED" : signed ,
646
671
"A_WIDTH" : len (operand ),
647
672
"Y_WIDTH" : cell .width ,
648
673
}, src = _src (cell .src_loc ))
649
674
elif len (cell .inputs ) == 2 :
650
675
cell_type , a_signed , b_signed = BINARY_OPERATORS [cell .operator ]
651
676
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 )
652
708
if cell .operator in ("u//" , "s//" , "u%" , "s%" ):
653
709
result = self .builder .wire (cell .width )
654
710
self .builder .cell (cell_type , ports = {
0 commit comments