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