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