b4c4bb2a0e705a6086b23b9f34981ffdb0acefba
[soc.git] / src / soc / fu / alu / main_stage.py
1 # This stage is intended to do most of the work of executing the Arithmetic
2 # instructions. This would be like the additions, compares, and sign-extension
3 # as well as carry and overflow generation. This module
4 # however should not gate the carry or overflow, that's up to the
5 # output stage
6
7 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
8 from nmigen import (Module, Signal, Cat, Repl, Mux, Const)
9 from nmutil.pipemodbase import PipeModBase
10 from nmutil.extend import exts, extz
11 from soc.fu.alu.pipe_data import ALUInputData, ALUOutputData
12 from ieee754.part.partsig import PartitionedSignal
13 from soc.decoder.power_enums import MicrOp
14
15 from soc.decoder.power_fields import DecodeFields
16 from soc.decoder.power_fieldsn import SignalBitRange
17
18
19 # microwatt calc_ov function.
20 def calc_ov(msb_a, msb_b, ca, msb_r):
21 return (ca ^ msb_r) & ~(msb_a ^ msb_b)
22
23
24 class ALUMainStage(PipeModBase):
25 def __init__(self, pspec):
26 super().__init__(pspec, "main")
27 self.fields = DecodeFields(SignalBitRange, [self.i.ctx.op.insn])
28 self.fields.create_specs()
29
30 def ispec(self):
31 return ALUInputData(self.pspec) # defines pipeline stage input format
32
33 def ospec(self):
34 return ALUOutputData(self.pspec) # defines pipeline stage output format
35
36 def elaborate(self, platform):
37 m = Module()
38 comb = m.d.comb
39
40 # convenience variables
41 cry_o, o, cr0 = self.o.xer_ca, self.o.o, self.o.cr0
42 xer_so_i, ov_o = self.i.xer_so, self.o.xer_ov
43 a, b, cry_i, op = self.i.a, self.i.b, self.i.xer_ca, self.i.ctx.op
44
45 # get L-field for OP_CMP
46 x_fields = self.fields.FormX
47 L = x_fields.L[0]
48
49 # check if op is 32-bit, and get sign bit from operand a
50 is_32bit = Signal(reset_less=True)
51
52 with m.If(op.insn_type == MicrOp.OP_CMP):
53 comb += is_32bit.eq(~L)
54
55 # little trick: do the add using only one add (not 2)
56 # LSB: carry-in [0]. op/result: [1:-1]. MSB: carry-out [-1]
57 add_a = Signal(a.width + 2, reset_less=True)
58 add_b = Signal(a.width + 2, reset_less=True)
59 add_o = Signal(a.width + 2, reset_less=True)
60
61 a_i = Signal.like(a)
62 b_i = Signal.like(b)
63 with m.If(op.insn_type == MicrOp.OP_CMP): # another temporary hack
64 comb += a_i.eq(a) # reaaaally need to move CMP
65 comb += b_i.eq(b) # into trap pipeline
66 with m.Elif(is_32bit):
67 with m.If(op.is_signed):
68 comb += a_i.eq(exts(a, 32, 64))
69 comb += b_i.eq(exts(b, 32, 64))
70 with m.Else():
71 comb += a_i.eq(extz(a, 32, 64))
72 comb += b_i.eq(extz(b, 32, 64))
73 with m.Else():
74 comb += a_i.eq(a)
75 comb += b_i.eq(b)
76
77 with m.If((op.insn_type == MicrOp.OP_ADD) |
78 (op.insn_type == MicrOp.OP_CMP)):
79 # in bit 0, 1+carry_in creates carry into bit 1 and above
80 comb += add_a.eq(Cat(cry_i[0], a_i, Const(0, 1)))
81 comb += add_b.eq(Cat(Const(1, 1), b_i, Const(0, 1)))
82 comb += add_o.eq(add_a + add_b)
83
84 ##########################
85 # main switch-statement for handling arithmetic operations
86
87 with m.Switch(op.insn_type):
88
89 ###################
90 #### CMP, CMPL v3.0B p85-86
91
92 with m.Case(MicrOp.OP_CMP):
93 a_n = Signal(64) # temporary - inverted a
94 tval = Signal(5)
95 a_lt = Signal()
96 carry_32 = Signal()
97 carry_64 = Signal()
98 zerolo = Signal()
99 zerohi = Signal()
100 msb_a = Signal()
101 msb_b = Signal()
102 newcrf = Signal(4)
103
104 # this is supposed to be inverted (b-a, not a-b)
105 comb += a_n.eq(~a) # sigh a gets inverted
106 comb += carry_32.eq(add_o[33] ^ a[32] ^ b[32])
107 comb += carry_64.eq(add_o[65])
108
109 comb += zerolo.eq(~((a_n[0:32] ^ b[0:32]).bool()))
110 comb += zerohi.eq(~((a_n[32:64] ^ b[32:64]).bool()))
111
112 with m.If(zerolo & (is_32bit | zerohi)):
113 # values are equal
114 comb += tval[2].eq(1)
115 with m.Else():
116 comb += msb_a.eq(Mux(is_32bit, a_n[31], a_n[63]))
117 comb += msb_b.eq(Mux(is_32bit, b[31], b[63]))
118 C0 = Const(0, 1)
119 with m.If(msb_a != msb_b):
120 # Subtraction might overflow, but
121 # comparison is clear from MSB difference.
122 # for signed, 0 is greater; for unsigned, 1 is greater
123 comb += tval.eq(Cat(msb_a, msb_b, C0, msb_b, msb_a))
124 with m.Else():
125 # Subtraction cannot overflow since MSBs are equal.
126 # carry = 1 indicates RA is smaller (signed or unsigned)
127 comb += a_lt.eq(Mux(is_32bit, carry_32, carry_64))
128 comb += tval.eq(Cat(~a_lt, a_lt, C0, ~a_lt, a_lt))
129 comb += cr0.data[0:2].eq(Cat(xer_so_i[0], tval[2]))
130 with m.If(op.is_signed):
131 comb += cr0.data[2:4].eq(tval[3:5])
132 with m.Else():
133 comb += cr0.data[2:4].eq(tval[0:2])
134 comb += cr0.ok.eq(1)
135
136 ###################
137 #### add v3.0B p67, p69-72
138
139 with m.Case(MicrOp.OP_ADD):
140 # bit 0 is not part of the result, top bit is the carry-out
141 comb += o.data.eq(add_o[1:-1])
142 comb += o.ok.eq(1) # output register
143
144 # see microwatt OP_ADD code
145 # https://bugs.libre-soc.org/show_bug.cgi?id=319#c5
146 ca = Signal(2, reset_less=True)
147 comb += ca[0].eq(add_o[-1]) # XER.CA
148 comb += ca[1].eq(add_o[33] ^ (a_i[32] ^ b_i[32])) # XER.CA32
149 comb += cry_o.data.eq(ca)
150 comb += cry_o.ok.eq(1)
151 # 32-bit (ov[1]) and 64-bit (ov[0]) overflow
152 ov = Signal(2, reset_less=True)
153 comb += ov[0].eq(calc_ov(a_i[-1], b_i[-1], ca[0], add_o[-2]))
154 comb += ov[1].eq(calc_ov(a_i[31], b_i[31], ca[1], add_o[32]))
155 comb += ov_o.data.eq(ov)
156 comb += ov_o.ok.eq(1)
157
158 ###################
159 #### exts (sign-extend) v3.0B p96, p99
160
161 with m.Case(MicrOp.OP_EXTS):
162 with m.If(op.data_len == 1):
163 comb += o.data.eq(exts(a, 8, 64))
164 with m.If(op.data_len == 2):
165 comb += o.data.eq(exts(a, 16, 64))
166 with m.If(op.data_len == 4):
167 comb += o.data.eq(exts(a, 32, 64))
168 comb += o.ok.eq(1) # output register
169
170 ###################
171 #### cmpeqb v3.0B p88
172
173 with m.Case(MicrOp.OP_CMPEQB):
174 eqs = Signal(8, reset_less=True)
175 src1 = Signal(8, reset_less=True)
176 comb += src1.eq(a[0:8])
177 for i in range(8):
178 comb += eqs[i].eq(src1 == b[8*i:8*(i+1)])
179 comb += o.data[0].eq(eqs.any())
180 comb += o.ok.eq(0) # use o.data but do *not* actually output
181 comb += cr0.data.eq(Cat(Const(0, 2), eqs.any(), Const(0, 1)))
182 comb += cr0.ok.eq(1)
183
184 ###### sticky overflow and context, both pass-through #####
185
186 comb += self.o.xer_so.data.eq(xer_so_i)
187 comb += self.o.ctx.eq(self.i.ctx)
188
189 return m