add bc ctr regression test when CTR=0 and CTR=1
[soc.git] / src / soc / fu / branch / main_stage.py
1 # License: LGPLv3
2 # Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
4
5 """Branch Pipeline
6
7 This stage is intended to do most of the work of executing branch
8 instructions. This is OP_B, OP_B, OP_BCREG
9
10 Note: it is PARTICULARLY important to pay attention to PowerDecode2
11 more specifically DecodeRA etc. as these work closely in conjunction
12 with the Branch pipeline, here.
13
14 The Branch pipeline itself does not and cannot read registers: it can
15 only process data and produce results. Therefore, something else needs
16 to know that BC needs CTR, and that one of the outputs from here is to
17 go into LR, and so on. Encoding of which registers are read and written
18 is the responsibility of PowerDecode2 and because some of those decisions
19 are conditional (based on BO2 for example) PowerDecode2 has to duplicate
20 some of that bitlevel operand field decoding.
21
22 It us therefore quite critical to read this code in conjunction side by
23 side with power_decode2.py
24
25 Links:
26 * https://bugs.libre-soc.org/show_bug.cgi?id=313
27 * https://bugs.libre-soc.org/show_bug.cgi?id=335
28 * https://libre-soc.org/openpower/isa/branch/
29 """
30
31 from nmigen import (Module, Signal, Cat, Mux, Const, Array)
32 from nmutil.pipemodbase import PipeModBase
33 from nmutil.extend import exts
34 from soc.fu.branch.pipe_data import BranchInputData, BranchOutputData
35 from soc.decoder.power_enums import MicrOp
36
37 from soc.decoder.power_fields import DecodeFields
38 from soc.decoder.power_fieldsn import SignalBitRange
39
40
41 def br_ext(bd):
42 """computes sign-extended NIA (assumes word-alignment)
43 """
44 return Cat(Const(0, 2), exts(bd, bd.shape().width, 64 - 2))
45
46
47 """
48 Notes on BO Field:
49
50 BO Description
51 0000z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=0
52 0001z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=0
53 001at Branch if CR[BI]=0
54 0100z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=1
55 0101z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=1
56 011at Branch if CR[BI]=1
57 1a00t Decrement the CTR, then branch if decremented CTR[M:63]!=0
58 1a01t Decrement the CTR, then branch if decremented CTR[M:63]=0
59 1z1zz Branch always
60 """
61
62 class BranchMainStage(PipeModBase):
63 def __init__(self, pspec):
64 super().__init__(pspec, "main")
65 self.fields = DecodeFields(SignalBitRange, [self.i.ctx.op.insn])
66 self.fields.create_specs()
67
68 def ispec(self):
69 return BranchInputData(self.pspec)
70
71 def ospec(self):
72 return BranchOutputData(self.pspec) # TODO: ALUIntermediateData
73
74 def elaborate(self, platform):
75 m = Module()
76 comb = m.d.comb
77 op = self.i.ctx.op
78 lk = op.lk # see PowerDecode2 as to why this is done
79 cr, cia, ctr, fast1 = self.i.cr, op.cia, self.i.ctr, self.i.fast1
80 fast2 = self.i.fast2
81 nia_o, lr_o, ctr_o = self.o.nia, self.o.lr, self.o.ctr
82
83 # obtain relevant instruction field AA, "Absolute Address" mode
84 i_fields = self.fields.FormI
85 AA = i_fields.AA[0:-1]
86
87 br_imm_addr = Signal(64, reset_less=True)
88 br_addr = Signal(64, reset_less=True)
89 br_taken = Signal(reset_less=True)
90
91 # Handle absolute or relative branches
92 with m.If(AA | (op.insn_type == MicrOp.OP_BCREG)):
93 comb += br_addr.eq(br_imm_addr)
94 with m.Else():
95 comb += br_addr.eq(br_imm_addr + cia)
96
97 # fields for conditional branches (BO and BI are same for BC and BCREG)
98 b_fields = self.fields.FormB
99 BO = b_fields.BO
100 BI = b_fields.BI[0:2] # CR0-7 selected already in PowerDecode2.
101
102 cr_bits = Array([cr[3-i] for i in range(4)]) # invert. Because POWER.
103
104 # copy of BO in a signal
105 bo = Signal(5, reset_less=True)
106 comb += bo.eq(BO[0:5])
107
108 # The bit of CR selected by BI
109 bi = Signal(2, reset_less=True)
110 cr_bit = Signal(reset_less=True)
111 comb += bi.eq(BI) # reduces gate-count due to pmux
112 comb += cr_bit.eq(cr_bits[bi])
113
114 # Whether ctr is written to on a conditional branch
115 ctr_write = Signal(reset_less=True)
116 comb += ctr_write.eq(0)
117
118 # Whether the conditional branch should be taken
119 bc_taken = Signal(reset_less=True)
120 with m.If(bo[2]):
121 comb += bc_taken.eq((cr_bit == bo[3]) | bo[4])
122 with m.Else():
123 # decrement the counter and place into output
124 ctr_n = Signal(64, reset_less=True)
125 comb += ctr_n.eq(ctr - 1)
126 comb += ctr_o.data.eq(ctr_n)
127 comb += ctr_write.eq(1)
128 # take either all 64 bits or only 32 of post-incremented counter
129 ctr_m = Signal(64, reset_less=True)
130 with m.If(op.is_32bit):
131 comb += ctr_m.eq(ctr[:32])
132 with m.Else():
133 comb += ctr_m.eq(ctr)
134 # check CTR zero/non-zero against bo[1]
135 ctr_zero_bo1 = Signal(reset_less=True) # bo[1] == (ctr==0)
136 comb += ctr_zero_bo1.eq(bo[1] ^ ctr_n.any())
137 with m.If(bo[3:5] == 0b00):
138 comb += bc_taken.eq(ctr_zero_bo1 & ~cr_bit)
139 with m.Elif(bo[3:5] == 0b01):
140 comb += bc_taken.eq(ctr_zero_bo1 & cr_bit)
141 with m.Elif(bo[4] == 1):
142 comb += bc_taken.eq(ctr_zero_bo1)
143
144 ### Main Switch Statement ###
145 with m.Switch(op.insn_type):
146 #### branch ####
147 with m.Case(MicrOp.OP_B):
148 LI = i_fields.LI[0:-1]
149 comb += br_imm_addr.eq(br_ext(LI))
150 comb += br_taken.eq(1)
151 #### branch conditional ####
152 with m.Case(MicrOp.OP_BC):
153 BD = b_fields.BD[0:-1]
154 comb += br_imm_addr.eq(br_ext(BD))
155 comb += br_taken.eq(bc_taken)
156 comb += ctr_o.ok.eq(ctr_write)
157 #### branch conditional reg ####
158 with m.Case(MicrOp.OP_BCREG):
159 xo = self.fields.FormXL.XO[0:-1]
160 with m.If(xo[9] & ~xo[5]):
161 comb += br_imm_addr.eq(Cat(Const(0, 2), fast1[2:]))
162 with m.Else():
163 comb += br_imm_addr.eq(Cat(Const(0, 2), fast2[2:]))
164 comb += br_taken.eq(bc_taken)
165 comb += ctr_o.ok.eq(ctr_write)
166
167 # output next instruction address
168 comb += nia_o.data.eq(br_addr)
169 comb += nia_o.ok.eq(br_taken)
170
171 # link register - only activate on operations marked as "lk"
172 with m.If(lk):
173 # ctx.op.lk is the AND of the insn LK field *and* whether the
174 # op is to "listen" to the link field
175 comb += lr_o.data.eq(cia + 4)
176 comb += lr_o.ok.eq(1)
177
178 # and context
179 comb += self.o.ctx.eq(self.i.ctx)
180
181 return m