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