rename InternalOp to MicrOp
[soc.git] / src / soc / fu / div / output_stage.py
1 # This stage is the setup stage that converts the inputs
2 # into the values expected by DivPipeCore
3 """
4 * https://bugs.libre-soc.org/show_bug.cgi?id=424
5 """
6
7 from nmigen import (Module, Signal, Cat, Repl, Mux, Const, Array)
8 from nmutil.pipemodbase import PipeModBase
9 from soc.fu.logical.pipe_data import LogicalInputData
10 from soc.fu.div.pipe_data import DivMulOutputData
11 from ieee754.part.partsig import PartitionedSignal
12 from soc.decoder.power_enums import MicrOp
13
14 from soc.decoder.power_fields import DecodeFields
15 from soc.decoder.power_fieldsn import SignalBitRange
16 from soc.fu.div.pipe_data import CoreOutputData
17
18
19 class DivOutputStage(PipeModBase):
20 def __init__(self, pspec):
21 super().__init__(pspec, "output_stage")
22 self.fields = DecodeFields(SignalBitRange, [self.i.ctx.op.insn])
23 self.fields.create_specs()
24 self.quotient_neg = Signal()
25 self.remainder_neg = Signal()
26 self.quotient_65 = Signal(65) # one extra spare bit for overflow
27 self.remainder_64 = Signal(64)
28
29 def ispec(self):
30 return CoreOutputData(self.pspec)
31
32 def ospec(self):
33 return DivMulOutputData(self.pspec)
34
35 def elaborate(self, platform):
36 m = Module()
37 comb = m.d.comb
38
39 # convenience variables
40 op = self.i.ctx.op
41 abs_quotient = self.i.core.quotient_root
42 fract_width = self.pspec.core_config.fract_width
43 # fract width of `DivPipeCoreOutputData.remainder`
44 remainder_fract_width = fract_width * 3
45 # fract width of `DivPipeCoreInputData.dividend`
46 dividend_fract_width = fract_width * 2
47 rem_start = remainder_fract_width - dividend_fract_width
48 abs_remainder = self.i.core.remainder[rem_start:rem_start+64]
49 dividend_neg = self.i.dividend_neg
50 divisor_neg = self.i.divisor_neg
51 quotient_65 = self.quotient_65
52 remainder_64 = self.remainder_64
53
54 # work out if sign of result is to be negative
55 comb += self.quotient_neg.eq(dividend_neg ^ divisor_neg)
56
57 # follows rules for truncating division
58 comb += self.remainder_neg.eq(dividend_neg)
59
60 # negation of a 64-bit value produces the same lower 32-bit
61 # result as negation of just the lower 32-bits, so we don't
62 # need to do anything special before negating
63 comb += [
64 quotient_65.eq(Mux(self.quotient_neg,
65 -abs_quotient, abs_quotient)),
66 remainder_64.eq(Mux(self.remainder_neg,
67 -abs_remainder, abs_remainder))
68 ]
69
70 # calculate overflow
71 self.o.xer_ov.ok.eq(1)
72 xer_ov = self.o.xer_ov.data
73
74 # see test_6_regression in div test_pipe_caller.py
75 # https://bugs.libre-soc.org/show_bug.cgi?id=425
76 def calc_overflow(dive_abs_overflow, sign_bit_mask):
77 nonlocal comb
78 overflow = dive_abs_overflow | self.i.div_by_zero
79 ov = Signal(reset_less=True)
80 with m.If(op.is_signed):
81 comb += ov.eq(overflow
82 | (abs_quotient > sign_bit_mask)
83 | ((abs_quotient == sign_bit_mask)
84 & ~self.quotient_neg))
85 with m.Else():
86 comb += ov.eq(overflow)
87 comb += xer_ov.eq(Repl(ov, 2)) # set OV _and_ OV32
88
89 # check 32/64 bit version of overflow
90 with m.If(op.is_32bit):
91 calc_overflow(self.i.dive_abs_ov32, 0x80000000)
92 with m.Else():
93 calc_overflow(self.i.dive_abs_ov64, 0x8000000000000000)
94
95 # microwatt overflow detection
96 ov = Signal(reset_less=True)
97 with m.If(self.i.div_by_zero):
98 comb += ov.eq(1)
99 with m.Elif(~op.is_32bit):
100 comb += ov.eq(self.i.dive_abs_ov64)
101 with m.If(op.is_signed & (quotient_65[64] ^ quotient_65[63])):
102 comb += ov.eq(1)
103 with m.Elif(op.is_signed):
104 comb += ov.eq(self.i.dive_abs_ov32)
105 with m.If(quotient_65[32] != quotient_65[31]):
106 comb += ov.eq(1)
107 with m.Else():
108 comb += ov.eq(self.i.dive_abs_ov32)
109 comb += xer_ov.eq(Repl(ov, 2)) # set OV _and_ OV32
110
111 ##########################
112 # main switch for DIV
113
114 o = self.o.o.data
115
116 with m.If(~ov): # result is valid (no overflow)
117 with m.Switch(op.insn_type):
118 with m.Case(MicrOp.OP_DIVE):
119 with m.If(op.is_32bit):
120 with m.If(op.is_signed):
121 # matches POWER9's divweo behavior
122 comb += o.eq(quotient_65[0:32].as_unsigned())
123 with m.Else():
124 comb += o.eq(quotient_65[0:32].as_unsigned())
125 with m.Else():
126 comb += o.eq(quotient_65)
127 with m.Case(MicrOp.OP_DIV):
128 with m.If(op.is_32bit):
129 with m.If(op.is_signed):
130 # matches POWER9's divwo behavior
131 comb += o.eq(quotient_65[0:32].as_unsigned())
132 with m.Else():
133 comb += o.eq(quotient_65[0:32].as_unsigned())
134 with m.Else():
135 comb += o.eq(quotient_65)
136 with m.Case(MicrOp.OP_MOD):
137 with m.If(op.is_32bit):
138 with m.If(op.is_signed):
139 # matches POWER9's modsw behavior
140 comb += o.eq(remainder_64[0:32].as_signed())
141 with m.Else():
142 comb += o.eq(remainder_64[0:32].as_unsigned())
143 with m.Else():
144 comb += o.eq(remainder_64)
145
146 ###### sticky overflow and context, both pass-through #####
147
148 comb += self.o.xer_so.data.eq(self.i.xer_so)
149 comb += self.o.ctx.eq(self.i.ctx)
150
151 return m