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 full_name = " xs_statistics_" + origin_name
58+ protected val dpic_func = s " dpic_ ${varname()}"
59+ val modulePath = chisel3.XSCompatibility .currentModule.getOrElse(chisel3.XSCompatibility .currentModule.get).toString()
60+
61+ private def judgeName (perfName : String ): Unit = {
62+ val regular = """ (\w+)""" .r
63+ perfName match {
64+ case regular(_) =>
65+ case _ => {
66+ throw new Exception (" Statistics: " + perfName + " is not '\\ w+' regular" )
67+ }
68+ }
69+ }
70+
71+ Xstatistics .register(this )
72+
73+ def varname () = full_name
74+
75+ def dumpFormat (name : String , v : String ) = " \" " + modulePath + " ." + name + " \\ t\\ t\" << " + v + s " << \"\\ t \\ t# ${descript}\\ n \" "
76+ // virtual:
77+ def getVarDef () = s " uint64_t ${full_name} = 0; "
78+ def getDumpingAndReset () = {
79+ s """
80+ |dout << ${dumpFormat(origin_name, varname())};
81+ | ${varname()} = 0; // reset after dump
82+ """ .stripMargin
83+ }
84+ def getDPIC (): String = " "
85+ }
86+
87+ class Scalar (name : String , desc : String ) extends Node (name, desc) with NodeExpr {
88+ private class ScalarModule (name : String ) extends HasDPICUtils {
89+ val io = IO (new Bundle {
90+ val clock = Input (Clock ())
91+ val reset = Input (Reset ())
92+ val en = Input (Bool ())
93+ val n = Input (UInt (64 .W ))
94+ })
95+ init(io, false , false , name)
96+ }
97+
98+ def sample (n : UInt , en : Bool , clock : Clock ) {
99+ val m = Module (new ScalarModule (dpic_func))
100+ m.io.clock := clock
101+ m.io.reset := false .B
102+ m.io.en := en
103+ m.io.n := n
104+ }
105+
106+ override def getDPIC () = {
107+ s """
108+ |void ${dpic_func}(uint64_t val) {
109+ | ${varname()} += val;
110+ |}
111+ """ .stripMargin
112+ }
113+ }
114+
115+ class Vector (n : Int , name : String , desc : String , val use_distr : Boolean = false ) extends Node (name, desc) {
116+ private val total = varname() + " _total"
117+ private val overflows = varname() + " _overflows"
118+ private val minvalue = varname() + " _min_value"
119+ private val maxvalue = varname() + " _max_value"
120+ private var subnames = new Array [String ](n)
121+ private val size = n
122+ private var min = 0 ;
123+ private var max = size - 1
124+ private var bkt = 1
125+
126+ subnames.zipWithIndex.foreach{ case (s, i) =>
127+ subnames(i) = i.toString()
128+ }
129+
130+ def apply (i : Int ) = {
131+ if (i >= size) {
132+ throw new Exception (s " Statistics: ${varname()} out of range " )
133+ }
134+ new ConstExpr (varname() + s " [ ${i}] " )
135+ }
136+
137+ def sum () = {
138+ new ConstExpr (s " std::accumulate( ${varname()}.begin(), ${varname()}.end(), 0) " )
139+ }
140+
141+ def maxCount () = {
142+ new ConstExpr (s " *std::max_element( ${varname()}.begin(), ${varname()}.end()); " )
143+ }
144+
145+ def minCount () = {
146+ new ConstExpr (s " *std::min_element( ${varname()}.begin(), ${varname()}.end()); " )
147+ }
148+
149+ def len () = size
150+
151+ def setSubname (name : Array [String ]) {
152+ require(subnames.size == name.size)
153+ subnames = name
154+ }
155+
156+ protected def setDist (min : Int , max : Int , bkt : Int ) {
157+ // counter <- [min, max]
158+ require(use_distr)
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 """
194+ |std::array<uint64_t, ${size}> ${varname()};
195+ """ .stripMargin
196+ if (use_distr) {
197+ s + s """
198+ |uint64_t ${total} = 0;
199+ |uint64_t ${overflows} = 0;
200+ |uint64_t ${minvalue} = 0;
201+ |uint64_t ${maxvalue} = 0;
202+ """ .stripMargin
203+ } else {
204+ s
205+ }
206+ }
207+ override def getDumpingAndReset () = {
208+ val s = s """
209+ | ${
210+ (for (i <- 0 until size) yield (origin_name + " ::" + subnames(i), varname() + s " [ ${i}] " )).map{case (n, v) =>
211+ s " dout << ${dumpFormat(n, v)}; \n "
212+ }.reduce(_ + _)
213+ }
214+ |std::fill( ${varname()}.begin(), ${varname()}.end(), 0);
215+ """ .stripMargin
216+ if (use_distr) {
217+ s + s """
218+ |dout << ${dumpFormat(origin_name + " ::" + " total" , total)};
219+ |dout << ${dumpFormat(origin_name + " ::" + " overflows" , overflows)};
220+ |dout << ${dumpFormat(origin_name + " ::" + " min_value" , minvalue)};
221+ |dout << ${dumpFormat(origin_name + " ::" + " max_value" , maxvalue)};
222+ | ${total} = 0;
223+ | ${overflows} = 0;
224+ | ${minvalue} = 0;
225+ | ${maxvalue} = 0;
226+ """ .stripMargin
227+ } else {
228+ s
229+ }
230+ }
231+
232+ override def getDPIC () = {
233+ if (use_distr) {
234+ s """
235+ |void ${dpic_func}(uint64_t index, uint64_t val) {
236+ | if (index <= ${max} && index >= ${min}) {
237+ | index = (uint64_t)(std::floor(index - ${min}) / ${bkt});
238+ | ${varname()}[index] += val;
239+ | } else {
240+ | ${overflows}++;
241+ | }
242+ | ${total} += index;
243+ | ${minvalue} = std::min(index, ${minvalue});
244+ | ${maxvalue} = std::max(index, ${maxvalue});
245+ |}
246+ """ .stripMargin
247+ } else {
248+ s """
249+ |void ${dpic_func}(uint64_t index, uint64_t val) {
250+ | if (index < ${size}) {
251+ | ${varname()}[index] += val;
252+ | }
253+ |}
254+ """ .stripMargin
255+ }
256+ }
257+ }
258+
259+ class Distribution (min : Int , max : Int , bkt : Int , name : String , desc : String ) extends Vector (((max + 1 - min).toFloat / bkt).ceil.round, name, desc, true )
260+ {
261+ // couner <- [min, max], group by bkt
262+ require(bkt > 0 )
263+ setDist(min, max, bkt)
264+ }
265+
266+ class Formula (name : String , desc : String ) extends Node (name, desc) with NodeExpr {
267+ var express : String = " "
268+ def := (expr : NodeExpr ) {
269+ express = expr.exprStr()
270+ }
271+
272+ override def getVarDef () = " "
273+ override def getDumpingAndReset () = {
274+ s """
275+ |dout << ${dumpFormat(origin_name, exprStr())};
276+ """ .stripMargin
277+ }
278+ override def exprStr () = express
279+ }
280+
281+
282+ object Xstatistics {
283+ val counters = new ListBuffer [Node ]
284+ def register (n : Node ) {
285+ val conflict = counters.exists(_.varname() == n.varname())
286+ if (conflict) {
287+ throw new Exception (s " Statistics: ${n.varname()} has already exists " )
288+ }
289+ counters.addOne(n)
290+ }
291+
292+ def getCpp () = {
293+ val cppvars : StringBuilder = new StringBuilder
294+ val dumpstr : StringBuilder = new StringBuilder
295+ val dpicfuncs : StringBuilder = new StringBuilder
296+
297+ dpicfuncs ++= " extern \" C\" {\n "
298+ counters.foreach{c =>
299+ cppvars ++= c.getVarDef() + " \n "
300+ dumpstr ++= c.getDumpingAndReset() + " \n "
301+ dpicfuncs ++= c.getDPIC() + " \n "
302+ }
303+ dpicfuncs ++= " }\n "
304+
305+
306+ s """
307+ |#include <cstdint>
308+ |#include <array>
309+ |#include <string>
310+ |#include <algorithm>
311+ |#include <numeric>
312+ |#include <cmath>
313+ |#include <iostream>
314+ |
315+ | ${cppvars}
316+ |
317+ |// call this function when dumping
318+ |void xs_statistics_dump() {
319+ |auto& dout = std::cerr;
320+ | ${dumpstr}
321+ |}
322+ |
323+ | ${dpicfuncs}
324+ |
325+ """ .stripMargin
326+ }
327+
328+ def addToFileRegisters {
329+ FileRegisters .add(" xs_statistics.cpp" , getCpp())
330+ }
331+ }
0 commit comments