Merge from master for release.
This commit is contained in:
commit
4618fd5a43
23
Changes
23
Changes
|
|
@ -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
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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>"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>{};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
113
src/V3Active.cpp
113
src/V3Active.cpp
|
|
@ -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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
144
src/V3Ast.h
144
src/V3Ast.h
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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 << " -> ";
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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> {};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 { //
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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(");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
195
src/V3EmitV.cpp
195
src/V3EmitV.cpp
|
|
@ -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}; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue