convert alu output to use Data for XER and CR0
[soc.git] / src / soc / fu / alu / output_stage.py
1 # This stage is intended to handle the gating of carry and overflow
2 # out, summary overflow generation, and updating the condition
3 # register
4 from nmigen import (Module, Signal, Cat, Repl)
5 from nmutil.pipemodbase import PipeModBase
6 from soc.fu.alu.pipe_data import ALUInputData, ALUOutputData
7 from ieee754.part.partsig import PartitionedSignal
8 from soc.decoder.power_enums import InternalOp
9
10
11 class ALUOutputStage(PipeModBase):
12 def __init__(self, pspec):
13 super().__init__(pspec, "output")
14
15 def ispec(self):
16 return ALUOutputData(self.pspec) # TODO: ALUIntermediateData
17
18 def ospec(self):
19 return ALUOutputData(self.pspec)
20
21 def elaborate(self, platform):
22 m = Module()
23 comb = m.d.comb
24 op = self.i.ctx.op
25
26 # op requests inversion of the output
27 o = Signal.like(self.i.o)
28 with m.If(op.invert_out):
29 comb += o.eq(~self.i.o)
30 with m.Else():
31 comb += o.eq(self.i.o)
32
33 # target register if 32-bit is only the 32 LSBs
34 target = Signal(64, reset_less=True)
35 with m.If(op.is_32bit):
36 comb += target.eq(o[:32])
37 with m.Else():
38 comb += target.eq(o)
39
40 # Handle carry_out
41 comb += self.o.xer_co.data.eq(self.i.xer_co.data)
42 comb += self.o.xer_co.ok.eq(op.output_carry)
43
44 # create condition register cr0 and sticky-overflow
45 is_zero = Signal(reset_less=True)
46 is_positive = Signal(reset_less=True)
47 is_negative = Signal(reset_less=True)
48 msb_test = Signal(reset_less=True) # set equal to MSB, invert if OP=CMP
49 is_cmp = Signal(reset_less=True) # true if OP=CMP
50 so = Signal(1, reset_less=True)
51 ov = Signal(2, reset_less=True) # OV, OV32
52 cr0 = Signal(4, reset_less=True)
53
54 # TODO: if o[63] is XORed with "operand == OP_CMP"
55 # that can be used as a test
56 # see https://bugs.libre-soc.org/show_bug.cgi?id=305#c60
57
58 comb += is_cmp.eq(op.insn_type == InternalOp.OP_CMP)
59 comb += msb_test.eq(target[-1] ^ is_cmp)
60 comb += is_zero.eq(target == 0)
61 comb += is_positive.eq(~is_zero & ~msb_test)
62 comb += is_negative.eq(~is_zero & msb_test)
63 # XXX see https://bugs.libre-soc.org/show_bug.cgi?id=319#c5
64 comb += ov[0].eq(self.i.xer_so.data | self.i.xer_ov.data[0]) # OV
65 comb += ov[1].eq(self.i.xer_so.data | self.i.xer_ov.data[1]) # OV32 XXX!
66 comb += so.eq(self.i.xer_so.data | self.i.xer_ov.data[0]) # OV
67
68 with m.If(op.insn_type != InternalOp.OP_CMPEQB):
69 comb += cr0.eq(Cat(so, is_zero, is_positive, is_negative))
70 with m.Else():
71 comb += cr0.eq(self.i.cr0)
72
73 # copy [inverted] cr0, output, sticky-overflow and context out
74 comb += self.o.o.eq(o)
75 comb += self.o.cr0.data.eq(cr0)
76 comb += self.o.cr0.ok.eq(op.rc.rc & op.rc.rc_ok) # CR0 to be set
77 comb += self.o.xer_so.data.eq(so)
78 comb += self.o.xer_so.ok.eq(op.oe.oe & op.oe.oe_ok) # SO is to be set
79 comb += self.o.xer_ov.data.eq(ov)
80 comb += self.o.xer_ov.ok.eq(op.oe.oe & op.oe.oe_ok) # OV/32 is to be set
81 comb += self.o.ctx.eq(self.i.ctx)
82
83 return m