Merge from master for release.

This commit is contained in:
Wilson Snyder 2022-05-02 22:22:20 -04:00
commit 4618fd5a43
603 changed files with 20380 additions and 19506 deletions

23
Changes
View File

@ -8,6 +8,29 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.222 2022-05-02
==========================
**Minor:**
* Split --prof-threads into --prof-exec and --prof-pgo (#3365). [Geza Lore, Shunyao CAD]
* Deprecate 'vluint64_t' and similar types (#3255).
* Raise error on assignment to const in initial blocks. [Geza Lore, Shunyao CAD]
* Issue INITIALDLY/COMBDLY/BLKSEQ warnings consistent with Verilator execution. [Geza Lore, Shunyao CAD]
* Support LoongArch ISA multithreading (#3353) (#3354). [Xi Zhang]
* Fix MSVC localtime_s (#3124).
* Fix Bison 3.8.2 error (#3366). [elike-ypq]
* Fix rare bug in -Oz (V3Localize) (#3286). [Geza Lore, Shunyao CAD]
* Fix tracing interfaces inside interfaces (#3309). [Kevin Millis]
* Fix filenames with dots overwriting debug .vpp files (#3373).
* Fix including VK_USER_OBJS in make library (#3370) (#3382). [Julien Margetts]
* Fix hang in generate symbol references (#3391) (#3398). [Yoda Lee]
* Fix missing #include <memory> (#3392). [Aliaksei Chapyzhenka]
* Fix crash in recursive module inlining (#3393). [david-sawatzke]
* Fix --protect-ids mangling names of library methods. [Geza Lore, Shunyao CAD]
* Fix foreach segmentation fault (#3400). [Kamil Rakoczy]
Verilator 4.220 2022-03-12
==========================

View File

@ -73,7 +73,7 @@ for you.
Performance
===========
Verilator does not simply convert Verilog HDL to C++ or SystemC. Rather,
Verilator does not directly translate Verilog HDL to C++ or SystemC. Rather,
Verilator compiles your code into a much faster optimized and optionally
thread-partitioned model, which is in turn wrapped inside a C++/SystemC
module. The results are a compiled Verilog model that executes even on a

View File

@ -348,7 +348,7 @@ detailed descriptions of these arguments.
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
--no-pins64 Don't use vluint64_t's for 33-64 bit sigs
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--no-skip-identical Disable skipping identical output
+notimingchecks Ignored
-O0 Disable optimizations
@ -370,7 +370,8 @@ detailed descriptions of these arguments.
--prefix <topname> Name of top level class
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-threads Enable generating gantt chart data for threads
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-lib <name> Create a DPI protected library
@ -445,10 +446,10 @@ description of these arguments.
+verilator+error+limit+<value> Set error limit
+verilator+help Display help
+verilator+noassert Disable assert checking
+verilator+prof+threads+file+<filename> Set profile filename
+verilator+prof+threads+start+<value> Set profile starting point
+verilator+prof+threads+window+<value> Set profile duration
+verilator+prof+vlt+file+<filename> Set profile guided filename
+verilator+prof+exec+file+<filename> Set execution profile filename
+verilator+prof+exec+start+<value> Set execution profile starting point
+verilator+prof+exec+window+<value> Set execution profile duration
+verilator+prof+vlt+file+<filename> Set PGO profile filename
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config

View File

@ -9,7 +9,7 @@ import re
import statistics
# from pprint import pprint
Threads = collections.defaultdict(lambda: {})
Threads = collections.defaultdict(lambda: collections.defaultdict(lambda: {}))
Mtasks = collections.defaultdict(lambda: {})
Evals = collections.defaultdict(lambda: {})
EvalLoops = collections.defaultdict(lambda: {})
@ -30,12 +30,12 @@ def process(filename):
def read_data(filename):
with open(filename) as fh:
re_prof = re.compile(
r'^VLPROF mtask\s(\d+)\sstart\s(\d+)\selapsed\s(\d+)\spredict_start\s(\d+)\spredict_cost\s(\d+)\scpu\s(\d+)\son thread (\d+)'
)
re_eval = re.compile(r'^VLPROF eval\sstart\s(\d+)\selapsed\s(\d+)')
re_loop = re.compile(
r'^VLPROF eval_loop\sstart\s(\d+)\selapsed\s(\d+)')
re_thread = re.compile(r'^VLPROFTHREAD (\d+)$')
re_record = re.compile(r'^VLPROFEXEC (\S+) (\d+)(.*)$')
re_payload_mtaskBegin = re.compile(
r'id (\d+) predictStart (\d+) cpu (\d+)')
re_payload_mtaskEnd = re.compile(r'id (\d+) predictCost (\d+)')
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.]*)\s*')
re_arg2 = re.compile(r'VLPROF arg\s+(\S+)\s+([0-9.]*)\s*$')
re_stat = re.compile(r'VLPROF stat\s+(\S+)\s+([0-9.]+)')
@ -43,47 +43,60 @@ def read_data(filename):
re_proc_cpu = re.compile(r'VLPROFPROC processor\s*:\s*(\d+)\s*$')
re_proc_dat = re.compile(r'VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$')
cpu = None
thread = None
lastEvalBeginTick = None
lastEvalLoopBeginTick = None
for line in fh:
if re_prof.match(line):
match = re_prof.match(line)
mtask = int(match.group(1))
start = int(match.group(2))
elapsed_time = int(match.group(3))
end = start + elapsed_time
predict_start = int(match.group(4))
predict_cost = int(match.group(5))
cpu = int(match.group(6))
thread = int(match.group(7))
if start not in Threads[thread]:
Threads[thread][start] = {}
Threads[thread][start]['mtask'] = mtask
Threads[thread][start]['end'] = end
Threads[thread][start]['cpu'] = cpu
Threads[thread][start]['predict_start'] = predict_start
Threads[thread][start]['predict_cost'] = predict_cost
if 'elapsed' not in Mtasks[mtask]:
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
Mtasks[mtask]['thread'] = thread
Mtasks[mtask]['elapsed'] += elapsed_time
Mtasks[mtask]['predict_start'] = predict_start
Mtasks[mtask]['predict_cost'] = predict_cost
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], end)
elif re_eval.match(line):
match = re_eval.match(line)
start = int(match.group(1))
elapsed_time = int(match.group(2))
Evals[start]['start'] = start
Evals[start]['end'] = start + elapsed_time
elif re_loop.match(line):
match = re_loop.match(line)
start = int(match.group(1))
elapsed_time = int(match.group(2))
EvalLoops[start]['start'] = start
EvalLoops[start]['end'] = start + elapsed_time
elif re.match(r'^VLPROFTHREAD', line):
None # pylint: disable=pointless-statement
recordMatch = re_record.match(line)
if recordMatch:
kind, tick, payload = recordMatch.groups()
tick = int(tick)
payload = payload.strip()
if kind == "EVAL_BEGIN":
Evals[tick]['start'] = tick
lastEvalBeginTick = tick
elif kind == "EVAL_END":
Evals[lastEvalBeginTick]['end'] = tick
lastEvalBeginTick = None
elif kind == "EVAL_LOOP_BEGIN":
EvalLoops[tick]['start'] = tick
lastEvalLoopBeginTick = tick
elif kind == "EVAL_LOOP_END":
EvalLoops[lastEvalLoopBeginTick]['end'] = tick
lastEvalLoopBeginTick = None
elif kind == "MTASK_BEGIN":
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(
payload).groups()
mtask = int(mtask)
predict_start = int(predict_start)
ecpu = int(ecpu)
Threads[thread][tick]['mtask'] = mtask
Threads[thread][tick]['predict_start'] = predict_start
Threads[thread][tick]['cpu'] = ecpu
if 'elapsed' not in Mtasks[mtask]:
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
Mtasks[mtask]['begin'] = tick
Mtasks[mtask]['thread'] = thread
Mtasks[mtask]['predict_start'] = predict_start
elif kind == "MTASK_END":
mtask, predict_cost = re_payload_mtaskEnd.match(
payload).groups()
mtask = int(mtask)
predict_cost = int(predict_cost)
begin = Mtasks[mtask]['begin']
Threads[thread][begin]['end'] = tick
Threads[thread][begin]['predict_cost'] = predict_cost
Mtasks[mtask]['elapsed'] += tick - begin
Mtasks[mtask]['predict_cost'] = predict_cost
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], tick)
elif Args.debug:
print("-Unknown execution trace record: %s" % line)
elif re_thread.match(line):
thread = int(re_thread.match(line).group(1))
elif re.match(r'^VLPROF(THREAD|VERSION)', line):
pass
elif re_arg1.match(line):
match = re_arg1.match(line)
Global['args'][match.group(1)] = match.group(2)
@ -105,7 +118,7 @@ def read_data(filename):
value = re.sub(r'\s+$', '', value)
Global['cpuinfo'][cpu][term] = value
elif re.match(r'^#', line):
None # pylint: disable=pointless-statement
pass
elif Args.debug:
print("-Unk: %s" % line)
# TODO -- this is parsing text printed by a client.
@ -131,11 +144,12 @@ def report():
plus = "+" if re.match(r'^\+', arg) else " "
print(" %s%s%s" % (arg, plus, Global['args'][arg]))
nthreads = len(Threads)
nthreads = int(Global['stats']['threads'])
Global['cpus'] = {}
for thread in Threads:
# Make potentially multiple characters per column
for start in Threads[thread]:
if not Threads[thread][start]: continue
cpu = Threads[thread][start]['cpu']
elapsed = Threads[thread][start]['end'] - start
if cpu not in Global['cpus']:
@ -169,74 +183,79 @@ def report():
print("\nAnalysis:")
print(" Total threads = %d" % nthreads)
print(" Total mtasks = %d" % len(Mtasks))
ncpus = len(Global['cpus'])
ncpus = max(len(Global['cpus']), 1)
print(" Total cpus used = %d" % ncpus)
print(" Total yields = %d" % int(Global['stats']['yields']))
print(" Total yields = %d" %
int(Global['stats'].get('yields', 0)))
print(" Total evals = %d" % len(Evals))
print(" Total eval loops = %d" % len(EvalLoops))
print(" Total eval time = %d rdtsc ticks" %
Global['measured_last_end'])
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
print(" All-thread mtask time = %d rdtsc ticks" %
measured_mt_mtask_time)
long_efficiency = long_mtask_time / (Global.get('measured_last_end', 1)
or 1)
print(" Longest-thread efficiency = %0.1f%%" % (long_efficiency * 100.0))
mt_efficiency = measured_mt_mtask_time / (
Global.get('measured_last_end', 1) * nthreads or 1)
print(" All-thread efficiency = %0.1f%%" % (mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" % (mt_efficiency * nthreads))
if Global['rdtsc_cycle_time'] > 0:
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
Global['rdtsc_cycle_time'] + " ut=" + ut)
if Mtasks:
print(" Total eval time = %d rdtsc ticks" %
Global['measured_last_end'])
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
print(" All-thread mtask time = %d rdtsc ticks" %
measured_mt_mtask_time)
long_efficiency = long_mtask_time / (Global.get(
'measured_last_end', 1) or 1)
print(" Longest-thread efficiency = %0.1f%%" %
(long_efficiency * 100.0))
mt_efficiency = measured_mt_mtask_time / (
Global.get('measured_last_end', 1) * nthreads or 1)
print(" All-thread efficiency = %0.1f%%" %
(mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(mt_efficiency * nthreads))
if Global['rdtsc_cycle_time'] > 0:
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
Global['rdtsc_cycle_time'] + " ut=" + ut)
predict_mt_efficiency = predict_mt_mtask_time / (
Global.get('predict_last_end', 1) * nthreads or 1)
print("\nPrediction (what Verilator used for scheduling):")
print(" All-thread efficiency = %0.1f%%" %
(predict_mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(predict_mt_efficiency * nthreads))
predict_mt_efficiency = predict_mt_mtask_time / (
Global.get('predict_last_end', 1) * nthreads or 1)
print("\nPrediction (what Verilator used for scheduling):")
print(" All-thread efficiency = %0.1f%%" %
(predict_mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(predict_mt_efficiency * nthreads))
p2e_ratios = []
min_p2e = 1000000
min_mtask = None
max_p2e = -1000000
max_mtask = None
p2e_ratios = []
min_p2e = 1000000
min_mtask = None
max_p2e = -1000000
max_mtask = None
for mtask in sorted(Mtasks.keys()):
if Mtasks[mtask]['elapsed'] > 0:
if Mtasks[mtask]['predict_cost'] == 0:
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
Mtasks[mtask]['elapsed'])
p2e_ratios.append(p2e_ratio)
for mtask in sorted(Mtasks.keys()):
if Mtasks[mtask]['elapsed'] > 0:
if Mtasks[mtask]['predict_cost'] == 0:
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
Mtasks[mtask]['elapsed'])
p2e_ratios.append(p2e_ratio)
if p2e_ratio > max_p2e:
max_p2e = p2e_ratio
max_mtask = mtask
if p2e_ratio < min_p2e:
min_p2e = p2e_ratio
min_mtask = mtask
if p2e_ratio > max_p2e:
max_p2e = p2e_ratio
max_mtask = mtask
if p2e_ratio < min_p2e:
min_p2e = p2e_ratio
min_mtask = mtask
print("\nStatistics:")
print(" min log(p2e) = %0.3f" % min_p2e, end="")
print(" from mtask %d (predict %d," %
(min_mtask, Mtasks[min_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
print(" max log(p2e) = %0.3f" % max_p2e, end="")
print(" from mtask %d (predict %d," %
(max_mtask, Mtasks[max_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
print("\nMTask statistics:")
print(" min log(p2e) = %0.3f" % min_p2e, end="")
print(" from mtask %d (predict %d," %
(min_mtask, Mtasks[min_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
print(" max log(p2e) = %0.3f" % max_p2e, end="")
print(" from mtask %d (predict %d," %
(max_mtask, Mtasks[max_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
stddev = statistics.pstdev(p2e_ratios)
mean = statistics.mean(p2e_ratios)
print(" mean = %0.3f" % mean)
print(" stddev = %0.3f" % stddev)
print(" e ^ stddev = %0.3f" % math.exp(stddev))
stddev = statistics.pstdev(p2e_ratios)
mean = statistics.mean(p2e_ratios)
print(" mean = %0.3f" % mean)
print(" stddev = %0.3f" % stddev)
print(" e ^ stddev = %0.3f" % math.exp(stddev))
report_cpus()
@ -375,44 +394,45 @@ def write_vcd(filename):
vcd['values'][eval_start][elcode] = n
vcd['values'][eval_end][elcode] = None
# Predicted graph
for eval_start in EvalLoops:
eval_end = EvalLoops[eval_start]['end']
# Compute scale so predicted graph is of same width as eval
measured_scaling = (eval_end -
eval_start) / Global['predict_last_end']
# Predict mtasks that fill the time the eval occupied
for mtask in Mtasks:
thread = Mtasks[mtask]['thread']
pred_scaled_start = eval_start + int(
Mtasks[mtask]['predict_start'] * measured_scaling)
pred_scaled_end = eval_start + int(
(Mtasks[mtask]['predict_start'] +
Mtasks[mtask]['predict_cost']) * measured_scaling)
if pred_scaled_start == pred_scaled_end:
continue
if Mtasks:
# Predicted graph
for eval_start in EvalLoops:
eval_end = EvalLoops[eval_start]['end']
# Compute scale so predicted graph is of same width as eval
measured_scaling = (eval_end -
eval_start) / Global['predict_last_end']
# Predict mtasks that fill the time the eval occupied
for mtask in Mtasks:
thread = Mtasks[mtask]['thread']
pred_scaled_start = eval_start + int(
Mtasks[mtask]['predict_start'] * measured_scaling)
pred_scaled_end = eval_start + int(
(Mtasks[mtask]['predict_start'] +
Mtasks[mtask]['predict_cost']) * measured_scaling)
if pred_scaled_start == pred_scaled_end:
continue
sig = "predicted_thread%d_mtask" % thread
if sig not in vcd['sigs']['predicted_threads']:
vcd['sigs']['predicted_threads'][sig] = code
code += 1
mcode = vcd['sigs']['predicted_threads'][sig]
sig = "predicted_thread%d_mtask" % thread
if sig not in vcd['sigs']['predicted_threads']:
vcd['sigs']['predicted_threads'][sig] = code
code += 1
mcode = vcd['sigs']['predicted_threads'][sig]
vcd['values'][pred_scaled_start][mcode] = mtask
vcd['values'][pred_scaled_end][mcode] = None
vcd['values'][pred_scaled_start][mcode] = mtask
vcd['values'][pred_scaled_end][mcode] = None
parallelism['predicted'][pred_scaled_start] += 1
parallelism['predicted'][pred_scaled_end] -= 1
parallelism['predicted'][pred_scaled_start] += 1
parallelism['predicted'][pred_scaled_end] -= 1
# Parallelism graph
for measpred in ('measured', 'predicted'):
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
pcode = code
code += 1
value = 0
for time in sorted(parallelism[measpred].keys()):
value += parallelism[measpred][time]
vcd['values'][time][pcode] = value
# Parallelism graph
for measpred in ('measured', 'predicted'):
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
pcode = code
code += 1
value = 0
for time in sorted(parallelism[measpred].keys()):
value += parallelism[measpred][time]
vcd['values'][time][pcode] = value
# Create output file
fh.write("$version Generated by verilator_gantt $end\n")
@ -476,10 +496,10 @@ parser.add_argument('--no-vcd',
action='store_true')
parser.add_argument('--vcd',
help='filename for vcd outpue',
default='profile_threads.vcd')
default='profile_exec.vcd')
parser.add_argument('filename',
help='input profile_threads.dat filename to process',
default='profile_threads.dat')
help='input profile_exec.dat filename to process',
default='profile_exec.dat')
Args = parser.parse_args()

View File

@ -7,14 +7,14 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.220 2022-03-12],
AC_INIT([Verilator],[4.222 2022-05-02],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
AC_CONFIG_HEADER(src/config_build.h)
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
# Version

View File

@ -7,6 +7,7 @@ Adrien Le Masle
Ahmed El-Mahmoudy
Alex Chadwick
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Chris Randall
@ -54,6 +55,7 @@ Josh Redford
Julie Schwartz
Julien Margetts
Kaleb Barrett
Kamil Rakoczy
Kanad Kanhere
Keith Colbert
Kevin Kiningham
@ -111,6 +113,8 @@ Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
Wilson Snyder
Xi Zhang
Yoda Lee
Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA

View File

@ -96,7 +96,7 @@ model. Here is a simple example:
Vtop *top; // Instantiation of model
vluint64_t main_time = 0; // Current simulation time
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
@ -150,7 +150,7 @@ netlist as an instantiation.
The SC_MODULE gets the same pinout as the Verilog module, with the
following type conversions: Pins of a single bit become bool. Pins 2-32
bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
uint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
become sc_bv's. (Uints simulate the fastest so are used where possible.)
Model internals, including lower level sub-modules are not pure SystemC
@ -200,7 +200,7 @@ DPI System Task/Functions
-------------------------
Verilator extends the DPI format to allow using the same scheme to
efficiently add system functions. Simply use a dollar-sign prefixed system
efficiently add system functions. Use a dollar-sign prefixed system
function name for the import, but note it must be escaped.
.. code-block:: sv
@ -257,7 +257,9 @@ with respect to that top level module, then the scope could be set with
#include "svdpi.h"
...
svSetScope(svGetScopeFromName("TOP.dut"));
const svScope scope = svGetScopeFromName("TOP.dut");
assert(scope); // Check for nullptr if scope not found
svSetScope(scope);
(Remember that Verilator adds a "TOP" to the top of the module hierarchy.)
@ -427,7 +429,7 @@ accesses the above signal "readme" would be:
#include "verilated.h"
#include "verilated_vpi.h" // Required to get definitions
vluint64_t main_time = 0; // See comments in first example
uint64_t main_time = 0; // See comments in first example
double sc_time_stamp() { return main_time; }
void read_and_check() {
@ -506,7 +508,7 @@ structure. If a ``VerilatedContext`` is not created prior to creating a
model, a default global one is created automatically.
The ``Verilated::`` methods, including the ``Verilated::commandArgs`` call
shown above, simply call VerilatedContext methods using the default global
shown above, call VerilatedContext methods using the default global
VerilatedContext. (Technically they operate on the last one used by a
given thread.) If you are using multiple simulation contexts you should
not use the Verilated:: methods, and instead always use VerilatedContext

View File

@ -23,14 +23,14 @@ Contributors
Many people have provided ideas and other assistance with Verilator.
Verilator is receiving major development support from the `CHIPS Alliance
<https://chipsalliance.org>`_.
<https://chipsalliance.org>`_ and `Shunyao CAD <https://shunyaocad.com>`_.
Previous major corporate sponsors of Verilator, by providing significant
contributions of time or funds included include: Atmel Corporation, Cavium
Inc., Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd.,
Hicamp Systems, Intel Corporation, Mindspeed Technologies Inc., MicroTune
Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
and SiCortex Inc.
SiCortex Inc, and Shunyao CAD.
The people who have contributed major functionality are: Byron Bradley,
Jeremy Bennett, Lane Brooks, John Coiner, Duane Galbi, Geza Lore, Todd

View File

@ -19,3 +19,14 @@ Verilated_heavy.h
Option `--cdc`
The experimental `--cdc` option is believed to be generally unused and is
planned for removal no sooner than January 2023.
Option `--prof-threads`
The `--prof-threads` option has been superseded by the `--prof-exec` and
`--prof-pgo` options and is planned for removal no sooner than April 2023.
Verilated model options `+verilator+prof+threads+*`
The `+verilator+prof+threads+start`, `+verilator+prof+threads+window` and
`+verilator+prof+threads+file` options have been superseded by the
`+verilator+prof+exec+start`, `+verilator+prof+exec+window` and
`+verilator+prof+exec+file` options respectively and are planned for removal
no sooner than April 2023.

View File

@ -38,33 +38,45 @@ Summary:
Display help and exit.
.. option:: +verilator+prof+threads+file+<filename>
.. option:: +verilator+prof+exec+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
When a model was Verilated using :vlopt:`--prof-exec`, sets the
simulation runtime filename to dump to. Defaults to
:file:`profile_threads.dat`.
:file:`profile_exec.dat`.
.. option:: +verilator+prof+threads+start+<value>
.. option:: +verilator+prof+exec+start+<value>
When a model was Verilated using :vlopt:`--prof-threads`, the simulation
When a model was Verilated using :vlopt:`--prof-exec`, the simulation
runtime will wait until $time is at this value (expressed in units of
the time precision), then start the profiling warmup, then
capturing. Generally this should be set to some time that is well within
the normal operation of the simulation, i.e. outside of reset. If 0, the
dump is disabled. Defaults to 1.
.. option:: +verilator+prof+threads+window+<value>
.. option:: +verilator+prof+exec+window+<value>
When a model was Verilated using :vlopt:`--prof-threads`, after $time
reaches :vlopt:`+verilator+prof+threads+start+\<value\>`, Verilator will
When a model was Verilated using :vlopt:`--prof-exec`, after $time
reaches :vlopt:`+verilator+prof+exec+start+\<value\>`, Verilator will
warm up the profiling for this number of eval() calls, then will capture
the profiling of this number of eval() calls. Defaults to 2, which
makes sense for a single-clock-domain module where it's typical to want
to capture one posedge eval() and one negedge eval().
.. option:: +verilator+prof+threads+file+<filename>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+file+\<filename\>`
.. option:: +verilator+prof+threads+start+<value>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+start+\<value\>`
.. option:: +verilator+prof+threads+window+<value>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<filename\>`
.. option:: +verilator+prof+vlt+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
When a model was Verilated using :vlopt:`--prof-pgo`, sets the
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.

View File

@ -99,10 +99,10 @@ Summary:
.. option:: --bbox-sys
Black box any unknown $system task or function calls. System tasks will
simply become no-operations, and system functions will be replaced with
unsized zero. Arguments to such functions will be parsed, but not
otherwise checked. This prevents errors when linting in the presence of
company specific PLI calls.
become no-operations, and system functions will be replaced with unsized
zero. Arguments to such functions will be parsed, but not otherwise
checked. This prevents errors when linting in the presence of company
specific PLI calls.
Using this argument will likely cause incorrect simulation.
@ -768,7 +768,7 @@ Summary:
.. option:: --pins-bv <width>
Specifies SystemC inputs/outputs of greater than or equal to <width>
bits wide should use sc_bv's instead of uint32/vluint64_t's. The
bits wide should use sc_bv's instead of uint32/uint64_t's. The
default is "--pins-bv 65", and the value must be less than or equal
to 65. Versions before Verilator 3.671 defaulted to "--pins-bv 33".
The more sc_bv is used, the worse for performance. Use the
@ -845,10 +845,19 @@ Summary:
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-exec
Enable collection of execution trace, that can be convered into a gantt
chart with verilator_gantt See :ref:`Execution Profiling`.
.. option:: --prof-pgo
Enable collection of profiling data for profile guided verilation. Currently
this is only useful with :vlopt:`--threads`. See :ref:`Thread PGO`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread
Profiling` and :ref:`Thread PGO`.
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-key <key>
@ -1638,7 +1647,7 @@ The grammar of configuration commands is as follows:
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
Sets the port to be of :code:`sc_bv<{width}>` type, instead of bool,
vluint32_t or vluint64_t. Same as :option:`/*verilator&32;sc_bv*/`
uint32_t or uint64_t. Same as :option:`/*verilator&32;sc_bv*/`
metacomment.
.. option:: sformat [-module "<modulename>"] [-task "<taskname>"] -var "<signame>"

View File

@ -72,7 +72,7 @@ verilator_gantt Arguments
.. option:: <filename>
The filename to read data from, defaults to "profile_threads.dat".
The filename to read data from, defaults to "profile_exec.dat".
.. option:: --help

View File

@ -445,7 +445,7 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;sc_bv*/
Used after a port declaration. It sets the port to be of
:code:`sc_bv<{width}>` type, instead of bool, vluint32_t or vluint64_t.
:code:`sc_bv<{width}>` type, instead of bool, uint32_t or uint64_t.
This may be useful if the port width is parameterized and the
instantiating C++ code wants to always have a sc_bv so it can accept any
width. In general you should avoid using this attribute when not

View File

@ -354,8 +354,8 @@ also use the "import DPI" SystemVerilog feature to call C code (see the
chapter above). There is also limited VPI access to public signals.
If you want something more complex, since Verilator emits standard C++
code, you can simply write your own C++ routines that can access and modify
signal values without needing any PLI interface code, and call it with
code, you can write your own C++ routines that can access and modify signal
values without needing any PLI interface code, and call it with
$c("{any_c++_statement}").
See the :ref:`Connecting` section.
@ -445,7 +445,7 @@ the signal, as you would any other member variable.
Signals are the smallest of 8-bit unsigned chars (equivalent to uint8_t),
16-bit unsigned shorts (uint16_t), 32-bit unsigned longs (uint32_t), or
64-bit unsigned long longs (uint64_t) that fits the width of the signal.
Generally, you can use just uint32_t's for 1 to 32 bits, or vluint64_t for
Generally, you can use just uint32_t's for 1 to 32 bits, or uint64_t for
1 to 64 bits, and the compiler will properly up-convert smaller entities.
Note even signed ports are declared as unsigned; you must sign extend
yourself to the appropriate signal width.
@ -482,7 +482,7 @@ by your code or you'll get strange results.
Should a module be in Verilog or SystemC?
"""""""""""""""""""""""""""""""""""""""""
Sometimes there is a block that just interconnects instances, and have a
Sometimes there is a block that only interconnects instances, and have a
choice as to if you write it in Verilog or SystemC. Everything else being
equal, best performance is when Verilator sees all of the design. So, look
at the hierarchy of your design, labeling instances as to if they are

View File

@ -120,7 +120,7 @@ In certain debug and other modes, it also creates:
- Debugging graph files (from --debug)
* - *{prefix}{misc}*\ .tree
- Debugging files (from --debug)
* - {mod_prefix}_{each_verilog_module}*{__n}*\ .vpp
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog (from --debug)
After running Make, the C++ compiler may produce the following:
@ -155,13 +155,13 @@ The Verilated executable may produce the following:
* - gmon.out
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
* - profile.vlt
- -profile data file for :ref:`Thread PGO`
* - profile_threads.dat
- -profile-threads data file for :command:`verilator_gantt`
- --prof-pgo data file for :ref:`Thread PGO`
* - profile_exec.dat
- --prof-exec data file for :command:`verilator_gantt`
Verilator_gantt may produce the following:
.. list-table::
* - profile_threads.vcd
* - profile_exec.vcd
- Gantt report waveform output

View File

@ -92,7 +92,7 @@ appropriate code to detect failing cases at simulation runtime and print an
Verilator likewise also asserts any "unique" or "priority" SystemVerilog
keywords on case statement, as well as "unique" on if statements. However,
"priority if" is currently simply ignored.
"priority if" is currently ignored.
.. _Language Limitations:
@ -174,9 +174,9 @@ Structures and Unions
---------------------
Presently Verilator only supports packed structs and packed unions. Rand
and randc tags on members are simply ignored. All structures and unions
are represented as a single vector, which means that generating one member
of a structure from blocking, and another from non-blocking assignments is
and randc tags on members are ignored. All structures and unions are
represented as a single vector, which means that generating one member of a
structure from blocking, and another from non-blocking assignments is
unsupported.

View File

@ -279,26 +279,25 @@ To use profiling:
is being spent.
.. _Thread Profiling:
.. _Execution Profiling:
Thread Profiling
================
Execution Profiling
===================
When using multithreaded mode (:vlopt:`--threads`), it is useful to see
statistics and visualize how well the multiple CPUs are being utilized.
For performance optimization, it is useful to see statistics and visualize how
execution time is distributed in a verilated model.
With the :vlopt:`--prof-threads` option, Verilator will:
With the :vlopt:`--prof-exec` option, Verilator will:
* Add code to the Verilated model to record the start and end time of each
macro-task across a number of calls to eval. (What is a macro-task? See
the Verilator internals document (:file:`docs/internals.rst` in the
distribution.)
* Add code to the Verilated model to record execution flow.
* Add code to save profiling data in non-human-friendly form to the file
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
specified with :vlopt:`+verilator+prof+exec+file+\<filename\>`.
* Add code to save profiling data for thread profile-guided
optimization. See :ref:`Thread PGO`.
* In multi-threaded models, add code to record the start and end time of each
macro-task across a number of calls to eval. (What is a macro-task? See the
Verilator internals document (:file:`docs/internals.rst` in the
distribution.)
The :command:`verilator_gantt` program may then be run to transform the
saved profiling file into a nicer visual format and produce some related
@ -406,8 +405,8 @@ others as they prove beneficial.
Thread Profile-Guided Optimization
----------------------------------
Verilator supports thread profile-guided optimization (Thread PGO) to
improve multithreaded performance.
Verilator supports profile-guided optimization (verilation) of multi-threaded
models (Thread PGO) to improve performance.
When using multithreading, Verilator computes how long macro tasks take and
tries to balance those across threads. (What is a macro-task? See the
@ -417,13 +416,14 @@ balanced, leading to decreased performance. Thread PGO allows collecting
profiling data to replace the estimates and better optimize these
decisions.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
option.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-pgo` option. This
will code to the verilated model to save profiling data for profile-guided
optimization.
Run the model executable. When the executable exits, it will create a
profile.vlt file.
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
Rerun Verilator, optionally omitting the :vlopt:`--prof-pgo` option,
and adding the profile.vlt generated earlier to the command line.
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
@ -433,8 +433,8 @@ directly without any prefix.
If results from multiple simulations are to be used in generating the
optimization, multiple simulation's profile.vlt may be concatenated
externally, or each of the files may be fed as separate command line
options into Verilator. Verilator will simply sum the profile results, so
a longer running test will have proportionally more weight for optimization
options into Verilator. Verilator will sum the profile results, so a
longer running test will have proportionally more weight for optimization
than a shorter running test.
If you provide any profile feedback data to Verilator, and it cannot use

View File

@ -265,7 +265,7 @@ This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
(presumably on socket 0) optimizing performance. Of course this must be
adjusted if you want another simulator using e.g. socket 1, or if you
Verilated with a different number of threads. To see what CPUs are
actually used, use :vlopt:`--prof-threads`.
actually used, use :vlopt:`--prof-exec`.
Multithreaded Verilog and Library Support

View File

@ -12,7 +12,7 @@ Disabling Warnings
Warnings may be disabled in multiple ways:
#. Disable the warning in the source code. When the warning is printed it
will include a warning code. Simply surround the offending line with a
will include a warning code. Surround the offending line with a
:code:`/*verilator&32;lint_off*/` and :code:`/*verilator&32;lint_on*/`
metacomment pair:
@ -162,7 +162,7 @@ List Of Warnings
always @(posedge clk) foo[0] <= ...
always_comb foo[1] = ...
Simply use a different register for the flop:
Instead use a different register for the flop:
.. code-block:: sv
@ -284,7 +284,7 @@ List Of Warnings
.. TODO better example
Warns that it is simply better style to use casez, and "?" in place of
Warns that it is better style to use casez, and "?" in place of
"x"'s. See
`http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf
<http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf>`_
@ -1212,10 +1212,10 @@ List Of Warnings
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
This is a warning because Verilator does not support delayed statements.
It will simply ignore all such delays. In many cases ignoring a delay
might be harmless, but if the delayed statement is, as in this example,
used to cause some important action at a later time, it might be an
important difference.
It will ignore all such delays. In many cases ignoring a delay might be
harmless, but if the delayed statement is, as in this example, used to
cause some important action at a later time, it might be an important
difference.
Some possible workarounds:

View File

@ -268,11 +268,11 @@ Estimating Logic Costs
To compute the cost of any given path through the graph, Verilator
estimates an execution cost for each task. Each macro-task has an execution
cost which is simply the sum of its tasks' costs. We assume that
communication overhead and synchronization overhead are zero, so the cost
of any given path through the graph is simply the sum of macro-task
execution costs. Sarkar does almost the same thing, except that he has
nonzero estimates for synchronization costs.
cost which is the sum of its tasks' costs. We assume that communication
overhead and synchronization overhead are zero, so the cost of any given
path through the graph is the sum of macro-task execution costs. Sarkar
does almost the same thing, except that he has nonzero estimates for
synchronization costs.
Verilator's cost estimates are assigned by ``InstrCountCostVisitor``. This
class is perhaps the most fragile piece of the multithread
@ -301,7 +301,7 @@ prerequisites on other threads have finished.
The synchronization cost is cheap if the prereqs are done. If they're not,
fragmentation (idle CPU cores waiting) is possible. This is the major
source of overhead in this approach. The ``--prof-threads`` switch and the
source of overhead in this approach. The ``--prof-exec`` switch and the
``verilator_gantt`` script can visualize the time lost to such
fragmentation.
@ -817,7 +817,7 @@ which you can install using cpan.
There are some traps to avoid when running regression tests
- When checking the MANIFEST, the test will barf on unexpected code in the
- When checking the MANIFEST, the test will fail on unexpected code in the
Verilator tree. So make sure to keep any such code outside the tree.
- Not all Linux systems install Perldoc by default. This is needed for the
@ -1186,7 +1186,7 @@ anticipated to be ever implemented for the reasons indicated.
IEEE 1800-2017 3.3 modules within modules
Little/no tool support, and arguably not a good practice.
IEEE 1800-2017 6.12 "shortreal"
Little/no tool support, and easily simply promoted to real.
Little/no tool support, and easily promoted to real.
IEEE 1800-2017 11.11 Min, typ, max
No SDF support so will always use typical.
IEEE 1800-2017 11.12 "let"

View File

@ -69,7 +69,7 @@ int main(int argc, char** argv, char** env) {
// Historical note, before Verilator 4.200 Verilated::gotFinish()
// was used above in place of contextp->gotFinish().
// Most of the contextp-> calls can use Verilated:: calls instead;
// the Verilated:: versions simply assume there's a single context
// the Verilated:: versions just assume there's a single context
// being used (per thread). It's faster and clearer to use the
// newer contextp-> versions.

View File

@ -58,11 +58,11 @@ int sc_main(int argc, char* argv[]) {
// Define interconnect
sc_signal<bool> reset_l;
sc_signal<vluint32_t> in_small;
sc_signal<vluint64_t> in_quad;
sc_signal<uint32_t> in_small;
sc_signal<uint64_t> in_quad;
sc_signal<sc_bv<70>> in_wide;
sc_signal<vluint32_t> out_small;
sc_signal<vluint64_t> out_quad;
sc_signal<uint32_t> out_small;
sc_signal<uint64_t> out_quad;
sc_signal<sc_bv<70>> out_wide;
// Construct the Verilated model, from inside Vtop.h

View File

@ -55,6 +55,7 @@
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <sys/stat.h> // mkdir
#include <list>
@ -73,10 +74,10 @@ constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
//===========================================================================
// Static sanity checks
static_assert(sizeof(vluint8_t) == 1, "vluint8_t is missized");
static_assert(sizeof(vluint16_t) == 2, "vluint8_t is missized");
static_assert(sizeof(vluint32_t) == 4, "vluint8_t is missized");
static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized");
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");
//===========================================================================
// Global variables
@ -243,21 +244,21 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
return out;
}
vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
uint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
#ifdef VL_THREADED
static std::atomic<vluint64_t> sequence;
static std::atomic<uint64_t> sequence;
#else
static vluint64_t sequence = 0;
static uint64_t sequence = 0;
#endif
return ++sequence;
}
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
uint32_t VL_THREAD_ID() VL_MT_SAFE {
#ifdef VL_THREADED
// Alternative is to use std::this_thread::get_id, but that returns a
// hard-to-read number and is very slow
static std::atomic<vluint32_t> s_nextId(0);
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
static std::atomic<uint32_t> s_nextId(0);
static VL_THREAD_LOCAL uint32_t t_myId = ++s_nextId;
return t_myId;
#else
return 0;
@ -295,7 +296,7 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
//===========================================================================
// Random -- Mostly called at init time, so not inline.
static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
static uint32_t vl_sys_rand32() VL_MT_UNSAFE {
// Return random 32-bits using system library.
// Used only to construct seed for Verilator's PNRG.
static VerilatedMutex s_mutex;
@ -308,9 +309,9 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
#endif
}
vluint64_t vl_rand64() VL_MT_SAFE {
static VL_THREAD_LOCAL vluint64_t t_state[2];
static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0;
uint64_t vl_rand64() VL_MT_SAFE {
static VL_THREAD_LOCAL uint64_t t_state[2];
static VL_THREAD_LOCAL uint32_t t_seedEpoch = 0;
// For speed, we use a thread-local epoch number to know when to reseed
// A thread always belongs to a single context, so this works out ok
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
@ -324,7 +325,7 @@ vluint64_t vl_rand64() VL_MT_SAFE {
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
}
// Xoroshiro128+ algorithm
const vluint64_t result = t_state[0] + t_state[1];
const uint64_t result = t_state[0] + t_state[1];
t_state[1] ^= t_state[0];
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
@ -409,11 +410,11 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
if (vw == 1) { // Single divisor word breaks rest of algorithm
vluint64_t k = 0;
uint64_t k = 0;
for (int j = uw - 1; j >= 0; --j) {
const vluint64_t unw64 = ((k << 32ULL) + static_cast<vluint64_t>(lwp[j]));
owp[j] = unw64 / static_cast<vluint64_t>(rwp[0]);
k = unw64 - static_cast<vluint64_t>(owp[j]) * static_cast<vluint64_t>(rwp[0]);
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]);
}
if (is_modulus) {
owp[0] = k;
@ -423,8 +424,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
}
// +1 word as we may shift during normalization
vluint32_t un[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
vluint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized
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
// Zero for ease of debugging and to save having to zero for shifts
// Note +1 as loop will use extra word
@ -433,7 +434,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
// Algorithm requires divisor MSB to be set
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
const vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
const uint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
for (int i = vw - 1; i > 0; --i) {
vn[i] = (rwp[i] << s) | (shift_mask & (rwp[i - 1] >> (32 - s)));
}
@ -453,10 +454,10 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
// Main loop
for (int j = uw - vw; j >= 0; --j) {
// Estimate
const vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
| static_cast<vluint64_t>(un[j + vw - 1]));
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw - 1]);
vluint64_t rhat = unw64 - qhat * static_cast<vluint64_t>(vn[vw - 1]);
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]);
again:
if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) {
@ -465,10 +466,10 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
if (rhat < 0x100000000ULL) goto again;
}
vlsint64_t t = 0; // Must be signed
vluint64_t k = 0;
int64_t t = 0; // Must be signed
uint64_t k = 0;
for (int i = 0; i < vw; ++i) {
const vluint64_t p = qhat * vn[i]; // Multiply by estimate
const uint64_t p = qhat * vn[i]; // Multiply by estimate
t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract
un[i + j] = t;
k = (p >> 32ULL) - (t >> 32ULL);
@ -482,7 +483,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
owp[j]--;
k = 0;
for (int i = 0; i < vw; ++i) {
t = static_cast<vluint64_t>(un[i + j]) + static_cast<vluint64_t>(vn[i]) + k;
t = static_cast<uint64_t>(un[i + j]) + static_cast<uint64_t>(vn[i]) + k;
un[i + j] = t;
k = t >> 32ULL;
}
@ -613,7 +614,7 @@ double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
}
double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
vluint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
uint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
VL_NEGATE_W(VL_WORDS_I(lbits), pos, lwp);
_vl_clean_inplace_w(lbits, pos);
return -VL_ITOR_D_W(lbits, pos);
@ -687,7 +688,7 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
const WDataInP max64Bit
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<vluint64_t>::max()); // breaks shifted
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
VlWide<w> zero, ten;
@ -711,7 +712,7 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
fracDigits, VL_SET_QW(frac), suffix.c_str());
}
} else {
const vluint64_t integer64 = VL_SET_QW(integer);
const uint64_t integer64 = VL_SET_QW(integer);
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 "%s", integer64,
suffix.c_str());
@ -878,9 +879,9 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
int digits = 0;
std::string append;
if (lbits <= VL_QUADSIZE) {
digits = VL_SNPRINTF(
t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64,
static_cast<vlsint64_t>(VL_EXTENDS_QQ(lbits, lbits, ld)));
digits
= VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64,
static_cast<int64_t>(VL_EXTENDS_QQ(lbits, lbits, ld)));
append = t_tmp;
} else {
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
@ -1183,7 +1184,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_skipspace(fp, floc, fromp, fstr);
_vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_");
if (!t_tmp[0]) goto done;
vlsint64_t ld = 0;
int64_t ld = 0;
std::sscanf(t_tmp, "%30" PRId64, &ld);
VL_SET_WQ(owp, ld);
break;
@ -1197,7 +1198,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// cppcheck-suppress unusedStructMember // It's used
union {
double r;
vlsint64_t ld;
int64_t ld;
} u;
u.r = std::strtod(t_tmp, nullptr);
VL_SET_WQ(owp, u.ld);
@ -1642,7 +1643,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_ZERO_RESET_W(rbits, rwp);
switch (std::tolower(fmt)) {
case 'd': {
vlsint64_t lld = 0;
int64_t lld = 0;
std::sscanf(dp, "%30" PRId64, &lld);
VL_SET_WQ(rwp, lld);
break;
@ -1677,7 +1678,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
break;
}
default: // Other simulators simply return 0 in these cases and don't error out
default: // Other simulators return 0 in these cases and don't error out
return 0;
}
_vl_clean_inplace_w(rbits, rwp);
@ -1767,7 +1768,7 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs;
const vlsint32_t rhs_s = rhs; // To signed value
const int32_t rhs_s = rhs; // To signed value
// 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;
@ -1775,15 +1776,15 @@ std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE {
CData v = 0;
const vlsint32_t rhs_s = rhs; // To signed value
const int32_t rhs_s = rhs; // To signed value
// 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 {
const vlsint32_t rhs_s = rhs; // To signed value
const vlsint32_t ths_s = ths; // To signed value
const int32_t rhs_s = rhs; // To signed value
const int32_t ths_s = ths; // To signed value
// 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
@ -1822,7 +1823,7 @@ static const char* memhFormat(int nBits) {
return t_buf;
}
static const char* formatBinary(int nBits, vluint32_t bits) {
static const char* formatBinary(int nBits, uint32_t bits) {
assert((nBits >= 1) && (nBits <= 32));
static VL_THREAD_LOCAL char t_buf[64];
@ -2026,9 +2027,9 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
}
} else if (m_bits <= 64) {
const QData* const datap = reinterpret_cast<const QData*>(valuep);
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
const vluint32_t lo = value & 0xffffffff;
const vluint32_t hi = value >> 32;
const uint64_t value = VL_MASK_Q(m_bits) & *datap;
const uint32_t lo = value & 0xffffffff;
const uint32_t hi = value >> 32;
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits - 32), hi);
fprintf(m_fp, "%08x\n", lo);
@ -2234,8 +2235,8 @@ double vl_time_multiplier(int scale) VL_PURE {
return pow10[scale];
}
}
vluint64_t vl_time_pow10(int n) {
static const vluint64_t pow10[20] = {
uint64_t vl_time_pow10(int n) {
static const uint64_t pow10[20] = {
1ULL,
10ULL,
100ULL,
@ -2279,7 +2280,7 @@ VerilatedContext::VerilatedContext()
: m_impdatap{new VerilatedContextImpData} {
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
@ -2347,21 +2348,21 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotFinish = flag;
}
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
void VerilatedContext::profExecStart(uint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsStart = flag;
m_ns.m_profExecStart = flag;
}
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
void VerilatedContext::profExecWindow(uint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsWindow = flag;
m_ns.m_profExecWindow = flag;
}
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
void VerilatedContext::profExecFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsFilename = flag;
m_ns.m_profExecFilename = flag;
}
std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
std::string VerilatedContext::profExecFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profThreadsFilename;
return m_ns.m_profExecFilename;
}
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
@ -2506,13 +2507,16 @@ std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_ar
void VerilatedContextImp::commandArgVl(const std::string& arg) {
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
std::string value;
std::string str;
uint64_t u64;
if (arg == "+verilator+debug") {
Verilated::debug(4);
} else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) {
Verilated::debug(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) {
errorLimit(std::atoi(value.c_str()));
} 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));
} else if (arg == "+verilator+help") {
VerilatedImp::versionDump();
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
@ -2520,18 +2524,22 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
"Exiting due to command line argument (not an error)");
} else if (arg == "+verilator+noassert") {
assertOn(false);
} else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) {
profThreadsStart(std::atoll(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) {
profThreadsWindow(std::atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
profThreadsFilename(value);
} else if (commandArgVlValue(arg, "+verilator+prof+vlt+file+", value /*ref*/)) {
profVltFilename(value);
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
randReset(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
randSeed(std::atoi(value.c_str()));
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)
|| commandArgVlUint64(arg, "+verilator+prof+threads+start+", u64)) {
profExecStart(u64);
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)
|| commandArgVlUint64(arg, "+verilator+prof+threads+window+", u64, 1)) {
profExecWindow(u64);
} else if (commandArgVlString(arg, "+verilator+prof+exec+file+", str)
|| commandArgVlString(arg, "+verilator+prof+threads+file+", str)) {
profExecFilename(str);
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
profVltFilename(str);
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
randReset(static_cast<int>(u64));
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
std::numeric_limits<int>::max())) {
randSeed(static_cast<int>(u64));
} else if (arg == "+verilator+V") {
VerilatedImp::versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "",
@ -2541,12 +2549,14 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
const std::string msg = "Unknown runtime argument: " + arg;
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
}
}
}
bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer) {
bool VerilatedContextImp::commandArgVlString(const std::string& arg, const std::string& prefix,
std::string& valuer) {
const size_t len = prefix.length();
if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
@ -2556,6 +2566,30 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s
}
}
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());
};
if (std::any_of(str.begin(), str.end(), [](int c) { return !std::isdigit(c); })) fail();
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;
}
//======================================================================
// VerilatedContext:: + VerilatedContextImp:: Methods - random
@ -2564,20 +2598,19 @@ void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
m_s.m_randSeed = val;
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
const uint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
#ifdef VL_THREADED
std::atomic_signal_fence(std::memory_order_release);
#endif
VerilatedContextImp::s().s_randSeedEpoch = newEpoch;
}
vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
if (randSeed() != 0) {
return ((static_cast<vluint64_t>(randSeed()) << 32)
^ (static_cast<vluint64_t>(randSeed())));
return ((static_cast<uint64_t>(randSeed()) << 32) ^ (static_cast<uint64_t>(randSeed())));
} else {
return ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<vluint64_t>(vl_sys_rand32())));
return ((static_cast<uint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<uint64_t>(vl_sys_rand32())));
}
}
@ -2821,8 +2854,8 @@ VerilatedModule::~VerilatedModule() {
// VerilatedVar:: Methods
// cppcheck-suppress unusedFunction // Used by applications
vluint32_t VerilatedVarProps::entSize() const {
vluint32_t size = 1;
uint32_t VerilatedVarProps::entSize() const {
uint32_t size = 1;
switch (vltype()) {
case VLVT_PTR: size = sizeof(void*); break;
case VLVT_UINT8: size = sizeof(CData); break;
@ -2845,7 +2878,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
const int indxAdj = indx - low(dim);
vluint8_t* bytep = reinterpret_cast<vluint8_t*>(datap);
uint8_t* bytep = reinterpret_cast<uint8_t*>(datap);
// If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
size_t slicesz = entSize();
for (int d = dim + 1; d <= m_udims; ++d) slicesz *= elements(d);
@ -2866,7 +2899,7 @@ VerilatedScope::~VerilatedScope() {
}
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, vlsint8_t timeunit,
const char* identifier, int8_t timeunit,
const Type& type) VL_MT_UNSAFE {
// Slowpath - called once/scope at construction
// We don't want the space and reference-count access overhead of strings.

View File

@ -101,11 +101,11 @@ class VerilatedVcdSc;
// clang-format off
// P // Packed data of bit type (C/S/I/Q/W)
using CData = vluint8_t; ///< Data representing 'bit' of 1-8 packed bits
using SData = vluint16_t; ///< Data representing 'bit' of 9-16 packed bits
using IData = vluint32_t; ///< Data representing 'bit' of 17-32 packed bits
using QData = vluint64_t; ///< Data representing 'bit' of 33-64 packed bits
using EData = vluint32_t; ///< Data representing one element of WData array
using CData = uint8_t; ///< Data representing 'bit' of 1-8 packed bits
using SData = uint16_t; ///< Data representing 'bit' of 9-16 packed bits
using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits
using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits
using EData = uint32_t; ///< Data representing one element of WData array
using WData = EData; ///< Data representing >64 packed bits (used as pointer)
// F = float; // No typedef needed; Verilator uses float
// D = double; // No typedef needed; Verilator uses double
@ -115,7 +115,7 @@ using WData = EData; ///< Data representing >64 packed bits (used as poin
using WDataInP = const WData*; ///< 'bit' of >64 packed bits as array input to a function
using WDataOutP = WData*; ///< 'bit' of >64 packed bits as array output from a function
enum VerilatedVarType : vluint8_t {
enum VerilatedVarType : uint8_t {
VLVT_UNKNOWN = 0,
VLVT_PTR, // Pointer to something
VLVT_UINT8, // AKA CData
@ -143,7 +143,7 @@ enum VerilatedVarFlags {
// Mutex and threading support
// Return current thread ID (or 0), not super fast, cache if needed
extern vluint32_t VL_THREAD_ID() VL_MT_SAFE;
extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
#if VL_THREADED
@ -226,7 +226,7 @@ public:
class VerilatedAssertOneThread final {
// MEMBERS
#if defined(VL_THREADED) && defined(VL_DEBUG)
vluint32_t m_threadid; // Thread that is legal
uint32_t m_threadid; // Thread that is legal
public:
// CONSTRUCTORS
// The constructor establishes the thread id for all later calls.
@ -320,10 +320,10 @@ protected:
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
bool m_gotError = false; // A $finish statement executed
bool m_gotFinish = false; // A $finish or $stop statement executed
vluint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
// Slow path
vlsint8_t m_timeunit; // Time unit as 0..15
vlsint8_t m_timeprecision; // Time precision as 0..15
int8_t m_timeunit; // Time unit as 0..15
int8_t m_timeprecision; // Time precision as 0..15
int m_errorCount = 0; // Number of errors
int m_errorLimit = 1; // Stop on error number
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
@ -344,10 +344,10 @@ protected:
struct NonSerialized { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
vluint64_t m_profThreadsStart = 1; // +prof+threads starting time
vluint32_t m_profThreadsWindow = 2; // +prof+threads window size
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
// Slow path
std::string m_profThreadsFilename; // +prof+threads filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
} m_ns;
@ -468,16 +468,16 @@ public:
/// timeInc, operating on the thread's context.
///
/// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy
/// 'vluint64_t vl_time_stamp64()' which must a function be defined by
/// 'uint64_t vl_time_stamp64()' which must a function be defined by
/// the user's wrapper.
///
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
/// must be a function defined by the user's wrapper.
vluint64_t time() const VL_MT_SAFE;
uint64_t time() const VL_MT_SAFE;
/// Set current simulation time. See time() for side effect details
void time(vluint64_t value) VL_MT_SAFE { m_s.m_time = value; }
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
void timeInc(vluint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
/// Return time units as power-of-ten
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
/// Set time units as power-of-ten
@ -518,13 +518,13 @@ public: // But for internal use only
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
// Internal: --prof-threads related settings
void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
vluint64_t profThreadsStart() const VL_MT_SAFE { return m_ns.m_profThreadsStart; }
void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; }
void profThreadsFilename(const std::string& flag) VL_MT_SAFE;
std::string profThreadsFilename() const VL_MT_SAFE;
// Internal: --prof-exec related settings
void profExecStart(uint64_t flag) VL_MT_SAFE;
uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
void profExecWindow(uint64_t flag) VL_MT_SAFE;
uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
void profExecFilename(const std::string& flag) VL_MT_SAFE;
std::string profExecFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
std::string profVltFilename() const VL_MT_SAFE;
@ -559,7 +559,7 @@ public: // But for internal use only
class VerilatedScope final {
public:
enum Type : vluint8_t {
enum Type : uint8_t {
SCOPE_MODULE,
SCOPE_OTHER
}; // Type of a scope, currently module is only interesting
@ -572,21 +572,21 @@ private:
VerilatedVarNameMap* m_varsp = nullptr; // Variable map
const char* m_namep = nullptr; // Scope name (Slowpath)
const char* m_identifierp = nullptr; // Identifier of scope (with escapes removed)
vlsint8_t m_timeunit = 0; // Timeunit in negative power-of-10
int8_t m_timeunit = 0; // Timeunit in negative power-of-10
Type m_type = SCOPE_OTHER; // Type of the scope
public: // But internals only - called from VerilatedModule's
VerilatedScope() = default;
~VerilatedScope();
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, vlsint8_t timeunit, const Type& type) VL_MT_UNSAFE;
const char* identifier, int8_t timeunit, const Type& type) VL_MT_UNSAFE;
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
void varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const { return m_namep; }
const char* identifier() const { return m_identifierp; }
vlsint8_t timeunit() const { return m_timeunit; }
int8_t timeunit() const { return m_timeunit; }
inline VerilatedSyms* symsp() const { return m_symsp; }
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
@ -636,9 +636,9 @@ class Verilated final {
// Fast path
VerilatedContext* t_contextp = nullptr; // Thread's context
#ifdef VL_THREADED
vluint32_t t_mtaskId = 0; // mtask# executing on this thread
uint32_t t_mtaskId = 0; // mtask# executing on this thread
// Messages maybe pending on thread, needs end-of-eval calls
vluint32_t t_endOfEvalReqd = 0;
uint32_t t_endOfEvalReqd = 0;
#endif
const VerilatedScope* t_dpiScopep = nullptr; // DPI context scope
const char* t_dpiFilename = nullptr; // DPI context filename
@ -760,11 +760,11 @@ public:
/// Return VerilatedContext::randSeed using current thread's VerilatedContext
static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); }
/// Call VerilatedContext::time using current thread's VerilatedContext
static void time(vluint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
static void time(uint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
/// Return VerilatedContext::time using current thread's VerilatedContext
static vluint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
static uint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
/// Call VerilatedContext::timeInc using current thread's VerilatedContext
static void timeInc(vluint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
static void timeInc(uint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
// Deprecated
static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); }
static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); }
@ -850,8 +850,8 @@ public:
#ifdef VL_THREADED
// Internal: Set the mtaskId, called when an mtask starts
// Per thread, so no need to be in VerilatedContext
static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }

View File

@ -193,6 +193,10 @@ VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
# but keeping the distinction for compatibility for now.
VK_GLOBAL_OBJS = $(addsuffix .o, $(VM_GLOBAL_FAST) $(VM_GLOBAL_SLOW))
# Need to re-build if the generated makefile changes, as compiler options might
# have changed.
$(VK_GLOBAL_OBJS): $(VM_PREFIX).mk
ifneq ($(VM_PARALLEL_BUILDS),1)
# Fast build for small designs: All .cpp files in one fell swoop. This
# saves total compute, but can be slower if only a little changes. It is

View File

@ -59,7 +59,7 @@ public: // But only local to this file
}
}
virtual ~VerilatedCovImpItem() = default;
virtual vluint64_t count() const = 0;
virtual uint64_t count() const = 0;
virtual void zero() const = 0;
};
@ -76,7 +76,7 @@ private:
public:
// METHODS
// cppcheck-suppress truncLongCastReturn
virtual vluint64_t count() const override { return *m_countp; }
virtual uint64_t count() const override { return *m_countp; }
virtual void zero() const override { *m_countp = 0; }
// CONSTRUCTORS
// cppcheck-suppress noExplicitConstructor
@ -372,7 +372,7 @@ public:
os << "# SystemC::Coverage-3\n";
// Build list of events; totalize if collapsing hierarchy
std::map<const std::string, std::pair<std::string, vluint64_t>> eventCounts;
std::map<const std::string, std::pair<std::string, uint64_t>> eventCounts;
for (const auto& itemp : m_items) {
std::string name;
std::string hier;
@ -439,11 +439,11 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
}
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<uint32_t>{itemp});
}
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<uint64_t>{itemp});
}
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);

View File

@ -72,7 +72,7 @@ class VerilatedCovImp;
///
/// Example:
///
/// vluint32_t m_cases[10]; // Storage for coverage data
/// uint32_t m_cases[10]; // Storage for coverage data
/// constructor() {
/// // Initialize
/// for (int i = 0; i < 10; ++i) m_cases[i] = 0;
@ -126,8 +126,8 @@ public: // But Internal use only
// Call _insert1, followed by _insert2 and _insert3
// Do not call directly; use VL_COVER_INSERT or higher level macros instead
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
void _inserti(vluint32_t* itemp) VL_MT_SAFE;
void _inserti(vluint64_t* itemp) VL_MT_SAFE;
void _inserti(uint32_t* itemp) VL_MT_SAFE;
void _inserti(uint64_t* itemp) VL_MT_SAFE;
// _insert2: Set default filename and line number
void _insertf(const char* filename, int lineno) VL_MT_SAFE;
// _insert3: Set parameters

View File

@ -417,7 +417,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
}
//======================================================================
// DPI accessors that simply call above functions
// DPI accessors that call above functions
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);

View File

@ -144,12 +144,12 @@ void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
fstWriterFlushContext(m_fst);
}
void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
void VerilatedFst::emitTimeChange(uint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
//=============================================================================
// Decl
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elements,
unsigned int minValbits, const char** itemNamesp,
const char** itemValuesp) {
const fstEnumHandle enumNum
@ -157,7 +157,7 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elem
m_local2fstdtype[dtypenum] = enumNum;
}
void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, bool bussed, int msb,
int lsb) {
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
@ -224,23 +224,23 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
}
}
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
}
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
}
@ -250,13 +250,13 @@ void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, f
// so always inline them.
VL_ATTR_ALWINLINE
void VerilatedFst::emitBit(vluint32_t code, CData newval) {
void VerilatedFst::emitBit(uint32_t code, CData newval) {
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
void VerilatedFst::emitCData(uint32_t code, CData newval, int bits) {
char buf[VL_BYTESIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
@ -264,7 +264,7 @@ void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
void VerilatedFst::emitSData(uint32_t code, SData newval, int bits) {
char buf[VL_SHORTSIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
@ -272,7 +272,7 @@ void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
void VerilatedFst::emitIData(uint32_t code, IData newval, int bits) {
char buf[VL_IDATASIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
@ -280,7 +280,7 @@ void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
void VerilatedFst::emitQData(uint32_t code, QData newval, int bits) {
char buf[VL_QUADSIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
@ -288,7 +288,7 @@ void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
void VerilatedFst::emitWData(uint32_t code, const WData* newvalp, int bits) {
int words = VL_WORDS_I(bits);
char* wp = m_strbuf;
// Convert the most significant word
@ -304,6 +304,6 @@ void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitDouble(vluint32_t code, double newval) {
void VerilatedFst::emitDouble(uint32_t code, double newval) {
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
}

View File

@ -45,7 +45,7 @@ private:
// FST specific internals
void* m_fst;
std::map<vluint32_t, fstHandle> m_code2symbol;
std::map<uint32_t, fstHandle> m_code2symbol;
std::map<int, fstEnumHandle> m_local2fstdtype;
std::list<std::string> m_curScope;
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
@ -53,7 +53,7 @@ private:
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedFst);
void declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
protected:
@ -61,7 +61,7 @@ protected:
// Implementation of VerilatedTrace interface
// Implementations of protected virtual methods for VerilatedTrace
virtual void emitTimeChange(vluint64_t timeui) override;
virtual void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
@ -69,13 +69,13 @@ protected:
// Implementations of duck-typed methods for VerilatedTrace. These are
// called from only one place (namely full*) so always inline them.
inline void emitBit(vluint32_t code, CData newval);
inline void emitCData(vluint32_t code, CData newval, int bits);
inline void emitSData(vluint32_t code, SData newval, int bits);
inline void emitIData(vluint32_t code, IData newval, int bits);
inline void emitQData(vluint32_t code, QData newval, int bits);
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
inline void emitDouble(vluint32_t code, double newval);
inline void emitBit(uint32_t code, CData newval);
inline void emitCData(uint32_t code, CData newval, int bits);
inline void emitSData(uint32_t code, SData newval, int bits);
inline void emitIData(uint32_t code, IData newval, int bits);
inline void emitQData(uint32_t code, QData newval, int bits);
inline void emitWData(uint32_t code, const WData* newvalp, int bits);
inline void emitDouble(uint32_t code, double newval);
public:
//=========================================================================
@ -98,25 +98,25 @@ public:
// Internal interface to Verilator generated code
// Inside dumping routines, declare a data type
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
unsigned int minValbits, const char** itemNamesp, const char** itemValuesp);
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
const char** itemNamesp, const char** itemValuesp);
// Inside dumping routines, declare a signal
void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum);
void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum);
};
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedTrace<VerilatedFst>::dump(vluint64_t timeui);
template <> void VerilatedTrace<VerilatedFst>::dump(uint64_t timeui);
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
@ -155,12 +155,12 @@ public:
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
void dump(uint64_t timeui) { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// conversion warnings. It's better to use a uint64_t time instead.
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
// METHODS - Internal/backward compatible
// \protectedsection

View File

@ -90,7 +90,7 @@ extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp);
extern IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE;
extern IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE;
inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
const vluint64_t rnd = vl_rand64();
const uint64_t rnd = vl_rand64();
if (VL_LIKELY(hi > lo)) {
// (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
@ -112,15 +112,6 @@ extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp);
/// Zero reset a signal (slow - else use VL_ZERO_W)
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp);
#if VL_THREADED
/// Return high-precision counter for profiling, or 0x0 if not available
inline QData VL_RDTSC_Q() {
vluint64_t val;
VL_RDTSC(val);
return val;
}
#endif
extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE;
@ -219,29 +210,27 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
// Return double from lhs (numeric) unsigned
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
return static_cast<double>(static_cast<vluint32_t>(lhs));
return static_cast<double>(static_cast<uint32_t>(lhs));
}
static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE {
return static_cast<double>(static_cast<vluint64_t>(lhs));
return static_cast<double>(static_cast<uint64_t>(lhs));
}
// Return double from lhs (numeric) signed
double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE {
if (lbits == 32) return static_cast<double>(static_cast<vlsint32_t>(lhs));
if (lbits == 32) return static_cast<double>(static_cast<int32_t>(lhs));
VlWide<VL_WQ_WORDS_E> lwp;
VL_SET_WI(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE {
if (lbits == 64) return static_cast<double>(static_cast<vlsint64_t>(lhs));
if (lbits == 64) return static_cast<double>(static_cast<int64_t>(lhs));
VlWide<VL_WQ_WORDS_E> lwp;
VL_SET_WQ(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
// Return QData from double (numeric)
static inline IData VL_RTOI_I_D(double lhs) VL_PURE {
return static_cast<vlsint32_t>(VL_TRUNC(lhs));
}
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
// (Requires clean input)
@ -290,28 +279,28 @@ extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
#if defined(SYSTEMC_VERSION)
/// Return current simulation time
// Already defined: extern sc_time sc_time_stamp();
inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
inline uint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
#else // Non-SystemC
# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
# ifdef VL_TIME_STAMP64
// vl_time_stamp64() may be optionally defined by the user to return time.
// On MSVC++ weak symbols are not supported so must be declared, or define
// VL_TIME_CONTEXT.
extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK;
extern uint64_t vl_time_stamp64() VL_ATTR_WEAK;
# else
// sc_time_stamp() may be optionally defined by the user to return time.
// On MSVC++ weak symbols are not supported so must be declared, or define
// VL_TIME_CONTEXT.
extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer
inline vluint64_t vl_time_stamp64() {
inline uint64_t vl_time_stamp64() {
// clang9.0.1 requires & although we really do want the weak symbol value
return VL_LIKELY(&sc_time_stamp) ? static_cast<vluint64_t>(sc_time_stamp()) : 0;
return VL_LIKELY(&sc_time_stamp) ? static_cast<uint64_t>(sc_time_stamp()) : 0;
}
# endif
# endif
#endif
inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
inline uint64_t VerilatedContext::time() const VL_MT_SAFE {
// When using non-default context, fastest path is return time
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
@ -336,7 +325,7 @@ inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
// Return time precision as multiplier of time units
double vl_time_multiplier(int scale) VL_PURE;
// Return power of 10. e.g. returns 100 if n==2
vluint64_t vl_time_pow10(int n) VL_PURE;
uint64_t vl_time_pow10(int n) VL_PURE;
#ifdef VL_DEBUG
/// Evaluate statement if VL_DEBUG defined
@ -882,46 +871,46 @@ static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) V
static inline IData VL_GTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
// For lbits==32, this becomes just a single instruction, otherwise ~5.
// GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed > rhs_signed;
}
static inline IData VL_GTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed > rhs_signed;
}
static inline IData VL_GTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed >= rhs_signed;
}
static inline IData VL_GTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed >= rhs_signed;
}
static inline IData VL_LTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed < rhs_signed;
}
static inline IData VL_LTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed < rhs_signed;
}
static inline IData VL_LTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed <= rhs_signed;
}
static inline IData VL_LTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed <= rhs_signed;
}
@ -1015,13 +1004,13 @@ static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp,
}
static inline IData VL_MULS_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
return lhs_signed * rhs_signed;
}
static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed * rhs_signed;
}
@ -1067,30 +1056,30 @@ static inline IData VL_DIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
return lhs_signed / rhs_signed;
}
static inline QData VL_DIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
return lhs_signed / rhs_signed;
}
static inline IData VL_MODDIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
return lhs_signed % rhs_signed;
}
static inline QData VL_MODDIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
return lhs_signed % rhs_signed;
}
@ -1421,8 +1410,8 @@ static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_P
// ret = 10324---
IData ret = ld;
if (rd_log2) {
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
const uint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
}
@ -1441,8 +1430,8 @@ static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_P
// Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
QData ret = ld;
if (rd_log2) {
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
const vluint32_t lbitsRem = lbits - lbitsFloor;
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
const uint32_t lbitsRem = lbits - lbitsFloor;
const QData msbMask = lbitsFloor == 64 ? 0ULL : VL_MASK_Q(lbitsRem) << lbitsFloor;
ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem));
}
@ -1940,8 +1929,8 @@ static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
if (lhs == 0.0) return 0;
const QData q = VL_CVT_Q_D(lhs);
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
vluint64_t out = 0;
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
uint64_t out = 0;
if (lsb < 0) {
out = mantissa >> -lsb;
} else if (lsb < 64) {
@ -1961,7 +1950,7 @@ static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) V
if (lhs == 0.0) return owp;
const QData q = VL_CVT_Q_D(lhs);
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
if (lsb < 0) {
VL_SET_WQ(owp, mantissa >> -lsb);
} else if (lsb < obits) {

View File

@ -34,6 +34,7 @@
#include <algorithm>
#include <deque>
#include <limits>
#include <map>
#include <numeric>
#include <set>
@ -64,7 +65,7 @@ public:
private:
// MEMBERS
vluint32_t m_mtaskId; // MTask that did enqueue
uint32_t m_mtaskId; // MTask that did enqueue
std::function<void()> m_cb; // Lambda to execute when message received
public:
// CONSTRUCTORS
@ -77,7 +78,7 @@ public:
VerilatedMsg& operator=(const VerilatedMsg&) = default;
VerilatedMsg& operator=(VerilatedMsg&&) = default;
// METHODS
vluint32_t mtaskId() const { return m_mtaskId; }
uint32_t mtaskId() const { return m_mtaskId; }
// Execute the lambda function
void run() const { m_cb(); }
};
@ -88,7 +89,7 @@ public:
class VerilatedEvalMsgQueue final {
using VerilatedThreadQueue = std::multiset<VerilatedMsg, VerilatedMsg::Cmp>;
std::atomic<vluint64_t> m_depth; // Current depth of queue (see comments below)
std::atomic<uint64_t> m_depth; // Current depth of queue (see comments below)
VerilatedMutex m_mutex; // Mutex protecting queue
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue
@ -244,8 +245,8 @@ public: // But only for verilated*.cpp
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
vluint64_t randSeedDefault64() const VL_MT_SAFE;
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
uint64_t randSeedDefault64() const VL_MT_SAFE;
static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
// METHODS - timeformat
int timeFormatUnits() const VL_MT_SAFE {
@ -394,7 +395,11 @@ protected:
// METHODS - protected
void commandArgsAddGuts(int argc, const char** argv);
void commandArgVl(const std::string& arg);
bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer);
bool commandArgVlString(const std::string& arg, const std::string& prefix,
std::string& valuer);
bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer,
uint64_t min = std::numeric_limits<uint64_t>::min(),
uint64_t max = std::numeric_limits<uint64_t>::max());
void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
};
@ -484,7 +489,7 @@ public:
public: // But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate.
// Slow ok - called once/scope on destruction, so we only iterate.
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {

View File

@ -0,0 +1,191 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2012-2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated run-time profiling implementation code
///
//=============================================================================
#include "verilatedos.h"
#include "verilated_profiler.h"
#if VL_THREADED
#include "verilated_threads.h"
#endif
#include <fstream>
#include <string>
//=============================================================================
// Globals
// Internal note: Globals may multi-construct, see verilated.cpp top.
VL_THREAD_LOCAL VlExecutionProfiler::ExecutionTrace VlExecutionProfiler::t_trace;
constexpr const char* const VlExecutionRecord::s_ascii[];
//=============================================================================
// VlPgoProfiler implementation
uint16_t VlExecutionRecord::getcpu() {
#if defined(__linux)
return sched_getcpu(); // TODO: this is a system call. Not exactly cheap.
#elif defined(__APPLE__) && !defined(__arm64__)
uint32_t info[4];
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
// info[1] is EBX, bits 24-31 are APIC ID
if ((info[3] & (1 << 9)) == 0) {
return -1; // no APIC on chip
} else {
return (unsigned)info[1] >> 24;
}
#elif defined(_WIN32)
return GetCurrentProcessorNumber();
#else
return 0;
#endif
}
//=============================================================================
// VlExecutionProfiler implementation
template <size_t N> size_t roundUptoMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
}
VlExecutionProfiler::VlExecutionProfiler() {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure(const VerilatedContext& context) {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true;
m_windowCount = context.profExecWindow() * 2;
m_lastStartReq = startReq;
}
}
void VlExecutionProfiler::setupThread(uint32_t threadId) {
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
// while profiling.
t_trace.reserve(RESERVED_TRACE_CAPACITY);
// Register thread-local buffer in list of all buffers
{
const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists);
}
}
void VlExecutionProfiler::clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (const auto& pair : m_traceps) {
ExecutionTrace* const tracep = pair.second;
const size_t reserve = roundUptoMultipleOf<RESERVED_TRACE_CAPACITY>(tracep->size());
tracep->clear();
tracep->reserve(reserve);
}
}
void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+exec writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) { VL_FATAL_MT(filenamep, 0, "", "+prof+exec+file file not writable"); }
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFVERSION 2.0 # Verilator execution profile version 2.0\n");
fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n",
Verilated::threadContextp()->profExecStart());
fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n",
Verilated::threadContextp()->profExecWindow());
const unsigned threads = static_cast<unsigned>(m_traceps.size());
fprintf(fp, "VLPROF stat threads %u\n", threads);
#ifdef VL_THREADED
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
#endif
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
for (const auto& pair : m_traceps) {
const uint32_t threadId = pair.first;
ExecutionTrace* const tracep = pair.second;
fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId);
for (const VlExecutionRecord& er : *tracep) {
const char* const name = VlExecutionRecord::s_ascii[static_cast<uint8_t>(er.m_type)];
const uint64_t time = er.m_tick - m_tickBegin;
fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time);
switch (er.m_type) {
case VlExecutionRecord::Type::EVAL_BEGIN:
case VlExecutionRecord::Type::EVAL_END:
case VlExecutionRecord::Type::EVAL_LOOP_BEGIN:
case VlExecutionRecord::Type::EVAL_LOOP_END:
// No payload
fprintf(fp, "\n");
break;
case VlExecutionRecord::Type::MTASK_BEGIN: {
const auto& payload = er.m_payload.mtaskBegin;
fprintf(fp, " id %u predictStart %u cpu %u\n", payload.m_id,
payload.m_predictStart, payload.m_cpu);
break;
}
case VlExecutionRecord::Type::MTASK_END: {
const auto& payload = er.m_payload.mtaskEnd;
fprintf(fp, " id %u predictCost %u\n", payload.m_id, payload.m_predictCost);
break;
}
default: abort(); // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - m_tickBegin);
std::fclose(fp);
}

View File

@ -12,7 +12,7 @@
//=============================================================================
///
/// \file
/// \brief Verilated general profiling header
/// \brief Verilated run-time profiling header
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
@ -23,58 +23,204 @@
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#include <deque>
#ifndef VL_PROFILER
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
#endif
#include "verilated.h"
#include <array>
#include <atomic>
#include <cassert>
#include <string>
#include <type_traits>
#include <vector>
class VlExecutionProfiler;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord()
//=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available
VL_ATTR_ALWINLINE
inline QData VL_CPU_TICK() {
uint64_t val;
VL_GET_CPU_TICK(val);
return val;
}
//=============================================================================
// Private class used by VlExecutionProfiler
#define _VL_FOREACH_APPLY(macro, arg) macro(arg, #arg)
// clang-format off
#define FOREACH_VlExecutionRecord_TYPE(macro) \
_VL_FOREACH_APPLY(macro, EVAL_BEGIN) \
_VL_FOREACH_APPLY(macro, EVAL_END) \
_VL_FOREACH_APPLY(macro, EVAL_LOOP_BEGIN) \
_VL_FOREACH_APPLY(macro, EVAL_LOOP_END) \
_VL_FOREACH_APPLY(macro, MTASK_BEGIN) \
_VL_FOREACH_APPLY(macro, MTASK_END)
// clang-format on
class VlExecutionRecord final {
friend class VlExecutionProfiler;
// TYPES
enum class Type : uint8_t {
#define VL_FOREACH_MACRO(id, name) id,
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
#undef VL_FOREACH_MACRO
};
static constexpr const char* const s_ascii[] = {
#define VL_FOREACH_MACRO(id, name) name,
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
#undef VL_FOREACH_MACRO
};
union Payload {
struct {
uint32_t m_id; // MTask id
uint32_t m_predictStart; // Time scheduler predicted would start
uint32_t m_cpu; // Executing CPU id
} mtaskBegin;
struct {
uint32_t m_id; // MTask id
uint32_t m_predictCost; // How long scheduler predicted would take
} mtaskEnd;
};
// STATE
// Layout below allows efficient packing.
const uint64_t m_tick = VL_CPU_TICK(); // Tick at construction
Payload m_payload; // The record payload
Type m_type; // The record type
static_assert(alignof(uint64_t) >= alignof(Payload), "Padding not allowed");
static_assert(alignof(Payload) >= alignof(Type), "Padding not allowed");
static uint16_t getcpu(); // Return currently executing CPU id
// Profile record, private class used only by this header
class VerilatedProfilerRec final {
const std::string m_name; // Hashed name of mtask/etc
const size_t m_counterNumber = 0; // Which counter has data
public:
// CONSTRUCTOR
VlExecutionRecord() = default;
// METHODS
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
: m_name{name}
, m_counterNumber{counterNumber} {}
VerilatedProfilerRec() = default;
size_t counterNumber() const { return m_counterNumber; }
std::string name() const { return m_name; }
void evalBegin() { m_type = Type::EVAL_BEGIN; }
void evalEnd() { m_type = Type::EVAL_END; }
void evalLoopBegin() { m_type = Type::EVAL_LOOP_BEGIN; }
void evalLoopEnd() { m_type = Type::EVAL_LOOP_END; }
void mtaskBegin(uint32_t id, uint32_t predictStart) {
m_payload.mtaskBegin.m_id = id;
m_payload.mtaskBegin.m_predictStart = predictStart;
m_payload.mtaskBegin.m_cpu = getcpu();
m_type = Type::MTASK_BEGIN;
}
void mtaskEnd(uint32_t id, uint32_t predictCost) {
m_payload.mtaskEnd.m_id = id;
m_payload.mtaskEnd.m_predictCost = predictCost;
m_type = Type::MTASK_END;
}
};
// Create some number of bucketed profilers
template <std::size_t T_Entries> class VerilatedProfiler final {
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
// reduce cache effects
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
std::deque<VerilatedProfilerRec> m_records; // Record information
static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
"VlExecutionRecord should be trivially destructible for fast buffer clearing");
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
// trace buffers are pre-allocated to be able to hold [a multiple] of this many records.
static constexpr size_t RESERVED_TRACE_CAPACITY = 4096;
// TYPES
// Execution traces are recorded into thread local vectors. We can append records of profiling
// events to this vector with very low overhead, and then dump them out later. This prevents
// the overhead of printf/malloc/IO from corrupting the profiling data. It's super cheap to
// append a VlProfileRec struct on the end of a pre-allocated vector; this is the only cost we
// pay in real-time during a profiling cycle. Internal note: Globals may multi-construct, see
// verilated.cpp top.
using ExecutionTrace = std::vector<VlExecutionRecord>;
// STATE
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
bool m_enabled = false; // Is profiling currently enabled
uint64_t m_tickBegin = 0; // Sample time (rdtsc() on x86) at beginning of collection
uint64_t m_lastStartReq = 0; // Last requested profiling start (in simulation time)
uint32_t m_windowCount = 0; // Track our position in the cache warmup and profile window
public:
// CONSTRUCTOR
VlExecutionProfiler();
// METHODS
// Is profiling enabled
inline bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
inline VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&);
// Setup profiling on a particular thread;
void setupThread(uint32_t threadId);
// Clear all profiling data
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
// Write profiling data into file
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
};
//=============================================================================
// VlPgoProfiler is for collecting profiling data for PGO
template <std::size_t T_Entries> class VlPgoProfiler final {
// TYPES
struct Record final {
const std::string m_name; // Hashed name of mtask/etc
const size_t m_counterNumber = 0; // Which counter has data
};
// Counters are stored packed, all together to reduce cache effects
std::array<uint64_t, T_Entries> m_counters; // Time spent on this record
std::vector<Record> m_records; // Record information
public:
// METHODS
VerilatedProfiler() = default;
~VerilatedProfiler() = default;
VlPgoProfiler() = default;
~VlPgoProfiler() = default;
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
void addCounter(size_t counter, const std::string& name) {
VL_DEBUG_IF(assert(counter < T_Entries););
m_records.emplace_back(VerilatedProfilerRec{counter, name});
m_records.emplace_back(Record{name, counter});
}
void startCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
// -= so when we add end time in stopCounter, we already subtracted
// out, without needing to hold another temporary
m_counters[counter] -= val;
}
void stopCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
m_counters[counter] += val;
// -= so when we add end time in stopCounter, the net effect is adding the difference,
// without needing to hold onto a temporary
m_counters[counter] -= VL_CPU_TICK();
}
void stopCounter(size_t counter) { m_counters[counter] += VL_CPU_TICK(); }
};
template <std::size_t T_Entries>
void VerilatedProfiler<T_Entries>::write(const char* modelp,
const std::string& filename) VL_MT_SAFE {
void VlPgoProfiler<T_Entries>::write(const char* modelp, const std::string& filename) VL_MT_SAFE {
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock{s_mutex};
@ -88,14 +234,9 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
FILE* fp = nullptr;
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
if (VL_UNLIKELY(!fp))
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
FILE* const fp = std::fopen(filename.c_str(), s_firstCall ? "w" : "a");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
s_firstCall = false;
@ -104,10 +245,9 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
fprintf(fp, "`verilator_config\n");
for (const auto& it : m_records) {
const std::string& name = it.name();
for (const Record& rec : m_records) {
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" PRIu64 "\n", modelp,
name.c_str(), m_counters[it.counterNumber()]);
rec.m_name.c_str(), m_counters[rec.m_counterNumber]);
}
std::fclose(fp);

View File

@ -63,8 +63,8 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved";
bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
size_t size) VL_MT_UNSAFE_ONE {
bufferCheck();
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
vluint8_t miss = 0;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
uint8_t miss = 0;
while (size--) miss |= (*dp++ ^ *m_cp++);
return (miss != 0);
}
@ -192,7 +192,7 @@ void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return;
const vluint8_t* wp = m_bufp;
const uint8_t* wp = m_bufp;
while (true) {
const ssize_t remaining = (m_cp - wp);
if (remaining == 0) break;
@ -219,8 +219,8 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return;
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
vluint8_t* rp = m_bufp;
for (vluint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
uint8_t* rp = m_bufp;
for (uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
m_endp = m_bufp + (m_endp - m_cp);
m_cp = m_bufp; // Reset buffer
// Read into buffer starting at m_endp

View File

@ -40,8 +40,8 @@ class VerilatedSerialize VL_NOT_FINAL {
protected:
// MEMBERS
// For speed, keep m_cp as the first member of this structure
vluint8_t* m_cp; // Current pointer into m_bufp buffer
vluint8_t* m_bufp; // Output buffer
uint8_t* m_cp; // Current pointer into m_bufp buffer
uint8_t* m_bufp; // Output buffer
bool m_isOpen = false; // True indicates open file/stream
std::string m_filename; // Filename, for error messages
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
@ -58,7 +58,7 @@ protected:
public:
/// Construct
VerilatedSerialize() {
m_bufp = new vluint8_t[bufferSize()];
m_bufp = new uint8_t[bufferSize()];
m_cp = m_bufp;
}
/// Flish, close, and destruct
@ -77,12 +77,12 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Write data to stream
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
const uint8_t* __restrict dp = (const uint8_t* __restrict)datap;
while (size) {
bufferCheck();
size_t blk = size;
if (blk > bufferInsertSize()) blk = bufferInsertSize();
const vluint8_t* __restrict maxp = dp + blk;
const uint8_t* __restrict maxp = dp + blk;
for (; dp < maxp; *m_cp++ = *dp++) {}
size -= blk;
}
@ -111,9 +111,9 @@ class VerilatedDeserialize VL_NOT_FINAL {
protected:
// MEMBERS
// For speed, keep m_cp as the first member of this structure
vluint8_t* m_cp; // Current pointer into m_bufp buffer
vluint8_t* m_bufp; // Output buffer
vluint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
uint8_t* m_cp; // Current pointer into m_bufp buffer
uint8_t* m_bufp; // Output buffer
uint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
bool m_isOpen = false; // True indicates open file/stream
std::string m_filename; // Filename, for error messages
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
@ -131,7 +131,7 @@ protected:
public:
/// Construct
VerilatedDeserialize() {
m_bufp = new vluint8_t[bufferSize()];
m_bufp = new uint8_t[bufferSize()];
m_cp = m_bufp;
}
/// Destruct
@ -150,12 +150,12 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Read data from stream
VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
vluint8_t* __restrict dp = static_cast<vluint8_t* __restrict>(datap);
uint8_t* __restrict dp = static_cast<uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
if (blk > bufferInsertSize()) blk = bufferInsertSize();
const vluint8_t* __restrict maxp = dp + blk;
const uint8_t* __restrict maxp = dp + blk;
for (; dp < maxp; *dp++ = *m_cp++) {}
size -= blk;
}
@ -165,7 +165,7 @@ public:
// Internal use:
// Read a datum and compare with expected value
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE {
VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE {
return readAssert(&data, sizeof(data));
}
@ -236,28 +236,28 @@ public:
//=============================================================================
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
@ -279,12 +279,12 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
const vluint32_t len = rhs.length();
const uint32_t len = rhs.length();
os << len;
return os.write(rhs.data(), len);
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
vluint32_t len = 0;
uint32_t len = 0;
os >> len;
rhs.resize(len);
return os.read((void*)rhs.data(), len);
@ -295,7 +295,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
template <class T_Key, class T_Value>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault();
const vluint32_t len = rhs.size();
const uint32_t len = rhs.size();
os << len;
for (const auto& i : rhs) {
const T_Key index = i.first; // Copy to get around const_iterator
@ -307,10 +307,10 @@ VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Val
template <class T_Key, class T_Value>
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os >> rhs.atDefault();
vluint32_t len = 0;
uint32_t len = 0;
os >> len;
rhs.clear();
for (vluint32_t i = 0; i < len; ++i) {
for (uint32_t i = 0; i < len; ++i) {
T_Key index;
T_Value value;
os >> index;

View File

@ -39,10 +39,10 @@
// This class is thread safe (though most of SystemC is not).
class VlScBvExposer final : public sc_bv_base {
public:
static const vluint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
static const uint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
return static_cast<const VlScBvExposer*>(&base)->sp_datatp();
}
const vluint32_t* sp_datatp() const { return reinterpret_cast<vluint32_t*>(m_data); }
const uint32_t* sp_datatp() const { return reinterpret_cast<uint32_t*>(m_data); }
// Above reads this protected element in sc_bv_base:
// sc_digit* m_data; // data array
};

View File

@ -69,9 +69,9 @@ public:
class VerilatedVarProps VL_NOT_FINAL {
// TYPES
static constexpr vluint32_t MAGIC = 0xddc4f829UL;
static constexpr uint32_t MAGIC = 0xddc4f829UL;
// MEMBERS
const vluint32_t m_magic; // Magic number
const uint32_t m_magic; // Magic number
const VerilatedVarType m_vltype; // Data type
const VerilatedVarFlags m_vlflags; // Direction
const int m_pdims; // Packed dimensions, 0 = none
@ -142,7 +142,7 @@ public:
VerilatedVarFlags vldir() const {
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
}
vluint32_t entSize() const;
uint32_t entSize() const;
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
// DPI compatible C standard layout
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }

View File

@ -24,8 +24,11 @@
#include "verilatedos.h"
#include "verilated_threads.h"
#ifdef VL_PROFILER
#include "verilated_profiler.h"
#endif
#include <cstdio>
#include <fstream>
#include <memory>
#include <string>
@ -34,14 +37,12 @@
// Internal note: Globals may multi-construct, see verilated.cpp top.
std::atomic<vluint64_t> VlMTaskVertex::s_yields;
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = nullptr;
std::atomic<uint64_t> VlMTaskVertex::s_yields;
//=============================================================================
// VlMTaskVertex
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
: m_upstreamDepsDone{0}
, m_upstreamDepCount{upstreamDepCount} {
assert(atomic_is_lock_free(&m_upstreamDepsDone));
@ -50,12 +51,11 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp)
: m_ready_size{0}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this}
, m_cthread{startWorker, this, threadId, profilerp}
, m_contextp{contextp} {}
VlWorkerThread::~VlWorkerThread() {
@ -66,8 +66,6 @@ VlWorkerThread::~VlWorkerThread() {
}
void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_profiling)) m_poolp->setupProfilingClientThread();
ExecRec work;
work.m_fnp = nullptr;
@ -82,143 +80,42 @@ void VlWorkerThread::workerLoop() {
work.m_fnp = nullptr;
}
}
if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread();
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp) {
Verilated::threadContextp(workerp->m_contextp);
#ifdef VL_PROFILER
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
if (profilerp) profilerp->setupThread(threadId);
#endif
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
VlExecutionProfiler* profiler) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads + 1) {
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads + 1);
cpus, nThreads);
}
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
// donated to run mtasks during the eval.
if (VL_UNLIKELY(m_profiling)) setupProfilingClientThread();
}
VlThreadPool::~VlThreadPool() {
// Each ~WorkerThread will wait for its thread to exit.
for (auto& i : m_workers) delete i;
if (VL_UNLIKELY(m_profiling)) tearDownProfilingClientThread();
}
void VlThreadPool::tearDownProfilingClientThread() {
assert(t_profilep);
delete t_profilep;
t_profilep = nullptr;
}
void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
assert(!t_profilep);
t_profilep = new ProfileTrace;
// Reserve some space in the thread-local profiling buffer;
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
const VerilatedLockGuard lock{m_mutex};
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec.
profilep->emplace_back(rec);
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
fprintf(fp, "VLPROF arg --threads %" PRIu64 "\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" PRIu64 "\n",
Verilated::threadContextp()->profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
vluint32_t thread_id = 0;
for (const auto& pi : m_allProfiles) {
++thread_id;
bool printing = false; // False while in warmup phase
for (const auto& ei : *pi) {
switch (ei.m_type) {
case VlProfileRec::TYPE_BARRIER: //
printing = true;
break;
case VlProfileRec::TYPE_EVAL:
if (!printing) break;
fprintf(fp,
"VLPROF eval start %" PRIu64 " elapsed %" PRIu64 " cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_EVAL_LOOP:
if (!printing) break;
fprintf(fp,
"VLPROF eval_loop start %" PRIu64 " elapsed %" PRIu64
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_MTASK_RUN:
if (!printing) break;
fprintf(fp,
"VLPROF mtask %d"
" start %" PRIu64 " elapsed %" PRIu64
" predict_start %u predict_cost %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
break;
default: assert(false); break; // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - tickStart);
std::fclose(fp);
}

View File

@ -35,8 +35,10 @@
#error "verilated_threads.h/cpp expected VL_THREADED (from verilator --threads)"
#endif
#include <atomic>
#include <condition_variable>
#include <set>
#include <thread>
#include <vector>
// clang-format off
@ -58,7 +60,7 @@ using VlExecFnp = void (*)(VlSelfP, bool);
// Track dependencies for a single MTask.
class VlMTaskVertex final {
// MEMBERS
static std::atomic<vluint64_t> s_yields; // Statistics
static std::atomic<uint64_t> s_yields; // Statistics
// On even cycles, _upstreamDepsDone increases as upstream
// dependencies complete. When it reaches _upstreamDepCount,
@ -76,8 +78,8 @@ class VlMTaskVertex final {
// during done-notification. Nobody's quantified that cost though.
// If we were really serious about shrinking this class, we could
// use 16-bit types here...)
std::atomic<vluint32_t> m_upstreamDepsDone;
const vluint32_t m_upstreamDepCount;
std::atomic<uint32_t> m_upstreamDepsDone;
const uint32_t m_upstreamDepCount;
public:
// CONSTRUCTORS
@ -85,10 +87,10 @@ public:
// 'upstreamDepCount' is the number of upstream MTaskVertex's
// that must notify this MTaskVertex before it will become ready
// to run.
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
explicit VlMTaskVertex(uint32_t upstreamDepCount);
~VlMTaskVertex() = default;
static vluint64_t yields() { return s_yields; }
static uint64_t yields() { return s_yields; }
static void yieldThread() {
++s_yields; // Statistics
std::this_thread::yield();
@ -99,19 +101,19 @@ public:
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
const vluint32_t upstreamDepsDone
const uint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
const vluint32_t upstreamDepsDone_prev
const uint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
@ -127,64 +129,7 @@ public:
}
};
// Profiling support
class VlProfileRec final {
protected:
friend class VlThreadPool;
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
// Layout below allows efficient packing.
// Leave endTime first, so no math needed to calculate address in endRecord
vluint64_t m_endTime = 0; // Tick at end of execution
vluint64_t m_startTime = 0; // Tick at start of execution
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
VlProfileE m_type = TYPE_BARRIER; // Record type
unsigned m_cpu; // Execution CPU number (at start anyways)
public:
class Barrier {};
VlProfileRec() = default;
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
void startEval(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL;
m_startTime = time;
m_cpu = getcpu();
}
void startEvalLoop(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL_LOOP;
m_startTime = time;
m_cpu = getcpu();
}
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
vluint32_t predictCost) {
m_type = VlProfileRec::TYPE_MTASK_RUN;
m_mtaskId = mtask;
m_predictStart = predictStart;
m_predictCost = predictCost;
m_startTime = time;
m_cpu = getcpu();
}
void endRecord(vluint64_t time) { m_endTime = time; }
static int getcpu() { // Return current executing CPU
#if defined(__linux)
return sched_getcpu();
#elif defined(__APPLE__) && !defined(__arm64__)
vluint32_t info[4];
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
// info[1] is EBX, bits 24-31 are APIC ID
if ((info[3] & (1 << 9)) == 0) {
return -1; // no APIC on chip
} else {
return (unsigned)info[1] >> 24;
}
#elif defined(_WIN32)
return GetCurrentProcessorNumber();
#else
return 0;
#endif
}
};
class VlExecutionProfiler;
class VlThreadPool;
class VlWorkerThread final {
@ -217,9 +162,6 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
VlThreadPool* const m_poolp; // Our associated thread pool
const bool m_profiling; // Is profiling enabled?
std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* const m_contextp; // Context for spawned thread
@ -228,7 +170,8 @@ private:
public:
// CONSTRUCTORS
explicit VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling);
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp);
~VlWorkerThread();
// METHODS
@ -265,34 +208,20 @@ public:
if (notify) m_cv.notify_one();
}
void workerLoop();
static void startWorker(VlWorkerThread* workerp);
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp);
};
class VlThreadPool final {
// TYPES
using ProfileTrace = std::vector<VlProfileRec>;
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
const bool m_profiling; // is profiling enabled?
// Support profiling -- we can append records of profiling events
// to this vector with very low overhead, and then dump them out
// later. This prevents the overhead of printf/malloc/IO from
// corrupting the profiling data. It's super cheap to append
// a VlProfileRec struct on the end of a pre-allocated vector;
// this is the only cost we pay in real-time during a profiling cycle.
// Internal note: Globals may multi-construct, see verilated.cpp top.
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
std::set<ProfileTrace*> m_allProfiles VL_GUARDED_BY(m_mutex);
VerilatedMutex m_mutex;
public:
// CONSTRUCTORS
// Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...)
VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling);
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
~VlThreadPool();
// METHODS
@ -302,17 +231,6 @@ public:
assert(index < m_workers.size());
return m_workers[index];
}
inline VlProfileRec* profileAppend() {
t_profilep->emplace_back();
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex);
// In profiling mode, each executing thread must call
// this once to setup profiling state:
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
void tearDownProfilingClientThread();
private:
VL_UNCOPYABLE(VlThreadPool);

View File

@ -92,7 +92,7 @@ class VerilatedTraceCommand final {
public:
// These must all fit in 4 bit at the moment, as the tracing routines
// pack parameters in the top bits.
enum : vluint8_t {
enum : uint8_t {
CHG_BIT_0 = 0x0,
CHG_BIT_1 = 0x1,
CHG_CDATA = 0x2,
@ -120,7 +120,7 @@ public:
//=========================================================================
// Generic tracing internals
using initCb_t = void (*)(void*, T_Derived*, vluint32_t); // Type of init callbacks
using initCb_t = void (*)(void*, T_Derived*, uint32_t); // Type of init callbacks
using dumpCb_t = void (*)(void*, T_Derived*); // Type of all but init callbacks
private:
@ -142,18 +142,18 @@ private:
, m_userp{userp} {}
};
vluint32_t* m_sigs_oldvalp; // Old value store
uint32_t* m_sigs_oldvalp; // Old value store
EData* m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
vluint64_t m_timeLastDump; // Last time we did a dump
uint64_t m_timeLastDump; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
bool m_fullDump; // Whether a full dump is required on the next call to 'dump'
vluint32_t m_nextCode; // Next code number to assign
vluint32_t m_numSignals; // Number of distinct signals
vluint32_t m_maxBits; // Number of bits in the widest signal
uint32_t m_nextCode; // Next code number to assign
uint32_t m_numSignals; // Number of distinct signals
uint32_t m_maxBits; // Number of bits in the widest signal
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
char m_scopeEscape;
@ -174,28 +174,28 @@ private:
#ifdef VL_TRACE_THREADED
// Number of total trace buffers that have been allocated
vluint32_t m_numTraceBuffers;
uint32_t m_numTraceBuffers;
// Size of trace buffers
size_t m_traceBufferSize;
// Buffers handed to worker for processing
VerilatedThreadQueue<vluint32_t*> m_buffersToWorker;
VerilatedThreadQueue<uint32_t*> m_buffersToWorker;
// Buffers returned from worker after processing
VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker;
VerilatedThreadQueue<uint32_t*> m_buffersFromWorker;
// Write pointer into current buffer
vluint32_t* m_traceBufferWritep;
uint32_t* m_traceBufferWritep;
// End of trace buffer
vluint32_t* m_traceBufferEndp;
uint32_t* m_traceBufferEndp;
// The worker thread itself
std::unique_ptr<std::thread> m_workerThread;
// Get a new trace buffer that can be populated. May block if none available
vluint32_t* getTraceBuffer();
uint32_t* getTraceBuffer();
// The function executed by the worker thread
void workerThreadMain();
// Wait until given buffer is placed in m_buffersFromWorker
void waitForBuffer(const vluint32_t* bufferp);
void waitForBuffer(const uint32_t* bufferp);
// Shut down and join worker, if it's running, otherwise do nothing
void shutdownWorker();
@ -210,11 +210,11 @@ protected:
VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
vluint32_t nextCode() const { return m_nextCode; }
vluint32_t numSignals() const { return m_numSignals; }
vluint32_t maxBits() const { return m_maxBits; }
uint32_t nextCode() const { return m_nextCode; }
uint32_t numSignals() const { return m_numSignals; }
uint32_t maxBits() const { return m_maxBits; }
void fullDump(bool value) { m_fullDump = value; }
vluint64_t timeLastDump() { return m_timeLastDump; }
uint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@ -223,7 +223,7 @@ protected:
void traceInit() VL_MT_UNSAFE;
// Declare new signal and return true if enabled
bool declCode(vluint32_t code, const char* namep, vluint32_t bits, bool tri);
bool declCode(uint32_t code, const char* namep, uint32_t bits, bool tri);
// Is this an escape?
bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; }
@ -239,7 +239,7 @@ protected:
// Virtual functions to be provided by the format specific implementation
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(vluint64_t timeui) = 0;
virtual void emitTimeChange(uint64_t timeui) = 0;
// These hooks are called before a full or change based dump is produced.
// The return value indicates whether to proceed with the dump.
@ -264,7 +264,7 @@ public:
void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
// Call
void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
//=========================================================================
// Non-hot path internal interface to Verilator generated code
@ -288,69 +288,69 @@ public:
// these here, but we cannot afford dynamic dispatch for calling these as
// this is very hot code during tracing.
// duck-typed void emitBit(vluint32_t code, CData newval) = 0;
// duck-typed void emitCData(vluint32_t code, CData newval, int bits) = 0;
// duck-typed void emitSData(vluint32_t code, SData newval, int bits) = 0;
// duck-typed void emitIData(vluint32_t code, IData newval, int bits) = 0;
// duck-typed void emitQData(vluint32_t code, QData newval, int bits) = 0;
// duck-typed void emitWData(vluint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(vluint32_t code, double newval) = 0;
// duck-typed void emitBit(uint32_t code, CData newval) = 0;
// duck-typed void emitCData(uint32_t code, CData newval, int bits) = 0;
// duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0;
// duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0;
// duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0;
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
// Write to previous value buffer value and emit trace entry.
void fullBit(vluint32_t* oldp, CData newval);
void fullCData(vluint32_t* oldp, CData newval, int bits);
void fullSData(vluint32_t* oldp, SData newval, int bits);
void fullIData(vluint32_t* oldp, IData newval, int bits);
void fullQData(vluint32_t* oldp, QData newval, int bits);
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(vluint32_t* oldp, double newval);
void fullBit(uint32_t* oldp, CData newval);
void fullCData(uint32_t* oldp, CData newval, int bits);
void fullSData(uint32_t* oldp, SData newval, int bits);
void fullIData(uint32_t* oldp, IData newval, int bits);
void fullQData(uint32_t* oldp, QData newval, int bits);
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(uint32_t* oldp, double newval);
#ifdef VL_TRACE_THREADED
// Threaded tracing. Just dump everything in the trace buffer
inline void chgBit(vluint32_t code, CData newval) {
inline void chgBit(uint32_t code, CData newval) {
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval;
m_traceBufferWritep[1] = code;
m_traceBufferWritep += 2;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgCData(vluint32_t code, CData newval, int bits) {
inline void chgCData(uint32_t code, CData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgSData(vluint32_t code, SData newval, int bits) {
inline void chgSData(uint32_t code, SData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgIData(vluint32_t code, IData newval, int bits) {
inline void chgIData(uint32_t code, IData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgQData(vluint32_t code, QData newval, int bits) {
inline void chgQData(uint32_t code, QData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA;
m_traceBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval;
m_traceBufferWritep += 4;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgWData(vluint32_t code, const WData* newvalp, int bits) {
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; }
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgDouble(vluint32_t code, double newval) {
inline void chgDouble(uint32_t code, double newval) {
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
m_traceBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
@ -369,27 +369,27 @@ public:
// thread and are called chg*Impl
// Check previous dumped value of signal. If changed, then emit trace entry
inline void CHG(Bit)(vluint32_t* oldp, CData newval) {
const vluint32_t diff = *oldp ^ newval;
inline void CHG(Bit)(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
inline void CHG(CData)(vluint32_t* oldp, CData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
inline void CHG(CData)(uint32_t* oldp, CData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
}
inline void CHG(SData)(vluint32_t* oldp, SData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
inline void CHG(SData)(uint32_t* oldp, SData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
}
inline void CHG(IData)(vluint32_t* oldp, IData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
inline void CHG(IData)(uint32_t* oldp, IData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
inline void CHG(QData)(vluint32_t* oldp, QData newval, int bits) {
const vluint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
inline void CHG(QData)(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
inline void CHG(WData)(vluint32_t* oldp, const WData* newvalp, int bits) {
inline void CHG(WData)(uint32_t* oldp, const WData* newvalp, int bits) {
for (int i = 0; i < (bits + 31) / 32; ++i) {
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
fullWData(oldp, newvalp, bits);
@ -397,7 +397,7 @@ public:
}
}
}
inline void CHG(Double)(vluint32_t* oldp, double newval) {
inline void CHG(Double)(uint32_t* oldp, double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
}

View File

@ -82,8 +82,8 @@ static std::string doubleToTimescale(double value) {
//=========================================================================
// Buffer management
template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
vluint32_t* bufferp;
template <> uint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
uint32_t* bufferp;
// Some jitter is expected, so some number of alternative trace buffers are
// required, but don't allocate more than 8 buffers.
if (m_numTraceBuffers < 8) {
@ -92,7 +92,7 @@ template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
++m_numTraceBuffers;
// Note: over allocate a bit so pointer comparison is well defined
// if we overflow only by a small amount
bufferp = new vluint32_t[m_traceBufferSize + 16];
bufferp = new uint32_t[m_traceBufferSize + 16];
}
} else {
// Block until a buffer becomes available
@ -101,10 +101,10 @@ template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
return bufferp;
}
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const vluint32_t* buffp) {
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const uint32_t* buffp) {
// Slow path code only called on flush/shutdown, so use a simple algorithm.
// Collect buffers from worker and stash them until we get the one we want.
std::deque<vluint32_t*> stash;
std::deque<uint32_t*> stash;
do { stash.push_back(m_buffersFromWorker.get()); } while (stash.back() != buffp);
// Now put them back in the queue, in the original order.
while (!stash.empty()) {
@ -120,18 +120,18 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
bool shutdown = false;
do {
vluint32_t* const bufferp = m_buffersToWorker.get();
uint32_t* const bufferp = m_buffersToWorker.get();
VL_TRACE_THREAD_DEBUG("");
VL_TRACE_THREAD_DEBUG("Got buffer: " << bufferp);
const vluint32_t* readp = bufferp;
const uint32_t* readp = bufferp;
while (true) {
const vluint32_t cmd = readp[0];
const vluint32_t top = cmd >> 4;
const uint32_t cmd = readp[0];
const uint32_t top = cmd >> 4;
// Always set this up, as it is almost always needed
vluint32_t* const oldp = m_sigs_oldvalp + readp[1];
uint32_t* const oldp = m_sigs_oldvalp + readp[1];
// Note this increment needs to be undone on commands which do not
// actually contain a code, but those are the rare cases.
readp += 2;
@ -187,7 +187,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
case VerilatedTraceCommand::TIME_CHANGE:
VL_TRACE_THREAD_DEBUG("Command TIME_CHANGE " << top);
readp -= 1; // No code in this command, undo increment
emitTimeChange(*reinterpret_cast<const vluint64_t*>(readp));
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
readp += 2;
continue;
@ -226,7 +226,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
if (!m_workerThread) return;
// Hand an buffer with a shutdown command to the worker thread
vluint32_t* const bufferp = getTraceBuffer();
uint32_t* const bufferp = getTraceBuffer();
bufferp[0] = VerilatedTraceCommand::SHUTDOWN;
m_buffersToWorker.put(bufferp);
// Wait for it to return
@ -254,7 +254,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
#ifdef VL_TRACE_THREADED
// Hand an empty buffer to the worker thread
vluint32_t* const bufferp = getTraceBuffer();
uint32_t* const bufferp = getTraceBuffer();
*bufferp = VerilatedTraceCommand::END;
m_buffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
@ -318,7 +318,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
const vluint32_t expectedCodes = nextCode();
const uint32_t expectedCodes = nextCode();
m_nextCode = 1;
m_numSignals = 0;
m_maxBits = 0;
@ -327,7 +327,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// Call all initialize callbacks, which will:
// - Call decl* for each signal (these eventually call ::declCode)
// - Store the base code
for (vluint32_t i = 0; i < m_initCbs.size(); ++i) {
for (uint32_t i = 0; i < m_initCbs.size(); ++i) {
const CallbackRecord& cbr = m_initCbs[i];
cbr.m_initCb(cbr.m_userp, self(), nextCode());
}
@ -339,7 +339,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// Now that we know the number of codes, allocate space for the buffer
// holding previous signal values.
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
if (!m_sigs_oldvalp) m_sigs_oldvalp = new uint32_t[nextCode()];
// Apply enables
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
@ -348,7 +348,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// But it isn't, so alloc one bit for each code to indicate enablement
// We don't want to still use m_signs_enabledVec as std::vector<bool> is not
// guarenteed to be fast
m_sigs_enabledp = new vluint32_t[1 + VL_WORDS_I(nextCode())]{0};
m_sigs_enabledp = new uint32_t[1 + VL_WORDS_I(nextCode())]{0};
m_sigs_enabledVec.reserve(nextCode());
for (size_t code = 0; code < nextCode(); ++code) {
if (m_sigs_enabledVec[code]) {
@ -376,7 +376,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
}
template <>
bool VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, const char* namep, vluint32_t bits,
bool VerilatedTrace<VL_DERIVED_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
bool tri) {
if (VL_UNCOVERABLE(!code)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
@ -455,8 +455,7 @@ void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier)
}
}
template <>
void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
template <> void VerilatedTrace<VL_DERIVED_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
@ -480,7 +479,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
#ifdef VL_TRACE_THREADED
// Currently only incremental dumps run on the worker thread
vluint32_t* bufferp = nullptr;
uint32_t* bufferp = nullptr;
if (VL_LIKELY(!m_fullDump)) {
// Get the trace buffer we are about to fill
bufferp = getTraceBuffer();
@ -489,7 +488,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Tell worker to update time point
m_traceBufferWritep[0] = VerilatedTraceCommand::TIME_CHANGE;
*reinterpret_cast<vluint64_t*>(m_traceBufferWritep + 1) = timeui;
*reinterpret_cast<uint64_t*>(m_traceBufferWritep + 1) = timeui;
m_traceBufferWritep += 3;
} else {
// Update time point
@ -504,18 +503,18 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Run the callbacks
if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full
for (vluint32_t i = 0; i < m_fullCbs.size(); ++i) {
for (uint32_t i = 0; i < m_fullCbs.size(); ++i) {
const CallbackRecord& cbr = m_fullCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
}
} else {
for (vluint32_t i = 0; i < m_chgCbs.size(); ++i) {
for (uint32_t i = 0; i < m_chgCbs.size(); ++i) {
const CallbackRecord& cbr = m_chgCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
}
}
for (vluint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
const CallbackRecord& cbr = m_cleanupCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
}
@ -584,39 +583,35 @@ template <> void VerilatedTrace<VL_DERIVED_T>::popNamePrefix(unsigned count) {
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, CData newval) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(uint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullCData(vluint32_t* oldp, CData newval, int bits) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullSData(vluint32_t* oldp, SData newval, int bits) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullIData(vluint32_t* oldp, IData newval, int bits) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int bits) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
@ -624,14 +619,14 @@ void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
void VerilatedTrace<VL_DERIVED_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
}
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;

View File

@ -73,7 +73,7 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//===================================================================
// Shuffle RNG
extern vluint64_t vl_rand64() VL_MT_SAFE;
extern uint64_t vl_rand64() VL_MT_SAFE;
class VlURNG final {
public:
@ -241,7 +241,7 @@ public:
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
void erase(int32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
@ -288,7 +288,7 @@ public:
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
T_Value& at(int32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
@ -299,7 +299,7 @@ public:
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
const T_Value& at(int32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
@ -309,18 +309,18 @@ public:
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
void insert(int32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue slice(int32_t lsb, int32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
@ -410,16 +410,16 @@ public:
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};

View File

@ -172,7 +172,7 @@ bool VerilatedVcd::preChangeDump() {
return isOpen();
}
void VerilatedVcd::emitTimeChange(vluint64_t timeui) {
void VerilatedVcd::emitTimeChange(uint64_t timeui) {
printStr("#");
printQuad(timeui);
printStr("\n");
@ -270,14 +270,14 @@ void VerilatedVcd::printStr(const char* str) {
}
}
void VerilatedVcd::printQuad(vluint64_t n) {
void VerilatedVcd::printQuad(uint64_t n) {
constexpr size_t LEN_STR_QUAD = 40;
char buf[LEN_STR_QUAD];
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" PRIu64, n);
printStr(buf);
}
void VerilatedVcd::bufferResize(vluint64_t minsize) {
void VerilatedVcd::bufferResize(uint64_t minsize) {
// minsize is size of largest write. We buffer at least 8 times as much data,
// writing when we are 3/4 full (with thus 2*minsize remaining free)
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
@ -328,7 +328,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
//=============================================================================
// VCD string code
char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
char* VerilatedVcd::writeCode(char* writep, uint32_t code) {
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
while (code) {
@ -459,7 +459,7 @@ void VerilatedVcd::dumpHeader() {
deleteNameMap();
}
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, bool array,
void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
int arraynum, bool tri, bool bussed, int msb, int lsb) {
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
@ -544,38 +544,38 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
m_namemapp->emplace(hiername, decl);
}
void VerilatedVcd::declBit(vluint32_t code, const char* name, bool array, int arraynum) {
void VerilatedVcd::declBit(uint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "wire", array, arraynum, false, false, 0, 0);
}
void VerilatedVcd::declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declDouble(vluint32_t code, const char* name, bool array, int arraynum) {
void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "real", array, arraynum, false, false, 63, 0);
}
#ifdef VL_TRACE_VCD_OLD_API
void VerilatedVcd::declTriBit(vluint32_t code, const char* name, bool array, int arraynum) {
void VerilatedVcd::declTriBit(uint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "wire", array, arraynum, true, false, 0, 0);
}
void VerilatedVcd::declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declTriBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
void VerilatedVcd::declTriQuad(vluint32_t code, const char* name, bool array, int arraynum,
int msb, int lsb) {
void VerilatedVcd::declTriQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, int arraynum,
int msb, int lsb) {
void VerilatedVcd::declTriArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
#endif // VL_TRACE_VCD_OLD_API
@ -593,7 +593,7 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
#ifdef VL_X86_64
// Copy the whole 8 bytes in one go, this works on little-endian machines
// supporting unaligned stores.
*reinterpret_cast<vluint64_t*>(writep) = *reinterpret_cast<const vluint64_t*>(suffixp);
*reinterpret_cast<uint64_t*>(writep) = *reinterpret_cast<const uint64_t*>(suffixp);
#else
// Portable variant
writep[0] = suffixp[0];
@ -606,7 +606,7 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
#endif
}
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
void VerilatedVcd::finishLine(uint32_t code, char* writep) {
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
VL_DEBUG_IFDEF(assert(suffixp[0]););
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
@ -625,7 +625,7 @@ void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
// so always inline them.
VL_ATTR_ALWINLINE
void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
void VerilatedVcd::emitBit(uint32_t code, CData newval) {
// Don't prefetch suffix as it's a bit too late;
char* wp = m_writep;
*wp++ = '0' | static_cast<char>(newval);
@ -633,7 +633,7 @@ void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
void VerilatedVcd::emitCData(uint32_t code, CData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
@ -641,7 +641,7 @@ void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
void VerilatedVcd::emitSData(uint32_t code, SData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
@ -649,7 +649,7 @@ void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
void VerilatedVcd::emitIData(uint32_t code, IData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
@ -657,7 +657,7 @@ void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
void VerilatedVcd::emitQData(uint32_t code, QData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
@ -665,7 +665,7 @@ void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
void VerilatedVcd::emitWData(uint32_t code, const WData* newvalp, int bits) {
int words = VL_WORDS_I(bits);
char* wp = m_writep;
*wp++ = 'b';
@ -682,7 +682,7 @@ void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitDouble(vluint32_t code, double newval) {
void VerilatedVcd::emitDouble(uint32_t code, double newval) {
char* wp = m_writep;
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval);
@ -692,7 +692,7 @@ void VerilatedVcd::emitDouble(vluint32_t code, double newval) {
#ifdef VL_TRACE_VCD_OLD_API
void VerilatedVcd::fullBit(vluint32_t code, const vluint32_t newval) {
void VerilatedVcd::fullBit(uint32_t code, const uint32_t newval) {
// Note the &1, so we don't require clean input -- makes more common no change case faster
*oldp(code) = newval;
*m_writep++ = ('0' + static_cast<char>(newval & 1));
@ -700,7 +700,7 @@ void VerilatedVcd::fullBit(vluint32_t code, const vluint32_t newval) {
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
void VerilatedVcd::fullBus(uint32_t code, const uint32_t newval, int bits) {
*oldp(code) = newval;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
@ -711,8 +711,8 @@ void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
void VerilatedVcd::fullQuad(uint32_t code, const uint64_t newval, int bits) {
(*(reinterpret_cast<uint64_t*>(oldp(code)))) = newval;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = ((newval & (1ULL << bit)) ? '1' : '0');
@ -722,7 +722,7 @@ void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits)
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
void VerilatedVcd::fullArray(uint32_t code, const uint32_t* newval, int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) { oldp(code)[word] = newval[word]; }
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
@ -733,7 +733,7 @@ void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits) {
void VerilatedVcd::fullArray(uint32_t code, const uint64_t* newval, int bits) {
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) { oldp(code)[word] = newval[word]; }
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
@ -744,7 +744,7 @@ void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
void VerilatedVcd::fullTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri) {
oldp(code)[0] = newval;
oldp(code)[1] = newtri;
*m_writep++ = "01zz"[newval | (newtri << 1)];
@ -752,7 +752,7 @@ void VerilatedVcd::fullTriBit(vluint32_t code, const vluint32_t newval, const vl
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
void VerilatedVcd::fullTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri,
int bits) {
oldp(code)[0] = newval;
oldp(code)[1] = newtri;
@ -765,10 +765,10 @@ void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vl
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
void VerilatedVcd::fullTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri,
int bits) {
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
(*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) = newtri;
(*(reinterpret_cast<uint64_t*>(oldp(code)))) = newval;
(*(reinterpret_cast<uint64_t*>(oldp(code + 1)))) = newtri;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = "01zz"[((newval >> bit) & 1ULL) | (((newtri >> bit) & 1ULL) << 1ULL)];
@ -778,16 +778,16 @@ void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const v
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
const vluint32_t* newtrip, int bits) {
void VerilatedVcd::fullTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip,
int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
oldp(code)[word * 2] = newvalp[word];
oldp(code)[word * 2 + 1] = newtrip[word];
}
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
vluint32_t valbit = (newvalp[(bit / 32)] >> (bit & 0x1f)) & 1;
vluint32_t tribit = (newtrip[(bit / 32)] >> (bit & 0x1f)) & 1;
uint32_t valbit = (newvalp[(bit / 32)] >> (bit & 0x1f)) & 1;
uint32_t tribit = (newtrip[(bit / 32)] >> (bit & 0x1f)) & 1;
*m_writep++ = "01zz"[valbit | (tribit << 1)];
}
*m_writep++ = ' ';
@ -795,7 +795,7 @@ void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
void VerilatedVcd::fullDouble(uint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
(*(reinterpret_cast<double*>(oldp(code)))) = newval;
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
@ -818,18 +818,18 @@ void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
extern void verilated_trace_imp_selftest();
vluint32_t v1, v2, s1, s2[3];
vluint32_t tri96[3];
vluint32_t tri96__tri[3];
vluint64_t quad96[2];
vluint64_t tquad;
vluint64_t tquad__tri;
vluint8_t ch;
vluint64_t timestamp = 1;
uint32_t v1, v2, s1, s2[3];
uint32_t tri96[3];
uint32_t tri96__tri[3];
uint64_t quad96[2];
uint64_t tquad;
uint64_t tquad__tri;
uint8_t ch;
uint64_t timestamp = 1;
double doub = 0.0;
float flo = 0.0f;
void vcdInit(void*, VerilatedVcd* vcdp, vluint32_t) {
void vcdInit(void*, VerilatedVcd* vcdp, uint32_t) {
vcdp->scopeEscape('.');
vcdp->pushNamePrefix("top.");
/**/ vcdp->declBus(0x2, "v1", -1, 0, 5, 1);
@ -933,8 +933,8 @@ void vcdTestMain(const char* filenamep) {
vcdp->dump(++timestamp);
vcdp->dump(++timestamp);
# ifdef VERILATED_VCD_TEST_64BIT
const vluint64_t bytesPerDump = 15ULL;
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
const uint64_t bytesPerDump = 15ULL;
for (uint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
v1 = i;
vcdp->dump(++timestamp);
}

View File

@ -70,21 +70,21 @@ private:
bool m_isOpen = false; // True indicates open file
bool m_evcd = false; // True for evcd format
std::string m_filename; // Filename we're writing to (if open)
vluint64_t m_rolloverMB = 0; // MB of file size to rollover at
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
const char* m_wrFlushp; // Output buffer flush trigger location
char* m_writep; // Write pointer into output buffer
vluint64_t m_wrChunkSize; // Output buffer size
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
uint64_t m_wrChunkSize; // Output buffer size
uint64_t m_wroteBytes = 0; // Number of bytes written to this file
std::vector<char> m_suffixes; // VCD line end string codes + metadata
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
void bufferResize(vluint64_t minsize);
void bufferResize(uint64_t minsize);
void bufferFlush() VL_MT_UNSAFE_ONE;
inline void bufferCheck() {
// Flush the write buffer if there's not enough space left for new information
@ -98,16 +98,16 @@ private:
void deleteNameMap();
void printIndent(int level_change);
void printStr(const char* str);
void printQuad(vluint64_t n);
void printTime(vluint64_t timeui);
void declare(vluint32_t code, const char* name, const char* wirep, bool array, int arraynum,
void printQuad(uint64_t n);
void printTime(uint64_t timeui);
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
bool tri, bool bussed, int msb, int lsb);
void dumpHeader();
static char* writeCode(char* writep, vluint32_t code);
static char* writeCode(char* writep, uint32_t code);
void finishLine(vluint32_t code, char* writep);
void finishLine(uint32_t code, char* writep);
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedVcd);
@ -117,7 +117,7 @@ protected:
// Implementation of VerilatedTrace interface
// Implementations of protected virtual methods for VerilatedTrace
virtual void emitTimeChange(vluint64_t timeui) override;
virtual void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
@ -125,13 +125,13 @@ protected:
// Implementations of duck-typed methods for VerilatedTrace. These are
// called from only one place (namely full*) so always inline them.
inline void emitBit(vluint32_t code, CData newval);
inline void emitCData(vluint32_t code, CData newval, int bits);
inline void emitSData(vluint32_t code, SData newval, int bits);
inline void emitIData(vluint32_t code, IData newval, int bits);
inline void emitQData(vluint32_t code, QData newval, int bits);
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
inline void emitDouble(vluint32_t code, double newval);
inline void emitBit(uint32_t code, CData newval);
inline void emitCData(uint32_t code, CData newval, int bits);
inline void emitSData(uint32_t code, SData newval, int bits);
inline void emitIData(uint32_t code, IData newval, int bits);
inline void emitQData(uint32_t code, QData newval, int bits);
inline void emitWData(uint32_t code, const WData* newvalp, int bits);
inline void emitDouble(uint32_t code, double newval);
public:
//=========================================================================
@ -142,7 +142,7 @@ public:
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// METHODS
// Open the file; call isOpen() to see if errors
@ -159,11 +159,11 @@ public:
//=========================================================================
// Internal interface to Verilator generated code
void declBit(vluint32_t code, const char* name, bool array, int arraynum);
void declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declDouble(vluint32_t code, const char* name, bool array, int arraynum);
void declBit(uint32_t code, const char* name, bool array, int arraynum);
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
#ifdef VL_TRACE_VCD_OLD_API
//=========================================================================
@ -171,88 +171,85 @@ public:
// code and is not used by Verilator. Do not use these as there is no
// guarantee of functionality.
void declTriBit(vluint32_t code, const char* name, bool array, int arraynum);
void declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declTriQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb);
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb);
void declTriBit(uint32_t code, const char* name, bool array, int arraynum);
void declTriBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declTriQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declTriArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void fullBit(vluint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
void fullCData(vluint32_t* oldp, CData newval, int bits) {
void fullBit(uint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
void fullCData(uint32_t* oldp, CData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullSData(vluint32_t* oldp, SData newval, int bits) {
void fullSData(uint32_t* oldp, SData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullIData(vluint32_t* oldp, IData newval, int bits) {
void fullIData(uint32_t* oldp, IData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullQData(vluint32_t* oldp, QData newval, int bits) {
void fullQData(uint32_t* oldp, QData newval, int bits) {
fullQuad(oldp - this->oldp(0), newval, bits);
}
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
void fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
fullArray(oldp - this->oldp(0), newvalp, bits);
}
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
void fullDouble(uint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
inline void chgBit(vluint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
inline void chgCData(vluint32_t* oldp, CData newval, int bits) {
inline void chgBit(uint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
inline void chgCData(uint32_t* oldp, CData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgSData(vluint32_t* oldp, SData newval, int bits) {
inline void chgSData(uint32_t* oldp, SData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgIData(vluint32_t* oldp, IData newval, int bits) {
inline void chgIData(uint32_t* oldp, IData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgQData(vluint32_t* oldp, QData newval, int bits) {
inline void chgQData(uint32_t* oldp, QData newval, int bits) {
chgQuad(oldp - this->oldp(0), newval, bits);
}
inline void chgWData(vluint32_t* oldp, const WData* newvalp, int bits) {
inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
chgArray(oldp - this->oldp(0), newvalp, bits);
}
inline void chgDouble(vluint32_t* oldp, double newval) {
inline void chgDouble(uint32_t* oldp, double newval) {
chgDouble(oldp - this->oldp(0), newval);
}
// Inside dumping routines, dump one signal, faster when not inlined
// due to code size reduction.
void fullBit(vluint32_t code, const vluint32_t newval);
void fullBus(vluint32_t code, const vluint32_t newval, int bits);
void fullQuad(vluint32_t code, const vluint64_t newval, int bits);
void fullArray(vluint32_t code, const vluint32_t* newvalp, int bits);
void fullArray(vluint32_t code, const vluint64_t* newvalp, int bits);
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri, int bits);
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
int bits);
void fullDouble(vluint32_t code, const double newval);
void fullBit(uint32_t code, const uint32_t newval);
void fullBus(uint32_t code, const uint32_t newval, int bits);
void fullQuad(uint32_t code, const uint64_t newval, int bits);
void fullArray(uint32_t code, const uint32_t* newvalp, int bits);
void fullArray(uint32_t code, const uint64_t* newvalp, int bits);
void fullTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri);
void fullTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri, int bits);
void fullTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri, int bits);
void fullTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip, int bits);
void fullDouble(uint32_t code, const double newval);
// Inside dumping routines, dump one signal if it has changed.
// We do want to inline these to avoid calls when the value did not change.
inline void chgBit(vluint32_t code, const vluint32_t newval) {
const vluint32_t diff = oldp(code)[0] ^ newval;
inline void chgBit(uint32_t code, const uint32_t newval) {
const uint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) fullBit(code, newval);
}
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
const vluint32_t diff = oldp(code)[0] ^ newval;
inline void chgBus(uint32_t code, const uint32_t newval, int bits) {
const uint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullBus(code, newval, bits);
}
}
}
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
inline void chgQuad(uint32_t code, const uint64_t newval, int bits) {
const uint64_t diff = (*(reinterpret_cast<uint64_t*>(oldp(code)))) ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullQuad(code, newval, bits);
}
}
}
inline void chgArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
inline void chgArray(uint32_t code, const uint32_t* newvalp, int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
fullArray(code, newvalp, bits);
@ -260,17 +257,17 @@ public:
}
}
}
inline void chgArray(vluint32_t code, const vluint64_t* newvalp, int bits) {
inline void chgArray(uint32_t code, const uint64_t* newvalp, int bits) {
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
if (VL_UNLIKELY(*(reinterpret_cast<vluint64_t*>(oldp(code + 2 * word)))
if (VL_UNLIKELY(*(reinterpret_cast<uint64_t*>(oldp(code + 2 * word)))
^ newvalp[word])) {
fullArray(code, newvalp, bits);
return;
}
}
}
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
inline void chgTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri) {
const uint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
@ -279,26 +276,24 @@ public:
}
}
}
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
int bits) {
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
inline void chgTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri, int bits) {
const uint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullTriBus(code, newval, newtri, bits);
}
}
}
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
int bits) {
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
inline void chgTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri, int bits) {
const uint64_t diff = (((*(reinterpret_cast<uint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<uint64_t*>(oldp(code + 1)))) ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullTriQuad(code, newval, newtri, bits);
}
}
}
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
inline void chgTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip,
int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
@ -308,7 +303,7 @@ public:
}
}
}
inline void chgDouble(vluint32_t code, const double newval) {
inline void chgDouble(uint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
fullDouble(code, newval);
@ -323,7 +318,7 @@ public:
#ifndef DOXYGEN
// Declare specializations here they are used in VerilatedVcdC just below
template <> void VerilatedTrace<VerilatedVcd>::dump(vluint64_t timeui);
template <> void VerilatedTrace<VerilatedVcd>::dump(uint64_t timeui);
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
@ -371,12 +366,12 @@ public:
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// conversion warnings. It's better to use a uint64_t time instead.
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
// METHODS - Internal/backward compatible
// \protectedsection

View File

@ -66,11 +66,11 @@ class VerilatedVpio VL_NOT_FINAL {
// CONSTANTS
// Magic value stored in front of object to detect double free etc
// Must be odd, as aligned pointer can never be odd
static constexpr vluint32_t activeMagic() { return 0xfeed100f; }
static constexpr uint32_t activeMagic() { return 0xfeed100f; }
// MEM MANGLEMENT
// Internal note: Globals may multi-construct, see verilated.cpp top.
static VL_THREAD_LOCAL vluint8_t* t_freeHead;
static VL_THREAD_LOCAL uint8_t* t_freeHead;
public:
// CONSTRUCTORS
@ -84,19 +84,19 @@ public:
static const size_t chunk = 96;
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
uint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<uint8_t**>(newp));
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
// +8: 8 bytes for next
vluint8_t* newp = reinterpret_cast<vluint8_t*>(::operator new(chunk + 8));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(chunk + 8));
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_release_handle() called on same object twice, or on non-Verilator "
"VPI object");
@ -117,8 +117,8 @@ public:
virtual const char* name() const { return "<null>"; }
virtual const char* fullname() const { return "<null>"; }
virtual const char* defname() const { return "<null>"; }
virtual vluint32_t type() const { return 0; }
virtual vluint32_t size() const { return 0; }
virtual uint32_t type() const { return 0; }
virtual uint32_t size() const { return 0; }
virtual const VerilatedRange* rangep() const { return nullptr; }
virtual vpiHandle dovpi_scan() { return nullptr; }
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
@ -127,52 +127,52 @@ public:
class VerilatedVpioTimedCb final : public VerilatedVpio {
// A handle to a timed callback created with vpi_register_cb
// User can call vpi_remove_cb or vpi_release_handle on it
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
const uint64_t m_id; // Unique id/sequence number to find schedule's event
const QData m_time;
public:
VerilatedVpioTimedCb(vluint64_t id, QData time)
VerilatedVpioTimedCb(uint64_t id, QData time)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioReasonCb final : public VerilatedVpio {
// A handle to a non-timed callback created with vpi_register_cb
// User can call vpi_remove_cb or vpi_release_handle on it
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
const uint64_t m_id; // Unique id/sequence number to find schedule's event
const PLI_INT32 m_reason; // VPI callback reason code
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioConst final : public VerilatedVpio {
const vlsint32_t m_num;
const int32_t m_num;
public:
explicit VerilatedVpioConst(vlsint32_t num)
explicit VerilatedVpioConst(int32_t num)
: m_num{num} {}
virtual ~VerilatedVpioConst() override = default;
static VerilatedVpioConst* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiConstant; }
vlsint32_t num() const { return m_num; }
virtual uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
@ -199,7 +199,7 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual vluint32_t size() const override { return get_range().elements(); }
virtual uint32_t size() const override { return get_range().elements(); }
virtual const VerilatedRange* rangep() const override { return &get_range(); }
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
@ -218,7 +218,7 @@ public:
static VerilatedVpioParam* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiParameter; }
virtual uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@ -232,8 +232,8 @@ public:
static VerilatedVpioRange* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiRange; }
virtual vluint32_t size() const override { return m_range->elements(); }
virtual uint32_t type() const override { return vpiRange; }
virtual uint32_t size() const override { return m_range->elements(); }
virtual const VerilatedRange* rangep() const override { return m_range; }
};
@ -249,7 +249,7 @@ public:
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
@ -271,22 +271,22 @@ public:
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiScope; }
virtual uint32_t type() const override { return vpiScope; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual const char* name() const override { return m_scopep->name(); }
virtual const char* fullname() const override { return m_scopep->name(); }
};
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
uint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
union {
vluint8_t u8[4];
vluint32_t u32;
uint8_t u8[4];
uint32_t u32;
} m_mask; // memoized variable mask
vluint32_t m_entSize = 0; // memoized variable size
uint32_t m_entSize = 0; // memoized variable size
protected:
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
vlsint32_t m_index = 0;
int32_t m_index = 0;
public:
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
@ -313,18 +313,18 @@ public:
static VerilatedVpioVar* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
}
vluint32_t mask() const { return m_mask.u32; }
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
vluint32_t entSize() const { return m_entSize; }
vluint32_t index() const { return m_index; }
virtual vluint32_t type() const override {
uint32_t mask() const { return m_mask.u32; }
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
uint32_t entSize() const { return m_entSize; }
uint32_t index() const { return m_index; }
virtual uint32_t type() const override {
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
}
void* prevDatap() const { return m_prevDatap; }
void* varDatap() const { return m_varDatap; }
void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) {
m_prevDatap = new vluint8_t[entSize()];
m_prevDatap = new uint8_t[entSize()];
std::memcpy(prevDatap(), varp()->datap(), entSize());
}
}
@ -332,18 +332,18 @@ public:
class VerilatedVpioMemoryWord final : public VerilatedVpioVar {
public:
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
vlsint32_t index, int offset)
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index,
int offset)
: VerilatedVpioVar{varp, scopep} {
m_index = index;
m_varDatap = (static_cast<vluint8_t*>(varp->datap())) + entSize() * offset;
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
}
virtual ~VerilatedVpioMemoryWord() override = default;
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiMemoryWord; }
virtual vluint32_t size() const override { return varp()->packed().elements(); }
virtual uint32_t type() const override { return vpiMemoryWord; }
virtual uint32_t size() const override { return varp()->packed().elements(); }
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
@ -367,7 +367,7 @@ public:
static VerilatedVpioVarIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
@ -394,8 +394,8 @@ public:
class VerilatedVpioMemoryWordIter final : public VerilatedVpio {
const vpiHandle m_handle;
const VerilatedVar* const m_varp;
vlsint32_t m_iteration;
const vlsint32_t m_direction;
int32_t m_iteration;
const int32_t m_direction;
bool m_done = false;
public:
@ -408,7 +408,7 @@ public:
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
void iterationInc() {
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
@ -437,7 +437,7 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiModule; }
virtual uint32_t type() const override { return vpiModule; }
virtual const char* name() const override { return m_name; }
virtual const char* fullname() const override { return m_fullname; }
};
@ -455,7 +455,7 @@ public:
static VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
@ -472,14 +472,14 @@ using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
class VerilatedVpiCbHolder final {
// Holds information needed to call a callback
vluint64_t m_id;
uint64_t m_id;
s_cb_data m_cbData;
s_vpi_value m_value;
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id{id}
, m_cbData{*cbDatap}
, m_varo{varop} {
@ -495,15 +495,15 @@ public:
~VerilatedVpiCbHolder() = default;
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
s_cb_data* cb_datap() { return &m_cbData; }
vluint64_t id() const { return m_id; }
uint64_t id() const { return m_id; }
bool invalid() const { return !m_id; }
void invalidate() { m_id = 0; }
};
struct VerilatedVpiTimedCbsCmp {
// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, vluint64_t>& a,
const std::pair<QData, vluint64_t>& b) const {
bool operator()(const std::pair<QData, uint64_t>& a,
const std::pair<QData, uint64_t>& b) const {
if (a.first < b.first) return true;
if (a.first > b.first) return false;
return a.second < b.second;
@ -515,14 +515,14 @@ class VerilatedVpiError;
class VerilatedVpiImp final {
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
using VpioCbList = std::list<VerilatedVpiCbHolder>;
using VpioTimedCbs = std::map<std::pair<QData, vluint64_t>, VerilatedVpiCbHolder>;
using VpioTimedCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
// All only medium-speed, so use singleton function
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
VpioTimedCbs m_timedCbs; // Time based callbacks
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
vluint64_t m_nextCallbackId = 1; // Id to identify callback
uint64_t m_nextCallbackId = 1; // Id to identify callback
static VerilatedVpiImp& s() { // Singleton
static VerilatedVpiImp s_s;
@ -531,9 +531,9 @@ class VerilatedVpiImp final {
public:
static void assertOneCheck() { s().m_assertOne.check(); }
static vluint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
static void cbReasonAdd(vluint64_t id, const s_cb_data* cb_data_p) {
static void cbReasonAdd(uint64_t id, const s_cb_data* cb_data_p) {
// The passed cb_data_p was property of the user, so need to recreate
if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
@ -544,7 +544,7 @@ public:
if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
s().m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
}
static void cbTimedAdd(vluint64_t id, const s_cb_data* cb_data_p, QData time) {
static void cbTimedAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
// The passed cb_data_p was property of the user, so need to recreate
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64
" delay=%" PRIu64 "\n",
@ -553,7 +553,7 @@ public:
std::forward_as_tuple(std::make_pair(time, id)),
std::forward_as_tuple(id, cb_data_p, nullptr));
}
static void cbReasonRemove(vluint64_t id, vluint32_t reason) {
static void cbReasonRemove(uint64_t id, uint32_t reason) {
// Id might no longer exist, if already removed due to call after event, or teardown
VpioCbList& cbObjList = s().m_cbObjLists[reason];
// We do not remove it now as we may be iterating the list,
@ -562,7 +562,7 @@ public:
if (ir.id() == id) ir.invalidate();
}
}
static void cbTimedRemove(vluint64_t id, QData time) {
static void cbTimedRemove(uint64_t id, QData time) {
// Id might no longer exist, if already removed due to call after event, or teardown
const auto it = s().m_timedCbs.find(std::make_pair(time, id));
if (VL_LIKELY(it != s().m_timedCbs.end())) it->second.invalidate();
@ -592,7 +592,7 @@ public:
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
return ~0ULL; // maxquad
}
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
VpioCbList& cbObjList = s().m_cbObjLists[reason];
bool called = false;
if (cbObjList.empty()) return called;
@ -664,7 +664,7 @@ public:
// Statics
// Internal note: Globals may multi-construct, see verilated.cpp top.
VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = nullptr;
VL_THREAD_LOCAL uint8_t* VerilatedVpio::t_freeHead = nullptr;
//======================================================================
// VerilatedVpiError
@ -744,7 +744,7 @@ void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callTimedC
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
bool VerilatedVpi::callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
return VerilatedVpiImp::callCbs(reason);
}
@ -1309,7 +1309,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
QData time = 0;
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
const QData abstime = VL_TIME_Q() + time;
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
const uint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
return vop->castVpiHandle();
@ -1324,7 +1324,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
const uint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
return vop->castVpiHandle();
@ -1474,7 +1474,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
case vpiIndex: {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
const vlsint32_t val = vop->index();
const int32_t val = vop->index();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiScope: {
@ -1996,7 +1996,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
for (int i = 0; i < chars; ++i) {
union {
char byte[2];
vluint16_t half;
uint16_t half;
} val;
idx = div(i * 3, 8);
if (i < len) {

View File

@ -47,7 +47,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;

View File

@ -282,6 +282,7 @@ void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
#include <cstdint>
#include <cinttypes>
#ifndef VL_NO_LEGACY
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
using vluint16_t = uint16_t; ///< 16-bit unsigned type (backward compatibility)
using vluint32_t = uint32_t; ///< 32-bit unsigned type (backward compatibility)
@ -290,6 +291,7 @@ using vlsint8_t = int8_t; ///< 8-bit signed type (backward compatibility)
using vlsint16_t = int16_t; ///< 16-bit signed type (backward compatibility)
using vlsint32_t = int32_t; ///< 32-bit signed type (backward compatibility)
using vlsint64_t = int64_t; ///< 64-bit signed type (backward compatibility)
#endif
#if defined(__CYGWIN__)
@ -320,12 +322,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Deprecated, favor C++11's PRIx64, etc, instead
#ifndef VL_NO_LEGACY
# ifdef _MSC_VER
# define VL_PRI64 "I64" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "I64" ///< print a uint64_t (backward compatibility)
# else // use standard C99 format specifiers
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
# define VL_PRI64 "l" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "l" ///< print a uint64_t (backward compatibility)
# else
# define VL_PRI64 "ll" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "ll" ///< print a uint64_t (backward compatibility)
# endif
# endif
#endif
@ -436,24 +438,24 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Performance counters
#if defined(__i386__) || defined(__x86_64__)
// The vluint64_t argument is loaded with a high-performance counter for profiling
// The uint64_t argument is loaded with a high-performance counter for profiling
// or 0x0 if not implemented on this platform
#define VL_RDTSC(val) \
#define VL_GET_CPU_TICK(val) \
{ \
vluint32_t hi, lo; \
uint32_t hi, lo; \
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
}
#elif defined(__aarch64__)
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
# define VL_RDTSC(val) \
# define VL_GET_CPU_TICK(val) \
{ \
asm volatile("isb" : : : "memory"); \
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
}
#else
// We just silently ignore unknown OSes, as only leads to missing statistics
# define VL_RDTSC(val) (val) = 0;
# define VL_GET_CPU_TICK(val) (val) = 0;
#endif
//=========================================================================
@ -475,6 +477,9 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
# define VL_CPU_RELAX() asm volatile("yield" ::: "memory")
# elif defined(__powerpc64__)
# define VL_CPU_RELAX() asm volatile("or 1, 1, 1; or 2, 2, 2;" ::: "memory")
# elif defined(__loongarch__)
// LoongArch does not currently have a yield/pause instruction
# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
# else
# error "Missing VL_CPU_RELAX() definition. Or, don't use VL_THREADED"
# endif
@ -489,10 +494,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
# define VL_STRCASECMP strcasecmp
#endif
#ifdef __MINGW32__
#if defined(__MINGW32__) || defined(_MSC_VER)
# define VL_LOCALTIME_R(timep, tmp) localtime_s((tmp), (timep))
#elif defined(_MSC_VER)
# define VL_LOCALTIME_R(timep, tmp) localtime_c((tmp), (timep))
#else
# define VL_LOCALTIME_R(timep, tmp) localtime_r((timep), (tmp))
#endif
@ -518,6 +521,19 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Conversions
namespace vlstd {
template <typename T> struct reverse_wrapper {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {}
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
};
// C++20's std::ranges::reverse_view
template <typename T> reverse_wrapper<T> reverse_view(const T& v) { return reverse_wrapper<T>(v); }
// C++17's std::as_const
template <class T> T const& as_const(T& v) { return v; }
}; // namespace vlstd

View File

@ -610,7 +610,7 @@ def getGcovCoverage(args):
coverage_files = getFilteredCoverageFiles(coverage_files, args.excludepre)
logging.info("Found {} coverage files after filtering".format(len(coverage_files)))
# We "zero" the "counters" by simply deleting all gcda files
# We "zero" the "counters" by deleting all gcda files
if args.zerocounters:
removeFiles(coverage_files)
logging.info("Removed {} .gcda files".format(len(coverage_files)))

View File

@ -321,75 +321,70 @@ public:
};
//######################################################################
// Active AssignDly replacement functions
// Replace unsupported non-blocking assignments with blocking assignments
class ActiveDlyVisitor final : public ActiveBaseVisitor {
public:
enum CheckType : uint8_t { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
private:
const CheckType m_check; // Combo logic or other
const AstNode* const m_alwaysp; // Always we're under
const AstNode* m_assignp = nullptr; // In assign
// MEMBERS
const CheckType m_check; // Process type we are checking
// VISITORS
virtual void visit(AstAssignDly* nodep) override {
if (m_check != CT_SEQ) {
// Convert to a non-delayed assignment
UINFO(5, " ASSIGNDLY " << nodep << endl);
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
<< nodep->warnMore()
<< "... Suggest blocking assignments (=)");
} else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races
} else if (!(VN_IS(nodep->lhsp(), VarRef)
&& VN_AS(nodep->lhsp(), VarRef)->varp()->isLatched())) {
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
" (non flop or latch) block\n"
<< nodep->warnMore()
<< "... Suggest blocking assignments (=)");
// Conversely, we could also suggest latches use delayed assignments, as
// recommended by Cliff Cummings?
}
AstNode* const newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
// Issue appropriate warning
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY,
"Non-blocking assignment '<=' in initial/final block\n"
<< nodep->warnMore()
<< "... This will be executed as a blocking assignment '='!");
} else {
nodep->v3warn(COMBDLY,
"Non-blocking assignment '<=' in combinational logic process\n"
<< nodep->warnMore()
<< "... This will be executed as a blocking assignment '='!");
}
// Convert to blocking assignment
nodep->replaceWith(new AstAssign{nodep->fileline(), //
nodep->lhsp()->unlinkFrBack(), //
nodep->rhsp()->unlinkFrBack()});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAssign* nodep) override {
if (m_check == CT_SEQ) {
VL_RESTORER(m_assignp);
m_assignp = nodep;
iterateAndNextNull(nodep->lhsp());
}
}
virtual void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (m_check == CT_SEQ && m_assignp && !varp->isUsedLoopIdx() // Ignore loop indices
&& !varp->isTemp()) {
// Allow turning off warnings on the always, or the variable also
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
m_assignp->v3warn(BLKSEQ,
"Blocking assignments (=) in sequential (flop or latch) block\n"
<< m_assignp->warnMore()
<< "... Suggest delayed assignments (<=)");
m_alwaysp->fileline()->modifyWarnOff(
V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
}
}
// Blocking assignments are always OK in combinational (and initial/final) processes
if (m_check != CT_SEQ) return;
const bool ignore = nodep->lhsp()->forall<AstVarRef>([&](const AstVarRef* refp) {
// Ignore reads (e.g.: index expressions)
if (refp->access().isReadOnly()) return true;
const AstVar* const varp = refp->varp();
// Ignore ...
return varp->isUsedLoopIdx() // ... loop indices
|| varp->isTemp() // ... temporaries
|| varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ); // ... user said so
});
if (ignore) return;
nodep->v3warn(BLKSEQ,
"Blocking assignment '=' in sequential logic process\n"
<< nodep->warnMore() //
<< "... Suggest using delayed assignment '<='");
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
ActiveDlyVisitor(AstNode* nodep, CheckType check)
: m_check{check}
, m_alwaysp{nodep} {
: m_check{check} {
iterate(nodep);
}
virtual ~ActiveDlyVisitor() override = default;
@ -422,6 +417,14 @@ private:
virtual void visit(AstActive* nodep) override {
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitialStatic* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
@ -527,12 +530,8 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_LATCH};
} else {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMBO};
}
} else if (!combo && sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
}

View File

@ -98,7 +98,7 @@ private:
? static_cast<AstNode*>(
new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
nodep, nullptr);
nodep);
newp->user1(true); // Don't assert/cover this if
return newp;
}
@ -154,7 +154,7 @@ private:
}
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp, nullptr);
ifp = new AstIf(nodep->fileline(), propp, bodysp);
bodysp = ifp;
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
if (nodep->immediate()) {
@ -262,7 +262,7 @@ private:
if (itemp->isDefault()) has_default = true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
// Simply need to add a default if there isn't one already
// Need to add a default if there isn't one already
++m_statAsFull;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(
@ -313,8 +313,7 @@ private:
AstIf* const ifp = new AstIf(
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep,
"synthesis parallel_case, but multiple matches found"),
nullptr);
"synthesis parallel_case, but multiple matches found"));
ifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp);
}
@ -384,7 +383,7 @@ private:
new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
new AstEq{fl, new AstConst{fl, monNum},
newMonitorNumVarRefp(nodep, VAccess::READ)}},
stmtsp, nullptr};
stmtsp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
m_modp->addStmtp(newp);
@ -402,8 +401,7 @@ private:
nodep->replaceWith(newsetp);
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
AstNode* const stmtsp = nodep;
AstIf* const ifp
= new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},

View File

@ -167,16 +167,14 @@ private:
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
// Block is the new expression to evaluate
AstNode* blockp = nodep->propp()->unlinkFrBack();
if (nodep->disablep()) {
m_disablep = nodep->disablep()->cloneTree(false);
if (AstNode* const disablep = nodep->disablep()) {
m_disablep = disablep->cloneTree(false);
if (VN_IS(nodep->backp(), Cover)) {
blockp = new AstAnd(
nodep->disablep()->fileline(),
new AstNot(nodep->disablep()->fileline(), nodep->disablep()->unlinkFrBack()),
blockp);
blockp = new AstAnd(disablep->fileline(),
new AstNot(disablep->fileline(), disablep->unlinkFrBack()),
blockp);
} else {
blockp = new AstOr(nodep->disablep()->fileline(),
nodep->disablep()->unlinkFrBack(), blockp);
blockp = new AstOr(disablep->fileline(), disablep->unlinkFrBack(), blockp);
}
}
// Unlink and just keep a pointer to it, convert to sentree as needed

View File

@ -30,8 +30,8 @@
//======================================================================
// Statics
vluint64_t AstNode::s_editCntLast = 0;
vluint64_t AstNode::s_editCntGbl = 0; // Hot cache line
uint64_t AstNode::s_editCntLast = 0;
uint64_t AstNode::s_editCntGbl = 0; // Hot cache line
// To allow for fast clearing of all user pointers, we keep a "timestamp"
// along with each userp, and thus by bumping this count we can make it look
@ -114,7 +114,7 @@ string AstNode::encodeName(const string& namein) {
return vname.hashedName();
}
string AstNode::encodeNumber(vlsint64_t num) {
string AstNode::encodeNumber(int64_t num) {
if (num < 0) {
return "__02D" + cvtToStr(-num); // 2D=-
} else {
@ -1135,49 +1135,33 @@ void AstNode::v3errorEndFatal(std::ostringstream& str) const {
VL_UNREACHABLE
}
string AstNode::locationStr() const {
string str = "... In instance ";
const AstNode* backp = this;
int itmax = 10000; // Max iterations before giving up on location search
while (backp) {
if (VL_UNCOVERABLE(--itmax < 0)) {
// Likely some circular back link, and V3Ast is trying to report a low-level error
UINFO(1, "Ran out of iterations finding locationStr on " << backp << endl);
return ""; // LCOV_EXCL_LINE
}
const AstScope* scopep;
if ((scopep = VN_CAST(backp, Scope))) {
// The design is flattened and there are no useful scopes
// This is probably because of inlining
if (scopep->isTop()) break;
string AstNode::instanceStr() const {
// Max iterations before giving up on location search,
// in case we have some circular reference bug.
constexpr unsigned maxIterations = 10000;
unsigned iterCount = 0;
str += scopep->prettyName();
return str;
for (const AstNode* backp = this; backp; backp = backp->backp(), ++iterCount) {
if (VL_UNCOVERABLE(iterCount >= maxIterations)) return ""; // LCOV_EXCL_LINE
// Prefer the enclosing scope, if there is one. This is always under the enclosing module,
// so just pick it up when encountered
if (const AstScope* const scopep = VN_CAST(backp, Scope)) {
return scopep->isTop() ? "" : "... In instance " + scopep->prettyName();
}
backp = backp->backp();
}
backp = this;
while (backp) {
const AstModule* modp;
const AstNodeVarRef* nvrp;
if ((modp = VN_CAST(backp, Module)) && !modp->hierName().empty()) {
str += modp->hierName();
return str;
} else if ((nvrp = VN_CAST(backp, NodeVarRef))) {
const string prettyName = nvrp->prettyName();
// VarRefs have not been flattened yet and do not contain location information
if (prettyName != nvrp->name()) {
str += prettyName;
return str;
}
// If scopes don't exist, report an example instance of the enclosing module
if (const AstModule* const modp = VN_CAST(backp, Module)) {
const string instanceName = modp->someInstanceName();
return instanceName.empty() ? "" : "... In instance " + instanceName;
}
backp = backp->backp();
}
return "";
}
void AstNode::v3errorEnd(std::ostringstream& str) const {
if (!m_fileline) {
V3Error::v3errorEnd(str, locationStr());
V3Error::v3errorEnd(str, instanceStr());
} else {
std::ostringstream nsstr;
nsstr << str.str();
@ -1187,7 +1171,7 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
const_cast<AstNode*>(this)->dump(nsstr);
nsstr << endl;
}
m_fileline->v3errorEnd(nsstr, locationStr());
m_fileline->v3errorEnd(nsstr, instanceStr());
}
}

View File

@ -83,7 +83,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
//######################################################################
@ -1398,10 +1398,10 @@ class AstNode VL_NOT_FINAL {
AstNodeDType* m_dtypep = nullptr; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
FileLine* m_fileline; // Where it was declared
vluint64_t m_editCount; // When it was last edited
static vluint64_t s_editCntGbl; // Global edit counter
uint64_t m_editCount; // When it was last edited
static uint64_t s_editCntGbl; // Global edit counter
// Global edit counter, last value for printing * near node #s
static vluint64_t s_editCntLast;
static uint64_t s_editCntLast;
AstNode* m_clonep
= nullptr; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
@ -1447,7 +1447,7 @@ private:
bool gateOnly);
void deleteTreeIter();
void deleteNode();
string locationStr() const;
string instanceStr() const;
public:
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
@ -1559,7 +1559,7 @@ public:
}
static string
encodeName(const string& namein); // Encode user name into internal C representation
static string encodeNumber(vlsint64_t num); // Encode number into internal C representation
static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const { return prettyName(name()); }
string prettyNameQ() const { return prettyNameQ(name()); }
@ -1666,12 +1666,12 @@ public:
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
vluint64_t editCount() const { return m_editCount; }
uint64_t editCount() const { return m_editCount; }
void editCountInc() {
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"
}
static vluint64_t editCountLast() { return s_editCntLast; }
static vluint64_t editCountGbl() { return s_editCntGbl; }
static uint64_t editCountLast() { return s_editCntLast; }
static uint64_t editCountGbl() { return s_editCntGbl; }
static void editCountSetLast() { s_editCntLast = editCountGbl(); }
// ACCESSORS for specific types
@ -1972,47 +1972,121 @@ private:
} while (nodep);
}
public:
// Traverse subtree and call given function 'f' in pre-order on each node that has type 'T'.
// Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a few) node
// types, as it's easier to write, but more importantly, the dispatch to the operation function
// in 'foreach' should be completely predictable by branch target caches in modern CPUs,
// while it is basically unpredictable for VNVisitor.
template <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
template <typename T_Arg, bool Default, bool VisitNext>
static bool predicateImpl(
// Using std::conditional for const correctness in the public 'foreach' functions
typename std::conditional<std::is_const<T_Arg>::value, const AstNode*, AstNode*>::type
nodep,
std::function<bool(T_Arg*)> p) {
// Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because
// debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes.
do {
// Prefetch children and next
ASTNODE_PREFETCH(nodep->op1p());
ASTNODE_PREFETCH(nodep->op2p());
ASTNODE_PREFETCH(nodep->op3p());
ASTNODE_PREFETCH(nodep->op4p());
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
// Apply function in pre-order
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
if (p(static_cast<T_Arg*>(nodep)) != Default) return !Default;
}
// Traverse children (including their 'nextp()' chains), unless futile
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
if (AstNode* const op1p = nodep->op1p()) {
if (predicateImpl<T_Arg, Default, true>(op1p, p) != Default) return !Default;
}
if (AstNode* const op2p = nodep->op2p()) {
if (predicateImpl<T_Arg, Default, true>(op2p, p) != Default) return !Default;
}
if (AstNode* const op3p = nodep->op3p()) {
if (predicateImpl<T_Arg, Default, true>(op3p, p) != Default) return !Default;
}
if (AstNode* const op4p = nodep->op4p()) {
if (predicateImpl<T_Arg, Default, true>(op4p, p) != Default) return !Default;
}
}
// Traverse 'nextp()' chain if requested
if /* TODO: 'constexpr' in C++17 */ (VisitNext) {
nodep = nodep->nextp();
} else {
break;
}
} while (nodep);
return Default;
}
template <typename T_Node> constexpr static void checkTypeParameter() {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
}
public:
// Traverse subtree and call given function 'f' in pre-order on each node that has type
// 'T_Node'. Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a
// few) node types, as it's easier to write, but more importantly, the dispatch to the
// operation function in 'foreach' should be completely predictable by branch target caches in
// modern CPUs, while it is basically unpredictable for VNVisitor.
template <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
checkTypeParameter<T_Node>();
foreachImpl<T_Node, /* VisitNext: */ false>(this, f);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void foreach (std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
checkTypeParameter<T_Node>();
foreachImpl<const T_Node, /* VisitNext: */ false>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(T_Node*)> f) {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
checkTypeParameter<T_Node>();
foreachImpl<T_Node, /* VisitNext: */ true>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
checkTypeParameter<T_Node>();
foreachImpl<const T_Node, /* VisitNext: */ true>(this, f);
}
// Given a predicate function 'p' return true if and only if there exists a node of type
// 'T_Node' that satisfies the predicate 'p'. Returns false if no node of type 'T_Node' is
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
// result can be determined.
template <typename T_Node> bool exists(std::function<bool(T_Node*)> p) {
checkTypeParameter<T_Node>();
return predicateImpl<T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void exists(std::function<bool(const T_Node*)> p) const {
checkTypeParameter<T_Node>();
return predicateImpl<const T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
}
// Given a predicate function 'p' return true if and only if all nodes of type
// 'T_Node' satisfy the predicate 'p'. Returns true if no node of type 'T_Node' is
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
// result can be determined.
template <typename T_Node> bool forall(std::function<bool(T_Node*)> p) {
checkTypeParameter<T_Node>();
return predicateImpl<T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void forall(std::function<bool(const T_Node*)> p) const {
checkTypeParameter<T_Node>();
return predicateImpl<const T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
}
int nodeCount() const {
// TODO: this should really return size_t, but need to fix use sites
int count = 0;
@ -2034,6 +2108,11 @@ template <> inline bool AstNode::privateMayBeUnder<AstNodeAssign>(const AstNode*
template <> inline bool AstNode::privateMayBeUnder<AstVarScope>(const AstNode* nodep) {
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
}
template <> inline bool AstNode::privateMayBeUnder<AstExecGraph>(const AstNode* nodep) {
if (VN_IS(nodep, ExecGraph)) return false; // Should not nest
if (VN_IS(nodep, NodeStmt)) return false; // Should be directly under CFunc
return true;
}
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
if (!rhs) {
@ -2562,7 +2641,7 @@ private:
string m_text;
protected:
// Node that simply puts text into the output stream
// Node that puts text into the output stream
AstNodeText(VNType t, FileLine* fl, const string& textp)
: AstNode{t, fl} {
m_text = textp; // Copy it
@ -3072,7 +3151,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
private:
string m_name; // Name of the module
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
string m_hierName; // Hierarchical name for errors, etc.
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
// Used for user messages only.
bool m_modPublic : 1; // Module has public references
bool m_modTrace : 1; // Tracing this module
bool m_inLibrary : 1; // From a library, no error if not used, never top level
@ -3114,8 +3194,8 @@ public:
// ACCESSORS
virtual void name(const string& name) override { m_name = name; }
virtual string origName() const override { return m_origName; }
string hierName() const { return m_hierName; }
void hierName(const string& hierName) { m_hierName = hierName; }
string someInstanceName() const { return m_someInstanceName; }
void someInstanceName(const string& name) { m_someInstanceName = name; }
bool inLibrary() const { return m_inLibrary; }
void inLibrary(bool flag) { m_inLibrary = flag; }
void level(int level) { m_level = level; }

View File

@ -225,9 +225,10 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
}
AstExecGraph::AstExecGraph(FileLine* fileline)
AstExecGraph::AstExecGraph(FileLine* fileline, const string& name)
: ASTGEN_SUPER_ExecGraph(fileline)
, m_depGraphp{new V3Graph} {}
, m_depGraphp{new V3Graph}
, m_name{name} {}
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
@ -463,7 +464,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
} else if (widthMin() <= VL_IDATASIZE) {
arg += "uint32_t";
} else if (widthMin() <= VL_QUADSIZE) {
arg += "vluint64_t";
arg += "uint64_t";
} else {
arg += "uint32_t"; // []'s added later
}
@ -600,7 +601,7 @@ string AstVar::scType() const {
return "uint32_t";
}
} else {
return "vluint64_t";
return "uint64_t";
}
}
@ -1071,8 +1072,8 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) return false;
// Compare initializer arrays by value. Note this is only called when they hash the same,
// so they likely run at most once per call to 'AstConstPool::findTable'.
const vluint64_t size = aDTypep->elementsConst();
for (vluint64_t n = 0; n < size; ++n) {
const uint64_t size = aDTypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
if (!valAp->sameTree(valBp)) return false;
@ -1853,6 +1854,10 @@ void AstFork::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (!joinType().join()) str << " [" << joinType() << "]";
}
void AstTraceDecl::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (code()) str << " [code=" << code() << "]";
}
void AstTraceInc::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";

View File

@ -115,14 +115,14 @@ public:
dtypeSetLogicUnsized(32, m_num.widthMin(), VSigning::SIGNED);
}
class Unsized64 {}; // for creator type-overload selection
AstConst(FileLine* fl, Unsized64, vluint64_t num)
AstConst(FileLine* fl, Unsized64, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, 64, 0) {
m_num.setQuad(num);
dtypeSetLogicSized(64, VSigning::UNSIGNED);
}
class SizedEData {}; // for creator type-overload selection
AstConst(FileLine* fl, SizedEData, vluint64_t num)
AstConst(FileLine* fl, SizedEData, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, VL_EDATASIZE, 0) {
m_num.setQuad(num);
@ -166,8 +166,8 @@ public:
const V3Number& num() const { return m_num; } // * = Value
V3Number& num() { return m_num; } // * = Value
uint32_t toUInt() const { return num().toUInt(); }
vlsint32_t toSInt() const { return num().toSInt(); }
vluint64_t toUQuad() const { return num().toUQuad(); }
int32_t toSInt() const { return num().toSInt(); }
uint64_t toUQuad() const { return num().toUQuad(); }
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
@ -1914,14 +1914,14 @@ private:
bool m_pure = false; // Pure optimizable
public:
AstCMethodHard(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name,
AstNode* pinsp)
AstNode* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl, false)
, m_name{name} {
setOp1p(fromp);
dtypep(nullptr); // V3Width will resolve
addNOp2p(pinsp);
}
AstCMethodHard(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp)
AstCMethodHard(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl, false)
, m_name{name} {
setOp1p(fromp);
@ -3407,7 +3407,7 @@ public:
};
class AstInitialAutomatic final : public AstNodeProcedure {
// initial for automatic variables
// Automatic variable initialization
// That is, it runs every function start, or class construction
public:
AstInitialAutomatic(FileLine* fl, AstNode* bodysp)
@ -3415,6 +3415,15 @@ public:
ASTNODE_NODE_FUNCS(InitialAutomatic)
};
class AstInitialStatic final : public AstNodeProcedure {
// Static variable initialization
// That is, it runs at the beginning of simulation, before 'initial' blocks
public:
AstInitialStatic(FileLine* fl, AstNode* bodysp)
: ASTGEN_SUPER_InitialStatic(fl, bodysp) {}
ASTNODE_NODE_FUNCS(InitialStatic)
};
class AstAlways final : public AstNodeProcedure {
const VAlwaysKwd m_keyword;
@ -4610,7 +4619,7 @@ public:
class AstWhile final : public AstNodeStmt {
public:
AstWhile(FileLine* fl, AstNode* condp, AstNode* bodysp, AstNode* incsp = nullptr)
AstWhile(FileLine* fl, AstNode* condp, AstNode* bodysp = nullptr, AstNode* incsp = nullptr)
: ASTGEN_SUPER_While(fl) {
setOp2p(condp);
addNOp3p(bodysp);
@ -4714,7 +4723,7 @@ private:
bool m_unique0Pragma; // unique0 case
bool m_priorityPragma; // priority case
public:
AstIf(FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp = nullptr)
AstIf(FileLine* fl, AstNode* condp, AstNode* ifsp = nullptr, AstNode* elsesp = nullptr)
: ASTGEN_SUPER_If(fl, condp, ifsp, elsesp) {
m_uniquePragma = false;
m_unique0Pragma = false;
@ -4927,7 +4936,7 @@ private:
bool m_generate; // Underneath a generate
const bool m_implied; // Not inserted by user
public:
// Node that simply puts name into the output stream
// Node that puts name into the output stream
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
bool implied = false)
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
@ -4951,7 +4960,7 @@ class AstFork final : public AstNodeBlock {
private:
VJoinType m_joinType; // Join keyword type
public:
// Node that simply puts name into the output stream
// Node that puts name into the output stream
AstFork(FileLine* fl, const string& name, AstNode* stmtsp)
: ASTGEN_SUPER_Fork(fl, name, stmtsp) {}
ASTNODE_NODE_FUNCS(Fork)
@ -5019,7 +5028,7 @@ class AstInitArray final : public AstNode {
// Parents: ASTVAR::init()
// Children: AstInitItem
public:
using KeyItemMap = std::map<vluint64_t, AstInitItem*>;
using KeyItemMap = std::map<uint64_t, AstInitItem*>;
private:
KeyItemMap m_map; // Node value for each array index
@ -5054,7 +5063,7 @@ public:
AstNode* initsp() const { return op2p(); } // op2 = Initial value expressions
void addValuep(AstNode* newp) { addIndexValuep(m_map.size(), newp); }
const KeyItemMap& map() const { return m_map; }
AstNode* addIndexValuep(vluint64_t index, AstNode* newp) {
AstNode* addIndexValuep(uint64_t index, AstNode* newp) {
// Returns old value, caller must garbage collect
AstNode* oldp = nullptr;
const auto it = m_map.find(index);
@ -5068,7 +5077,7 @@ public:
}
return oldp;
}
AstNode* getIndexValuep(vluint64_t index) const {
AstNode* getIndexValuep(uint64_t index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return nullptr;
@ -5076,7 +5085,7 @@ public:
return it->second->valuep();
}
}
AstNode* getIndexDefaultedValuep(vluint64_t index) const {
AstNode* getIndexDefaultedValuep(uint64_t index) const {
AstNode* valuep = getIndexValuep(index);
if (!valuep) valuep = defaultp();
return valuep;
@ -5330,6 +5339,7 @@ public:
dtypeFrom(valuep);
addNOp1p(valuep);
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 100; } // Large...
ASTNODE_NODE_FUNCS(TraceDecl)
virtual string name() const override { return m_showname; }
@ -9199,27 +9209,30 @@ public:
class AstExecGraph final : public AstNode {
// For parallel execution, this node contains a dependency graph. Each
// node in the graph is an ExecMTask, which contains a body for the
// mtask, which contains a set of AstActive's, each of which calls a
// leaf AstCFunc. whew!
// vertex in the graph is an ExecMTask, which contains a body for the
// mtask (an AstMTaskBody), which contains sequentially executed statements.
//
// The mtask bodies are also children of this node, so we can visit
// them without traversing the graph (it's not always needed to
// traverse the graph.)
// The AstMTaskBody nodes are also children of this node, so we can visit
// them without traversing the graph.
private:
V3Graph* const m_depGraphp; // contains ExecMTask's
V3Graph* const m_depGraphp; // contains ExecMTask vertices
const string m_name; // Name of this AstExecGraph (for uniqueness at code generation)
public:
explicit AstExecGraph(FileLine* fl);
explicit AstExecGraph(FileLine* fl, const string& name);
ASTNODE_NODE_FUNCS_NO_DTOR(ExecGraph)
virtual ~AstExecGraph() override;
virtual const char* broken() const override {
BROKEN_RTN(!m_depGraphp);
return nullptr;
}
virtual string name() const override { return m_name; }
V3Graph* depGraphp() { return m_depGraphp; }
const V3Graph* depGraphp() const { return m_depGraphp; }
V3Graph* mutableDepGraphp() { return m_depGraphp; }
void addMTaskBody(AstMTaskBody* bodyp) { addOp1p(bodyp); }
// op1: The mtask bodies
AstMTaskBody* mTaskBodiesp() const { return VN_AS(op1p(), MTaskBody); }
void addMTaskBodyp(AstMTaskBody* bodyp) { addOp1p(bodyp); }
// op2: In later phases, the statements that start the parallel execution
void addStmtsp(AstNode* stmtp) { addOp2p(stmtp); }
};
@ -9319,13 +9332,15 @@ private:
AstConstPool* const m_constPoolp; // Reference to constant pool, for faster lookup
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
AstTopScope* m_topScopep = nullptr; // The singleton AstTopScope under the top module
VTimescale m_timeunit; // Global time unit
VTimescale m_timeprecision; // Global time precision
bool m_changeRequest = false; // Have _change_request method
bool m_timescaleSpecified = false; // Input HDL specified timescale
uint32_t m_nextFreeMTaskID = 1; // Next unique MTask ID within netlist
// starts at 1 so 0 means no MTask ID
uint32_t m_nextFreeMTaskProfilingID = 0; // Next unique ID to use for PGO
public:
AstNetlist();
ASTNODE_NODE_FUNCS(Netlist)
@ -9369,8 +9384,6 @@ public:
}
AstCFunc* evalp() const { return m_evalp; }
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
AstExecGraph* execGraphp() const { return m_execGraphp; }
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstTopScope* topScopep() const { return m_topScopep; }
@ -9390,6 +9403,9 @@ public:
void timeprecisionMerge(FileLine*, const VTimescale& value);
void timescaleSpecified(bool specified) { m_timescaleSpecified = specified; }
bool timescaleSpecified() const { return m_timescaleSpecified; }
uint32_t allocNextMTaskID() { return m_nextFreeMTaskID++; }
uint32_t allocNextMTaskProfilingID() { return m_nextFreeMTaskProfilingID++; }
uint32_t usedMTaskProfilingIDs() const { return m_nextFreeMTaskProfilingID; }
};
//######################################################################

View File

@ -117,7 +117,7 @@ public:
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
// and is there for a bit of extra type safety. T_Data is the type of the data structure
// managed by the allocator.
template <class T_Node, class T_Data>
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};

View File

@ -384,7 +384,7 @@ private:
VL_DANGLING(iconstp);
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
} else {
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
// Not a caseX mask, we can build CASEEQ(cexpr icond)
AstNode* const and1p = cexprp->cloneTree(false);
AstNode* const and2p = icondp;
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
@ -427,8 +427,7 @@ private:
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
if (depth == 1) { // First group or starting new group
itemnextp = nullptr;
AstIf* const newp
= new AstIf(itemp->fileline(), ifexprp->cloneTree(true), nullptr, nullptr);
AstIf* const newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true));
if (groupnextp) {
groupnextp->addElsesp(newp);
} else {
@ -448,7 +447,7 @@ private:
VL_DO_DANGLING(itemexprp->deleteTree(), itemexprp);
itemexprp = new AstConst(itemp->fileline(), AstConst::BitTrue());
}
AstIf* const newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, nullptr);
AstIf* const newp = new AstIf(itemp->fileline(), itemexprp, istmtsp);
if (itemnextp) {
itemnextp->addElsesp(newp);
} else {

View File

@ -716,6 +716,7 @@ private:
// Ignores
virtual void visit(AstInitial*) override {}
virtual void visit(AstInitialAutomatic*) override {}
virtual void visit(AstInitialStatic*) override {}
virtual void visit(AstTraceDecl*) override {}
virtual void visit(AstCoverToggle*) override {}
virtual void visit(AstNodeDType*) override {}

View File

@ -153,6 +153,11 @@ private:
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
}
virtual void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
}
virtual void visit(AstNodeMath* nodep) override {} // Short circuit
virtual void visit(AstNodeStmt* nodep) override {} // Short circuit
@ -173,7 +178,7 @@ public:
vscp->scopep(scopep);
vscp->unlinkFrBack();
scopep->addVarp(vscp);
} else if (VN_IS(nodep, Initial)) {
} else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) {
nodep->unlinkFrBack();
scopep->addActivep(nodep);
} else {

View File

@ -185,7 +185,7 @@ private:
AstIf* makeActiveIf(AstSenTree* sensesp) {
AstNode* const senEqnp = createSenseEquation(sensesp->sensesp());
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
AstIf* const newifp = new AstIf(sensesp->fileline(), senEqnp, nullptr, nullptr);
AstIf* const newifp = new AstIf(sensesp->fileline(), senEqnp);
return newifp;
}
void clearLastSen() {
@ -310,8 +310,8 @@ private:
AstNode* const origp = nodep->origp()->unlinkFrBack();
AstNode* const changeWrp = nodep->changep()->unlinkFrBack();
AstNode* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
AstIf* const newp = new AstIf(
nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp), incp, nullptr);
AstIf* const newp
= new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp), incp);
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though versus a potential memory miss.
// We'll go with the miss.
@ -411,7 +411,8 @@ private:
}
}
virtual void visit(AstExecGraph* nodep) override {
for (m_mtaskBodyp = VN_AS(nodep->op1p(), MTaskBody); m_mtaskBodyp;
VL_RESTORER(m_mtaskBodyp);
for (m_mtaskBodyp = nodep->mTaskBodiesp(); m_mtaskBodyp;
m_mtaskBodyp = VN_AS(m_mtaskBodyp->nextp(), MTaskBody)) {
clearLastSen();
iterate(m_mtaskBodyp);

View File

@ -350,7 +350,7 @@ using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
class V3ConfigResolver final {
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
std::unordered_map<string, std::unordered_map<string, vluint64_t>>
std::unordered_map<string, std::unordered_map<string, uint64_t>>
m_profileData; // Access to profile_data records
FileLine* m_profileFileLine = nullptr;
@ -364,12 +364,12 @@ public:
V3ConfigModuleResolver& modules() { return m_modules; }
V3ConfigFileResolver& files() { return m_files; }
void addProfileData(FileLine* fl, const string& model, const string& key, vluint64_t cost) {
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) {
if (!m_profileFileLine) m_profileFileLine = fl;
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
m_profileData[model][key] += cost;
}
vluint64_t getProfileData(const string& model, const string& key) const {
uint64_t getProfileData(const string& model, const string& key) const {
const auto mit = m_profileData.find(model);
if (mit == m_profileData.cend()) return 0;
const auto it = mit->second.find(key);
@ -430,7 +430,7 @@ void V3Config::addModulePragma(const string& module, VPragmaType pragma) {
}
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost) {
uint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
}
@ -534,7 +534,7 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar
if (vp) vp->apply(varp);
}
vluint64_t V3Config::getProfileData(const string& model, const string& key) {
uint64_t V3Config::getProfileData(const string& model, const string& key) {
return V3ConfigResolver::s().getProfileData(model, key);
}
FileLine* V3Config::getProfileDataFileLine() {

View File

@ -36,7 +36,7 @@ public:
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, VPragmaType pragma);
static void addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost);
uint64_t cost);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& signal, VAttrType type, AstSenTree* nodep);
@ -48,7 +48,7 @@ public:
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
static vluint64_t getProfileData(const string& model, const string& key);
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};

View File

@ -33,6 +33,7 @@
#include "V3UniqueNames.h"
#include <algorithm>
#include <memory>
#include <type_traits>
//######################################################################
@ -2658,7 +2659,7 @@ private:
// SENTREE(... SENITEM(x), SENGATE(SENITEM(x),*) ...) => SENITEM(x)
// Do we need the SENITEM's to be identical? No because we're
// ORing between them; we just need to ensure that the result is at
// least as frequently activating. So we simply
// least as frequently activating. So we
// SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the
// other SENITEM(x).
{

View File

@ -351,8 +351,7 @@ private:
"Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
postLogicp = new AstIf(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
nullptr, nullptr);
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ));
UINFO(9, " Created " << postLogicp << endl);
finalp->addStmtp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable

View File

@ -161,7 +161,7 @@ private:
new AstCMath(funcp->fileline(),
string("&(") + funcp->scopep()->nameVlSym() + ")",
64)),
returnp, nullptr);
returnp);
newfuncp->addStmtsp(ifp);
} else {
newfuncp->addStmtsp(returnp);
@ -176,7 +176,7 @@ private:
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
if (debug() >= 9) newfuncp->dumpTree(cout, " newfunc: ");
} else {
// Only a single function under this name, we can simply rename it
// Only a single function under this name, we can rename it
UINFO(6, " Wrapping " << name << " just one " << topFuncp << endl);
topFuncp->name(name);
}

View File

@ -73,7 +73,7 @@ protected:
ofp()->putsNoTracking("}");
} else if (const AstUnpackArrayDType* const dtypep
= VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
const vluint64_t size = dtypep->elementsConst();
const uint64_t size = dtypep->elementsConst();
const uint32_t tabMod = tabModulus(dtypep->subDTypep());
// Note the double {{ initializer. The first { starts the initializer of the
// VlUnpacked, and the second starts the initializer of m_storage within the
@ -81,7 +81,7 @@ protected:
puts("{");
ofp()->putsNoTracking("{");
puts("\n");
for (vluint64_t n = 0; n < size; ++n) {
for (uint64_t n = 0; n < size; ++n) {
m_unpackedWord = n;
if (n) puts((n % tabMod) ? ", " : ",\n");
iterate(nodep->getIndexDefaultedValuep(n));

View File

@ -543,7 +543,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
}
for (int word = VL_WORDS_I(upWidth) - 1; word >= 0; word--) {
// Only 32 bits - llx + long long here just to appease CPP format warning
ofp()->printf(",0x%08" PRIx64, static_cast<vluint64_t>(nodep->num().edataWord(
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
word + chunks * EMITC_NUM_CONSTW)));
}
puts(")");
@ -565,7 +565,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
}
for (int word = EMITC_NUM_CONSTW - 1; word >= 0; word--) {
// Only 32 bits - llx + long long here just to appease CPP format warning
ofp()->printf(",0x%08" PRIx64, static_cast<vluint64_t>(nodep->num().edataWord(
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
word + chunks * EMITC_NUM_CONSTW)));
}
puts(")");
@ -580,7 +580,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
ofp()->printf("%.17e", nodep->num().toDouble());
}
} else if (nodep->isQuad()) {
const vluint64_t num = nodep->toUQuad();
const uint64_t num = nodep->toUQuad();
if (num < 10) {
ofp()->printf("%" PRIu64 "ULL", num);
} else {
@ -592,7 +592,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (num < 10) {
puts(cvtToStr(num));
} else {
ofp()->printf("0x%" PRIx64, static_cast<vluint64_t>(num));
ofp()->printf("0x%" PRIx64, static_cast<uint64_t>(num));
}
// If signed, we'll do our own functions
// But must be here, or <= comparisons etc may end up signed

View File

@ -406,7 +406,7 @@ public:
virtual void visit(AstCMethodHard* nodep) override {
iterate(nodep->fromp());
puts(".");
puts(nodep->nameProtect());
puts(nodep->name());
puts("(");
bool comma = false;
for (AstNode* subnodep = nodep->pinsp(); subnodep; subnodep = subnodep->nextp()) {
@ -1137,7 +1137,7 @@ public:
} else if (nodep->isWide()) {
UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp");
emitConstant(nodep, m_wideTempRefp, "");
m_wideTempRefp = nullptr; // We used it, barf if set it a second time
m_wideTempRefp = nullptr; // We used it, fail if set it a second time
} else {
emitConstant(nodep, nullptr, "");
}
@ -1202,11 +1202,9 @@ public:
emitVarReset(varp);
}
virtual void visit(AstExecGraph* nodep) override {
UASSERT_OBJ(nodep == v3Global.rootp()->execGraphp(), nodep,
"ExecGraph should be a singleton!");
// The location of the AstExecGraph within the containing _eval()
// function is where we want to invoke the graph and wait for it to
// complete. Emitting the children does just that.
// The location of the AstExecGraph within the containing AstCFunc is where we want to
// invoke the graph and wait for it to complete. Emitting the children does just that.
UASSERT_OBJ(!nodep->mTaskBodiesp(), nodep, "These should have been lowered");
iterateChildrenConst(nodep);
}
virtual void visit(AstChangeDet* nodep) override { //

View File

@ -118,7 +118,7 @@ class EmitCHeader final : public EmitCConstInit {
void emitInternalVarDecls(const AstNodeModule* modp) {
if (!VN_IS(modp, Class)) {
putsDecoration("\n// INTERNAL VARIABLES\n");
puts(symClassName() + "* vlSymsp; // Symbol table\n");
puts(symClassName() + "* const vlSymsp;\n");
}
}
void emitParamDecls(const AstNodeModule* modp) {
@ -146,7 +146,7 @@ class EmitCHeader final : public EmitCConstInit {
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
const string& name = prefixNameProtect(modp);
putsDecoration("\n// CONSTRUCTORS\n");
puts(name + "(const char* name);\n");
puts(name + "(" + symClassName() + "* symsp, const char* name);\n");
puts("~" + name + "();\n");
puts("VL_UNCOPYABLE(" + name + ");\n");
}
@ -157,8 +157,7 @@ class EmitCHeader final : public EmitCConstInit {
if (!VN_IS(modp, Class)) {
decorateFirst(first, section);
puts("void " + protect("__Vconfigure") + "(" + symClassName()
+ "* symsp, bool first);\n");
puts("void " + protect("__Vconfigure") + "(bool first);\n");
}
if (v3Global.opt.coverage()) {

View File

@ -63,7 +63,7 @@ class EmitCGatherDependencies final : VNVisitor {
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
"Unknown self pointer: '" << selfPointer << "'");
// Dereferencing vlSymsp, so we need it's definition...
m_dependencies.insert(EmitCBaseVisitor::symClassName());
addSymsDependency();
}
}
@ -117,9 +117,7 @@ class EmitCGatherDependencies final : VNVisitor {
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeSimpleText* nodep) override {
if (nodep->text().find("vlSymsp") != string::npos) {
m_dependencies.insert(EmitCBaseVisitor::symClassName());
}
if (nodep->text().find("vlSymsp") != string::npos) addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
@ -236,8 +234,8 @@ class EmitCImp final : EmitCFunc {
"(" + modName + "* vlSelf);");
puts("\n");
puts(modName + "::" + modName + "(const char* _vcname__)\n");
puts(" : VerilatedModule(_vcname__)\n");
puts(modName + "::" + modName + "(" + symClassName() + "* symsp, const char* name)\n");
puts(" : VerilatedModule{name}\n");
ofp()->indentInc();
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
@ -260,6 +258,7 @@ class EmitCImp final : EmitCFunc {
}
}
}
puts(", vlSymsp{symsp}\n");
ofp()->indentDec();
puts(" {\n");
@ -279,10 +278,8 @@ class EmitCImp final : EmitCFunc {
"(" + modName + "* vlSelf, bool first);");
}
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName()
+ "* _vlSymsp, bool first) {\n");
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(bool first) {\n");
puts("if (false && first) {} // Prevent unused\n");
puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it.
if (v3Global.opt.coverage()) {
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
}
@ -353,8 +350,8 @@ class EmitCImp final : EmitCFunc {
hash.insert(varp->dtypep()->width());
}
}
ofp()->printf("vluint64_t __Vcheckval = 0x%" PRIx64 "ULL;\n",
static_cast<vluint64_t>(hash.digestUInt64()));
ofp()->printf("uint64_t __Vcheckval = 0x%" PRIx64 "ULL;\n",
static_cast<uint64_t>(hash.digestUInt64()));
if (de) {
puts("os.readAssert(__Vcheckval);\n");
} else {
@ -786,7 +783,7 @@ class EmitCTrace final : EmitCFunc {
AstVar* const varp = varrefp->varp();
puts("(");
if (emitTraceIsScBigUint(nodep)) {
puts("(vluint32_t*)");
puts("(uint32_t*)");
} else if (emitTraceIsScBv(nodep)) {
puts("VL_SC_BV_DATAP(");
}

View File

@ -178,6 +178,9 @@ class CMakeEmitter final {
if (v3Global.opt.mtasks()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
}
if (v3Global.opt.usesProfiler()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
}
if (!v3Global.opt.libCreate().empty()) {
global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.libCreate() + ".cpp");
}

View File

@ -330,21 +330,14 @@ class EmitCModel final : public EmitCFunc {
if (initial)
puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n");
const string recName = "__Vprfloop";
if (v3Global.opt.profThreads() && !initial) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEvalLoop(VL_RDTSC_Q());\n");
puts("}\n");
if (v3Global.opt.profExec() && !initial) {
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalLoopBegin();\n");
}
puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n");
if (v3Global.opt.profThreads() && !initial) {
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
if (v3Global.opt.profExec() && !initial) {
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalLoopEnd();\n");
}
if (v3Global.rootp()->changeRequest()) {
@ -434,61 +427,9 @@ class EmitCModel final : public EmitCFunc {
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
}
if (v3Global.opt.profThreads()) {
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
"vlSymsp->__Vm_profile_time_finished)\n");
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
// Within a profile (either starting, middle, or end)
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
puts(/**/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile start warmup\\n\"););\n");
// Start profile on this cycle. We'll capture a window worth, then
// only analyze the next window worth. The idea is that the first window
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
// by the time we hit the second window, we hope.
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
// "* 2" as first half is warmup, second half is collection
puts(/****/ "vlSymsp->__Vm_profile_window_ct"
" = vlSymsp->_vm_contextp__->profThreadsWindow()"
" * 2 + 1;\n");
puts(/**/ "}\n");
puts(/**/ "--(vlSymsp->__Vm_profile_window_ct);\n");
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct"
" == vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
// This barrier record in every threads' profile demarcates the
// cache-warm-up cycles before the barrier from the actual profile
// cycles afterward.
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileAppendAll(");
puts(/****/ "VlProfileRec{VlProfileRec::Barrier{}});\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts(/**/ "}\n");
// Ending trace file?
puts(/**/ "else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
puts(/****/ "vluint64_t tick_end = VL_RDTSC_Q();\n");
puts(/****/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile end\\n\"););\n");
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileDump("
"vlSymsp->_vm_contextp__->profThreadsFilename().c_str(), "
"vlSymsp->__Vm_profile_cycle_start, "
"tick_end);\n");
// This turns off the test to enter the profiling code, but still
// allows the user to collect another profile by changing
// profThreadsStart
puts(/****/ "vlSymsp->__Vm_profile_time_finished = "
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = 0;\n");
puts(/**/ "}\n");
puts("}\n");
}
const string recName = "__Vprfeval";
if (v3Global.opt.profThreads()) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEval(VL_RDTSC_Q());\n");
puts("}\n");
if (v3Global.opt.profExec()) {
puts("vlSymsp->__Vm_executionProfiler.configure(*(vlSymsp->_vm_contextp__));\n");
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalBegin();\n");
}
emitSettleLoop(modp, /* initial: */ false);
@ -499,10 +440,7 @@ class EmitCModel final : public EmitCFunc {
}
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
if (v3Global.opt.profThreads()) {
// End eval record
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
}
if (v3Global.opt.profExec()) puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalEnd();\n");
puts("}\n");
}

View File

@ -395,7 +395,7 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.needTraceDumper()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
if (v3Global.opt.profThreads()) puts("#include \"verilated_profiler.h\"\n");
if (v3Global.opt.usesProfiler()) puts("#include \"verilated_profiler.h\"\n");
puts("\n// INCLUDE MODEL CLASS\n");
puts("\n#include \"" + topClassName() + ".h\"\n");
@ -445,18 +445,15 @@ void EmitCSyms::emitSymHdr() {
}
puts("bool __Vm_didInit = false;\n");
if (v3Global.opt.profExec()) {
puts("\n// EXECUTION PROFILING\n");
puts("VlExecutionProfiler __Vm_executionProfiler;\n");
}
if (v3Global.opt.mtasks()) {
puts("\n// MULTI-THREADING\n");
puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle = false;\n");
if (v3Global.opt.profThreads()) {
// rdtsc() at current cycle start
puts("vluint64_t __Vm_profile_cycle_start = 0;\n");
// Time we finished analysis
puts("vluint64_t __Vm_profile_time_finished = 0;\n");
// Track our position in the cache warmup and actual profile window
puts("vluint32_t __Vm_profile_window_ct = 0;\n");
}
}
puts("\n// MODULE INSTANCE STATE\n");
@ -477,20 +474,10 @@ void EmitCSyms::emitSymHdr() {
puts("];\n");
}
if (v3Global.opt.profThreads()) {
puts("\n// PROFILING\n");
vluint64_t maxProfilerId = 0;
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
const ExecMTask* const mtp
= dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
if (maxProfilerId < mtp->profilerId()) maxProfilerId = mtp->profilerId();
}
}
++maxProfilerId; // As size must include 0
puts("VerilatedProfiler<" + cvtToStr(maxProfilerId) + "> _vm_profiler;\n");
if (v3Global.opt.profPgo()) {
puts("\n// PGO PROFILING\n");
const uint32_t usedMTaskProfilingIDs = v3Global.rootp()->usedMTaskProfilingIDs();
puts("VlPgoProfiler<" + cvtToStr(usedMTaskProfilingIDs) + "> _vm_pgoProfiler;\n");
}
if (!m_scopeNames.empty()) { // Scope names
@ -682,8 +669,8 @@ void EmitCSyms::emitSymImp() {
puts("if (__Vm_dumping) _traceDumpClose();\n");
puts("#endif // VM_TRACE\n");
}
if (v3Global.opt.profThreads()) {
puts("_vm_profiler.write(\"" + topClassName()
if (v3Global.opt.profPgo()) {
puts("_vm_pgoProfiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n");
}
if (v3Global.opt.mtasks()) puts("delete __Vm_threadPoolp;\n");
@ -719,8 +706,8 @@ void EmitCSyms::emitSymImp() {
// that calls eval() becomes the final Nth thread for the
// duration of the eval call.
puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, "
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads())
+ "}}\n");
+ cvtToStr(v3Global.opt.threads() - 1) + ", "
+ (v3Global.opt.profExec() ? "&__Vm_executionProfiler" : "nullptr") + "}}\n");
}
puts(" // Setup module instances\n");
@ -729,28 +716,32 @@ void EmitCSyms::emitSymImp() {
const AstNodeModule* const modp = i.second;
puts(" , ");
puts(protect(scopep->nameDotless()));
puts("{this");
if (modp->isTop()) {
puts("(namep)\n");
puts(", namep");
} else {
// The "." is added by catName
puts("(Verilated::catName(namep, ");
puts(", Verilated::catName(namep, ");
putsQuoted(protectWordsIf(scopep->prettyName(), scopep->protect()));
puts("))\n");
puts(")");
}
puts("}\n");
++m_numStmts;
}
puts("{\n");
if (v3Global.opt.profThreads()) {
puts("// Configure profiling\n");
if (v3Global.opt.profPgo()) {
puts("// Configure profiling for PGO\n");
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
puts("_vm_profiler.addCounter(" + cvtToStr(mtp->profilerId()) + ", \""
+ mtp->hashName() + "\");\n");
}
v3Global.rootp()->topModulep()->foreach<AstExecGraph>(
[&](const AstExecGraph* execGraphp) {
for (const V3GraphVertex* vxp = execGraphp->depGraphp()->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
const ExecMTask* const mtp = static_cast<const ExecMTask*>(vxp);
puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mtp->profilerId()) + ", \""
+ mtp->hashName() + "\");\n");
}
});
}
}
@ -796,7 +787,7 @@ void EmitCSyms::emitSymImp() {
const bool first = !modp->user1();
modp->user1(true);
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect("__Vconfigure")
+ "(this, " + (first ? "true" : "false") + ");\n");
+ "(" + (first ? "true" : "false") + ");\n");
++m_numStmts;
}

View File

@ -112,6 +112,9 @@ public:
}
}
if (v3Global.opt.mtasks()) putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
putMakeClassEntry(of, "verilated_profiler.cpp");
}
} else if (support == 2 && slow) {
} else {
for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep;
@ -189,6 +192,7 @@ public:
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
of.puts("VM_USER_CFLAGS = \\\n");
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
if (v3Global.opt.usesProfiler()) of.puts("\t-DVL_PROFILER \\\n");
const V3StringList& cFlags = v3Global.opt.cFlags();
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
of.puts("\n");
@ -244,8 +248,8 @@ public:
}
if (!v3Global.opt.libCreate().empty()) {
const string libCreateDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) " + v3Global.opt.libCreate()
+ ".o $(VM_HIER_LIBS)";
const string libCreateDeps = "$(VK_OBJS) $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) "
+ v3Global.opt.libCreate() + ".o $(VM_HIER_LIBS)";
of.puts("\n### Library rules from --lib-create\n");
// The rule to create .a is defined in verilated.mk, so just define dependency here.
of.puts(v3Global.opt.libCreateName(false) + ": " + libCreateDeps + "\n");
@ -329,8 +333,8 @@ class EmitMkHierVerilation final {
const V3HierBlockPlan::HierVector blocks
= m_planp->hierBlocksSorted(); // leaf comes first
// List in order of leaf-last order so that linker can resolve dependency
for (auto it = blocks.rbegin(); it != blocks.rend(); ++it) {
of.puts("\t" + (*it)->hierLib(true) + " \\\n");
for (auto& block : vlstd::reverse_view(blocks)) {
of.puts("\t" + block->hierLib(true) + " \\\n");
}
of.puts("\n");

View File

@ -52,10 +52,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
// VISITORS
virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); }
virtual void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
virtual void visit(AstNodeModule* nodep) override {
putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end" + nodep->verilogKwd() + "\n");
}
virtual void visit(AstPort* nodep) override {}
@ -65,7 +65,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(nodep->prettyName());
puts(";\n");
// Only putfs the first time for each visitor; later for same node is putqs
iterateAndNextNull(nodep->stmtsp());
iterateAndNextConstNull(nodep->stmtsp());
putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n");
}
@ -75,7 +75,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
} else {
putbs("begin : " + nodep->name() + "\n");
}
iterateChildren(nodep);
iterateChildrenConst(nodep);
puts("end\n");
}
virtual void visit(AstFork* nodep) override {
@ -84,75 +84,76 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
} else {
putbs("fork : " + nodep->name() + "\n");
}
iterateChildren(nodep);
iterateChildrenConst(nodep);
puts(nodep->joinType().verilogKwd());
puts("\n");
}
virtual void visit(AstFinal* nodep) override {
putfs(nodep, "final begin\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstInitial* nodep) override {
putfs(nodep, "initial begin\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstInitialAutomatic* nodep) override { iterateChildren(nodep); }
virtual void visit(AstInitialAutomatic* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstInitialStatic* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstAlways* nodep) override {
putfs(nodep, "always ");
if (m_sensesp) {
iterateAndNextNull(m_sensesp);
iterateAndNextConstNull(m_sensesp);
} // In active
else {
iterateAndNextNull(nodep->sensesp());
iterateAndNextConstNull(nodep->sensesp());
}
putbs(" begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstAlwaysPublic* nodep) override {
putfs(nodep, "/*verilator public_flat_rw ");
if (m_sensesp) {
iterateAndNextNull(m_sensesp);
iterateAndNextConstNull(m_sensesp);
} // In active
else {
iterateAndNextNull(nodep->sensesp());
iterateAndNextConstNull(nodep->sensesp());
}
putqs(nodep, " ");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "*/\n");
}
virtual void visit(AstNodeAssign* nodep) override {
if (VN_IS(nodep, AssignForce)) puts("force ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " " + nodep->verilogKwd() + " ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep) override {
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " <= ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
puts(";\n");
}
virtual void visit(AstAssignAlias* nodep) override {
putbs("alias ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " = ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignW* nodep) override {
putfs(nodep, "assign ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putbs(" = ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstRelease* nodep) override {
puts("release ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstBreak*) override {
@ -172,7 +173,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, "");
puts(nodep->edgeType().verilogKwd());
if (nodep->sensp()) puts(" ");
iterateChildren(nodep);
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeCase* nodep) override {
putfs(nodep, "");
@ -183,7 +184,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
puts(nodep->verilogKwd());
puts(" (");
iterateAndNextNull(nodep->exprp());
iterateAndNextConstNull(nodep->exprp());
puts(")\n");
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
if (casep->fullPragma() || casep->parallelPragma()) {
@ -192,22 +193,22 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
if (casep->parallelPragma()) puts(" parallel_case");
}
}
iterateAndNextNull(nodep->itemsp());
iterateAndNextConstNull(nodep->itemsp());
putqs(nodep, "endcase\n");
}
virtual void visit(AstCaseItem* nodep) override {
if (nodep->condsp()) {
iterateAndNextNull(nodep->condsp());
iterateAndNextConstNull(nodep->condsp());
} else {
putbs("default");
}
putfs(nodep, ": begin ");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstComment* nodep) override {
puts(string("// ") + nodep->name() + "\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
}
virtual void visit(AstContinue*) override {
putbs("continue");
@ -222,13 +223,13 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (fileOrStrgp) {
iterateAndNextNull(fileOrStrgp);
iterateAndNextConstNull(fileOrStrgp);
putbs(", ");
}
putsQuoted(text);
for (AstNode* expp = exprsp; expp; expp = expp->nextp()) {
puts(", ");
iterateAndNextNull(expp);
iterateAndNextConstNull(expp);
}
puts(");\n");
}
@ -254,32 +255,32 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstFOpen* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->filenamep());
iterateAndNextConstNull(nodep->filenamep());
putbs(", ");
iterateAndNextNull(nodep->modep());
iterateAndNextConstNull(nodep->modep());
puts(");\n");
}
virtual void visit(AstFOpenMcd* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->filenamep());
iterateAndNextConstNull(nodep->filenamep());
puts(");\n");
}
virtual void visit(AstFClose* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filep()) iterateAndNextNull(nodep->filep());
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstFFlush* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filep()) iterateAndNextNull(nodep->filep());
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstJumpBlock* nodep) override {
putbs("begin : label" + cvtToStr(nodep->labelNum()) + "\n");
if (nodep->stmtsp()) iterateAndNextNull(nodep->stmtsp());
if (nodep->stmtsp()) iterateAndNextConstNull(nodep->stmtsp());
puts("end\n");
}
virtual void visit(AstJumpGo* nodep) override {
@ -291,27 +292,27 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstNodeReadWriteMem* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
if (nodep->filenamep()) iterateAndNextConstNull(nodep->filenamep());
putbs(", ");
if (nodep->memp()) iterateAndNextNull(nodep->memp());
if (nodep->memp()) iterateAndNextConstNull(nodep->memp());
if (nodep->lsbp()) {
putbs(", ");
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
}
if (nodep->msbp()) {
putbs(", ");
iterateAndNextNull(nodep->msbp());
iterateAndNextConstNull(nodep->msbp());
}
puts(");\n");
}
virtual void visit(AstSysFuncAsTask* nodep) override {
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstSysIgnore* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->exprsp());
iterateAndNextConstNull(nodep->exprsp());
puts(");\n");
}
virtual void visit(AstNodeFor* nodep) override {
@ -319,31 +320,31 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
{
VL_RESTORER(m_suppressSemi);
m_suppressSemi = true;
iterateAndNextNull(nodep->initsp());
iterateAndNextConstNull(nodep->initsp());
puts(";");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(";");
iterateAndNextNull(nodep->incsp());
iterateAndNextConstNull(nodep->incsp());
}
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstRepeat* nodep) override {
putfs(nodep, "repeat (");
iterateAndNextNull(nodep->countp());
iterateAndNextConstNull(nodep->countp());
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putfs(nodep, "end\n");
}
virtual void visit(AstWhile* nodep) override {
iterateAndNextNull(nodep->precondsp());
iterateAndNextConstNull(nodep->precondsp());
putfs(nodep, "while (");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->incsp());
iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop
iterateAndNextConstNull(nodep->bodysp());
iterateAndNextConstNull(nodep->incsp());
iterateAndNextConstNull(nodep->precondsp()); // Need to recompute before next loop
putfs(nodep, "end\n");
}
virtual void visit(AstNodeIf* nodep) override {
@ -354,28 +355,28 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
if (ifp->unique0Pragma()) puts("unique0 ");
}
puts("if (");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(") begin\n");
iterateAndNextNull(nodep->ifsp());
iterateAndNextConstNull(nodep->ifsp());
if (nodep->elsesp()) {
putqs(nodep, "end\n");
putqs(nodep, "else begin\n");
iterateAndNextNull(nodep->elsesp());
iterateAndNextConstNull(nodep->elsesp());
}
putqs(nodep, "end\n");
}
virtual void visit(AstPast* nodep) override {
putfs(nodep, "$past(");
iterateAndNextNull(nodep->exprp());
iterateAndNextConstNull(nodep->exprp());
if (nodep->ticksp()) {
puts(", ");
iterateAndNextNull(nodep->ticksp());
iterateAndNextConstNull(nodep->ticksp());
}
puts(")");
}
virtual void visit(AstReturn* nodep) override {
putfs(nodep, "return ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstStop* nodep) override { putfs(nodep, "$stop;\n"); }
@ -401,22 +402,22 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstScopeName* nodep) override {}
virtual void visit(AstCStmt* nodep) override {
putfs(nodep, "$_CSTMT(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstCMath* nodep) override {
putfs(nodep, "$_CMATH(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCStmt* nodep) override {
putfs(nodep, "$c(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCFunc* nodep) override {
putfs(nodep, "$c(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(")");
}
@ -450,27 +451,27 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
case 'k': putbs(""); break;
case 'l': {
UASSERT_OBJ(lhsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(lhsp);
iterateAndNextConstNull(lhsp);
break;
}
case 'r': {
UASSERT_OBJ(rhsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(rhsp);
iterateAndNextConstNull(rhsp);
break;
}
case 't': {
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(thsp);
iterateAndNextConstNull(thsp);
break;
}
case 'o': {
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(fhsp);
iterateAndNextConstNull(fhsp);
break;
}
case 'd': {
UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node");
iterateAndNextNull(nodep->dtypep());
iterateAndNextConstNull(nodep->dtypep());
break;
}
default: nodep->v3fatalSrc("Unknown emitVerilog format code: %" << c); break;
@ -494,10 +495,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstAttrOf* nodep) override {
putfs(nodep, "$_ATTROF(");
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
if (nodep->dimp()) {
putbs(", ");
iterateAndNextNull(nodep->dimp());
iterateAndNextConstNull(nodep->dimp());
}
puts(")");
}
@ -516,11 +517,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstNodeCond* nodep) override {
putbs("(");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
putfs(nodep, " ? ");
iterateAndNextNull(nodep->expr1p());
iterateAndNextConstNull(nodep->expr1p());
putbs(" : ");
iterateAndNextNull(nodep->expr2p());
iterateAndNextConstNull(nodep->expr2p());
puts(")");
}
virtual void visit(AstRange* nodep) override {
@ -532,21 +533,21 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(cvtToStr(nodep->rightConst()));
puts("]");
} else {
iterateAndNextNull(nodep->leftp());
iterateAndNextConstNull(nodep->leftp());
puts(":");
iterateAndNextNull(nodep->rightp());
iterateAndNextConstNull(nodep->rightp());
puts("]");
}
}
virtual void visit(AstSel* nodep) override {
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
puts("[");
if (VN_IS(nodep->lsbp(), Const)) {
if (nodep->widthp()->isOne()) {
if (VN_IS(nodep->lsbp(), Const)) {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
} else {
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
}
} else {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()
@ -555,20 +556,20 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
}
} else {
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
putfs(nodep, "+:");
iterateAndNextNull(nodep->widthp());
iterateAndNextConstNull(nodep->widthp());
puts("]");
}
puts("]");
}
virtual void visit(AstSliceSel* nodep) override {
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
puts(cvtToStr(nodep->declRange()));
}
virtual void visit(AstTypedef* nodep) override {
putfs(nodep, "typedef ");
iterateAndNextNull(nodep->dtypep());
iterateAndNextConstNull(nodep->dtypep());
puts(" ");
puts(nodep->prettyName());
puts(";\n");
@ -578,7 +579,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->prettyName());
if (nodep->rangep()) {
puts(" ");
iterateAndNextNull(nodep->rangep());
iterateAndNextConstNull(nodep->rangep());
puts(" ");
} else if (nodep->isRanged()) {
puts(" [");
@ -592,14 +593,14 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstNodeArrayDType* nodep) override {
iterate(nodep->subDTypep());
iterateAndNextNull(nodep->rangep());
iterateAndNextConstNull(nodep->rangep());
}
virtual void visit(AstNodeUOrStructDType* nodep) override {
puts(nodep->verilogKwd() + " ");
if (nodep->packed()) puts("packed ");
puts("\n");
puts("{");
iterateAndNextNull(nodep->membersp());
iterateAndNextConstNull(nodep->membersp());
puts("}");
}
virtual void visit(AstMemberDType* nodep) override {
@ -616,10 +617,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->prettyName());
}
puts("(");
iterateAndNextNull(nodep->pinsp());
iterateAndNextConstNull(nodep->pinsp());
puts(")");
}
virtual void visit(AstArg* nodep) override { iterateAndNextNull(nodep->exprp()); }
virtual void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
virtual void visit(AstPrintTimeScale* nodep) override {
puts(nodep->verilogKwd());
puts(";\n");
@ -654,8 +655,8 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstConst* nodep) override { putfs(nodep, nodep->num().ascii(true, true)); }
// Just iterate
virtual void visit(AstTopScope* nodep) override { iterateChildren(nodep); }
virtual void visit(AstScope* nodep) override { iterateChildren(nodep); }
virtual void visit(AstTopScope* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstScope* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstVar* nodep) override {
if (nodep->isIO()) {
putfs(nodep, nodep->verilogKwd());
@ -686,7 +687,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstActive* nodep) override {
m_sensesp = nodep->sensesp();
iterateAndNextNull(nodep->stmtsp());
iterateAndNextConstNull(nodep->stmtsp());
m_sensesp = nullptr;
}
virtual void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
@ -700,7 +701,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
// Default
virtual void visit(AstNode* nodep) override {
puts(string("\n???? // ") + nodep->prettyTypeName() + "\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
// Not v3fatalSrc so we keep processing
if (!m_suppressUnknown) {
nodep->v3error(
@ -754,10 +755,10 @@ class EmitVStreamVisitor final : public EmitVBaseVisitor {
virtual void putqs(AstNode*, const string& str) override { putbs(str); }
public:
EmitVStreamVisitor(AstNode* nodep, std::ostream& os)
EmitVStreamVisitor(const AstNode* nodep, std::ostream& os)
: EmitVBaseVisitor{false, nullptr}
, m_os(os) { // Need () or GCC 4.8 false warning
iterate(nodep);
iterate(const_cast<AstNode*>(nodep));
}
virtual ~EmitVStreamVisitor() override = default;
};
@ -828,12 +829,12 @@ class EmitVPrefixedVisitor final : public EmitVBaseVisitor {
}
public:
EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
EmitVPrefixedVisitor(const AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
AstSenTree* domainp, bool user3mark)
: EmitVBaseVisitor{false, domainp}
, m_formatter{os, prefix, flWidth} {
if (user3mark) VNUser3InUse::check();
iterate(nodep);
iterate(const_cast<AstNode*>(nodep));
}
virtual ~EmitVPrefixedVisitor() override = default;
};
@ -841,11 +842,11 @@ public:
//######################################################################
// EmitV class functions
void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) {
void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) {
{ EmitVStreamVisitor{nodep, os}; }
}
void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix,
void V3EmitV::verilogPrefixedTree(const AstNode* nodep, std::ostream& os, const string& prefix,
int flWidth, AstSenTree* domainp, bool user3mark) {
{ EmitVPrefixedVisitor{nodep, os, prefix, flWidth, domainp, user3mark}; }
}

View File

@ -27,8 +27,8 @@ class AstSenTree;
class V3EmitV final {
public:
static void verilogForTree(AstNode* nodep, std::ostream& os = std::cout);
static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix,
static void verilogForTree(const AstNode* nodep, std::ostream& os = std::cout);
static void verilogPrefixedTree(const AstNode* nodep, std::ostream& os, const string& prefix,
int flWidth, AstSenTree* domainp, bool user3mark);
static void emitvFiles();
static void debugEmitV(const string& filename);

View File

@ -183,7 +183,7 @@ void V3Error::suppressThisWarning() {
string V3Error::warnMore() { return string(msgPrefix().size(), ' '); }
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
#if defined(__COVERITY__) || defined(__cppcheck__)
if (s_errorCode == V3ErrorCode::EC_FATAL) __coverity_panic__(x);
#endif
@ -209,10 +209,10 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
// Suppress duplicate messages
if (s_messages.find(msg) != s_messages.end()) return;
s_messages.insert(msg);
if (!locationStr.empty()) {
const string locationMsg = warnMore() + locationStr + "\n";
if (!extra.empty()) {
const string extraMsg = warnMore() + extra + "\n";
const size_t pos = msg.find('\n');
msg.insert(pos + 1, locationMsg);
msg.insert(pos + 1, extraMsg);
}
// Output
if (

View File

@ -313,7 +313,7 @@ public:
static void vlAbortOrExit();
static void vlAbort();
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& locationStr = "");
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "");
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.

View File

@ -340,15 +340,15 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
}
}
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) {
std::ostringstream nsstr;
if (lastLineno()) nsstr << this;
nsstr << sstr.str();
nsstr << endl;
std::ostringstream lstr;
if (!locationStr.empty()) {
if (!extra.empty()) {
lstr << std::setw(ascii().length()) << " "
<< ": " << locationStr;
<< ": " << extra;
}
m_waive = V3Config::waive(this, V3Error::errorCode(), sstr.str());
if (warnIsOff(V3Error::errorCode()) || m_waive) {

View File

@ -245,7 +245,7 @@ public:
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); }
// OPERATORS
void v3errorEnd(std::ostringstream& str, const string& locationStr = "");
void v3errorEnd(std::ostringstream& str, const string& extra = "");
void v3errorEndFatal(std::ostringstream& str);
/// When building an error, prefix for printing continuation lines
/// e.g. information referring to the same FileLine as before

View File

@ -23,6 +23,8 @@
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
int V3Graph::s_debug = 0;
@ -325,11 +327,19 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
*logp << "\t\t rankdir=" << dotRankDir() << "];\n";
// List of all possible subgraphs
// Collections of explicit ranks
std::unordered_set<std::string> ranks;
std::unordered_multimap<std::string, V3GraphVertex*> rankSets;
std::multimap<std::string, V3GraphVertex*> subgraphs;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
const string vertexSubgraph
= (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
subgraphs.emplace(vertexSubgraph, vertexp);
const string& dotRank = vertexp->dotRank();
if (!dotRank.empty()) {
ranks.emplace(dotRank);
rankSets.emplace(dotRank, vertexp);
}
}
// We use a map here, as we don't want to corrupt anything (userp) in the graph,
@ -346,7 +356,10 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
if (subgr != vertexSubgraph) {
if (subgr != "") *logp << "\t};\n";
subgr = vertexSubgraph;
if (subgr != "") *logp << "\tsubgraph cluster_" << subgr << " {\n";
if (subgr != "") {
*logp << "\tsubgraph cluster_" << subgr << " {\n";
*logp << "\tlabel=\"" << subgr << "\"\n";
}
}
if (subgr != "") *logp << "\t";
*logp << "\tn" << vertexp->dotName() << (n++) << "\t[fontsize=8 "
@ -382,6 +395,24 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
}
}
}
// Print ranks
for (auto dotRank : ranks) {
*logp << "\t{ rank=";
if (dotRank != "sink" && dotRank != "source" && dotRank != "min" && dotRank != "max") {
*logp << "same";
} else {
*logp << dotRank;
}
*logp << "; ";
auto bounds = rankSets.equal_range(dotRank);
for (auto it{bounds.first}; it != bounds.second; ++it) {
if (it != bounds.first) *logp << ", ";
*logp << 'n' << numMap[it->second] << "";
}
*logp << " }\n";
}
// Vertex::m_user end, now unused
// Trailer

View File

@ -218,6 +218,7 @@ public:
virtual string dotShape() const { return ""; }
virtual string dotStyle() const { return ""; }
virtual string dotName() const { return ""; }
virtual string dotRank() const { return ""; }
virtual uint32_t rankAdder() const { return 1; }
virtual FileLine* fileline() const { return nullptr; } // nullptr for unknown
virtual int sortCmp(const V3GraphVertex* rhsp) const {

View File

@ -32,13 +32,13 @@ struct GraphPCNode {
//
// Unlike the LogicMTasks's, we have no cost info for the generic graph
// accepted by GraphPathChecker, so assume each node has unit cost.
std::array<vluint32_t, GraphWay::NUM_WAYS> m_cp;
std::array<uint32_t, GraphWay::NUM_WAYS> m_cp;
// Detect if we've seen this node before in a given recursive
// operation. We'll use this in pathExistsInternal() to avoid checking
// the same node twice, and again in updateHalfCriticalPath() to assert
// there are no cycles.
vluint64_t m_seenAtGeneration = 0;
uint64_t m_seenAtGeneration = 0;
// CONSTRUCTORS
GraphPCNode() {

View File

@ -34,7 +34,7 @@ class GraphPathChecker final : GraphAlg<const V3Graph> {
// the graph. Each node is marked with the last generation that scanned
// it, to enable asserting there are no cycles, and to avoid recursing
// through the same node twice while searching for a path.
vluint64_t m_generation = 0;
uint64_t m_generation = 0;
public:
// CONSTRUCTORS

View File

@ -299,9 +299,8 @@ private:
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstNodeModule* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { //
m_hash += nodep->origName();
m_hash += nodep->hierName();
});
}
virtual void visit(AstNodePreSel* nodep) override {

View File

@ -190,8 +190,7 @@ private:
// Iterate through all modules in bottom-up order.
// Make a final inlining decision for each.
for (auto it = m_allMods.rbegin(); it != m_allMods.rend(); ++it) {
AstNodeModule* const modp = *it;
for (AstNodeModule* const modp : vlstd::reverse_view(m_allMods)) {
// If we're going to inline some modules into this one,
// update user4 (statement count) to reflect that:
@ -647,7 +646,11 @@ private:
m_scope += "__DOT__" + nodep->name();
}
if (AstModule* const modp = VN_CAST(nodep->modp(), Module)) {
if (VN_IS(nodep->modp(), Iface)) {
nodep->addIntfRefp(new AstIntfRef{nodep->fileline(), m_scope});
}
{
AstNodeModule* const modp = nodep->modp();
// Pass Cell pointers down to the next module
for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
AstVar* const varp = pinp->modVarp();
@ -666,9 +669,6 @@ private:
}
iterateChildren(modp);
} else if (VN_IS(nodep->modp(), Iface)) {
nodep->addIntfRefp(new AstIntfRef(nodep->fileline(), m_scope));
// No need to iterate on interface cells
}
}
virtual void visit(AstAssignVarScope* nodep) override {

View File

@ -178,7 +178,7 @@ public:
}
}
void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) {
// Variable rvalue. If it references a constant, we can simply replace it
// Variable rvalue. If it references a constant, we can replace it
const auto it = m_map.find(nodep);
if (it != m_map.end()) {
if (AstConst* const constp = it->second.constNodep()) {
@ -302,7 +302,7 @@ private:
virtual void visit(AstNodeAssign* nodep) override {
// Collect any used variables first, as lhs may also be on rhs
// Similar code in V3Dead
const vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
const uint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
m_sideEffect = false;
iterateAndNextNull(nodep->rhsp());
if (lastEdit != AstNode::editCountGbl()) {

View File

@ -315,6 +315,7 @@ private:
}
virtual void visit(AstExecGraph* nodep) override {
// Treat the ExecGraph like a call to each mtask body
UASSERT_OBJ(!m_mtasksGraphp, nodep, "Cannot handle more than one AstExecGraph");
m_mtasksGraphp = nodep->depGraphp();
for (V3GraphVertex* mtaskVxp = m_mtasksGraphp->verticesBeginp(); mtaskVxp;
mtaskVxp = mtaskVxp->verticesNextp()) {

View File

@ -259,7 +259,7 @@ public:
// table's import wouldn't warn
} else if (VN_IS(nodep, Begin) && VN_IS(fnodep, Begin)
&& VN_AS(nodep, Begin)->generate()) {
// Begin: ... blocks often replicate under genif/genfor, so simply
// Begin: ... blocks often replicate under genif/genfor, so
// suppress duplicate checks. See t_gen_forif.v for an example.
} else {
UINFO(4, "name " << name << endl); // Not always same as nodep->name
@ -675,13 +675,14 @@ public:
<< ((lookupSymp->symPrefix() == "") ? "" : " as ")
<< ((lookupSymp->symPrefix() == "") ? "" : lookupSymp->symPrefix() + dotname)
<< " at se" << lookupSymp << endl);
const string prefix = lookupSymp->symPrefix();
string prefix = lookupSymp->symPrefix();
VSymEnt* foundp = nullptr;
while (!foundp) {
foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be nullptr
if (prefix.empty()) break;
const string nextPrefix = removeLastInlineScope(prefix);
if (prefix == nextPrefix) break;
prefix = nextPrefix;
}
if (!foundp) baddot = dotname;
return foundp;

View File

@ -263,10 +263,10 @@ private:
UINFO(8, " DISABLE " << nodep << endl);
iterateChildren(nodep);
AstNodeBlock* blockp = nullptr;
for (auto it = m_blockStack.rbegin(); it != m_blockStack.rend(); ++it) {
UINFO(9, " UNDERBLK " << *it << endl);
if ((*it)->name() == nodep->name()) {
blockp = *it;
for (AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) {
UINFO(9, " UNDERBLK " << stackp << endl);
if (stackp->name() == nodep->name()) {
blockp = stackp;
break;
}
}

View File

@ -275,7 +275,7 @@ private:
if (nodep->lifetime().isAutomatic()) {
nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
} else {
nodep->addNextHere(new AstInitial{newfl, assp});
nodep->addNextHere(new AstInitialStatic{newfl, assp});
}
} // 4. Under blocks, it's an initial value to be under an assign
else {

View File

@ -61,7 +61,7 @@ private:
// "this" must be a element inside of *basep
// Use that to determine a structure offset, then apply to the new base
// to get our new pointer information
return (V3ListEnt*)(((vluint8_t*)newbasep) + offset);
return (V3ListEnt*)(((uint8_t*)newbasep) + offset);
}
public:
@ -78,7 +78,7 @@ public:
void pushBack(V3List<T>& listr, T newp) {
// "this" must be a element inside of *newp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp);
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
m_nextp = nullptr;
if (!listr.m_headp) listr.m_headp = newp;
m_prevp = listr.m_tailp;
@ -88,7 +88,7 @@ public:
void pushFront(V3List<T>& listr, T newp) {
// "this" must be a element inside of *newp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp);
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
m_nextp = listr.m_headp;
if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = newp;
listr.m_headp = newp;
@ -99,7 +99,7 @@ public:
void unlink(V3List<T>& listr, T oldp) {
// "this" must be a element inside of *oldp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(oldp);
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(oldp);
if (m_nextp) {
baseToListEnt(m_nextp, offset)->m_prevp = m_prevp;
} else {

View File

@ -40,15 +40,15 @@ class LocalizeVisitor final : public VNVisitor {
private:
// NODE STATE
// AstVarScope::user1() -> Bool indicating VarScope is not optimizable.
// AstCFunc::user1() -> Bool indicating CFunc is not a leaf function.
// AstVarScope::user2() -> Bool indicating VarScope was fully assigned in the current
// function.
// AstVarScope::user3p() -> Set of CFuncs referencing this VarScope. (via m_accessors)
// AstCFunc::user4p() -> Multimap of 'VarScope -> VarRefs that reference that VarScope'
// in this function. (via m_references)
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
const VNUser4InUse m_inuser4;
const VNUser1InUse m_user1InUse;
const VNUser3InUse m_user3InUse;
const VNUser4InUse m_user4InUse;
AstUser3Allocator<AstVarScope, std::unordered_set<AstCFunc*>> m_accessors;
AstUser4Allocator<AstCFunc, std::unordered_multimap<const AstVarScope*, AstVarRef*>>
@ -69,6 +69,13 @@ private:
&& m_accessors(nodep).size() == 1); // .. a block temp used in a single CFunc
}
bool existsNonLeaf(const std::unordered_set<AstCFunc*>& funcps) {
for (const AstCFunc* const funcp : funcps) {
if (funcp->user1()) return true;
}
return false;
}
void moveVarScopes() {
for (AstVarScope* const nodep : m_varScopeps) {
if (!isOptimizable(nodep)) continue; // Not optimizable
@ -76,6 +83,12 @@ private:
const std::unordered_set<AstCFunc*>& funcps = m_accessors(nodep);
if (funcps.empty()) continue; // No referencing functions at all
// If more than one referencing function, but not all are leaf
// functions, then don't localize, as one of the referencing
// functions might be calling another, which the current analysis
// cannot cope with. This should be rare (introduced by V3Depth).
if (funcps.size() > 1 && existsNonLeaf(funcps)) continue;
UINFO(4, "Localizing " << nodep << endl);
++m_statLocVars;
@ -121,11 +134,16 @@ private:
{
m_cfuncp = nodep;
m_nodeDepth = 0;
AstNode::user2ClearTree(); // Check each function independently
const VNUser2InUse user2InUse;
iterateChildrenConst(nodep);
}
}
virtual void visit(AstCCall* nodep) override {
m_cfuncp->user1(true); // Mark caller as not a leaf function
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeAssign* nodep) override {
// Analyze RHS first so "a = a + 1" is detected as a read before write
iterate(nodep->rhsp());

View File

@ -266,7 +266,7 @@ private:
// and we also need to keep track of it for comparisons later.
m_mgCondp = m_mgCondp->cloneTree(false);
// Create equivalent 'if' statement and insert it before the first node
AstIf* const resultp = new AstIf(m_mgCondp->fileline(), m_mgCondp, nullptr, nullptr);
AstIf* const resultp = new AstIf(m_mgCondp->fileline(), m_mgCondp);
m_mgFirstp->addHereThisAsNext(resultp);
// Unzip the list and insert under branches
AstNode* nextp = m_mgFirstp;
@ -344,7 +344,7 @@ private:
return true;
}
}
// Is it simply 'lhs = cond'?
// Is it 'lhs = cond'?
if (assignp->rhsp()->sameTree(m_mgCondp)) return true;
}
}

View File

@ -375,7 +375,7 @@ V3Number& V3Number::setZero() {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
return *this;
}
V3Number& V3Number::setQuad(vluint64_t value) {
V3Number& V3Number::setQuad(uint64_t value) {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
m_value[0].m_value = value & 0xffffffffULL;
if (width() > 32) m_value[1].m_value = (value >> 32ULL) & 0xffffffffULL;
@ -388,11 +388,11 @@ V3Number& V3Number::setLong(uint32_t value) {
opCleanThis();
return *this;
}
V3Number& V3Number::setLongS(vlsint32_t value) {
V3Number& V3Number::setLongS(int32_t value) {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
union {
uint32_t u;
vlsint32_t s;
int32_t s;
} u;
u.s = value;
if (u.s) {}
@ -698,7 +698,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
// To get the number of digits required, we want to compute
// log10(2**mantissabits) and round it up. To be able to handle
// a very wide mantissa, we use log2(2**mantissabits)/log2(10),
// which is simply (+1.0 is for rounding bias):
// which is (+1.0 is for rounding bias):
double dchars = mantissabits / 3.321928094887362 + 1.0;
if (issigned) dchars++; // space for sign
fmtsize = cvtToStr(int(dchars));
@ -878,40 +878,40 @@ double V3Number::toDouble() const {
return u.d;
}
vlsint32_t V3Number::toSInt() const {
int32_t V3Number::toSInt() const {
if (isSigned()) {
const uint32_t v = toUInt();
const uint32_t signExtend = (-(v & (1UL << (width() - 1))));
const uint32_t extended = v | signExtend;
return static_cast<vlsint32_t>(extended);
return static_cast<int32_t>(extended);
} else {
// Where we use this (widths, etc) and care about signedness,
// we can reasonably assume the MSB isn't set on unsigned numbers.
return static_cast<vlsint32_t>(toUInt());
return static_cast<int32_t>(toUInt());
}
}
vluint64_t V3Number::toUQuad() const {
uint64_t V3Number::toUQuad() const {
UASSERT(!isFourState(), "toUQuad with 4-state " << *this);
// We allow wide numbers that represent values <= 64 bits
if (isDouble()) return static_cast<vluint64_t>(toDouble());
if (isDouble()) return static_cast<uint64_t>(toDouble());
for (int i = 2; i < words(); ++i) {
if (m_value[i].m_value) {
v3error("Value too wide for 64-bits expected in this context " << *this);
break;
}
}
if (width() <= 32) return (static_cast<vluint64_t>(toUInt()));
return ((static_cast<vluint64_t>(m_value[1].m_value) << 32ULL)
| (static_cast<vluint64_t>(m_value[0].m_value)));
if (width() <= 32) return (static_cast<uint64_t>(toUInt()));
return ((static_cast<uint64_t>(m_value[1].m_value) << 32ULL)
| (static_cast<uint64_t>(m_value[0].m_value)));
}
vlsint64_t V3Number::toSQuad() const {
if (isDouble()) return static_cast<vlsint64_t>(toDouble());
const vluint64_t v = toUQuad();
const vluint64_t signExtend = (-(v & (1ULL << (width() - 1))));
const vluint64_t extended = v | signExtend;
return static_cast<vlsint64_t>(extended);
int64_t V3Number::toSQuad() const {
if (isDouble()) return static_cast<int64_t>(toDouble());
const uint64_t v = toUQuad();
const uint64_t signExtend = (-(v & (1ULL << (width() - 1))));
const uint64_t extended = v | signExtend;
return static_cast<int64_t>(extended);
}
string V3Number::toString() const {
@ -1499,15 +1499,15 @@ V3Number& V3Number::opAtoN(const V3Number& lhs, int base) {
errno = 0;
auto v = std::strtol(str.c_str(), nullptr, base);
if (errno != 0) v = 0;
return setLongS(static_cast<vlsint32_t>(v));
return setLongS(static_cast<int32_t>(v));
}
V3Number& V3Number::opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
NUM_ASSERT_STRING_ARGS1(lhs);
string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
const vlsint32_t c = ths.toSInt() & 0xFF;
const int32_t i = rhs.toSInt();
const int32_t c = ths.toSInt() & 0xFF;
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
// when evaluating the second condition, i must be positive.
if (0 <= i && static_cast<uint32_t>(i) < lstring.length() && c != 0) lstring[i] = c;
@ -1518,8 +1518,8 @@ V3Number& V3Number::opGetcN(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_STRING_ARGS1(lhs);
const string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
vlsint32_t v = 0;
const int32_t i = rhs.toSInt();
int32_t v = 0;
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
// when evaluating the second condition, i must be positive.
if (0 <= i && static_cast<uint32_t>(i) < lstring.length()) v = lstring[i];
@ -1530,8 +1530,8 @@ V3Number& V3Number::opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
NUM_ASSERT_STRING_ARGS1(lhs);
const string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
const vlsint32_t j = ths.toSInt();
const int32_t i = rhs.toSInt();
const int32_t j = ths.toSInt();
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
// when evaluating the third condition, j must be positive because 0 <= i <= j is guaranteed by
// the former two conditions.
@ -1837,14 +1837,14 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) {
opCleanThis(); // Mult produces extra bits in result
} else {
for (int lword = 0; lword < lhs.words(); lword++) {
const vluint64_t lwordval = static_cast<vluint64_t>(lhs.m_value[lword].m_value);
const uint64_t lwordval = static_cast<uint64_t>(lhs.m_value[lword].m_value);
if (lwordval == 0) continue;
for (int rword = 0; rword < rhs.words(); rword++) {
const vluint64_t rwordval = static_cast<vluint64_t>(rhs.m_value[rword].m_value);
const uint64_t rwordval = static_cast<uint64_t>(rhs.m_value[rword].m_value);
if (rwordval == 0) continue;
vluint64_t mul = lwordval * rwordval;
uint64_t mul = lwordval * rwordval;
for (int qword = lword + rword; qword < this->words(); qword++) {
mul += static_cast<vluint64_t>(m_value[qword].m_value);
mul += static_cast<uint64_t>(m_value[qword].m_value);
m_value[qword].m_value = (mul & 0xffffffffULL);
mul = (mul >> 32ULL) & 0xffffffffULL;
if (mul == 0) break;
@ -1960,14 +1960,13 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
const int vw = (vmsbp1 + 31) / 32; // aka "n" in the algorithm
if (vw == 1) { // Single divisor word breaks rest of algorithm
vluint64_t k = 0;
uint64_t k = 0;
for (int j = uw - 1; j >= 0; j--) {
const vluint64_t unw64
= ((k << 32ULL) + static_cast<vluint64_t>(lhs.m_value[j].m_value));
m_value[j].m_value = unw64 / static_cast<vluint64_t>(rhs.m_value[0].m_value);
const uint64_t unw64 = ((k << 32ULL) + static_cast<uint64_t>(lhs.m_value[j].m_value));
m_value[j].m_value = unw64 / static_cast<uint64_t>(rhs.m_value[0].m_value);
k = unw64
- (static_cast<vluint64_t>(m_value[j].m_value)
* static_cast<vluint64_t>(rhs.m_value[0].m_value));
- (static_cast<uint64_t>(m_value[j].m_value)
* static_cast<uint64_t>(rhs.m_value[0].m_value));
}
UINFO(9, " opmoddiv-1w " << lhs << " " << rhs << " q=" << *this << " rem=0x" << std::hex
<< k << std::dec << endl);
@ -2016,10 +2015,10 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
// Main loop
for (int j = uw - vw; j >= 0; j--) {
// Estimate
const vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
| static_cast<vluint64_t>(un[j + vw - 1]));
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw - 1]);
vluint64_t rhat = unw64 - qhat * static_cast<vluint64_t>(vn[vw - 1]);
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]);
again:
if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) {
@ -2028,10 +2027,10 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
if (rhat < 0x100000000ULL) goto again;
}
vlsint64_t t = 0; // Must be signed
vluint64_t k = 0;
int64_t t = 0; // Must be signed
uint64_t k = 0;
for (int i = 0; i < vw; i++) {
const vluint64_t p = qhat * vn[i]; // Multiply by estimate
const uint64_t p = qhat * vn[i]; // Multiply by estimate
t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract
un[i + j] = t;
k = (p >> 32ULL) - (t >> 32ULL);
@ -2045,7 +2044,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
this->m_value[j].m_value--;
k = 0;
for (int i = 0; i < vw; i++) {
t = static_cast<vluint64_t>(un[i + j]) + static_cast<vluint64_t>(vn[i]) + k;
t = static_cast<uint64_t>(un[i + j]) + static_cast<uint64_t>(vn[i]) + k;
un[i + j] = t;
k = t >> 32ULL;
}
@ -2265,7 +2264,7 @@ V3Number& V3Number::opRToIS(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_DOUBLE_ARGS1(lhs);
const double v = VL_TRUNC(lhs.toDouble());
const vlsint32_t i = static_cast<vlsint32_t>(v); // C converts from double to vlsint32
const int32_t i = static_cast<int32_t>(v); // C converts from double to int32_t
return setLongS(i);
}
V3Number& V3Number::opRToIRoundS(const V3Number& lhs) {
@ -2275,14 +2274,14 @@ V3Number& V3Number::opRToIRoundS(const V3Number& lhs) {
setZero();
union {
double d;
vluint64_t q;
uint64_t q;
} u;
u.d = v;
if (u.d == 0.0) {}
const int exp = static_cast<int>((u.q >> 52ULL) & VL_MASK_Q(11)) - 1023;
const int lsb = exp - 52;
const vluint64_t mantissa = (u.q & VL_MASK_Q(52)) | (1ULL << 52);
const uint64_t mantissa = (u.q & VL_MASK_Q(52)) | (1ULL << 52);
if (v != 0) {
// IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
// This does not need to support subnormals as they are sub-integral

View File

@ -111,9 +111,9 @@ public:
void nodep(AstNode* nodep) { setNames(nodep); }
FileLine* fileline() const { return m_fileline; }
V3Number& setZero();
V3Number& setQuad(vluint64_t value);
V3Number& setQuad(uint64_t value);
V3Number& setLong(uint32_t value);
V3Number& setLongS(vlsint32_t value);
V3Number& setLongS(int32_t value);
V3Number& setDouble(double value);
void setBit(int bit, char value) { // Note must be pre-zeroed!
if (bit >= m_width) return;
@ -339,9 +339,9 @@ public:
bool isAnyZ() const;
bool isMsbXZ() const { return bitIsXZ(m_width); }
uint32_t toUInt() const;
vlsint32_t toSInt() const;
vluint64_t toUQuad() const;
vlsint64_t toSQuad() const;
int32_t toSInt() const;
uint64_t toUQuad() const;
int64_t toSQuad() const;
string toString() const;
string toDecimalS() const; // return ASCII signed decimal number
string toDecimalU() const; // return ASCII unsigned decimal number

View File

@ -1236,7 +1236,13 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; });
DECL_OPTION("-profile-cfuncs", CbCall,
[this]() { m_profC = m_profCFuncs = true; }); // Renamed
DECL_OPTION("-prof-threads", OnOff, &m_profThreads);
DECL_OPTION("-prof-exec", OnOff, &m_profExec);
DECL_OPTION("-prof-pgo", OnOff, &m_profPgo);
DECL_OPTION("-prof-threads", CbOnOff, [this, fl](bool flag) {
fl->v3warn(DEPRECATED, "Option --prof-threads is deprecated. "
"Use --prof-exec and --prof-pgo instead.");
m_profExec = m_profPgo = flag;
});
DECL_OPTION("-protect-ids", OnOff, &m_protectIds);
DECL_OPTION("-protect-key", Set, &m_protectKey);
DECL_OPTION("-protect-lib", CbVal, [this](const char* valp) {

View File

@ -255,7 +255,8 @@ private:
bool m_ppComments = false; // main switch: --pp-comments
bool m_profC = false; // main switch: --prof-c
bool m_profCFuncs = false; // main switch: --prof-cfuncs
bool m_profThreads = false; // main switch: --prof-threads
bool m_profExec = false; // main switch: --prof-exec
bool m_profPgo = false; // main switch: --prof-pgo
bool m_protectIds = false; // main switch: --protect-ids
bool m_public = false; // main switch: --public
bool m_publicFlatRW = false; // main switch: --public-flat-rw
@ -468,7 +469,9 @@ public:
bool ppComments() const { return m_ppComments; }
bool profC() const { return m_profC; }
bool profCFuncs() const { return m_profCFuncs; }
bool profThreads() const { return m_profThreads; }
bool profExec() const { return m_profExec; }
bool profPgo() const { return m_profPgo; }
bool usesProfiler() const { return profExec() || profPgo(); }
bool protectIds() const { return m_protectIds; }
bool allPublic() const { return m_public; }
bool publicFlatRW() const { return m_publicFlatRW; }

View File

@ -662,6 +662,9 @@ class OrderBuildVisitor final : public VNVisitor {
virtual void visit(AstInitialAutomatic* nodep) override { //
iterateLogic(nodep);
}
virtual void visit(AstInitialStatic* nodep) override { //
iterateLogic(nodep);
}
virtual void visit(AstAlways* nodep) override { //
iterateLogic(nodep);
}
@ -1025,8 +1028,8 @@ public:
virtual bool operator()(const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const {
const MTaskMoveVertex* const l_vxp = dynamic_cast<const MTaskMoveVertex*>(lhsp);
const MTaskMoveVertex* const r_vxp = dynamic_cast<const MTaskMoveVertex*>(rhsp);
vluint64_t l_id = m_ids.findId(l_vxp->domainp());
vluint64_t r_id = m_ids.findId(r_vxp->domainp());
uint64_t l_id = m_ids.findId(l_vxp->domainp());
uint64_t r_id = m_ids.findId(r_vxp->domainp());
if (l_id < r_id) return true;
if (l_id > r_id) return false;
l_id = m_ids.findId(l_vxp->scopep());
@ -1954,9 +1957,8 @@ void OrderProcess::processMTasks() {
// Create the AstExecGraph node which represents the execution
// of the MTask graph.
FileLine* const rootFlp = v3Global.rootp()->fileline();
AstExecGraph* const execGraphp = new AstExecGraph(rootFlp);
AstExecGraph* const execGraphp = new AstExecGraph{rootFlp, "eval"};
m_scopetop.addActivep(execGraphp);
v3Global.rootp()->execGraphp(execGraphp);
// Create CFuncs and bodies for each MTask.
GraphStream<MTaskVxIdLessThan> emit_mtasks(&mtasks);
@ -1994,7 +1996,8 @@ void OrderProcess::processMTasks() {
// and OrderLogicVertex's which are ephemeral to V3Order.
// - The ExecMTask graph and the AstMTaskBody's produced here
// persist until code generation time.
state.m_execMTaskp = new ExecMTask(execGraphp->mutableDepGraphp(), bodyp, mtaskp->id());
V3Graph* const depGraphp = execGraphp->depGraphp();
state.m_execMTaskp = new ExecMTask(depGraphp, bodyp, mtaskp->id());
// Cross-link each ExecMTask and MTaskBody
// Q: Why even have two objects?
// A: One is an AstNode, the other is a GraphVertex,
@ -2005,10 +2008,9 @@ void OrderProcess::processMTasks() {
const AbstractLogicMTask* const fromp
= dynamic_cast<const AbstractLogicMTask*>(fromVxp);
const MTaskState& fromState = mtaskStates[fromp->id()];
new V3GraphEdge(execGraphp->mutableDepGraphp(), fromState.m_execMTaskp,
state.m_execMTaskp, 1);
new V3GraphEdge(depGraphp, fromState.m_execMTaskp, state.m_execMTaskp, 1);
}
execGraphp->addMTaskBody(bodyp);
execGraphp->addMTaskBodyp(bodyp);
}
}

Some files were not shown because too many files have changed in this diff Show More