|
| 1 | +/************************************************************************* |
| 2 | +
|
| 3 | + This file is part of the ParaNut project. |
| 4 | +
|
| 5 | + Copyright (C) 2010-2022 Alexander Bahle <alexander.bahle@hs-augsburg.de> |
| 6 | + Gundolf Kiefer <gundolf.kiefer@hs-augsburg.de> |
| 7 | + Christian H. Meyer <christian.meyer@hs-augsburg.de> |
| 8 | + Hochschule Augsburg, University of Applied Sciences |
| 9 | +
|
| 10 | + Redistribution and use in source and binary forms, with or without modification, |
| 11 | + are permitted provided that the following conditions are met: |
| 12 | +
|
| 13 | + 1. Redistributions of source code must retain the above copyright notice, this |
| 14 | + list of conditions and the following disclaimer. |
| 15 | +
|
| 16 | + 2. Redistributions in binary form must reproduce the above copyright notice, |
| 17 | + this list of conditions and the following disclaimer in the documentation and/or |
| 18 | + other materials provided with the distribution. |
| 19 | +
|
| 20 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 21 | + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 22 | + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 23 | + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| 24 | + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 25 | + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 26 | + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| 27 | + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 29 | + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | +
|
| 31 | + *************************************************************************/ |
| 32 | + |
| 33 | + |
| 34 | +#include "base.h" |
| 35 | + |
| 36 | +#include <float.h> |
| 37 | +#include <stdarg.h> |
| 38 | +#include <stdio.h> |
| 39 | +#include <string.h> |
| 40 | + |
| 41 | +#include <systemc.h> |
| 42 | + |
| 43 | +// *********** Dynamic Configuration ************ |
| 44 | + |
| 45 | + |
| 46 | +int pn_cfg_vcd_level = 0; |
| 47 | +int pn_cfg_insn_trace = 0; |
| 48 | +bool pn_cfg_disable_cache = 0; |
| 49 | +bool pn_cfg_debug_mode = 0; |
| 50 | + |
| 51 | + |
| 52 | +// **************** Tracing ********************* |
| 53 | + |
| 54 | + |
| 55 | +bool pn_trace_verbose = false; |
| 56 | + |
| 57 | + |
| 58 | +std::string pn_GetTraceName (sc_object *obj, const char *name, int dim, int arg1, int arg2) { |
| 59 | + std::string ret; |
| 60 | + |
| 61 | + if (dim < 0 || dim > 2) |
| 62 | + PN_ERRORF (("pn_GetTraceName: Parameter dim outside of range (0-2): %d", dim)); |
| 63 | + |
| 64 | + // Read full object name and get the base module name |
| 65 | + ret = obj->name (); |
| 66 | + ret = ret.substr (0, ret.find_last_of ('.') + 1); |
| 67 | + |
| 68 | + // Add name... |
| 69 | + ret += name; |
| 70 | + |
| 71 | + // Add first dimension... |
| 72 | + if (dim > 0) |
| 73 | + ret += "(" + std::to_string (arg1) + ")"; |
| 74 | + |
| 75 | + // Add second dimension... |
| 76 | + if (dim == 2) |
| 77 | + ret += "(" + std::to_string (arg2) + ")"; |
| 78 | + |
| 79 | + return ret; |
| 80 | +} |
| 81 | + |
| 82 | + |
| 83 | +// **************** Testbench helpers *********** |
| 84 | + |
| 85 | + |
| 86 | +sc_trace_file *pn_trace_file = NULL; |
| 87 | + |
| 88 | + |
| 89 | +char *pn_TbPrintf (const char *format, ...) { |
| 90 | + static char buf[200]; |
| 91 | + |
| 92 | + va_list ap; |
| 93 | + va_start (ap, format); |
| 94 | + vsprintf (buf, format, ap); |
| 95 | + return buf; |
| 96 | +} |
| 97 | + |
| 98 | + |
| 99 | +void pn_TbAssert (bool cond, const char *msg, const char *filename, const int line) { |
| 100 | + if (!cond) { |
| 101 | + fprintf (stderr, "ASSERTION FAILURE: %s, %s:%i", sc_time_stamp ().to_string ().c_str (), filename, line); |
| 102 | + if (msg) |
| 103 | + fprintf (stderr, ": %s\n", msg); |
| 104 | + else |
| 105 | + fprintf (stderr, "\n"); |
| 106 | + if (pn_trace_file) sc_close_vcd_trace_file (pn_trace_file); |
| 107 | + abort (); |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | + |
| 112 | +void pn_TbInfo (const char *msg, const char *filename, const int line) { |
| 113 | + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 15; |
| 114 | + fprintf (stderr, "(INFO): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); |
| 115 | +} |
| 116 | + |
| 117 | + |
| 118 | +void pn_TbWarning (const char *msg, const char *filename, const int line) { |
| 119 | + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 12; |
| 120 | + fprintf (stderr, "(WARNING): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); |
| 121 | +} |
| 122 | + |
| 123 | + |
| 124 | +void pn_TbError (const char *msg, const char *filename, const int line) { |
| 125 | + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 14; |
| 126 | + fprintf (stderr, "(ERROR): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); |
| 127 | + if (pn_trace_file) sc_close_vcd_trace_file (pn_trace_file); |
| 128 | + exit (3); |
| 129 | +} |
| 130 | + |
| 131 | + |
| 132 | +// **************** DisAss ********************** |
| 133 | + |
| 134 | + |
| 135 | +char *pn_DisAss (TWord insn) { |
| 136 | + static char ret[80] = ""; |
| 137 | + TWord opcode, funct3, funct7, rs1, rs2, rd, bit30, bit20, bit21, bit25, bit28, bit29, itype, utype, btype, jtype, stype; |
| 138 | + sc_uint<32> inst = insn; |
| 139 | + |
| 140 | + opcode = insn & 0x7f; |
| 141 | + funct3 = (insn >> 12) & 0x7; |
| 142 | + funct7 = (insn >> 25); |
| 143 | + |
| 144 | + rd = (insn >> 7) & 0x1f; |
| 145 | + rs1 = (insn >> 15) & 0x1f; |
| 146 | + rs2 = (insn >> 20) & 0x1f; |
| 147 | + |
| 148 | + bit20 = (insn >> 20) & 0x1; |
| 149 | + bit21 = (insn >> 21) & 0x1; |
| 150 | + bit25 = (insn >> 25) & 0x1; |
| 151 | + bit28 = (insn >> 28) & 0x1; |
| 152 | + bit29 = (insn >> 28) & 0x1; |
| 153 | + bit30 = (insn >> 30) & 0x1; |
| 154 | + |
| 155 | + itype = ((insn >> 20) ^ 0x800) - 0x800; |
| 156 | + utype = insn & 0xFFFFF000; |
| 157 | + btype = ((sc_uint<32>)(inst[31], inst[7], inst (30, 25), inst (11, 8), 0) ^ 0x1000) - 0x1000; |
| 158 | + jtype = ((sc_uint<32>)(inst[31], inst (19, 12), inst[20], inst (30, 25), inst (24, 21), 0) ^ 0x100000) - 0x100000; |
| 159 | + stype = ((((sc_uint<32>)(inst (31, 25), 0, 0, 0, 0, 0)).value () + rd) ^ 0x800) - 0x800; |
| 160 | + |
| 161 | + strcpy (ret, " "); |
| 162 | + |
| 163 | + // ALU instructions... |
| 164 | + if (opcode == 0x13) { // OP_IMM |
| 165 | + const char *table[] = { "addi", "slli", "slti", "sltiu", "xori", "srli", "ori", "andi" }; |
| 166 | + if (funct3 == 5 || funct3 == 1) { |
| 167 | + if (bit30) table[funct3] = "srai"; |
| 168 | + itype &= 0x1F; // shamt |
| 169 | + } |
| 170 | + sprintf (ret + 2, "%s r%i, r%i, 0x%x", table[funct3], rd, rs1, itype); |
| 171 | + } else if (opcode == 0x33 && !bit25) { // OP (Bit 25 is on for M-Extension (DIV/MUL...) |
| 172 | + const char *table[] = { "add", "sll", "slt", "sltu", "xor", "srl", "or", "and" }; |
| 173 | + if (funct3 == 0) { |
| 174 | + if (bit30) table[funct3] = "sub"; |
| 175 | + } else if (funct3 == 5) { |
| 176 | + if (bit30) table[funct3] = "sra"; |
| 177 | + } |
| 178 | + sprintf (ret + 2, "%s r%i, r%i, r%i", table[funct3], rd, rs1, rs2); |
| 179 | + } else if ((opcode == 0x33 && bit25)) { // OP - M-Extension |
| 180 | + static const char *table[] = { "mul", "mulh", "mulhsu", "mulhu", |
| 181 | + "div", "divu", "rem", "remu" }; |
| 182 | + sprintf (ret + 2, "%s r%i, r%i, r%i", table[funct3], rd, rs1, rs2); |
| 183 | + } else if (opcode == 0x17) { // AUIPC |
| 184 | + sprintf (ret + 2, "auipc r%i, 0x%x", rd, utype); |
| 185 | + } else if (opcode == 0x37) { // LUI |
| 186 | + sprintf (ret + 2, "lui r%i, 0x%x", rd, utype); |
| 187 | + } else if (opcode == 0x63) { // BRANCH |
| 188 | + const char *table[] = { "beq", "bneq", "INV_SUB", "INV_SUB", "blt", "bge", "bltu", "bgeu" }; |
| 189 | + sprintf (ret + 2, "%s r%i, r%i, 0x%x", table[funct3], rs1, rs2, btype); |
| 190 | + } else if (opcode == 0x6F) { // JAL |
| 191 | + sprintf (ret + 2, "jal r%i, 0x%x", rd, jtype); |
| 192 | + } else if (opcode == 0x67) { // JALR |
| 193 | + sprintf (ret + 2, "jalr r%i, r%i, 0x%x", rd, rs1, itype); |
| 194 | + } else if (opcode == 0x03) { // LOAD |
| 195 | + static const char *table[] = { "lb", "lh", "lw", "INV_SUB", "lbu", "lhu" }; |
| 196 | + sprintf (ret + 2, "%s r%i, 0x%x(r%i)", table[funct3], rd, itype, rs1); |
| 197 | + } else if (opcode == 0x23) { // STORE |
| 198 | + static const char *table[] = { "sb", "sh", "sw" }; |
| 199 | + sprintf (ret + 2, "%s r%i, 0x%x(r%i)", table[funct3], rs2, stype, rs1); |
| 200 | + } else if (opcode == 0x73) { // SYSTEM |
| 201 | + const char *table[] = { "ecall", "csrrw", "csrrs", "csrrc", |
| 202 | + "INV_SUB", "csrrwi", "csrrsi", "csrrci" }; |
| 203 | + if (bit25 && bit28) table[0] = "sfence.vma"; |
| 204 | + if (bit21) table[0] = bit28 ? bit29 ? "mret" : "sret" : "uret"; |
| 205 | + if (bit20) table[0] = "ebreak"; |
| 206 | + if (funct3 == 0) |
| 207 | + sprintf (ret + 2, "%s", table[funct3]); |
| 208 | + else if (funct3 < 4) |
| 209 | + sprintf (ret + 2, "%s r%i, 0x%x, r%i", table[funct3], rd, itype, rs1); |
| 210 | + else |
| 211 | + sprintf (ret + 2, "%s r%i, 0x%x, 0x%x", table[funct3], rd, itype, rs1); |
| 212 | + } else if (opcode == 0x0F) { |
| 213 | + sprintf (ret + 2, "fence"); |
| 214 | + } else if (opcode == 0xB) { // PARA |
| 215 | + static const char *table[] = { "halt", "cinvalidate", "cwriteback", "cflush" }; |
| 216 | + if (funct3 == 0) |
| 217 | + sprintf (ret + 2, "%s", table[funct3]); |
| 218 | + else |
| 219 | + sprintf (ret + 2, "%s 0x%x(r%i) ", table[funct3], itype, rs1); |
| 220 | + } else if (opcode == 0x2F) { // AMO |
| 221 | + if (funct3 == 2 && (funct7 >> 2) == 2) // LR.W |
| 222 | + sprintf (ret + 2, "lr.w r%i, (r%i)", rd, rs1); |
| 223 | + else if (funct3 == 2 && (funct7 >> 2) == 3) // SC.W |
| 224 | + sprintf (ret + 2, "sc.w r%i, r%i, (r%i) ", rd, rs2, rs1); |
| 225 | + else |
| 226 | + sprintf (ret + 2, "AMO_INVALID r%i, r%i, (r%i) ", rd, rs2, rs1); |
| 227 | + } else |
| 228 | + sprintf (ret, "? 0x%08x ?", insn); |
| 229 | + |
| 230 | + return ret; |
| 231 | +} |
| 232 | + |
| 233 | + |
| 234 | +// **************** Performance measuring ***************** |
| 235 | + |
| 236 | + |
| 237 | +void CPerfMon::Init (int events, CEventDef *ev_tab) { |
| 238 | + events_ = events; |
| 239 | + ev_tab_ = ev_tab; |
| 240 | + count_tab_ = new int[events]; |
| 241 | + time_tab_ = new double[events]; |
| 242 | + min_tab_ = new double[events]; |
| 243 | + max_tab_ = new double[events]; |
| 244 | + Reset (); |
| 245 | +} |
| 246 | + |
| 247 | + |
| 248 | +void CPerfMon::Done () { |
| 249 | + if (events_ > 0) { |
| 250 | + delete[] count_tab_; |
| 251 | + delete[] time_tab_; |
| 252 | + delete[] min_tab_; |
| 253 | + delete[] max_tab_; |
| 254 | + } |
| 255 | +} |
| 256 | + |
| 257 | + |
| 258 | +void CPerfMon::Reset () { |
| 259 | + last_no_ = -1; |
| 260 | + for (int n = 0; n < events_; n++) { |
| 261 | + count_tab_[n] = 0; |
| 262 | + time_tab_[n] = max_tab_[n] = 0.0; |
| 263 | + min_tab_[n] = DBL_MAX; |
| 264 | + } |
| 265 | +} |
| 266 | + |
| 267 | + |
| 268 | +void CPerfMon::Count (int ev_no) { |
| 269 | + double curStamp = sc_time_stamp ().to_double (); |
| 270 | + |
| 271 | + if (last_no_ >= 0) { |
| 272 | + if (ev_tab_[last_no_].is_timed) { |
| 273 | + double t = curStamp - last_stamp_; |
| 274 | + if (t == 0) // Time of 0 between two events is not plausible |
| 275 | + return; |
| 276 | + time_tab_[last_no_] += t; |
| 277 | + if (t < min_tab_[last_no_]) |
| 278 | + min_tab_[last_no_] = t; |
| 279 | + if (t > max_tab_[last_no_]) |
| 280 | + max_tab_[last_no_] = t; |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + count_tab_[ev_no]++; |
| 285 | + if (ev_tab_[ev_no].is_timed) last_stamp_ = curStamp; |
| 286 | + last_no_ = ev_no; |
| 287 | +} |
| 288 | + |
| 289 | + |
| 290 | +static void DisplayLine (const char *name, int count, int avg_count, double total, double min, double max, bool is_timed) { |
| 291 | + if (avg_count > 0 && is_timed) |
| 292 | + fprintf (stderr, "(PERF): %-10s %7i %8.1lf %8.1lf %8.1lf %11.1lf\n", name, count, min, |
| 293 | + total / avg_count, max, total); |
| 294 | + else |
| 295 | + fprintf (stderr, "(PERF): %-10s %7i\n", name, count); |
| 296 | +} |
| 297 | + |
| 298 | + |
| 299 | +void CPerfMon::Display (const char *name) { |
| 300 | + double time_total, min_total, max_total; |
| 301 | + int count_total, avg_count_total; |
| 302 | + |
| 303 | + fprintf (stderr, "(PERF):\n" |
| 304 | + "(PERF): ********** Performance statics "); |
| 305 | + if (name) fprintf (stderr, "of unit '%s'", name); |
| 306 | + fprintf (stderr, "\n" |
| 307 | + "(PERF):\n" |
| 308 | + "(PERF): Time [ns]\n" |
| 309 | + "(PERF): Event Count min avg max Total\n" |
| 310 | + "(PERF): -----------------------------------------------------------\n"); |
| 311 | + count_total = avg_count_total = 0; |
| 312 | + time_total = max_total = 0.0; |
| 313 | + min_total = DBL_MAX; |
| 314 | + for (int n = 0; n < events_; n++) { |
| 315 | + DisplayLine (ev_tab_[n].name, count_tab_[n], count_tab_[n], time_tab_[n], min_tab_[n], |
| 316 | + max_tab_[n], ev_tab_[n].is_timed); |
| 317 | + count_total += count_tab_[n]; |
| 318 | + if (ev_tab_[n].is_timed) avg_count_total += count_tab_[n]; |
| 319 | + time_total += time_tab_[n]; |
| 320 | + if (min_tab_[n] < min_total) min_total = min_tab_[n]; |
| 321 | + if (max_tab_[n] > max_total) max_total = max_tab_[n]; |
| 322 | + } |
| 323 | + fprintf (stderr, "(PERF): -----------------------------------------------------------\n"); |
| 324 | + DisplayLine ("Total", count_total, avg_count_total, time_total, min_total, max_total, true); |
| 325 | + fprintf (stderr, "(PERF):\n"); |
| 326 | +} |
| 327 | + |
| 328 | + |
| 329 | +// ***** CPerfMonCPU ***** |
| 330 | + |
| 331 | + |
| 332 | +void CPerfMonCPU::Init () { |
| 333 | + static CEventDef CPU_events[] = { |
| 334 | + { "ALU", true }, { "Load", true }, { "Store", true }, { "Jump", true }, { "Other", true }, { "IFetch", true } |
| 335 | + }; |
| 336 | + CPerfMon::Init (6, CPU_events); |
| 337 | +} |
0 commit comments