convert alu output to use Data for XER and CR0
[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 from nmigen import (Module, Signal, Cat, Repl, Mux, Const)
7 from nmutil.pipemodbase import PipeModBase
8 from soc.fu.alu.pipe_data import ALUInputData, ALUOutputData
9 from ieee754.part.partsig import PartitionedSignal
10 from soc.decoder.power_enums import InternalOp
11
12
13 class ALUMainStage(PipeModBase):
14 def __init__(self, pspec):
15 super().__init__(pspec, "main")
16
17 def ispec(self):
18 return ALUInputData(self.pspec)
19
20 def ospec(self):
21 return ALUOutputData(self.pspec) # TODO: ALUIntermediateData
22
23 def elaborate(self, platform):
24 m = Module()
25 comb = m.d.comb
26 carry_out, o = self.o.xer_co, self.o.o
27
28 # check if op is 32-bit, and get sign bit from operand a
29 is_32bit = Signal(reset_less=True)
30 sign_bit = Signal(reset_less=True)
31 comb += is_32bit.eq(self.i.ctx.op.is_32bit)
32 comb += sign_bit.eq(Mux(is_32bit, self.i.a[31], self.i.a[63]))
33
34 # little trick: do the add using only one add (not 2)
35 add_a = Signal(self.i.a.width + 2, reset_less=True)
36 add_b = Signal(self.i.a.width + 2, reset_less=True)
37 add_output = Signal(self.i.a.width + 2, reset_less=True)
38 with m.If((self.i.ctx.op.insn_type == InternalOp.OP_ADD) |
39 (self.i.ctx.op.insn_type == InternalOp.OP_CMP)):
40 # in bit 0, 1+carry_in creates carry into bit 1 and above
41 comb += add_a.eq(Cat(self.i.carry_in, self.i.a, Const(0, 1)))
42 comb += add_b.eq(Cat(Const(1, 1), self.i.b, Const(0, 1)))
43 comb += add_output.eq(add_a + add_b)
44
45 ##########################
46 # main switch-statement for handling arithmetic operations
47
48 with m.Switch(self.i.ctx.op.insn_type):
49 #### CMP, CMPL ####
50 with m.Case(InternalOp.OP_CMP):
51 # this is supposed to be inverted (b-a, not a-b)
52 # however we have a trick: instead of adding either 2x 64-bit
53 # MUXes to invert a and b, or messing with a 64-bit output,
54 # swap +ve and -ve test in the *output* stage using an XOR gate
55 comb += o.eq(add_output[1:-1])
56
57 #### add ####
58 with m.Case(InternalOp.OP_ADD):
59 # bit 0 is not part of the result, top bit is the carry-out
60 comb += o.eq(add_output[1:-1])
61 comb += carry_out.data[0].eq(add_output[-1]) # XER.CO
62
63 # XXX no! wrongggg, see microwatt OP_ADD code
64 # https://bugs.libre-soc.org/show_bug.cgi?id=319#c5
65 comb += carry_out.data[1].eq(add_output[-1]) # XER.CO32
66
67 #### exts (sign-extend) ####
68 with m.Case(InternalOp.OP_EXTS):
69 with m.If(self.i.ctx.op.data_len == 1):
70 comb += o.eq(Cat(self.i.a[0:8], Repl(self.i.a[7], 64-8)))
71 with m.If(self.i.ctx.op.data_len == 2):
72 comb += o.eq(Cat(self.i.a[0:16], Repl(self.i.a[15], 64-16)))
73 with m.If(self.i.ctx.op.data_len == 4):
74 comb += o.eq(Cat(self.i.a[0:32], Repl(self.i.a[31], 64-32)))
75 with m.Case(InternalOp.OP_CMPEQB):
76 eqs = Signal(8, reset_less=True)
77 src1 = Signal(8, reset_less=True)
78 comb += src1.eq(self.i.a[0:8])
79 for i in range(8):
80 comb += eqs[i].eq(src1 == self.i.b[8*i:8*(i+1)])
81 comb += self.o.cr0.eq(Cat(Const(0, 2), eqs.any(), Const(0, 1)))
82
83 ###### sticky overflow and context, both pass-through #####
84
85 comb += self.o.xer_so.data.eq(self.i.so)
86 comb += self.o.ctx.eq(self.i.ctx)
87
88 return m