comments on branch pipeline
[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 InternalOp
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, spr1 = self.i.cr, self.i.cia, self.i.ctr, self.i.spr1
61 nia_o, lr_o, ctr_o = self.o.nia, self.o.lr, self.o.ctr
62
63 # obtain relevant instruction field AA, "Absolute Address" mode
64 i_fields = self.fields.FormI
65 AA = i_fields.AA[0:-1]
66
67 br_imm_addr = Signal(64, reset_less=True)
68 br_addr = Signal(64, reset_less=True)
69 br_taken = Signal(reset_less=True)
70
71 # Handle absolute or relative branches
72 with m.If(AA | (op.insn_type == InternalOp.OP_BCREG)):
73 comb += br_addr.eq(br_imm_addr)
74 with m.Else():
75 comb += br_addr.eq(br_imm_addr + cia)
76
77 # fields for conditional branches (BO and BI are same for BC and BCREG)
78 # NOTE: here, BO and BI we would like be treated as CR regfile
79 # selectors (similar to RA, RB, RS, RT). see comment here:
80 # https://bugs.libre-soc.org/show_bug.cgi?id=313#c2
81 b_fields = self.fields.FormB
82 BO = b_fields.BO[0:-1]
83 BI = b_fields.BI[0:-1][0:2]
84
85 cr_bits = Array([cr[3-i] for i in range(4)])
86
87 # The bit of CR selected by BI
88 cr_bit = Signal(reset_less=True)
89 comb += cr_bit.eq(cr_bits[BI])
90
91 # Whether the conditional branch should be taken
92 bc_taken = Signal(reset_less=True)
93 with m.If(BO[2]):
94 comb += bc_taken.eq((cr_bit == BO[3]) | BO[4])
95 with m.Else():
96 # decrement the counter and place into output
97 ctr_n = Signal(64, reset_less=True)
98 comb += ctr_n.eq(ctr - 1)
99 comb += ctr_o.data.eq(ctr_n)
100 comb += ctr_o.ok.eq(1)
101 # take either all 64 bits or only 32 of post-incremented counter
102 ctr_m = Signal(64, reset_less=True)
103 with m.If(op.is_32bit):
104 comb += ctr_m.eq(ctr[:32])
105 with m.Else():
106 comb += ctr_m.eq(ctr)
107 # check CTR zero/non-zero against BO[1]
108 ctr_zero_bo1 = Signal(reset_less=True) # BO[1] == (ctr==0)
109 comb += ctr_zero_bo1.eq(BO[1] ^ ctr_m.any())
110 with m.If(BO[3:5] == 0b00):
111 comb += bc_taken.eq(ctr_zero_bo1 & ~cr_bit)
112 with m.Elif(BO[3:5] == 0b01):
113 comb += bc_taken.eq(ctr_zero_bo1 & cr_bit)
114 with m.Elif(BO[4] == 1):
115 comb += bc_taken.eq(ctr_zero_bo1)
116
117 ### Main Switch Statement ###
118 with m.Switch(op.insn_type):
119 #### branch ####
120 with m.Case(InternalOp.OP_B):
121 LI = i_fields.LI[0:-1]
122 comb += br_imm_addr.eq(br_ext(LI))
123 comb += br_taken.eq(1)
124 #### branch conditional ####
125 with m.Case(InternalOp.OP_BC):
126 BD = b_fields.BD[0:-1]
127 comb += br_imm_addr.eq(br_ext(BD))
128 comb += br_taken.eq(bc_taken)
129 #### branch conditional reg ####
130 with m.Case(InternalOp.OP_BCREG):
131 comb += br_imm_addr.eq(spr1) # SPR1 is set by decode unit
132 comb += br_taken.eq(bc_taken)
133
134 # output next instruction address
135 comb += nia_o.data.eq(br_addr)
136 comb += nia_o.ok.eq(br_taken)
137
138 # link register - only activate on operations marked as "lk"
139 with m.If(lk):
140 # ctx.op.lk is the AND of the insn LK field *and* whether the
141 # op is to "listen" to the link field
142 comb += lr_o.data.eq(cia + 4)
143 comb += lr_o.ok.eq(1)
144
145 # and context
146 comb += self.o.ctx.eq(self.i.ctx)
147
148 return m