31218c66ef0af904704b7cfbbabd397d76290e33
[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, signed)
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 SimdSignal
12 from openpower.decoder.power_enums import MicrOp
13
14 from openpower.decoder.power_fields import DecodeFields
15 from openpower.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 comb += self.o.xer_ov.ok.eq(1)
72 xer_ov = self.o.xer_ov.data
73
74 # microwatt overflow detection
75 ov = Signal(reset_less=True)
76 with m.If(self.i.div_by_zero):
77 comb += ov.eq(1)
78 with m.Elif(~op.is_32bit):
79 comb += ov.eq(self.i.dive_abs_ov64)
80 with m.If(op.is_signed & (quotient_65[64] ^ quotient_65[63])):
81 comb += ov.eq(1)
82 with m.Elif(op.is_signed):
83 comb += ov.eq(self.i.dive_abs_ov32)
84 with m.If(quotient_65[32] != quotient_65[31]):
85 comb += ov.eq(1)
86 with m.Else():
87 comb += ov.eq(self.i.dive_abs_ov32)
88 comb += xer_ov.eq(Repl(ov, 2)) # set OV _and_ OV32
89
90 ##########################
91 # main switch for Div
92
93 comb += self.o.o.ok.eq(1)
94 o = self.o.o.data
95
96 # work around https://github.com/nmigen/nmigen/issues/502
97 remainder_s32 = Signal(signed(32))
98 comb += remainder_s32.eq(remainder_64[0:32])
99 remainder_s32_as_s64 = Signal(signed(64))
100 comb += remainder_s32_as_s64.eq(remainder_s32)
101
102 with m.If(~ov): # result is valid (no overflow)
103 with m.Switch(op.insn_type):
104 with m.Case(MicrOp.OP_DIVE):
105 with m.If(op.is_32bit):
106 with m.If(op.is_signed):
107 # matches POWER9's divweo behavior
108 comb += o.eq(quotient_65[0:32].as_unsigned())
109 with m.Else():
110 comb += o.eq(quotient_65[0:32].as_unsigned())
111 with m.Else():
112 comb += o.eq(quotient_65)
113 with m.Case(MicrOp.OP_DIV):
114 with m.If(op.is_32bit):
115 with m.If(op.is_signed):
116 # matches POWER9's divwo behavior
117 comb += o.eq(quotient_65[0:32].as_unsigned())
118 with m.Else():
119 comb += o.eq(quotient_65[0:32].as_unsigned())
120 with m.Else():
121 comb += o.eq(quotient_65)
122 with m.Case(MicrOp.OP_MOD):
123 with m.If(op.is_32bit):
124 with m.If(op.is_signed):
125 # matches POWER9's modsw behavior
126 comb += o.eq(remainder_s32_as_s64)
127 with m.Else():
128 comb += o.eq(remainder_64[0:32].as_unsigned())
129 with m.Else():
130 comb += o.eq(remainder_64)
131
132 ###### sticky overflow and context, both pass-through #####
133
134 comb += self.o.xer_so.data.eq(self.i.xer_so)
135 comb += self.o.ctx.eq(self.i.ctx)
136
137 return m