xref: /XiangShan/src/main/scala/top/BusPerfMonitor.scala (revision bb2f3f51dd67f6e16e0cc1ffe43368c9fc7e4aef)
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 top
18
19import org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp}
23import freechips.rocketchip.tilelink.TLMessages._
24import freechips.rocketchip.tilelink._
25import utility.{GTimer, MemReqSource, ReqSourceKey, XSPerfAccumulate}
26
27class BusPerfMonitor(name: String, stat_latency: Boolean)(implicit p: Parameters) extends LazyModule {
28  val node = TLAdapterNode()
29  lazy val module = new BusPerfMonitorImp(this, name, stat_latency)
30}
31
32class BusPerfMonitorImp(outer: BusPerfMonitor, name: String, stat_latency: Boolean)
33  extends LazyModuleImp(outer)
34{
35
36  outer.node.in.zip(outer.node.out).foreach{
37    case ((in, edgeIn), (out, edgeOut)) =>
38      out <> in
39  }
40
41  def PERF_CHN[T <: TLChannel](clientName: String, chn: DecoupledIO[T]) = {
42
43    val channelName = chn.bits.channelName.replaceAll(" ", "_").replaceAll("'", "")
44    XSPerfAccumulate(s"${clientName}_${channelName}_fire", chn.fire)
45    XSPerfAccumulate(s"${clientName}_${channelName}_stall", chn.valid && !chn.ready)
46
47    val ops = chn.bits match {
48      case _: TLBundleA => TLMessages.a.map(_._1)
49      case _: TLBundleB => TLMessages.b.map(_._1)
50      case _: TLBundleC => TLMessages.c.map(_._1)
51      case _: TLBundleD => TLMessages.d.map(_._1)
52      case _: TLBundleE => Nil
53    }
54
55    for((op_raw, i) <- ops.zipWithIndex){
56      val op = s"${op_raw}".replaceAll(" ", "_")
57      chn.bits match {
58        case a: TLBundleA =>
59          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_fire",
60            i.U === a.opcode && chn.fire
61          )
62          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_stall",
63            i.U === a.opcode && chn.valid && !chn.ready
64          )
65        case b: TLBundleB =>
66          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_fire",
67            i.U === b.opcode && chn.fire
68          )
69          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_stall",
70            i.U === b.opcode && chn.valid && !chn.ready
71          )
72        case c: TLBundleC =>
73          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_fire",
74            i.U === c.opcode && chn.fire
75          )
76          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_stall",
77            i.U === c.opcode && chn.valid && !chn.ready
78          )
79        case d: TLBundleD =>
80          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_fire",
81            i.U === d.opcode && chn.fire
82          )
83          XSPerfAccumulate(s"${clientName}_${channelName}_${op}_stall",
84            i.U === d.opcode && chn.valid && !chn.ready
85          )
86        case e: TLBundleE => throw new IllegalArgumentException("Cannot reach here")
87      }
88    }
89  }
90
91  for (((in, edgeIn), i) <- outer.node.in.zipWithIndex) {
92    val clientName = s"${name}_${edgeIn.master.masters.head.name}_bank_$i"
93    PERF_CHN(clientName, in.a)
94    PERF_CHN(clientName, in.d)
95    if (in.params.hasBCE) {
96      PERF_CHN(clientName, in.b)
97      PERF_CHN(clientName, in.c)
98      PERF_CHN(clientName, in.e)
99    }
100  }
101
102  if (stat_latency) {
103    val nrEdge = outer.node.in.length.toInt
104    if (outer.node.in.isEmpty) {
105      println(s"name = $name")
106    }
107    val edgeIn = outer.node.in.head._2
108
109    class RecordEntry()(implicit p: Parameters) extends Bundle {
110      val valid = Bool()
111      val timeStamp = UInt(64.W)
112      val reqType = UInt(8.W)
113    }
114
115    // For simplicity, latency statistic works between nodes with SINGLE edge
116    require(nrEdge == 1)
117    val timer = GTimer()
118    val nrSource = math.pow(2, edgeIn.bundle.sourceBits).toInt
119    val latencyRecord = RegInit(VecInit(Seq.fill(nrSource)(0.U.asTypeOf(new RecordEntry()))))
120    val latencySum = RegInit(0.U(128.W))
121    val nrRecord = RegInit(0.U(128.W))
122
123    outer.node.in.zip(outer.node.out).zipWithIndex.foreach {
124      case (((in, edgeIn), (out, edgeOut)), i) =>
125        val channelA = in.a
126        when(channelA.fire &&
127          channelA.bits.opcode =/= Hint &&
128          channelA.bits.opcode =/= PutFullData &&
129          channelA.bits.opcode =/= PutPartialData
130        ) {
131          // Valid channel A fire, record it
132          assert(latencyRecord(channelA.bits.source).valid === false.B)
133          latencyRecord(channelA.bits.source).valid := true.B
134          latencyRecord(channelA.bits.source).timeStamp := timer
135          latencyRecord(channelA.bits.source).reqType := channelA.bits.user.lift(ReqSourceKey).getOrElse(MemReqSource.NoWhere.id.U)
136        }
137        val channelD = in.d
138        val (first, _, _, _) = edgeIn.count(channelD)
139        // Valid channel D fire, resolve it
140        val resolveRecord = channelD.fire && first &&
141          channelD.bits.opcode =/= ReleaseAck &&
142          channelD.bits.opcode =/= AccessAck
143        val latency = WireInit(0.U(64.W))
144        when(resolveRecord) {
145          assert(latencyRecord(channelD.bits.source).valid === true.B)
146          latencyRecord(channelD.bits.source).valid := false.B
147          latency := timer - latencyRecord(channelD.bits.source).timeStamp
148          latencySum := latencySum + timer
149          nrRecord := nrRecord + 1.U
150          // printf("timer: %x\n", latency)
151        }
152        XSPerfAccumulate(name + "_nrRecord_all", resolveRecord)
153        XSPerfAccumulate(name + "_latencySum_all", Mux(resolveRecord, latency, 0.U))
154
155        for (j <- 0 until MemReqSource.ReqSourceCount.id) {
156          val typeMatch = latencyRecord(channelD.bits.source).reqType === j.U
157          XSPerfAccumulate(name + s"_nrRecord_type${j}", resolveRecord && typeMatch)
158          XSPerfAccumulate(name + s"_latencySum_type${j}", Mux(resolveRecord && typeMatch, latency, 0.U))
159        }
160    }
161  }
162
163}
164
165object BusPerfMonitor {
166  def apply(
167     name: String,
168     enable: Boolean = false,
169     stat_latency: Boolean = false,
170     add_reqkey: Boolean = false)(implicit p: Parameters) =
171  {
172    if(enable){
173      val busPMU = LazyModule(new BusPerfMonitor(name, stat_latency))
174      busPMU.node
175    } else {
176      TLTempNode()
177    }
178  }
179}
180