1/* 2 * Copyright 2018 Google, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer; 8 * redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution; 11 * neither the name of the copyright holders nor the names of its 12 * contributors may be used to endorse or promote products derived from 13 * this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * Authors: Gabe Black 28 */ 29 30#include "systemc/utils/vcd.hh" 31 32#include <ctime> 33#include <iomanip> 34 35#include "base/bitfield.hh" 36#include "base/cprintf.hh" 37#include "systemc/core/scheduler.hh" 38#include "systemc/ext/core/sc_event.hh" 39#include "systemc/ext/core/sc_main.hh" 40#include "systemc/ext/core/sc_time.hh" 41#include "systemc/ext/dt/bit/sc_bv_base.hh" 42#include "systemc/ext/dt/bit/sc_logic.hh" 43#include "systemc/ext/dt/bit/sc_lv_base.hh" 44#include "systemc/ext/dt/fx/sc_fxnum.hh" 45#include "systemc/ext/dt/fx/sc_fxval.hh" 46#include "systemc/ext/dt/int/sc_int_base.hh" 47#include "systemc/ext/dt/int/sc_signed.hh" 48#include "systemc/ext/dt/int/sc_uint_base.hh" 49#include "systemc/ext/dt/int/sc_unsigned.hh" 50#include "systemc/ext/utils/functions.hh" 51 52namespace sc_gem5 53{ 54 55namespace 56{ 57 58std::string 59cleanName(std::string name) 60{ 61 for (int i = 0; i < name.length(); i++) { 62 if (name[i] == '[') 63 name[i] = '('; 64 else if (name[i] == ']') 65 name[i] = ')'; 66 } 67 return name; 68} 69 70} // anonymous namespace 71 72class VcdTraceValBase : public TraceValBase 73{ 74 protected: 75 std::string _vcdName; 76 77 const char * 78 stripLeadingBits(const char *orig) 79 { 80 const char first = orig[0]; 81 82 if (first != 'z' && first != 'x' && first != '0') 83 return orig; 84 85 const char *res = orig; 86 while (*++res == first) {} 87 88 if (first != '0' || *res != '1') 89 res--; 90 91 return res; 92 } 93 94 char 95 scLogicToVcdState(char in) 96 { 97 switch (in) { 98 case 'U': 99 case 'X': 100 case 'W': 101 case 'D': 102 return 'x'; 103 case '0': 104 case 'L': 105 return '0'; 106 case '1': 107 case 'H': 108 return '1'; 109 case 'Z': 110 return 'z'; 111 default: 112 return '?'; 113 } 114 } 115 116 void 117 printVal(std::ostream &os, const std::string &rep) 118 { 119 switch (width()) { 120 case 0: 121 return; 122 case 1: 123 os << rep << vcdName() << std::endl;; 124 return; 125 default: 126 os << "b" << stripLeadingBits(rep.c_str()) << " " << 127 vcdName() << std::endl; 128 return; 129 } 130 } 131 132 public: 133 VcdTraceValBase(int width) : TraceValBase(width) {} 134 ~VcdTraceValBase() {} 135 136 void vcdName(const std::string &vcd_name) { _vcdName = vcd_name; } 137 const std::string &vcdName() { return _vcdName; } 138 virtual std::string vcdType() { return "wire"; } 139 140 virtual void output(std::ostream &os) = 0; 141}; 142 143void 144VcdTraceScope::addValue(const std::string &name, VcdTraceValBase *value) 145{ 146 size_t pos = name.find_first_of('.'); 147 if (pos == std::string::npos) { 148 values.emplace_back(name, value); 149 } else { 150 std::string sname = name.substr(0, pos); 151 auto it = scopes.find(sname); 152 if (it == scopes.end()) 153 it = scopes.emplace(sname, new VcdTraceScope).first; 154 it->second->addValue(name.substr(pos + 1), value); 155 } 156} 157 158void 159VcdTraceScope::output(const std::string &name, std::ostream &os) 160{ 161 os << "$scope module " << name << " $end" << std::endl; 162 163 for (auto &p: values) { 164 const std::string &name = p.first; 165 VcdTraceValBase *value = p.second; 166 167 int w = value->width(); 168 if (w <= 0) { 169 std::string msg = csprintf("'%s' has 0 bits", name); 170 // The typo in this error message is intentional to match the 171 // Accellera output. 172 SC_REPORT_ERROR("(E710) object cannot not be traced", msg.c_str()); 173 return; 174 } 175 176 std::string clean_name = cleanName(name); 177 if (w == 1) { 178 ccprintf(os, "$var %s % 3d %s %s $end\n", 179 value->vcdType(), w, value->vcdName(), clean_name); 180 } else { 181 ccprintf(os, "$var %s % 3d %s %s [%d:0] $end\n", 182 value->vcdType(), w, value->vcdName(), clean_name, w - 1); 183 } 184 } 185 186 for (auto &p: scopes) 187 p.second->output(p.first, os); 188 189 os << "$upscope $end" << std::endl; 190} 191 192template <typename T> 193class VcdTraceVal : public TraceVal<T, VcdTraceValBase> 194{ 195 public: 196 typedef T TracedType; 197 198 VcdTraceVal(const T* t, const std::string &vcd_name, int width) : 199 TraceVal<T, VcdTraceValBase>(t, width) 200 { 201 this->vcdName(vcd_name); 202 } 203}; 204 205std::string 206VcdTraceFile::nextSignalName() 207{ 208 std::string name(_nextName); 209 210 bool carry = false; 211 int pos = NextNameChars - 1; 212 do { 213 carry = (_nextName[pos] == 'z'); 214 if (carry) 215 _nextName[pos--] = 'a'; 216 else 217 _nextName[pos--]++; 218 } while (carry && pos >= 0); 219 220 return name; 221} 222 223void 224VcdTraceFile::initialize() 225{ 226 finalizeTime(); 227 228 // Date. 229 stream() << "$date" << std::endl; 230 time_t long_time; 231 time(&long_time); 232 struct tm *p_tm = localtime(&long_time); 233 stream() << std::put_time(p_tm, " %b %d, %Y %H:%M:%S\n"); 234 stream() << "$end" << std::endl << std::endl; 235 236 // Version. 237 stream() << "$version" << std::endl; 238 stream() << " " << ::sc_core::sc_version() << std::endl; 239 stream() << "$end" << std::endl << std::endl; 240 241 // Timescale. 242 stream() << "$timescale" << std::endl; 243 stream() << " " << ::sc_core::sc_time::from_value(timeUnitTicks) << 244 std::endl; 245 stream() << "$end" << std::endl << std::endl; 246 247 for (auto tv: traceVals) 248 tv->finalize(); 249 250 topScope.output("SystemC", stream()); 251 252 stream() << "$enddefinitions $end" << std::endl << std::endl; 253 254 Tick now = scheduler.getCurTick(); 255 256 std::string timedump_comment = 257 csprintf("All initial values are dumped below at time " 258 "%g sec = %g timescale units.", 259 static_cast<double>(now) / SimClock::Float::s, 260 static_cast<double>(now / timeUnitTicks)); 261 writeComment(timedump_comment); 262 263 lastPrintedTime = now / timeUnitTicks; 264 265 stream() << "$dumpvars" << std::endl; 266 for (auto tv: traceVals) 267 tv->output(stream()); 268 stream() << "$end" << std::endl << std::endl; 269 270 initialized = true; 271} 272 273VcdTraceFile::~VcdTraceFile() 274{ 275 for (auto tv: traceVals) 276 delete tv; 277 traceVals.clear(); 278 279 if (timeUnitTicks) 280 ccprintf(stream(), "#%u\n", scheduler.getCurTick() / timeUnitTicks); 281} 282 283void 284VcdTraceFile::trace(bool delta) 285{ 286 if (!delta) 287 deltasAtNow = 0; 288 289 uint64_t deltaOffset = deltasAtNow; 290 291 if (delta) 292 deltaOffset = deltasAtNow++; 293 294 if (_traceDeltas != delta) 295 return; 296 297 if (!initialized) { 298 initialize(); 299 return; 300 } 301 302 Tick now = scheduler.getCurTick() / timeUnitTicks + deltaOffset; 303 304 if (now <= lastPrintedTime) { 305 // TODO warn about reversed time? 306 return; 307 } 308 309 bool time_printed = false; 310 for (auto tv: traceVals) { 311 if (tv->check()) { 312 if (!time_printed) { 313 lastPrintedTime = now; 314 ccprintf(stream(), "#%u\n", now); 315 time_printed = true; 316 } 317 318 tv->output(stream()); 319 } 320 } 321 if (time_printed) 322 stream() << std::endl; 323} 324 325class VcdTraceValBool : public VcdTraceVal<bool> 326{ 327 public: 328 using VcdTraceVal<bool>::VcdTraceVal; 329 330 void 331 output(std::ostream &os) override 332 { 333 printVal(os, this->value() ? "1" : "0"); 334 } 335}; 336 337void 338VcdTraceFile::addTraceVal(const bool *v, const std::string &name) 339{ 340 addNewTraceVal<VcdTraceValBool>(v, name); 341} 342 343template <typename T> 344class VcdTraceValFloat : public VcdTraceVal<T> 345{ 346 public: 347 using VcdTraceVal<T>::VcdTraceVal; 348 349 std::string vcdType() override { return "real"; } 350 351 void 352 output(std::ostream &os) override 353 { 354 ccprintf(os, "r%.16g %s\n", this->value(), this->vcdName()); 355 } 356}; 357 358void 359VcdTraceFile::addTraceVal(const float *v, const std::string &name) 360{ 361 addNewTraceVal<VcdTraceValFloat<float>>(v, name); 362} 363void 364VcdTraceFile::addTraceVal(const double *v, const std::string &name) 365{ 366 addNewTraceVal<VcdTraceValFloat<double>>(v, name); 367} 368 369class VcdTraceValScLogic : public VcdTraceVal<sc_dt::sc_logic> 370{ 371 public: 372 using VcdTraceVal<sc_dt::sc_logic>::VcdTraceVal; 373 374 void 375 output(std::ostream &os) override 376 { 377 char str[2] = { 378 scLogicToVcdState(value().to_char()), 379 '\0' 380 }; 381 printVal(os, str); 382 } 383}; 384 385void 386VcdTraceFile::addTraceVal(const sc_dt::sc_logic *v, const std::string &name) 387{ 388 addNewTraceVal<VcdTraceValScLogic>(v, name); 389} 390 391template <typename T> 392class VcdTraceValFinite : public VcdTraceVal<T> 393{ 394 public: 395 using VcdTraceVal<T>::VcdTraceVal; 396 397 void 398 finalize() override 399 { 400 VcdTraceVal<T>::finalize(); 401 this->_width = this->value().length(); 402 } 403 404 void 405 output(std::ostream &os) override 406 { 407 std::string str; 408 const int w = this->width(); 409 410 str.reserve(w); 411 for (int i = w - 1; i >= 0; i--) 412 str += this->value()[i].to_bool() ? '1' : '0'; 413 414 this->printVal(os, str); 415 } 416}; 417 418void 419VcdTraceFile::addTraceVal(const sc_dt::sc_int_base *v, 420 const std::string &name) 421{ 422 addNewTraceVal<VcdTraceValFinite<sc_dt::sc_int_base>>(v, name); 423} 424void 425VcdTraceFile::addTraceVal(const sc_dt::sc_uint_base *v, 426 const std::string &name) 427{ 428 addNewTraceVal<VcdTraceValFinite<sc_dt::sc_uint_base>>(v, name); 429} 430 431void 432VcdTraceFile::addTraceVal(const sc_dt::sc_signed *v, const std::string &name) 433{ 434 addNewTraceVal<VcdTraceValFinite<sc_dt::sc_signed>>(v, name); 435} 436void 437VcdTraceFile::addTraceVal(const sc_dt::sc_unsigned *v, 438 const std::string &name) 439{ 440 addNewTraceVal<VcdTraceValFinite<sc_dt::sc_unsigned>>(v, name); 441} 442 443template <typename T> 444class VcdTraceValLogic : public VcdTraceVal<T> 445{ 446 public: 447 using VcdTraceVal<T>::VcdTraceVal; 448 449 void 450 finalize() override 451 { 452 VcdTraceVal<T>::finalize(); 453 this->_width = this->value().length(); 454 } 455 456 void 457 output(std::ostream &os) override 458 { 459 this->printVal(os, this->value().to_string()); 460 } 461}; 462 463void 464VcdTraceFile::addTraceVal(const sc_dt::sc_bv_base *v, const std::string &name) 465{ 466 addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_bv_base>>(v, name); 467} 468void 469VcdTraceFile::addTraceVal(const sc_dt::sc_lv_base *v, const std::string &name) 470{ 471 addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_lv_base>>(v, name); 472} 473 474template <typename T> 475class VcdTraceValFxval : public VcdTraceVal<T> 476{ 477 public: 478 using VcdTraceVal<T>::VcdTraceVal; 479 480 std::string vcdType() override { return "real"; } 481 482 void 483 output(std::ostream &os) override 484 { 485 ccprintf(os, "r%.16g %s\n", 486 this->value().to_double(), this->vcdName()); 487 } 488}; 489 490void 491VcdTraceFile::addTraceVal(const sc_dt::sc_fxval *v, const std::string &name) 492{ 493 addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval>>(v, name); 494} 495void 496VcdTraceFile::addTraceVal(const sc_dt::sc_fxval_fast *v, 497 const std::string &name) 498{ 499 addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval_fast>>(v, name); 500} 501 502template <typename T> 503class VcdTraceValFxnum : public VcdTraceVal<T> 504{ 505 public: 506 using VcdTraceVal<T>::VcdTraceVal; 507 508 void 509 output(std::ostream &os) override 510 { 511 std::string str; 512 const int w = this->width(); 513 514 str.reserve(w); 515 for (int i = w - 1; i >= 0; i--) 516 str += this->value()[i] ? '1' : '0'; 517 518 this->printVal(os, str); 519 } 520}; 521 522void 523VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum *v, const std::string &name) 524{ 525 addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum>>(v, name); 526} 527void 528VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum_fast *v, 529 const std::string &name) 530{ 531 addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum_fast>>(v, name); 532} 533 534class VcdTraceValEvent : public VcdTraceVal<::sc_core::sc_event> 535{ 536 public: 537 using VcdTraceVal<::sc_core::sc_event>::VcdTraceVal; 538 539 std::string vcdType() override { return "event"; } 540 541 void 542 output(std::ostream &os) override 543 { 544 if (value()) 545 printVal(os, "1"); 546 else 547 os << std::endl; 548 } 549}; 550 551void 552VcdTraceFile::addTraceVal(const sc_core::sc_event *v, const std::string &name) 553{ 554 addNewTraceVal<VcdTraceValEvent>(v, name); 555} 556 557class VcdTraceValTime : public VcdTraceVal<::sc_core::sc_time> 558{ 559 private: 560 static const int TimeWidth = 64; 561 562 public: 563 using VcdTraceVal<::sc_core::sc_time>::VcdTraceVal; 564 565 std::string vcdType() override { return "time"; } 566 567 void 568 finalize() override 569 { 570 VcdTraceVal<::sc_core::sc_time>::finalize(); 571 _width = TimeWidth; 572 } 573 574 void 575 output(std::ostream &os) override 576 { 577 char str[TimeWidth + 1]; 578 str[TimeWidth] = '\0'; 579 580 const uint64_t val = value().value(); 581 for (int i = 0; i < TimeWidth; i++) 582 str[i] = ::bits(val, TimeWidth - i - 1) ? '1' : '0'; 583 584 printVal(os, str); 585 } 586}; 587void 588VcdTraceFile::addTraceVal(const sc_core::sc_time *v, const std::string &name) 589{ 590 addNewTraceVal<VcdTraceValTime>(v, name); 591} 592 593template <typename T> 594class VcdTraceValInt : public VcdTraceVal<T> 595{ 596 public: 597 using VcdTraceVal<T>::VcdTraceVal; 598 599 void 600 output(std::ostream &os) override 601 { 602 const int w = this->width(); 603 char str[w + 1]; 604 str[w] = '\0'; 605 606 const uint64_t val = 607 static_cast<uint64_t>(this->value()) & ::mask(sizeof(T) * 8); 608 609 if (::mask(w) < val) { 610 for (int i = 0; i < w; i++) 611 str[i] = 'x'; 612 } else { 613 for (int i = 0; i < w; i++) 614 str[i] = ::bits(val, w - i - 1) ? '1' : '0'; 615 } 616 617 this->printVal(os, str); 618 } 619}; 620 621void 622VcdTraceFile::addTraceVal(const unsigned char *v, const std::string &name, 623 int width) 624{ 625 addNewTraceVal<VcdTraceValInt<unsigned char>>(v, name, width); 626} 627void 628VcdTraceFile::addTraceVal(const char *v, const std::string &name, int width) 629{ 630 addNewTraceVal<VcdTraceValInt<char>>(v, name, width); 631} 632void 633VcdTraceFile::addTraceVal(const unsigned short *v, const std::string &name, 634 int width) 635{ 636 addNewTraceVal<VcdTraceValInt<unsigned short>>(v, name, width); 637} 638void 639VcdTraceFile::addTraceVal(const short *v, const std::string &name, int width) 640{ 641 addNewTraceVal<VcdTraceValInt<short>>(v, name, width); 642} 643void 644VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name, 645 int width) 646{ 647 addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, width); 648} 649void 650VcdTraceFile::addTraceVal(const int *v, const std::string &name, int width) 651{ 652 addNewTraceVal<VcdTraceValInt<int>>(v, name, width); 653} 654void 655VcdTraceFile::addTraceVal(const unsigned long *v, const std::string &name, 656 int width) 657{ 658 addNewTraceVal<VcdTraceValInt<unsigned long>>(v, name, width); 659} 660void 661VcdTraceFile::addTraceVal(const long *v, const std::string &name, int width) 662{ 663 addNewTraceVal<VcdTraceValInt<long>>(v, name, width); 664} 665 666void 667VcdTraceFile::addTraceVal(const sc_dt::int64 *v, const std::string &name, 668 int width) 669{ 670 addNewTraceVal<VcdTraceValInt<sc_dt::int64>>(v, name, width); 671} 672void 673VcdTraceFile::addTraceVal(const sc_dt::uint64 *v, const std::string &name, 674 int width) 675{ 676 addNewTraceVal<VcdTraceValInt<sc_dt::uint64>>(v, name, width); 677} 678 679void 680VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name, 681 const char **literals) 682{ 683 uint64_t count = 0; 684 while (*literals++) 685 count++; 686 687 int bits = 0; 688 while (count >> bits) 689 bits++; 690 691 addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, bits); 692} 693 694void 695VcdTraceFile::writeComment(const std::string &comment) 696{ 697 stream() << "$comment" << std::endl; 698 stream() << comment << std::endl; 699 stream() << "$end" << std::endl << std::endl; 700} 701 702} // namespace sc_gem5 703