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