xref: /XiangShan/src/main/scala/xiangshan/backend/decode/FusionDecoder.scala (revision 5827388ebcab11864d0da723aabf1f18703b8369)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16
17package xiangshan.backend.decode
18
19import chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.util._
22import freechips.rocketchip.rocket.Instructions
23import utils._
24import xiangshan._
25
26abstract class BaseFusionCase(pair: Seq[Valid[UInt]])(implicit p: Parameters)
27  extends DecodeUnitConstants {
28  require(pair.length == 2)
29
30  protected def instr: Seq[UInt] = pair.map(_.bits)
31  protected def pairValid: Bool = VecInit(pair.map(_.valid)).asUInt.andR
32  protected def instr1Rs1: UInt = instr(0)(RS1_MSB, RS1_LSB)
33  protected def instr1Rs2: UInt = instr(0)(RS2_MSB, RS2_LSB)
34  protected def instr1Rd: UInt = instr(0)(RD_MSB, RD_LSB)
35  def instr2Rs1: UInt = instr(1)(RS1_MSB, RS1_LSB)
36  def instr2Rs2: UInt = instr(1)(RS2_MSB, RS2_LSB)
37  protected def instr2Rd: UInt = instr(1)(RD_MSB, RD_LSB)
38  protected def withSameDest: Bool = instr1Rd === instr2Rd
39  def destToRs1: Bool = instr1Rd === instr2Rs1
40  protected def destToRs2: Bool = instr1Rd === instr2Rs2
41
42  protected def getInstrTable(pat: BitPat): List[BitPat] = {
43    // Only these instructions can be fused now
44    val allDecodeTable = XDecode.table ++ X64Decode.table ++ BDecode.table
45    allDecodeTable.filter(_._1 == pat).map(_._2).head
46  }
47  // Must sync these indices with MicroOp.decode
48  protected def getInstrFuType(pat: BitPat): BitPat = getInstrTable(pat)(3)
49  protected def getInstrFuOpType(pat: BitPat): BitPat = getInstrTable(pat)(4)
50  protected def getInstrSrc1Type(pat: BitPat): BitPat = getInstrTable(pat)(0)
51  protected def getInstrSrc2Type(pat: BitPat): BitPat = getInstrTable(pat)(1)
52
53  def isValid: Bool
54  // To optimize the timing, only these control signals can be affected by instruction fusion.
55  def thisInstr: Option[BitPat] = None
56  def fusedInstr: Option[BitPat] = None
57  // By default, None means unchanged.
58  private def compareAndGet(func: BitPat => BitPat): Option[Int] = {
59    if (fusedInstr.isDefined) {
60      require(thisInstr.isDefined, "thisInstr must be defined to infer the ctrl signals")
61      val fused = func(fusedInstr.get)
62      // Only when the two instructions have different control field, we make it not None.
63      if (fused != func(thisInstr.get)) Some(fused.value.toInt) else None
64    } else None
65  }
66  // We assume only fuType, fuOpType, lsrc2 may be changed now.
67  def fuType: Option[Int] = compareAndGet(getInstrFuType)
68  def fuOpType: Option[UInt => UInt] = {
69    val t = compareAndGet(getInstrFuOpType)
70    if (t.isDefined) Some((_: UInt) => t.get.U) else None
71  }
72  def src2Type: Option[Int] = compareAndGet(getInstrSrc2Type)
73  def lsrc2NeedZero: Boolean = false
74  def lsrc2NeedMux: Boolean = false
75  def lsrc2MuxResult: UInt = Mux(destToRs1, instr2Rs2, instr2Rs1)
76
77  def fusionName: String
78}
79
80// There are 2 types of instruction fusion.
81// (1) fused into a fixed new instruction with new control signals.
82// (2) the first instruction's op is replaced with another one.
83
84// Case: clear upper 32 bits / get lower 32 bits
85// Source: `slli r1, r0, 32` + `srli r1, r1, 32`
86// Target: `add.uw r1, r0, zero` (pseudo instruction: `zext.w r1, r0`)
87class FusedAdduw(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
88  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
89  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 32.U
90
91  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
92  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
93  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD_UW)
94  override def lsrc2NeedZero: Boolean = true
95
96  def fusionName: String = "slli32_srli32"
97}
98
99// Case: clear upper 48 bits / get lower 16 bits
100// Source: `slli r1, r0, 48` + `srli r1, r1, 48`
101// Target: `zext.h r1, r0`
102class FusedZexth(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
103  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 48.U
104  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 48.U
105
106  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
107  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
108  override def fusedInstr: Option[BitPat] = Some(Instructions.PACKW)
109  override def lsrc2NeedZero: Boolean = true
110
111  def fusionName: String = "slli48_srli48"
112}
113
114// Another case of Zext.h
115// Source: `slliw r1, r0, 16` + `srliw r1, r1, 16`
116// Target: `zext.h r1, r0`
117class FusedZexth1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends FusedZexth(pair) {
118  override def inst1Cond: Bool = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
119  override def inst2Cond: Bool = instr(1) === Instructions.SRLIW && instr(1)(24, 20) === 16.U
120
121  override def thisInstr: Option[BitPat] = Some(Instructions.SLLIW)
122
123  override def fusionName: String = "slliw16_srliw16"
124}
125
126// Case: sign-extend a 16-bit number
127// Source: `slliw r1, r0, 16` + `sraiw r1, r1, 16`
128// Target: `sext.h r1, r0`
129class FusedSexth(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
130  def inst1Cond = instr(0) === Instructions.SLLIW && instr(0)(24, 20) === 16.U
131  def inst2Cond = instr(1) === Instructions.SRAIW && instr(1)(24, 20) === 16.U
132
133  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
134  override def thisInstr: Option[BitPat] = Some(Instructions.SLLIW)
135  override def fusedInstr: Option[BitPat] = Some(Instructions.SEXT_H)
136  override def lsrc2NeedZero: Boolean = true
137
138  def fusionName: String = "slliw16_sraiw16"
139}
140
141// Case: shift left by one and add
142// Source: `slli r1, r0, 1` + `add r1, r1, r2`
143// Target: `sh1add r1, r0, r2`
144class FusedSh1add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
145  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 1.U
146  def inst2Cond = instr(1) === Instructions.ADD
147
148  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
149  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
150  override def fusedInstr: Option[BitPat] = Some(Instructions.SH1ADD)
151  override def lsrc2NeedMux: Boolean = true
152
153  def fusionName: String = "slli1_add"
154}
155
156// Case: shift left by two and add
157// Source: `slli r1, r0, 2` + `add r1, r1, r2`
158// Target: `sh2add r1, r0, r2`
159class FusedSh2add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
160  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 2.U
161  def inst2Cond = instr(1) === Instructions.ADD
162
163  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
164  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
165  override def fusedInstr: Option[BitPat] = Some(Instructions.SH2ADD)
166  override def lsrc2NeedMux: Boolean = true
167
168  def fusionName: String = "slli2_add"
169}
170
171// Case: shift left by three and add
172// Source: `slli r1, r0, 3` + `add r1, r1, r2`
173// Target: `sh3add r1, r0, r2`
174class FusedSh3add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
175  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 3.U
176  def inst2Cond = instr(1) === Instructions.ADD
177
178  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
179  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
180  override def fusedInstr: Option[BitPat] = Some(Instructions.SH3ADD)
181  override def lsrc2NeedMux: Boolean = true
182
183  def fusionName: String = "slli3_add"
184}
185
186// Case: shift zero-extended word left by one
187// Source: `slli r1, r0, 32` + `srli r1, r1, 31`
188// Target: `szewl1 r1, r0` (customized internal opcode)
189class FusedSzewl1(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
190  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
191  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 31.U
192
193  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
194  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.szewl1)
195
196  def fusionName: String = "slli32_srli31"
197}
198
199// Case: shift zero-extended word left by two
200// Source: `slli r1, r0, 32` + `srli r1, r1, 30`
201// Target: `szewl2 r1, r0` (customized internal opcode)
202class FusedSzewl2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
203  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
204  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 30.U
205
206  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
207  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.szewl2)
208
209  def fusionName: String = "slli32_srli30"
210}
211
212// Case: shift zero-extended word left by three
213// Source: `slli r1, r0, 32` + `srli r1, r1, 29`
214// Target: `szewl3 r1, r0` (customized internal opcode)
215class FusedSzewl3(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
216  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 32.U
217  def inst2Cond = instr(1) === Instructions.SRLI && instr(1)(25, 20) === 29.U
218
219  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
220  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.szewl3)
221
222  def fusionName: String = "slli32_srli29"
223}
224
225// Case: get the second byte
226// Source: `srli r1, r0, 8` + `andi r1, r1, 255`
227// Target: `byte2 r1, r0` (customized internal opcode)
228class FusedByte2(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
229  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 8.U
230  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 255.U
231
232  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
233  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.byte2)
234
235  def fusionName: String = "srli8_andi255"
236}
237
238// Case: shift left by four and add
239// Source: `slli r1, r0, 4` + `add r1, r1, r2`
240// Target: `sh4add r1, r0, r2` (customized internal opcode)
241class FusedSh4add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
242  def inst1Cond = instr(0) === Instructions.SLLI && instr(0)(25, 20) === 4.U
243  def inst2Cond = instr(1) === Instructions.ADD
244
245  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
246  override def thisInstr: Option[BitPat] = Some(Instructions.SLLI)
247  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
248  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.sh4add)
249  override def lsrc2NeedMux: Boolean = true
250
251  def fusionName: String = "slli4_add"
252}
253
254// Case: shift right by 29 and add
255// Source: `srli r1, r0, 29` + `add r1, r1, r2`
256// Target: `sr29add r1, r0, r2` (customized internal opcode)
257class FusedSr29add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
258  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 29.U
259  def inst2Cond = instr(1) === Instructions.ADD
260
261  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
262  override def thisInstr: Option[BitPat] = Some(Instructions.SRLI)
263  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
264  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.sr29add)
265  override def lsrc2NeedMux: Boolean = true
266
267  def fusionName: String = "srli29_add"
268}
269
270// Case: shift right by 30 and add
271// Source: `srli r1, r0, 30` + `add r1, r1, r2`
272// Target: `sr30add r1, r0, r2` (customized internal opcode)
273class FusedSr30add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
274  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 30.U
275  def inst2Cond = instr(1) === Instructions.ADD
276
277  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
278  override def thisInstr: Option[BitPat] = Some(Instructions.SRLI)
279  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
280  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.sr30add)
281  override def lsrc2NeedMux: Boolean = true
282
283  def fusionName: String = "srli30_add"
284}
285
286// Case: shift right by 31 and add
287// Source: `srli r1, r0, 31` + `add r1, r1, r2`
288// Target: `sr31add r1, r0, r2` (customized internal opcode)
289class FusedSr31add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
290  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 31.U
291  def inst2Cond = instr(1) === Instructions.ADD
292
293  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
294  override def thisInstr: Option[BitPat] = Some(Instructions.SRLI)
295  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
296  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.sr31add)
297  override def lsrc2NeedMux: Boolean = true
298
299  def fusionName: String = "srli31_add"
300}
301
302// Case: shift right by 32 and add
303// Source: `srli r1, r0, 32` + `add r1, r1, r2`
304// Target: `sr32add r1, r0, r2` (customized internal opcode)
305class FusedSr32add(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
306  def inst1Cond = instr(0) === Instructions.SRLI && instr(0)(25, 20) === 32.U
307  def inst2Cond = instr(1) === Instructions.ADD
308
309  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
310  override def thisInstr: Option[BitPat] = Some(Instructions.SRLI)
311  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
312  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.sr32add)
313  override def lsrc2NeedMux: Boolean = true
314
315  def fusionName: String = "srli32_add"
316}
317
318// Case: add one if odd, otherwise unchanged
319// Source: `andi r1, r0, 1`` + `add r1, r1, r2`
320// Target: `oddadd r1, r0, r2` (customized internal opcode)
321class FusedOddadd(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
322  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
323  def inst2Cond = instr(1) === Instructions.ADD
324
325  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
326  override def thisInstr: Option[BitPat] = Some(Instructions.ANDI)
327  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
328  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.oddadd)
329  override def lsrc2NeedMux: Boolean = true
330
331  def fusionName: String = "andi1_add"
332}
333
334// Case: add one if odd (in word format), otherwise unchanged
335// Source: `andi r1, r0, 1`` + `addw r1, r1, r2`
336// Target: `oddaddw r1, r0, r2` (customized internal opcode)
337class FusedOddaddw(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
338  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 1.U
339  def inst2Cond = instr(1) === Instructions.ADDW
340
341  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
342  override def thisInstr: Option[BitPat] = Some(Instructions.ANDI)
343  override def fusedInstr: Option[BitPat] = Some(Instructions.ADD)
344  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.oddaddw)
345  override def lsrc2NeedMux: Boolean = true
346
347  def fusionName: String = "andi1_addw"
348}
349
350// Case: addw and extract its lower 8 bits (fused into addwbyte)
351class FusedAddwbyte(pair: Seq[Valid[UInt]])(implicit p: Parameters)
352  extends BaseFusionCase(pair) {
353  // the first instruction is a ALUOpType.addw
354  // According to DecodeUnit.scala, only ADDIW and ADDW are ALUOpType.addw, which are used for inst1Cond.
355  def inst1Cond = instr(0) === Instructions.ADDIW || instr(0) === Instructions.ADDW
356  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0xff.U
357
358  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
359  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.addwbyte)
360
361  def fusionName: String = "addw_andi255"
362}
363
364// Case: addw and extract its lower 1 bit (fused into addwbit)
365class FusedAddwbit(pair: Seq[Valid[UInt]])(implicit p: Parameters)
366  extends FusedAddwbyte(pair) {
367
368  override def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 0x1.U
369
370  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.addwbit)
371
372  override def fusionName: String = "addw_andi1"
373}
374
375// Case: addw and zext.h (fused into addwzexth)
376class FusedAddwzexth(pair: Seq[Valid[UInt]])(implicit p: Parameters)
377  extends FusedAddwbyte(pair) {
378
379  override def inst2Cond = instr(1) === Instructions.ZEXT_H
380
381  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.addwzexth)
382
383  override def fusionName: String = "addw_zexth"
384}
385
386// Case: addw and sext.h (fused into addwsexth)
387class FusedAddwsexth(pair: Seq[Valid[UInt]])(implicit p: Parameters)
388  extends FusedAddwbyte(pair) {
389
390  override def inst2Cond = instr(1) === Instructions.SEXT_H
391
392  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.addwsexth)
393
394  override def fusionName: String = "addw_sexth"
395}
396
397// Case: logic operation and extract its LSB
398
399class FusedLogiclsb(pair: Seq[Valid[UInt]])(implicit p: Parameters)
400  extends BaseFusionCase(pair) {
401  // the first instruction is a logic (and, or, xor, orcb)
402  // (1) def ANDI               = BitPat("b?????????????????111?????0010011")
403  // (2) def AND                = BitPat("b0000000??????????111?????0110011")
404  // (3) def ORI                = BitPat("b?????????????????110?????0010011")
405  // (4) def OR                 = BitPat("b0000000??????????110?????0110011")
406  // (5) def XORI               = BitPat("b?????????????????100?????0010011")
407  // (6) def XOR                = BitPat("b0000000??????????100?????0110011")
408  // (7) def ORC_B              = BitPat("b001010000111?????101?????0010011")
409  val logicInstrList = Seq(Instructions.ANDI, Instructions.AND, Instructions.ORI, Instructions.OR,
410    Instructions.XORI, Instructions.XOR, Instructions.ORC_B)
411  def inst1Cond = VecInit(logicInstrList.map(_ === instr(0))).asUInt.orR
412  def inst2Cond = instr(1) === Instructions.ANDI && instr(1)(31, 20) === 1.U
413
414  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && destToRs1
415  override def fuOpType: Option[UInt => UInt] = Some(ALUOpType.logicToLsb)
416
417  def fusionName: String = "logic_andi1"
418}
419
420class FusedLogicZexth(pair: Seq[Valid[UInt]])(implicit p: Parameters)
421  extends FusedLogiclsb(pair) {
422
423  override def inst2Cond = instr(1) === Instructions.ZEXT_H
424  override def fuOpType: Option[UInt => UInt] = Some(ALUOpType.logicToZexth)
425
426  override def fusionName: String = "logic_zexth"
427}
428
429// Case: OR(Cat(src1(63, 8), 0.U(8.W)), src2)
430// Source: `andi r1, r0, -256`` + `or r1, r1, r2`
431class FusedOrh48(pair: Seq[Valid[UInt]])(implicit p: Parameters) extends BaseFusionCase(pair) {
432  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 0xf00.U
433  def inst2Cond = instr(1) === Instructions.OR
434
435  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
436  override def thisInstr: Option[BitPat] = Some(Instructions.ANDI)
437  override def fusedInstr: Option[BitPat] = Some(Instructions.OR)
438  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => ALUOpType.orh48)
439  override def lsrc2NeedMux: Boolean = true
440
441  def fusionName: String = "andi_f00_or"
442}
443
444// Case: mul 7bit data with 32-bit data
445// Source: `andi r1, r0, 127`` + `mulw r1, r1, r2`
446// Target: `mulw7 r1, r0, r2`
447class FusedMulw7(pair: Seq[Valid[UInt]])(implicit p: Parameters)
448  extends BaseFusionCase(pair) {
449  def inst1Cond = instr(0) === Instructions.ANDI && instr(0)(31, 20) === 127.U
450  def inst2Cond = instr(1) === Instructions.MULW
451
452  def isValid: Bool = inst1Cond && inst2Cond && withSameDest && (destToRs1 || destToRs2)
453  override def thisInstr: Option[BitPat] = Some(Instructions.ANDI)
454  override def fusedInstr: Option[BitPat] = Some(Instructions.MULW)
455  override def fuOpType: Option[UInt => UInt] = Some((_: UInt) => MDUOpType.mulw7)
456  override def lsrc2NeedMux: Boolean = true
457
458  def fusionName: String = "andi127_mulw"
459}
460
461class FusionDecodeInfo extends Bundle {
462  val rs2FromRs1 = Output(Bool())
463  val rs2FromRs2 = Output(Bool())
464  val rs2FromZero = Output(Bool())
465}
466
467class FusionDecodeReplace extends Bundle {
468  val fuType = Valid(FuType())
469  val fuOpType = Valid(FuOpType())
470  val lsrc2 = Valid(UInt(5.W))
471  val src2Type = Valid(SrcType())
472
473  def update(cs: CtrlSignals): Unit = {
474    when (fuType.valid) {
475      cs.fuType := fuType.bits
476    }
477    when (fuOpType.valid) {
478      cs.fuOpType := fuOpType.bits
479    }
480    when (lsrc2.valid) {
481      cs.lsrc(1) := lsrc2.bits
482    }
483    when (src2Type.valid) {
484      cs.srcType(1) := src2Type.bits
485    }
486  }
487}
488
489class FusionDecoder(implicit p: Parameters) extends XSModule {
490  val io = IO(new Bundle {
491    // T0: detect instruction fusions in these instructions
492    val in = Vec(DecodeWidth, Flipped(ValidIO(UInt(32.W))))
493    val inReady = Vec(DecodeWidth - 1, Input(Bool())) // dropRight(1)
494    // T1: decode result
495    val dec = Vec(DecodeWidth - 1, Input(new CtrlSignals)) // dropRight(1)
496    // T1: whether an instruction fusion is found
497    val out = Vec(DecodeWidth - 1, ValidIO(new FusionDecodeReplace)) // dropRight(1)
498    val info = Vec(DecodeWidth - 1, new FusionDecodeInfo) // dropRight(1)
499    // T1: fused instruction needs to be cleared
500    val clear = Vec(DecodeWidth, Output(Bool()))
501  })
502
503  io.clear.head := false.B
504
505  val instrPairs = io.in.dropRight(1).zip(io.in.drop(1)).map(x => Seq(x._1, x._2))
506  instrPairs.zip(io.dec).zip(io.out).zipWithIndex.foreach{ case (((pair, dec), out), i) =>
507    val fusionList = Seq(
508      new FusedAdduw(pair),
509      new FusedZexth(pair),
510      new FusedZexth1(pair),
511      new FusedSexth(pair),
512      new FusedSh1add(pair),
513      new FusedSh2add(pair),
514      new FusedSh3add(pair),
515      new FusedSzewl1(pair),
516      new FusedSzewl2(pair),
517      new FusedSzewl3(pair),
518      new FusedByte2(pair),
519      new FusedSh4add(pair),
520      new FusedSr29add(pair),
521      new FusedSr30add(pair),
522      new FusedSr31add(pair),
523      new FusedSr32add(pair),
524      new FusedOddadd(pair),
525      new FusedOddaddw(pair),
526      new FusedOrh48(pair),
527      new FusedMulw7(pair),
528      new FusedAddwbyte(pair),
529      new FusedAddwbit(pair),
530      new FusedAddwzexth(pair),
531      new FusedAddwsexth(pair),
532      new FusedLogiclsb(pair),
533      new FusedLogicZexth(pair)
534    )
535    val fire = io.in(i).valid && io.inReady(i)
536    val instrPairValid = RegEnable(VecInit(pair.map(_.valid)).asUInt.andR, false.B, io.inReady(i))
537    val fusionVec = RegEnable(VecInit(fusionList.map(_.isValid)), fire)
538    val thisCleared = io.clear(i)
539    out.valid := instrPairValid && !thisCleared && fusionVec.asUInt.orR
540    XSError(instrPairValid && PopCount(fusionVec) > 1.U, "more then one fusion matched\n")
541    def connectByInt(field: FusionDecodeReplace => Valid[UInt], replace: Seq[Option[Int]]): Unit = {
542      field(out.bits).valid := false.B
543      field(out.bits).bits := DontCare
544      val replaceVec = fusionVec.zip(replace).filter(_._2.isDefined)
545      if (replaceVec.nonEmpty) {
546        // constant values are grouped together for better timing.
547        val replEnable = VecInit(replaceVec.map(_._1)).asUInt.orR
548        val replTypes = replaceVec.map(_._2.get).distinct
549        val replSel = replTypes.map(t => VecInit(replaceVec.filter(_._2.get == t).map(_._1)).asUInt.orR)
550        field(out.bits).valid := replEnable
551        field(out.bits).bits := Mux1H(replSel, replTypes.map(_.U))
552      }
553    }
554    def connectByUIntFunc(
555      field: FusionDecodeReplace => Valid[UInt],
556      csField: CtrlSignals => UInt,
557      replace: Seq[Option[UInt => UInt]]
558    ): Unit = {
559      field(out.bits).valid := false.B
560      field(out.bits).bits := DontCare
561      val replaceVec = fusionVec.zip(replace).filter(_._2.isDefined).map(x => (x._1, x._2.get(csField(dec))))
562      if (replaceVec.nonEmpty) {
563        val replEnable = VecInit(replaceVec.map(_._1)).asUInt.orR
564        // constant values are grouped together for better timing.
565        val constReplVec = replaceVec.filter(_._2.isLit).map(x => (x._1, x._2.litValue))
566        val constReplTypes = constReplVec.map(_._2).distinct
567        val constReplEnable = constReplTypes.map(t => VecInit(constReplVec.filter(_._2 == t).map(_._1)).asUInt.orR)
568        val constReplResult = Mux1H(constReplEnable, constReplTypes.map(_.U))
569        // non-constant values have to be processed naively.
570        val noLitRepl = replaceVec.filterNot(_._2.isLit)
571        field(out.bits).valid := replEnable
572        field(out.bits).bits := Mux(VecInit(noLitRepl.map(_._1)).asUInt.orR, Mux1H(noLitRepl), constReplResult)
573      }
574    }
575    connectByInt((x: FusionDecodeReplace) => x.fuType, fusionList.map(_.fuType))
576    connectByUIntFunc((x: FusionDecodeReplace) => x.fuOpType, (x: CtrlSignals) => x.fuOpType, fusionList.map(_.fuOpType))
577    connectByInt((x: FusionDecodeReplace) => x.src2Type, fusionList.map(_.src2Type))
578    val src2WithZero = VecInit(fusionVec.zip(fusionList.map(_.lsrc2NeedZero)).filter(_._2).map(_._1)).asUInt.orR
579    val src2WithMux = VecInit(fusionVec.zip(fusionList.map(_.lsrc2NeedMux)).filter(_._2).map(_._1)).asUInt.orR
580    io.info(i).rs2FromZero := src2WithZero
581    io.info(i).rs2FromRs1 := src2WithMux && !RegEnable(fusionList.head.destToRs1, fire)
582    io.info(i).rs2FromRs2 := src2WithMux && RegEnable(fusionList.head.destToRs1, fire)
583    out.bits.lsrc2.valid := src2WithMux || src2WithZero
584    when (src2WithMux) {
585      out.bits.lsrc2.bits := RegEnable(fusionList.head.lsrc2MuxResult, fire)
586    }.otherwise {//elsewhen (src2WithZero) {
587      out.bits.lsrc2.bits := 0.U
588    }
589    // TODO: assume every instruction fusion clears the second instruction now
590    io.clear(i + 1) := out.valid
591    val lastFire = RegNext(fire)
592    fusionList.zip(fusionVec).foreach { case (f, v) =>
593      XSPerfAccumulate(s"case_${f.fusionName}_$i", instrPairValid && !thisCleared && v && lastFire)
594    }
595    XSPerfAccumulate(s"conflict_fusion_$i", instrPairValid && thisCleared && fusionVec.asUInt.orR && lastFire)
596  }
597
598  XSPerfAccumulate("fused_instr", PopCount(io.out.map(_.fire)))
599}
600