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