rename InternalOp to MicrOp
[soc.git] / src / soc / fu / branch / main_stage.py
1 """Branch Pipeline
2
3 This stage is intended to do most of the work of executing branch
4 instructions. This is OP_B, OP_B, OP_BCREG
5
6 Links:
7 * https://bugs.libre-soc.org/show_bug.cgi?id=313
8 * https://bugs.libre-soc.org/show_bug.cgi?id=335
9 * https://libre-soc.org/openpower/isa/branch/
10 """
11
12 from nmigen import (Module, Signal, Cat, Mux, Const, Array)
13 from nmutil.pipemodbase import PipeModBase
14 from nmutil.extend import exts
15 from soc.fu.branch.pipe_data import BranchInputData, BranchOutputData
16 from soc.decoder.power_enums import MicrOp
17
18 from soc.decoder.power_fields import DecodeFields
19 from soc.decoder.power_fieldsn import SignalBitRange
20
21
22 def br_ext(bd):
23 """computes sign-extended NIA (assumes word-alignment)
24 """
25 return Cat(Const(0, 2), exts(bd, bd.shape().width, 64 - 2))
26
27
28 """
29 Notes on BO Field:
30
31 BO Description
32 0000z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=0
33 0001z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=0
34 001at Branch if CR[BI]=0
35 0100z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=1
36 0101z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=1
37 011at Branch if CR[BI]=1
38 1a00t Decrement the CTR, then branch if decremented CTR[M:63]!=0
39 1a01t Decrement the CTR, then branch if decremented CTR[M:63]=0
40 1z1zz Branch always
41 """
42
43 class BranchMainStage(PipeModBase):
44 def __init__(self, pspec):
45 super().__init__(pspec, "main")
46 self.fields = DecodeFields(SignalBitRange, [self.i.ctx.op.insn])
47 self.fields.create_specs()
48
49 def ispec(self):
50 return BranchInputData(self.pspec)
51
52 def ospec(self):
53 return BranchOutputData(self.pspec) # TODO: ALUIntermediateData
54
55 def elaborate(self, platform):
56 m = Module()
57 comb = m.d.comb
58 op = self.i.ctx.op
59 lk = op.lk # see PowerDecode2 as to why this is done
60 cr, cia, ctr, fast1 = self.i.cr, self.i.cia, self.i.ctr, self.i.fast1
61 fast2 = self.i.fast2
62 nia_o, lr_o, ctr_o = self.o.nia, self.o.lr, self.o.ctr
63
64 # obtain relevant instruction field AA, "Absolute Address" mode
65 i_fields = self.fields.FormI
66 AA = i_fields.AA[0:-1]
67
68 br_imm_addr = Signal(64, reset_less=True)
69 br_addr = Signal(64, reset_less=True)
70 br_taken = Signal(reset_less=True)
71
72 # Handle absolute or relative branches
73 with m.If(AA | (op.insn_type == MicrOp.OP_BCREG)):
74 comb += br_addr.eq(br_imm_addr)
75 with m.Else():
76 comb += br_addr.eq(br_imm_addr + cia)
77
78 # fields for conditional branches (BO and BI are same for BC and BCREG)
79 b_fields = self.fields.FormB
80 BO = b_fields.BO[0:-1]
81 BI = b_fields.BI[0:-1][0:2] # CR0-7 selected already in PowerDecode2.
82
83 cr_bits = Array([cr[3-i] for i in range(4)]) # invert. Because POWER.
84
85 # The bit of CR selected by BI
86 bi = Signal(2, reset_less=True)
87 cr_bit = Signal(reset_less=True)
88 comb += bi.eq(BI) # reduces gate-count due to pmux
89 comb += cr_bit.eq(cr_bits[bi])
90
91 # Whether ctr is written to on a conditional branch
92 ctr_write = Signal(reset_less=True)
93 comb += ctr_write.eq(0)
94
95 # Whether the conditional branch should be taken
96 bc_taken = Signal(reset_less=True)
97 with m.If(BO[2]):
98 comb += bc_taken.eq((cr_bit == BO[3]) | BO[4])
99 with m.Else():
100 # decrement the counter and place into output
101 ctr_n = Signal(64, reset_less=True)
102 comb += ctr_n.eq(ctr - 1)
103 comb += ctr_o.data.eq(ctr_n)
104 comb += ctr_write.eq(1)
105 # take either all 64 bits or only 32 of post-incremented counter
106 ctr_m = Signal(64, reset_less=True)
107 with m.If(op.is_32bit):
108 comb += ctr_m.eq(ctr[:32])
109 with m.Else():
110 comb += ctr_m.eq(ctr)
111 # check CTR zero/non-zero against BO[1]
112 ctr_zero_bo1 = Signal(reset_less=True) # BO[1] == (ctr==0)
113 comb += ctr_zero_bo1.eq(BO[1] ^ ctr_m.any())
114 with m.If(BO[3:5] == 0b00):
115 comb += bc_taken.eq(ctr_zero_bo1 & ~cr_bit)
116 with m.Elif(BO[3:5] == 0b01):
117 comb += bc_taken.eq(ctr_zero_bo1 & cr_bit)
118 with m.Elif(BO[4] == 1):
119 comb += bc_taken.eq(ctr_zero_bo1)
120
121 ### Main Switch Statement ###
122 with m.Switch(op.insn_type):
123 #### branch ####
124 with m.Case(MicrOp.OP_B):
125 LI = i_fields.LI[0:-1]
126 comb += br_imm_addr.eq(br_ext(LI))
127 comb += br_taken.eq(1)
128 #### branch conditional ####
129 with m.Case(MicrOp.OP_BC):
130 BD = b_fields.BD[0:-1]
131 comb += br_imm_addr.eq(br_ext(BD))
132 comb += br_taken.eq(bc_taken)
133 comb += ctr_o.ok.eq(ctr_write)
134 #### branch conditional reg ####
135 with m.Case(MicrOp.OP_BCREG):
136 xo = self.fields.FormXL.XO[0:-1]
137 with m.If(xo[9] & ~xo[5]):
138 comb += br_imm_addr.eq(Cat(Const(0, 2), fast1[2:]))
139 with m.Else():
140 comb += br_imm_addr.eq(Cat(Const(0, 2), fast2[2:]))
141 comb += br_taken.eq(bc_taken)
142 comb += ctr_o.ok.eq(ctr_write)
143
144 # output next instruction address
145 comb += nia_o.data.eq(br_addr)
146 comb += nia_o.ok.eq(br_taken)
147
148 # link register - only activate on operations marked as "lk"
149 with m.If(lk):
150 # ctx.op.lk is the AND of the insn LK field *and* whether the
151 # op is to "listen" to the link field
152 comb += lr_o.data.eq(cia + 4)
153 comb += lr_o.ok.eq(1)
154
155 # and context
156 comb += self.o.ctx.eq(self.i.ctx)
157
158 return m