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