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