2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2021-03-20 22:46:00 +01:00
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
2026-01-27 02:24:34 +01:00
|
|
|
// This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
// under the terms of either the GNU Lesser General Public License Version 3
|
|
|
|
|
// or the Perl Artistic License Version 2.0.
|
|
|
|
|
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//=========================================================================
|
|
|
|
|
///
|
|
|
|
|
/// \file
|
2021-03-20 22:46:00 +01:00
|
|
|
/// \brief Verilated general routine implementation code
|
2006-08-26 13:35:28 +02:00
|
|
|
///
|
2021-03-20 22:46:00 +01:00
|
|
|
/// This file must be compiled and linked against all Verilated objects
|
|
|
|
|
/// (all code created from Verilator).
|
2006-08-26 13:35:28 +02:00
|
|
|
///
|
2021-03-20 22:46:00 +01:00
|
|
|
/// Verilator always adds this file to the Makefile for the linker.
|
2021-03-04 04:53:50 +01:00
|
|
|
///
|
2021-03-20 22:46:00 +01:00
|
|
|
/// Those macro/function/variable starting or ending in _ are internal,
|
|
|
|
|
/// however many of the other function/macros here are also internal.
|
2006-08-26 13:35:28 +02:00
|
|
|
///
|
|
|
|
|
//=========================================================================
|
2021-03-05 01:23:40 +01:00
|
|
|
// Internal note:
|
|
|
|
|
//
|
2021-11-14 15:39:31 +01:00
|
|
|
// verilated.o may exist both in --lib-create (incrementally linked .a/.so)
|
2021-03-05 01:23:40 +01:00
|
|
|
// and the main module. Both refer the same instance of static
|
2022-11-05 13:47:34 +01:00
|
|
|
// variables/thread_local in verilated.o such as Verilated, or
|
2021-03-05 01:23:40 +01:00
|
|
|
// VerilatedImpData. This is important to share that state, but the
|
|
|
|
|
// sharing may cause a double-free error when shutting down because the
|
|
|
|
|
// loader will insert a constructor/destructor at each reference to
|
|
|
|
|
// verilated.o, resulting in at runtime constructors/destructors being
|
|
|
|
|
// called multiple times.
|
|
|
|
|
//
|
|
|
|
|
// To avoid the trouble:
|
|
|
|
|
// * Statics declared inside functions. The compiler will wrap
|
|
|
|
|
// the construction in must-be-one-time checks.
|
|
|
|
|
// * Or, use only C++20 constinit types. (TODO: Make a VL_CONSTINIT).
|
|
|
|
|
// * Or, use types that are multi-constructor safe.
|
|
|
|
|
// * Or, the static should be of a union, which will avoid compiler
|
|
|
|
|
// construction, and appropriately check for duplicate construction.
|
|
|
|
|
// * Or, code is not linked in protected library. e.g. the VPI
|
|
|
|
|
// and DPI libraries are not needed there.
|
|
|
|
|
//=========================================================================
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2021-03-04 03:57:07 +01:00
|
|
|
#define VERILATOR_VERILATED_CPP_
|
2018-10-14 17:21:09 +02:00
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "verilated_config.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
#include "verilated.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "verilated_imp.h"
|
2026-03-08 20:53:32 +01:00
|
|
|
#include "verilated_sym_props.h"
|
2018-10-14 17:21:09 +02:00
|
|
|
|
2019-12-10 01:17:52 +01:00
|
|
|
#include <algorithm>
|
2008-06-30 19:11:25 +02:00
|
|
|
#include <cctype>
|
2019-12-10 01:17:52 +01:00
|
|
|
#include <cerrno>
|
2026-05-05 18:10:51 +02:00
|
|
|
#include <chrono>
|
2022-03-26 23:47:10 +01:00
|
|
|
#include <cstdlib>
|
2026-05-05 18:10:51 +02:00
|
|
|
#include <ctime>
|
2026-05-27 20:33:19 +02:00
|
|
|
#include <fcntl.h>
|
2025-03-11 18:32:34 +01:00
|
|
|
#include <iostream>
|
2021-01-11 17:23:54 +01:00
|
|
|
#include <limits>
|
2022-08-05 11:56:57 +02:00
|
|
|
#include <list>
|
2026-03-08 20:53:32 +01:00
|
|
|
#include <memory>
|
2022-08-05 11:56:57 +02:00
|
|
|
#include <sstream>
|
2020-06-12 08:15:42 +02:00
|
|
|
#include <utility>
|
2018-08-28 00:07:52 +02:00
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include <sys/stat.h> // mkdir
|
|
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
// clang-format off
|
2019-03-05 02:29:01 +01:00
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
2018-08-28 00:07:52 +02:00
|
|
|
# include <direct.h> // mkdir
|
2026-05-27 20:33:19 +02:00
|
|
|
# include <io.h> // open, read, write, close
|
|
|
|
|
# define STDOUT_FILENO _fileno(stdout)
|
|
|
|
|
# define STDERR_FILENO _fileno(stderr)
|
2018-08-28 00:07:52 +02:00
|
|
|
#endif
|
2023-01-05 21:44:27 +01:00
|
|
|
#ifdef __GLIBC__
|
2026-02-03 01:01:24 +01:00
|
|
|
# include <cxxabi.h>
|
2022-11-18 01:12:54 +01:00
|
|
|
# include <execinfo.h>
|
|
|
|
|
# define _VL_HAVE_STACKTRACE
|
|
|
|
|
#endif
|
2025-08-23 16:49:03 +02:00
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
2024-01-06 22:14:58 +01:00
|
|
|
# include <sys/time.h>
|
|
|
|
|
# include <sys/resource.h>
|
2026-05-05 18:10:51 +02:00
|
|
|
# include <unistd.h>
|
2024-01-06 22:14:58 +01:00
|
|
|
# define _VL_HAVE_GETRLIMIT
|
|
|
|
|
#endif
|
2026-06-28 15:28:09 +02:00
|
|
|
#if VM_VPI
|
|
|
|
|
# include <cstring>
|
|
|
|
|
# ifndef _WIN32
|
|
|
|
|
# include <dlfcn.h> // dlopen
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
2022-07-12 12:41:15 +02:00
|
|
|
|
2022-11-05 13:47:34 +01:00
|
|
|
#include "verilated_threads.h"
|
2020-04-14 04:51:35 +02:00
|
|
|
// clang-format on
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-07-20 12:27:10 +02:00
|
|
|
#include "verilated_trace.h"
|
|
|
|
|
|
2024-05-17 16:38:34 +02:00
|
|
|
#ifdef VM_SOLVER_DEFAULT
|
|
|
|
|
#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
|
|
|
|
|
#else
|
|
|
|
|
#define VL_SOLVER_DEFAULT "z3 --in"
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-01-26 00:26:14 +01:00
|
|
|
//===========================================================================
|
2021-02-24 01:02:04 +01:00
|
|
|
// Static sanity checks
|
2019-01-26 00:26:14 +01:00
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
static_assert(sizeof(uint8_t) == 1, "uint8_t is missized");
|
|
|
|
|
static_assert(sizeof(uint16_t) == 2, "uint8_t is missized");
|
|
|
|
|
static_assert(sizeof(uint32_t) == 4, "uint8_t is missized");
|
|
|
|
|
static_assert(sizeof(uint64_t) == 8, "uint8_t is missized");
|
2019-01-26 00:26:14 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// Global variables
|
2021-03-07 17:01:54 +01:00
|
|
|
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
// Fast path, keep together
|
|
|
|
|
int Verilated::s_debug = 0;
|
|
|
|
|
VerilatedContext* Verilated::s_lastContextp = nullptr;
|
2010-03-13 02:00:08 +01:00
|
|
|
|
|
|
|
|
// Keep below together in one cache line
|
2021-03-07 17:01:54 +01:00
|
|
|
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
2022-11-05 13:47:34 +01:00
|
|
|
thread_local Verilated::ThreadLocal Verilated::t_s;
|
2017-10-27 03:51:51 +02:00
|
|
|
|
2026-01-08 07:01:54 +01:00
|
|
|
//===========================================================================
|
|
|
|
|
// Warning print helper
|
|
|
|
|
|
|
|
|
|
void vl_print_warn_error(const char* prefix, const char* filename, int linenum,
|
|
|
|
|
const char* msg) VL_MT_UNSAFE {
|
|
|
|
|
// A msg of "ERRORCODE: ..." is a code that changes to a prefix, e.g. "%Error-ERRORCODE: ..."
|
|
|
|
|
// This avoids changing public API of the vl_stop and related functions.
|
|
|
|
|
const char* msgNoCp = msg;
|
|
|
|
|
for (; isupper(*msgNoCp); ++msgNoCp);
|
|
|
|
|
if (msgNoCp[0] == ':' && msgNoCp[1] == ' ') {
|
|
|
|
|
const int codeWidth = static_cast<int>(msgNoCp - msg);
|
|
|
|
|
msgNoCp += 2;
|
|
|
|
|
if (filename && filename[0]) {
|
|
|
|
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
|
|
|
|
"%s-%.*s: %s:%d: %s\n", prefix, codeWidth, msg, filename, linenum, msgNoCp);
|
|
|
|
|
} else {
|
|
|
|
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
|
|
|
|
"%s-%.*s: %s\n", prefix, codeWidth, msg, msgNoCp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (filename && filename[0]) {
|
|
|
|
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
|
|
|
|
"%s: %s:%d: %s\n", prefix, filename, linenum, msg);
|
|
|
|
|
} else {
|
|
|
|
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
|
|
|
|
"%s: %s\n", prefix, msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// User definable functions
|
2019-11-17 00:25:47 +01:00
|
|
|
// Note a TODO is a future version of the API will pass a structure so that
|
|
|
|
|
// the calling arguments allow for extension
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
#ifndef VL_USER_FINISH ///< Define this to override the vl_finish function
|
2018-08-25 15:52:45 +02:00
|
|
|
void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
2026-01-08 07:01:54 +01:00
|
|
|
(void)hier; // hier is unused in the default implementation.
|
2019-05-15 04:49:21 +02:00
|
|
|
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
|
|
|
|
"- %s:%d: Verilog $finish\n", filename, linenum);
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->gotFinish(true);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
#ifndef VL_USER_STOP ///< Define this to override the vl_stop function
|
2018-08-25 15:52:45 +02:00
|
|
|
void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
2024-09-14 02:45:44 +02:00
|
|
|
// $stop or $fatal reporting; would break current API to add param as to which
|
2026-03-12 18:09:54 +01:00
|
|
|
if (Verilated::threadContextp()->gotFinish()
|
|
|
|
|
&& !Verilated::threadContextp()->executingFinal()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
const char* const msg = "Verilog $stop";
|
|
|
|
|
Verilated::threadContextp()->gotError(true);
|
|
|
|
|
Verilated::threadContextp()->gotFinish(true);
|
|
|
|
|
if (Verilated::threadContextp()->fatalOnError()) {
|
|
|
|
|
vl_fatal(filename, linenum, hier, msg);
|
|
|
|
|
} else {
|
2026-01-08 07:01:54 +01:00
|
|
|
vl_print_warn_error("%Error", filename, linenum, msg);
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::runFlushCallbacks();
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
#ifndef VL_USER_FATAL ///< Define this to override the vl_fatal function
|
2018-08-25 15:52:45 +02:00
|
|
|
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
|
2026-01-08 07:01:54 +01:00
|
|
|
(void)hier; // hier is unused in the default implementation.
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->gotError(true);
|
|
|
|
|
Verilated::threadContextp()->gotFinish(true);
|
2026-01-08 07:01:54 +01:00
|
|
|
vl_print_warn_error("%Error", filename, linenum, msg);
|
2020-06-12 08:15:42 +02:00
|
|
|
Verilated::runFlushCallbacks();
|
2014-09-06 03:40:04 +02:00
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread
|
2020-06-12 08:15:42 +02:00
|
|
|
|
|
|
|
|
// Second flush in case VL_PRINTF does something needing a flush
|
|
|
|
|
Verilated::runFlushCallbacks();
|
|
|
|
|
|
|
|
|
|
// Callbacks prior to termination
|
|
|
|
|
Verilated::runExitCallbacks();
|
2025-08-06 00:43:29 +02:00
|
|
|
|
|
|
|
|
if (Verilated::debug()) {
|
|
|
|
|
std::abort();
|
|
|
|
|
} else {
|
|
|
|
|
std::exit(1);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
#ifndef VL_USER_STOP_MAYBE ///< Define this to override the vl_stop_maybe function
|
2019-11-17 00:25:47 +01:00
|
|
|
void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE {
|
2024-09-14 02:45:44 +02:00
|
|
|
// $stop or $fatal
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->errorCountInc();
|
|
|
|
|
if (maybe
|
|
|
|
|
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
|
2024-03-23 00:35:42 +01:00
|
|
|
// Do just once when cross error limit
|
|
|
|
|
if (Verilated::threadContextp()->errorCount() == 1) {
|
2026-01-08 07:01:54 +01:00
|
|
|
vl_print_warn_error("-Info", filename, linenum,
|
|
|
|
|
"Verilog $stop, ignored due to +verilator+error+limit");
|
2024-03-23 00:35:42 +01:00
|
|
|
}
|
2019-11-17 14:12:39 +01:00
|
|
|
} else {
|
2019-11-17 00:25:47 +01:00
|
|
|
vl_stop(filename, linenum, hier);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-17 14:12:39 +01:00
|
|
|
#endif
|
2019-11-17 00:25:47 +01:00
|
|
|
|
2021-12-22 01:55:04 +01:00
|
|
|
#ifndef VL_USER_WARN ///< Define this to override the vl_warn function
|
|
|
|
|
void vl_warn(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
|
2026-01-08 07:01:54 +01:00
|
|
|
(void)hier; // hier is unused in the default implementation.
|
|
|
|
|
vl_print_warn_error("%Warning", filename, linenum, msg);
|
2021-12-22 01:55:04 +01:00
|
|
|
Verilated::runFlushCallbacks();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-10-20 01:40:51 +02:00
|
|
|
//===========================================================================
|
2017-10-27 02:05:42 +02:00
|
|
|
// Wrapper to call certain functions via messages when multithreaded
|
2017-10-20 01:40:51 +02:00
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
const bool haveFilename = filename;
|
|
|
|
|
const bool haveHier = hier;
|
|
|
|
|
const std::string filenameStr{haveFilename ? filename : ""};
|
|
|
|
|
const std::string hierStr{haveHier ? hier : ""};
|
2021-07-24 14:36:11 +02:00
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
vl_finish(haveFilename ? filenameStr.c_str() : nullptr, linenum,
|
|
|
|
|
haveHier ? hierStr.c_str() : nullptr);
|
2021-07-24 14:36:11 +02:00
|
|
|
}});
|
2017-10-20 01:40:51 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-17 00:25:47 +01:00
|
|
|
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
const bool haveFilename = filename;
|
|
|
|
|
const bool haveHier = hier;
|
|
|
|
|
const std::string filenameStr{haveFilename ? filename : ""};
|
|
|
|
|
const std::string hierStr{haveHier ? hier : ""};
|
2021-07-24 14:36:11 +02:00
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
vl_stop_maybe(haveFilename ? filenameStr.c_str() : nullptr, linenum,
|
|
|
|
|
haveHier ? hierStr.c_str() : nullptr, maybe);
|
2021-07-24 14:36:11 +02:00
|
|
|
}});
|
2017-10-20 01:40:51 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
const bool haveFilename = filename;
|
|
|
|
|
const bool haveHier = hier;
|
|
|
|
|
const bool haveMsg = msg;
|
|
|
|
|
const std::string filenameStr{haveFilename ? filename : ""};
|
|
|
|
|
const std::string hierStr{haveHier ? hier : ""};
|
|
|
|
|
const std::string msgStr{haveMsg ? msg : ""};
|
2021-07-24 14:36:11 +02:00
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
vl_fatal(haveFilename ? filenameStr.c_str() : nullptr, linenum,
|
|
|
|
|
haveHier ? hierStr.c_str() : nullptr, haveMsg ? msgStr.c_str() : nullptr);
|
2021-07-24 14:36:11 +02:00
|
|
|
}});
|
2017-10-20 01:40:51 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 01:55:04 +01:00
|
|
|
void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
const bool haveFilename = filename;
|
|
|
|
|
const bool haveHier = hier;
|
|
|
|
|
const bool haveMsg = msg;
|
|
|
|
|
const std::string filenameStr{haveFilename ? filename : ""};
|
|
|
|
|
const std::string hierStr{haveHier ? hier : ""};
|
|
|
|
|
const std::string msgStr{haveMsg ? msg : ""};
|
2021-12-22 01:55:04 +01:00
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
Copy MT message string arguments
Context:
The multithreaded finish, stop, fatal, and warning wrappers deferred raw const char pointers into the evaluation message queue. Temporary caller strings could be destroyed before the main thread consumed the message, while nullable arguments also require preserving the distinction between nullptr and a non-null empty string.
Changes:
- Copy nullable filename, hierarchy, and message arguments before posting each deferred callback.
- Reconstruct the original pointer nullness when invoking the existing single-threaded handlers, including non-null empty strings.
- Add an AddressSanitizer regression with independent warning, finish, stop, and fatal modes covering temporary strings, empty strings, and null arguments.
- Check pointer nullness separately from copied string contents.
Evidence:
- Each wrapper mode fails against origin/master with a heap-use-after-free and passes with this change under AddressSanitizer and C++14.
- A temporary mutation that collapses empty filenames to null is rejected by all four modes with a filename-nullness mismatch.
- Seven adjacent multithreaded tests and focused distribution checks passed during candidate verification.
- clang-format 18.1.8 and git diff --check pass.
Boundary:
The change only owns string lifetime and nullable-string identity across the MT message queue. It does not alter finish, stop, fatal, or warning policy.
2026-07-13 11:27:46 +02:00
|
|
|
vl_warn(haveFilename ? filenameStr.c_str() : nullptr, linenum,
|
|
|
|
|
haveHier ? hierStr.c_str() : nullptr, haveMsg ? msgStr.c_str() : nullptr);
|
2021-12-22 01:55:04 +01:00
|
|
|
}});
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 15:28:09 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// Runtime VPI shared library loading (--vpi)
|
|
|
|
|
|
|
|
|
|
// Load one VPI shared library named by a +verilator+vpi+<lib>[:<bootstrap>] argument.
|
|
|
|
|
// 'arg' is the payload after the prefix: either "<lib>" (invoke the library's
|
|
|
|
|
// vlog_startup_routines array) or "<lib>:<bootstrap>" (invoke the named bootstrap).
|
|
|
|
|
void Verilated::loadVpiLib(const std::string& arg) VL_MT_UNSAFE {
|
|
|
|
|
#if VM_VPI
|
|
|
|
|
if (arg.empty()) return;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
VL_FATAL_MT("", 0, "",
|
|
|
|
|
"+verilator+vpi+: runtime VPI library loading is not supported on"
|
|
|
|
|
" Windows; link the VPI code into the model instead");
|
|
|
|
|
#else
|
|
|
|
|
using vlog_startup_t = void (*)();
|
|
|
|
|
// Split <lib>:<bootstrap> on the last ':'
|
|
|
|
|
const std::string::size_type colon_pos = arg.rfind(':');
|
|
|
|
|
const bool has_entry = (colon_pos != std::string::npos);
|
|
|
|
|
const std::string libpath = has_entry ? arg.substr(0, colon_pos) : arg;
|
|
|
|
|
const std::string entry_name = has_entry ? arg.substr(colon_pos + 1) : std::string{};
|
|
|
|
|
void* handle = dlopen(libpath.c_str(), RTLD_LAZY);
|
|
|
|
|
if (!handle)
|
|
|
|
|
// The library path is stable; the dlerror() text is platform-specific, so put it on
|
|
|
|
|
// a separate "- " line (test golden files strip "- " lines, keeping output portable).
|
|
|
|
|
VL_FATAL_MT(
|
|
|
|
|
"", 0, "",
|
|
|
|
|
(std::string{"Cannot load VPI library: "} + libpath + "\n- dlerror: " + dlerror())
|
|
|
|
|
.c_str());
|
|
|
|
|
if (has_entry) {
|
|
|
|
|
vlog_startup_t bsp = reinterpret_cast<vlog_startup_t>(dlsym(handle, entry_name.c_str()));
|
|
|
|
|
if (!bsp)
|
|
|
|
|
VL_FATAL_MT(
|
|
|
|
|
"", 0, "",
|
|
|
|
|
(std::string{"Cannot find VPI bootstrap '"} + entry_name + "' in: " + libpath)
|
|
|
|
|
.c_str());
|
|
|
|
|
bsp();
|
|
|
|
|
} else {
|
|
|
|
|
vlog_startup_t* routinesp
|
|
|
|
|
= reinterpret_cast<vlog_startup_t*>(dlsym(handle, "vlog_startup_routines"));
|
|
|
|
|
if (!routinesp)
|
|
|
|
|
VL_FATAL_MT(
|
|
|
|
|
"", 0, "",
|
|
|
|
|
(std::string{"Cannot find 'vlog_startup_routines' in: "} + libpath).c_str());
|
|
|
|
|
for (int j = 0; routinesp[j]; ++j) routinesp[j]();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
// Never reached: the command-line handler only calls this when compiled with --vpi.
|
|
|
|
|
(void)arg;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-25 04:56:58 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// Debug prints
|
|
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
// sprintf but return as string (this isn't fast, for print messages only)
|
2017-10-27 02:05:42 +02:00
|
|
|
std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
2017-10-26 05:01:45 +02:00
|
|
|
va_list aq;
|
|
|
|
|
va_copy(aq, ap);
|
2021-11-25 15:05:50 +01:00
|
|
|
const size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
|
2018-06-13 03:14:20 +02:00
|
|
|
va_end(aq);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (VL_UNLIKELY(len < 1)) return "";
|
2017-10-26 05:01:45 +02:00
|
|
|
|
2021-07-24 14:36:11 +02:00
|
|
|
char* const bufp = new char[len + 1];
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
|
2018-06-13 03:14:20 +02:00
|
|
|
|
2023-04-09 04:11:28 +02:00
|
|
|
std::string result{bufp, len}; // Not const to allow move optimization
|
2017-10-26 13:27:47 +02:00
|
|
|
delete[] bufp;
|
2023-04-09 04:11:28 +02:00
|
|
|
return result;
|
2017-10-25 04:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
|
2025-10-28 01:49:41 +01:00
|
|
|
static std::atomic<uint64_t> s_sequence;
|
|
|
|
|
return ++s_sequence;
|
2017-10-25 04:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
uint32_t VL_THREAD_ID() VL_MT_SAFE {
|
2019-05-15 04:49:21 +02:00
|
|
|
// Alternative is to use std::this_thread::get_id, but that returns a
|
|
|
|
|
// hard-to-read number and is very slow
|
2022-03-27 21:27:40 +02:00
|
|
|
static std::atomic<uint32_t> s_nextId(0);
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local uint32_t t_myId = ++s_nextId;
|
2017-10-27 03:51:51 +02:00
|
|
|
return t_myId;
|
2017-10-25 04:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
2017-10-26 05:01:45 +02:00
|
|
|
// We're still using c printf formats instead of operator<< so we can avoid the heavy
|
|
|
|
|
// includes that otherwise would be required in every Verilated module
|
2017-10-25 04:56:58 +02:00
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, formatp);
|
2023-04-09 04:11:28 +02:00
|
|
|
const std::string result = _vl_string_vprintf(formatp, ap);
|
2017-10-25 04:56:58 +02:00
|
|
|
va_end(ap);
|
2022-01-01 22:04:20 +01:00
|
|
|
// printf("-imm-V{t%d,%" PRId64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(),
|
2023-04-09 04:11:28 +02:00
|
|
|
// result.c_str());
|
2018-05-09 03:43:32 +02:00
|
|
|
|
|
|
|
|
// Using VL_PRINTF not VL_PRINTF_MT so that we can call VL_DBG_MSGF
|
|
|
|
|
// from within the guts of the thread execution machinery (and it goes
|
|
|
|
|
// to the screen and not into the queues we're debugging)
|
2023-04-09 04:11:28 +02:00
|
|
|
VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), result.c_str());
|
2017-10-25 04:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 03:51:51 +02:00
|
|
|
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, formatp);
|
2023-04-09 04:11:28 +02:00
|
|
|
const std::string result = _vl_string_vprintf(formatp, ap);
|
2017-10-27 03:51:51 +02:00
|
|
|
va_end(ap);
|
2021-07-24 14:36:11 +02:00
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
2023-04-09 04:11:28 +02:00
|
|
|
VL_PRINTF("%s", result.c_str());
|
2021-07-24 14:36:11 +02:00
|
|
|
}});
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-14 19:01:59 +02:00
|
|
|
void VL_FFLUSH_MT() VL_MT_SAFE {
|
|
|
|
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
|
|
|
|
Verilated::runFlushCallbacks();
|
|
|
|
|
}});
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 18:55:14 +01:00
|
|
|
template <typename... snprintf_args_ts>
|
|
|
|
|
static size_t _vl_snprintf_string(std::string& str, const char* format,
|
|
|
|
|
snprintf_args_ts... args) VL_MT_SAFE {
|
|
|
|
|
constexpr size_t FIRST_TRY_SIZE = 128;
|
|
|
|
|
str.resize(FIRST_TRY_SIZE);
|
2026-03-28 04:14:18 +01:00
|
|
|
const size_t req_size = VL_SNPRINTF(&str[0], FIRST_TRY_SIZE + 1, format, args...);
|
2026-03-26 18:55:14 +01:00
|
|
|
if (VL_LIKELY(req_size <= FIRST_TRY_SIZE)) {
|
|
|
|
|
str.resize(req_size); // Resize the string down to the real size,
|
|
|
|
|
// otherwise it will break things later
|
|
|
|
|
return req_size;
|
|
|
|
|
}
|
|
|
|
|
str.resize(req_size);
|
2026-03-28 04:14:18 +01:00
|
|
|
(void)VL_SNPRINTF(&str[0], req_size + 1, format, args...);
|
2026-03-26 18:55:14 +01:00
|
|
|
return req_size;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 03:11:07 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// Process -- parts of std::process implementation
|
|
|
|
|
|
2026-04-13 19:58:53 +02:00
|
|
|
thread_local VlProcess* VlProcess::t_currentp = nullptr;
|
|
|
|
|
|
|
|
|
|
std::string VlProcess::randstate() const VL_MT_UNSAFE { return m_rng.get_randstate(); }
|
|
|
|
|
void VlProcess::randstate(const std::string& state) VL_MT_UNSAFE { m_rng.set_randstate(state); }
|
|
|
|
|
VlRNG& VlProcess::currentRng() VL_MT_SAFE {
|
|
|
|
|
if (t_currentp) return t_currentp->m_rng;
|
|
|
|
|
return VlRNG::vl_thread_rng();
|
2025-10-17 03:11:07 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
2018-08-26 13:07:01 +02:00
|
|
|
// Random -- Mostly called at init time, so not inline.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2026-02-01 20:53:33 +01:00
|
|
|
static std::pair<uint64_t, uint64_t> vl_splitmix64(uint64_t x) VL_PURE {
|
|
|
|
|
// SplitMix64 algorithm, copied under public domain from
|
|
|
|
|
// https://prng.di.unimi.it/splitmix64.c
|
|
|
|
|
// by Sebastiano Vigna
|
|
|
|
|
uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
|
|
|
|
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
|
|
|
|
|
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
|
|
|
|
|
return {x, z ^ (z >> 31)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Xoroshiro128** algorithm, copied under public domain from
|
|
|
|
|
// https://xoshiro.di.unimi.it/xoroshiro128starstar.c
|
|
|
|
|
// by David Blackman and Sebastiano Vigna
|
|
|
|
|
|
|
|
|
|
static uint64_t vl_rolt(const uint64_t x, int k) VL_PURE { return (x << k) | (x >> (64 - k)); }
|
|
|
|
|
|
|
|
|
|
static std::array<uint64_t, 2> vl_rng_state_from_seed(uint64_t seed) VL_PURE {
|
|
|
|
|
const auto split1 = vl_splitmix64(seed);
|
|
|
|
|
const auto split2 = vl_splitmix64(split1.first);
|
|
|
|
|
return {split1.second, split2.second};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint64_t vl_rng_result(const std::array<uint64_t, 2>& state) VL_PURE {
|
|
|
|
|
const uint64_t s0 = state[0];
|
|
|
|
|
return vl_rolt(s0 * 5, 7) * 9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::array<uint64_t, 2>
|
|
|
|
|
vl_rng_compute_new_state(const std::array<uint64_t, 2>& current_state) VL_PURE {
|
|
|
|
|
const uint64_t s0 = current_state[0];
|
|
|
|
|
uint64_t s1 = current_state[1];
|
|
|
|
|
|
|
|
|
|
s1 ^= s0;
|
|
|
|
|
const uint64_t new_s0 = vl_rolt(s0, 24) ^ s1 ^ (s1 << 16); // a, b
|
|
|
|
|
const uint64_t new_s1 = vl_rolt(s1, 37); // c
|
|
|
|
|
|
|
|
|
|
return {new_s0, new_s1};
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 16:50:27 +02:00
|
|
|
VlRNG::VlRNG() VL_MT_SAFE {
|
2026-04-13 19:58:53 +02:00
|
|
|
// Seed from process RNG if in a process, else thread RNG (IEEE 1800-2023 18.14.1)
|
|
|
|
|
VlRNG& fromr = VlProcess::currentRng();
|
2026-02-01 20:53:33 +01:00
|
|
|
|
|
|
|
|
const uint64_t s0 = vl_rng_result(fromr.m_state);
|
|
|
|
|
fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
|
|
|
|
|
|
|
|
|
|
const uint64_t s1 = vl_rng_result(fromr.m_state);
|
|
|
|
|
fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
|
|
|
|
|
|
|
|
|
|
m_state = {s0, s1};
|
2023-04-01 16:50:27 +02:00
|
|
|
}
|
2026-02-01 20:53:33 +01:00
|
|
|
|
|
|
|
|
VlRNG::VlRNG(uint64_t seed) VL_PURE { m_state = vl_rng_state_from_seed(seed); }
|
|
|
|
|
void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { m_state = vl_rng_state_from_seed(n); }
|
|
|
|
|
|
2023-04-01 16:50:27 +02:00
|
|
|
uint64_t VlRNG::rand64() VL_MT_UNSAFE {
|
2026-02-01 20:53:33 +01:00
|
|
|
const uint64_t result = vl_rng_result(m_state);
|
|
|
|
|
m_state = vl_rng_compute_new_state(m_state);
|
2023-04-01 16:50:27 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE {
|
|
|
|
|
VlRNG& fromr = vl_thread_rng();
|
2026-02-01 20:53:33 +01:00
|
|
|
const uint64_t result = vl_rng_result(fromr.m_state);
|
|
|
|
|
fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
|
2023-04-01 16:50:27 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2026-04-13 19:58:53 +02:00
|
|
|
uint64_t VlRNG::vl_current_rng_rand64() VL_MT_SAFE {
|
|
|
|
|
VlRNG& fromr = VlProcess::currentRng();
|
|
|
|
|
const uint64_t result = vl_rng_result(fromr.m_state);
|
|
|
|
|
fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2026-02-01 20:53:33 +01:00
|
|
|
|
2023-05-07 01:09:19 +02:00
|
|
|
std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
|
|
|
|
|
// Though not stated in IEEE, assumption is the string must be printable
|
|
|
|
|
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
|
2023-05-07 02:23:35 +02:00
|
|
|
static_assert(sizeof(m_state) == 16, "");
|
2023-05-07 01:09:19 +02:00
|
|
|
std::string result{"R00112233445566770011223344556677"};
|
2023-09-01 23:59:30 +02:00
|
|
|
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
2023-05-07 01:09:19 +02:00
|
|
|
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
|
|
|
|
|
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
|
|
|
|
|
if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) {
|
|
|
|
|
VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
|
2023-09-01 23:59:30 +02:00
|
|
|
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
2023-05-07 01:09:19 +02:00
|
|
|
stateCharsp[i]
|
|
|
|
|
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-01 16:50:27 +02:00
|
|
|
|
2022-11-28 13:08:34 +01:00
|
|
|
static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
2018-08-26 13:07:01 +02:00
|
|
|
// Return random 32-bits using system library.
|
2022-12-03 00:46:38 +01:00
|
|
|
// Used only to construct seed for Verilator's PRNG.
|
2020-11-20 03:32:33 +01:00
|
|
|
static VerilatedMutex s_mutex;
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe
|
2018-08-26 13:07:01 +02:00
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|
|
|
// Windows doesn't have lrand48(), although Cygwin does.
|
2021-03-27 02:23:18 +01:00
|
|
|
return (std::rand() << 16) ^ std::rand();
|
2018-08-26 13:07:01 +02:00
|
|
|
#else
|
2020-04-14 04:51:35 +02:00
|
|
|
return (lrand48() << 16) ^ lrand48();
|
2018-08-26 13:07:01 +02:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 16:50:27 +02:00
|
|
|
VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE {
|
|
|
|
|
static thread_local VlRNG t_rng{0};
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local uint32_t t_seedEpoch = 0;
|
2020-11-20 03:32:33 +01:00
|
|
|
// For speed, we use a thread-local epoch number to know when to reseed
|
2021-03-07 17:01:54 +01:00
|
|
|
// A thread always belongs to a single context, so this works out ok
|
|
|
|
|
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
|
|
|
|
|
// Set epoch before state, to avoid race case with new seeding
|
|
|
|
|
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
|
2026-02-01 20:53:33 +01:00
|
|
|
t_rng.m_state
|
|
|
|
|
= vl_rng_state_from_seed(Verilated::threadContextp()->impp()->randSeedDefault64());
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2023-04-01 16:50:27 +02:00
|
|
|
return t_rng;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 03:51:51 +02:00
|
|
|
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
2021-11-28 20:00:19 +01:00
|
|
|
for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = vl_rand64();
|
|
|
|
|
// Last word is unclean
|
2008-06-27 17:36:25 +02:00
|
|
|
return outwp;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 01:40:13 +01:00
|
|
|
double VL_RANDOM_RNG_D(VlRNG& rngr) VL_MT_UNSAFE { return VL_CVT_D_Q(VL_RANDOM_RNG_Q(rngr)); }
|
2026-02-21 01:39:20 +01:00
|
|
|
|
2023-04-01 16:50:27 +02:00
|
|
|
WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE {
|
|
|
|
|
for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64();
|
|
|
|
|
// Last word is unclean
|
|
|
|
|
return outwp;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-01 22:43:06 +01:00
|
|
|
IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE {
|
|
|
|
|
// $random - seed is a new seed to apply, then we return new seed
|
|
|
|
|
Verilated::threadContextp()->randSeed(static_cast<int>(seedr));
|
|
|
|
|
seedr = VL_RANDOM_I();
|
|
|
|
|
return VL_RANDOM_I();
|
|
|
|
|
}
|
|
|
|
|
IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
|
|
|
|
|
// $urandom - seed is a new seed to apply
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->randSeed(static_cast<int>(seed));
|
2021-11-28 20:00:19 +01:00
|
|
|
return VL_RANDOM_I();
|
2020-11-20 03:32:33 +01:00
|
|
|
}
|
2025-05-27 15:31:55 +02:00
|
|
|
|
|
|
|
|
IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
2026-07-02 15:50:55 +02:00
|
|
|
const int randReset = Verilated::threadContextp()->randReset();
|
|
|
|
|
if (randReset == 0) return 0;
|
2025-05-27 15:31:55 +02:00
|
|
|
IData data = ~0;
|
2026-07-02 15:50:55 +02:00
|
|
|
if (randReset != 1) { // if 2, randomize
|
2025-06-28 18:29:41 +02:00
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
2025-05-27 15:31:55 +02:00
|
|
|
data = rng.rand64();
|
|
|
|
|
}
|
|
|
|
|
data &= VL_MASK_I(obits);
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
2026-07-02 15:50:55 +02:00
|
|
|
const int randReset = Verilated::threadContextp()->randReset();
|
|
|
|
|
if (randReset == 0) return 0;
|
2025-05-27 15:31:55 +02:00
|
|
|
QData data = ~0ULL;
|
2026-07-02 15:50:55 +02:00
|
|
|
if (randReset != 1) { // if 2, randomize
|
2025-06-28 18:29:41 +02:00
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
2025-05-27 15:31:55 +02:00
|
|
|
data = rng.rand64();
|
|
|
|
|
}
|
|
|
|
|
data &= VL_MASK_Q(obits);
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
|
|
|
|
|
uint64_t salt) VL_MT_UNSAFE {
|
2026-07-02 15:50:55 +02:00
|
|
|
const int words = VL_WORDS_I(obits);
|
|
|
|
|
const int randReset = Verilated::threadContextp()->randReset();
|
|
|
|
|
if (randReset == 0) {
|
|
|
|
|
VL_MEMSET_ZERO_W(outwp, words);
|
|
|
|
|
} else if (randReset == 1) {
|
|
|
|
|
VL_MEMSET_ONES_W(outwp, words);
|
|
|
|
|
} else {
|
|
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
|
|
|
|
for (int i = 0; i < words; ++i) outwp[i] = rng.rand64();
|
|
|
|
|
}
|
|
|
|
|
outwp[words - 1] &= VL_MASK_E(obits);
|
2025-05-27 15:31:55 +02:00
|
|
|
return outwp;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 23:59:01 +02:00
|
|
|
IData VL_SCOPED_RAND_RESET_ASSIGN_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
2025-06-28 18:29:41 +02:00
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
|
|
|
|
const IData data = rng.rand64() & VL_MASK_I(obits);
|
2025-06-09 23:59:01 +02:00
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QData VL_SCOPED_RAND_RESET_ASSIGN_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
2025-06-28 18:29:41 +02:00
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
|
|
|
|
const QData data = rng.rand64() & VL_MASK_Q(obits);
|
2025-06-09 23:59:01 +02:00
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t scopeHash,
|
|
|
|
|
uint64_t salt) VL_MT_UNSAFE {
|
2025-06-28 18:29:41 +02:00
|
|
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
2025-06-09 23:59:01 +02:00
|
|
|
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
|
|
|
|
|
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
|
|
|
|
|
return outwp;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 03:51:51 +02:00
|
|
|
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
2026-07-02 15:50:55 +02:00
|
|
|
const int randReset = Verilated::threadContextp()->randReset();
|
|
|
|
|
if (randReset == 0) return 0;
|
2006-08-26 13:35:28 +02:00
|
|
|
IData data = ~0;
|
2026-07-02 15:50:55 +02:00
|
|
|
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
|
2019-12-09 03:36:38 +01:00
|
|
|
data &= VL_MASK_I(obits);
|
2006-08-26 13:35:28 +02:00
|
|
|
return data;
|
|
|
|
|
}
|
2024-07-14 23:04:00 +02:00
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
2022-12-23 16:51:52 +01:00
|
|
|
// Not inlined to speed up compilation of slowpath code
|
|
|
|
|
return VL_ZERO_W(obits, outwp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-17 20:48:39 +01:00
|
|
|
//===========================================================================
|
|
|
|
|
// Debug
|
|
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
void _vl_debug_print_w(int lbits, const WDataInP iwp) VL_MT_SAFE {
|
2017-10-20 01:40:51 +02:00
|
|
|
VL_PRINTF_MT(" Data: w%d: ", lbits);
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]);
|
2017-10-20 01:40:51 +02:00
|
|
|
VL_PRINTF_MT("\n");
|
2010-01-17 20:48:39 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-27 01:12:09 +01:00
|
|
|
//===========================================================================
|
2022-10-12 11:19:21 +02:00
|
|
|
// Slow expressions
|
2009-10-27 01:12:09 +01:00
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WDataInP rwp,
|
2019-05-15 04:49:21 +02:00
|
|
|
bool is_modulus) VL_MT_SAFE {
|
2009-10-27 01:12:09 +01:00
|
|
|
// See Knuth Algorithm D. Computes u/v = q.r
|
|
|
|
|
// This isn't massively tuned, as wide division is rare
|
|
|
|
|
// for debug see V3Number version
|
|
|
|
|
// Requires clean input
|
2021-06-19 04:19:35 +02:00
|
|
|
const int words = VL_WORDS_I(lbits);
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0; i < words; ++i) owp[i] = 0;
|
2009-10-27 01:12:09 +01:00
|
|
|
// Find MSB and check for zero.
|
2021-06-19 04:19:35 +02:00
|
|
|
const int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend
|
|
|
|
|
const int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor
|
2020-04-14 04:51:35 +02:00
|
|
|
if (VL_UNLIKELY(vmsbp1 == 0) // rwp==0 so division by zero. Return 0.
|
|
|
|
|
|| VL_UNLIKELY(umsbp1 == 0)) { // 0/x so short circuit and return 0
|
2019-05-09 03:13:38 +02:00
|
|
|
return owp;
|
2009-10-27 01:12:09 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
const int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
|
|
|
|
|
const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
|
2023-12-30 21:31:58 +01:00
|
|
|
VL_DEBUG_IFDEF(assert(uw <= VL_MULS_MAX_WORDS););
|
|
|
|
|
VL_DEBUG_IFDEF(assert(vw <= VL_MULS_MAX_WORDS););
|
2009-10-27 01:12:09 +01:00
|
|
|
|
|
|
|
|
if (vw == 1) { // Single divisor word breaks rest of algorithm
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t k = 0;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int j = uw - 1; j >= 0; --j) {
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t unw64 = ((k << 32ULL) + static_cast<uint64_t>(lwp[j]));
|
|
|
|
|
owp[j] = unw64 / static_cast<uint64_t>(rwp[0]);
|
|
|
|
|
k = unw64 - static_cast<uint64_t>(owp[j]) * static_cast<uint64_t>(rwp[0]);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
if (is_modulus) {
|
|
|
|
|
owp[0] = k;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 1; i < words; ++i) owp[i] = 0;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
return owp;
|
2009-10-27 01:12:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// +1 word as we may shift during normalization
|
2022-03-27 21:27:40 +02:00
|
|
|
uint32_t un[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
|
|
|
|
|
uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized
|
2009-10-27 01:12:09 +01:00
|
|
|
|
|
|
|
|
// Zero for ease of debugging and to save having to zero for shifts
|
2014-05-04 02:09:45 +02:00
|
|
|
// Note +1 as loop will use extra word
|
2023-12-30 20:46:57 +01:00
|
|
|
for (int i = 0; i < words + 1; ++i) un[i] = vn[i] = 0;
|
2009-10-27 01:12:09 +01:00
|
|
|
|
|
|
|
|
// Algorithm requires divisor MSB to be set
|
|
|
|
|
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
|
2021-06-19 04:19:35 +02:00
|
|
|
const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
|
2009-10-27 01:12:09 +01:00
|
|
|
// Copy and shift dividend by same amount; may set new upper word
|
2020-04-14 04:51:35 +02:00
|
|
|
if (s) {
|
2023-12-30 20:46:57 +01:00
|
|
|
for (int i = vw - 1; i > 0; --i) vn[i] = (rwp[i] << s) | (rwp[i - 1] >> (32 - s));
|
|
|
|
|
vn[0] = rwp[0] << s;
|
2020-04-14 04:51:35 +02:00
|
|
|
un[uw] = lwp[uw - 1] >> (32 - s);
|
2023-12-30 20:46:57 +01:00
|
|
|
for (int i = uw - 1; i > 0; --i) un[i] = (lwp[i] << s) | (lwp[i - 1] >> (32 - s));
|
|
|
|
|
un[0] = lwp[0] << s;
|
2020-04-14 04:51:35 +02:00
|
|
|
} else {
|
2023-12-30 20:46:57 +01:00
|
|
|
for (int i = vw - 1; i > 0; --i) vn[i] = rwp[i];
|
|
|
|
|
vn[0] = rwp[0];
|
2020-04-14 04:51:35 +02:00
|
|
|
un[uw] = 0;
|
2023-12-30 20:46:57 +01:00
|
|
|
for (int i = uw - 1; i > 0; --i) un[i] = lwp[i];
|
|
|
|
|
un[0] = lwp[0];
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2009-10-27 01:12:09 +01:00
|
|
|
|
|
|
|
|
// Main loop
|
2017-06-06 02:16:51 +02:00
|
|
|
for (int j = uw - vw; j >= 0; --j) {
|
2019-05-09 03:13:38 +02:00
|
|
|
// Estimate
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t unw64
|
|
|
|
|
= (static_cast<uint64_t>(un[j + vw]) << 32ULL | static_cast<uint64_t>(un[j + vw - 1]));
|
|
|
|
|
uint64_t qhat = unw64 / static_cast<uint64_t>(vn[vw - 1]);
|
|
|
|
|
uint64_t rhat = unw64 - qhat * static_cast<uint64_t>(vn[vw - 1]);
|
2009-10-27 01:12:09 +01:00
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
again:
|
2020-05-29 02:32:07 +02:00
|
|
|
if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) {
|
2019-05-09 03:13:38 +02:00
|
|
|
qhat = qhat - 1;
|
2020-04-14 04:51:35 +02:00
|
|
|
rhat = rhat + vn[vw - 1];
|
2020-05-29 02:32:07 +02:00
|
|
|
if (rhat < 0x100000000ULL) goto again;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
int64_t t = 0; // Must be signed
|
|
|
|
|
uint64_t k = 0;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0; i < vw; ++i) {
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t p = qhat * vn[i]; // Multiply by estimate
|
2020-05-29 02:32:07 +02:00
|
|
|
t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract
|
2020-04-14 04:51:35 +02:00
|
|
|
un[i + j] = t;
|
2020-05-29 02:32:07 +02:00
|
|
|
k = (p >> 32ULL) - (t >> 32ULL);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
t = un[j + vw] - k;
|
|
|
|
|
un[j + vw] = t;
|
2018-11-29 01:59:10 +01:00
|
|
|
owp[j] = qhat; // Save quotient digit
|
2009-10-27 01:12:09 +01:00
|
|
|
|
2019-05-09 03:13:38 +02:00
|
|
|
if (t < 0) {
|
|
|
|
|
// Over subtracted; correct by adding back
|
|
|
|
|
owp[j]--;
|
|
|
|
|
k = 0;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0; i < vw; ++i) {
|
2022-03-27 21:27:40 +02:00
|
|
|
t = static_cast<uint64_t>(un[i + j]) + static_cast<uint64_t>(vn[i]) + k;
|
2020-04-14 04:51:35 +02:00
|
|
|
un[i + j] = t;
|
2020-05-29 02:32:07 +02:00
|
|
|
k = t >> 32ULL;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
un[j + vw] = un[j + vw] + k;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2009-10-27 01:12:09 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-29 01:59:10 +01:00
|
|
|
if (is_modulus) { // modulus
|
|
|
|
|
// Need to reverse normalization on copy to output
|
2023-12-30 20:46:57 +01:00
|
|
|
if (s) {
|
|
|
|
|
for (int i = 0; i < vw; ++i) owp[i] = (un[i] >> s) | (un[i + 1] << (32 - s));
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < vw; ++i) owp[i] = un[i];
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = vw; i < words; ++i) owp[i] = 0;
|
2018-11-29 01:59:10 +01:00
|
|
|
return owp;
|
2009-10-27 01:12:09 +01:00
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
// division
|
|
|
|
|
return owp;
|
2009-10-27 01:12:09 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
|
|
|
|
|
const WDataInP rwp) VL_MT_SAFE {
|
2017-07-13 02:08:32 +02:00
|
|
|
// obits==lbits, rbits can be different
|
2023-12-30 21:31:58 +01:00
|
|
|
const int owords = VL_WORDS_I(obits);
|
|
|
|
|
VL_DEBUG_IFDEF(assert(owords <= VL_MULS_MAX_WORDS););
|
2017-06-06 02:30:01 +02:00
|
|
|
owp[0] = 1;
|
2025-05-09 14:25:54 +02:00
|
|
|
for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_MULS_MAX_WORDS> powstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
|
|
|
|
VlWide<VL_MULS_MAX_WORDS> lastpowstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
|
|
|
|
VlWide<VL_MULS_MAX_WORDS> lastoutstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
2017-06-06 02:30:01 +02:00
|
|
|
VL_ASSIGN_W(obits, powstore, lwp);
|
2025-05-09 14:25:54 +02:00
|
|
|
for (int bit = 0; bit < rbits; ++bit) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (bit > 0) { // power = power*power
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_ASSIGN_W(obits, lastpowstore, powstore);
|
2023-12-30 21:31:58 +01:00
|
|
|
VL_MUL_W(owords, powstore, lastpowstore, lastpowstore);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
if (VL_BITISSET_W(rwp, bit)) { // out *= power
|
|
|
|
|
VL_ASSIGN_W(obits, lastoutstore, owp);
|
2023-12-30 21:31:58 +01:00
|
|
|
VL_MUL_W(owords, owp, lastoutstore, powstore);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2017-06-06 02:30:01 +02:00
|
|
|
}
|
|
|
|
|
return owp;
|
|
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
|
2020-04-14 04:51:35 +02:00
|
|
|
QData rhs) VL_MT_SAFE {
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> rhsw;
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_SET_WQ(rhsw, rhs);
|
2019-05-09 03:13:38 +02:00
|
|
|
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
|
2017-07-13 02:08:32 +02:00
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE {
|
2023-11-26 23:11:22 +01:00
|
|
|
const int rwords = VL_WORDS_I(rbits);
|
|
|
|
|
EData rnz = rwp[0];
|
|
|
|
|
for (int w = 1; w < rwords; ++w) rnz |= rwp[w];
|
|
|
|
|
if (!rnz) return 1; // rwp == 0
|
2020-04-14 04:51:35 +02:00
|
|
|
if (VL_UNLIKELY(lhs == 0)) return 0;
|
2017-07-13 02:08:32 +02:00
|
|
|
QData power = lhs;
|
2023-04-09 04:11:28 +02:00
|
|
|
QData result = 1ULL;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int bit = 0; bit < rbits; ++bit) {
|
|
|
|
|
if (bit > 0) power = power * power;
|
2023-04-09 04:11:28 +02:00
|
|
|
if (VL_BITISSET_W(rwp, bit)) result *= power;
|
2017-07-13 02:08:32 +02:00
|
|
|
}
|
2023-04-09 04:11:28 +02:00
|
|
|
return result;
|
2017-07-13 02:08:32 +02:00
|
|
|
}
|
2017-06-06 02:30:01 +02:00
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
|
|
|
|
|
const WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
2017-07-13 02:08:32 +02:00
|
|
|
// obits==lbits, rbits can be different
|
2017-06-06 02:30:01 +02:00
|
|
|
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const int words = VL_WORDS_I(obits);
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_ZERO_W(obits, owp);
|
2019-12-09 03:36:38 +01:00
|
|
|
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
|
2024-03-27 22:57:49 +01:00
|
|
|
for (int i = 1; i < (words - 1); ++i) lor |= lwp[i];
|
2020-04-14 04:51:35 +02:00
|
|
|
lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0);
|
|
|
|
|
if (lor == 0 && lwp[0] == 0) { // "X" so return 0
|
|
|
|
|
return owp;
|
2026-03-28 04:14:18 +01:00
|
|
|
}
|
|
|
|
|
if (lor == 0 && lwp[0] == 1) { // 1
|
2020-04-14 04:51:35 +02:00
|
|
|
owp[0] = 1;
|
|
|
|
|
return owp;
|
2026-03-28 04:14:18 +01:00
|
|
|
}
|
|
|
|
|
if (lsign && lor == ~VL_EUL(0) && lwp[0] == ~VL_EUL(0)) { // -1
|
2020-04-14 04:51:35 +02:00
|
|
|
if (rwp[0] & 1) { // -1^odd=-1
|
|
|
|
|
return VL_ALLONES_W(obits, owp);
|
|
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
// -1^even=1
|
|
|
|
|
owp[0] = 1;
|
|
|
|
|
return owp;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2020-11-11 03:40:14 +01:00
|
|
|
return owp;
|
2017-06-06 02:30:01 +02:00
|
|
|
}
|
|
|
|
|
return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
|
|
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
|
|
|
|
|
QData rhs, bool lsign, bool rsign) VL_MT_SAFE {
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> rhsw;
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_SET_WQ(rhsw, rhs);
|
2019-05-09 03:13:38 +02:00
|
|
|
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
|
2017-07-13 02:08:32 +02:00
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, const WDataInP rwp, bool lsign,
|
2020-04-14 04:51:35 +02:00
|
|
|
bool rsign) VL_MT_SAFE {
|
2017-07-13 02:08:32 +02:00
|
|
|
// Skip check for rhs == 0, as short-circuit doesn't save time
|
|
|
|
|
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
2026-03-28 04:14:18 +01:00
|
|
|
if (lhs == 0) return 0; // "X"
|
|
|
|
|
if (lhs == 1) return 1;
|
|
|
|
|
if (lsign && lhs == VL_MASK_Q(obits)) { // -1
|
|
|
|
|
if (rwp[0] & 1) return VL_MASK_Q(obits); // -1^odd=-1
|
|
|
|
|
return 1; // -1^even=1
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2019-05-09 03:13:38 +02:00
|
|
|
return 0;
|
2017-07-13 02:08:32 +02:00
|
|
|
}
|
|
|
|
|
return VL_POW_QQW(obits, rbits, rbits, lhs, rwp);
|
|
|
|
|
}
|
2017-06-06 02:30:01 +02:00
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
|
2020-08-07 03:56:43 +02:00
|
|
|
int ms_word = VL_WORDS_I(lbits) - 1;
|
|
|
|
|
for (; !lwp[ms_word] && ms_word > 0;) --ms_word;
|
|
|
|
|
if (ms_word == 0) return static_cast<double>(lwp[0]);
|
|
|
|
|
if (ms_word == 1) return static_cast<double>(VL_SET_QW(lwp));
|
|
|
|
|
// We need 53 bits of mantissa, which might mean looking at 3 words
|
|
|
|
|
// namely ms_word, ms_word-1 and ms_word-2
|
2021-06-19 04:19:35 +02:00
|
|
|
const EData ihi = lwp[ms_word];
|
|
|
|
|
const EData imid = lwp[ms_word - 1];
|
|
|
|
|
const EData ilo = lwp[ms_word - 2];
|
|
|
|
|
const double hi = static_cast<double>(ihi) * std::exp2(2 * VL_EDATASIZE);
|
|
|
|
|
const double mid = static_cast<double>(imid) * std::exp2(VL_EDATASIZE);
|
|
|
|
|
const double lo = static_cast<double>(ilo);
|
|
|
|
|
const double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
|
2020-08-07 03:56:43 +02:00
|
|
|
return d;
|
|
|
|
|
}
|
2022-12-12 05:03:27 +01:00
|
|
|
double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_MT_SAFE {
|
2020-08-07 03:56:43 +02:00
|
|
|
if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
|
2023-12-30 21:31:58 +01:00
|
|
|
const int words = VL_WORDS_I(lbits);
|
|
|
|
|
VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS););
|
2026-05-22 21:05:08 +02:00
|
|
|
VlWide<VL_MULS_MAX_WORDS + 1> pos;
|
2023-12-30 21:31:58 +01:00
|
|
|
VL_NEGATE_W(words, pos, lwp);
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_clean_inplace_w(lbits, pos);
|
2020-08-07 03:56:43 +02:00
|
|
|
return -VL_ITOR_D_W(lbits, pos);
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// Formatting
|
|
|
|
|
|
2021-03-28 17:50:05 +02:00
|
|
|
// Output a string representation of a wide number
|
2021-06-19 04:19:35 +02:00
|
|
|
std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE {
|
|
|
|
|
const int maxdecwidth = (width + 3) * 4 / 3;
|
2017-12-03 04:10:58 +01:00
|
|
|
// Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word
|
2026-03-26 18:55:14 +01:00
|
|
|
std::vector<EData> bcd(VL_WORDS_I(maxdecwidth));
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP bcdp = WDataOutP::external(bcd.data());
|
|
|
|
|
VL_ZERO_W(maxdecwidth, bcdp);
|
2026-03-26 18:55:14 +01:00
|
|
|
std::vector<EData> tmp(VL_WORDS_I(maxdecwidth));
|
|
|
|
|
std::vector<EData> tmp2(VL_WORDS_I(maxdecwidth));
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP tmpp = WDataOutP::external(tmp.data());
|
|
|
|
|
WDataOutP tmp2p = WDataOutP::external(tmp2.data());
|
2020-04-14 04:51:35 +02:00
|
|
|
int from_bit = width - 1;
|
2017-12-03 04:10:58 +01:00
|
|
|
// Skip all leading zeros
|
2020-04-14 04:51:35 +02:00
|
|
|
for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {}
|
2017-12-03 04:10:58 +01:00
|
|
|
// Double-dabble algorithm
|
|
|
|
|
for (; from_bit >= 0; --from_bit) {
|
|
|
|
|
// Any digits >= 5 need an add 3 (via tmp)
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit += 4) {
|
2017-12-03 04:10:58 +01:00
|
|
|
if ((VL_BITRSHIFT_W(bcd, nibble_bit) & 0xf) >= 5) {
|
2026-05-22 21:05:08 +02:00
|
|
|
VL_ZERO_W(maxdecwidth, tmp2p);
|
2019-12-09 03:36:38 +01:00
|
|
|
tmp2[VL_BITWORD_E(nibble_bit)] |= VL_EUL(0x3) << VL_BITBIT_E(nibble_bit);
|
2026-05-22 21:05:08 +02:00
|
|
|
VL_ASSIGN_W(maxdecwidth, tmpp, bcdp);
|
|
|
|
|
VL_ADD_W(VL_WORDS_I(maxdecwidth), bcdp, tmpp, tmp2p);
|
2017-12-03 04:10:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Shift; bcd = bcd << 1
|
2026-05-22 21:05:08 +02:00
|
|
|
VL_ASSIGN_W(maxdecwidth, tmpp, bcdp);
|
|
|
|
|
VL_SHIFTL_WWI(maxdecwidth, maxdecwidth, 32, bcdp, tmpp, 1);
|
2017-12-03 04:10:58 +01:00
|
|
|
// bcd[0] = lwp[from_bit]
|
|
|
|
|
if (VL_BITISSET_W(lwp, from_bit)) bcd[0] |= 1;
|
|
|
|
|
}
|
|
|
|
|
std::string output;
|
2020-04-14 04:51:35 +02:00
|
|
|
int lsb = (maxdecwidth - 1) & ~3;
|
|
|
|
|
for (; lsb > 0; lsb -= 4) { // Skip leading zeros
|
2017-12-03 04:10:58 +01:00
|
|
|
if (VL_BITRSHIFT_W(bcd, lsb) & 0xf) break;
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
for (; lsb >= 0; lsb -= 4) {
|
2017-12-03 04:10:58 +01:00
|
|
|
output += ('0' + (VL_BITRSHIFT_W(bcd, lsb) & 0xf)); // 0..9
|
|
|
|
|
}
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 12:01:03 +02:00
|
|
|
template <typename T>
|
2026-03-26 18:55:14 +01:00
|
|
|
std::string _vl_vsformat_time(std::string& tmp, T ld, int timeunit, bool left,
|
|
|
|
|
size_t width) VL_MT_SAFE {
|
2021-05-16 12:01:03 +02:00
|
|
|
const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
|
|
|
|
|
const std::string suffix = ctxImpp->timeFormatSuffix();
|
|
|
|
|
const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15
|
|
|
|
|
const int fracDigits = ctxImpp->timeFormatPrecision(); // 0..N
|
|
|
|
|
const int shift = -userUnits + fracDigits + timeunit; // 0..-15
|
2020-06-02 05:16:02 +02:00
|
|
|
int digits = 0;
|
2021-05-16 12:01:03 +02:00
|
|
|
if (std::numeric_limits<T>::is_integer) {
|
|
|
|
|
constexpr int b = 128;
|
|
|
|
|
constexpr int w = VL_WORDS_I(b);
|
2022-07-30 16:01:25 +02:00
|
|
|
VlWide<w> tmp0;
|
|
|
|
|
VlWide<w> tmp1;
|
|
|
|
|
VlWide<w> tmp2;
|
|
|
|
|
VlWide<w> tmp3;
|
2021-05-16 12:01:03 +02:00
|
|
|
|
2021-05-17 00:38:43 +02:00
|
|
|
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
|
2021-05-16 12:01:03 +02:00
|
|
|
if (shift < 0) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
|
2021-05-16 12:01:03 +02:00
|
|
|
shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
|
|
|
|
|
} else {
|
2021-06-19 04:19:35 +02:00
|
|
|
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
|
2021-05-16 12:01:03 +02:00
|
|
|
shifted = VL_MUL_W(w, tmp2, shifted, pow10);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
const WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
|
|
|
|
|
const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
|
|
|
|
|
const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
|
|
|
|
|
const WDataInP max64Bit
|
2022-03-27 21:27:40 +02:00
|
|
|
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
|
2021-05-16 12:01:03 +02:00
|
|
|
if (VL_GT_W(w, integer, max64Bit)) {
|
|
|
|
|
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
|
2022-07-30 17:52:35 +02:00
|
|
|
VlWide<w> zero;
|
|
|
|
|
VlWide<w> ten;
|
2021-05-16 12:01:03 +02:00
|
|
|
VL_ZERO_W(b, zero);
|
|
|
|
|
VL_EXTEND_WI(b, 0, ten, 10);
|
|
|
|
|
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
|
|
|
|
|
char* ptr = buf + sizeof(buf) - 1;
|
|
|
|
|
*ptr = '\0';
|
|
|
|
|
while (VL_GT_W(w, v, zero)) {
|
|
|
|
|
--ptr;
|
2021-06-19 04:19:35 +02:00
|
|
|
const WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
|
2021-05-16 12:01:03 +02:00
|
|
|
*ptr = "0123456789"[VL_SET_QW(mod)];
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<w> divided;
|
2021-05-16 12:01:03 +02:00
|
|
|
VL_DIV_WWW(b, divided, v, ten);
|
|
|
|
|
VL_ASSIGN_W(b, v, divided);
|
|
|
|
|
}
|
|
|
|
|
if (!fracDigits) {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%s%s", ptr, suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
} else {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%s.%0*" PRIu64 "%s", ptr, fracDigits,
|
|
|
|
|
VL_SET_QW(frac), suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t integer64 = VL_SET_QW(integer);
|
2021-05-16 12:01:03 +02:00
|
|
|
if (!fracDigits) {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%" PRIu64 "%s", integer64, suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
} else {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%" PRIu64 ".%0*" PRIu64 "%s", integer64,
|
|
|
|
|
fracDigits, VL_SET_QW(frac), suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-16 01:39:03 +02:00
|
|
|
} else {
|
2021-11-25 15:05:50 +01:00
|
|
|
const double shiftd = vl_time_multiplier(shift);
|
|
|
|
|
const double scaled = ld * shiftd;
|
2021-05-16 12:01:03 +02:00
|
|
|
const double fracDiv = vl_time_multiplier(fracDigits);
|
|
|
|
|
const double whole = scaled / fracDiv;
|
|
|
|
|
if (!fracDigits) {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%.0f%s", whole, suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
} else {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(tmp, "%.*f%s", fracDigits, whole, suffix.c_str());
|
2021-05-16 12:01:03 +02:00
|
|
|
}
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2021-05-07 18:00:36 +02:00
|
|
|
|
2024-01-28 20:47:14 +01:00
|
|
|
const int needmore = static_cast<int>(width) - digits;
|
2020-04-16 01:39:03 +02:00
|
|
|
std::string padding;
|
|
|
|
|
if (needmore > 0) padding.append(needmore, ' '); // Pad with spaces
|
|
|
|
|
return left ? (tmp + padding) : (padding + tmp);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-30 20:31:58 +02:00
|
|
|
// Do a va_arg returning a quad, assuming input argument is anything less than wide
|
2021-03-04 04:53:50 +01:00
|
|
|
#define VL_VA_ARG_Q_(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData))
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2026-03-15 03:43:56 +01:00
|
|
|
void _vl_vsformat(std::string& output, const std::string& format, int argc,
|
|
|
|
|
va_list ap) VL_MT_SAFE {
|
2008-06-30 20:31:58 +02:00
|
|
|
// Format a Verilog $write style format into the output list
|
2026-03-15 03:43:56 +01:00
|
|
|
// The format must be pre-processed (and lower cased) by Verilator.
|
2026-05-22 21:05:08 +02:00
|
|
|
// Arguments are each {"VFormatAttr character, int width, arg-value (or EData* if wide)"}
|
2008-07-01 20:15:10 +02:00
|
|
|
//
|
2026-03-15 03:43:56 +01:00
|
|
|
// Uses a single buffer internally; presumes only one usage per printf.
|
|
|
|
|
// Also assumes variables < 64 are not wide, this assumption is
|
2008-07-01 20:15:10 +02:00
|
|
|
// sometimes not true in low-level routines written here in verilated.cpp
|
2026-03-15 03:43:56 +01:00
|
|
|
|
|
|
|
|
// Look ahead at args to capture any %m/%t baseline information
|
|
|
|
|
char formatAttr = '\0'; // Fetched format for _next_ argument
|
|
|
|
|
bool formatAttrValid = false;
|
|
|
|
|
const char* modulep = nullptr;
|
|
|
|
|
const char* scopep = nullptr;
|
|
|
|
|
int timeunit = 0;
|
|
|
|
|
int argn = 0;
|
|
|
|
|
while (argn < argc) {
|
|
|
|
|
formatAttr = va_arg(ap, int); // Char promoted to int
|
|
|
|
|
switch (formatAttr) {
|
|
|
|
|
case VL_VFORMATATTR_TIMEUNIT:
|
|
|
|
|
++argn;
|
|
|
|
|
timeunit = va_arg(ap, int);
|
|
|
|
|
continue;
|
|
|
|
|
case VL_VFORMATATTR_SCOPE:
|
|
|
|
|
// No width
|
|
|
|
|
++argn;
|
|
|
|
|
modulep = va_arg(ap, const char*);
|
|
|
|
|
scopep = va_arg(ap, const char*);
|
|
|
|
|
continue;
|
|
|
|
|
default: // Normal arg; will consume formatAttr later
|
|
|
|
|
formatAttrValid = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse format
|
2026-03-26 18:55:14 +01:00
|
|
|
static thread_local std::string t_tmp;
|
2024-01-28 15:05:50 +01:00
|
|
|
std::string::const_iterator pctit = format.end(); // Most recent %##.##g format
|
2008-06-30 20:31:58 +02:00
|
|
|
bool inPct = false;
|
|
|
|
|
bool widthSet = false;
|
2020-01-15 13:32:45 +01:00
|
|
|
bool left = false;
|
2020-01-15 13:40:22 +01:00
|
|
|
size_t width = 0;
|
2026-06-03 10:54:02 +02:00
|
|
|
output.clear();
|
2026-03-15 03:43:56 +01:00
|
|
|
output.reserve(format.length());
|
2024-02-25 23:12:13 +01:00
|
|
|
for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!inPct && pos[0] == '%') {
|
2024-01-28 15:05:50 +01:00
|
|
|
pctit = pos;
|
2019-05-09 03:13:38 +02:00
|
|
|
inPct = true;
|
|
|
|
|
widthSet = false;
|
2018-11-29 01:59:10 +01:00
|
|
|
width = 0;
|
|
|
|
|
} else if (!inPct) { // Normal text
|
|
|
|
|
// Fast-forward to next escape and add to output
|
2024-01-28 15:05:50 +01:00
|
|
|
std::string::const_iterator ep = pos;
|
|
|
|
|
while (ep != format.end() && ep[0] != '%') ++ep;
|
2018-11-29 01:59:10 +01:00
|
|
|
if (ep != pos) {
|
2024-01-28 15:05:50 +01:00
|
|
|
output.append(pos, ep);
|
|
|
|
|
pos = ep - 1;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
|
|
|
|
} else { // Format character
|
|
|
|
|
inPct = false;
|
2026-03-15 03:43:56 +01:00
|
|
|
char fmt = std::tolower(pos[0]);
|
2019-05-09 03:13:38 +02:00
|
|
|
switch (fmt) {
|
2020-05-28 23:39:20 +02:00
|
|
|
case '0': // FALLTHRU
|
|
|
|
|
case '1': // FALLTHRU
|
|
|
|
|
case '2': // FALLTHRU
|
|
|
|
|
case '3': // FALLTHRU
|
|
|
|
|
case '4': // FALLTHRU
|
|
|
|
|
case '5': // FALLTHRU
|
|
|
|
|
case '6': // FALLTHRU
|
|
|
|
|
case '7': // FALLTHRU
|
|
|
|
|
case '8': // FALLTHRU
|
|
|
|
|
case '9':
|
2019-05-09 03:13:38 +02:00
|
|
|
inPct = true; // Get more digits
|
|
|
|
|
widthSet = true;
|
2020-04-14 04:51:35 +02:00
|
|
|
width = width * 10 + (fmt - '0');
|
2026-03-15 03:43:56 +01:00
|
|
|
continue;
|
2020-01-15 13:32:45 +01:00
|
|
|
case '-':
|
|
|
|
|
left = true;
|
|
|
|
|
inPct = true; // Get more digits
|
2026-03-15 03:43:56 +01:00
|
|
|
continue;
|
2019-05-09 03:13:38 +02:00
|
|
|
case '.':
|
|
|
|
|
inPct = true; // Get more digits
|
2026-03-15 03:43:56 +01:00
|
|
|
continue;
|
2020-04-14 04:51:35 +02:00
|
|
|
case '%': //
|
2019-05-09 03:13:38 +02:00
|
|
|
output += '%';
|
2026-03-15 03:43:56 +01:00
|
|
|
continue;
|
|
|
|
|
case 'l':
|
|
|
|
|
output += "----"; // Library - compile-time only
|
|
|
|
|
continue;
|
|
|
|
|
case 'm':
|
|
|
|
|
if (modulep) output += modulep;
|
|
|
|
|
if (modulep && modulep[0] && scopep && scopep[0]) output += '.';
|
|
|
|
|
if (scopep) output += scopep;
|
|
|
|
|
continue;
|
|
|
|
|
//--------
|
|
|
|
|
// Standard format handling -- all take arguments
|
|
|
|
|
case 'b': // FALLTHRU
|
|
|
|
|
case 'c': // FALLTHRU
|
|
|
|
|
case 'd': // FALLTHRU
|
|
|
|
|
case 'e': // FALLTHRU
|
|
|
|
|
case 'f': // FALLTHRU
|
|
|
|
|
case 'g': // FALLTHRU
|
|
|
|
|
case 'h': // FALLTHRU
|
|
|
|
|
case 'o': // FALLTHRU
|
|
|
|
|
case 'p': // FALLTHRU
|
|
|
|
|
case 's': // FALLTHRU
|
|
|
|
|
case 't': // FALLTHRU
|
|
|
|
|
case 'u': // FALLTHRU
|
|
|
|
|
case 'v': // FALLTHRU
|
|
|
|
|
case 'x': // FALLTHRU
|
|
|
|
|
case 'z': // FALLTHRU
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
2026-03-15 03:43:56 +01:00
|
|
|
//--------
|
|
|
|
|
default: // Bad escape, just print %letter so user sees it
|
|
|
|
|
output += '%';
|
|
|
|
|
output += fmt;
|
|
|
|
|
continue;
|
|
|
|
|
} // switch
|
|
|
|
|
|
|
|
|
|
// At this point only have escapes that expect arguments
|
|
|
|
|
if (++argn > argc) {
|
|
|
|
|
output += '%';
|
|
|
|
|
output += fmt;
|
|
|
|
|
continue; // Out of arguments
|
2025-03-11 18:32:34 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
if (!formatAttrValid) formatAttr = va_arg(ap, int); // char promoted to int
|
|
|
|
|
formatAttrValid = false;
|
|
|
|
|
|
|
|
|
|
// Process an argument
|
|
|
|
|
// Similar code flow in V3Number::displayed
|
|
|
|
|
int lbits = 0;
|
|
|
|
|
void* thingp = nullptr;
|
2026-06-03 20:55:00 +02:00
|
|
|
const std::string* enump = nullptr;
|
2026-03-15 03:43:56 +01:00
|
|
|
QData ld = 0;
|
2026-03-26 18:55:14 +01:00
|
|
|
std::vector<EData> strwide;
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataInP lwp{nullptr};
|
2026-03-15 03:43:56 +01:00
|
|
|
int lsb = 0;
|
|
|
|
|
double real = 0.0;
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_COMPLEX) { // printed as string
|
|
|
|
|
thingp = va_arg(ap, std::string*);
|
|
|
|
|
if (fmt != 'p') fmt = 's'; // Override
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_DOUBLE) {
|
|
|
|
|
real = va_arg(ap, double);
|
|
|
|
|
ld = VL_RTOIROUND_Q_D(real);
|
2026-03-26 18:55:14 +01:00
|
|
|
strwide.resize(2);
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
|
|
|
|
VL_SET_WQ(strwidep, ld);
|
|
|
|
|
lwp = strwidep;
|
2026-03-15 03:43:56 +01:00
|
|
|
lbits = 64;
|
|
|
|
|
// Not changint fmt == 'p' to fmt = 'g', as need fmts correct
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_STRING) {
|
|
|
|
|
thingp = va_arg(ap, std::string*);
|
|
|
|
|
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
|
2026-06-03 20:55:00 +02:00
|
|
|
} else if (formatAttr == VL_VFORMATATTR_ENUM) {
|
|
|
|
|
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
|
|
|
|
|
lbits = va_arg(ap, int);
|
|
|
|
|
ld = VL_VA_ARG_Q_(ap, lbits);
|
|
|
|
|
strwide.resize(2);
|
|
|
|
|
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
|
|
|
|
VL_SET_WQ(strwidep, ld);
|
|
|
|
|
lwp = strwidep;
|
|
|
|
|
lsb = lbits - 1;
|
|
|
|
|
++argn; // Enum value is followed by the generated name string argument
|
|
|
|
|
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
|
|
|
|
|
enump = va_arg(ap, std::string*);
|
|
|
|
|
if (enump && !enump->empty()) {
|
|
|
|
|
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
|
|
|
|
|
thingp = const_cast<std::string*>(enump);
|
|
|
|
|
} else if (fmt == 'p' && widthSet && width == 0) {
|
|
|
|
|
output += "'h";
|
|
|
|
|
fmt = 'h';
|
|
|
|
|
formatAttr = VL_VFORMATATTR_UNSIGNED;
|
|
|
|
|
} else {
|
|
|
|
|
if (fmt == 'p') width = 0;
|
|
|
|
|
widthSet = true;
|
|
|
|
|
fmt = 'd';
|
|
|
|
|
formatAttr = VL_VFORMATATTR_UNSIGNED;
|
|
|
|
|
}
|
|
|
|
|
if (widthSet && width == 0) {
|
|
|
|
|
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
|
|
|
|
|
}
|
2026-04-30 16:29:47 +02:00
|
|
|
} else { // Numeric
|
2026-03-15 03:43:56 +01:00
|
|
|
lbits = va_arg(ap, int);
|
2026-04-30 16:29:47 +02:00
|
|
|
if (lbits <= VL_QUADSIZE) {
|
|
|
|
|
ld = VL_VA_ARG_Q_(ap, lbits);
|
|
|
|
|
strwide.resize(2);
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
|
|
|
|
VL_SET_WQ(strwidep, ld);
|
|
|
|
|
lwp = strwidep;
|
2019-05-09 03:13:38 +02:00
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
lwp = WDataInP::external(va_arg(ap, EData*));
|
2026-06-03 20:55:00 +02:00
|
|
|
ld = VL_SET_QW(lwp); // Low 64 bits, for %c/%t
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
if (fmt == 'p') {
|
|
|
|
|
if (widthSet && width == 0) { // For %0p, IEEE our choice, use 'h%0h
|
|
|
|
|
output += "'h";
|
|
|
|
|
fmt = 'h';
|
|
|
|
|
} else { // UVM tests require %0d
|
|
|
|
|
widthSet = true;
|
|
|
|
|
width = 0;
|
|
|
|
|
fmt = 'd';
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-30 16:29:47 +02:00
|
|
|
lsb = lbits - 1;
|
2020-04-14 04:51:35 +02:00
|
|
|
if (widthSet && width == 0) {
|
|
|
|
|
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
|
|
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fmt may have been overridden above based on formatAttr datatype passed
|
|
|
|
|
switch (fmt) {
|
|
|
|
|
case 'c': {
|
|
|
|
|
const IData charval = ld & 0xff;
|
|
|
|
|
output += static_cast<char>(charval);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'e': // FALLTHRU
|
|
|
|
|
case 'f': // FALLTHRU
|
|
|
|
|
case 'g': {
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_SIGNED) {
|
|
|
|
|
real = VL_ISTOR_D_W(lbits, lwp);
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_UNSIGNED) {
|
|
|
|
|
real = VL_ITOR_D_W(lbits, lwp);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
const std::string fmts{pctit, pos + 1};
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_snprintf_string(t_tmp, fmts.c_str(), real);
|
2026-03-15 03:43:56 +01:00
|
|
|
output += t_tmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 's': {
|
|
|
|
|
if (thingp) { // VNumber::STRING Verilog 'string'
|
|
|
|
|
const std::string* const strp = static_cast<const std::string*>(thingp);
|
|
|
|
|
std::string padding;
|
|
|
|
|
if (width > strp->size()) padding.append(width - strp->size(), ' ');
|
|
|
|
|
output += left ? (*strp + padding) : (padding + *strp);
|
|
|
|
|
break;
|
2020-01-10 01:39:27 +01:00
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
// Number-based string
|
|
|
|
|
std::string field;
|
|
|
|
|
for (; lsb >= 0; --lsb) {
|
|
|
|
|
lsb = (lsb / 8) * 8; // Next digit
|
|
|
|
|
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
|
|
|
|
field += (charval == 0) ? ' ' : charval;
|
|
|
|
|
}
|
|
|
|
|
std::string padding;
|
|
|
|
|
if (width > field.size()) padding.append(width - field.size(), ' ');
|
|
|
|
|
output += left ? (field + padding) : (padding + field);
|
2026-03-15 03:43:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'p': { // Pattern
|
|
|
|
|
// 'p' with NUMBER was earlier converted to 'd'
|
|
|
|
|
if (formatAttr
|
|
|
|
|
== VL_VFORMATATTR_DOUBLE) { // Can't just change to 'g' as need fixed format
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_snprintf_string(t_tmp, "%g", real);
|
2026-03-15 03:43:56 +01:00
|
|
|
output += t_tmp;
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_STRING) {
|
|
|
|
|
const std::string* const strp = static_cast<const std::string*>(thingp);
|
|
|
|
|
output += '"' + *strp + '"';
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
|
|
|
|
|
const std::string* const strp = static_cast<const std::string*>(thingp);
|
|
|
|
|
output += *strp;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'd': { // Signed/unsigned decimal
|
|
|
|
|
int digits = 0;
|
|
|
|
|
std::string append;
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_SIGNED) {
|
2017-12-03 04:10:58 +01:00
|
|
|
if (lbits <= VL_QUADSIZE) {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(
|
|
|
|
|
t_tmp, "%" PRId64,
|
|
|
|
|
static_cast<int64_t>(VL_EXTENDS_QQ(lbits, lbits, ld)));
|
2020-12-02 01:01:20 +01:00
|
|
|
append = t_tmp;
|
2017-12-03 04:10:58 +01:00
|
|
|
} else {
|
2019-12-09 03:36:38 +01:00
|
|
|
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
|
2026-03-26 18:55:14 +01:00
|
|
|
std::vector<EData> neg(VL_WORDS_I(lbits));
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP negp = WDataOutP::external(neg.data());
|
|
|
|
|
VL_NEGATE_W(VL_WORDS_I(lbits), negp, lwp);
|
|
|
|
|
append = "-"s + VL_DECIMAL_NW(lbits, negp);
|
2017-12-03 04:10:58 +01:00
|
|
|
} else {
|
|
|
|
|
append = VL_DECIMAL_NW(lbits, lwp);
|
|
|
|
|
}
|
2024-01-28 20:47:14 +01:00
|
|
|
digits = static_cast<int>(append.length());
|
2017-12-03 04:10:58 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
} else { // Unsigned decimal
|
2017-12-03 04:10:58 +01:00
|
|
|
if (lbits <= VL_QUADSIZE) {
|
2026-03-26 18:55:14 +01:00
|
|
|
digits = _vl_snprintf_string(t_tmp, "%" PRIu64, ld);
|
2020-12-02 01:01:20 +01:00
|
|
|
append = t_tmp;
|
2017-12-03 04:10:58 +01:00
|
|
|
} else {
|
|
|
|
|
append = VL_DECIMAL_NW(lbits, lwp);
|
2024-01-28 20:47:14 +01:00
|
|
|
digits = static_cast<int>(append.length());
|
2017-12-03 04:10:58 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
|
|
|
|
if (!widthSet) {
|
|
|
|
|
const double mantissabits
|
|
|
|
|
= lbits - ((formatAttr == VL_VFORMATATTR_SIGNED) ? 1 : 0);
|
|
|
|
|
// This is log10(2**mantissabits) as log2(2**mantissabits)/log2(10),
|
|
|
|
|
// + 1.0 rounding bias.
|
|
|
|
|
double dchars = mantissabits / 3.321928094887362 + 1.0;
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_SIGNED) ++dchars; // space for sign
|
2026-03-28 04:14:18 +01:00
|
|
|
width = static_cast<int>(dchars);
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
|
|
|
|
const int needmore = static_cast<int>(width) - digits;
|
|
|
|
|
if (needmore > 0) {
|
|
|
|
|
std::string padding;
|
|
|
|
|
if (left) {
|
|
|
|
|
padding.append(needmore, ' '); // Pre-pad spaces
|
|
|
|
|
output += append + padding;
|
|
|
|
|
} else {
|
|
|
|
|
if (pctit != format.end() && pctit[0] && pctit[1] == '0') { // %0
|
|
|
|
|
padding.append(needmore, '0'); // Pre-pad zero
|
2022-10-19 03:10:35 +02:00
|
|
|
} else {
|
2026-03-15 03:43:56 +01:00
|
|
|
padding.append(needmore, ' '); // Pre-pad spaces
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
output += padding + append;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
} else {
|
|
|
|
|
output += append;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 't': { // Time
|
|
|
|
|
// Timeunit was read earlier from up-front arguments
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_DOUBLE) { // Realtime
|
|
|
|
|
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
|
|
|
|
output += _vl_vsformat_time(t_tmp, real, timeunit, left, width);
|
|
|
|
|
} else {
|
2021-03-07 17:01:54 +01:00
|
|
|
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
2021-05-16 12:01:03 +02:00
|
|
|
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'b': // FALLTHRU
|
|
|
|
|
case 'h': // FALLTHRU
|
|
|
|
|
case 'o': // FALLTHRU
|
|
|
|
|
case 'x': {
|
|
|
|
|
if (formatAttr == VL_VFORMATATTR_STRING) {
|
|
|
|
|
// V3Width errors on const %x of string, but V3Randomize may make a %x on a
|
|
|
|
|
// string, or may have a runtime format
|
|
|
|
|
const std::string* const strp = static_cast<const std::string*>(thingp);
|
2026-03-28 04:14:18 +01:00
|
|
|
const int chars = static_cast<int>(strp->size());
|
2026-03-15 03:43:56 +01:00
|
|
|
int truncFront = widthSet ? (chars - (static_cast<int>(width) / 2)) : 0;
|
|
|
|
|
if (truncFront < 0) truncFront = 0;
|
|
|
|
|
lbits = chars * 8;
|
2026-03-26 18:55:14 +01:00
|
|
|
strwide.resize(VL_WORDS_I(lbits));
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
|
|
|
|
lwp = strwidep;
|
2026-03-15 03:43:56 +01:00
|
|
|
lsb = lbits - 1;
|
2026-05-22 21:05:08 +02:00
|
|
|
VL_NTOI_W(lbits, strwidep, *strp, truncFront);
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
2022-10-19 03:10:35 +02:00
|
|
|
|
2026-03-15 03:43:56 +01:00
|
|
|
if (widthSet || left) {
|
|
|
|
|
lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp);
|
|
|
|
|
lsb = (lsb < 1) ? 0 : (lsb - 1);
|
|
|
|
|
}
|
2022-10-19 03:10:35 +02:00
|
|
|
|
2026-03-15 03:43:56 +01:00
|
|
|
std::string append;
|
|
|
|
|
int digits;
|
|
|
|
|
switch (fmt) {
|
|
|
|
|
case 'b': {
|
|
|
|
|
digits = lsb + 1;
|
|
|
|
|
for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
|
|
|
|
case 'o': {
|
|
|
|
|
digits = (lsb + 1 + 2) / 3;
|
|
|
|
|
for (; lsb >= 0; --lsb) {
|
|
|
|
|
lsb = (lsb / 3) * 3; // Next digit
|
|
|
|
|
// Octal numbers may span more than one wide word,
|
|
|
|
|
// so we need to grab each bit separately and check for overrun
|
|
|
|
|
// Octal is rare, so we'll do it a slow simple way
|
|
|
|
|
append += static_cast<char>(
|
|
|
|
|
'0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
|
|
|
|
|
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
|
|
|
|
|
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2020-05-18 14:10:32 +02:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
default: { // 'x'
|
|
|
|
|
digits = (lsb + 1 + 3) / 4;
|
|
|
|
|
for (; lsb >= 0; --lsb) {
|
|
|
|
|
lsb = (lsb / 4) * 4; // Next digit
|
|
|
|
|
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
|
|
|
|
|
append += "0123456789abcdef"[charval];
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2026-03-15 03:43:56 +01:00
|
|
|
}
|
2018-11-29 01:59:10 +01:00
|
|
|
} // switch
|
2026-03-15 03:43:56 +01:00
|
|
|
|
|
|
|
|
const int needmore = static_cast<int>(width) - digits;
|
|
|
|
|
if (needmore > 0) {
|
|
|
|
|
std::string padding;
|
|
|
|
|
if (left) {
|
|
|
|
|
padding.append(needmore, ' '); // Pre-pad spaces
|
|
|
|
|
output += append + padding;
|
|
|
|
|
} else {
|
|
|
|
|
padding.append(needmore, '0'); // Pre-pad zero
|
|
|
|
|
output += padding + append;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
output += append;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} // b / o / x
|
|
|
|
|
case 'u':
|
|
|
|
|
case 'z': { // Packed 4-state
|
|
|
|
|
const bool is_4_state = (fmt == 'z');
|
|
|
|
|
output.reserve(output.size() + ((is_4_state ? 2 : 1) * VL_WORDS_I(lbits)));
|
|
|
|
|
int bytes_to_go = VL_BYTES_I(lbits);
|
|
|
|
|
int bit = 0;
|
|
|
|
|
while (bytes_to_go > 0) {
|
|
|
|
|
const int wr_bytes = std::min(4, bytes_to_go);
|
|
|
|
|
for (int byte = 0; byte < wr_bytes; byte++, bit += 8)
|
|
|
|
|
output += static_cast<char>(VL_BITRSHIFT_W(lwp, bit) & 0xff);
|
|
|
|
|
output.append(4 - wr_bytes, static_cast<char>(0));
|
|
|
|
|
if (is_4_state) output.append(4, static_cast<char>(0));
|
|
|
|
|
bytes_to_go -= wr_bytes;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
case 'v': // Strength; assume always strong
|
|
|
|
|
for (lsb = lbits - 1; lsb >= 0; --lsb) {
|
|
|
|
|
if (VL_BITRSHIFT_W(lwp, lsb) & 1) {
|
|
|
|
|
output += "St1 ";
|
|
|
|
|
} else {
|
|
|
|
|
output += "St0 ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default: { // LCOV_EXCL_START
|
|
|
|
|
VL_DEBUG_IFDEF(assert(0);); // Missing case between this case, and one above
|
|
|
|
|
break;
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2018-11-29 01:59:10 +01:00
|
|
|
} // switch
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 14:17:38 +02:00
|
|
|
static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
|
2021-07-25 19:38:27 +02:00
|
|
|
if (VL_LIKELY(fp)) {
|
2021-03-27 02:23:18 +01:00
|
|
|
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
|
2020-04-06 00:30:46 +02:00
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
return floc < 0;
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
2021-07-25 19:38:27 +02:00
|
|
|
if (VL_LIKELY(fp)) {
|
2026-03-28 04:14:18 +01:00
|
|
|
(void)std::fgetc(fp);
|
2020-04-14 04:51:35 +02:00
|
|
|
} else {
|
|
|
|
|
floc -= 8;
|
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
|
|
|
|
const std::string& fstr) VL_MT_SAFE {
|
2008-07-01 20:15:10 +02:00
|
|
|
// Get a character without advancing
|
2021-07-25 19:38:27 +02:00
|
|
|
if (VL_LIKELY(fp)) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const int data = std::fgetc(fp);
|
2019-05-09 03:13:38 +02:00
|
|
|
if (data == EOF) return EOF;
|
2026-03-28 04:14:18 +01:00
|
|
|
ungetc(data, fp); // No (void), might be macro
|
2019-05-09 03:13:38 +02:00
|
|
|
return data;
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
if (floc < 0) return EOF;
|
|
|
|
|
floc = floc & ~7; // Align to closest character
|
|
|
|
|
if (!fromp) return fstr[fstr.length() - 1 - (floc >> 3)];
|
|
|
|
|
return VL_BITRSHIFT_W(fromp, floc) & 0xff;
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
|
|
|
|
const std::string& fstr) VL_MT_SAFE {
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2026-05-31 23:25:28 +02:00
|
|
|
if (c == EOF || !(std::isspace(c) || c == '\0')) return;
|
2019-05-09 03:13:38 +02:00
|
|
|
_vl_vsss_advance(fp, floc);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
|
2026-03-26 18:55:14 +01:00
|
|
|
std::back_insert_iterator<std::string> tmpp,
|
|
|
|
|
const char* acceptp) VL_MT_SAFE {
|
2008-07-01 20:15:10 +02:00
|
|
|
// Read into tmp, consisting of characters from acceptp list
|
2026-03-26 18:55:14 +01:00
|
|
|
auto cp = tmpp;
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2019-05-09 03:13:38 +02:00
|
|
|
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2021-03-27 02:23:18 +01:00
|
|
|
if (c == EOF || std::isspace(c)) break;
|
|
|
|
|
if (acceptp && nullptr == std::strchr(acceptp, c)) break; // String - allow anything
|
|
|
|
|
if (acceptp) c = std::tolower(c); // Non-strings we'll simplify
|
2019-05-09 03:13:38 +02:00
|
|
|
*cp++ = c;
|
|
|
|
|
_vl_vsss_advance(fp, floc);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
|
2022-12-16 16:14:02 +01:00
|
|
|
char* beginp, std::size_t n,
|
|
|
|
|
const bool inhibit = false) VL_MT_SAFE {
|
2020-05-18 14:10:32 +02:00
|
|
|
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
|
|
|
|
|
// specifically for block reads of N bytes (read operations are not demarcated by
|
|
|
|
|
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
|
|
|
|
|
while (n-- > 0) {
|
|
|
|
|
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2020-08-15 16:12:55 +02:00
|
|
|
if (c == EOF) return nullptr;
|
2020-05-18 14:10:32 +02:00
|
|
|
if (!inhibit) *beginp++ = c;
|
|
|
|
|
_vl_vsss_advance(fp, floc);
|
|
|
|
|
}
|
|
|
|
|
return beginp;
|
|
|
|
|
}
|
2022-12-16 04:00:21 +01:00
|
|
|
static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
|
|
|
|
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
|
|
|
|
|
size_t posend) VL_MT_SAFE {
|
2009-11-19 23:04:21 +01:00
|
|
|
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
2022-12-16 04:00:21 +01:00
|
|
|
VL_ZERO_W(obits, owp);
|
2009-11-19 23:04:21 +01:00
|
|
|
int lsb = 0;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0, pos = static_cast<int>(posend) - 1;
|
2026-03-28 02:43:11 +01:00
|
|
|
i < obits && pos >= static_cast<int>(posstart); --pos, ++i) {
|
2020-04-14 04:51:35 +02:00
|
|
|
// clang-format off
|
2026-03-15 03:43:56 +01:00
|
|
|
switch (std::tolower (strp[pos])) {
|
2018-11-29 01:59:10 +01:00
|
|
|
case 'x': case 'z': case '?': // FALLTHRU
|
|
|
|
|
case '0': lsb += baseLog2; break;
|
2020-04-14 04:51:35 +02:00
|
|
|
case '1': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 1); lsb += baseLog2; break;
|
|
|
|
|
case '2': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 2); lsb += baseLog2; break;
|
|
|
|
|
case '3': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 3); lsb += baseLog2; break;
|
|
|
|
|
case '4': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 4); lsb += baseLog2; break;
|
|
|
|
|
case '5': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 5); lsb += baseLog2; break;
|
|
|
|
|
case '6': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 6); lsb += baseLog2; break;
|
|
|
|
|
case '7': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 7); lsb += baseLog2; break;
|
|
|
|
|
case '8': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 8); lsb += baseLog2; break;
|
|
|
|
|
case '9': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 9); lsb += baseLog2; break;
|
|
|
|
|
case 'a': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 10); lsb += baseLog2; break;
|
|
|
|
|
case 'b': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 11); lsb += baseLog2; break;
|
|
|
|
|
case 'c': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 12); lsb += baseLog2; break;
|
|
|
|
|
case 'd': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 13); lsb += baseLog2; break;
|
|
|
|
|
case 'e': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 14); lsb += baseLog2; break;
|
|
|
|
|
case 'f': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 15); lsb += baseLog2; break;
|
2026-03-28 02:43:11 +01:00
|
|
|
default: break;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
// clang-format on
|
2009-11-19 23:04:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
|
|
|
|
|
IData _vl_vsscanf(FILE* fp, // If a fscanf
|
2021-06-19 04:19:35 +02:00
|
|
|
int fbits, const WDataInP fromp, // Else if a sscanf
|
2019-05-09 03:13:38 +02:00
|
|
|
const std::string& fstr, // if a sscanf to string
|
2026-03-15 03:43:56 +01:00
|
|
|
const std::string& format, int argc, va_list ap) VL_MT_SAFE {
|
2008-07-01 20:15:10 +02:00
|
|
|
// Read a Verilog $sscanf/$fscanf style format into the output list
|
|
|
|
|
// The format must be pre-processed (and lower cased) by Verilator
|
2026-05-22 21:05:08 +02:00
|
|
|
// Arguments are in "width, arg-value (or EData* if wide)" form
|
2026-03-26 18:55:14 +01:00
|
|
|
static thread_local std::string t_tmp;
|
2008-07-01 20:15:10 +02:00
|
|
|
int floc = fbits - 1;
|
|
|
|
|
IData got = 0;
|
|
|
|
|
bool inPct = false;
|
2020-05-12 04:13:59 +02:00
|
|
|
bool inIgnore = false;
|
2026-03-15 03:43:56 +01:00
|
|
|
int argn = 0;
|
|
|
|
|
|
|
|
|
|
char formatAttr = '\0'; // Fetched format for _next_ argument
|
|
|
|
|
bool formatAttrValid = false;
|
|
|
|
|
int timeunit = 0;
|
|
|
|
|
while (argn < argc) {
|
|
|
|
|
formatAttr = va_arg(ap, int); // Char promoted to int
|
|
|
|
|
switch (formatAttr) {
|
|
|
|
|
case VL_VFORMATATTR_TIMEUNIT:
|
|
|
|
|
++argn;
|
|
|
|
|
timeunit = va_arg(ap, int);
|
|
|
|
|
continue;
|
|
|
|
|
default: // Normal arg; will consume formatAttr later
|
|
|
|
|
formatAttrValid = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-25 23:12:13 +01:00
|
|
|
std::string::const_iterator pos = format.cbegin();
|
2025-03-25 13:28:36 +01:00
|
|
|
for (; pos != format.cend(); ++pos) {
|
2026-03-26 18:55:14 +01:00
|
|
|
t_tmp.clear();
|
2025-03-25 13:28:36 +01:00
|
|
|
// VL_DBG_MSGF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc,
|
|
|
|
|
// _vl_vsss_peek(fp, floc, fromp, fstr));
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!inPct && pos[0] == '%') {
|
2019-05-09 03:13:38 +02:00
|
|
|
inPct = true;
|
2020-05-12 04:13:59 +02:00
|
|
|
inIgnore = false;
|
2021-03-27 02:23:18 +01:00
|
|
|
} else if (!inPct && std::isspace(pos[0])) { // Format spaces
|
2024-03-08 01:20:00 +01:00
|
|
|
while (std::isspace(pos[1])) ++pos;
|
2019-05-09 03:13:38 +02:00
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2018-11-29 01:59:10 +01:00
|
|
|
} else if (!inPct) { // Expected Format
|
2019-05-09 03:13:38 +02:00
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2021-11-25 15:05:50 +01:00
|
|
|
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2018-11-29 01:59:10 +01:00
|
|
|
if (c != pos[0]) goto done;
|
2020-04-14 04:51:35 +02:00
|
|
|
_vl_vsss_advance(fp, floc);
|
2018-11-29 01:59:10 +01:00
|
|
|
} else { // Format character
|
2019-05-09 03:13:38 +02:00
|
|
|
// Skip loading spaces
|
|
|
|
|
inPct = false;
|
2026-03-15 03:43:56 +01:00
|
|
|
const char fmt = std::tolower(pos[0]);
|
2019-05-09 03:13:38 +02:00
|
|
|
switch (fmt) {
|
|
|
|
|
case '%': {
|
2021-11-25 15:05:50 +01:00
|
|
|
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2019-05-09 03:13:38 +02:00
|
|
|
if (c != '%') goto done;
|
2020-04-14 04:51:35 +02:00
|
|
|
_vl_vsss_advance(fp, floc);
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2025-09-20 16:26:36 +02:00
|
|
|
case '0': // FALLTHRU
|
|
|
|
|
case '1': // FALLTHRU
|
|
|
|
|
case '2': // FALLTHRU
|
|
|
|
|
case '3': // FALLTHRU
|
|
|
|
|
case '4': // FALLTHRU
|
|
|
|
|
case '5': // FALLTHRU
|
|
|
|
|
case '6': // FALLTHRU
|
|
|
|
|
case '7': // FALLTHRU
|
|
|
|
|
case '8': // FALLTHRU
|
|
|
|
|
case '9': {
|
|
|
|
|
inPct = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-05-12 04:13:59 +02:00
|
|
|
case '*':
|
|
|
|
|
inPct = true;
|
|
|
|
|
inIgnore = true;
|
|
|
|
|
break;
|
2019-05-09 03:13:38 +02:00
|
|
|
default: {
|
|
|
|
|
// Deal with all read-and-scan somethings
|
|
|
|
|
// Note LSBs are preserved if there's an overflow
|
2026-03-15 03:43:56 +01:00
|
|
|
if (!inIgnore && (++argn > argc)) inIgnore = true; // Overflowed arguments
|
|
|
|
|
if (!inIgnore) {
|
|
|
|
|
if (!formatAttrValid) formatAttr = va_arg(ap, int); // char promoted to int
|
|
|
|
|
formatAttrValid = false;
|
|
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
const int obits = (!inIgnore
|
|
|
|
|
&& (formatAttr == VL_VFORMATATTR_UNSIGNED
|
|
|
|
|
|| formatAttr == VL_VFORMATATTR_SIGNED))
|
|
|
|
|
? va_arg(ap, int)
|
|
|
|
|
: 0;
|
2026-03-15 03:43:56 +01:00
|
|
|
void* const thingp = inIgnore ? nullptr : va_arg(ap, void*);
|
|
|
|
|
double real = 0;
|
|
|
|
|
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> qowp;
|
2020-05-29 02:32:07 +02:00
|
|
|
VL_SET_WQ(qowp, 0ULL);
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataOutP owp = WDataOutP::external((obits <= 64) ? qowp.data()
|
|
|
|
|
: static_cast<EData*>(thingp));
|
2020-10-28 00:37:12 +01:00
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
|
2026-03-26 18:55:14 +01:00
|
|
|
t_tmp.clear();
|
2019-05-09 03:13:38 +02:00
|
|
|
switch (fmt) {
|
|
|
|
|
case 'c': {
|
2021-11-25 15:05:50 +01:00
|
|
|
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (c == EOF) goto done;
|
|
|
|
|
_vl_vsss_advance(fp, floc);
|
2019-05-09 03:13:38 +02:00
|
|
|
owp[0] = c;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 's': {
|
|
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp}, nullptr);
|
2020-12-02 01:01:20 +01:00
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-26 18:55:14 +01:00
|
|
|
int lpos = (static_cast<int>(t_tmp.size())) - 1;
|
2026-03-15 03:43:56 +01:00
|
|
|
int lsb = 0;
|
|
|
|
|
for (int i = 0; i < obits && lpos >= 0; --lpos) {
|
|
|
|
|
_vl_vsss_setbit(owp, obits, lsb, 8, t_tmp[lpos]);
|
|
|
|
|
lsb += 8;
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
break;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
case 'd': { // Signed/unsigned decimal
|
2019-05-09 03:13:38 +02:00
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp},
|
|
|
|
|
"0123456789+-xXzZ?_");
|
2020-12-02 01:01:20 +01:00
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-15 03:43:56 +01:00
|
|
|
if (formatAttr == VL_VFORMATATTR_SIGNED) {
|
|
|
|
|
QData ld = 0;
|
2026-03-26 18:55:14 +01:00
|
|
|
std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld);
|
2026-03-15 03:43:56 +01:00
|
|
|
VL_SET_WQ(owp, ld);
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_UNSIGNED) {
|
|
|
|
|
int64_t ld = 0;
|
2026-03-26 18:55:14 +01:00
|
|
|
std::sscanf(t_tmp.c_str(), "%30" PRId64, &ld);
|
2026-03-15 03:43:56 +01:00
|
|
|
VL_SET_WQ(owp, ld);
|
|
|
|
|
}
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'f':
|
2018-11-29 01:59:10 +01:00
|
|
|
case 'e':
|
|
|
|
|
case 'g': { // Real number
|
2019-05-09 03:13:38 +02:00
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp},
|
|
|
|
|
"+-.0123456789eE");
|
2020-12-02 01:01:20 +01:00
|
|
|
if (!t_tmp[0]) goto done;
|
2020-04-14 04:51:35 +02:00
|
|
|
union {
|
|
|
|
|
double r;
|
2022-03-27 21:27:40 +02:00
|
|
|
int64_t ld;
|
2020-04-14 04:51:35 +02:00
|
|
|
} u;
|
2026-03-26 18:55:14 +01:00
|
|
|
real = std::strtod(t_tmp.c_str(), nullptr);
|
2026-03-15 03:43:56 +01:00
|
|
|
u.r = real;
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_SET_WQ(owp, u.ld);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-01 20:40:22 +02:00
|
|
|
case 't': { // Time
|
|
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp},
|
|
|
|
|
"+-.0123456789eE");
|
2025-09-01 20:40:22 +02:00
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-15 03:43:56 +01:00
|
|
|
// Timeunit was read earlier from up-front arguments
|
|
|
|
|
const int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits();
|
|
|
|
|
// 0..-15
|
2025-09-01 20:40:22 +02:00
|
|
|
const int shift = -userUnits + timeunit; // 0..-15
|
2026-03-26 18:55:14 +01:00
|
|
|
real = std::strtod(t_tmp.c_str(), nullptr) * vl_time_multiplier(-shift);
|
2026-03-15 03:43:56 +01:00
|
|
|
VL_SET_WQ(owp, static_cast<uint64_t>(real));
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'b': {
|
|
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp}, "01xXzZ?_");
|
2020-12-02 01:01:20 +01:00
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_based(owp, obits, 1, t_tmp.c_str(), 0, t_tmp.size());
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'o': {
|
|
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp},
|
|
|
|
|
"01234567xXzZ?_");
|
2020-12-02 01:01:20 +01:00
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_based(owp, obits, 3, t_tmp.c_str(), 0, t_tmp.size());
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
case 'h': // FALLTHRU
|
2019-05-09 03:13:38 +02:00
|
|
|
case 'x': {
|
|
|
|
|
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
|
|
|
|
std::back_insert_iterator<std::string>{t_tmp},
|
2020-12-02 01:01:20 +01:00
|
|
|
"0123456789abcdefABCDEFxXzZ?_");
|
|
|
|
|
if (!t_tmp[0]) goto done;
|
2026-03-26 18:55:14 +01:00
|
|
|
_vl_vsss_based(owp, obits, 4, t_tmp.c_str(), 0, t_tmp.size());
|
2019-05-09 03:13:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2020-05-18 14:10:32 +02:00
|
|
|
case 'u': {
|
|
|
|
|
// Read packed 2-value binary data
|
|
|
|
|
const int bytes = VL_BYTES_I(obits);
|
2026-05-22 21:05:08 +02:00
|
|
|
char* const out = reinterpret_cast<char*>(owp.datap());
|
2020-05-18 14:10:32 +02:00
|
|
|
if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done;
|
|
|
|
|
const int last = bytes % 4;
|
|
|
|
|
if (last != 0
|
|
|
|
|
&& !_vl_vsss_read_bin(fp, floc, fromp, fstr, out, 4 - last, true))
|
|
|
|
|
goto done;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'z': {
|
|
|
|
|
// Read packed 4-value binary data
|
2026-05-22 21:05:08 +02:00
|
|
|
char* out = reinterpret_cast<char*>(owp.datap());
|
2020-05-18 14:10:32 +02:00
|
|
|
int bytes = VL_BYTES_I(obits);
|
|
|
|
|
while (bytes > 0) {
|
|
|
|
|
const int abytes = std::min(4, bytes);
|
|
|
|
|
// aval (4B) read {0, 1} state
|
|
|
|
|
out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, abytes);
|
|
|
|
|
if (!out) goto done;
|
|
|
|
|
// bval (4B) disregard {X, Z} state and align to new 8B boundary.
|
|
|
|
|
out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, 8 - abytes, true);
|
|
|
|
|
if (!out) goto done;
|
|
|
|
|
bytes -= abytes;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-19 03:27:36 +02:00
|
|
|
default: { // LCOV_EXCL_START
|
2024-01-29 02:24:28 +01:00
|
|
|
const std::string msg = "Unknown _vl_vsscanf code: "s + pos[0];
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
2018-11-29 01:59:10 +01:00
|
|
|
break;
|
2020-09-19 03:27:36 +02:00
|
|
|
} // LCOV_EXCL_STOP
|
|
|
|
|
|
2018-11-29 01:59:10 +01:00
|
|
|
} // switch
|
2008-07-01 20:15:10 +02:00
|
|
|
|
2020-05-12 04:13:59 +02:00
|
|
|
if (!inIgnore) ++got;
|
2019-05-09 03:13:38 +02:00
|
|
|
// Reload data if non-wide (if wide, we put it in the right place directly)
|
2026-03-15 03:43:56 +01:00
|
|
|
if (inIgnore) {
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_DOUBLE) {
|
|
|
|
|
double* const p = static_cast<double*>(thingp);
|
|
|
|
|
*p = real;
|
|
|
|
|
} else if (formatAttr == VL_VFORMATATTR_STRING) {
|
|
|
|
|
std::string* const p = static_cast<std::string*>(thingp);
|
2020-12-02 01:01:20 +01:00
|
|
|
*p = t_tmp;
|
2020-05-12 04:13:59 +02:00
|
|
|
} else if (obits <= VL_BYTESIZE) {
|
2026-03-15 03:43:56 +01:00
|
|
|
CData* const p = static_cast<CData*>(thingp);
|
2023-05-07 14:25:10 +02:00
|
|
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
2019-05-09 03:13:38 +02:00
|
|
|
} else if (obits <= VL_SHORTSIZE) {
|
2026-03-15 03:43:56 +01:00
|
|
|
SData* const p = static_cast<SData*>(thingp);
|
2023-05-07 14:25:10 +02:00
|
|
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
2019-12-09 03:36:38 +01:00
|
|
|
} else if (obits <= VL_IDATASIZE) {
|
2026-03-15 03:43:56 +01:00
|
|
|
IData* const p = static_cast<IData*>(thingp);
|
2023-05-07 14:25:10 +02:00
|
|
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
2019-05-09 03:13:38 +02:00
|
|
|
} else if (obits <= VL_QUADSIZE) {
|
2026-03-15 03:43:56 +01:00
|
|
|
QData* const p = static_cast<QData*>(thingp);
|
2023-05-07 14:25:10 +02:00
|
|
|
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
|
|
|
|
|
} else {
|
|
|
|
|
_vl_clean_inplace_w(obits, owp);
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-11-29 01:59:10 +01:00
|
|
|
} // switch
|
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2025-03-25 13:28:36 +01:00
|
|
|
// Processed all arguments
|
|
|
|
|
return got;
|
|
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
done:
|
2025-03-25 13:28:36 +01:00
|
|
|
// Scan stopped early, return parsed or EOF
|
|
|
|
|
if (_vl_vsss_eof(fp, floc)) return -1;
|
2008-07-01 20:15:10 +02:00
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// File I/O
|
|
|
|
|
|
2020-05-15 00:03:00 +02:00
|
|
|
FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
|
2020-05-28 23:59:18 +02:00
|
|
|
// Expected non-MCD case; returns null on MCD descriptors.
|
2021-03-07 17:01:54 +01:00
|
|
|
return Verilated::threadContextp()->impp()->fdToFp(lhs);
|
2020-05-15 00:03:00 +02:00
|
|
|
}
|
2011-07-01 19:41:21 +02:00
|
|
|
|
2021-06-19 04:19:35 +02:00
|
|
|
void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT_SAFE {
|
2010-01-17 21:10:37 +01:00
|
|
|
// See also VL_DATA_TO_STRING_NW
|
2020-04-14 04:51:35 +02:00
|
|
|
int lsb = obits - 1;
|
|
|
|
|
bool start = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
char* destp = destoutp;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (; lsb >= 0; --lsb) {
|
2018-11-29 01:59:10 +01:00
|
|
|
lsb = (lsb / 8) * 8; // Next digit
|
2021-11-25 15:05:50 +01:00
|
|
|
const IData charval = VL_BITRSHIFT_W(sourcep, lsb) & 0xff;
|
2018-11-29 01:59:10 +01:00
|
|
|
if (!start || charval) {
|
2020-04-14 04:51:35 +02:00
|
|
|
*destp++ = (charval == 0) ? ' ' : charval;
|
2018-11-29 01:59:10 +01:00
|
|
|
start = false; // Drop leading 0s
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-11-29 01:59:10 +01:00
|
|
|
*destp = '\0'; // Terminate
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!start) { // Drop trailing spaces
|
2021-03-27 02:23:18 +01:00
|
|
|
while (std::isspace(*(destp - 1)) && destp > destoutp) *--destp = '\0';
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE {
|
2008-06-30 13:10:23 +02:00
|
|
|
// Convert C string to Verilog format
|
2021-06-19 04:19:35 +02:00
|
|
|
const size_t bytes = VL_BYTES_I(obits);
|
2017-10-07 21:01:19 +02:00
|
|
|
char* op = reinterpret_cast<char*>(destp);
|
2008-06-30 13:10:23 +02:00
|
|
|
if (srclen > bytes) srclen = bytes; // Don't overflow destination
|
2020-06-02 05:16:02 +02:00
|
|
|
size_t i = 0;
|
2024-03-27 22:57:49 +01:00
|
|
|
for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i];
|
|
|
|
|
for (; i < bytes; ++i) *op++ = 0;
|
2008-06-30 13:10:23 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-28 00:37:12 +01:00
|
|
|
static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
|
|
|
|
|
str.clear();
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
// While threadsafe, each thread can only access different file handles
|
2021-06-19 04:19:35 +02:00
|
|
|
FILE* const fp = VL_CVT_I_FP(fpi);
|
2008-06-30 20:31:58 +02:00
|
|
|
if (VL_UNLIKELY(!fp)) return 0;
|
2008-06-28 02:04:20 +02:00
|
|
|
|
|
|
|
|
// We don't use fgets, as we must read \0s.
|
2020-10-28 00:37:12 +01:00
|
|
|
while (str.size() < maxLen) {
|
|
|
|
|
const int c = getc(fp); // getc() is threadsafe
|
2020-04-14 04:51:35 +02:00
|
|
|
if (c == EOF) break;
|
2020-10-28 00:37:12 +01:00
|
|
|
str.push_back(c);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (c == '\n') break;
|
2008-06-28 02:04:20 +02:00
|
|
|
}
|
2022-12-22 18:19:09 +01:00
|
|
|
return static_cast<IData>(str.size());
|
2020-10-28 00:37:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
|
|
|
|
std::string str;
|
|
|
|
|
const IData bytes = VL_BYTES_I(obits);
|
2021-06-19 04:19:35 +02:00
|
|
|
const IData got = getLine(str, fpi, bytes);
|
2020-10-28 00:37:12 +01:00
|
|
|
|
|
|
|
|
if (VL_UNLIKELY(str.empty())) return 0;
|
2008-06-28 02:04:20 +02:00
|
|
|
|
2021-08-24 03:13:33 +02:00
|
|
|
// V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe
|
2020-10-28 00:37:12 +01:00
|
|
|
if (VL_UNCOVERABLE(bytes < str.size())) {
|
|
|
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, destp, got, str.data());
|
2008-06-28 02:04:20 +02:00
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-11 03:40:14 +01:00
|
|
|
IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
|
|
|
|
|
return getLine(dest, fpi, std::numeric_limits<size_t>::max());
|
2020-10-28 00:37:12 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-05 17:22:05 +02:00
|
|
|
IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
|
|
|
|
|
// We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
|
2021-06-19 04:19:35 +02:00
|
|
|
const IData ret = errno;
|
2021-07-24 14:36:11 +02:00
|
|
|
outputr = std::string{::std::strerror(ret)};
|
2020-04-05 17:22:05 +02:00
|
|
|
return ret;
|
|
|
|
|
}
|
2023-02-05 16:18:03 +01:00
|
|
|
IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE {
|
|
|
|
|
std::string output;
|
|
|
|
|
const IData ret = VL_FERROR_IN(fpi, output /*ref*/);
|
2026-05-22 21:05:08 +02:00
|
|
|
_vl_string_to_vint(obits, outwp.datap(), output.length(), output.c_str());
|
2023-02-05 16:18:03 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
2020-04-05 17:22:05 +02:00
|
|
|
|
2020-05-15 00:03:00 +02:00
|
|
|
IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) {
|
2021-03-07 17:01:54 +01:00
|
|
|
return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str());
|
2015-07-22 00:36:27 +02:00
|
|
|
}
|
2020-05-15 00:03:00 +02:00
|
|
|
IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE {
|
2021-03-07 17:01:54 +01:00
|
|
|
return Verilated::threadContextp()->impp()->fdNewMcd(filename.c_str());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-05-15 00:03:00 +02:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdFlush(fdi); }
|
2020-05-15 00:03:00 +02:00
|
|
|
IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE {
|
2021-03-07 17:01:54 +01:00
|
|
|
return Verilated::threadContextp()->impp()->fdSeek(fdi, offset, origin);
|
2020-05-15 00:03:00 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return Verilated::threadContextp()->impp()->fdTell(fdi); }
|
2017-10-27 02:05:42 +02:00
|
|
|
void VL_FCLOSE_I(IData fdi) VL_MT_SAFE {
|
|
|
|
|
// While threadsafe, each thread can only access different file handles
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->impp()->fdClose(fdi);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-03 10:54:02 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// String formatting functions taking const std::string& as format string
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2014-11-27 18:32:41 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2014-11-27 18:32:41 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
2014-11-27 18:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2014-11-27 18:32:41 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2014-11-27 18:32:41 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
2014-11-27 18:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2014-11-27 18:32:41 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2014-11-27 18:32:41 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
2014-11-27 18:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2014-11-27 18:32:41 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2014-11-27 18:32:41 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
2014-11-27 18:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-22 21:05:08 +02:00
|
|
|
void VL_SFORMAT_NX(int obits, EData* destp, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2009-11-24 03:24:55 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2009-11-24 03:24:55 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str());
|
2009-11-24 03:24:55 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-03 09:42:48 +02:00
|
|
|
void VL_SFORMAT_NX(std::string& output, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-07-25 17:36:34 +02:00
|
|
|
std::string temp_output;
|
2013-08-15 03:37:13 +02:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(temp_output, format, argc, ap);
|
2013-08-15 03:37:13 +02:00
|
|
|
va_end(ap);
|
2022-07-25 17:36:34 +02:00
|
|
|
output = temp_output;
|
2013-08-15 03:37:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE {
|
2026-06-03 10:54:02 +02:00
|
|
|
std::string output;
|
2010-01-18 01:13:44 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-06-03 10:54:02 +02:00
|
|
|
_vl_vsformat(output, format, argc, ap);
|
2010-01-18 01:13:44 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2026-06-03 10:54:02 +02:00
|
|
|
return output;
|
2010-01-18 01:13:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE {
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2008-06-30 20:31:58 +02:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2008-06-30 20:31:58 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2020-12-02 01:01:20 +01:00
|
|
|
VL_PRINTF_MT("%s", t_output.c_str());
|
2008-06-30 20:31:58 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2017-10-27 02:05:42 +02:00
|
|
|
// While threadsafe, each thread can only access different file handles
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local std::string t_output; // static only for speed
|
2008-06-30 20:31:58 +02:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
_vl_vsformat(t_output, format, argc, ap);
|
2008-06-30 20:31:58 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->impp()->fdWrite(fpi, t_output);
|
2008-06-30 20:31:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-03 10:54:02 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// String formatting functions taking const char* as format string
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(int obits, CData& destr, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(int obits, SData& destr, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(int obits, IData& destr, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(int obits, QData& destr, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
_vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(int obits, EData* destp, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
_vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_SFORMAT_NX(std::string& output, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
std::string temp_output;
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(temp_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
output = temp_output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string VL_SFORMATF_N_NX(const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
std::string output;
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_WRITEF_NX(const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
VL_PRINTF_MT("%s", t_output.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_FWRITEF_NX(IData fpi, const char* formatp, int argc, ...) VL_MT_SAFE {
|
|
|
|
|
// While threadsafe, each thread can only access different file handles
|
|
|
|
|
static thread_local std::string t_output; // static only for speed
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, argc);
|
|
|
|
|
_vl_vsformat(t_output, formatp, argc, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
Verilated::threadContextp()->impp()->fdWrite(fpi, t_output);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2017-10-27 02:05:42 +02:00
|
|
|
// While threadsafe, each thread can only access different file handles
|
2021-06-19 04:19:35 +02:00
|
|
|
FILE* const fp = VL_CVT_I_FP(fpi);
|
2022-03-07 23:43:33 +01:00
|
|
|
if (VL_UNLIKELY(!fp)) return ~0U; // -1
|
2008-07-01 20:15:10 +02:00
|
|
|
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataInP fromp{nullptr};
|
2008-07-01 20:15:10 +02:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-05-22 21:05:08 +02:00
|
|
|
const IData got = _vl_vsscanf(fp, 0, fromp, "", format, argc, ap);
|
2008-07-01 20:15:10 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-28 17:05:38 +01:00
|
|
|
IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> fnw;
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_SET_WI(fnw, ld);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
|
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, argc, ap);
|
2008-07-01 20:15:10 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
2024-01-28 17:05:38 +01:00
|
|
|
IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int argc, ...) VL_MT_SAFE {
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> fnw;
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_SET_WQ(fnw, ld);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
|
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, argc, ap);
|
2008-07-01 20:15:10 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
2024-01-28 17:05:38 +01:00
|
|
|
IData VL_SSCANF_IWNX(int lbits, const WDataInP lwp, const std::string& format, int argc,
|
|
|
|
|
...) VL_MT_SAFE {
|
2008-07-01 20:15:10 +02:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2026-03-15 03:43:56 +01:00
|
|
|
const IData got = _vl_vsscanf(nullptr, lbits, lwp, "", format, argc, ap);
|
2014-12-25 03:50:38 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
2024-01-28 17:05:38 +01:00
|
|
|
IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int argc,
|
|
|
|
|
...) VL_MT_SAFE {
|
2026-05-22 21:05:08 +02:00
|
|
|
WDataInP fromp{nullptr};
|
2014-12-25 03:50:38 +01:00
|
|
|
va_list ap;
|
2024-01-28 17:05:38 +01:00
|
|
|
va_start(ap, argc);
|
2024-03-24 15:19:26 +01:00
|
|
|
const IData got
|
2026-05-22 21:05:08 +02:00
|
|
|
= _vl_vsscanf(nullptr, static_cast<int>(ld.length() * 8), fromp, ld, format, argc, ap);
|
2008-07-01 20:15:10 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-27 15:31:55 +02:00
|
|
|
// MurmurHash64A
|
|
|
|
|
uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE {
|
|
|
|
|
const size_t len = strlen(key);
|
|
|
|
|
const uint64_t seed = 0;
|
|
|
|
|
const uint64_t m = 0xc6a4a7935bd1e995ULL;
|
|
|
|
|
const int r = 47;
|
|
|
|
|
|
|
|
|
|
uint64_t h = seed ^ (len * m);
|
|
|
|
|
|
2025-08-22 02:06:43 +02:00
|
|
|
const uint64_t* data = reinterpret_cast<const uint64_t*>(key);
|
2025-05-27 15:31:55 +02:00
|
|
|
const uint64_t* end = data + (len / 8);
|
|
|
|
|
|
|
|
|
|
while (data != end) {
|
|
|
|
|
uint64_t k = *data++;
|
|
|
|
|
|
|
|
|
|
k *= m;
|
|
|
|
|
k ^= k >> r;
|
|
|
|
|
k *= m;
|
|
|
|
|
|
|
|
|
|
h ^= k;
|
|
|
|
|
h *= m;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 02:06:43 +02:00
|
|
|
const unsigned char* data2 = reinterpret_cast<const unsigned char*>(data);
|
2025-05-27 15:31:55 +02:00
|
|
|
|
|
|
|
|
switch (len & 7) {
|
2026-03-28 04:14:18 +01:00
|
|
|
case 7: h ^= static_cast<uint64_t>(data2[6]) << 48; // FALLTHRU
|
|
|
|
|
case 6: h ^= static_cast<uint64_t>(data2[5]) << 40; // FALLTHRU
|
|
|
|
|
case 5: h ^= static_cast<uint64_t>(data2[4]) << 32; // FALLTHRU
|
|
|
|
|
case 4: h ^= static_cast<uint64_t>(data2[3]) << 24; // FALLTHRU
|
|
|
|
|
case 3: h ^= static_cast<uint64_t>(data2[2]) << 16; // FALLTHRU
|
|
|
|
|
case 2: h ^= static_cast<uint64_t>(data2[1]) << 8; // FALLTHRU
|
|
|
|
|
case 1: h ^= static_cast<uint64_t>(data2[0]); h *= m; // FALLTHRU
|
|
|
|
|
default:;
|
2025-05-27 15:31:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
h ^= h >> r;
|
|
|
|
|
h *= m;
|
|
|
|
|
h ^= h >> r;
|
|
|
|
|
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start,
|
|
|
|
|
IData count) VL_MT_SAFE {
|
2019-03-08 02:56:53 +01:00
|
|
|
// While threadsafe, each thread can only access different file handles
|
2021-06-19 04:19:35 +02:00
|
|
|
FILE* const fp = VL_CVT_I_FP(fpi);
|
2019-03-08 02:56:53 +01:00
|
|
|
if (VL_UNLIKELY(!fp)) return 0;
|
|
|
|
|
if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb);
|
|
|
|
|
// Prep for reading
|
|
|
|
|
IData read_count = 0;
|
|
|
|
|
IData read_elements = 0;
|
2021-11-25 15:05:50 +01:00
|
|
|
const int start_shift = (width - 1) & ~7; // bit+7:bit gets first character
|
2019-03-08 02:56:53 +01:00
|
|
|
int shift = start_shift;
|
|
|
|
|
// Read the data
|
|
|
|
|
// We process a character at a time, as then we don't need to deal
|
|
|
|
|
// with changing buffer sizes dynamically, etc.
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2021-11-25 15:05:50 +01:00
|
|
|
const int c = std::fgetc(fp);
|
2020-01-11 18:36:30 +01:00
|
|
|
if (VL_UNLIKELY(c == EOF)) break;
|
2019-03-08 02:56:53 +01:00
|
|
|
// Shift value in
|
2021-11-25 15:05:50 +01:00
|
|
|
const IData entry = read_elements + start - array_lsb;
|
2019-03-08 02:56:53 +01:00
|
|
|
if (width <= 8) {
|
2021-07-24 14:36:11 +02:00
|
|
|
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
|
2021-02-22 03:25:21 +01:00
|
|
|
if (shift == start_shift) *datap = 0;
|
2019-03-08 02:56:53 +01:00
|
|
|
*datap |= (c << shift) & VL_MASK_I(width);
|
|
|
|
|
} else if (width <= 16) {
|
2021-07-24 14:36:11 +02:00
|
|
|
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
|
2021-02-22 03:25:21 +01:00
|
|
|
if (shift == start_shift) *datap = 0;
|
2019-03-08 02:56:53 +01:00
|
|
|
*datap |= (c << shift) & VL_MASK_I(width);
|
2019-12-09 03:36:38 +01:00
|
|
|
} else if (width <= VL_IDATASIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
|
2021-02-22 03:25:21 +01:00
|
|
|
if (shift == start_shift) *datap = 0;
|
2019-03-08 02:56:53 +01:00
|
|
|
*datap |= (c << shift) & VL_MASK_I(width);
|
|
|
|
|
} else if (width <= VL_QUADSIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
|
2021-02-22 03:25:21 +01:00
|
|
|
if (shift == start_shift) *datap = 0;
|
2020-04-14 04:51:35 +02:00
|
|
|
*datap |= ((static_cast<QData>(c) << static_cast<QData>(shift)) & VL_MASK_Q(width));
|
2019-12-09 03:36:38 +01:00
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
const WDataOutP datap = WDataOutP::external(
|
|
|
|
|
&(reinterpret_cast<EData*>(memp))[entry * VL_WORDS_I(width)]);
|
2022-12-23 16:51:52 +01:00
|
|
|
if (shift == start_shift) VL_ZERO_W(width, datap);
|
2019-12-09 03:36:38 +01:00
|
|
|
datap[VL_BITWORD_E(shift)] |= (static_cast<EData>(c) << VL_BITBIT_E(shift));
|
2019-03-08 02:56:53 +01:00
|
|
|
}
|
|
|
|
|
// Prep for next
|
|
|
|
|
++read_count;
|
|
|
|
|
shift -= 8;
|
|
|
|
|
if (shift < 0) {
|
|
|
|
|
shift = start_shift;
|
|
|
|
|
++read_elements;
|
|
|
|
|
if (VL_UNLIKELY(read_elements >= count)) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return read_count;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 01:01:24 +01:00
|
|
|
#ifdef _VL_HAVE_STACKTRACE
|
2026-02-03 04:47:24 +01:00
|
|
|
static std::string _vl_stacktrace_demangle(const std::string& input) VL_MT_SAFE {
|
|
|
|
|
static VerilatedMutex s_demangleMutex;
|
|
|
|
|
const VerilatedLockGuard lock{s_demangleMutex};
|
|
|
|
|
|
2026-02-03 01:01:24 +01:00
|
|
|
std::string result;
|
|
|
|
|
result.reserve(input.size());
|
|
|
|
|
|
|
|
|
|
std::string word;
|
|
|
|
|
for (const char c : input) {
|
|
|
|
|
if (std::isalpha(c) || c == '_') {
|
|
|
|
|
word += c;
|
|
|
|
|
} else if (!word.empty() && std::isdigit(c)) {
|
|
|
|
|
word += c;
|
|
|
|
|
} else {
|
|
|
|
|
if (!word.empty()) {
|
|
|
|
|
// abi::__cxa_demangle mallocs demangled_name
|
|
|
|
|
int status = 0;
|
|
|
|
|
char* const demangled_name
|
2026-03-28 04:14:18 +01:00
|
|
|
= abi::__cxa_demangle(word.c_str(), nullptr, nullptr, &status);
|
2026-02-03 01:01:24 +01:00
|
|
|
if (status == 0) {
|
|
|
|
|
result += std::string{demangled_name};
|
|
|
|
|
std::free(demangled_name); // Free the allocated memory
|
|
|
|
|
} else {
|
|
|
|
|
result += word;
|
|
|
|
|
}
|
|
|
|
|
word.clear();
|
|
|
|
|
}
|
|
|
|
|
result += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// input requires final newline, so last word can't be symbol
|
|
|
|
|
result += word;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-11-18 01:12:54 +01:00
|
|
|
std::string VL_STACKTRACE_N() VL_MT_SAFE {
|
|
|
|
|
static VerilatedMutex s_stackTraceMutex;
|
|
|
|
|
const VerilatedLockGuard lock{s_stackTraceMutex};
|
|
|
|
|
|
2026-02-03 03:00:56 +01:00
|
|
|
#ifdef _VL_HAVE_STACKTRACE
|
2022-11-18 01:12:54 +01:00
|
|
|
int nptrs = 0;
|
2022-11-18 01:14:05 +01:00
|
|
|
char** strings = nullptr;
|
2022-11-18 01:12:54 +01:00
|
|
|
|
2022-12-22 18:19:09 +01:00
|
|
|
constexpr int BT_BUF_SIZE = 100;
|
|
|
|
|
void* buffer[BT_BUF_SIZE];
|
2022-11-18 01:12:54 +01:00
|
|
|
nptrs = backtrace(buffer, BT_BUF_SIZE);
|
|
|
|
|
strings = backtrace_symbols(buffer, nptrs);
|
|
|
|
|
|
2022-11-22 03:40:49 +01:00
|
|
|
// cppcheck-suppress knownConditionTrueFalse
|
2026-02-03 03:00:56 +01:00
|
|
|
if (!strings) return "Unable to backtrace, call failed\n";
|
2022-11-18 01:12:54 +01:00
|
|
|
|
2023-04-09 04:11:28 +02:00
|
|
|
std::string result = "Backtrace:\n";
|
2026-02-03 01:01:24 +01:00
|
|
|
for (int j = 0; j < nptrs; ++j)
|
|
|
|
|
result += _vl_stacktrace_demangle(std::string{strings[j]} + "\n"s);
|
|
|
|
|
|
2022-11-18 01:12:54 +01:00
|
|
|
free(strings);
|
2023-04-09 04:11:28 +02:00
|
|
|
return result;
|
2026-02-03 03:00:56 +01:00
|
|
|
#else
|
|
|
|
|
return "Unable to backtrace; not supported\n";
|
|
|
|
|
#endif
|
2022-11-18 01:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_STACKTRACE() VL_MT_SAFE {
|
2023-04-09 04:11:28 +02:00
|
|
|
const std::string result = VL_STACKTRACE_N();
|
|
|
|
|
VL_PRINTF("%s", result.c_str());
|
2022-11-18 01:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
2021-06-18 03:17:15 +02:00
|
|
|
VlWide<VL_WQ_WORDS_E> lhsw;
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_SET_WQ(lhsw, lhs);
|
2019-12-09 03:36:38 +01:00
|
|
|
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
|
2011-11-20 08:01:48 +01:00
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
2024-10-22 15:25:58 +02:00
|
|
|
const std::string lhs = VL_CVT_PACK_STR_NW(lhswords, lhsp);
|
|
|
|
|
return VL_SYSTEM_IN(lhs);
|
2024-04-11 23:31:14 +02:00
|
|
|
}
|
|
|
|
|
IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
|
2026-05-27 20:33:19 +02:00
|
|
|
VerilatedContext* ctxp = Verilated::threadContextp();
|
|
|
|
|
const bool sending = ctxp->logOutputToFile();
|
|
|
|
|
// system() command may echo and read so restore default stdout and stderr if sending
|
|
|
|
|
if (sending) ctxp->logRestoreOutput();
|
2024-04-11 23:31:14 +02:00
|
|
|
const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe
|
2026-05-27 20:33:19 +02:00
|
|
|
// Log to file again if we were sending
|
|
|
|
|
if (sending) ctxp->logOutputToFile(true /* append */);
|
2011-11-20 08:01:48 +01:00
|
|
|
return code >> 8; // Want exit status
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 12:21:35 +02:00
|
|
|
IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
|
|
|
|
|
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
|
2020-04-14 04:51:35 +02:00
|
|
|
return match.empty() ? 0 : 1;
|
2009-11-19 23:04:21 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE {
|
2017-09-23 13:32:37 +02:00
|
|
|
std::string prefix;
|
2017-05-19 04:41:43 +02:00
|
|
|
bool inPct = false;
|
|
|
|
|
bool done = false;
|
|
|
|
|
char fmt = ' ';
|
|
|
|
|
for (const char* posp = ld.c_str(); !done && *posp; ++posp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!inPct && posp[0] == '%') {
|
2019-05-09 03:13:38 +02:00
|
|
|
inPct = true;
|
2018-11-29 01:59:10 +01:00
|
|
|
} else if (!inPct) { // Normal text
|
|
|
|
|
prefix += *posp;
|
2022-11-27 11:52:40 +01:00
|
|
|
} else if (*posp == '0') { // %0
|
2018-11-29 01:59:10 +01:00
|
|
|
} else { // Format character
|
2021-03-27 02:23:18 +01:00
|
|
|
switch (std::tolower(*posp)) {
|
2019-05-09 03:13:38 +02:00
|
|
|
case '%':
|
|
|
|
|
prefix += *posp;
|
|
|
|
|
inPct = false;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fmt = *posp;
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-19 04:41:43 +02:00
|
|
|
}
|
2016-05-17 03:57:49 +02:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
|
2021-07-24 14:36:11 +02:00
|
|
|
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
2018-10-15 00:39:33 +02:00
|
|
|
if (match.empty()) return 0;
|
2017-05-19 04:41:43 +02:00
|
|
|
|
2022-12-23 16:51:52 +01:00
|
|
|
VL_ZERO_W(rbits, rwp);
|
2021-03-27 02:23:18 +01:00
|
|
|
switch (std::tolower(fmt)) {
|
2020-06-02 05:16:02 +02:00
|
|
|
case 'd': {
|
2022-03-27 21:27:40 +02:00
|
|
|
int64_t lld = 0;
|
2022-01-01 22:04:20 +01:00
|
|
|
std::sscanf(dp, "%30" PRId64, &lld);
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_SET_WQ(rwp, lld);
|
|
|
|
|
break;
|
2020-06-02 05:16:02 +02:00
|
|
|
}
|
2021-03-27 02:23:18 +01:00
|
|
|
case 'b': _vl_vsss_based(rwp, rbits, 1, dp, 0, std::strlen(dp)); break;
|
|
|
|
|
case 'o': _vl_vsss_based(rwp, rbits, 3, dp, 0, std::strlen(dp)); break;
|
2018-11-29 01:59:10 +01:00
|
|
|
case 'h': // FALLTHRU
|
2021-03-27 02:23:18 +01:00
|
|
|
case 'x': _vl_vsss_based(rwp, rbits, 4, dp, 0, std::strlen(dp)); break;
|
2020-06-02 05:16:02 +02:00
|
|
|
case 's': { // string/no conversion
|
2021-03-27 02:23:18 +01:00
|
|
|
for (int i = 0, lsb = 0, posp = static_cast<int>(std::strlen(dp)) - 1;
|
|
|
|
|
i < rbits && posp >= 0; --posp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
_vl_vsss_setbit(rwp, rbits, lsb, 8, dp[posp]);
|
|
|
|
|
lsb += 8;
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2020-06-02 05:16:02 +02:00
|
|
|
}
|
2019-12-04 00:22:17 +01:00
|
|
|
case 'e': {
|
2020-06-02 05:16:02 +02:00
|
|
|
double temp = 0.F;
|
2021-03-27 02:23:18 +01:00
|
|
|
std::sscanf(dp, "%le", &temp);
|
2019-12-04 00:22:17 +01:00
|
|
|
VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'f': {
|
2020-06-02 05:16:02 +02:00
|
|
|
double temp = 0.F;
|
2021-03-27 02:23:18 +01:00
|
|
|
std::sscanf(dp, "%lf", &temp);
|
2019-12-04 00:22:17 +01:00
|
|
|
VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'g': {
|
2020-06-02 05:16:02 +02:00
|
|
|
double temp = 0.F;
|
2021-03-27 02:23:18 +01:00
|
|
|
std::sscanf(dp, "%lg", &temp);
|
2019-12-04 00:22:17 +01:00
|
|
|
VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-03-31 02:17:59 +02:00
|
|
|
default: // Other simulators return 0 in these cases and don't error out
|
2019-05-09 03:13:38 +02:00
|
|
|
return 0;
|
2009-11-19 23:04:21 +01:00
|
|
|
}
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_clean_inplace_w(rbits, rwp);
|
2009-11-19 23:04:21 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2017-10-27 02:05:42 +02:00
|
|
|
IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE {
|
2017-09-23 13:32:37 +02:00
|
|
|
std::string prefix;
|
2017-05-19 04:41:43 +02:00
|
|
|
bool inPct = false;
|
|
|
|
|
bool done = false;
|
|
|
|
|
for (const char* posp = ld.c_str(); !done && *posp; ++posp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!inPct && posp[0] == '%') {
|
2019-05-09 03:13:38 +02:00
|
|
|
inPct = true;
|
2018-11-29 01:59:10 +01:00
|
|
|
} else if (!inPct) { // Normal text
|
|
|
|
|
prefix += *posp;
|
|
|
|
|
} else { // Format character
|
2021-03-27 02:23:18 +01:00
|
|
|
switch (std::tolower(*posp)) {
|
2019-05-09 03:13:38 +02:00
|
|
|
case '%':
|
|
|
|
|
prefix += *posp;
|
|
|
|
|
inPct = false;
|
|
|
|
|
break;
|
2020-04-14 04:51:35 +02:00
|
|
|
default: //
|
2019-05-09 03:13:38 +02:00
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-19 04:41:43 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
|
2021-07-24 14:36:11 +02:00
|
|
|
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
2018-10-15 00:39:33 +02:00
|
|
|
if (match.empty()) return 0;
|
2021-07-24 14:36:11 +02:00
|
|
|
rdr = std::string{dp};
|
2017-05-19 04:41:43 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
2009-11-19 23:04:21 +01:00
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE {
|
2021-03-07 17:01:54 +01:00
|
|
|
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefixp);
|
2026-03-26 18:55:14 +01:00
|
|
|
static thread_local std::string t_outstr;
|
2020-08-15 16:12:55 +02:00
|
|
|
if (match.empty()) return nullptr;
|
2026-03-26 18:55:14 +01:00
|
|
|
t_outstr = match.c_str() + std::strlen(prefixp) + 1;
|
|
|
|
|
return t_outstr.c_str();
|
2010-02-03 12:33:00 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-17 21:10:37 +01:00
|
|
|
//===========================================================================
|
2019-11-17 11:05:09 +01:00
|
|
|
// Heavy string functions
|
|
|
|
|
|
2026-03-15 03:43:56 +01:00
|
|
|
// TODO these could be accelerated with a dedicated to-Hex formatter
|
|
|
|
|
// instead of using VL_SFORMATF_N_NX
|
|
|
|
|
std::string VL_TO_STRING(CData lhs) {
|
|
|
|
|
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, 8, lhs);
|
|
|
|
|
}
|
|
|
|
|
std::string VL_TO_STRING(SData lhs) {
|
|
|
|
|
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, 16, lhs);
|
|
|
|
|
}
|
|
|
|
|
std::string VL_TO_STRING(IData lhs) {
|
|
|
|
|
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, 32, lhs);
|
|
|
|
|
}
|
|
|
|
|
std::string VL_TO_STRING(QData lhs) {
|
|
|
|
|
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, 64, lhs);
|
|
|
|
|
}
|
|
|
|
|
std::string VL_TO_STRING(double lhs) {
|
|
|
|
|
return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_DOUBLE, lhs);
|
|
|
|
|
}
|
2021-06-19 04:19:35 +02:00
|
|
|
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
2026-03-15 03:43:56 +01:00
|
|
|
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
2019-12-01 17:52:48 +01:00
|
|
|
|
2022-12-12 05:03:27 +01:00
|
|
|
std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE {
|
2023-04-09 04:11:28 +02:00
|
|
|
std::string result = ld;
|
|
|
|
|
for (auto& cr : result) cr = std::tolower(cr);
|
|
|
|
|
return result;
|
2019-11-17 11:05:09 +01:00
|
|
|
}
|
2022-12-12 05:03:27 +01:00
|
|
|
std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE {
|
2023-04-09 04:11:28 +02:00
|
|
|
std::string result = ld;
|
|
|
|
|
for (auto& cr : result) cr = std::toupper(cr);
|
|
|
|
|
return result;
|
2019-11-17 11:05:09 +01:00
|
|
|
}
|
2010-01-17 21:10:37 +01:00
|
|
|
|
2022-12-16 03:32:34 +01:00
|
|
|
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
|
2021-03-04 04:53:50 +01:00
|
|
|
// See also _vl_vint_to_string
|
2024-10-22 15:25:58 +02:00
|
|
|
std::string result;
|
|
|
|
|
result.reserve((lwords * VL_EDATASIZE) / 8 + 1);
|
2021-11-25 15:05:50 +01:00
|
|
|
const int obits = lwords * VL_EDATASIZE;
|
2020-04-14 04:51:35 +02:00
|
|
|
int lsb = obits - 1;
|
|
|
|
|
for (; lsb >= 0; --lsb) {
|
2018-11-29 01:59:10 +01:00
|
|
|
lsb = (lsb / 8) * 8; // Next digit
|
2021-11-25 15:05:50 +01:00
|
|
|
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
2024-10-22 15:25:58 +02:00
|
|
|
if (charval) result += static_cast<char>(charval);
|
2010-01-17 21:10:37 +01:00
|
|
|
}
|
2024-10-22 15:25:58 +02:00
|
|
|
return result;
|
2010-01-17 21:10:37 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-14 04:46:42 +02:00
|
|
|
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
|
|
|
|
|
std::string output;
|
|
|
|
|
for (const std::string& s : q) output += s;
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-15 14:09:52 +01:00
|
|
|
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
|
|
|
|
|
std::string lstring = lhs;
|
2022-03-27 21:27:40 +02:00
|
|
|
const int32_t rhs_s = rhs; // To signed value
|
2019-12-15 14:09:52 +01:00
|
|
|
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
|
|
|
|
|
if (0 <= rhs_s && rhs < lhs.length() && ths != 0) lstring[rhs] = ths;
|
|
|
|
|
return lstring;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE {
|
|
|
|
|
CData v = 0;
|
2022-03-27 21:27:40 +02:00
|
|
|
const int32_t rhs_s = rhs; // To signed value
|
2019-12-15 14:09:52 +01:00
|
|
|
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
|
|
|
|
|
if (0 <= rhs_s && rhs < lhs.length()) v = lhs[rhs];
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
|
2022-03-27 21:27:40 +02:00
|
|
|
const int32_t rhs_s = rhs; // To signed value
|
|
|
|
|
const int32_t ths_s = ths; // To signed value
|
2019-12-15 14:09:52 +01:00
|
|
|
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
|
|
|
|
|
if (rhs_s < 0 || ths_s < rhs_s || ths >= lhs.length()) return "";
|
|
|
|
|
// Second parameter of std::string::substr(i, n) is length, not position as in SystemVerilog
|
|
|
|
|
return lhs.substr(rhs, ths - rhs + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 01:17:52 +01:00
|
|
|
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
2019-12-15 14:09:52 +01:00
|
|
|
std::string str_mod = str;
|
2024-03-02 15:05:21 +01:00
|
|
|
// IEEE 1800-2023 6.16.9 says '_' may exist.
|
2019-12-10 01:17:52 +01:00
|
|
|
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
2020-08-16 20:55:46 +02:00
|
|
|
auto v = std::strtol(str_mod.c_str(), nullptr, base);
|
2019-12-10 01:17:52 +01:00
|
|
|
if (errno != 0) v = 0;
|
|
|
|
|
return static_cast<IData>(v);
|
|
|
|
|
}
|
2023-03-01 05:34:33 +01:00
|
|
|
IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); }
|
|
|
|
|
QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE {
|
|
|
|
|
QData out = 0;
|
|
|
|
|
const char* const datap = str.data();
|
|
|
|
|
int pos = static_cast<int>(str.length()) - 1;
|
|
|
|
|
int bit = 0;
|
|
|
|
|
while (bit < obits && pos >= 0) {
|
|
|
|
|
out |= static_cast<QData>(datap[pos]) << VL_BITBIT_Q(bit);
|
|
|
|
|
bit += 8;
|
|
|
|
|
--pos;
|
|
|
|
|
}
|
|
|
|
|
return out & VL_MASK_Q(obits);
|
|
|
|
|
}
|
2026-03-15 03:43:56 +01:00
|
|
|
void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str, int truncFront) VL_PURE {
|
|
|
|
|
// Could also be called VL_CVT_PACK_STR_WN; converts string to wide
|
2023-03-01 05:34:33 +01:00
|
|
|
const int words = VL_WORDS_I(obits);
|
|
|
|
|
for (int i = 0; i < words; ++i) owp[i] = 0;
|
|
|
|
|
const char* const datap = str.data();
|
2026-03-15 03:43:56 +01:00
|
|
|
int pos = static_cast<int>(str.length()) - 1 - truncFront;
|
2023-03-01 05:34:33 +01:00
|
|
|
int bit = 0;
|
|
|
|
|
while (bit < obits && pos >= 0) {
|
|
|
|
|
owp[VL_BITWORD_I(bit)] |= static_cast<EData>(datap[pos]) << VL_BITBIT_I(bit);
|
|
|
|
|
bit += 8;
|
|
|
|
|
--pos;
|
|
|
|
|
}
|
|
|
|
|
owp[words - 1] &= VL_MASK_E(obits);
|
|
|
|
|
}
|
2019-12-10 01:17:52 +01:00
|
|
|
|
2020-01-14 13:01:17 +01:00
|
|
|
//===========================================================================
|
|
|
|
|
// Readmem/writemem
|
|
|
|
|
|
|
|
|
|
static const char* memhFormat(int nBits) {
|
|
|
|
|
assert((nBits >= 1) && (nBits <= 32));
|
|
|
|
|
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local char t_buf[32];
|
2020-01-14 13:01:17 +01:00
|
|
|
switch ((nBits - 1) / 4) {
|
2026-03-28 04:14:18 +01:00
|
|
|
case 0: (void)VL_SNPRINTF(t_buf, 32, "%%01x"); break;
|
|
|
|
|
case 1: (void)VL_SNPRINTF(t_buf, 32, "%%02x"); break;
|
|
|
|
|
case 2: (void)VL_SNPRINTF(t_buf, 32, "%%03x"); break;
|
|
|
|
|
case 3: (void)VL_SNPRINTF(t_buf, 32, "%%04x"); break;
|
|
|
|
|
case 4: (void)VL_SNPRINTF(t_buf, 32, "%%05x"); break;
|
|
|
|
|
case 5: (void)VL_SNPRINTF(t_buf, 32, "%%06x"); break;
|
|
|
|
|
case 6: (void)VL_SNPRINTF(t_buf, 32, "%%07x"); break;
|
|
|
|
|
case 7: (void)VL_SNPRINTF(t_buf, 32, "%%08x"); break;
|
2020-01-14 13:01:17 +01:00
|
|
|
default: assert(false); break; // LCOV_EXCL_LINE
|
|
|
|
|
}
|
2020-12-02 01:01:20 +01:00
|
|
|
return t_buf;
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
static const char* formatBinary(int nBits, uint32_t bits) {
|
2020-07-01 23:32:15 +02:00
|
|
|
assert((nBits >= 1) && (nBits <= 32));
|
|
|
|
|
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local char t_buf[64];
|
2025-05-09 14:25:54 +02:00
|
|
|
for (int i = 0; i < nBits; ++i) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const bool isOne = bits & (1 << (nBits - 1 - i));
|
2020-12-02 01:01:20 +01:00
|
|
|
t_buf[i] = (isOne ? '1' : '0');
|
2020-07-01 23:32:15 +02:00
|
|
|
}
|
2020-12-02 01:01:20 +01:00
|
|
|
t_buf[nBits] = '\0';
|
|
|
|
|
return t_buf;
|
2020-07-01 23:32:15 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 13:01:17 +01:00
|
|
|
VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_hex{hex}
|
|
|
|
|
, m_bits{bits}
|
2020-08-25 00:49:36 +02:00
|
|
|
, m_filename(filename) // Need () or GCC 4.8 false warning
|
2020-08-16 15:55:36 +02:00
|
|
|
, m_end{end}
|
2022-07-30 17:52:35 +02:00
|
|
|
, m_addr{start} {
|
2021-03-27 02:23:18 +01:00
|
|
|
m_fp = std::fopen(filename.c_str(), "r");
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(!m_fp)) {
|
|
|
|
|
// We don't report the Verilog source filename as it slow to have to pass it down
|
2022-02-19 16:03:55 +01:00
|
|
|
VL_WARN_MT(filename.c_str(), 0, "", "$readmem file not found");
|
2020-01-14 13:01:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VlReadMem::~VlReadMem() {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (m_fp) {
|
2021-03-27 02:23:18 +01:00
|
|
|
std::fclose(m_fp);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_fp = nullptr;
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
|
|
|
|
if (VL_UNLIKELY(!m_fp)) return false;
|
|
|
|
|
valuer = "";
|
|
|
|
|
// Prep for reading
|
2024-05-21 23:27:32 +02:00
|
|
|
bool inData = false;
|
|
|
|
|
bool ignoreToEol = false;
|
|
|
|
|
bool ignoreToComment = false;
|
|
|
|
|
bool readingAddress = false;
|
|
|
|
|
int lastCh = ' ';
|
2020-01-14 13:01:17 +01:00
|
|
|
// Read the data
|
|
|
|
|
// We process a character at a time, as then we don't need to deal
|
|
|
|
|
// with changing buffer sizes dynamically, etc.
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2021-03-27 02:23:18 +01:00
|
|
|
int c = std::fgetc(m_fp);
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(c == EOF)) break;
|
2024-05-21 23:27:32 +02:00
|
|
|
const bool chIs4StateBin
|
|
|
|
|
= c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
|
|
|
|
|
const bool chIs2StateHex = std::isxdigit(c);
|
|
|
|
|
const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
|
2020-01-14 13:01:17 +01:00
|
|
|
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
|
2024-05-21 23:27:32 +02:00
|
|
|
// m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
|
2020-01-14 13:01:17 +01:00
|
|
|
// See if previous data value has completed, and if so return
|
|
|
|
|
if (c == '_') continue; // Ignore _ e.g. inside a number
|
2024-05-21 23:27:32 +02:00
|
|
|
if (inData && !chIs4StateHex) {
|
2020-01-14 13:01:17 +01:00
|
|
|
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
|
|
|
|
|
ungetc(c, m_fp);
|
|
|
|
|
addrr = m_addr;
|
|
|
|
|
++m_addr;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Parse line
|
|
|
|
|
if (c == '\n') {
|
2020-04-14 04:51:35 +02:00
|
|
|
++m_linenum;
|
2024-05-21 23:27:32 +02:00
|
|
|
ignoreToEol = false;
|
|
|
|
|
readingAddress = false;
|
2020-01-14 13:01:17 +01:00
|
|
|
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
2024-05-21 23:27:32 +02:00
|
|
|
readingAddress = false;
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
// Skip // comments and detect /* comments
|
2024-05-21 23:27:32 +02:00
|
|
|
else if (ignoreToComment && lastCh == '*' && c == '/') {
|
|
|
|
|
ignoreToComment = false;
|
|
|
|
|
readingAddress = false;
|
|
|
|
|
} else if (!ignoreToEol && !ignoreToComment) {
|
|
|
|
|
if (lastCh == '/' && c == '*') {
|
|
|
|
|
ignoreToComment = true;
|
|
|
|
|
} else if (lastCh == '/' && c == '/') {
|
|
|
|
|
ignoreToEol = true;
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (c == '/') { // Part of /* or //
|
|
|
|
|
} else if (c == '#') {
|
2024-05-21 23:27:32 +02:00
|
|
|
ignoreToEol = true;
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (c == '@') {
|
2024-05-21 23:27:32 +02:00
|
|
|
readingAddress = true;
|
2021-12-22 01:55:04 +01:00
|
|
|
m_anyAddr = true;
|
2020-04-14 04:51:35 +02:00
|
|
|
m_addr = 0;
|
2024-05-21 23:27:32 +02:00
|
|
|
} else if (readingAddress && chIs2StateHex) {
|
2021-03-27 02:23:18 +01:00
|
|
|
c = std::tolower(c);
|
2024-05-21 23:27:32 +02:00
|
|
|
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
|
|
|
|
|
m_addr = (m_addr << 4) + addressValue;
|
|
|
|
|
} else if (readingAddress && chIs4StateHex) {
|
|
|
|
|
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
|
|
|
|
"$readmem address contains 4-state characters");
|
|
|
|
|
} else if (chIs4StateHex) {
|
|
|
|
|
inData = true;
|
|
|
|
|
valuer += static_cast<char>(c);
|
|
|
|
|
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
|
|
|
|
|
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
|
|
|
|
"$readmemb (binary) file contains hex characters");
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-21 23:27:32 +02:00
|
|
|
lastCh = c;
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 01:55:04 +01:00
|
|
|
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
|
|
|
|
|
VL_WARN_MT(m_filename.c_str(), m_linenum, "",
|
2024-03-02 15:05:21 +01:00
|
|
|
"$readmem file ended before specified final address (IEEE 1800-2023 21.4)");
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-27 23:42:20 +01:00
|
|
|
addrr = m_addr;
|
2024-05-21 23:27:32 +02:00
|
|
|
return inData; // EOF
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const QData shift = m_hex ? 4ULL : 1ULL;
|
2020-01-14 13:01:17 +01:00
|
|
|
bool innum = false;
|
|
|
|
|
// Shift value in
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const auto& i : rhs) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const char c = std::tolower(i);
|
2024-05-21 23:27:32 +02:00
|
|
|
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
|
|
|
|
|
: (c >= 'a') ? (c - 'a' + 10)
|
|
|
|
|
: (c - '0');
|
2020-01-14 13:01:17 +01:00
|
|
|
if (m_bits <= 8) {
|
2021-07-24 14:36:11 +02:00
|
|
|
CData* const datap = reinterpret_cast<CData*>(valuep);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!innum) *datap = 0;
|
2020-01-14 13:01:17 +01:00
|
|
|
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
|
|
|
|
} else if (m_bits <= 16) {
|
2021-07-24 14:36:11 +02:00
|
|
|
SData* const datap = reinterpret_cast<SData*>(valuep);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!innum) *datap = 0;
|
2020-01-14 13:01:17 +01:00
|
|
|
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
|
|
|
|
} else if (m_bits <= VL_IDATASIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
IData* const datap = reinterpret_cast<IData*>(valuep);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!innum) *datap = 0;
|
2020-01-14 13:01:17 +01:00
|
|
|
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
|
|
|
|
} else if (m_bits <= VL_QUADSIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
QData* const datap = reinterpret_cast<QData*>(valuep);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!innum) *datap = 0;
|
2020-01-14 13:01:17 +01:00
|
|
|
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
|
|
|
|
|
& VL_MASK_Q(m_bits);
|
|
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
const WDataOutP datap = WDataOutP::external(reinterpret_cast<EData*>(valuep));
|
2022-12-23 16:51:52 +01:00
|
|
|
if (!innum) VL_ZERO_W(m_bits, datap);
|
2021-03-04 04:53:50 +01:00
|
|
|
_vl_shiftl_inplace_w(m_bits, datap, static_cast<IData>(shift));
|
2020-01-14 13:01:17 +01:00
|
|
|
datap[0] |= value;
|
|
|
|
|
}
|
|
|
|
|
innum = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_hex{hex}
|
2022-07-30 17:52:35 +02:00
|
|
|
, m_bits{bits} {
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(start > end)) {
|
|
|
|
|
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 02:23:18 +01:00
|
|
|
m_fp = std::fopen(filename.c_str(), "w");
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(!m_fp)) {
|
|
|
|
|
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VlWriteMem::~VlWriteMem() {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (m_fp) {
|
2021-03-27 02:23:18 +01:00
|
|
|
std::fclose(m_fp);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_fp = nullptr;
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
|
|
|
|
if (VL_UNLIKELY(!m_fp)) return;
|
|
|
|
|
if (addr != m_addr && addrstamp) { // Only assoc has time stamps
|
2022-01-01 22:04:20 +01:00
|
|
|
fprintf(m_fp, "@%" PRIx64 "\n", addr);
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
|
|
|
|
m_addr = addr + 1;
|
|
|
|
|
if (m_bits <= 8) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const CData* const datap = reinterpret_cast<const CData*>(valuep);
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
|
|
|
|
fprintf(m_fp, "\n");
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
} else if (m_bits <= 16) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const SData* const datap = reinterpret_cast<const SData*>(valuep);
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
|
|
|
|
fprintf(m_fp, "\n");
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
} else if (m_bits <= 32) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const IData* const datap = reinterpret_cast<const IData*>(valuep);
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
|
|
|
|
fprintf(m_fp, "\n");
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
} else if (m_bits <= 64) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const QData* const datap = reinterpret_cast<const QData*>(valuep);
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t value = VL_MASK_Q(m_bits) & *datap;
|
|
|
|
|
const uint32_t lo = value & 0xffffffff;
|
|
|
|
|
const uint32_t hi = value >> 32;
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, memhFormat(m_bits - 32), hi);
|
|
|
|
|
fprintf(m_fp, "%08x\n", lo);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s", formatBinary(m_bits - 32, hi));
|
|
|
|
|
fprintf(m_fp, "%s\n", formatBinary(32, lo));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
const WDataInP datap = WDataInP::external(reinterpret_cast<const EData*>(valuep));
|
2020-01-14 13:01:17 +01:00
|
|
|
// output as a sequence of VL_EDATASIZE'd words
|
|
|
|
|
// from MSB to LSB. Mask off the MSB word which could
|
|
|
|
|
// contain junk above the top of valid data.
|
|
|
|
|
int word_idx = ((m_bits - 1) / VL_EDATASIZE);
|
|
|
|
|
bool first = true;
|
|
|
|
|
while (word_idx >= 0) {
|
|
|
|
|
EData data = datap[word_idx];
|
|
|
|
|
if (first) {
|
|
|
|
|
data &= VL_MASK_E(m_bits);
|
2021-06-19 04:19:35 +02:00
|
|
|
const int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, memhFormat(top_word_nbits), data);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s", formatBinary(top_word_nbits, data));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
} else {
|
2020-07-01 23:32:15 +02:00
|
|
|
if (m_hex) {
|
|
|
|
|
fprintf(m_fp, "%08x", data);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(m_fp, "%s", formatBinary(32, data));
|
|
|
|
|
}
|
2020-01-14 13:01:17 +01:00
|
|
|
}
|
2021-07-24 14:36:11 +02:00
|
|
|
--word_idx;
|
2020-01-14 13:01:17 +01:00
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
fprintf(m_fp, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_READMEM_N(bool hex, // Hex format, else binary
|
|
|
|
|
int bits, // M_Bits of each array row
|
|
|
|
|
QData depth, // Number of rows
|
|
|
|
|
int array_lsb, // Index of first row. Valid row addresses
|
|
|
|
|
// // range from array_lsb up to (array_lsb + depth - 1)
|
|
|
|
|
const std::string& filename, // Input file name
|
|
|
|
|
void* memp, // Array state
|
|
|
|
|
QData start, // First array row address to read
|
|
|
|
|
QData end // Last row address to read
|
|
|
|
|
) VL_MT_SAFE {
|
2020-02-08 16:58:07 +01:00
|
|
|
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
|
2020-01-14 13:01:17 +01:00
|
|
|
|
2021-07-24 14:36:11 +02:00
|
|
|
VlReadMem rmem{hex, bits, filename, start, end};
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2020-06-02 05:16:02 +02:00
|
|
|
QData addr = 0;
|
2020-01-14 13:01:17 +01:00
|
|
|
std::string value;
|
2020-04-14 04:51:35 +02:00
|
|
|
if (rmem.get(addr /*ref*/, value /*ref*/)) {
|
2024-03-27 23:42:20 +01:00
|
|
|
// printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str());
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(addr < static_cast<QData>(array_lsb)
|
|
|
|
|
|| addr >= static_cast<QData>(array_lsb + depth))) {
|
|
|
|
|
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
|
|
|
|
|
"$readmem file address beyond bounds of array");
|
|
|
|
|
} else {
|
2021-11-25 15:05:50 +01:00
|
|
|
const QData entry = addr - array_lsb;
|
2020-01-14 13:01:17 +01:00
|
|
|
if (bits <= 8) {
|
2021-07-24 14:36:11 +02:00
|
|
|
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
|
2020-01-14 13:01:17 +01:00
|
|
|
rmem.setData(datap, value);
|
|
|
|
|
} else if (bits <= 16) {
|
2021-07-24 14:36:11 +02:00
|
|
|
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
|
2020-01-14 13:01:17 +01:00
|
|
|
rmem.setData(datap, value);
|
|
|
|
|
} else if (bits <= VL_IDATASIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
|
2020-01-14 13:01:17 +01:00
|
|
|
rmem.setData(datap, value);
|
|
|
|
|
} else if (bits <= VL_QUADSIZE) {
|
2021-07-24 14:36:11 +02:00
|
|
|
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
|
2020-01-14 13:01:17 +01:00
|
|
|
rmem.setData(datap, value);
|
|
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
EData* const datap
|
|
|
|
|
= &(reinterpret_cast<EData*>(memp))[entry * VL_WORDS_I(bits)];
|
2020-01-14 13:01:17 +01:00
|
|
|
rmem.setData(datap, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
|
|
|
|
int bits, // Width of each array row
|
|
|
|
|
QData depth, // Number of rows
|
|
|
|
|
int array_lsb, // Index of first row. Valid row addresses
|
|
|
|
|
// // range from array_lsb up to (array_lsb + depth - 1)
|
|
|
|
|
const std::string& filename, // Output file name
|
|
|
|
|
const void* memp, // Array state
|
|
|
|
|
QData start, // First array row address to write
|
|
|
|
|
QData end // Last address to write, or ~0 when not specified
|
|
|
|
|
) VL_MT_SAFE {
|
2021-11-25 15:05:50 +01:00
|
|
|
const QData addr_max = array_lsb + depth - 1;
|
2020-02-08 16:58:07 +01:00
|
|
|
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
|
2020-01-14 13:01:17 +01:00
|
|
|
if (end > addr_max) end = addr_max;
|
|
|
|
|
|
2021-07-24 14:36:11 +02:00
|
|
|
VlWriteMem wmem{hex, bits, filename, start, end};
|
2020-01-14 13:01:17 +01:00
|
|
|
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
|
|
|
|
|
|
|
|
|
for (QData addr = start; addr <= end; ++addr) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const QData row_offset = addr - array_lsb;
|
2020-01-14 13:01:17 +01:00
|
|
|
if (bits <= 8) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const CData* const datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
2020-01-14 13:01:17 +01:00
|
|
|
wmem.print(addr, false, datap);
|
|
|
|
|
} else if (bits <= 16) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const SData* const datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
|
2020-01-14 13:01:17 +01:00
|
|
|
wmem.print(addr, false, datap);
|
|
|
|
|
} else if (bits <= 32) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const IData* const datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
|
2020-01-14 13:01:17 +01:00
|
|
|
wmem.print(addr, false, datap);
|
|
|
|
|
} else if (bits <= 64) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const QData* const datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
2020-01-14 13:01:17 +01:00
|
|
|
wmem.print(addr, false, datap);
|
|
|
|
|
} else {
|
2026-05-22 21:05:08 +02:00
|
|
|
const EData* const datap
|
|
|
|
|
= &(reinterpret_cast<const EData*>(memp))[row_offset * VL_WORDS_I(bits)];
|
2020-01-14 13:01:17 +01:00
|
|
|
wmem.print(addr, false, datap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 01:11:37 +01:00
|
|
|
//===========================================================================
|
|
|
|
|
// Timescale conversion
|
|
|
|
|
|
2021-02-25 04:13:24 +01:00
|
|
|
static const char* vl_time_str(int scale) VL_PURE {
|
2025-10-28 01:49:41 +01:00
|
|
|
static const char* const s_names[]
|
2020-05-11 14:15:52 +02:00
|
|
|
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
|
|
|
|
|
"100ns", "10ns", "1ns", "100ps", "10ps", "1ps", "100fs", "10fs", "1fs"};
|
|
|
|
|
if (VL_UNLIKELY(scale > 2 || scale < -15)) scale = 0;
|
2025-10-28 01:49:41 +01:00
|
|
|
return s_names[2 - scale];
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2021-02-25 04:13:24 +01:00
|
|
|
double vl_time_multiplier(int scale) VL_PURE {
|
2022-12-03 00:46:38 +01:00
|
|
|
// Return timescale multiplier -18 to +18
|
2020-04-16 01:39:03 +02:00
|
|
|
// For speed, this does not check for illegal values
|
|
|
|
|
if (scale < 0) {
|
2020-05-29 03:04:36 +02:00
|
|
|
static const double neg10[] = {1.0,
|
|
|
|
|
0.1,
|
|
|
|
|
0.01,
|
|
|
|
|
0.001,
|
|
|
|
|
0.0001,
|
|
|
|
|
0.00001,
|
|
|
|
|
0.000001,
|
|
|
|
|
0.0000001,
|
|
|
|
|
0.00000001,
|
|
|
|
|
0.000000001,
|
|
|
|
|
0.0000000001,
|
|
|
|
|
0.00000000001,
|
|
|
|
|
0.000000000001,
|
|
|
|
|
0.0000000000001,
|
|
|
|
|
0.00000000000001,
|
|
|
|
|
0.000000000000001,
|
|
|
|
|
0.0000000000000001,
|
|
|
|
|
0.00000000000000001,
|
|
|
|
|
0.000000000000000001};
|
2020-04-16 01:39:03 +02:00
|
|
|
return neg10[-scale];
|
|
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
static const double pow10[] = {1.0,
|
|
|
|
|
10.0,
|
|
|
|
|
100.0,
|
|
|
|
|
1000.0,
|
|
|
|
|
10000.0,
|
|
|
|
|
100000.0,
|
|
|
|
|
1000000.0,
|
|
|
|
|
10000000.0,
|
|
|
|
|
100000000.0,
|
|
|
|
|
1000000000.0,
|
|
|
|
|
10000000000.0,
|
|
|
|
|
100000000000.0,
|
|
|
|
|
1000000000000.0,
|
|
|
|
|
10000000000000.0,
|
|
|
|
|
100000000000000.0,
|
|
|
|
|
1000000000000000.0,
|
|
|
|
|
10000000000000000.0,
|
|
|
|
|
100000000000000000.0,
|
|
|
|
|
1000000000000000000.0};
|
|
|
|
|
return pow10[scale];
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t vl_time_pow10(int n) {
|
|
|
|
|
static const uint64_t pow10[20] = {
|
2021-05-16 12:01:03 +02:00
|
|
|
1ULL,
|
|
|
|
|
10ULL,
|
|
|
|
|
100ULL,
|
|
|
|
|
1000ULL,
|
|
|
|
|
10000ULL,
|
|
|
|
|
100000ULL,
|
|
|
|
|
1000000ULL,
|
|
|
|
|
10000000ULL,
|
|
|
|
|
100000000ULL,
|
|
|
|
|
1000000000ULL,
|
|
|
|
|
10000000000ULL,
|
|
|
|
|
100000000000ULL,
|
|
|
|
|
1000000000000ULL,
|
|
|
|
|
10000000000000ULL,
|
|
|
|
|
100000000000000ULL,
|
|
|
|
|
1000000000000000ULL,
|
|
|
|
|
10000000000000000ULL,
|
|
|
|
|
100000000000000000ULL,
|
|
|
|
|
1000000000000000000ULL,
|
|
|
|
|
};
|
|
|
|
|
return pow10[n];
|
|
|
|
|
}
|
2020-04-16 01:39:03 +02:00
|
|
|
|
2024-03-11 03:34:32 +01:00
|
|
|
std::string vl_timescaled_double(double value, const char* format) VL_PURE {
|
|
|
|
|
const char* suffixp = "s";
|
|
|
|
|
// clang-format off
|
|
|
|
|
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
|
|
|
|
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
|
|
|
|
|
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
|
|
|
|
|
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
|
|
|
|
|
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
|
|
|
|
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
|
|
|
|
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
|
|
|
|
// clang-format on
|
|
|
|
|
char valuestr[100];
|
2026-03-28 04:14:18 +01:00
|
|
|
(void)VL_SNPRINTF(valuestr, 100, format, value, suffixp);
|
2024-03-11 03:34:32 +01:00
|
|
|
return std::string{valuestr}; // Gets converted to string, so no ref to stack
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
|
|
|
|
const VerilatedContext* contextp) VL_MT_SAFE {
|
2020-04-16 01:39:03 +02:00
|
|
|
VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
|
2021-03-07 17:01:54 +01:00
|
|
|
contextp->timeprecisionString());
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2025-06-24 23:30:05 +02:00
|
|
|
void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, bool hasSuffix,
|
|
|
|
|
const std::string& suffix, bool hasWidth, int width,
|
2021-03-07 17:01:54 +01:00
|
|
|
VerilatedContext* contextp) VL_MT_SAFE {
|
2025-06-24 23:30:05 +02:00
|
|
|
if (hasUnits) contextp->impp()->timeFormatUnits(units);
|
|
|
|
|
if (hasPrecision) contextp->impp()->timeFormatPrecision(precision);
|
|
|
|
|
if (hasSuffix) contextp->impp()->timeFormatSuffix(suffix);
|
|
|
|
|
if (hasWidth) contextp->impp()->timeFormatWidth(width);
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2019-12-14 01:11:37 +01:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContext:: Methods
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
VerilatedContext::VerilatedContext()
|
|
|
|
|
: m_impdatap{new VerilatedContextImpData} {
|
|
|
|
|
Verilated::lastContextp(this);
|
|
|
|
|
Verilated::threadContextp(this);
|
2024-01-20 18:28:49 +01:00
|
|
|
m_ns.m_coverageFilename = "coverage.dat";
|
2022-03-25 20:46:50 +01:00
|
|
|
m_ns.m_profExecFilename = "profile_exec.dat";
|
2021-09-27 04:51:11 +02:00
|
|
|
m_ns.m_profVltFilename = "profile.vlt";
|
2024-05-17 16:38:34 +02:00
|
|
|
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
|
2026-05-27 20:33:19 +02:00
|
|
|
m_ns.m_logFD = -1;
|
|
|
|
|
m_ns.m_stdoutFD = -1;
|
|
|
|
|
m_ns.m_stderrFD = -1;
|
2021-03-07 17:01:54 +01:00
|
|
|
m_fdps.resize(31);
|
2021-07-25 19:38:27 +02:00
|
|
|
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
2021-03-07 17:01:54 +01:00
|
|
|
m_fdFreeMct.resize(30);
|
2024-03-24 15:19:26 +01:00
|
|
|
IData id = 1;
|
|
|
|
|
for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
2017-10-25 04:56:58 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
|
|
|
|
// Must declare here not in interface, as otherwise forward declarations not known
|
2022-05-15 16:50:44 +02:00
|
|
|
VerilatedContext::~VerilatedContext() {
|
|
|
|
|
checkMagic(this);
|
|
|
|
|
m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
|
2026-05-27 20:33:19 +02:00
|
|
|
logRestoreOutput();
|
2022-05-15 16:50:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
|
|
|
|
|
if (VL_UNLIKELY(!contextp || contextp->m_magic != MAGIC)) {
|
|
|
|
|
VL_FATAL_MT("", 0, "", // LCOV_EXCL_LINE
|
|
|
|
|
"Attempt to create model using a bad/deleted VerilatedContext pointer");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
|
|
|
|
VerilatedContext::Serialized::Serialized() {
|
2022-07-20 14:15:19 +02:00
|
|
|
constexpr int8_t picosecond = -12;
|
2022-12-03 00:46:38 +01:00
|
|
|
m_timeunit = picosecond; // Initial value until overridden by _Vconfigure
|
|
|
|
|
m_timeprecision = picosecond; // Initial value until overridden by _Vconfigure
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
2024-08-05 23:54:13 +02:00
|
|
|
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
2024-08-05 23:54:13 +02:00
|
|
|
// Set all assert and directive types when true, clear otherwise.
|
|
|
|
|
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
2024-07-10 11:06:13 +02:00
|
|
|
}
|
2024-08-05 23:54:13 +02:00
|
|
|
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
|
|
|
|
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
2026-06-23 00:51:41 +02:00
|
|
|
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
|
2024-07-10 11:06:13 +02:00
|
|
|
}
|
2026-06-17 13:17:39 +02:00
|
|
|
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
|
|
|
|
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
|
|
|
|
// Place the directive bits at each selected assertion type's 3-bit group.
|
|
|
|
|
uint32_t mask = 0;
|
2024-08-05 23:54:13 +02:00
|
|
|
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
2026-06-17 13:17:39 +02:00
|
|
|
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
2024-08-05 23:54:13 +02:00
|
|
|
}
|
2026-06-17 13:17:39 +02:00
|
|
|
return mask;
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
|
|
|
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
|
|
|
|
m_s.m_assertOn |= assertOnMask(types, directives);
|
2024-07-10 11:06:13 +02:00
|
|
|
}
|
2024-08-05 23:54:13 +02:00
|
|
|
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
|
|
|
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
2026-06-17 13:17:39 +02:00
|
|
|
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
|
|
|
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
|
|
|
|
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
|
|
|
|
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
|
|
|
|
const uint32_t mask = assertOnMask(types, directives);
|
2026-06-23 00:51:41 +02:00
|
|
|
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
2026-06-17 13:17:39 +02:00
|
|
|
switch (controlType) {
|
|
|
|
|
case 1: // Lock
|
|
|
|
|
m_s.m_assertLock |= mask;
|
|
|
|
|
break;
|
|
|
|
|
case 2: // Unlock
|
|
|
|
|
m_s.m_assertLock &= ~mask;
|
|
|
|
|
break;
|
|
|
|
|
case 3: // On
|
2026-06-23 00:51:41 +02:00
|
|
|
m_s.m_assertOn |= lockedMask;
|
2026-06-17 13:17:39 +02:00
|
|
|
break;
|
|
|
|
|
case 4: // Off
|
2026-06-23 00:51:41 +02:00
|
|
|
m_s.m_assertOn &= ~lockedMask;
|
2026-06-17 13:17:39 +02:00
|
|
|
break;
|
2026-06-23 00:51:41 +02:00
|
|
|
case 5: { // Kill
|
|
|
|
|
m_s.m_assertOn &= ~lockedMask;
|
|
|
|
|
for (int slot = 0; slot < static_cast<int>(ASSERT_CONTROL_SLOT_COUNT); ++slot) {
|
|
|
|
|
if (VL_BITISSET_I(lockedMask, slot)) { m_s.m_assertKill[slot]++; }
|
|
|
|
|
}
|
2026-06-17 13:17:39 +02:00
|
|
|
break;
|
2024-08-05 23:54:13 +02:00
|
|
|
}
|
2026-06-23 00:51:41 +02:00
|
|
|
case 6: // PassOn
|
|
|
|
|
m_s.m_assertPassOnVacuous |= lockedMask;
|
|
|
|
|
m_s.m_assertPassOnNonvacuous |= lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
case 7: // PassOff
|
|
|
|
|
m_s.m_assertPassOnVacuous &= ~lockedMask;
|
|
|
|
|
m_s.m_assertPassOnNonvacuous &= ~lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
case 8: // FailOn
|
|
|
|
|
m_s.m_assertFailOn |= lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
case 9: // FailOff
|
|
|
|
|
m_s.m_assertFailOn &= ~lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
case 10: // NonvacuousOn
|
|
|
|
|
m_s.m_assertPassOnNonvacuous |= lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
case 11: // VacuousOff
|
|
|
|
|
m_s.m_assertPassOnVacuous &= ~lockedMask;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
VL_WARN_MT("", 0, "",
|
|
|
|
|
("Bad $assertcontrol control_type '" + std::to_string(controlType)
|
|
|
|
|
+ "' (IEEE 1800-2023 Table 20-5)")
|
|
|
|
|
.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uint32_t
|
|
|
|
|
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
|
|
|
|
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
|
|
|
|
const uint32_t mask = assertOnMask(type, directive);
|
|
|
|
|
if (!mask) return 0;
|
|
|
|
|
switch (query) { // LCOV_EXCL_BR_LINE
|
|
|
|
|
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
|
|
|
|
|
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
|
|
|
|
|
assert(mask && (mask & (mask - 1)) == 0);
|
|
|
|
|
return m_s.m_assertKill[VL_CLOG2_I(mask)];
|
|
|
|
|
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
|
|
|
|
|
return (m_s.m_assertPassOnVacuous & mask) != 0;
|
|
|
|
|
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
|
|
|
|
|
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
|
|
|
|
|
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
|
|
|
|
|
default: // LCOV_EXCL_START
|
|
|
|
|
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
|
|
|
|
|
VL_UNREACHABLE;
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2020-11-20 03:32:33 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_calcUnusedSigs = flag;
|
|
|
|
|
}
|
2024-01-20 18:28:49 +01:00
|
|
|
void VerilatedContext::coverageFilename(const std::string& flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_coverageFilename = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::coverageFilename() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_coverageFilename;
|
|
|
|
|
}
|
2026-05-27 20:33:19 +02:00
|
|
|
void VerilatedContext::logFilename(const std::string& flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
assert(m_ns.m_logFD == -1);
|
|
|
|
|
m_ns.m_logFilename = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::logFilename() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_logFilename;
|
|
|
|
|
}
|
|
|
|
|
bool VerilatedContext::logOutputToFile() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_logFD >= 0;
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::logOutputToFile(bool append) VL_MT_SAFE {
|
|
|
|
|
int log_fd = ::open(m_ns.m_logFilename.c_str(),
|
|
|
|
|
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
|
|
|
|
|
std::string error_msg;
|
|
|
|
|
if (log_fd >= 0) {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_logFD = log_fd;
|
|
|
|
|
// See https://man7.org/linux/man-pages/man2/dup.2.html
|
|
|
|
|
m_ns.m_stdoutFD = ::dup(STDOUT_FILENO); // Save original fd
|
|
|
|
|
::dup2(m_ns.m_logFD, STDOUT_FILENO); // Replace STDOUT_FILENO with m_logFD
|
|
|
|
|
m_ns.m_stderrFD = ::dup(STDERR_FILENO); // Save original fd
|
|
|
|
|
::dup2(m_ns.m_logFD, STDERR_FILENO); // Replace STDERR_FILENO with m_logFD
|
|
|
|
|
} else {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_logFD = -1;
|
|
|
|
|
const std::string op_str = append ? "appended" : "created";
|
|
|
|
|
error_msg = "Logfile " + m_ns.m_logFilename + " cannot be " + op_str;
|
|
|
|
|
}
|
|
|
|
|
if (!error_msg.empty()) { VL_FATAL_MT("", 0, "", error_msg.c_str()); }
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::logRestoreOutput() VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
if (m_ns.m_logFD >= 0) {
|
|
|
|
|
std::fflush(stdout); // Flush logfile
|
|
|
|
|
std::fflush(stderr);
|
|
|
|
|
::dup2(m_ns.m_stdoutFD, STDOUT_FILENO); // Restore STDOUT_FILENO with saved m_stdoutFD
|
|
|
|
|
::dup2(m_ns.m_stderrFD, STDERR_FILENO); // Restore STDERR_FILENO with saved m_stderrFD
|
|
|
|
|
::close(m_ns.m_logFD); // Close logfile
|
|
|
|
|
m_ns.m_logFD = -1;
|
|
|
|
|
m_ns.m_stdoutFD = -1;
|
|
|
|
|
m_ns.m_stderrFD = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_timeDumpMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_dumpfile = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_timeDumpMutex};
|
2021-03-14 14:08:17 +01:00
|
|
|
return m_dumpfile;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
2022-07-30 17:52:35 +02:00
|
|
|
std::string out = dumpfile();
|
2021-03-14 14:08:17 +01:00
|
|
|
if (VL_UNLIKELY(out.empty())) {
|
2022-10-09 20:18:14 +02:00
|
|
|
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not preceded by $dumpfile\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
return "";
|
2020-11-20 03:32:33 +01:00
|
|
|
}
|
2021-03-14 14:08:17 +01:00
|
|
|
return out;
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
void VerilatedContext::errorCount(int val) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_errorCount = val;
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::errorCountInc() VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
++m_s.m_errorCount;
|
2019-02-05 02:28:17 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_errorLimit = val;
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_fatalOnError = flag;
|
2019-11-17 00:25:47 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_fatalOnVpiError = flag;
|
2019-11-17 00:25:47 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_gotError = flag;
|
2019-11-17 00:25:47 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_gotFinish = flag;
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2026-03-12 18:09:54 +01:00
|
|
|
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_executingFinal;
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContext::executingFinal(bool flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_executingFinal = flag;
|
|
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
void VerilatedContext::profExecStart(uint64_t flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2022-03-25 20:46:50 +01:00
|
|
|
m_ns.m_profExecStart = flag;
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
void VerilatedContext::profExecWindow(uint64_t flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2022-03-25 20:46:50 +01:00
|
|
|
m_ns.m_profExecWindow = flag;
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2022-03-25 20:46:50 +01:00
|
|
|
void VerilatedContext::profExecFilename(const std::string& flag) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2022-03-25 20:46:50 +01:00
|
|
|
m_ns.m_profExecFilename = flag;
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
2022-03-25 20:46:50 +01:00
|
|
|
std::string VerilatedContext::profExecFilename() const VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2022-03-25 20:46:50 +01:00
|
|
|
return m_ns.m_profExecFilename;
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
2021-09-27 04:51:11 +02:00
|
|
|
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_profVltFilename = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_profVltFilename;
|
|
|
|
|
}
|
2026-04-04 23:26:43 +02:00
|
|
|
void VerilatedContext::solverLogFilename(const std::string& flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_solverLogFilename = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::solverLogFilename() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_solverLogFilename;
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_solverProgram = flag;
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_solverProgram;
|
|
|
|
|
}
|
2024-03-25 12:03:17 +01:00
|
|
|
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_s.m_quiet = flag;
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::randReset(int val) VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_randReset = val;
|
|
|
|
|
}
|
2026-04-04 23:26:43 +02:00
|
|
|
|
|
|
|
|
std::string VerilatedContext::timeWithUnitString() const VL_MT_SAFE {
|
|
|
|
|
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
|
|
|
|
* vl_time_multiplier(timeprecision() - timeunit());
|
|
|
|
|
return vl_timescaled_double(simtimeInUnits);
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
|
2020-04-16 01:39:03 +02:00
|
|
|
if (value < 0) value = -value; // Stored as 0..15
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_timeunit = value;
|
2020-04-16 01:39:03 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
|
|
|
|
|
const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
|
|
|
|
|
return vl_time_str(timeprecision());
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
2022-07-12 12:41:15 +02:00
|
|
|
void VerilatedContext::threads(unsigned n) {
|
2024-12-11 14:52:41 +01:00
|
|
|
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
|
2022-07-12 12:41:15 +02:00
|
|
|
|
|
|
|
|
if (m_threadPool) {
|
|
|
|
|
VL_FATAL_MT(
|
|
|
|
|
__FILE__, __LINE__, "",
|
|
|
|
|
"%Error: Cannot set simulation threads after the thread pool has been created.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 17:05:28 +01:00
|
|
|
m_useNumaAssign = true;
|
2022-07-12 12:41:15 +02:00
|
|
|
if (m_threads == n) return; // To avoid unnecessary warnings
|
|
|
|
|
m_threads = n;
|
2025-10-28 19:10:40 +01:00
|
|
|
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
|
|
|
|
|
if (m_threads > threadsAvailableToProcess) {
|
|
|
|
|
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
|
|
|
|
|
"count set to %u. This will likely cause significant slowdown.\n",
|
|
|
|
|
threadsAvailableToProcess, m_threads);
|
2022-07-12 12:41:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 17:05:28 +01:00
|
|
|
void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
2022-12-15 03:12:54 +01:00
|
|
|
// Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts
|
|
|
|
|
// m_argMutex here is the same as in impp()->commandArgsAddGuts;
|
|
|
|
|
// due to clang limitations, it doesn't properly check it
|
|
|
|
|
impp()->commandArgsGuts(argc, argv);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContext::commandArgsAdd(int argc, const char** argv)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
2022-12-15 03:12:54 +01:00
|
|
|
// Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts
|
|
|
|
|
// m_argMutex here is the same as in impp()->commandArgsAddGuts;
|
|
|
|
|
// due to clang limitations, it doesn't properly check it
|
|
|
|
|
impp()->commandArgsAddGutsLock(argc, argv);
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
|
|
|
|
const std::string& match = impp()->argPlusMatch(prefixp);
|
2026-03-26 18:55:14 +01:00
|
|
|
static thread_local std::string t_outstr;
|
2021-03-07 17:01:54 +01:00
|
|
|
if (match.empty()) return "";
|
2026-03-28 04:14:18 +01:00
|
|
|
t_outstr = match;
|
2026-03-26 18:55:14 +01:00
|
|
|
return t_outstr.c_str();
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
void VerilatedContext::internalsDump() const VL_MT_SAFE {
|
|
|
|
|
VL_PRINTF_MT("internalsDump:\n");
|
|
|
|
|
VerilatedImp::versionDump();
|
|
|
|
|
impp()->commandArgDump();
|
|
|
|
|
impp()->scopesDump();
|
|
|
|
|
VerilatedImp::exportsDump();
|
|
|
|
|
VerilatedImp::userDump();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 03:36:52 +02:00
|
|
|
void VerilatedContext::addModel(const VerilatedModel* modelp) {
|
2024-03-25 12:03:17 +01:00
|
|
|
if (!quiet()) {
|
|
|
|
|
// CPU time isn't read as starting point until model creation, so that quiet() is set
|
|
|
|
|
// Thus if quiet(), avoids slow OS read affecting some usages that make many models
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_cpuTimeStart.start();
|
|
|
|
|
m_ns.m_wallTimeStart.start();
|
|
|
|
|
}
|
2022-07-12 12:41:15 +02:00
|
|
|
|
2024-06-12 01:38:58 +02:00
|
|
|
// We look for time passing, as opposed to post-eval(), as embedded
|
|
|
|
|
// models might get added inside initial blocks.
|
2025-09-20 17:02:42 +02:00
|
|
|
if (VL_UNLIKELY(time())) {
|
|
|
|
|
const std::string msg
|
|
|
|
|
= "Adding model '"s + modelp->hierName()
|
|
|
|
|
+ "' when time is non-zero. ... Suggest check time(), or for restarting"
|
|
|
|
|
" model use a new VerilatedContext";
|
|
|
|
|
VL_FATAL_MT("", 0, "", msg.c_str());
|
|
|
|
|
}
|
2024-06-12 01:38:58 +02:00
|
|
|
|
|
|
|
|
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
2024-03-30 16:13:14 +01:00
|
|
|
m_threadsInModels += modelp->threads();
|
2022-11-30 02:36:56 +01:00
|
|
|
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
2022-07-12 12:41:15 +02:00
|
|
|
std::ostringstream msg;
|
|
|
|
|
msg << "VerilatedContext has " << m_threads << " threads but model '"
|
|
|
|
|
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
|
|
|
|
|
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
|
|
|
|
|
const std::string str = msg.str();
|
|
|
|
|
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
|
|
|
|
if (m_threads == 1) return nullptr;
|
|
|
|
|
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
|
|
|
|
|
return m_threadPool.get();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-30 13:02:55 +02:00
|
|
|
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
|
|
|
|
|
|
|
|
|
|
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
|
2025-08-20 03:36:52 +02:00
|
|
|
if (VL_UNLIKELY(m_threadPool)) (void)m_threadPool.release();
|
2023-09-02 00:07:15 +02:00
|
|
|
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
|
2023-08-30 13:02:55 +02:00
|
|
|
return m_threadPool.get();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 12:41:15 +02:00
|
|
|
VerilatedVirtualBase*
|
|
|
|
|
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
|
|
|
|
|
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
|
|
|
|
|
return m_executionProfiler.get();
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContextImp:: Methods - command line
|
2024-03-30 18:16:36 +01:00
|
|
|
|
2022-12-15 03:12:54 +01:00
|
|
|
void VerilatedContextImp::commandArgsGuts(int argc, const char** argv)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{m_argMutex};
|
|
|
|
|
m_args.m_argVec.clear(); // Empty first, then add
|
|
|
|
|
commandArgsAddGuts(argc, argv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VerilatedContextImp::commandArgsAddGutsLock(int argc, const char** argv)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{m_argMutex};
|
|
|
|
|
commandArgsAddGuts(argc, argv);
|
|
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
|
|
|
|
void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
|
|
|
|
|
if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
|
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
2021-07-25 19:38:27 +02:00
|
|
|
m_args.m_argVec.emplace_back(argv[i]);
|
2021-03-07 17:01:54 +01:00
|
|
|
commandArgVl(argv[i]);
|
|
|
|
|
}
|
|
|
|
|
m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_argMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
VL_PRINTF_MT(" Argv:");
|
|
|
|
|
for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
|
|
|
|
|
VL_PRINTF_MT("\n");
|
|
|
|
|
}
|
|
|
|
|
std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_argMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
// Note prefixp does not include the leading "+"
|
2021-06-19 04:19:35 +02:00
|
|
|
const size_t len = std::strlen(prefixp);
|
2021-03-07 17:01:54 +01:00
|
|
|
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
|
|
|
|
|
m_args.m_argVecLoaded = true; // Complain only once
|
|
|
|
|
VL_FATAL_MT("unknown", 0, "",
|
|
|
|
|
"%Error: Verilog called $test$plusargs or $value$plusargs without"
|
|
|
|
|
" testbench C first calling Verilated::commandArgs(argc,argv).");
|
|
|
|
|
}
|
|
|
|
|
for (const auto& i : m_args.m_argVec) {
|
|
|
|
|
if (i[0] == '+') {
|
2021-03-27 02:23:18 +01:00
|
|
|
if (0 == std::strncmp(prefixp, i.c_str() + 1, len)) return i;
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
// Return string representing current argv
|
|
|
|
|
// Only used by VPI so uses static storage, only supports most recent called context
|
|
|
|
|
std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_argMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
static bool s_loaded = false;
|
|
|
|
|
static int s_argc = 0;
|
|
|
|
|
static char** s_argvp = nullptr;
|
|
|
|
|
if (VL_UNLIKELY(!s_loaded)) {
|
|
|
|
|
s_loaded = true;
|
2022-12-22 18:19:09 +01:00
|
|
|
s_argc = static_cast<int>(m_args.m_argVec.size());
|
2021-03-07 17:01:54 +01:00
|
|
|
s_argvp = new char*[s_argc + 1];
|
|
|
|
|
int in = 0;
|
|
|
|
|
for (const auto& i : m_args.m_argVec) {
|
|
|
|
|
s_argvp[in] = new char[i.length() + 1];
|
2024-01-18 01:48:07 +01:00
|
|
|
std::memcpy(s_argvp[in], i.c_str(), i.length() + 1);
|
2021-03-07 17:01:54 +01:00
|
|
|
++in;
|
|
|
|
|
}
|
|
|
|
|
s_argvp[s_argc] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
return std::make_pair(s_argc, s_argvp);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 18:10:51 +02:00
|
|
|
// Derive a non-deterministic non-zero positive seed value from the system
|
|
|
|
|
// clocks. Used to implement +verilator+seed+0, which means "pick a random
|
|
|
|
|
// seed". Returning the actual picked value lets $get_initial_random_seed()
|
|
|
|
|
// expose it so the user can reproduce the run later by passing
|
|
|
|
|
// +verilator+seed+<that_value>.
|
|
|
|
|
static uint64_t pickRandomSeed() VL_MT_SAFE {
|
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
// Combine steady_clock and system_clock to get entropy even when one has
|
|
|
|
|
// low resolution (e.g. high_resolution_clock aliases system_clock on MSVC).
|
|
|
|
|
const uint64_t t1 = static_cast<uint64_t>(
|
|
|
|
|
duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count());
|
|
|
|
|
const uint64_t t2 = static_cast<uint64_t>(
|
|
|
|
|
duration_cast<microseconds>(system_clock::now().time_since_epoch()).count());
|
|
|
|
|
// vl_splitmix64 avalanches bits so closely-spaced timestamps look unrelated.
|
|
|
|
|
uint64_t seed = vl_splitmix64(t1 ^ t2).second;
|
|
|
|
|
// Keep within [1, INT_MAX] so it round-trips through the int seed field.
|
|
|
|
|
seed &= 0x7fffffffULL;
|
|
|
|
|
if (seed == 0) seed = 1;
|
|
|
|
|
return seed;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
2021-03-27 02:23:18 +01:00
|
|
|
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
2022-03-26 23:47:10 +01:00
|
|
|
std::string str;
|
|
|
|
|
uint64_t u64;
|
2024-01-20 18:28:49 +01:00
|
|
|
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
|
|
|
|
coverageFilename(str);
|
|
|
|
|
} else if (arg == "+verilator+debug") {
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::debug(4);
|
2022-03-26 23:47:10 +01:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+debugi+", u64, 0,
|
|
|
|
|
std::numeric_limits<int>::max())) {
|
|
|
|
|
Verilated::debug(static_cast<int>(u64));
|
|
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+error+limit+", u64, 0,
|
|
|
|
|
std::numeric_limits<int>::max())) {
|
|
|
|
|
errorLimit(static_cast<int>(u64));
|
2021-03-07 17:01:54 +01:00
|
|
|
} else if (arg == "+verilator+help") {
|
|
|
|
|
VerilatedImp::versionDump();
|
|
|
|
|
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
|
|
|
|
|
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
|
|
|
|
"Exiting due to command line argument (not an error)");
|
2026-05-27 20:33:19 +02:00
|
|
|
} else if (commandArgVlString(arg, "+verilator+log+file+", str)) {
|
|
|
|
|
logFilename(str);
|
|
|
|
|
logOutputToFile(false /* append */);
|
2021-03-17 23:25:54 +01:00
|
|
|
} else if (arg == "+verilator+noassert") {
|
|
|
|
|
assertOn(false);
|
2023-11-04 18:28:36 +01:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
2022-03-25 20:46:50 +01:00
|
|
|
profExecStart(u64);
|
2023-11-04 18:28:36 +01:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
2022-03-25 20:46:50 +01:00
|
|
|
profExecWindow(u64);
|
2023-11-04 18:28:36 +01:00
|
|
|
} else if (commandArgVlString(arg, "+verilator+prof+exec+file+", str)) {
|
2022-03-25 20:46:50 +01:00
|
|
|
profExecFilename(str);
|
2022-03-26 23:47:10 +01:00
|
|
|
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
|
|
|
|
|
profVltFilename(str);
|
2024-03-25 12:03:17 +01:00
|
|
|
} else if (arg == "+verilator+quiet") {
|
|
|
|
|
quiet(true);
|
2022-03-26 23:47:10 +01:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
|
|
|
|
|
randReset(static_cast<int>(u64));
|
2026-04-04 23:26:43 +02:00
|
|
|
} else if (commandArgVlString(arg, "+verilator+solver+file+", str)) {
|
|
|
|
|
solverLogFilename(str);
|
2026-01-12 15:53:49 +01:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+wno+unsatconstr+", u64, 0, 1)) {
|
|
|
|
|
warnUnsatConstr(u64 == 0); // wno means disable, so invert
|
2026-05-05 18:10:51 +02:00
|
|
|
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 0,
|
2022-03-26 23:47:10 +01:00
|
|
|
std::numeric_limits<int>::max())) {
|
2026-05-05 18:10:51 +02:00
|
|
|
// +verilator+seed+0 means "pick a random seed". Replace the
|
|
|
|
|
// user-supplied 0 with a non-deterministic non-zero value derived
|
|
|
|
|
// from the high-resolution clock and store that as the actual
|
|
|
|
|
// seed, so $get_initial_random_seed() returns the picked value
|
|
|
|
|
// and the run can be reproduced by passing +verilator+seed+<that_value>.
|
|
|
|
|
if (u64 == 0) u64 = pickRandomSeed();
|
2022-03-26 23:47:10 +01:00
|
|
|
randSeed(static_cast<int>(u64));
|
2026-06-28 15:28:09 +02:00
|
|
|
} else if (commandArgVlString(arg, "+verilator+vpi+", str)) {
|
|
|
|
|
// With --vpi, load the requested shared library now. Without --vpi there is
|
|
|
|
|
// no VPI runtime, so warn the argument is ignored.
|
|
|
|
|
#if VM_VPI
|
|
|
|
|
Verilated::loadVpiLib(str);
|
|
|
|
|
#else
|
|
|
|
|
VL_WARN_MT(
|
|
|
|
|
"COMMAND_LINE", 0, "",
|
|
|
|
|
("+verilator+vpi+ ignored: simulation was not compiled with --vpi '" + arg + "'")
|
|
|
|
|
.c_str()); // LCOV_EXCL_LINE (gcov zeroes this wrapped continuation line)
|
|
|
|
|
#endif
|
2021-03-07 17:01:54 +01:00
|
|
|
} else if (arg == "+verilator+V") {
|
|
|
|
|
VerilatedImp::versionDump(); // Someday more info too
|
|
|
|
|
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
|
|
|
|
"Exiting due to command line argument (not an error)");
|
|
|
|
|
} else if (arg == "+verilator+version") {
|
|
|
|
|
VerilatedImp::versionDump();
|
|
|
|
|
VL_FATAL_MT("COMMAND_LINE", 0, "",
|
|
|
|
|
"Exiting due to command line argument (not an error)");
|
|
|
|
|
} else {
|
2022-03-26 23:47:10 +01:00
|
|
|
const std::string msg = "Unknown runtime argument: " + arg;
|
|
|
|
|
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-26 23:47:10 +01:00
|
|
|
|
|
|
|
|
bool VerilatedContextImp::commandArgVlString(const std::string& arg, const std::string& prefix,
|
|
|
|
|
std::string& valuer) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const size_t len = prefix.length();
|
2021-03-27 02:23:18 +01:00
|
|
|
if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
|
2021-03-07 17:01:54 +01:00
|
|
|
valuer = arg.substr(len);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
return false;
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-26 23:47:10 +01:00
|
|
|
bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::string& prefix,
|
|
|
|
|
uint64_t& valuer, uint64_t min, uint64_t max) {
|
|
|
|
|
std::string str;
|
|
|
|
|
if (commandArgVlString(arg, prefix, str)) {
|
|
|
|
|
const auto fail = [&](const std::string& extra = "") {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Argument '" << prefix << "' must be an unsigned integer";
|
|
|
|
|
if (min != std::numeric_limits<uint64_t>::min()) ss << ", greater than " << min - 1;
|
|
|
|
|
if (max != std::numeric_limits<uint64_t>::max()) ss << ", less than " << max + 1;
|
|
|
|
|
if (!extra.empty()) ss << ". " << extra;
|
|
|
|
|
const std::string& msg = ss.str();
|
|
|
|
|
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-25 23:12:13 +01:00
|
|
|
if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail();
|
2022-03-26 23:47:10 +01:00
|
|
|
char* end;
|
|
|
|
|
valuer = std::strtoull(str.c_str(), &end, 10);
|
|
|
|
|
if (errno == ERANGE) fail("Value out of range of uint64_t");
|
|
|
|
|
if (valuer < min || valuer > max) fail();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContext:: + VerilatedContextImp:: Methods - random
|
|
|
|
|
|
|
|
|
|
void VerilatedContext::randSeed(int val) VL_MT_SAFE {
|
|
|
|
|
// As we have per-thread state, the epoch must be static,
|
|
|
|
|
// and so the rand seed's mutex must also be static
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
m_s.m_randSeed = val;
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
|
2022-12-03 00:46:38 +01:00
|
|
|
// Observers must see new epoch AFTER seed updated
|
2021-03-07 17:01:54 +01:00
|
|
|
std::atomic_signal_fence(std::memory_order_release);
|
|
|
|
|
VerilatedContextImp::s().s_randSeedEpoch = newEpoch;
|
|
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
|
2021-03-07 17:01:54 +01:00
|
|
|
if (randSeed() != 0) {
|
2022-03-27 21:27:40 +02:00
|
|
|
return ((static_cast<uint64_t>(randSeed()) << 32) ^ (static_cast<uint64_t>(randSeed())));
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
2026-03-28 04:14:18 +01:00
|
|
|
return ((static_cast<uint64_t>(vl_sys_rand32()) << 32)
|
|
|
|
|
^ (static_cast<uint64_t>(vl_sys_rand32())));
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-25 12:03:17 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContext:: Statistics
|
|
|
|
|
|
|
|
|
|
double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_cpuTimeStart.deltaTime();
|
|
|
|
|
}
|
|
|
|
|
double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
return m_ns.m_wallTimeStart.deltaTime();
|
|
|
|
|
}
|
2024-03-30 18:16:36 +01:00
|
|
|
void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
|
|
|
|
if (quiet()) return;
|
|
|
|
|
VL_PRINTF("- S i m u l a t i o n R e p o r t: %s %s\n", Verilated::productName(),
|
|
|
|
|
Verilated::productVersion());
|
|
|
|
|
const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
|
|
|
|
|
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
|
|
|
|
* vl_time_multiplier(timeprecision() - timeunit());
|
2026-04-04 23:26:43 +02:00
|
|
|
const std::string simtime = timeWithUnitString();
|
2024-03-30 18:16:36 +01:00
|
|
|
const double walltime = statWallTimeSinceStart();
|
|
|
|
|
const double cputime = statCpuTimeSinceStart();
|
|
|
|
|
const std::string simtimePerf
|
2026-05-28 08:20:54 +02:00
|
|
|
= vl_timescaled_double((walltime != 0.0) ? (simtimeInUnits / walltime) : 0, "%0.3f %s");
|
2024-03-30 18:16:36 +01:00
|
|
|
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
|
|
|
|
|
simtime.c_str(), walltime, simtimePerf.c_str());
|
2026-03-28 04:14:18 +01:00
|
|
|
uint64_t memPeak;
|
|
|
|
|
uint64_t memCurrent;
|
2025-07-24 07:44:33 +02:00
|
|
|
VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/);
|
|
|
|
|
const double modelMB = memPeak / 1024.0 / 1024.0;
|
2026-01-19 14:31:22 +01:00
|
|
|
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; allocated %0.0f MB\n", cputime,
|
2024-03-30 18:16:36 +01:00
|
|
|
threadsInModels(), modelMB);
|
|
|
|
|
}
|
2024-03-25 12:03:17 +01:00
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContext:: Methods - scopes
|
|
|
|
|
|
|
|
|
|
void VerilatedContext::scopesDump() const VL_MT_SAFE {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
VL_PRINTF_MT(" scopesDump:\n");
|
|
|
|
|
for (const auto& i : m_impdatap->m_nameMap) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedScope* const scopep = i.second;
|
2021-03-07 17:01:54 +01:00
|
|
|
scopep->scopeDump();
|
|
|
|
|
}
|
|
|
|
|
VL_PRINTF_MT("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
|
|
|
|
|
// Slow ok - called once/scope at construction
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
const auto it = m_impdatap->m_nameMap.find(scopep->name());
|
|
|
|
|
if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
|
|
|
|
|
}
|
|
|
|
|
void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
|
|
|
|
// Slow ok - called once/scope at destruction
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
VerilatedImp::userEraseScope(scopep);
|
|
|
|
|
const auto it = m_impdatap->m_nameMap.find(scopep->name());
|
|
|
|
|
if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
|
|
|
|
|
}
|
|
|
|
|
const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
|
|
|
|
|
// Thread save only assuming this is called only after model construction completed
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
2021-03-07 17:01:54 +01:00
|
|
|
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
|
|
|
|
const auto& it = m_impdatap->m_nameMap.find(namep);
|
|
|
|
|
if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
|
|
|
|
|
return &(impp()->m_impdatap->m_nameMap);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 21:00:52 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedContext:: Methods - trace
|
|
|
|
|
|
2024-09-04 03:42:06 +02:00
|
|
|
void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) {
|
2024-03-30 21:00:52 +01:00
|
|
|
VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n"););
|
|
|
|
|
if (tfp->isOpen()) {
|
|
|
|
|
VL_FATAL_MT("", 0, "",
|
|
|
|
|
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
|
|
|
|
" after 'VerilatedTrace*::open()'\n");
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
|
|
|
|
// So check for and suppress second and later calls
|
|
|
|
|
if (tfp->modelConnected()) return;
|
|
|
|
|
tfp->modelConnected(true);
|
|
|
|
|
}
|
|
|
|
|
// We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called
|
|
|
|
|
// nope: const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
if (m_ns.m_traceBaseModelCbs.empty())
|
|
|
|
|
VL_FATAL_MT("", 0, "",
|
|
|
|
|
"Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated"
|
2025-04-05 16:46:39 +02:00
|
|
|
" with --trace-fst or --trace-vcd option");
|
2025-08-20 03:36:52 +02:00
|
|
|
for (const auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options);
|
2024-03-30 21:00:52 +01:00
|
|
|
}
|
|
|
|
|
void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE {
|
|
|
|
|
// Model creation registering a callback for when Verilated::trace() called
|
|
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
|
|
|
|
m_ns.m_traceBaseModelCbs.push_back(cb);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedSyms:: Methods
|
|
|
|
|
|
|
|
|
|
VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
|
|
|
|
|
: _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) {
|
2022-05-15 16:50:44 +02:00
|
|
|
VerilatedContext::checkMagic(_vm_contextp__);
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp(_vm_contextp__);
|
|
|
|
|
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerilatedSyms::~VerilatedSyms() {
|
2022-05-15 16:50:44 +02:00
|
|
|
VerilatedContext::checkMagic(_vm_contextp__);
|
2021-03-07 17:01:54 +01:00
|
|
|
delete __Vm_evalMsgQp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
// Verilated:: Methods
|
|
|
|
|
|
|
|
|
|
void Verilated::debug(int level) VL_MT_SAFE {
|
|
|
|
|
s_debug = level;
|
|
|
|
|
if (level) {
|
|
|
|
|
#ifdef VL_DEBUG
|
|
|
|
|
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on."
|
|
|
|
|
" Message prefix indicates {<thread>,<sequence_number>}.\n"););
|
|
|
|
|
#else
|
|
|
|
|
VL_PRINTF_MT("- Verilated::debug attempted,"
|
|
|
|
|
" but compiled without VL_DEBUG, so messages suppressed.\n"
|
|
|
|
|
"- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-19 16:15:07 +01:00
|
|
|
const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Used by symbol table creation to make module names
|
2022-11-05 13:47:34 +01:00
|
|
|
static thread_local char* t_strp = nullptr;
|
|
|
|
|
static thread_local size_t t_len = 0;
|
2021-12-19 16:15:07 +01:00
|
|
|
const size_t newlen = std::strlen(n1) + std::strlen(n2) + std::strlen(delimiter) + 1;
|
2021-03-06 16:33:43 +01:00
|
|
|
if (VL_UNLIKELY(!t_strp || newlen > t_len)) {
|
2020-12-02 01:01:20 +01:00
|
|
|
if (t_strp) delete[] t_strp;
|
|
|
|
|
t_strp = new char[newlen];
|
|
|
|
|
t_len = newlen;
|
2006-08-30 21:50:24 +02:00
|
|
|
}
|
2021-03-06 16:33:43 +01:00
|
|
|
char* dp = t_strp;
|
|
|
|
|
for (const char* sp = n1; *sp;) *dp++ = *sp++;
|
|
|
|
|
for (const char* sp = delimiter; *sp;) *dp++ = *sp++;
|
|
|
|
|
for (const char* sp = n2; *sp;) *dp++ = *sp++;
|
|
|
|
|
*dp++ = '\0';
|
2020-12-02 01:01:20 +01:00
|
|
|
return t_strp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-12 08:15:42 +02:00
|
|
|
//=========================================================================
|
|
|
|
|
// Flush and exit callbacks
|
|
|
|
|
|
|
|
|
|
// Keeping these out of class Verilated to avoid having to include <list>
|
|
|
|
|
// in verilated.h (for compilation speed)
|
2021-03-13 00:10:45 +01:00
|
|
|
using VoidPCbList = std::list<std::pair<Verilated::VoidPCb, void*>>;
|
2021-02-27 20:43:52 +01:00
|
|
|
static struct {
|
|
|
|
|
VerilatedMutex s_flushMutex;
|
|
|
|
|
VoidPCbList s_flushCbs VL_GUARDED_BY(s_flushMutex);
|
|
|
|
|
VerilatedMutex s_exitMutex;
|
|
|
|
|
VoidPCbList s_exitCbs VL_GUARDED_BY(s_exitMutex);
|
|
|
|
|
} VlCbStatic;
|
2020-06-12 08:15:42 +02:00
|
|
|
|
2022-12-16 16:14:02 +01:00
|
|
|
static void addCbFlush(Verilated::VoidPCb cb, void* datap)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
2026-03-28 04:14:18 +01:00
|
|
|
const std::pair<Verilated::VoidPCb, void*> pair(cb, datap);
|
2022-12-16 16:14:02 +01:00
|
|
|
VlCbStatic.s_flushCbs.remove(pair); // Just in case it's a duplicate
|
|
|
|
|
VlCbStatic.s_flushCbs.push_back(pair);
|
|
|
|
|
}
|
|
|
|
|
static void addCbExit(Verilated::VoidPCb cb, void* datap)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
2026-03-28 04:14:18 +01:00
|
|
|
const std::pair<Verilated::VoidPCb, void*> pair(cb, datap);
|
2022-12-16 16:14:02 +01:00
|
|
|
VlCbStatic.s_exitCbs.remove(pair); // Just in case it's a duplicate
|
|
|
|
|
VlCbStatic.s_exitCbs.push_back(pair);
|
2020-06-12 08:15:42 +02:00
|
|
|
}
|
2022-12-16 16:14:02 +01:00
|
|
|
static void removeCbFlush(Verilated::VoidPCb cb, void* datap)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
2026-03-28 04:14:18 +01:00
|
|
|
const std::pair<Verilated::VoidPCb, void*> pair(cb, datap);
|
2022-12-16 16:14:02 +01:00
|
|
|
VlCbStatic.s_flushCbs.remove(pair);
|
|
|
|
|
}
|
|
|
|
|
static void removeCbExit(Verilated::VoidPCb cb, void* datap)
|
|
|
|
|
VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) {
|
|
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
2026-03-28 04:14:18 +01:00
|
|
|
const std::pair<Verilated::VoidPCb, void*> pair(cb, datap);
|
2022-12-16 16:14:02 +01:00
|
|
|
VlCbStatic.s_exitCbs.remove(pair);
|
2020-06-12 08:15:42 +02:00
|
|
|
}
|
2020-12-23 21:21:33 +01:00
|
|
|
static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE {
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const auto& i : cbs) i.first(i.second);
|
2010-03-13 02:00:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-16 16:14:02 +01:00
|
|
|
void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbFlush(cb, datap); }
|
|
|
|
|
void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbFlush(cb, datap); }
|
2020-06-12 08:15:42 +02:00
|
|
|
void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
2021-02-27 20:43:52 +01:00
|
|
|
// Flush routines may call flush, so avoid mutex deadlock
|
|
|
|
|
static std::atomic<int> s_recursing;
|
|
|
|
|
if (!s_recursing++) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
2021-02-27 20:43:52 +01:00
|
|
|
runCallbacks(VlCbStatic.s_flushCbs);
|
|
|
|
|
}
|
|
|
|
|
--s_recursing;
|
2021-03-27 02:23:18 +01:00
|
|
|
std::fflush(stderr);
|
|
|
|
|
std::fflush(stdout);
|
2020-06-12 08:15:42 +02:00
|
|
|
// When running internal code coverage (gcc --coverage, as opposed to
|
|
|
|
|
// verilator --coverage), dump coverage data to properly cover failing
|
|
|
|
|
// tests.
|
2022-07-27 19:42:45 +02:00
|
|
|
VL_GCOV_DUMP();
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-16 16:14:02 +01:00
|
|
|
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
|
|
|
|
|
void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbExit(cb, datap); }
|
2020-06-12 08:15:42 +02:00
|
|
|
void Verilated::runExitCallbacks() VL_MT_SAFE {
|
2021-02-27 20:43:52 +01:00
|
|
|
static std::atomic<int> s_recursing;
|
|
|
|
|
if (!s_recursing++) {
|
2021-07-24 14:36:11 +02:00
|
|
|
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
2021-02-27 20:43:52 +01:00
|
|
|
runCallbacks(VlCbStatic.s_exitCbs);
|
|
|
|
|
}
|
|
|
|
|
--s_recursing;
|
2020-06-12 08:15:42 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; }
|
|
|
|
|
const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; }
|
2018-10-14 17:21:09 +02:00
|
|
|
|
2020-04-05 15:30:23 +02:00
|
|
|
void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
|
|
|
|
|
// Slowpath - Called only on error
|
|
|
|
|
VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
|
2022-10-02 22:35:45 +02:00
|
|
|
VL_UNREACHABLE;
|
2020-04-05 15:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-06 03:47:55 +01:00
|
|
|
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
|
|
|
|
// Slowpath - Called only when signal sets too high of a bit
|
2024-01-29 02:24:28 +01:00
|
|
|
const std::string msg = ("Testbench C set input '"s + signame
|
2021-06-19 04:19:35 +02:00
|
|
|
+ "' to value that overflows what the signal's width can fit");
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
2022-10-02 22:35:45 +02:00
|
|
|
VL_UNREACHABLE;
|
2017-11-06 03:47:55 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-30 04:44:37 +01:00
|
|
|
void Verilated::scTimePrecisionError(int sc_prec, int vl_prec) VL_MT_SAFE {
|
|
|
|
|
std::ostringstream msg;
|
|
|
|
|
msg << "SystemC's sc_set_time_resolution is 10^-" << sc_prec
|
|
|
|
|
<< ", which does not match Verilog timeprecision 10^-" << vl_prec
|
|
|
|
|
<< ". Suggest use 'sc_set_time_resolution(" << vl_time_str(vl_prec)
|
|
|
|
|
<< ")', or Verilator '--timescale-override " << vl_time_str(sc_prec) << "/"
|
|
|
|
|
<< vl_time_str(sc_prec) << "'";
|
|
|
|
|
const std::string msgs = msg.str();
|
|
|
|
|
VL_FATAL_MT("", 0, "", msgs.c_str());
|
|
|
|
|
VL_UNREACHABLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Verilated::scTraceBeforeElaborationError() VL_MT_SAFE {
|
2022-11-30 04:17:50 +01:00
|
|
|
// Slowpath - Called only when trace file opened before SystemC elaboration
|
|
|
|
|
VL_FATAL_MT("unknown", 0, "",
|
|
|
|
|
"%Error: Verilated*Sc::open(...) was called before sc_core::sc_start(). "
|
|
|
|
|
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.");
|
|
|
|
|
VL_UNREACHABLE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-06 22:14:58 +01:00
|
|
|
void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
|
|
|
|
|
// Slowpath - Called only when constructing
|
|
|
|
|
#ifdef _VL_HAVE_GETRLIMIT
|
|
|
|
|
QData haveSize = 0;
|
|
|
|
|
rlimit rlim;
|
|
|
|
|
if (0 == getrlimit(RLIMIT_STACK, &rlim)) {
|
|
|
|
|
haveSize = rlim.rlim_cur;
|
|
|
|
|
if (haveSize == RLIM_INFINITY) haveSize = rlim.rlim_max;
|
|
|
|
|
if (haveSize == RLIM_INFINITY) haveSize = 0;
|
|
|
|
|
}
|
|
|
|
|
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
|
2024-05-09 04:40:42 +02:00
|
|
|
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
|
2026-03-28 04:14:18 +01:00
|
|
|
const QData requestSize = needSize + needSize / 2;
|
2024-05-09 04:40:42 +02:00
|
|
|
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
|
|
|
|
|
// Try to increase the stack limit to the requested size
|
|
|
|
|
rlim.rlim_cur = requestSize;
|
|
|
|
|
if (
|
|
|
|
|
#ifdef _VL_TEST_RLIMIT_FAIL
|
|
|
|
|
true ||
|
|
|
|
|
#endif
|
|
|
|
|
setrlimit(RLIMIT_STACK, &rlim)) {
|
|
|
|
|
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
|
|
|
|
" which may be too small; failed to request more"
|
|
|
|
|
" using 'ulimit -s %" PRIu64 "'\n",
|
|
|
|
|
haveSize / 1024, requestSize);
|
|
|
|
|
}
|
2024-01-06 22:14:58 +01:00
|
|
|
}
|
|
|
|
|
#else
|
2024-03-27 23:07:14 +01:00
|
|
|
(void)needSize; // Unused argument
|
2024-01-06 22:14:58 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 00:07:52 +02:00
|
|
|
void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE {
|
|
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
|
|
|
|
::mkdir(dirname);
|
|
|
|
|
#else
|
|
|
|
|
::mkdir(dirname, 0777);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 03:51:51 +02:00
|
|
|
void Verilated::quiesce() VL_MT_SAFE {
|
|
|
|
|
// Wait until all threads under this evaluation are quiet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE {
|
2026-02-25 01:06:05 +01:00
|
|
|
return VerilatedImp::exportFindNum(namep);
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 03:43:32 +02:00
|
|
|
void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
|
|
|
|
VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n"););
|
2017-10-27 03:51:51 +02:00
|
|
|
VerilatedThreadMsgQueue::flush(evalMsgQp);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-25 01:21:02 +01:00
|
|
|
void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
|
|
|
|
// It doesn't work to set endOfEvalReqd on the threadpool thread
|
|
|
|
|
// and then check it on the eval thread since it's thread local.
|
|
|
|
|
// It should be ok to call into endOfEvalGuts, it returns immediately
|
|
|
|
|
// if there are no transactions.
|
2017-10-27 03:51:51 +02:00
|
|
|
VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n"););
|
2023-10-21 19:01:45 +02:00
|
|
|
VerilatedThreadMsgQueue::flush(evalMsgQp);
|
2017-10-27 03:51:51 +02:00
|
|
|
evalMsgQp->process();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 14:36:39 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// VerilatedImp:: Methods
|
|
|
|
|
|
|
|
|
|
void VerilatedImp::versionDump() VL_MT_SAFE {
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
2018-05-20 14:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-12 16:38:24 +02:00
|
|
|
//===========================================================================
|
|
|
|
|
// VerilatedModel:: Methods
|
|
|
|
|
|
|
|
|
|
VerilatedModel::VerilatedModel(VerilatedContext& context)
|
|
|
|
|
: m_context{context} {}
|
|
|
|
|
|
2022-07-20 12:27:10 +02:00
|
|
|
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
|
|
|
|
|
|
2010-04-06 02:01:17 +02:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedVar:: Methods
|
|
|
|
|
|
2013-02-03 19:27:37 +01:00
|
|
|
// cppcheck-suppress unusedFunction // Used by applications
|
2023-03-03 00:37:07 +01:00
|
|
|
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
2026-06-23 13:04:51 +02:00
|
|
|
if (m_entSize) return m_entSize;
|
2022-03-27 21:27:40 +02:00
|
|
|
uint32_t size = 1;
|
2010-04-06 02:01:17 +02:00
|
|
|
switch (vltype()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
case VLVT_PTR: size = sizeof(void*); break;
|
|
|
|
|
case VLVT_UINT8: size = sizeof(CData); break;
|
|
|
|
|
case VLVT_UINT16: size = sizeof(SData); break;
|
|
|
|
|
case VLVT_UINT32: size = sizeof(IData); break;
|
|
|
|
|
case VLVT_UINT64: size = sizeof(QData); break;
|
2025-01-10 01:04:26 +01:00
|
|
|
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
|
2020-09-19 03:27:36 +02:00
|
|
|
default: size = 0; break; // LCOV_EXCL_LINE
|
2010-04-06 02:01:17 +02:00
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-17 22:28:58 +01:00
|
|
|
size_t VerilatedVarProps::totalSize() const {
|
|
|
|
|
size_t size = entSize();
|
2020-12-16 04:09:24 +01:00
|
|
|
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
|
2017-12-17 22:28:58 +01:00
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 00:37:07 +01:00
|
|
|
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
|
2020-08-15 16:12:55 +02:00
|
|
|
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
|
|
|
|
|
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
|
2021-11-25 15:05:50 +01:00
|
|
|
const int indxAdj = indx - low(dim);
|
2022-03-27 21:27:40 +02:00
|
|
|
uint8_t* bytep = reinterpret_cast<uint8_t*>(datap);
|
2017-12-17 22:28:58 +01:00
|
|
|
// If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
|
|
|
|
|
size_t slicesz = entSize();
|
2025-01-10 01:04:26 +01:00
|
|
|
for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d);
|
2020-04-14 04:51:35 +02:00
|
|
|
bytep += indxAdj * slicesz;
|
2017-12-17 22:28:58 +01:00
|
|
|
return bytep;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-05 16:38:49 +01:00
|
|
|
//======================================================================
|
|
|
|
|
// VerilatedScope:: Methods
|
|
|
|
|
|
2025-11-08 22:48:00 +01:00
|
|
|
VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const char* identifier,
|
|
|
|
|
const char* defnamep, int8_t timeunit, Type type)
|
2025-11-08 16:56:15 +01:00
|
|
|
: m_symsp{symsp}
|
2025-11-08 22:48:00 +01:00
|
|
|
, m_namep{[symsp, suffixp]() {
|
2025-11-08 16:56:15 +01:00
|
|
|
// We don't want the space and reference-count access overhead of strings.
|
2025-11-08 22:48:00 +01:00
|
|
|
const char* prefixp = symsp->name();
|
2021-07-24 14:36:11 +02:00
|
|
|
char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
|
2021-03-06 16:33:43 +01:00
|
|
|
char* dp = namep;
|
|
|
|
|
for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
|
|
|
|
|
if (*prefixp && *suffixp) *dp++ = '.';
|
|
|
|
|
for (const char* sp = suffixp; *sp;) *dp++ = *sp++;
|
|
|
|
|
*dp++ = '\0';
|
2025-11-08 16:56:15 +01:00
|
|
|
return namep;
|
|
|
|
|
}()}
|
|
|
|
|
, m_identifierp{identifier}
|
|
|
|
|
, m_defnamep{defnamep}
|
|
|
|
|
, m_timeunit{timeunit}
|
|
|
|
|
, m_type{type} {
|
2021-03-07 17:01:54 +01:00
|
|
|
Verilated::threadContextp()->impp()->scopeInsert(this);
|
2009-12-05 16:38:49 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-08 16:56:15 +01:00
|
|
|
VerilatedScope::~VerilatedScope() {
|
|
|
|
|
// Memory cleanup - not called during normal operation
|
|
|
|
|
Verilated::threadContextp()->impp()->scopeErase(this);
|
|
|
|
|
VL_DO_DANGLING(delete[] m_namep, m_namep);
|
|
|
|
|
VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp);
|
|
|
|
|
VL_DO_DANGLING(delete m_varsp, m_varsp);
|
|
|
|
|
VL_DEBUG_IFDEF(m_funcnumMax = 0;);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {
|
2009-12-20 14:27:00 +01:00
|
|
|
// Slowpath - called once/scope*export at construction
|
|
|
|
|
// Insert a exported function into scope table
|
2026-02-25 01:06:05 +01:00
|
|
|
const int funcnum = VerilatedImp::exportInsert(namep, cb);
|
2009-12-20 14:27:00 +01:00
|
|
|
if (!finalize) {
|
2019-05-09 03:13:38 +02:00
|
|
|
// Need two passes so we know array size to create
|
|
|
|
|
// Alternative is to dynamically stretch the array, which is more code, and slower.
|
2021-02-22 03:25:21 +01:00
|
|
|
if (funcnum >= m_funcnumMax) m_funcnumMax = funcnum + 1;
|
2009-12-20 14:27:00 +01:00
|
|
|
} else {
|
2019-07-01 04:37:03 +02:00
|
|
|
if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) {
|
|
|
|
|
VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE
|
2019-05-09 03:13:38 +02:00
|
|
|
"Internal: Bad funcnum vs. pre-finalize maximum");
|
2018-11-29 01:59:10 +01:00
|
|
|
}
|
|
|
|
|
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
|
2020-04-14 04:51:35 +02:00
|
|
|
m_callbacksp = new void*[m_funcnumMax];
|
2021-03-27 02:23:18 +01:00
|
|
|
std::memset(m_callbacksp, 0, m_funcnumMax * sizeof(void*));
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
|
|
|
|
m_callbacksp[funcnum] = cb;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isParam,
|
|
|
|
|
VerilatedVarType vltype, int vlflags, int udims,
|
|
|
|
|
int pdims...) VL_MT_UNSAFE {
|
2010-03-17 13:22:49 +01:00
|
|
|
// Grab dimensions
|
2019-05-15 04:49:21 +02:00
|
|
|
// In the future we may just create a large table at emit time and
|
|
|
|
|
// statically construct from that.
|
2010-03-17 13:22:49 +01:00
|
|
|
|
2021-07-24 14:36:11 +02:00
|
|
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
2025-01-10 01:05:16 +01:00
|
|
|
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, pdims,
|
|
|
|
|
isParam);
|
2010-03-17 13:22:49 +01:00
|
|
|
|
|
|
|
|
va_list ap;
|
2025-01-10 01:04:26 +01:00
|
|
|
va_start(ap, pdims);
|
|
|
|
|
for (int i = 0; i < udims; ++i) {
|
2021-06-19 04:19:35 +02:00
|
|
|
const int msb = va_arg(ap, int);
|
|
|
|
|
const int lsb = va_arg(ap, int);
|
2025-01-10 01:04:26 +01:00
|
|
|
var.m_unpacked[i].m_left = msb;
|
|
|
|
|
var.m_unpacked[i].m_right = lsb;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < pdims; ++i) {
|
|
|
|
|
const int msb = va_arg(ap, int);
|
|
|
|
|
const int lsb = va_arg(ap, int);
|
|
|
|
|
var.m_packed[i].m_left = msb;
|
|
|
|
|
var.m_packed[i].m_right = lsb;
|
2010-03-17 13:22:49 +01:00
|
|
|
}
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
m_varsp->emplace(namep, std::move(var));
|
|
|
|
|
return &(m_varsp->find(namep)->second);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-23 13:04:51 +02:00
|
|
|
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
|
|
|
|
VerilatedVarType vltype, int vlflags, int udims,
|
|
|
|
|
uint32_t entSize...) VL_MT_UNSAFE {
|
|
|
|
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
|
|
|
|
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, 0,
|
|
|
|
|
isParam, entSize);
|
|
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, entSize);
|
|
|
|
|
for (int i = 0; i < udims; ++i) {
|
|
|
|
|
const int msb = va_arg(ap, int);
|
|
|
|
|
const int lsb = va_arg(ap, int);
|
|
|
|
|
var.m_unpacked[i].m_left = msb;
|
|
|
|
|
var.m_unpacked[i].m_right = lsb;
|
|
|
|
|
}
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
m_varsp->emplace(namep, std::move(var));
|
|
|
|
|
return &(m_varsp->find(namep)->second);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
VerilatedVar*
|
|
|
|
|
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
2026-04-10 01:08:36 +02:00
|
|
|
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
|
|
|
|
const char* const forceReadSignalName,
|
2026-03-08 20:53:32 +01:00
|
|
|
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
|
|
|
|
int udims, int pdims...) VL_MT_UNSAFE {
|
|
|
|
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
|
|
|
|
|
2026-04-10 01:08:36 +02:00
|
|
|
// TODO: While the force read signal would be *expected* to have the same vltype and vlflags
|
|
|
|
|
// (except for forceable and public flags) as the base signal, this is not guaranteed. It would
|
|
|
|
|
// be a safer solution to adapt V3EmitCSyms to find the __VforceRd signal and give its vltype
|
|
|
|
|
// and vlflags to this function as arguments.
|
|
|
|
|
|
|
|
|
|
// Use same flags as base signal, but remove forceable and public flags
|
|
|
|
|
const VerilatedVarFlags forceReadValueVlflags
|
|
|
|
|
= static_cast<VerilatedVarFlags>(vlflags & ~VLVF_FORCEABLE & ~VLVF_PUB_RW & ~VLVF_PUB_RD);
|
|
|
|
|
|
|
|
|
|
VerilatedVar forceReadSignal{forceReadSignalName,
|
|
|
|
|
forceReadSignalData,
|
|
|
|
|
vltype,
|
|
|
|
|
forceReadValueVlflags,
|
|
|
|
|
udims,
|
|
|
|
|
pdims,
|
|
|
|
|
isParam};
|
|
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, pdims);
|
2026-06-06 02:43:06 +02:00
|
|
|
for (int i = 0; i < udims; ++i) {
|
|
|
|
|
forceReadSignal.m_unpacked[i].m_left = va_arg(ap, int);
|
|
|
|
|
forceReadSignal.m_unpacked[i].m_right = va_arg(ap, int);
|
|
|
|
|
}
|
2026-04-10 01:08:36 +02:00
|
|
|
for (int i = 0; i < pdims; ++i) {
|
|
|
|
|
const int msb = va_arg(ap, int);
|
|
|
|
|
const int lsb = va_arg(ap, int);
|
|
|
|
|
forceReadSignal.m_packed[i].m_left = msb;
|
|
|
|
|
forceReadSignal.m_packed[i].m_right = lsb;
|
|
|
|
|
}
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
std::unique_ptr<VerilatedForceControlSignals> verilatedForceControlSignalsp
|
2026-04-10 01:08:36 +02:00
|
|
|
= std::unique_ptr<VerilatedForceControlSignals>(new VerilatedForceControlSignals{
|
|
|
|
|
forceControlSignals.first, forceControlSignals.second, std::move(forceReadSignal)});
|
2026-03-08 20:53:32 +01:00
|
|
|
|
|
|
|
|
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, pdims,
|
|
|
|
|
isParam, std::move(verilatedForceControlSignalsp));
|
|
|
|
|
verilatedForceControlSignalsp = nullptr;
|
|
|
|
|
|
|
|
|
|
va_start(ap, pdims);
|
2026-06-06 02:43:06 +02:00
|
|
|
for (int i = 0; i < udims; ++i) {
|
|
|
|
|
var.m_unpacked[i].m_left = va_arg(ap, int);
|
|
|
|
|
var.m_unpacked[i].m_right = va_arg(ap, int);
|
|
|
|
|
}
|
2026-03-08 20:53:32 +01:00
|
|
|
for (int i = 0; i < pdims; ++i) {
|
|
|
|
|
const int msb = va_arg(ap, int);
|
|
|
|
|
const int lsb = va_arg(ap, int);
|
|
|
|
|
var.m_packed[i].m_left = msb;
|
|
|
|
|
var.m_packed[i].m_right = lsb;
|
|
|
|
|
}
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
m_varsp->emplace(namep, std::move(var));
|
|
|
|
|
return &(m_varsp->find(namep)->second);
|
2010-03-17 13:22:49 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-03 19:27:37 +01:00
|
|
|
// cppcheck-suppress unusedFunction // Used by applications
|
2017-10-27 02:05:42 +02:00
|
|
|
VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTINIT {
|
2010-03-17 13:22:49 +01:00
|
|
|
if (VL_LIKELY(m_varsp)) {
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_varsp->find(namep);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (VL_LIKELY(it != m_varsp->end())) return &(it->second);
|
2010-03-17 13:22:49 +01:00
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2010-03-17 13:22:49 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-25 01:06:05 +01:00
|
|
|
void* VerilatedScope::exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
|
|
|
|
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
|
|
|
|
// If function is registered only once across all scopes, fast path it.
|
|
|
|
|
// UVM for example expects to find uvm_polling_value_change_notify
|
|
|
|
|
// from a different scope than where decared.
|
|
|
|
|
VL_DEBUG_IFDEF(assert(funcnum < VerilatedImp::exportFlatCbs().size()););
|
|
|
|
|
{
|
|
|
|
|
void* const cbp = VerilatedImp::exportFlatCbs()[funcnum];
|
|
|
|
|
if (VL_LIKELY(cbp)) return cbp;
|
|
|
|
|
}
|
|
|
|
|
// Else specific scope-based export call
|
|
|
|
|
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
|
|
|
|
// m_callbacksp must be declared, as Max'es are > 0
|
|
|
|
|
void* const cbp = scopep->m_callbacksp[funcnum];
|
|
|
|
|
if (VL_LIKELY(cbp)) return cbp;
|
|
|
|
|
}
|
|
|
|
|
return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 02:05:42 +02:00
|
|
|
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
2009-12-20 14:27:00 +01:00
|
|
|
// Slowpath - Called only when find has failed
|
2024-01-29 02:24:28 +01:00
|
|
|
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
|
|
|
|
|
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
2024-03-02 15:05:21 +01:00
|
|
|
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-03 00:37:07 +01:00
|
|
|
void* VerilatedScope::exportFindError(int funcnum) const VL_MT_SAFE {
|
2009-12-20 14:27:00 +01:00
|
|
|
// Slowpath - Called only when find has failed
|
2021-06-19 04:19:35 +02:00
|
|
|
const std::string msg
|
2024-01-29 02:24:28 +01:00
|
|
|
= ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
|
2021-06-19 04:19:35 +02:00
|
|
|
+ "' but this DPI export function exists only in other scopes, not scope '" + name()
|
|
|
|
|
+ "'");
|
2019-05-09 03:13:38 +02:00
|
|
|
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VerilatedScope::scopeDump() const {
|
2017-10-20 01:40:51 +02:00
|
|
|
VL_PRINTF_MT(" SCOPE %p: %s\n", this, name());
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int i = 0; i < m_funcnumMax; ++i) {
|
2019-05-09 03:13:38 +02:00
|
|
|
if (m_callbacksp && m_callbacksp[i]) {
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_PRINTF_MT(" DPI-EXPORT %p: %s\n", m_callbacksp[i],
|
|
|
|
|
VerilatedImp::exportName(i));
|
2019-05-09 03:13:38 +02:00
|
|
|
}
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
2025-08-20 03:36:52 +02:00
|
|
|
if (const VerilatedVarNameMap* const ivarsp = this->varsp()) {
|
|
|
|
|
for (const auto& i : *ivarsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first);
|
2010-03-17 13:22:49 +01:00
|
|
|
}
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-20 03:36:52 +02:00
|
|
|
void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
|
2019-10-02 03:57:45 +02:00
|
|
|
VerilatedImp::hierarchyAdd(fromp, top);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 03:36:52 +02:00
|
|
|
void VerilatedHierarchy::remove(const VerilatedScope* fromp, const VerilatedScope* top) {
|
2020-12-17 18:21:40 +01:00
|
|
|
VerilatedImp::hierarchyRemove(fromp, top);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-07 21:42:29 +01:00
|
|
|
void VerilatedHierarchy::clear() { VerilatedImp::hierarchyClear(); }
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//===========================================================================
|
2017-10-27 03:51:51 +02:00
|
|
|
// VerilatedOneThreaded:: Methods
|
|
|
|
|
|
2022-11-05 13:47:34 +01:00
|
|
|
#ifdef VL_DEBUG
|
2017-10-27 03:51:51 +02:00
|
|
|
void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
|
2020-04-14 04:51:35 +02:00
|
|
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
|
|
|
|
"Routine called that is single threaded, but called from"
|
2025-04-01 13:54:56 +02:00
|
|
|
" a different thread than the expected constructing thread");
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
2022-09-29 00:54:18 +02:00
|
|
|
// VlDeleter:: Methods
|
|
|
|
|
|
2023-05-13 16:32:33 +02:00
|
|
|
void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE {
|
2022-09-29 00:54:18 +02:00
|
|
|
while (true) {
|
2023-05-13 16:32:33 +02:00
|
|
|
{
|
2026-03-28 04:14:18 +01:00
|
|
|
const VerilatedLockGuard lock{m_mutex};
|
2023-05-13 16:32:33 +02:00
|
|
|
if (m_newGarbage.empty()) break;
|
|
|
|
|
m_deleteMutex.lock();
|
2023-11-12 02:47:10 +01:00
|
|
|
std::swap(m_newGarbage, m_deleteNow);
|
2023-05-13 16:32:33 +02:00
|
|
|
// m_mutex is unlocked here, so destructors can enqueue new objects
|
|
|
|
|
}
|
2023-11-12 02:47:10 +01:00
|
|
|
for (VlDeletable* const objp : m_deleteNow) delete objp;
|
|
|
|
|
m_deleteNow.clear();
|
2023-05-13 16:32:33 +02:00
|
|
|
m_deleteMutex.unlock();
|
2022-09-29 00:54:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
2024-03-25 12:03:17 +01:00
|
|
|
// OS functions (last, so we have minimal OS dependencies above)
|
|
|
|
|
|
|
|
|
|
#define VL_ALLOW_VERILATEDOS_C
|
|
|
|
|
#include "verilatedos_c.h"
|