Skip to content

Commit 0738ff4

Browse files
committed
feat(Xstatistics): powerful perf counter
1 parent fcb8223 commit 0738ff4

File tree

2 files changed

+343
-4
lines changed

2 files changed

+343
-4
lines changed

src/main/scala/utility/ChiselTaggedTrace.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,19 @@ import freechips.rocketchip.amba.ahb.AHBImpMaster.bundle
2424

2525
trait HasDPICUtils extends BlackBox with HasBlackBoxInline {
2626
var moduleName: String = ""
27-
def init(args: Bundle, negedge: Boolean = false, comb_output: Boolean = false) = {
27+
28+
/**
29+
*
30+
* make module that call dpic function
31+
*
32+
* io must contains "clock, reset, en"
33+
*
34+
* @param args input the io
35+
* @param negedge trigger on negedge clock
36+
* @param comb_output if has output, use wire out not reg
37+
* @param overrideFuncname override the dpic name, default is class name
38+
*/
39+
def init(args: Bundle, negedge: Boolean = false, comb_output: Boolean = false, overrideFuncname: String = "") {
2840
val field = args.elements.map(t => {
2941
val name = t._1
3042
val tpes = t._2.getClass.getMethods.map(x => x.getName()).toList
@@ -48,9 +60,9 @@ trait HasDPICUtils extends BlackBox with HasBlackBoxInline {
4860
throw new Exception
4961
}
5062

51-
val className = this.getClass().getSimpleName()
52-
moduleName = className + "_DPIC_Helper"
53-
val dpicFunc = lang.Character.toLowerCase(className.charAt(0)) + className.substring(1)
63+
val dpic_name = if (overrideFuncname.isEmpty()) this.getClass().getSimpleName() else overrideFuncname
64+
moduleName = dpic_name + "_DPIC_Helper"
65+
val dpicFunc = lang.Character.toLowerCase(dpic_name.charAt(0)) + dpic_name.substring(1)
5466
val verilog =
5567
s"""
5668
|import "DPI-C" function ${if (has_out) "longint unsigned" else "void"} $dpicFunc
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/***************************************************************************************
2+
* Copyright (c) 2024-2026 Beijing Institute of Open Source Chip
3+
*
4+
* Utility is licensed under Mulan PSL v2.
5+
* You can use this software according to the terms and conditions of the Mulan PSL v2.
6+
* You may obtain a copy of Mulan PSL v2 at:
7+
* http://license.coscl.org.cn/MulanPSL2
8+
*
9+
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
10+
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
11+
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
12+
*
13+
* See the Mulan PSL v2 for more details.
14+
***************************************************************************************/
15+
16+
package utility.Xstatistics
17+
18+
import chisel3._
19+
import chisel3.util.HasBlackBoxInline
20+
import scala.collection.mutable.ArrayBuffer
21+
import utility.HasDPICUtils
22+
import freechips.rocketchip.diplomacy.BufferParams.default
23+
import utility.FileRegisters
24+
import scala.collection.mutable.ListBuffer
25+
26+
trait NodeExpr {
27+
def +(b: NodeExpr) = {
28+
new ConstExpr("(" + exprStr() + "+" + b.exprStr() + ")")
29+
}
30+
def -(b: NodeExpr) = {
31+
new ConstExpr("(" + exprStr() + "-" + b.exprStr() + ")")
32+
}
33+
def *(b: NodeExpr) = {
34+
new ConstExpr("(" + exprStr() + "*" + b.exprStr() + ")")
35+
}
36+
def /(b: NodeExpr) = {
37+
new ConstExpr("(" + "(double)" + exprStr() + "/" + b.exprStr() + ")")
38+
}
39+
40+
def funcWrapping(mathFunc: String) {
41+
new ConstExpr(mathFunc + s"(${exprStr()})")
42+
}
43+
44+
// virtual:
45+
def exprStr() = this match {
46+
case node: Node => node.varname()
47+
case _ => ???
48+
}
49+
}
50+
51+
class ConstExpr(val express: String) extends NodeExpr {
52+
override def exprStr() = express
53+
}
54+
55+
abstract class Node(val origin_name: String, val descript: String) {
56+
judgeName(origin_name)
57+
private val curModule = chisel3.XSCompatibility.currentModule.getOrElse(chisel3.XSCompatibility.currentModule.get)
58+
private val full_name = "Xstat" + curModule.hashCode.toHexString + "__" + origin_name
59+
protected val dpic_func = s"dpic_${varname()}"
60+
Xstatistics.register(this)
61+
62+
private def judgeName(perfName: String): Unit = {
63+
val regular = """(\w+)""".r
64+
perfName match {
65+
case regular(_) =>
66+
case _ => {
67+
throw new Exception("Statistics: " + perfName + " is not '\\w+' regular")
68+
}
69+
}
70+
}
71+
72+
def varname() = full_name
73+
// modulePath only can be called after chisel elaborate
74+
def modulePath() = curModule.pathName
75+
def dumpFormat(name: String, v: String) ="\"" + modulePath + "." + name + "\\t\\t\\t\" << " + v + s" << \"\\t\\t\\t#${descript}\\n\""
76+
77+
// virtual:
78+
def getVarDef() = s"uint64_t ${full_name} = 0;"
79+
// return the cpp code of dump and reset
80+
def getDumpingAndReset(): (String, String) = {
81+
(s"dout << ${dumpFormat(origin_name, varname())};", s"${varname()} = 0;")
82+
}
83+
def getDPIC(): String = ""
84+
}
85+
86+
class Scalar(name: String, desc: String) extends Node(name, desc) with NodeExpr {
87+
private class ScalarModule(name: String) extends HasDPICUtils {
88+
val io = IO(new Bundle{
89+
val clock = Input(Clock())
90+
val reset = Input(Reset())
91+
val en = Input(Bool())
92+
val n = Input(UInt(64.W))
93+
})
94+
init(io, false, false, name)
95+
}
96+
97+
def sample(n: UInt, en: Bool, clock: Clock) {
98+
val m = Module(new ScalarModule(dpic_func))
99+
m.io.clock := clock
100+
m.io.reset := false.B
101+
m.io.en := en
102+
m.io.n := n
103+
}
104+
105+
override def getDPIC() = {
106+
s"""
107+
|void ${dpic_func}(uint64_t val) {
108+
| ${varname()} += val;
109+
|}
110+
""".stripMargin
111+
}
112+
}
113+
114+
class Vector(n: Int, name: String, desc: String, val use_distr: Boolean = false) extends Node(name, desc) {
115+
private val total = varname() + "_total"
116+
private val overflows = varname() + "_overflows"
117+
private val minvalue = varname() + "_min_value"
118+
private val maxvalue = varname() + "_max_value"
119+
private var subnames = new Array[String](n)
120+
private val size = n
121+
private var min = 0;
122+
private var max = size - 1
123+
private var bkt = 1
124+
125+
subnames.zipWithIndex.foreach{ case (s, i) =>
126+
subnames(i) = i.toString()
127+
}
128+
129+
def apply(i : Int) = {
130+
if (i >= size) {
131+
throw new Exception(s"Statistics: ${varname()} out of range")
132+
}
133+
new ConstExpr(varname() + s"[${i}]")
134+
}
135+
136+
def sum() = {
137+
new ConstExpr(s"std::accumulate(${varname()}.begin(), ${varname()}.end(), 0)")
138+
}
139+
140+
def maxCount() = {
141+
new ConstExpr(s"*std::max_element(${varname()}.begin(), ${varname()}.end());")
142+
}
143+
144+
def minCount() = {
145+
new ConstExpr(s"*std::min_element(${varname()}.begin(), ${varname()}.end());")
146+
}
147+
148+
def len() = size
149+
150+
def setSubname(name: Array[String]) {
151+
require(subnames.size == name.size)
152+
subnames = name
153+
}
154+
155+
protected def setDist(min: Int, max: Int, bkt: Int) {
156+
// counter <- [min, max]
157+
require(use_distr)
158+
require(bkt > 0)
159+
require((max - min) / bkt < size)
160+
this.min = min
161+
this.max = max
162+
this.bkt = bkt
163+
for (i <- 0 until size) {
164+
// [low, up]
165+
val low = i * bkt + min
166+
var up = (i + 1) * bkt + min - 1
167+
up = math.min(up, max)
168+
subnames(i) = s"${low}-${up}"
169+
}
170+
}
171+
172+
private class VectorModule(name: String) extends HasDPICUtils {
173+
val io = IO(new Bundle{
174+
val clock = Input(Clock())
175+
val reset = Input(Reset())
176+
val en = Input(Bool())
177+
val i = Input(UInt(64.W))
178+
val n = Input(UInt(64.W))
179+
})
180+
init(io, false, false, name)
181+
}
182+
183+
def sample(i: UInt, n: UInt, en: Bool, clock: Clock) = {
184+
val m = Module(new VectorModule(dpic_func))
185+
m.io.clock := clock
186+
m.io.reset := false.B
187+
m.io.en := en
188+
m.io.i := i
189+
m.io.n := n
190+
}
191+
192+
override def getVarDef() = {
193+
val s = s"std::array<uint64_t, ${size}> ${varname()};"
194+
if (use_distr) {
195+
s + s"""
196+
|uint64_t ${total} = 0;
197+
|uint64_t ${overflows} = 0;
198+
|uint64_t ${minvalue} = 0;
199+
|uint64_t ${maxvalue} = 0;
200+
""".stripMargin
201+
} else {
202+
s
203+
}
204+
}
205+
override def getDumpingAndReset() = {
206+
var dumpstr = s"${
207+
(for (i <- 0 until size) yield (origin_name + "::" + subnames(i), varname() + s"[${i}]")).map{case (n, v) =>
208+
s"dout << ${dumpFormat(n, v)};\n"
209+
}.reduce(_ + _)
210+
}"
211+
var resetstr = s"std::fill(${varname()}.begin(), ${varname()}.end(), 0);"
212+
if (use_distr) {
213+
dumpstr += s"""
214+
|dout << ${dumpFormat(origin_name + "::" + "total", total)};
215+
|dout << ${dumpFormat(origin_name + "::" + "overflows", overflows)};
216+
|dout << ${dumpFormat(origin_name + "::" + "min_value", minvalue)};
217+
|dout << ${dumpFormat(origin_name + "::" + "max_value", maxvalue)};
218+
""".stripMargin
219+
resetstr += s"""
220+
|${total} = 0;
221+
|${overflows} = 0;
222+
|${minvalue} = 0;
223+
|${maxvalue} = 0;
224+
""".stripMargin
225+
}
226+
(dumpstr, resetstr)
227+
}
228+
229+
override def getDPIC() = {
230+
if (use_distr) {
231+
s"""
232+
|void ${dpic_func}(uint64_t index, uint64_t val) {
233+
| if (index <= ${max} && index >= ${min}) {
234+
| unsigned t = (unsigned)(std::floor(index - ${min}) / ${bkt});
235+
| ${varname()}[t] += val;
236+
| } else {
237+
| ${overflows} += val;
238+
| }
239+
| ${total} += index * val;
240+
| ${minvalue} = std::min(index, ${minvalue});
241+
| ${maxvalue} = std::max(index, ${maxvalue});
242+
|}
243+
""".stripMargin
244+
} else {
245+
s"""
246+
|void ${dpic_func}(uint64_t index, uint64_t val) {
247+
| if (index < ${size}) {
248+
| ${varname()}[index] += val;
249+
| }
250+
|}
251+
""".stripMargin
252+
}
253+
}
254+
}
255+
256+
class Distribution(min: Int, max: Int, bkt: Int, name: String, desc: String) extends Vector(((max + 1 - min).toFloat / bkt).ceil.round, name, desc, true)
257+
{
258+
// couner <- [min, max], group by bkt
259+
setDist(min, max, bkt)
260+
}
261+
262+
class Formula(name: String, desc: String) extends Node(name, desc) with NodeExpr {
263+
var express: String = ""
264+
def :=(expr: NodeExpr) {
265+
express = expr.exprStr()
266+
}
267+
268+
override def getVarDef() = ""
269+
override def getDumpingAndReset() = {
270+
(s"dout << ${dumpFormat(origin_name, exprStr())};", "")
271+
}
272+
override def exprStr() = express
273+
}
274+
275+
object Xstatistics {
276+
val counters = new ListBuffer[Node]
277+
def register(n: Node) {
278+
val conflict = counters.exists(_.varname() == n.varname())
279+
if (conflict) {
280+
throw new Exception(s"Statistics: ${n.varname()} has already exists")
281+
}
282+
counters.addOne(n)
283+
}
284+
285+
def getCpp() = {
286+
val cppvars: StringBuilder = new StringBuilder
287+
val dumpstr: StringBuilder = new StringBuilder
288+
val resetstr: StringBuilder = new StringBuilder
289+
val dpicfuncs: StringBuilder = new StringBuilder
290+
291+
counters.foreach{c =>
292+
cppvars ++= c.getVarDef() + "\n"
293+
val t = c.getDumpingAndReset()
294+
dumpstr ++= t._1 + "\n"
295+
resetstr ++= t._2 + "\n"
296+
dpicfuncs ++= c.getDPIC() + "\n"
297+
}
298+
299+
s"""
300+
|#include <cstdint>
301+
|#include <array>
302+
|#include <string>
303+
|#include <algorithm>
304+
|#include <numeric>
305+
|#include <cmath>
306+
|#include <iostream>
307+
|
308+
|${cppvars}
309+
|
310+
|// call this function when dumping
311+
|void xs_statistics_dump() {
312+
|auto& dout = std::cerr;
313+
|${dumpstr}
314+
|${resetstr}
315+
|}
316+
|
317+
|extern "C" {
318+
|${dpicfuncs}
319+
|}
320+
|
321+
""".stripMargin
322+
}
323+
324+
def addToFileRegisters {
325+
FileRegisters.add("xs_statistics.cpp", getCpp())
326+
}
327+
}

0 commit comments

Comments
 (0)