add back (totally confusing) accidentally-removed code due to use of "types"
[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 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 with m.If(~ov): # result is valid (no overflow)
97 with m.Switch(op.insn_type):
98 with m.Case(MicrOp.OP_DIVE):
99 with m.If(op.is_32bit):
100 with m.If(op.is_signed):
101 # matches POWER9's divweo behavior
102 comb += o.eq(quotient_65[0:32].as_unsigned())
103 with m.Else():
104 comb += o.eq(quotient_65[0:32].as_unsigned())
105 with m.Else():
106 comb += o.eq(quotient_65)
107 with m.Case(MicrOp.OP_DIV):
108 with m.If(op.is_32bit):
109 with m.If(op.is_signed):
110 # matches POWER9's divwo behavior
111 comb += o.eq(quotient_65[0:32].as_unsigned())
112 with m.Else():
113 comb += o.eq(quotient_65[0:32].as_unsigned())
114 with m.Else():
115 comb += o.eq(quotient_65)
116 with m.Case(MicrOp.OP_MOD):
117 with m.If(op.is_32bit):
118 with m.If(op.is_signed):
119 # matches POWER9's modsw behavior
120 comb += o.eq(remainder_64[0:32].as_signed())
121 with m.Else():
122 comb += o.eq(remainder_64[0:32].as_unsigned())
123 with m.Else():
124 comb += o.eq(remainder_64)
125
126 ###### sticky overflow and context, both pass-through #####
127
128 comb += self.o.xer_so.data.eq(self.i.xer_so)
129 comb += self.o.ctx.eq(self.i.ctx)
130
131 return m