mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 00:53:42 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58fc922737 | ||
|
|
3b5c791332 | ||
|
|
6e16bc1b7b | ||
|
|
1aba0f6379 | ||
|
|
11cfa3c072 | ||
|
|
59141b1678 | ||
|
|
2083c071ce | ||
|
|
3ad5872d30 | ||
|
|
ea6bb21cdc | ||
|
|
3d4fe0364a | ||
|
|
710d7c0ee5 | ||
|
|
1c8fd37745 | ||
|
|
956c854662 | ||
|
|
318a6e348c | ||
|
|
e471a34323 | ||
|
|
95c5b85e88 | ||
|
|
a46a556a64 | ||
|
|
fa3e03e071 | ||
|
|
74d7c55f1b | ||
|
|
79e02858cb | ||
|
|
7738b202ac | ||
|
|
b4d4917c6c | ||
|
|
5c93520b27 | ||
|
|
94f0809d33 | ||
|
|
ca4e3c1737 | ||
|
|
ba7b4f261a | ||
|
|
4f42c25c7c | ||
|
|
621ef70c31 | ||
|
|
e3f17e7853 | ||
|
|
73c897ac69 | ||
|
|
a4db880809 | ||
|
|
f9697463cc | ||
|
|
f283076570 | ||
|
|
e15228498d | ||
|
|
53155b3a36 | ||
|
|
12ae70ba95 | ||
|
|
88809587f9 | ||
|
|
1b20481100 | ||
|
|
21eb939f37 | ||
|
|
0a765fbb54 | ||
|
|
749fdaae31 | ||
|
|
6f48185a1f | ||
|
|
7f1b16837e |
@@ -3,9 +3,58 @@ Revision history for Verilator
|
||||
The contributors that suggested a given feature are shown in []. [by ...]
|
||||
indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
* Verilator 3.60**
|
||||
* Verilator 3.622 10/17/2006 Stable
|
||||
|
||||
*** Changed how internal functions are invoked to avoid aliasing in GCC 3.3+.
|
||||
**** Fix --skip-identical without --debug, broken in 3.621. [Andy Meier]
|
||||
|
||||
* Verilator 3.621 10/11/2006 Beta
|
||||
|
||||
** Add /*verilator no_inline_task*/ to prevent over-expansion. [Eugene Weber]
|
||||
|
||||
*** Public functions now allow > 64 bit arguments.
|
||||
|
||||
**** Remove .vpp intermediate files when not under --debug.
|
||||
|
||||
**** Fix link error when using --exe with --trace. [Eugene Weber]
|
||||
|
||||
**** Fix mis-optimization of wide concats with constants.
|
||||
|
||||
**** Fix core dump on printing error when not under --debug. [Allan Cochrane]
|
||||
|
||||
* Verilator 3.620 10/04/2006 Stable
|
||||
|
||||
*** Support simple inout task ports. [Eugene Weber]
|
||||
|
||||
*** Allow overriding Perl, Flex and Bison versions. [by Robert Farrell]
|
||||
|
||||
*** Optimize variables set to constants within basic blocks for ~3%.
|
||||
|
||||
**** Default make no longer makes the docs; if you edit the documentation.
|
||||
sources, run "make info" to get them.
|
||||
|
||||
**** Optimize additional boolean identities (a|a = a, etc.)
|
||||
|
||||
**** Fix coredump when dotted cross-ref inside task call. [Eugene Weber]
|
||||
|
||||
**** Fix dotted variables in always sensitivity lists. [Allan Cochrane]
|
||||
|
||||
* Verilator 3.610 09/20/2006 Stable
|
||||
|
||||
*** Verilator now works under DJGPP (Pentium GCC). [John Stroebel]
|
||||
|
||||
**** Add default define for VL_PRINTF. [John Stroebel]
|
||||
|
||||
**** Removed coverage request variable; see Coverage limitations in docs.
|
||||
|
||||
**** Fix DOS carrage returns in multiline defines. [Ralf Karge]
|
||||
|
||||
**** Fix printf format warnings on 64-bit linux.
|
||||
|
||||
* Verilator 3.602 09/11/2006 Stable
|
||||
|
||||
**** Fix function references under top inlined module. [David Hewson]
|
||||
|
||||
* Verilator 3.601 09/06/2006 Beta
|
||||
|
||||
*** Added --inhibit-sim flag for environments using old __Vm_inhibitSim.
|
||||
|
||||
@@ -13,13 +62,22 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
*** Added -MP to make phony dependencies, ala GCC's.
|
||||
|
||||
**** Declare optimized lookup tables as 'static', to reduce D-Cache miss rate.
|
||||
*** Changed how internal functions are invoked to reduce aliasing.
|
||||
Useful when using GCC's -O2 or -fstrict-aliasing, to gain another ~4%.
|
||||
|
||||
**** Fix memory leak when destroying modules. [John Stroebel]
|
||||
|
||||
**** Fix coredump when unused modules have unused cells. [David Hewson]
|
||||
|
||||
**** Fix 3.600 internal error with arrayed instances. [David Hewson]
|
||||
|
||||
**** Fix 3.600 internal error with non-unrolled function loops. [David Hewson]
|
||||
|
||||
**** Fix $display %m name not matching Verilog name inside SystemC modules.
|
||||
|
||||
* Verilator 3.600 08/28/2006
|
||||
**** Declare optimized lookup tables as 'static', to reduce D-Cache miss rate.
|
||||
|
||||
* Verilator 3.600 08/28/2006 Beta
|
||||
|
||||
** Support dotted cross-hierarchy variable and task references.
|
||||
|
||||
@@ -386,7 +444,7 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
**** Added NC Verilog as alternative to VCS for reference tests.
|
||||
|
||||
**** Support implicit wire declarations on input-only signals.
|
||||
(Dangerous, as leads to wires without drivers, but allowed by spec.)
|
||||
(Dangerous, as leads to wires without drivers, but allowed by spec.)
|
||||
|
||||
**** Fixed compile warnings on Suse 9.1
|
||||
|
||||
|
||||
+3
-3
@@ -91,7 +91,7 @@ SUBDIRS = src test_verilated test_c test_sc test_sp test_regress test_vcs
|
||||
INFOS = README verilator.txt verilator.html verilator.1 verilator.pdf
|
||||
|
||||
# Files that can be generated, but should be up to date for a distribution.
|
||||
DISTDEP = $(INFOS) Makefile
|
||||
DISTDEP = info Makefile
|
||||
# Files to distribute.
|
||||
DISTBIN = $(wildcard bin/verilator-*)
|
||||
|
||||
@@ -126,7 +126,7 @@ INST_PROJ_BIN_FILES = \
|
||||
DISTFILES := $(DISTFILES_INC)
|
||||
|
||||
all: all_nomsg msg_test
|
||||
all_nomsg: verilator_exe info
|
||||
all_nomsg: verilator_exe
|
||||
|
||||
.PHONY:verilator_exe
|
||||
verilator_exe verilator_bin verilator_bin_dbg:
|
||||
@@ -218,7 +218,7 @@ installman:
|
||||
$(INSTALL_PROGRAM) $$p $(mandir)/man1/$$p; \
|
||||
done
|
||||
|
||||
install: all_nomsg info installdirs installbin installman install-msg
|
||||
install: all_nomsg installdirs installbin installman install-msg
|
||||
|
||||
install-here: installman ftp
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ Performance:
|
||||
Track recirculation and convert into clock-enables
|
||||
Clock enables should become new clocking domains for speed
|
||||
If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
|
||||
Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
|
||||
|
||||
//**********************************************************************
|
||||
//* Detailed notes on 'todo' features
|
||||
|
||||
+196
-64
@@ -113,7 +113,6 @@ if ($Opt_Sp eq 'sp' || $Opt_Trace) {
|
||||
# Determine runtime flags
|
||||
my $vcmd =(($opt_gdb?"$opt_gdb ":"")
|
||||
.verilator_bin()
|
||||
." --bin ".verilator_bin() # So we get binary name into .d file
|
||||
." ".join(' ',@Opt_Verilator_Sw)
|
||||
);
|
||||
|
||||
@@ -169,7 +168,7 @@ sub run {
|
||||
warn "%Error: export VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
die "%Error: Command Failed $command, $status, stopped";
|
||||
die "%Error: Command Failed $command\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +233,13 @@ linking with make rules you write yourself.
|
||||
Enable all assertions, includes enabling the --psl flag. (If psl is not
|
||||
desired, but other assertions are, use --assert --nopsl.)
|
||||
|
||||
=item --bin I<filename>
|
||||
|
||||
Rarely needed. Override the default filename for Verilator itself. When a
|
||||
dependancy (.d) file is created, this filename will become a source
|
||||
dependency, such that a change in this binary will have make rebuild the
|
||||
output files.
|
||||
|
||||
=item --cc
|
||||
|
||||
Specifies C++ without SystemC output mode; see also --sc and --sp.
|
||||
@@ -248,9 +254,9 @@ Specifies basic block line coverage analysis code should be inserted.
|
||||
|
||||
Coverage analysis adds statements at each code flow change point, which are
|
||||
the branches of IF and CASE statements, a superset of normal Verilog Line
|
||||
Coverage. At each such branch a C++ macro is called, which SystemPerl uses
|
||||
to increment an array. At the end of a test, logs/coverage.pl is written
|
||||
with the data from the array.
|
||||
Coverage. At each such branch a unique counter is incremented. At the end
|
||||
of a test, the counters along with the filename and line number
|
||||
corresponding to each counter are written into logs/coverage.pl.
|
||||
|
||||
After running multiple tests, the vcoverage utility (from the SystemPerl
|
||||
package) is executed. Vcoverage reads the logs/coverage.pl file(s), and
|
||||
@@ -261,6 +267,10 @@ them, as it is assumed $stop branches contain an error check that should
|
||||
not occur. A /*verilator coverage_block_off*/ comment will perform a
|
||||
similar function on any code in that block or below.
|
||||
|
||||
Note Verilator may over-count combinatorial (non-clocked) blocks when those
|
||||
blocks receive signals which have had the UNOPTFLAT warning disabled; for
|
||||
most accurate results do not disable this warning when using coverage.
|
||||
|
||||
For an example, after running 'make test' in the Verilator distribution,
|
||||
see the test_sp/logs/coverage_source directory. Grep for lines starting
|
||||
with '%' to see what lines Verilator believes need more coverage.
|
||||
@@ -279,8 +289,13 @@ the comment "DefaultClock":
|
||||
|
||||
=item --debug
|
||||
|
||||
Select the debug built image of Verilator (if available), and enable
|
||||
debugging messages and intermediate form dump files.
|
||||
Select the debug built image of Verilator (if available), and enable more
|
||||
internal assertions, debugging messages, and intermediate form dump files.
|
||||
|
||||
=item --debug-check
|
||||
|
||||
Rarely needed. Enable internal debugging assertion checks, without
|
||||
changing debug verbosity. Enabled automatically when --debug specified.
|
||||
|
||||
=item -E
|
||||
|
||||
@@ -299,10 +314,10 @@ Displays this message and program version and exits.
|
||||
|
||||
=item --inhibit-sim
|
||||
|
||||
Create a "inhibitSim(bool)" function to enable and disable evaluation.
|
||||
This allows a upper level testbench to disable modules that are not
|
||||
important in a given simulation, without needing to recompile or change the
|
||||
SystemC modules instantiated.
|
||||
Rarely needed. Create a "inhibitSim(bool)" function to enable and disable
|
||||
evaluation. This allows a upper level testbench to disable modules that
|
||||
are not important in a given simulation, without needing to recompile or
|
||||
change the SystemC modules instantiated.
|
||||
|
||||
=item --inline-mult I<value>
|
||||
|
||||
@@ -335,8 +350,8 @@ the same as --prefix.
|
||||
|
||||
=item --no-skip-identical
|
||||
|
||||
Disables skipping execution of Verilator if all source files are identical,
|
||||
and all output files exist with newer dates.
|
||||
Rarely needed. Disables skipping execution of Verilator if all source
|
||||
files are identical, and all output files exist with newer dates.
|
||||
|
||||
=item -O0
|
||||
|
||||
@@ -349,11 +364,11 @@ cost of compile time. This currently sets --inline-mult -1.
|
||||
|
||||
=item -OI<optimization-letter>
|
||||
|
||||
Enables or disables a specific optimizations, with the optimization
|
||||
selected based on the letter passed. A lowercase letter disables an
|
||||
optimization, an upper case letter enables it. This is intended for
|
||||
debugging use only; see the source code for version-dependent mappings of
|
||||
optimizations to -O letters.
|
||||
Rarely needed. Enables or disables a specific optimizations, with the
|
||||
optimization selected based on the letter passed. A lowercase letter
|
||||
disables an optimization, an upper case letter enables it. This is
|
||||
intended for debugging use only; see the source code for version-dependent
|
||||
mappings of optimizations to -O letters.
|
||||
|
||||
=item --output-split I<bytes>
|
||||
|
||||
@@ -392,17 +407,20 @@ compatibility.
|
||||
|
||||
=item --psl
|
||||
|
||||
Enable PSL parsing. Without this switch, psl meta-comments are ignored.
|
||||
Enable PSL parsing. Without this switch, PSL meta-comments are ignored.
|
||||
See the --assert flag to enable all assertions, and --coverage-user to
|
||||
enable functional coverage.
|
||||
|
||||
=item --public
|
||||
|
||||
This is only for debug, and may result in mis-simulation of generated
|
||||
clocks.
|
||||
|
||||
Declares all signals and modules public. This will turn off signal
|
||||
optimizations as if all signals had a /*verilator public*/ comments. This
|
||||
will also turn off inlining as if all modules had a /*verilator
|
||||
public_module*/, unless the module specifically enabled it with /*verilator
|
||||
inline_module*/.
|
||||
optimizations as if all signals had a /*verilator public*/ comments and
|
||||
inlining. This will also turn off inlining as if all modules had a
|
||||
/*verilator public_module*/, unless the module specifically enabled it with
|
||||
/*verilator inline_module*/.
|
||||
|
||||
=item --sc
|
||||
|
||||
@@ -424,10 +442,10 @@ on during model execution.
|
||||
|
||||
=item --underline-zero
|
||||
|
||||
Signals starting with a underline should be initialized to zero, as was
|
||||
done in Verilator 2. Default is for all signals including those with
|
||||
underlines being randomized. This option may be depreciated in future
|
||||
versions.
|
||||
Rarely needed. Signals starting with a underline should be initialized to
|
||||
zero, as was done in Verilator 2. Default is for all signals including
|
||||
those with underlines being randomized. This option may be depreciated in
|
||||
future versions.
|
||||
|
||||
=item -Werror-I<message>
|
||||
|
||||
@@ -548,7 +566,7 @@ And we get as output
|
||||
Hello World
|
||||
- our.v:2: Verilog $finish
|
||||
|
||||
Really, you're better off using a Makefile to do all this for you. Then,
|
||||
Really, you're better off writing a Makefile to do all this for you. Then,
|
||||
when your source changes it will automatically run all of these steps. See
|
||||
the test_c directory in the distribution for an example.
|
||||
|
||||
@@ -591,7 +609,7 @@ Then we convert the SystemPerl output to SystemC.
|
||||
$SYSTEMPERL/sp_preproc --preproc *.sp
|
||||
|
||||
(You can also skip the above sp_preproc by getting pure SystemC from
|
||||
verilator by replacing the verilator --sp flag in the previous step with
|
||||
Verilator by replacing the verilator --sp flag in the previous step with
|
||||
-sc.)
|
||||
|
||||
We then can compile it
|
||||
@@ -702,7 +720,7 @@ In certain optimization modes, it also creates:
|
||||
|
||||
It also creates internal files that can be mostly ignored:
|
||||
|
||||
{each_verilog_module}.vpp // Post-processed verilog source code
|
||||
{each_verilog_module}.vpp // Post-processed verilog (--debug)
|
||||
{prefix}.flags_vbin // Verilator dependencies
|
||||
{prefix}.flags_vpp // Pre-processor dependencies
|
||||
{prefix}{misc}.d // Make dependencies (-MMD)
|
||||
@@ -920,7 +938,7 @@ call C++ functions from your Verilog code.
|
||||
String arguments will be put directly into the output C++ code. Expression
|
||||
arguments will have the code to evaluate the expression inserted. Thus to
|
||||
call a C++ function, $c("func(",a,")") will result in 'func(a)' in the
|
||||
output C++ code. For input arguments, rather then hardcoding variable
|
||||
output C++ code. For input arguments, rather then hard-coding variable
|
||||
names in the string $c("func(a)"), instead pass the variable as an
|
||||
expression $c("func(",a,")"). This will allow the call to work inside
|
||||
Verilog functions where the variable is flattened out, and also enable
|
||||
@@ -936,9 +954,9 @@ each different output width.
|
||||
|
||||
=item $display, $write, $fdisplay, $fwrite
|
||||
|
||||
$display format arguments may use C fprintf sizes after the % escape. Per
|
||||
the Verilog standard, %x prints a number with the natural width, %0x prints
|
||||
a number with minimum width, however %5x prints 5 digits per the C standard
|
||||
Format arguments may use C fprintf sizes after the % escape. Per the
|
||||
Verilog standard, %x prints a number with the natural width, %0x prints a
|
||||
number with minimum width, however %5x prints 5 digits per the C standard
|
||||
(it's unspecified in Verilog).
|
||||
|
||||
=item $fopen, $fclose, $fdisplay, $fwrite
|
||||
@@ -1038,6 +1056,14 @@ Disable the specified warning message for any warnings following the comment.
|
||||
|
||||
Re-enable the specified warning message for any warnings following the comment.
|
||||
|
||||
=item /*verilator no_inline_task*/
|
||||
|
||||
Used in a function or task variable definition section to specify the
|
||||
function or task should not be inlined into where it is used. This may
|
||||
reduce the size of the final executable when a task is used a very large
|
||||
number of times. For this flag to work, the task and tasks below it must
|
||||
be pure; they cannot reference any variables outside the task itself.
|
||||
|
||||
=item /*verilator sc_clock*/
|
||||
|
||||
Used after a input declaration to indicate the signal should be declared in
|
||||
@@ -1057,7 +1083,7 @@ Re-enable waveform tracing for all future signals that are declared.
|
||||
|
||||
Used after a input, output, register, or wire declaration to indicate the
|
||||
signal should be declared so that C code may read or write the value
|
||||
of the signal. Set for all signals when using the --public switch.
|
||||
of the signal.
|
||||
|
||||
=item /*verilator public*/ (task/function)
|
||||
|
||||
@@ -1070,9 +1096,9 @@ function. Any output arguments will become C++ reference arguments. Any
|
||||
local registers/integers will become function automatic variables on the
|
||||
stack.
|
||||
|
||||
Wide variables over 64 bits cannot be I/O to the module, to avoid exposing
|
||||
complexities; generally the cleanest technique is to pass a word number to
|
||||
the function, and do appropriate bit selection.
|
||||
Wide variables over 64 bits cannot be function returns, to avoid exposing
|
||||
complexities. However, wide variables can be input/outputs; they will be
|
||||
passed as references to an array of 32 bit numbers.
|
||||
|
||||
This feature is still somewhat experimental. Generally, only the values of
|
||||
stored state (flops) should be written, as the model will NOT notice
|
||||
@@ -1116,7 +1142,25 @@ always @* to prevent these issues.)
|
||||
|
||||
Verilator supports dotted references to variables, functions and tasks in
|
||||
different modules. However, references into named blocks and function-local
|
||||
variables are not supported.
|
||||
variables are not supported. References into arrayed and generated
|
||||
instances work, but still require some work-arounds in the Verilog code:
|
||||
|
||||
References into arrayed instances use different names from the Verilog
|
||||
standard; arrayed instances are named {cellName}__{instanceNumber}. For
|
||||
example a[2] is instead a__2. Thus you cannot use a parameter or variable
|
||||
to select the index number; expand it into a case statement manually.
|
||||
|
||||
References into generate statements also use different names from the
|
||||
Verilog standard; the top level generate, any begin statement, and any
|
||||
place where there is a implied begin statement (under each generate-if or
|
||||
generate-case) adds a hierarchy level named genblkI<instanceNumber>. And
|
||||
each generate-for adds genforI<loopCount>. The best bet is to comment out
|
||||
the references and run with --debug and look in the at the _begin.tree
|
||||
file, and see the hierarchy by looking at the names of all of the CELLs in
|
||||
question. Replace __DOT__ with . in your program. For example most cells
|
||||
under a for loop would be something like
|
||||
"genblk__DOT__genfor0__DOT__I<cellname>"; so use genblk.genfor0.I<cellname> to
|
||||
see inside that cell.
|
||||
|
||||
=head2 Latches
|
||||
|
||||
@@ -1230,7 +1274,12 @@ will give a width warning and wrap around the power-of-2 size. For
|
||||
non-power-of-2 sizes, it will return a unspecified constant of the
|
||||
appropriate width.
|
||||
|
||||
=head1 ERRORS
|
||||
=item $display, $write, $fdisplay, $fwrite
|
||||
|
||||
$display and friends must have a constant format string as the first
|
||||
argument (as with C's printf), you cannot simply list variables standalone.
|
||||
|
||||
=head1 ERRORS AND WARNINGS
|
||||
|
||||
Warnings may be disabled in two ways. First, when the warning is
|
||||
printed it will include a warning code. Simply surround the offending
|
||||
@@ -1240,13 +1289,13 @@ line with a warn_off/warn_on pair:
|
||||
if (`DEF_THAT_IS_EQ_ZERO <= 3) $stop;
|
||||
// verilator lint_on UNSIGNED
|
||||
|
||||
They may also be globally disabled by invoking Verilator with the
|
||||
C<-Wno-I<warning>> switch. This should be avoided, as it removes
|
||||
all checking across the designs, and prevents other users from
|
||||
compiling your code without knowing the magic set of disables needed
|
||||
to successfully compile your design.
|
||||
Warnings may also be globally disabled by invoking Verilator with the
|
||||
C<-Wno-I<warning>> switch. This should be avoided, as it removes all
|
||||
checking across the designs, and prevents other users from compiling your
|
||||
code without knowing the magic set of disables needed to successfully
|
||||
compile your design.
|
||||
|
||||
Warnings:
|
||||
List of all warnings:
|
||||
|
||||
=over 4
|
||||
|
||||
@@ -1303,12 +1352,41 @@ signal before it is implicitly declared by a cell, and can lead to dangling
|
||||
nets. A better option is the /*AUTOWIRE*/ feature of Verilog-Mode for
|
||||
Emacs, available from L<http://www.veripool.com/>
|
||||
|
||||
=item IMPURE
|
||||
|
||||
Warns that a task or function that has been marked with /*verilator
|
||||
no_inline_task*/ references variables that are not local to the task.
|
||||
Verilator cannot schedule these variables correctly.
|
||||
|
||||
=item MULTIDRIVEN
|
||||
|
||||
Warns that the specified signal comes from multiple always blocks. This is
|
||||
often unsupported by synthesis tools, and is considered bad style. It will
|
||||
also cause longer runtimes due to reduced optimizations.
|
||||
|
||||
=item TASKNSVAR
|
||||
|
||||
Error when a call to a task or function has a output from that task tied to
|
||||
a non-simple signal. Instead connect the task output to a temporary signal
|
||||
of the appropriate width, and use that signal to set the appropriate
|
||||
expression as the next statement. For example:
|
||||
|
||||
task foo; output sig; ... endtask
|
||||
always @* begin
|
||||
foo(bus_we_select_from[2]); // Will get TASKNSVAR error
|
||||
end
|
||||
|
||||
Change this to:
|
||||
|
||||
reg foo_temp_out;
|
||||
always @* begin
|
||||
foo(foo_temp_out);
|
||||
bus_we_select_from[2] = foo_temp_out;
|
||||
end
|
||||
|
||||
Verilator doesn't do this conversion for you, as some more complicated
|
||||
cases would result in simulator mismatches.
|
||||
|
||||
=item UNDRIVEN
|
||||
|
||||
Warns that the specified signal is never sourced.
|
||||
@@ -1334,11 +1412,11 @@ may be only one particular usage of a multiply instantiated block. The
|
||||
construct should be cleaned up to improve runtime; two times better
|
||||
performance may be possible by fixing these warnings.
|
||||
|
||||
Unlike UNOPT this occurs after netlist flattening, and indicates a more
|
||||
basic problem, as the less obvious case described under UNOPT does not
|
||||
apply.
|
||||
Unlike the UNOPT warning, this occurs after netlist flattening, and
|
||||
indicates a more basic problem, as the less obvious case described under
|
||||
UNOPT does not apply.
|
||||
|
||||
Often this is caused by logic that isn't truly circular as viewed by
|
||||
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
|
||||
synthesis which analyzes interconnection per-bit, but is circular to
|
||||
simulation which analyzes per-bus:
|
||||
|
||||
@@ -1346,10 +1424,26 @@ simulation which analyzes per-bus:
|
||||
|
||||
This statement needs to be evaluated multiple times, as a change in
|
||||
"shift_in" requires "x" to be computed 3 times before it becomes stable.
|
||||
For significantly better performance, split this into 2 separate signals:
|
||||
For significantly better performance, split this into 2 separate signals,
|
||||
and then if necessary generate the original signal:
|
||||
|
||||
wire [2:1] x_21 = x[1:0];
|
||||
wire [0:0] x_0 = shift_in;
|
||||
wire [2:0] x = {x_21, x_0};
|
||||
|
||||
This logic needs to be evaluated only once. These sort of changes may also
|
||||
speed up your traditional event driven simulator, as it will result in
|
||||
fewer events per cycle.
|
||||
|
||||
The most complicated UNOPTFLAT path we've seen was due to low bits of a bus
|
||||
being generated from an always statement that consumed high bits of the
|
||||
same bus processed by another series of always blocks. The fix is the
|
||||
same; split it into two separate signals, then create the bus from the two
|
||||
separate signals.
|
||||
|
||||
The UNOPTFLAT warning may also be due to clock enables, identified from the
|
||||
reported path going through a clock gating cell. To fix these, use the
|
||||
clock_enable meta comment described above.
|
||||
|
||||
=item UNSIGNED
|
||||
|
||||
@@ -1406,8 +1500,8 @@ that is not yet supported in Verilator. See the Limitations chapter.
|
||||
=item Verilated model didn't converge
|
||||
|
||||
Verilator sometimes has to evaluate combinatorial logic multiple times,
|
||||
usually around code where a UNOPT warning was issued, but disabled. For
|
||||
example:
|
||||
usually around code where a UNOPTFLAT warning was issued, but disabled.
|
||||
For example:
|
||||
|
||||
always @ (a) b=~a;
|
||||
always @ (b) a=b
|
||||
@@ -1453,18 +1547,32 @@ so your simulator shouldn't have to be -- and Verilator is closer to the
|
||||
synthesis interpretation, so this is a good thing for getting working
|
||||
silicon.
|
||||
|
||||
=item How do I generate waveforms (traces)?
|
||||
=item How do I generate waveforms (traces) in C++?
|
||||
|
||||
See the next question for SystemC mode.
|
||||
|
||||
Add the --trace switch to Verilator, and make sure the SystemPerl package
|
||||
is installed (SystemC itself isn't required for C++ only tracing.) In your
|
||||
top level C code, call Verilated::traceEverOn(true).
|
||||
is installed. SystemC itself does not need to be installed for C++ only
|
||||
tracing. You do not even need to compile SystemPerl; you may simply untar
|
||||
the SystemPerl kit and point the SYSTEMPERL environment variable to the
|
||||
untarred directory.
|
||||
|
||||
Then, in SystemC mode, create a SpTraceFile object. For an example, see
|
||||
the call to SpTraceFile in the test_sp/sc_main.cpp file of the
|
||||
distribution.
|
||||
In your top level C code, call Verilated::traceEverOn(true). Then create a
|
||||
SpTraceVcdC object. For an example, see the call to SpTraceVcdC in the
|
||||
test_c/sc_main.cpp file of the distribution.
|
||||
|
||||
Or, in C++ mode, create a SpTraceVcdCFile object, and see
|
||||
test_c/sim_main.cpp.
|
||||
You also need to compile SpTraceVcdC.cpp and add it to your link. This is
|
||||
done for you if using the Verilator --exe flag.
|
||||
|
||||
=item How do I generate waveforms (traces) in SystemC?
|
||||
|
||||
Add the --trace switch to Verilator, and make sure the SystemPerl package
|
||||
is installed.
|
||||
|
||||
In your top level C sc_main code, call Verilated::traceEverOn(true). Then
|
||||
create a SpTraceFile object as you would create a normal SystemC trace
|
||||
file. For an example, see the call to SpTraceFile in the
|
||||
test_sp/sc_main.cpp file of the distribution.
|
||||
|
||||
=item Where is the translate_off command? (How do I ignore a construct?)
|
||||
|
||||
@@ -1483,6 +1591,11 @@ Call Verilated::assertOn(false) before you first call the model, then turn
|
||||
it back on after reset. It defaults to true. When false, all assertions
|
||||
controlled by --assert are disabled.
|
||||
|
||||
=item Why do I get "undefined reference to `sc_time_stamp()'"?
|
||||
|
||||
In C++ (non SystemC) code you need to define this function so that the
|
||||
simulator knows the current time. See the "CONNECTING TO C++" examples.
|
||||
|
||||
=item Is the PLI supported?
|
||||
|
||||
No.
|
||||
@@ -1542,6 +1655,25 @@ In this example, clk is a bool you can read or set as any other variable.
|
||||
The value of normal signals may be set, though clocks shouldn't be changed
|
||||
by your code or you'll get strange results.
|
||||
|
||||
=item Should a module be in Verilog or SystemC?
|
||||
|
||||
Sometimes there is a block that just interconnects cells, 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 cells as to if they are SystemC or
|
||||
Verilog. Then:
|
||||
|
||||
A module with only SystemC cells below must be SystemC.
|
||||
|
||||
A module with a mix of Verilog and SystemC cells below must be SystemC. (As
|
||||
Verilator cannot connect to lower-level SystemC cells.)
|
||||
|
||||
A module with only Verilog cells below can be either, but for best
|
||||
performance should be Verilog. (The exception is if you have a design that
|
||||
is instantiated many times; in this case Verilating one of the lower
|
||||
modules and instantiating that Verilated cells multiple times into a
|
||||
SystemC module *may* be faster.)
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
@@ -1566,7 +1698,7 @@ outputs. Now, the following should fail:
|
||||
cd test_regress
|
||||
t/t_BUG.pl
|
||||
|
||||
Finally, Mail the bug report to C<[email protected]>
|
||||
Finally, Mail the bug report to C<[email protected]>.
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
@@ -1593,8 +1725,8 @@ scratch in C++. This added many optimizations, yielding about a 2-5x
|
||||
performance gain.
|
||||
|
||||
Currently, various language features and performance enhancements are added
|
||||
as the need arises. Verilator is now about 2x faster then in 2002, and
|
||||
as fast as most popular commercial simulators.
|
||||
as the need arises. Verilator is now about 2x faster then in 2002, and is
|
||||
faster then many popular commercial simulators.
|
||||
|
||||
=head1 CONTRIBUTORS
|
||||
|
||||
|
||||
@@ -192,5 +192,5 @@ C<verilator>
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "$V4/bin/verilator_difftree ~/SandBox/workwsnyder/verilator/test_c/obj_dir/V*_03_*.tree $V4/test_c/obj_dir/V*_03_*.tree"
|
||||
### compile-command: "$V4/bin/verilator_difftree $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
|
||||
### End:
|
||||
|
||||
@@ -16,6 +16,8 @@ AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_INSTALL
|
||||
AC_PATH_PROG(PERL,perl)
|
||||
AC_PATH_PROG(YACC,bison)
|
||||
AC_PATH_PROG(LEX,flex)
|
||||
|
||||
dnl Checks for libraries.
|
||||
|
||||
|
||||
+44
-5
@@ -40,11 +40,11 @@ using namespace std;
|
||||
//=========================================================================
|
||||
// Basic types
|
||||
|
||||
typedef uint8_t CData; ///< Verilated data, 1-8 bits
|
||||
typedef uint16_t SData; ///< Verilated data, 9-16 bits
|
||||
typedef uint32_t IData; ///< Verilated data, 17-32 bits
|
||||
typedef vluint64_t QData; ///< Verilated data, 33-64 bits
|
||||
typedef uint32_t WData; ///< Verilated data, >64 bits, as an array
|
||||
typedef vluint8_t CData; ///< Verilated data, 1-8 bits
|
||||
typedef vluint16_t SData; ///< Verilated data, 9-16 bits
|
||||
typedef vluint32_t IData; ///< Verilated data, 17-32 bits
|
||||
typedef vluint64_t QData; ///< Verilated data, 33-64 bits
|
||||
typedef vluint32_t WData; ///< Verilated data, >64 bits, as an array
|
||||
|
||||
typedef const WData* WDataInP; ///< Array input to a function
|
||||
typedef WData* WDataOutP; ///< Array output from a function
|
||||
@@ -115,6 +115,13 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Functions overridable by user defines
|
||||
|
||||
#ifndef VL_PRINTF
|
||||
# define VL_PRINTF printf ///< Print ala printf; may redefine if desired
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator global static information class
|
||||
|
||||
@@ -1321,6 +1328,38 @@ static inline WDataOutP VL_CONST_W_16X(int obits, WDataOutP o,
|
||||
o[0]=d0; o[1]=d1; o[2]=d2; o[3]=d3; o[4]=d4; o[5]=d5; o[6]=d6; o[7]=d7;
|
||||
o[8]=d8; o[9]=d9; o[10]=d10; o[11]=d11; o[12]=d12; o[13]=d13; o[14]=d14; o[15]=d15;
|
||||
_END(obits,16); }
|
||||
static inline WDataOutP VL_CONST_W_17X(int obits, WDataOutP o,
|
||||
I d16,
|
||||
I d15,I d14,I d13,I d12,I d11,I d10,I d9,I d8,
|
||||
I d7,I d6,I d5,I d4,I d3,I d2,I d1,I d0) {
|
||||
o[0]=d0; o[1]=d1; o[2]=d2; o[3]=d3; o[4]=d4; o[5]=d5; o[6]=d6; o[7]=d7;
|
||||
o[8]=d8; o[9]=d9; o[10]=d10; o[11]=d11; o[12]=d12; o[13]=d13; o[14]=d14; o[15]=d15;
|
||||
o[16]=d16;
|
||||
_END(obits,17); }
|
||||
static inline WDataOutP VL_CONST_W_18X(int obits, WDataOutP o,
|
||||
I d17,I d16,
|
||||
I d15,I d14,I d13,I d12,I d11,I d10,I d9,I d8,
|
||||
I d7,I d6,I d5,I d4,I d3,I d2,I d1,I d0) {
|
||||
o[0]=d0; o[1]=d1; o[2]=d2; o[3]=d3; o[4]=d4; o[5]=d5; o[6]=d6; o[7]=d7;
|
||||
o[8]=d8; o[9]=d9; o[10]=d10; o[11]=d11; o[12]=d12; o[13]=d13; o[14]=d14; o[15]=d15;
|
||||
o[16]=d16; o[17]=d17;
|
||||
_END(obits,18); }
|
||||
static inline WDataOutP VL_CONST_W_19X(int obits, WDataOutP o,
|
||||
I d18,I d17,I d16,
|
||||
I d15,I d14,I d13,I d12,I d11,I d10,I d9,I d8,
|
||||
I d7,I d6,I d5,I d4,I d3,I d2,I d1,I d0) {
|
||||
o[0]=d0; o[1]=d1; o[2]=d2; o[3]=d3; o[4]=d4; o[5]=d5; o[6]=d6; o[7]=d7;
|
||||
o[8]=d8; o[9]=d9; o[10]=d10; o[11]=d11; o[12]=d12; o[13]=d13; o[14]=d14; o[15]=d15;
|
||||
o[16]=d16; o[17]=d17; o[18]=d18;
|
||||
_END(obits,19); }
|
||||
static inline WDataOutP VL_CONST_W_20X(int obits, WDataOutP o,
|
||||
I d19,I d18,I d17,I d16,
|
||||
I d15,I d14,I d13,I d12,I d11,I d10,I d9,I d8,
|
||||
I d7,I d6,I d5,I d4,I d3,I d2,I d1,I d0) {
|
||||
o[0]=d0; o[1]=d1; o[2]=d2; o[3]=d3; o[4]=d4; o[5]=d5; o[6]=d6; o[7]=d7;
|
||||
o[8]=d8; o[9]=d9; o[10]=d10; o[11]=d11; o[12]=d12; o[13]=d13; o[14]=d14; o[15]=d15;
|
||||
o[16]=d16; o[17]=d17; o[18]=d18; o[19]=d19;
|
||||
_END(obits,20); }
|
||||
static inline WDataOutP VL_CONST_W_24X(int obits, WDataOutP o,
|
||||
I d23,I d22,I d21,I d20,I d19,I d18,I d17,I d16,
|
||||
I d15,I d14,I d13,I d12,I d11,I d10,I d9,I d8,
|
||||
|
||||
+22
-12
@@ -68,24 +68,34 @@
|
||||
#ifdef VL_UINTS_DEFINED
|
||||
#elif defined(__CYGWIN__)
|
||||
# include <stdint.h>
|
||||
typedef unsigned char uint8_t; ///< 8-bit basic type
|
||||
typedef unsigned short int uint16_t; ///< 16-bit basic type
|
||||
typedef unsigned long uint32_t; ///< 32-bit basic type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit basic type
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
#elif defined(_WIN32)
|
||||
typedef unsigned char uint8_t; ///< 8-bit basic type
|
||||
typedef unsigned short int uint16_t; ///< 16-bit basic type
|
||||
typedef unsigned int uint32_t; ///< 32-bit basic type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit basic type
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned int vluint32_t; ///< 32-bit unsigned type
|
||||
typedef __int64 vlsint64_t; ///< 64-bit signed type
|
||||
#else // Linux or compliant Unix flavors
|
||||
# include <stdint.h>
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit basic type
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
|
||||
#else // Linux or compliant Unix flavors, -m64
|
||||
# include <stdint.h> // Linux and most flavors
|
||||
# include <inttypes.h> // Solaris
|
||||
typedef uint8_t vluint8_t; ///< 32-bit unsigned type
|
||||
typedef uint16_t vluint16_t; ///< 32-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit signed type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -85,3 +85,6 @@ clean mostlyclean distclean maintainer-clean::
|
||||
-rm -f *__gen*
|
||||
-rm -f *.yy.* y.output y.tab.[cho] *_test
|
||||
-rm -f .objcache*
|
||||
|
||||
distclean maintainer-clean::
|
||||
-rm -f Makefile Makefile_obj
|
||||
|
||||
+3
-3
@@ -48,10 +48,10 @@ COPT = -ggdb -DVL_DEBUG
|
||||
endif
|
||||
#################
|
||||
|
||||
LEX = flex
|
||||
LEX = @LEX@
|
||||
LFLAGS = -d
|
||||
YACC = bison -y
|
||||
YFLAGS = -d -v
|
||||
YACC = @YACC@
|
||||
YFLAGS = -y -d -v
|
||||
|
||||
#LIBS += -ldl
|
||||
#CCMALLOC = /usr/local/lib/ccmalloc-gcc.o -lccmalloc -ldl
|
||||
|
||||
@@ -298,8 +298,10 @@ private:
|
||||
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
||||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||
// Summarize a long list of combo inputs as just "combo"
|
||||
#ifndef __COVERITY__ // Else dead code on next line.
|
||||
if (combo) nodep->sensesp()->addSensesp
|
||||
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
|
||||
#endif
|
||||
wantactivep = m_namer.getActive(nodep->fileline(), nodep->sensesp());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ private:
|
||||
bodysp = newFireAssert(nodep,message);
|
||||
// We assert the property is always true... so report when it fails
|
||||
// (Note this is opposite the behavior of coverage statements.)
|
||||
//FIX 'never' operator: not hold in current or any future cycle
|
||||
// Need: 'never' operator: not hold in current or any future cycle
|
||||
propp = new AstLogNot (nodep->fileline(), propp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
|
||||
+6
-4
@@ -33,8 +33,8 @@
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
uint64_t AstNode::s_editCntGbl=0;
|
||||
uint64_t AstNode::s_editCntLast=0;
|
||||
vluint64_t AstNode::s_editCntGbl=0;
|
||||
vluint64_t AstNode::s_editCntLast=0;
|
||||
|
||||
// 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
|
||||
@@ -784,14 +784,16 @@ void AstNode::dumpTreeFile(const string& filename, bool append) {
|
||||
dumpTree(*logsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.debugCheck() || v3Global.opt.dumpTree()) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
// set by other steps if it is called in the middle of other operations
|
||||
if (AstNetlist* netp=this->castNetlist()) V3Broken::brokenAll(netp);
|
||||
// Next dump can indicate start from here
|
||||
editCountSetLast();
|
||||
}
|
||||
// Next dump can indicate start from here
|
||||
editCountSetLast();
|
||||
}
|
||||
|
||||
void AstNode::v3errorEnd(ostringstream& str) {
|
||||
|
||||
+13
-10
@@ -57,6 +57,7 @@ public:
|
||||
COVERAGE_BLOCK_OFF,
|
||||
INLINE_MODULE,
|
||||
NO_INLINE_MODULE,
|
||||
NO_INLINE_TASK,
|
||||
PUBLIC_MODULE,
|
||||
PUBLIC_TASK
|
||||
};
|
||||
@@ -391,9 +392,9 @@ private:
|
||||
static int s_cloneCntGbl; // Count of which userp is set
|
||||
|
||||
FileLine* m_fileline; // Where it was declared
|
||||
uint64_t m_editCount; // When it was last edited
|
||||
static uint64_t s_editCntGbl;// Global edit counter
|
||||
static uint64_t s_editCntLast;// Global edit counter, last value for printing * near node #s
|
||||
vluint64_t m_editCount; // When it was last edited
|
||||
static vluint64_t s_editCntGbl; // Global edit counter
|
||||
static vluint64_t s_editCntLast;// Global edit counter, last value for printing * near node #s
|
||||
|
||||
// Attributes
|
||||
bool m_signed; // Node is signed
|
||||
@@ -539,10 +540,10 @@ public:
|
||||
void user5(int val) { user5p(AstNUser::fromInt(val)); }
|
||||
static void user5ClearTree() { s_user5CntGbl++; } // Clear userp()'s across the entire tree
|
||||
|
||||
uint64_t editCount() const { return m_editCount; }
|
||||
vluint64_t editCount() const { return m_editCount; }
|
||||
void editCountInc() { m_editCount = s_editCntGbl++; }
|
||||
static uint64_t editCountLast() { return s_editCntLast; }
|
||||
static uint64_t editCountGbl() { return s_editCntGbl; }
|
||||
static vluint64_t editCountLast() { return s_editCntLast; }
|
||||
static vluint64_t editCountGbl() { return s_editCntGbl; }
|
||||
static void editCountSetLast() { s_editCntLast = editCountGbl(); }
|
||||
|
||||
// ACCESSORS for specific types
|
||||
@@ -585,6 +586,7 @@ public:
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash::Illegal()); } // Not a node that supports it
|
||||
virtual bool same(AstNode* otherp) const { return true; }
|
||||
virtual bool maybePointedTo() const { return false; } // Another AstNode* may have a pointer into this node, other then normal front/back/etc.
|
||||
virtual bool broken() const { return false; }
|
||||
virtual bool emitWordForm() { return false; }
|
||||
|
||||
@@ -774,14 +776,14 @@ struct AstNodeAssign : public AstNodeStmt {
|
||||
|
||||
struct AstNodeFor : public AstNodeStmt {
|
||||
AstNodeFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
|
||||
AstNode* assignsp, AstNode* bodysp)
|
||||
AstNode* incsp, AstNode* bodysp)
|
||||
: AstNodeStmt(fileline) {
|
||||
addNOp1p(initsp); setOp2p(condp); addNOp3p(assignsp); addNOp4p(bodysp);
|
||||
addNOp1p(initsp); setOp2p(condp); addNOp3p(incsp); addNOp4p(bodysp);
|
||||
}
|
||||
virtual ~AstNodeFor() {}
|
||||
AstNode* initsp() const { return op1p()->castNode(); } // op1= initial statement
|
||||
AstNode* initsp() const { return op1p()->castNode(); } // op1= initial statements
|
||||
AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue
|
||||
AstNode* assignsp() const { return op3p()->castNode(); } // op3= final statements
|
||||
AstNode* incsp() const { return op3p()->castNode(); } // op3= increment statements
|
||||
AstNode* bodysp() const { return op4p()->castNode(); } // op4= body of loop
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
@@ -922,6 +924,7 @@ public:
|
||||
virtual ~AstNodeFTask() {}
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
// {AstFunc only} op1 = Range output variable
|
||||
// op3 = Statements/Ports/Vars
|
||||
void name(const string& name) { m_name = name; }
|
||||
|
||||
+25
-6
@@ -85,7 +85,7 @@ int AstVar::widthTotalBytes() const {
|
||||
}
|
||||
|
||||
string AstVar::verilogKwd() const {
|
||||
if (isTristate()) {
|
||||
if (isInout()) {
|
||||
return "inout";
|
||||
} else if (isInput()) {
|
||||
return "input";
|
||||
@@ -93,6 +93,8 @@ string AstVar::verilogKwd() const {
|
||||
return "output";
|
||||
} else if (isInteger()) {
|
||||
return "integer";
|
||||
} else if (isTristate()) {
|
||||
return "tri";
|
||||
} else if (varType()==AstVarType::WIRE) {
|
||||
return "wire";
|
||||
} else {
|
||||
@@ -101,6 +103,18 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
|
||||
string AstVar::cType() const {
|
||||
if (widthMin() == 1) {
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_WORDSIZE) {
|
||||
return "uint32_t";
|
||||
} else if (isScWide()) {
|
||||
return "uint32_t"; // []'s added later
|
||||
} else {
|
||||
return "uint64_t";
|
||||
}
|
||||
}
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (widthMin() == 1) {
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_WORDSIZE) {
|
||||
@@ -130,7 +144,7 @@ uint32_t AstVar::arrayElements() const {
|
||||
bool AstScope::broken() const {
|
||||
return ((m_aboveScopep && !m_aboveScopep->brokeExists())
|
||||
|| (m_aboveCellp && !m_aboveCellp->brokeExists())
|
||||
|| !m_modp || !((AstNode*)m_modp)->brokeExists());
|
||||
|| !m_modp || !m_modp->brokeExists());
|
||||
}
|
||||
|
||||
void AstScope::cloneRelink() {
|
||||
@@ -325,14 +339,18 @@ void AstVarRef::dump(ostream& str) {
|
||||
void AstVar::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isSc()) str<<" [SC]";
|
||||
if (isInput()) str<<" [I]";
|
||||
if (isPrimaryIO()) str<<(isInput()?" [PI]":" [PO]");
|
||||
if (isOutput()) str<<" [O]";
|
||||
if (isPrimaryIO()) str<<(isInout()?" [PIO]":(isInput()?" [PI]":" [PO]"));
|
||||
else {
|
||||
if (isInout()) str<<" [IO]";
|
||||
else if (isInput()) str<<" [I]";
|
||||
else if (isOutput()) str<<" [O]";
|
||||
}
|
||||
if (isUsedClock()) str<<" [C]";
|
||||
if (isSigPublic()) str<<" [P]";
|
||||
if (attrClockEn()) str<<" [aCLKEN]";
|
||||
if (attrFileDescr()) str<<" [aFD]";
|
||||
if (isFuncLocal() || isFuncReturn()) str<<" [FUNC]";
|
||||
if (isFuncReturn()) str<<" [FUNCRTN]";
|
||||
else if (isFuncLocal()) str<<" [FUNC]";
|
||||
str<<" "<<varType();
|
||||
}
|
||||
void AstSenTree::dump(ostream& str) {
|
||||
@@ -358,6 +376,7 @@ void AstNodeFTaskRef::dump(ostream& str) {
|
||||
}
|
||||
void AstNodeFTask::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (taskPublic()) str<<" [PUBLIC]";
|
||||
}
|
||||
void AstCoverDecl::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
|
||||
+36
-15
@@ -286,8 +286,10 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
AstVarType varType() const { return m_varType; } // * = Type of variable
|
||||
string cType() const; // Return C type: bool, uint32_t, uint64_t, etc.
|
||||
string cType() const; // Return C type for declaration: bool, uint32_t, uint64_t, etc.
|
||||
string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv
|
||||
void combineType(AstVarType type);
|
||||
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable
|
||||
AstRange* arraysp() const { return op2p()->castRange(); } // op2 = Array(s) of variable
|
||||
@@ -314,10 +316,13 @@ public:
|
||||
void name(const string& name) { m_name = name; }
|
||||
bool isInput() const { return m_input; }
|
||||
bool isOutput() const { return m_output; }
|
||||
bool isTristate() const { return (m_tristate); }
|
||||
bool isInOnly() const { return m_input && !m_output; }
|
||||
bool isOutOnly() const { return m_output && !m_input; }
|
||||
bool isInout() const { return m_input && m_output; }
|
||||
bool isTristate() const { return m_tristate; }
|
||||
bool isPrimaryIO() const { return m_primaryIO; }
|
||||
bool isPrimaryIn() const { return isPrimaryIO() && isInput(); }
|
||||
bool isIO() const { return (m_input||m_output||m_tristate); }
|
||||
bool isIO() const { return (m_input||m_output); }
|
||||
bool isSignal() const { return (varType()==AstVarType::WIRE || varType()==AstVarType::IMPLICIT
|
||||
|| varType()==AstVarType::REG || varType()==AstVarType::INTEGER); }
|
||||
bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP
|
||||
@@ -419,6 +424,7 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void cloneRelink();
|
||||
virtual bool broken() const;
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const { return m_name; } // * = Scope name
|
||||
void name(const string& name) { m_name = name; }
|
||||
string nameDotless() const;
|
||||
@@ -480,6 +486,7 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual bool broken() const { return ( (m_varp && !m_varp->brokeExists())
|
||||
|| (m_scopep && !m_scopep->brokeExists())); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name
|
||||
virtual void dump(ostream& str);
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
@@ -604,6 +611,7 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool broken() const { return (m_clkReqVarp && !m_clkReqVarp->brokeExists()); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const { return m_name; }
|
||||
AstNode* stmtsp() const { return op2p()->castNode(); } // op2 = List of statements
|
||||
AstActive* activesp() const { return op3p()->castActive(); } // op3 = List of i/sblocks
|
||||
@@ -648,19 +656,21 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool broken() const { return (m_modp && !m_modp->brokeExists()); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
// ACCESSORS
|
||||
virtual string name() const { return m_name; } // * = Cell name
|
||||
void name(const string& name) { m_name = name; }
|
||||
string origName() const { return m_origName; } // * = Original name
|
||||
void origName(const string& name) { m_origName = name; }
|
||||
string modName() const { return m_modName; } // * = Instance name
|
||||
void modName(const string& name) { m_modName = name; }
|
||||
AstPin* pinsp() const { return op1p()->castPin(); } // op1 = List of cell ports
|
||||
AstPin* paramsp() const { return op2p()->castPin(); } // op2 = List of parameter #(##) values
|
||||
AstRange* rangep() const { return op3p()->castRange(); } // op3 = Range of arrayed instants (NULL=not ranged)
|
||||
AstModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
|
||||
void addPinsp(AstPin* pinp) { addOp1p(pinp); }
|
||||
void addParamsp(AstPin* pinp) { addOp2p(pinp); }
|
||||
void modp(AstModule* modp) { m_modp = modp; }
|
||||
void modName(const string& name) { m_modName = name; }
|
||||
void name(const string& name) { m_name = name; }
|
||||
void addPinsp(AstPin* nodep) { addOp1p(nodep); }
|
||||
void addParamsp(AstPin* nodep) { addOp2p(nodep); }
|
||||
void modp(AstModule* nodep) { m_modp = nodep; }
|
||||
};
|
||||
|
||||
struct AstCellInline : public AstNode {
|
||||
@@ -805,7 +815,7 @@ public:
|
||||
class Initial {}; // for creator type-overload selection
|
||||
class Settle {}; // for creator type-overload selection
|
||||
class Never {}; // for creator type-overload selection
|
||||
AstSenItem(FileLine* fl, AstEdgeType edgeType, AstVarRef* varrefp)
|
||||
AstSenItem(FileLine* fl, AstEdgeType edgeType, AstNodeVarRef* varrefp)
|
||||
: AstNode(fl) {
|
||||
m_edgeType = edgeType;
|
||||
setOp1p(varrefp);
|
||||
@@ -838,7 +848,7 @@ public:
|
||||
AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge
|
||||
void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge
|
||||
AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized
|
||||
AstVarRef* varrefp() const { return op1p()->castVarRef(); } // op1 = Signal sensitized
|
||||
AstNodeVarRef* varrefp() const { return op1p()->castNodeVarRef(); } // op1 = Signal sensitized
|
||||
//
|
||||
bool isClocked() const { return edgeType().clockedStmt(); }
|
||||
bool isCombo() const { return edgeType()==AstEdgeType::COMBO; }
|
||||
@@ -864,6 +874,7 @@ public:
|
||||
virtual AstNode* clone() { return new AstSenTree(*this); }
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
bool isMulti() const { return m_multi; }
|
||||
AstSenItem* sensesp() const { return op1p()->castSenItem(); } // op1 = Sensitivity list
|
||||
void addSensesp(AstSenItem* nodep) { addOp1p(nodep); }
|
||||
@@ -1049,6 +1060,7 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
int column() const { return m_column; }
|
||||
const string& comment() const { return m_text; } // text to insert in code
|
||||
const string& typeText() const { return m_typeText; }
|
||||
@@ -1241,8 +1253,8 @@ struct AstFOpen : public AstNodeStmt {
|
||||
|
||||
struct AstGenFor : public AstNodeFor {
|
||||
AstGenFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
|
||||
AstNode* assignsp, AstNode* bodysp)
|
||||
: AstNodeFor(fileline, initsp, condp, assignsp, bodysp) {
|
||||
AstNode* incsp, AstNode* bodysp)
|
||||
: AstNodeFor(fileline, initsp, condp, incsp, bodysp) {
|
||||
}
|
||||
virtual ~AstGenFor() {}
|
||||
virtual AstType type() const { return AstType::GENFOR;}
|
||||
@@ -1252,8 +1264,8 @@ struct AstGenFor : public AstNodeFor {
|
||||
|
||||
struct AstFor : public AstNodeFor {
|
||||
AstFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
|
||||
AstNode* assignsp, AstNode* bodysp)
|
||||
: AstNodeFor(fileline, initsp, condp, assignsp, bodysp) {
|
||||
AstNode* incsp, AstNode* bodysp)
|
||||
: AstNodeFor(fileline, initsp, condp, incsp, bodysp) {
|
||||
}
|
||||
virtual ~AstFor() {}
|
||||
virtual AstType type() const { return AstType::FOR;}
|
||||
@@ -1502,6 +1514,7 @@ public:
|
||||
virtual AstNode* clone() { return new AstTraceDecl(*this); }
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual string name() const { return m_showname; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
string showname() const { return m_showname; } // * = Var name
|
||||
virtual bool same(AstNode* samep) const { return false; }
|
||||
// Details on what we're tracing
|
||||
@@ -1593,7 +1606,8 @@ public:
|
||||
bool hasClocked() const { return m_sensesp->hasClocked(); }
|
||||
};
|
||||
|
||||
class AstAttrOf : public AstNode {
|
||||
struct AstAttrOf : public AstNode {
|
||||
private:
|
||||
// Return a value of a attribute, for example a LSB or array LSB of a signal
|
||||
AstAttrType m_attrType; // What sort of extraction
|
||||
int m_dimension; // Dimension number (0 is leftmost), for ARRAY_LSB extractions
|
||||
@@ -1735,6 +1749,7 @@ struct AstRedXor : public AstNodeUniop {
|
||||
virtual int instrCount() const { return 1+V3Number::log2b(width()); }
|
||||
};
|
||||
struct AstRedXnor : public AstNodeUniop {
|
||||
// AstRedXnors are replaced with AstRedXors in V3Const.
|
||||
AstRedXnor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {
|
||||
width(1,1); }
|
||||
virtual ~AstRedXnor() {}
|
||||
@@ -2772,6 +2787,7 @@ private:
|
||||
bool m_funcPublic:1; // From user public task/function
|
||||
bool m_isStatic:1; // Function is declared static (no this)
|
||||
bool m_symProlog:1; // Setup symbol table for later instructions
|
||||
bool m_entryPoint:1; // User may call into this top level function
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="")
|
||||
: AstNode(fl) {
|
||||
@@ -2787,12 +2803,15 @@ public:
|
||||
m_funcPublic = false;
|
||||
m_isStatic = true; // Note defaults to static, later we see where thisp is needed
|
||||
m_symProlog = false;
|
||||
m_entryPoint = false;
|
||||
}
|
||||
virtual ~AstCFunc() {}
|
||||
virtual AstType type() const { return AstType::CFUNC;}
|
||||
virtual AstNode* clone() { return new AstCFunc(*this); }
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); }
|
||||
virtual string name() const { return m_name; }
|
||||
virtual bool broken() const { return ( (m_scopep && !m_scopep->brokeExists())); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return ((funcType()==samep->castCFunc()->funcType())
|
||||
&& (rtnTypeVoid()==samep->castCFunc()->rtnTypeVoid())
|
||||
@@ -2822,6 +2841,8 @@ public:
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
bool symProlog() const { return m_symProlog; }
|
||||
void symProlog(bool flag) { m_symProlog = flag; }
|
||||
bool entryPoint() const { return m_entryPoint; }
|
||||
void entryPoint(bool flag) { m_entryPoint = flag; }
|
||||
//
|
||||
// If adding node accessors, see below
|
||||
AstNode* argsp() const { return op1p()->castNode(); }
|
||||
|
||||
+18
-29
@@ -23,6 +23,7 @@
|
||||
// Each module:
|
||||
// Look for BEGINs
|
||||
// BEGIN(VAR...) -> VAR ... {renamed}
|
||||
// FOR -> WHILEs
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -48,25 +49,6 @@ private:
|
||||
string m_beginScope; // Name of begin blocks above us
|
||||
//int debug() { return 9; }
|
||||
|
||||
bool nameMatchesGen(const char* namep, string& numr) {
|
||||
numr = "";
|
||||
bool needbar = false;
|
||||
for (const char* cp=namep; *cp; ) {
|
||||
if (0==strncmp(cp,"genblk",6) || 0==strncmp(cp,"genfor",6)) {
|
||||
cp += 6;
|
||||
} else if (isdigit(*cp)) {
|
||||
if (needbar) { numr += '_'; needbar = false; }
|
||||
numr += *cp++;
|
||||
} else if (*cp=='_') {
|
||||
cp++;
|
||||
needbar = true;
|
||||
} else {
|
||||
return false; // Not exact match
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstModule* nodep, AstNUser*) {
|
||||
m_modp = nodep;
|
||||
@@ -83,15 +65,6 @@ private:
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
string oldScope = m_beginScope;
|
||||
{
|
||||
//string nameNum;
|
||||
//string oldNum;
|
||||
//if (nameMatchesGen(oldScope.c_str(), oldNum/*ref*/)
|
||||
// && nameMatchesGen(nodep->name().c_str(), nameNum/*ref*/)
|
||||
// && 0) { // Messes up V3Link
|
||||
// // Need to leave the dot or we mess up later V3LinkDot
|
||||
// // gen[blk|for]##_gen[blk|for]## -> gen[blk|for]##__DOT__##...
|
||||
// m_beginScope = oldScope + "__DOT__"+nameNum;
|
||||
|
||||
//UINFO(8,"nname "<<m_beginScope<<endl);
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
@@ -99,7 +72,7 @@ private:
|
||||
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
||||
string ident = dottedname.substr(0,pos);
|
||||
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
||||
if (m_beginScope=="") m_beginScope = nodep->name();
|
||||
if (m_beginScope=="") m_beginScope = ident;
|
||||
else m_beginScope = m_beginScope + "__DOT__"+ident;
|
||||
// Create CellInline for dotted resolution
|
||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||
@@ -141,6 +114,22 @@ private:
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstFor* nodep, AstNUser*) {
|
||||
// So later optimizations don't need to deal with them,
|
||||
// FOR(init,cond,assign,body) -> init,WHILE(cond) { body, assign }
|
||||
AstNode* initsp = nodep->initsp(); if (initsp) initsp->unlinkFrBackWithNext();
|
||||
AstNode* condp = nodep->condp(); if (condp) condp->unlinkFrBackWithNext();
|
||||
AstNode* incsp = nodep->incsp(); if (incsp) incsp->unlinkFrBackWithNext();
|
||||
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
||||
bodysp = bodysp->addNext(incsp);
|
||||
AstNode* newp = new AstWhile(nodep->fileline(),
|
||||
condp,
|
||||
bodysp);
|
||||
initsp = initsp->addNext(newp);
|
||||
newp = initsp;
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
+6
-4
@@ -81,13 +81,15 @@ private:
|
||||
// // so userp and friends may not be used
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
BrokenTable::add(nodep);
|
||||
if (nodep->maybePointedTo()) {
|
||||
BrokenTable::add(nodep);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
BrokenMarkVisitor(AstNode* nodep) {
|
||||
nodep->iterateAndNext(*this, NULL);
|
||||
BrokenMarkVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~BrokenMarkVisitor() {}
|
||||
};
|
||||
@@ -99,7 +101,7 @@ class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
if (nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node\n");
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n");
|
||||
}
|
||||
if (v3Global.assertWidthsSame()) {
|
||||
if (nodep->width() != nodep->widthMin()) {
|
||||
|
||||
+16
-15
@@ -29,12 +29,12 @@
|
||||
// (other items))
|
||||
// body
|
||||
// Or, converts to a if/else tree.
|
||||
// Constants:
|
||||
// RHS, Replace 5'bx_1_x with a module global we init to a random value
|
||||
// CONST(5'bx_1_x) -> VARREF(_{numberedtemp})
|
||||
// -> VAR(_{numberedtemp})
|
||||
// -> INITIAL(VARREF(_{numberedtemp}), OR(5'bx_1_x,AND(random,5'b0_1_x))
|
||||
// OPTIMIZE: Must not collapse this initial back into the equation.
|
||||
// FUTURES:
|
||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
||||
// Enter all into multimap, sort by value and use a tree of < and == compares.
|
||||
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
|
||||
// Ignoring mask, check each value is unique (using multimap as above?)
|
||||
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -126,22 +127,22 @@ private:
|
||||
//int debug() { return 9; }
|
||||
|
||||
// METHODS
|
||||
bool checkCaseTree(AstCase* nodep) {
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (!icondp->castConst()) width = 999; // Can't parse; not a constant
|
||||
if (!icondp->castConst()) width = CASE_BARF; // Can't parse; not a constant
|
||||
m_caseItems++;
|
||||
}
|
||||
}
|
||||
m_caseWidth = width;
|
||||
if (width==0 || width > CASE_OVERLAP_WIDTH) {
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false; // Too wide for analysis
|
||||
}
|
||||
m_caseWidth = width;
|
||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||
// Zero list of items for each value
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) m_valueItem[i] = NULL;
|
||||
@@ -172,7 +173,7 @@ private:
|
||||
}
|
||||
}
|
||||
// Defaults were moved to last in the caseitem list by V3Link
|
||||
if (!itemp->condsp()) { // Case statement's default... Fill the table
|
||||
if (itemp->isDefault()) { // Case statement's default... Fill the table
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
@@ -207,7 +208,7 @@ private:
|
||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
||||
|
||||
if (tree0p == tree1p) {
|
||||
// Same logic on both sides, so we can just return one of em
|
||||
// Same logic on both sides, so we can just return one of 'em
|
||||
return tree0p;
|
||||
}
|
||||
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
||||
@@ -357,7 +358,7 @@ private:
|
||||
V3Case::caseLint(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
|
||||
if (checkCaseTree(nodep) && v3Global.opt.oCase()) {
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
// we can make a tree of statements to avoid extra comparisons
|
||||
m_statCaseFast++;
|
||||
@@ -375,13 +376,13 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
CaseVisitor(AstNode* nodep) {
|
||||
CaseVisitor(AstNetlist* nodep) {
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~CaseVisitor() {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
V3Stats::addStat("Optimizations, Cases priority-encoded", m_statCaseSlow);
|
||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -239,6 +239,10 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstCCall* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->argsp());
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
|
||||
@@ -192,6 +192,7 @@ private:
|
||||
funcp->dontCombine(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_evalFuncp = funcp;
|
||||
}
|
||||
@@ -202,6 +203,7 @@ private:
|
||||
funcp->slow(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_initFuncp = funcp;
|
||||
}
|
||||
@@ -211,6 +213,7 @@ private:
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->entryPoint(true);
|
||||
funcp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(),
|
||||
" "+EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
@@ -225,6 +228,7 @@ private:
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->entryPoint(true);
|
||||
m_scopep->addActivep(funcp);
|
||||
m_settleFuncp = funcp;
|
||||
}
|
||||
|
||||
+90
-18
@@ -70,6 +70,16 @@ private:
|
||||
if (!rnodep->lhsp()->castConst()) return false;
|
||||
return true;
|
||||
}
|
||||
bool operandAsvSame (AstNode* nodep) {
|
||||
// BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...)
|
||||
AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv();
|
||||
if (!bnodep) return false;
|
||||
AstNodeBiComAsv* rnodep = bnodep->rhsp()->castNodeBiComAsv();
|
||||
if (!rnodep) return false;
|
||||
if (rnodep->type() != bnodep->type()) return false;
|
||||
if (rnodep->width() != bnodep->width()) return false;
|
||||
return operandsSame(bnodep->lhsp(), rnodep->lhsp());
|
||||
}
|
||||
bool operandHugeShiftL(AstNodeBiop* nodep) {
|
||||
return (nodep->rhsp()->castConst()
|
||||
&& nodep->rhsp()->castConst()->asInt() >= (uint32_t)(nodep->width()));
|
||||
@@ -87,6 +97,10 @@ private:
|
||||
return (nodep->castConst()
|
||||
&& !nodep->castConst()->num().isFourState());
|
||||
}
|
||||
bool operandIsPowTwo(AstNode* nodep) {
|
||||
if (!operandIsTwostate(nodep)) return false;
|
||||
return (1==nodep->castConst()->num().countOnes());
|
||||
}
|
||||
bool operandShiftOp(AstNodeBiop* nodep) {
|
||||
if (!nodep->rhsp()->castConst()) return false;
|
||||
AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop();
|
||||
@@ -166,15 +180,10 @@ private:
|
||||
static bool operandsSame(AstNode* node1p, AstNode* node2p) {
|
||||
// For now we just detect constants & simple vars, though it could be more generic
|
||||
if (node1p->castConst() && node2p->castConst()) {
|
||||
// Match ignoring any X values
|
||||
V3Number num (node1p->fileline(), 1);
|
||||
num.opCaseEq(node1p->castConst()->num(), node2p->castConst()->num());
|
||||
return num.isNeqZero();
|
||||
return node1p->sameTree(node2p);
|
||||
}
|
||||
else if (node1p->castVarRef() && node2p->castVarRef()
|
||||
&& node1p->sameTree(node2p)) {
|
||||
// Same variable
|
||||
return true;
|
||||
else if (node1p->castVarRef() && node2p->castVarRef()) {
|
||||
return node1p->sameTree(node2p);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -235,6 +244,12 @@ private:
|
||||
void replaceZero(AstNode* nodep) {
|
||||
replaceNum(nodep, 0); nodep=NULL;
|
||||
}
|
||||
void replaceAllOnes (AstNode* nodep) {
|
||||
V3Number num (nodep->fileline(), nodep->width(), 0);
|
||||
V3Number ones (nodep->fileline(), nodep->width());
|
||||
ones.opNot(num);
|
||||
replaceNum(nodep, ones); nodep=NULL;
|
||||
}
|
||||
void replaceConst(AstNodeUniop* nodep) {
|
||||
V3Number num (nodep->fileline(), nodep->width());
|
||||
nodep->numberOperate(num, nodep->lhsp()->castConst()->num());
|
||||
@@ -279,8 +294,9 @@ private:
|
||||
// Keep RHS, remove LHS
|
||||
replaceWChild(nodep, nodep->rhsp());
|
||||
}
|
||||
void replaceAsvConst (AstNodeBiop* nodep) {
|
||||
void replaceAsv (AstNodeBiop* nodep) {
|
||||
// BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c)
|
||||
// BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c)
|
||||
//nodep->dumpTree(cout, " repAsvConst_old: ");
|
||||
AstNode* ap = nodep->lhsp();
|
||||
AstNodeBiop* rp = nodep->rhsp()->castNodeBiop();
|
||||
@@ -294,7 +310,7 @@ private:
|
||||
nodep->rhsp(cp);
|
||||
rp->lhsp(ap);
|
||||
rp->rhsp(bp);
|
||||
replaceConst(rp);
|
||||
if (rp->lhsp()->castConst() && rp->rhsp()->castConst()) replaceConst(rp);
|
||||
//nodep->dumpTree(cout, " repAsvConst_new: ");
|
||||
}
|
||||
void replaceExtend (AstNode* nodep, AstNode* arg0p) {
|
||||
@@ -317,6 +333,24 @@ private:
|
||||
newp->lhsp()->widthSignedFrom(nodep);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
void replaceMulShift (AstMul* nodep) { // Mul, but not MulS as not simple shift
|
||||
UINFO(5,"MUL(2^n,b)->SHIFTL(b,n) "<<nodep<<endl);
|
||||
int amount = nodep->lhsp()->castConst()->num().mostSetBitP1()-1; // 2^n->n+1
|
||||
AstNode* opp = nodep->rhsp()->unlinkFrBack();
|
||||
AstShiftL* newp = new AstShiftL(nodep->fileline(),
|
||||
opp, new AstConst(nodep->fileline(), amount));
|
||||
newp->widthSignedFrom(nodep);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
void replaceDivShift (AstDiv* nodep) { // Mul, but not MulS as not simple shift
|
||||
UINFO(5,"DIV(b,2^n)->SHIFTR(b,n) "<<nodep<<endl);
|
||||
int amount = nodep->rhsp()->castConst()->num().mostSetBitP1()-1; // 2^n->n+1
|
||||
AstNode* opp = nodep->lhsp()->unlinkFrBack();
|
||||
AstShiftR* newp = new AstShiftR(nodep->fileline(),
|
||||
opp, new AstConst(nodep->fileline(), amount));
|
||||
newp->widthSignedFrom(nodep);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
void replaceShiftOp (AstNodeBiop* nodep) {
|
||||
UINFO(5,"SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) "<<nodep<<endl);
|
||||
AstNRelinker handle;
|
||||
@@ -781,7 +815,7 @@ private:
|
||||
}
|
||||
UINFO(8,"senItem(NOT...) "<<nodep<<" "<<invert<<endl);
|
||||
if (invert) nodep->edgeType( nodep->edgeType().invert() );
|
||||
AstVarRef* senvarp = lastSensp->unlinkFrBack()->castVarRef();
|
||||
AstNodeVarRef* senvarp = lastSensp->unlinkFrBack()->castNodeVarRef();
|
||||
if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable");
|
||||
sensp->replaceWith(senvarp);
|
||||
sensp->deleteTree(); sensp=NULL;
|
||||
@@ -923,6 +957,7 @@ private:
|
||||
// }" # bracket not paren
|
||||
// ,"function to call"
|
||||
// or ,"AstREPLACEMENT_TYPE{ $accessor }"
|
||||
// or ,"! # Print line number when matches, so can see operations
|
||||
|
||||
// Lint Checks
|
||||
// v--- *1* These ops are always first, as we warn before replacing
|
||||
@@ -947,6 +982,7 @@ private:
|
||||
TREEOP("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
||||
TREEOP("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
||||
TREEOP("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}");
|
||||
TREEOP("AstSub {$lhsp.isZero, $rhsp}", "AstUnaryMin{$rhsp}");
|
||||
TREEOP("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstAnd {$lhsp, $rhsp.isZero}", "replaceZero(nodep)");
|
||||
@@ -960,6 +996,7 @@ private:
|
||||
TREEOP("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstXnor {$lhsp, $rhsp.isZero}", "AstNot{$lhsp}");
|
||||
// Non-zero on one side or the other
|
||||
TREEOP("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)");
|
||||
@@ -970,8 +1007,15 @@ private:
|
||||
TREEOP("AstOr {$lhsp, $rhsp.isAllOnes}", "replaceWRhs(nodep)"); //->allOnes
|
||||
TREEOP("AstLogOr {$lhsp, $rhsp.isNeqZero}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
|
||||
TREEOP("AstPow {operandIsTwo($lhsp), $rhsp}","replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
TREEOP("AstPowS {operandIsTwo($lhsp), $rhsp}","replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
TREEOP("AstXnor {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstMul {operandIsPowTwo($lhsp), $rhsp}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
||||
TREEOP("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
TREEOP("AstPowS {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
// Trinary ops
|
||||
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
|
||||
TREEOP("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
|
||||
@@ -984,8 +1028,9 @@ private:
|
||||
TREEOP("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&b
|
||||
TREEOP("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
||||
// Prefer constants on left, since that often needs a shift, it lets constant red remove the shift
|
||||
TREEOP("AstNodeBiCom{$lhsp, $rhsp.castConst}", "swapSides(nodep)");
|
||||
TREEOP("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsvConst(nodep)");
|
||||
TREEOP("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)");
|
||||
TREEOP("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsv(nodep)");
|
||||
TREEOP("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)");
|
||||
// v--- *1* as These ops are always first, as we warn before replacing
|
||||
TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)");
|
||||
TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)");
|
||||
@@ -1030,6 +1075,31 @@ private:
|
||||
TREEOP ("AstShiftL{operandShiftShift(nodep)}", "replaceShiftShift(nodep)");
|
||||
TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)");
|
||||
TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)");
|
||||
// Identical operands on both sides
|
||||
// AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules
|
||||
// AstAdd->ShiftL(#,1) but uncommon
|
||||
TREEOP("AstAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstChangeXor{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstDiv {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstDivS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstSub {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstXnor {operandsSame($lhsp,,$rhsp)}", "replaceAllOnes(nodep)");
|
||||
TREEOP("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
|
||||
TREEOP("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
||||
TREEOP("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
||||
TREEOP("AstLogAnd {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOP("AstLogOr {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
///=== Verilog operators
|
||||
// Comparison against 1'b0/1'b1; must be careful about widths.
|
||||
// These use Not, so must be Verilog only
|
||||
@@ -1047,7 +1117,6 @@ private:
|
||||
TREEOPV("AstLte {$lhsp->width()==$rhsp->width(), $rhsp.isAllOnes}", "replaceNum(nodep,1)");
|
||||
// Simplify reduction operators
|
||||
// This also gets &{...,0,....} => const 0 (Common for unused_ok signals)
|
||||
TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's
|
||||
TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
|
||||
@@ -1080,6 +1149,7 @@ private:
|
||||
TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)");
|
||||
TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.isOne, }", "replaceSelReplicate(nodep)");
|
||||
// Conversions
|
||||
TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's
|
||||
TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
|
||||
TREEOPV("AstLogIff{$lhsp, $rhsp}", "AstLogNot{AstXor{$lhsp,$rhsp}}");
|
||||
|
||||
@@ -1143,12 +1213,14 @@ void V3Const::constifyAll(AstNetlist* nodep) {
|
||||
visitor.main(nodep);
|
||||
}
|
||||
|
||||
void V3Const::constifyAllLint(AstNode* nodep) {
|
||||
void V3Const::constifyAllLint(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
ConstVisitor visitor (false,false,true,false);
|
||||
visitor.main(nodep);
|
||||
}
|
||||
|
||||
void V3Const::constifyCpp(AstNode* nodep) {
|
||||
void V3Const::constifyCpp(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
ConstVisitor visitor (false,false,false,true);
|
||||
visitor.main(nodep);
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,9 +34,9 @@ public:
|
||||
// Everything that's possible
|
||||
static void constifyAll(AstNetlist* nodep);
|
||||
// Also, warn
|
||||
static void constifyAllLint(AstNode* nodep);
|
||||
static void constifyAllLint(AstNetlist* nodep);
|
||||
// C++ datatypes
|
||||
static void constifyCpp(AstNode* nodep);
|
||||
static void constifyCpp(AstNetlist* nodep);
|
||||
// Only the current node and lower
|
||||
static void constifyTree(AstNode* nodep);
|
||||
};
|
||||
|
||||
+70
-12
@@ -29,11 +29,37 @@
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Dead.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
class DeadModVisitor : public AstNVisitor {
|
||||
// In a module that is dead, cleanup the in-use counts of the modules
|
||||
private:
|
||||
// NODE STATE
|
||||
// ** Shared with DeadVisitor **
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->modp()->user(nodep->modp()->user() - 1);
|
||||
}
|
||||
//-----
|
||||
virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
DeadModVisitor(AstModule* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~DeadModVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Dead state, as a visitor of each AstNode
|
||||
|
||||
@@ -45,10 +71,15 @@ private:
|
||||
// AstVar::user() -> int. Count of number of references
|
||||
// AstVarScope::user() -> int. Count of number of references
|
||||
|
||||
// TYPES
|
||||
typedef multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
|
||||
// STATE
|
||||
vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through three
|
||||
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through three
|
||||
vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_elimUserVars; // Allow removal of user's vars
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
//int debug() { return 9; }
|
||||
|
||||
// METHODS
|
||||
@@ -77,6 +108,25 @@ private:
|
||||
m_varsp.push_back(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
// See if simple assignments to variables may be eliminated because that variable is never used.
|
||||
// Similar code in V3Life
|
||||
m_sideEffect = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
AstVarRef* varrefp = nodep->lhsp()->castVarRef();
|
||||
if (varrefp && !m_sideEffect
|
||||
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
|
||||
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
|
||||
} else { // Track like any other statement
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep, AstNUser*) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
@@ -85,6 +135,8 @@ private:
|
||||
// METHODS
|
||||
void deadCheckMod() {
|
||||
// Kill any unused modules
|
||||
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
||||
// after we've done all the generate blocks
|
||||
for (bool retry=true; retry; ) {
|
||||
retry=false;
|
||||
AstModule* nextmodp;
|
||||
@@ -93,28 +145,33 @@ private:
|
||||
if (modp->level()>2 && modp->user()==0) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4," Dead module "<<modp<<endl);
|
||||
// And its children may now be killable too....
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstCell* cellp=nodep->castCell()) {
|
||||
cellp->modp()->user( cellp->modp()->user() - 1);
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
DeadModVisitor visitor(modp);
|
||||
modp->unlinkFrBack()->deleteTree(); modp=NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool canElim(AstVar* nodep) {
|
||||
return (!nodep->isSigPublic() // Can't elim publics!
|
||||
&& !nodep->isIO()
|
||||
&& (nodep->isTemp() || nodep->isParam() || m_elimUserVars));
|
||||
}
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
for (vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
|
||||
if ((*it)->user() == 0 && canElim((*it)->varp())) {
|
||||
UINFO(4," Dead "<<(*it)<<endl);
|
||||
(*it)->unlinkFrBack()->deleteTree(); (*it)=NULL;
|
||||
AstVarScope* vscp = *it;
|
||||
if (vscp->user() == 0 && canElim(vscp->varp())) {
|
||||
UINFO(4," Dead "<<vscp<<endl);
|
||||
pair <AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
|
||||
for (AssignMap::iterator it = eqrange.first; it != eqrange.second; ++it) {
|
||||
AstNodeAssign* assp = it->second;
|
||||
UINFO(4," Dead assign "<<assp<<endl);
|
||||
assp->unlinkFrBack()->deleteTree(); assp=NULL;
|
||||
}
|
||||
vscp->unlinkFrBack()->deleteTree(); vscp=NULL;
|
||||
}
|
||||
}
|
||||
for (vector<AstVar*>::iterator it = m_varsp.begin(); it!=m_varsp.end(); ++it) {
|
||||
@@ -129,6 +186,7 @@ public:
|
||||
// CONSTRUCTORS
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars) {
|
||||
m_elimUserVars = elimUserVars;
|
||||
m_sideEffect = false;
|
||||
// Operate on whole netlist
|
||||
AstNode::userClearTree(); // userp() used on entire tree
|
||||
nodep->accept(*this);
|
||||
|
||||
+7
-6
@@ -224,12 +224,13 @@ private:
|
||||
if (m_scopep) {
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
if (nodep->funcPublic()) {
|
||||
// There may be multiple public functions by the same name;
|
||||
// record for later correction or making of shells
|
||||
m_modFuncs.insert(make_pair(nodep->name(), nodep));
|
||||
nodep->name(m_scopep->nameDotless() +"__" + nodep->name());
|
||||
|
||||
if (nodep->funcPublic()) {
|
||||
// There may be multiple public functions by the same name;
|
||||
// record for later correction or making of shells
|
||||
m_modFuncs.insert(make_pair(nodep->name(), nodep));
|
||||
nodep->name(m_scopep->nameDotless() +"__" + nodep->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-18
@@ -96,12 +96,17 @@ public:
|
||||
string cFuncArgs(AstCFunc* nodep) {
|
||||
// Return argument list for given C function
|
||||
string args = nodep->argTypes();
|
||||
if (args=="") {
|
||||
// Might be a user function with argument list.
|
||||
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args+= ", ";
|
||||
// Might be a user function with argument list.
|
||||
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args+= ", ";
|
||||
if (portp->isWide()) {
|
||||
if (portp->isInOnly()) args += "const ";
|
||||
args += portp->cType();
|
||||
args += " (& "+portp->name();
|
||||
args += ")["+cvtToStr(portp->widthWords())+"]";
|
||||
} else {
|
||||
args += portp->cType();
|
||||
if (portp->isOutput()) args += "&";
|
||||
args += " "+portp->name();
|
||||
@@ -217,8 +222,7 @@ public:
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstCoverInc* nodep, AstNUser*) {
|
||||
puts("if (VL_LIKELY(vlSymsp->__Vm_coverageRequest))");
|
||||
puts(" ++this->__Vcoverage[");
|
||||
puts("++this->__Vcoverage[");
|
||||
puts(cvtToStr(m_coverIds.remap(nodep->declp()))); puts("];\n");
|
||||
}
|
||||
virtual void visit(AstCReturn* nodep, AstNUser*) {
|
||||
@@ -703,19 +707,19 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
if (nodep->attrScClocked() && nodep->isInput()) {
|
||||
puts("sc_in_clk\t");
|
||||
} else {
|
||||
if (nodep->isTristate()) puts("sc_inout<");
|
||||
if (nodep->isInout()) puts("sc_inout<");
|
||||
else if (nodep->isInput()) puts("sc_in<");
|
||||
else if (nodep->isOutput()) puts("sc_out<");
|
||||
else nodep->v3fatalSrc("Unknown type");
|
||||
|
||||
puts(nodep->cType());
|
||||
puts(nodep->scType());
|
||||
puts(">\t");
|
||||
}
|
||||
puts(nodep->name());
|
||||
puts(";\n");
|
||||
} else { // C++ signals
|
||||
ofp()->putAlign(nodep->isStatic(), nodep->widthAlignBytes());
|
||||
if (nodep->isTristate()) puts("VL_INOUT");
|
||||
if (nodep->isInout()) puts("VL_INOUT");
|
||||
else if (nodep->isInput()) puts("VL_IN");
|
||||
else if (nodep->isOutput()) puts("VL_OUT");
|
||||
else nodep->v3fatalSrc("Unknown type");
|
||||
@@ -910,7 +914,6 @@ string EmitCStmts::displayFormat(AstNode* widthNodep, string in,
|
||||
} else {
|
||||
fmt=in;
|
||||
}
|
||||
if (widthNodep->isQuad() && fmtLetter!='c' && fmtLetter!='s' && !reallyString) fmt+="ll";
|
||||
return fmt;
|
||||
}
|
||||
|
||||
@@ -941,10 +944,17 @@ void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, cha
|
||||
emitDispState.pushFormat(pfmt);
|
||||
emitDispState.pushArg(*elistp,func);
|
||||
} else {
|
||||
string func;
|
||||
string nfmt = displayFormat(*elistp, fmt, fmtLetter, true, false);
|
||||
// We don't need to check for fmtLetter=='d', as it is above.
|
||||
if ((*elistp)->isQuad() && (fmtLetter=='u'||fmtLetter=='o'||fmtLetter=='x')) {
|
||||
nfmt+="ll";
|
||||
func="(unsigned long long)("; // Must match %ull to avoid warnings
|
||||
}
|
||||
string pfmt = "%"+nfmt+fmtLetter;
|
||||
|
||||
emitDispState.pushFormat(pfmt);
|
||||
emitDispState.pushArg(*elistp,"");
|
||||
emitDispState.pushArg(*elistp,func);
|
||||
}
|
||||
// Next parameter
|
||||
*elistp = (*elistp)->nextp();
|
||||
@@ -1246,12 +1256,8 @@ void EmitCImp::emitWrapEval(AstModule* modp) {
|
||||
puts("while (VL_LIKELY(__Vchange)) {\n");
|
||||
puts( "VL_DEBUG_IF(cout<<\" Clock loop\"<<endl;);\n");
|
||||
#endif
|
||||
// Not exactly correct for coverage. w/combo logic interspersed, this is complicated. Punt.
|
||||
// Perhaps have temp & each _combo_ turn it on or off
|
||||
if (v3Global.opt.coverage()) puts( "vlSymsp->__Vm_coverageRequest = true;\n");
|
||||
puts( "vlSymsp->__Vm_activity = true;\n");
|
||||
puts( "_eval(vlSymsp);\n");
|
||||
if (v3Global.opt.coverage()) puts( "vlSymsp->__Vm_coverageRequest = false;\n");
|
||||
#ifndef NEW_ORDERING
|
||||
puts( "__Vchange = _change_request(vlSymsp);\n");
|
||||
puts( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n");
|
||||
@@ -1284,7 +1290,7 @@ void EmitCImp::emitWrapEval(AstModule* modp) {
|
||||
|
||||
void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp) {
|
||||
// Put out a list of signal declarations
|
||||
// in order of 0:clocks, 1:uint8, 2:uint16, 4:uint32, 5:uint64, 6:wide, 7:arrays
|
||||
// in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays
|
||||
// This aids cache packing and locality
|
||||
// Largest->smallest reduces the number of pad variables.
|
||||
// But for now, Smallest->largest makes it more likely a small offset will allow access to the signal.
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ public:
|
||||
bool optSystemPerl() { return v3Global.opt.systemPerl(); }
|
||||
static string symClassName() { return v3Global.opt.prefix()+"__Syms"; }
|
||||
static string symClassVar() { return symClassName()+"* __restrict vlSymsp"; }
|
||||
static string symTopAssign() { return v3Global.opt.prefix()+"* __restrict vlTOPp = vlSymsp->TOPp;"; }
|
||||
static string symTopAssign() { return v3Global.opt.prefix()+"* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;"; }
|
||||
static string modClassName(AstModule* modp) { // Return name of current module being processed
|
||||
if (modp->isTop()) {
|
||||
return v3Global.opt.prefix();
|
||||
|
||||
+2
-7
@@ -124,18 +124,14 @@ void EmitCSyms::emitInt() {
|
||||
//puts("\n// STATIC STATE\n");
|
||||
|
||||
puts("\n// LOCAL STATE\n");
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(uint64_t));
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(vluint64_t));
|
||||
puts("const char* __Vm_namep;\n"); // Must be before subcells, as constructor order needed before _vlCoverInsert.
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(bool));
|
||||
puts("bool\t__Vm_activity;\t\t///< Used by trace routines to determine change occurred\n");
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(bool));
|
||||
puts("bool\t__Vm_didInit;\n");
|
||||
if (v3Global.opt.coverage()) {
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(bool));
|
||||
puts("bool __Vm_coverageRequest;\n");
|
||||
}
|
||||
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(uint64_t));
|
||||
ofp()->putAlign(V3OutFile::AL_AUTO, sizeof(vluint64_t));
|
||||
puts("\n// SUBCELL STATE\n");
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstModule* modp = it->second;
|
||||
@@ -184,7 +180,6 @@ void EmitCSyms::emitImp() {
|
||||
puts("\t: __Vm_namep(namep)\n"); // No leak, as we get destroyed when the top is destroyed
|
||||
puts("\t, __Vm_activity(false)\n");
|
||||
puts("\t, __Vm_didInit(false)\n");
|
||||
if (v3Global.opt.coverage()) puts("\t, __Vm_coverageRequest(false)\n");
|
||||
puts("\t// Setup submodule names\n");
|
||||
char comma=',';
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
|
||||
@@ -96,6 +96,9 @@ public:
|
||||
// Imply generating verilated.o
|
||||
if (v3Global.opt.exe()) {
|
||||
v3Global.opt.addCppFile("verilated.cpp");
|
||||
if (v3Global.opt.trace()) {
|
||||
v3Global.opt.addCppFile("SpTraceVcdC.cpp");
|
||||
}
|
||||
}
|
||||
|
||||
V3StringSet dirs;
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ public:
|
||||
puts(";");
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
puts(";");
|
||||
nodep->assignsp()->iterateAndNext(*this);
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
m_suppressSemi = false;
|
||||
puts(") {\n");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
|
||||
+15
-7
@@ -92,7 +92,7 @@ void FileLine::lineDirective(const char* textp) {
|
||||
|
||||
bool FileLine::warnOff(const string& msg, bool flag) {
|
||||
V3ErrorCode code (msg.c_str());
|
||||
if (code == V3ErrorCode::ERROR) {
|
||||
if (code < V3ErrorCode::FIRST_WARN) {
|
||||
return false;
|
||||
} else {
|
||||
warnOff(code, flag);
|
||||
@@ -168,9 +168,8 @@ void FileLine::v3errorEnd(ostringstream& str) {
|
||||
void V3Error::init() {
|
||||
for (int i=0; i<V3ErrorCode::MAX; i++) {
|
||||
s_describedEachWarn[i] = false;
|
||||
s_pretendError[i] = false;
|
||||
s_pretendError[i] = V3ErrorCode(i).pretendError();
|
||||
}
|
||||
pretendError(V3ErrorCode::BLKANDNBLK, true);
|
||||
|
||||
if (string(V3ErrorCode(V3ErrorCode::MAX).ascii()) != " MAX") {
|
||||
v3fatalSrc("Enum table in V3ErrorCode::ascii() is munged");
|
||||
@@ -204,8 +203,9 @@ void V3Error::abortIfErrors() {
|
||||
string V3Error::msgPrefix(V3ErrorCode code) {
|
||||
if (code==V3ErrorCode::SUPPRESS) return "-arning-suppressed: ";
|
||||
else if (code==V3ErrorCode::FATAL) return "%Error: ";
|
||||
else if (code==V3ErrorCode::ERROR
|
||||
|| s_pretendError[code]) return "%Error: ";
|
||||
else if (code==V3ErrorCode::ERROR) return "%Error: ";
|
||||
else if (code<V3ErrorCode::FIRST_WARN
|
||||
|| s_pretendError[code]) return "%Error-"+(string)code.ascii()+": ";
|
||||
else return "%Warning-"+(string)code.ascii()+": ";
|
||||
}
|
||||
|
||||
@@ -236,6 +236,9 @@ void V3Error::v3abort () {
|
||||
}
|
||||
|
||||
void V3Error::v3errorEnd (ostringstream& sstr) {
|
||||
#ifdef __COVERITY__
|
||||
if (s_errorCode==V3ErrorCode::FATAL) __coverity_panic__(x);
|
||||
#endif
|
||||
if (s_errorCode!=V3ErrorCode::SUPPRESS || debug()) {
|
||||
cerr<<msgPrefix()<<sstr.str();
|
||||
if (sstr.str()[sstr.str().length()-1] != '\n') {
|
||||
@@ -268,8 +271,13 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
abort();
|
||||
// exit(10);
|
||||
|
||||
if (V3Error::debugDefault()) {
|
||||
cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
|
||||
abort();
|
||||
} else {
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -34,9 +34,12 @@ public:
|
||||
enum en {
|
||||
SUPPRESS, // Warning suppressed by user
|
||||
FATAL, // Kill the program
|
||||
ERROR, // Error out, can't suppress
|
||||
ERROR, // General error out, can't suppress
|
||||
// Error codes:
|
||||
TASKNSVAR, // Error: Task I/O not simple
|
||||
// Warning codes:
|
||||
FIRST_WARN, // Just a code so the program knows where to start warnings
|
||||
//
|
||||
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
|
||||
CASEINCOMPLETE, // Case statement has missing values
|
||||
CASEOVERLAP, // Case statements overlap
|
||||
@@ -45,6 +48,7 @@ public:
|
||||
COMBDLY, // Combinatorial delayed assignment
|
||||
GENCLK, // Generated Clock
|
||||
IMPLICIT, // Implicit wire
|
||||
IMPURE, // Impure function not being inlined
|
||||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
UNDRIVEN, // No drivers
|
||||
UNOPT, // Unoptimizable block
|
||||
@@ -66,10 +70,11 @@ public:
|
||||
const char* names[] = {
|
||||
// Leading spaces indicate it can't be disabled.
|
||||
" SUPPRESS", " FATAL", " ERROR",
|
||||
"TASKNSVAR",
|
||||
" FIRST_WARN",
|
||||
"BLKANDNBLK",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEX", "CMPCONST",
|
||||
"COMBDLY", "GENCLK", "IMPLICIT",
|
||||
"COMBDLY", "GENCLK", "IMPLICIT", "IMPURE",
|
||||
"MULTIDRIVEN",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNSIGNED", "UNUSED",
|
||||
"VARHIDDEN", "WIDTH",
|
||||
@@ -79,6 +84,9 @@ public:
|
||||
};
|
||||
// Warnings that warn about nasty side effects
|
||||
bool dangerous() const { return ( m_e==COMBDLY );};
|
||||
// Warnings we'll present to the user as errors
|
||||
// Later -Werror- options may make more of these.
|
||||
bool pretendError() const { return ( m_e==BLKANDNBLK || m_e==IMPURE); };
|
||||
};
|
||||
inline bool operator== (V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); }
|
||||
|
||||
@@ -660,6 +660,9 @@ private:
|
||||
// Lhs or Rhs may be word, long, or quad.
|
||||
// newAstWordSelClone nicely abstracts the difference.
|
||||
int rhsshift = rhsp->rhsp()->widthMin();
|
||||
// Sometimes doing the words backwards is preferrable.
|
||||
// When we have x={x,foo} backwards is better, when x={foo,x} forward is better
|
||||
// However V3Subst tends to rip this up, so not worth optimizing now.
|
||||
for (int w=0; w<rhsp->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstOr (rhsp->fileline(),
|
||||
|
||||
@@ -42,6 +42,9 @@ public:
|
||||
}
|
||||
static ofstream* new_ofstream(const string& filename, bool append=false) {
|
||||
addTgtDepend(filename);
|
||||
return new_ofstream_nodepend (filename, append);
|
||||
}
|
||||
static ofstream* new_ofstream_nodepend(const string& filename, bool append=false) {
|
||||
if (append) {
|
||||
return new ofstream(filename.c_str(), ios::app);
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ class GraphNfaToDfa : GraphAlg {
|
||||
private:
|
||||
// TYPES
|
||||
typedef deque<DfaVertex*> DfaStates;
|
||||
typedef multimap<uint64_t,DfaVertex*> HashMap;
|
||||
typedef multimap<vluint64_t,DfaVertex*> HashMap;
|
||||
|
||||
// MEMBERS
|
||||
uint32_t m_step; // Processing step, so we can avoid clearUser all the time
|
||||
|
||||
+1
-3
@@ -111,7 +111,6 @@ private:
|
||||
nodep->name(), nodep->modp()->origName());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
// Create assignments to the pins
|
||||
AstNode* assignlistsp = NULL;
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
UINFO(6," Pin change from "<<pinp->modVarp()<<endl);
|
||||
// First, simplify it
|
||||
@@ -142,13 +141,11 @@ private:
|
||||
// Cleanup var names, etc, to not conflict
|
||||
m_cellp = nodep;
|
||||
newmodp->iterateAndNext(*this);
|
||||
if (assignlistsp) assignlistsp->iterateAndNext(*this); // And cleanup any varrefs under assigns we created...
|
||||
m_cellp = NULL;
|
||||
// Move statements to top module
|
||||
if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); }
|
||||
AstNode* stmtsp = newmodp->stmtsp();
|
||||
if (stmtsp) stmtsp->unlinkFrBackWithNext();
|
||||
if (assignlistsp) m_modp->addStmtp(assignlistsp);
|
||||
if (stmtsp) m_modp->addStmtp(stmtsp);
|
||||
// Remove the cell
|
||||
newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc
|
||||
@@ -353,6 +350,7 @@ private:
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
nodep->taskp(NULL);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
// Nop's to speed up the loop
|
||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||
|
||||
+13
-5
@@ -79,7 +79,7 @@ private:
|
||||
// or ASSIGNW(expr,VARXREF(p)) (if sub's output)
|
||||
UINFO(4," PIN "<<nodep<<endl);
|
||||
if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: ");
|
||||
if (nodep->modVarp()->isOutput() && nodep->exprp()->castConst())
|
||||
if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst())
|
||||
nodep->v3error("Output pin is assigned to a constant, electrical short");
|
||||
// Use userp on the PIN to indicate we created an assign for this pin
|
||||
if (!nodep->user()) {
|
||||
@@ -90,7 +90,9 @@ private:
|
||||
AstNode* exprp = nodep->exprp()->cloneTree(false);
|
||||
if (nodep->width() != nodep->modVarp()->width())
|
||||
nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple\n");
|
||||
if (nodep->modVarp()->isOutput()) {
|
||||
if (nodep->modVarp()->isInout()) {
|
||||
nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator");
|
||||
} else if (nodep->modVarp()->isOutput()) {
|
||||
AstNode* rhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
|
||||
rhsp->widthSignedFrom(nodep);
|
||||
AstAssignW* assp = new AstAssignW (exprp->fileline(), exprp, rhsp);
|
||||
@@ -157,7 +159,11 @@ private:
|
||||
nodep->addNextHere(newp);
|
||||
// Remove ranging and fix name
|
||||
newp->rangep()->unlinkFrBack()->deleteTree();
|
||||
// Somewhat illogically, we need to rename the orignal name of the cell too.
|
||||
// as that is the name users expect for dotting
|
||||
// The spec says we add [x], but that won't work in C...
|
||||
newp->name(newp->name()+"__"+cvtToStr(m_instNum));
|
||||
newp->origName(newp->origName()+"__"+cvtToStr(m_instNum));
|
||||
// Fixup pins
|
||||
newp->pinsp()->iterateAndNext(*this);
|
||||
if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<<endl; }
|
||||
@@ -232,12 +238,14 @@ void V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstModule* modp) {
|
||||
} else {
|
||||
// Make a new temp wire
|
||||
//if (1||debug()>=9) { pinp->dumpTree(cout,"in_pin:"); }
|
||||
AstAssignW* assignp;
|
||||
AstAssignW* assignp = NULL;
|
||||
AstNode* pinexprp = pinp->exprp()->unlinkFrBack();
|
||||
string newvarname = "__Vcellinp__"+cellp->name()+"__"+pinp->name();
|
||||
AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp);
|
||||
modp->addStmtp(newvarp);
|
||||
if (pinVarp->isOutput()) {
|
||||
if (pinVarp->isInout()) {
|
||||
pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be direct one-to-one connection (without any expression)");
|
||||
} else if (pinVarp->isOutput()) {
|
||||
// See also V3Inst
|
||||
AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false);
|
||||
if (pinp->width() > rhsp->width()) {
|
||||
@@ -261,7 +269,7 @@ void V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstModule* modp) {
|
||||
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false));
|
||||
}
|
||||
pinp->widthSignedFrom(pinp->exprp());
|
||||
modp->addStmtp(assignp);
|
||||
if (assignp) modp->addStmtp(assignp);
|
||||
//if (1||debug()) { pinp->dumpTree(cout," out:"); }
|
||||
//if (1||debug()) { assignp->dumpTree(cout," aout:"); }
|
||||
}
|
||||
|
||||
+297
-136
@@ -38,152 +38,243 @@
|
||||
#include "V3Life.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
|
||||
//######################################################################
|
||||
// Life state, as a visitor of each AstNode
|
||||
// Structure for global state
|
||||
|
||||
class LifeVarEntry {
|
||||
AstNode* m_assignp; // Last assignment to this varscope
|
||||
bool m_firstSet; // True if creation was a set (and thus block above may have a set that can be deleted
|
||||
class LifeState {
|
||||
// STATE
|
||||
public:
|
||||
LifeVarEntry() { // Construction for when a var is used
|
||||
clearAssign();
|
||||
m_firstSet = false;
|
||||
V3Double0 m_statAssnDel; // Statistic tracking
|
||||
V3Double0 m_statAssnCon; // Statistic tracking
|
||||
|
||||
public:
|
||||
LifeState() {}
|
||||
~LifeState() {
|
||||
V3Stats::addStat("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||
V3Stats::addStat("Optimizations, Lifetime constant prop", m_statAssnCon);
|
||||
}
|
||||
LifeVarEntry(AstNode* assp) { // Construction for when a var is set
|
||||
m_assignp = assp;
|
||||
m_firstSet = true;
|
||||
}
|
||||
LifeVarEntry(bool) { // Construction for when a var is set, but ass isn't
|
||||
m_assignp = NULL;
|
||||
m_firstSet = true;
|
||||
}
|
||||
AstNode* assignp() const { return m_assignp; }
|
||||
bool firstSet() const { return m_firstSet; }
|
||||
void clearAssign() { m_assignp = NULL; }
|
||||
void newerAssign(AstNode* assp) { m_assignp = assp; }
|
||||
};
|
||||
|
||||
class LifeVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVarScope::user() -> int. Used in combining to detect duplicates
|
||||
//######################################################################
|
||||
// Structure for each variable encountered
|
||||
|
||||
// STATE
|
||||
//static int debug() { return 9; }
|
||||
int m_ifCount; // If counter
|
||||
V3Double0 m_statAssnDel; // Statistic tracking
|
||||
class LifeVarEntry {
|
||||
AstNodeAssign* m_assignp; // Last assignment to this varscope, NULL if no longer relevant
|
||||
AstConst* m_constp; // Known constant value
|
||||
bool m_setBeforeUse; // First access was a set (and thus block above may have a set that can be deleted
|
||||
bool m_everSet; // Was ever assigned (and thus above block may not preserve constant propagation)
|
||||
|
||||
inline void init (bool setBeforeUse) {
|
||||
m_assignp = NULL;
|
||||
m_constp = NULL;
|
||||
m_setBeforeUse = setBeforeUse;
|
||||
m_everSet = false;
|
||||
}
|
||||
public:
|
||||
class SIMPLEASSIGN {};
|
||||
class COMPLEXASSIGN {};
|
||||
class CONSUMED {};
|
||||
|
||||
LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* assp) {
|
||||
init(true); simpleAssign(assp);
|
||||
}
|
||||
LifeVarEntry(COMPLEXASSIGN) {
|
||||
init(false); complexAssign();
|
||||
}
|
||||
LifeVarEntry(CONSUMED) {
|
||||
init(false); consumed();
|
||||
}
|
||||
~LifeVarEntry() {}
|
||||
inline void simpleAssign(AstNodeAssign* assp) { // New simple A=.... assignment
|
||||
m_assignp = assp;
|
||||
m_constp = NULL;
|
||||
m_everSet = true;
|
||||
if (assp->rhsp()->castConst()) m_constp = assp->rhsp()->castConst();
|
||||
}
|
||||
inline void complexAssign() { // A[x]=... or some complicated assignment
|
||||
m_assignp = NULL;
|
||||
m_constp = NULL;
|
||||
m_everSet = true;
|
||||
}
|
||||
inline void consumed() { // Rvalue read of A
|
||||
m_assignp = NULL;
|
||||
}
|
||||
AstNodeAssign* assignp() const { return m_assignp; }
|
||||
AstConst* constNodep() const { return m_constp; }
|
||||
bool setBeforeUse() const { return m_setBeforeUse; }
|
||||
bool everSet() const { return m_everSet; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Structure for all variables under a given meta-basic block
|
||||
|
||||
class LifeBlock {
|
||||
// NODE STATE
|
||||
// Cleared each AstIf:
|
||||
// AstVarScope::user() -> int. Used in combining to detect duplicates
|
||||
|
||||
// LIFE MAP
|
||||
// For each basic block, we'll make a new map of what variables that if/else is changing
|
||||
typedef std::map<AstVarScope*, LifeVarEntry> LifeMap;
|
||||
LifeMap *m_lifep; // Current active lifetime map for current scope
|
||||
LifeMap m_map; // Current active lifetime map for current scope
|
||||
LifeBlock* m_aboveLifep; // Upper life, or NULL
|
||||
LifeState* m_statep; // Current global state
|
||||
|
||||
public:
|
||||
LifeBlock(LifeBlock* aboveLifep, LifeState* statep) {
|
||||
m_aboveLifep = aboveLifep; // Null if top
|
||||
m_statep = statep;
|
||||
}
|
||||
~LifeBlock() {}
|
||||
// METHODS
|
||||
void checkRemoveAssign(LifeVarEntry* entp) {
|
||||
// Check the var entry, and remove if appropriate
|
||||
if (AstNode* oldassp = entp->assignp()) {
|
||||
UINFO(7," PREV: "<<oldassp<<endl);
|
||||
// Redundant assignment, in same level block
|
||||
if (debug()>4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK ");
|
||||
entp->clearAssign();
|
||||
oldassp->unlinkFrBack();
|
||||
pushDeletep(oldassp);
|
||||
m_statAssnDel++;
|
||||
void checkRemoveAssign(const LifeMap::iterator& it) {
|
||||
AstVar* varp = it->first->varp();
|
||||
LifeVarEntry* entp = &(it->second);
|
||||
if (!varp->isSigPublic()) {
|
||||
// Rather then track what sigs AstUCFunc/AstUCStmt may change,
|
||||
// we just don't optimize any public sigs
|
||||
// Check the var entry, and remove if appropriate
|
||||
if (AstNode* oldassp = entp->assignp()) {
|
||||
UINFO(7," PREV: "<<oldassp<<endl);
|
||||
// Redundant assignment, in same level block
|
||||
if (debug()>4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK ");
|
||||
entp->complexAssign();
|
||||
oldassp->unlinkFrBack()->deleteTree(); oldassp=NULL;
|
||||
m_statep->m_statAssnDel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void setLast(AstVarScope* nodep, AstNode* assp) {
|
||||
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
||||
// Do we have a old assignment we can nuke?
|
||||
UINFO(4," ASSIGNof: "<<nodep<<endl);
|
||||
UINFO(7," new: "<<assp<<endl);
|
||||
LifeMap::iterator iter = m_lifep->find(nodep);
|
||||
if (iter != m_lifep->end()) {
|
||||
checkRemoveAssign(&(iter->second));
|
||||
iter->second.newerAssign(assp);
|
||||
LifeMap::iterator it = m_map.find(nodep);
|
||||
if (it != m_map.end()) {
|
||||
checkRemoveAssign(it);
|
||||
it->second.simpleAssign(assp);
|
||||
} else {
|
||||
m_lifep->insert(make_pair(nodep,LifeVarEntry(assp)));
|
||||
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::SIMPLEASSIGN(), assp)));
|
||||
}
|
||||
//lifeDump();
|
||||
}
|
||||
void clearLastAssign(AstVarScope* nodep) {
|
||||
void complexAssign(AstVarScope* nodep) {
|
||||
UINFO(4," clearof: "<<nodep<<endl);
|
||||
LifeMap::iterator iter = m_lifep->find(nodep);
|
||||
if (iter != m_lifep->end()) {
|
||||
iter->second.clearAssign();
|
||||
LifeMap::iterator it = m_map.find(nodep);
|
||||
if (it != m_map.end()) {
|
||||
it->second.complexAssign();
|
||||
} else {
|
||||
m_lifep->insert(make_pair(nodep,LifeVarEntry()));
|
||||
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN())));
|
||||
}
|
||||
}
|
||||
void lifeDump(LifeMap* lifep) {
|
||||
void varUsageReplace (AstVarScope* nodep, AstVarRef* varrefp) {
|
||||
// Variable rvalue. If it references a constant, we can simply replace it
|
||||
LifeMap::iterator it = m_map.find(nodep);
|
||||
if (it != m_map.end()) {
|
||||
if (AstConst* constp = it->second.constNodep()) {
|
||||
if (!varrefp->varp()->isSigPublic()) {
|
||||
// Aha, variable is constant; substitute in.
|
||||
// We'll later constant propagate
|
||||
UINFO(4," replaceconst: "<<varrefp<<endl);
|
||||
varrefp->replaceWith(constp->cloneTree(false));
|
||||
varrefp->deleteTree(); varrefp=NULL;
|
||||
m_statep->m_statAssnCon++;
|
||||
return; // **DONE, no longer a var reference**
|
||||
}
|
||||
}
|
||||
UINFO(4," usage: "<<nodep<<endl);
|
||||
it->second.consumed();
|
||||
} else {
|
||||
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED())));
|
||||
}
|
||||
}
|
||||
void complexAssignFind(AstVarScope* nodep) {
|
||||
LifeMap::iterator it = m_map.find(nodep);
|
||||
if (it != m_map.end()) {
|
||||
UINFO(4," casfind: "<<it->first<<endl);
|
||||
it->second.complexAssign();
|
||||
} else {
|
||||
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN())));
|
||||
}
|
||||
}
|
||||
void consumedFind(AstVarScope* nodep) {
|
||||
LifeMap::iterator it = m_map.find(nodep);
|
||||
if (it != m_map.end()) {
|
||||
it->second.consumed();
|
||||
} else {
|
||||
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED())));
|
||||
}
|
||||
}
|
||||
void lifeToAbove() {
|
||||
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
||||
if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level");
|
||||
for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) {
|
||||
AstVarScope* nodep = it->first;
|
||||
m_aboveLifep->complexAssignFind(nodep);
|
||||
if (it->second.everSet()) {
|
||||
// Record there may be an assignment, so we don't constant propagate across the if.
|
||||
complexAssignFind(nodep);
|
||||
} else {
|
||||
// Record consumption, so we don't eliminate earlier assignments
|
||||
consumedFind(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
void dualBranch (LifeBlock* life1p, LifeBlock* life2p) {
|
||||
// Find any common sets on both branches of IF and propagate upwards
|
||||
//life1p->lifeDump();
|
||||
//life2p->lifeDump();
|
||||
AstNode::userClearTree(); // userp() used on entire tree
|
||||
for (LifeMap::iterator it = life1p->m_map.begin(); it!=life1p->m_map.end(); ++it) {
|
||||
// When the if branch sets a var before it's used, mark that variable
|
||||
if (it->second.setBeforeUse()) it->first->user(1);
|
||||
}
|
||||
for (LifeMap::iterator it = life2p->m_map.begin(); it!=life2p->m_map.end(); ++it) {
|
||||
// When the else branch sets a var before it's used
|
||||
AstVarScope* nodep = it->first;
|
||||
if (it->second.setBeforeUse() && nodep->user()) {
|
||||
// Both branches set the var, we can remove the assignment before the IF.
|
||||
UINFO(4,"DUALBRANCH "<<nodep<<endl);
|
||||
LifeMap::iterator itab = m_map.find(nodep);
|
||||
if (itab != m_map.end()) {
|
||||
checkRemoveAssign(itab);
|
||||
}
|
||||
}
|
||||
}
|
||||
//this->lifeDump();
|
||||
}
|
||||
// DEBUG
|
||||
void lifeDump() {
|
||||
UINFO(5, " LifeMap:"<<endl);
|
||||
for (LifeMap::iterator it = lifep->begin(); it!=lifep->end(); ++it) {
|
||||
for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) {
|
||||
UINFO(5, " Ent: "
|
||||
<<(it->second.firstSet()?"[F] ":" ")
|
||||
<<(it->second.setBeforeUse()?"[F] ":" ")
|
||||
<<it->first<<endl);
|
||||
if (it->second.assignp()) {
|
||||
UINFO(5, " Ass: "<<it->second.assignp()<<endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
void clearLifeFromSublife (LifeMap* sublifep) {
|
||||
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
||||
for (LifeMap::iterator it = sublifep->begin(); it!=sublifep->end(); ++it) {
|
||||
clearLastAssign(it->first);
|
||||
}
|
||||
}
|
||||
void dualBranch (LifeMap* life1p, LifeMap* life2p) {
|
||||
// Find any common sets on both branches of IF and propagate upwards
|
||||
//lifeDump(life1p);
|
||||
//lifeDump(life2p);
|
||||
m_ifCount++;
|
||||
for (LifeMap::iterator it = life1p->begin(); it!=life1p->end(); ++it) {
|
||||
// When the if branch sets a var before it's used, mark that variable
|
||||
if (it->second.firstSet()) it->first->user(m_ifCount);
|
||||
}
|
||||
for (LifeMap::iterator it = life2p->begin(); it!=life2p->end(); ++it) {
|
||||
// When the else branch sets a var before it's used
|
||||
AstVarScope* nodep = it->first;
|
||||
if (it->second.firstSet()
|
||||
&& nodep->user()==m_ifCount) { // And the if hit the same var
|
||||
UINFO(4,"DUALBRANCH "<<nodep<<endl);
|
||||
LifeMap::iterator iter = m_lifep->find(nodep);
|
||||
if (iter != m_lifep->end()) {
|
||||
checkRemoveAssign(&(iter->second));
|
||||
iter->second.clearAssign();
|
||||
} else {
|
||||
m_lifep->insert(make_pair(nodep,LifeVarEntry(true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
//lifeDump(m_lifep);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Life state, as a visitor of each AstNode
|
||||
|
||||
class LifeVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
LifeState* m_statep; // Current state
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
//static int debug() { return 9; }
|
||||
|
||||
// LIFE MAP
|
||||
// For each basic block, we'll make a new map of what variables that if/else is changing
|
||||
typedef std::map<AstVarScope*, LifeVarEntry> LifeMap;
|
||||
LifeBlock* m_lifep; // Current active lifetime map for current scope
|
||||
|
||||
// METHODS
|
||||
// VISITORS
|
||||
virtual void visit(AstModule* nodep, AstNUser*) {
|
||||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstTopScope* nodep, AstNUser*) {
|
||||
AstNode::userClearTree(); // userp() used on entire tree
|
||||
AstNode::user2ClearTree(); // userp() used on entire tree
|
||||
AstNode::user3ClearTree(); // userp() used on entire tree
|
||||
AstNode::user4ClearTree(); // userp() used on entire tree
|
||||
|
||||
AstScope* scopep = nodep->scopep();
|
||||
if (!scopep) nodep->v3fatalSrc("TopScope has no scope\n");
|
||||
AstCFunc* evalp = NULL;
|
||||
for (AstNode* searchp = scopep->blocksp(); searchp; searchp=searchp->nextp()) {
|
||||
if (AstCFunc* funcp = searchp->castCFunc()) {
|
||||
if (funcp->name() == "_eval") {
|
||||
evalp = funcp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!evalp) nodep->v3fatalSrc("Top _eval entry function not found\n");
|
||||
evalp->iterateChildren(*this);
|
||||
}
|
||||
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// Consumption/generation of a variable,
|
||||
// it's used so can't elim assignment before this use.
|
||||
@@ -191,44 +282,61 @@ private:
|
||||
//
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
clearLastAssign(vscp);
|
||||
if (nodep->lvalue()) {
|
||||
m_lifep->complexAssign(vscp);
|
||||
} else {
|
||||
m_lifep->varUsageReplace(vscp, nodep); nodep=NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
// Collect any used variables first, as lhs may also be on rhs
|
||||
// Similar code in V3Dead
|
||||
vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
|
||||
m_sideEffect = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
if (lastEdit != AstNode::editCountGbl()) {
|
||||
// We changed something, try to constant propagate, but don't delete the
|
||||
// assignment as we still need nodep to remain.
|
||||
V3Const::constifyTree(nodep->rhsp());
|
||||
}
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
if (nodep->lhsp()->castVarRef()) {
|
||||
if (nodep->lhsp()->castVarRef() && !m_sideEffect) {
|
||||
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||
if (!vscp) vscp->v3fatalSrc("Scope lost on variable");
|
||||
setLast(vscp, nodep);
|
||||
m_lifep->simpleAssign(vscp, nodep);
|
||||
} else {
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep, AstNUser*) {
|
||||
// Don't treat as normal assign; V3Life doesn't understand time sense
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
//---- Track control flow changes
|
||||
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
||||
UINFO(4," IF "<<nodep<<endl);
|
||||
// Condition is part of PREVIOUS block
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
LifeMap* prevLifeMapp = m_lifep;
|
||||
LifeMap* ifLifep = new LifeMap;
|
||||
LifeMap* elseLifep = new LifeMap;
|
||||
LifeBlock* prevLifep = m_lifep;
|
||||
LifeBlock* ifLifep = new LifeBlock (prevLifep, m_statep);
|
||||
LifeBlock* elseLifep = new LifeBlock (prevLifep, m_statep);
|
||||
{
|
||||
m_lifep = ifLifep;
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
{
|
||||
m_lifep = elseLifep;
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
UINFO(4," join "<<endl);
|
||||
m_lifep = prevLifeMapp;
|
||||
// Find sets on both flows
|
||||
dualBranch (ifLifep, elseLifep);
|
||||
m_lifep->dualBranch (ifLifep, elseLifep);
|
||||
// For the next assignments, clear any variables that were read or written in the block
|
||||
clearLifeFromSublife(ifLifep);
|
||||
clearLifeFromSublife(elseLifep);
|
||||
ifLifep->lifeToAbove();
|
||||
elseLifep->lifeToAbove();
|
||||
delete ifLifep;
|
||||
delete elseLifep;
|
||||
}
|
||||
@@ -240,24 +348,25 @@ private:
|
||||
// assignment in the body that's used in the cond. (And otherwise
|
||||
// would because it only appears used after-the-fact. So, we model
|
||||
// it as a IF statement, and just don't allow elimination of
|
||||
// variables in the body.
|
||||
LifeMap* prevLifeMapp = m_lifep;
|
||||
LifeMap* condLifep = new LifeMap;
|
||||
LifeMap* bodyLifep = new LifeMap;
|
||||
// variables across the body.
|
||||
LifeBlock* prevLifep = m_lifep;
|
||||
LifeBlock* condLifep = new LifeBlock (prevLifep, m_statep);
|
||||
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
|
||||
{
|
||||
m_lifep = condLifep;
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
{
|
||||
m_lifep = bodyLifep;
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
UINFO(4," joinfor"<<endl);
|
||||
m_lifep = prevLifeMapp;
|
||||
// For the next assignments, clear any variables that were read or written in the block
|
||||
clearLifeFromSublife(condLifep);
|
||||
clearLifeFromSublife(bodyLifep);
|
||||
condLifep->lifeToAbove();
|
||||
bodyLifep->lifeToAbove();
|
||||
delete condLifep;
|
||||
delete bodyLifep;
|
||||
}
|
||||
@@ -265,8 +374,15 @@ private:
|
||||
//UINFO(4," CCALL "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
// Enter the function and trace it
|
||||
nodep->funcp()->accept(*this);
|
||||
if (!nodep->funcp()->entryPoint()) { // else is non-inline or public function we optimize separately
|
||||
nodep->funcp()->accept(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep, AstNUser*) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
virtual void visit(AstVar*, AstNUser*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
@@ -274,15 +390,60 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
LifeVisitor(AstNetlist* nodep) {
|
||||
m_ifCount = 1;
|
||||
m_lifep = new LifeMap;
|
||||
LifeVisitor(AstNode* nodep, LifeState* statep) {
|
||||
UINFO(4," LifeVisitor on "<<nodep<<endl);
|
||||
m_statep = statep;
|
||||
m_sideEffect = false;
|
||||
{
|
||||
m_lifep = new LifeBlock (NULL, m_statep);
|
||||
nodep->accept(*this);
|
||||
delete m_lifep; m_lifep=NULL;
|
||||
}
|
||||
}
|
||||
virtual ~LifeVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class LifeTopVisitor : public AstNVisitor {
|
||||
// Visit all top nodes searching for functions that are entry points we want to start
|
||||
// finding code within.
|
||||
private:
|
||||
// STATE
|
||||
LifeState* m_statep; // Current state
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||
if (nodep->entryPoint()) {
|
||||
// Usage model 1: Simulate all C code, doing lifetime analysis
|
||||
LifeVisitor visitor (nodep, m_statep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||
// Usage model 2: Cleanup basic blocks
|
||||
LifeVisitor visitor (nodep, m_statep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep, AstNUser*) {
|
||||
// Usage model 2: Cleanup basic blocks
|
||||
LifeVisitor visitor (nodep, m_statep);
|
||||
}
|
||||
virtual void visit(AstFinal* nodep, AstNUser*) {
|
||||
// Usage model 2: Cleanup basic blocks
|
||||
LifeVisitor visitor (nodep, m_statep);
|
||||
}
|
||||
virtual void visit(AstVar*, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNodeStmt*, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNodeMath*, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
LifeTopVisitor(AstNetlist* nodep, LifeState* statep) {
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
delete m_lifep;
|
||||
}
|
||||
virtual ~LifeVisitor() {
|
||||
V3Stats::addStat("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||
}
|
||||
virtual ~LifeTopVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -290,6 +451,6 @@ public:
|
||||
|
||||
void V3Life::lifeAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
// Delete lifelicate assignments
|
||||
LifeVisitor visitor (nodep);
|
||||
LifeState state;
|
||||
LifeTopVisitor visitor (nodep, &state);
|
||||
}
|
||||
|
||||
+38
-25
@@ -68,12 +68,18 @@ public:
|
||||
void insertSubcellName(const string& name, LinkDotBaseVertex* toVertexp) {
|
||||
m_nameToVtxMap.insert(make_pair(name,toVertexp));
|
||||
}
|
||||
LinkDotBaseVertex* findSubcell(const string& name) {
|
||||
LinkDotBaseVertex* findSubcell(const string& name, const string& altname) {
|
||||
// Find a vertex under this one by name.
|
||||
// We could walk the edge top() list, but that would be O(n) for large lists of cells
|
||||
NameVtxMap::iterator iter = m_nameToVtxMap.find(name);
|
||||
if (iter == m_nameToVtxMap.end()) return NULL;
|
||||
else return iter->second;
|
||||
{
|
||||
NameVtxMap::iterator iter = m_nameToVtxMap.find(name);
|
||||
if (iter != m_nameToVtxMap.end()) return iter->second;
|
||||
}
|
||||
if (altname != "") {
|
||||
NameVtxMap::iterator iter = m_nameToVtxMap.find(altname);
|
||||
if (iter != m_nameToVtxMap.end()) return iter->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,18 +140,20 @@ private:
|
||||
// MEMBERS
|
||||
LinkDotGraph m_graph; // Graph of hiearchy
|
||||
NameScopeMap m_nameScopeMap; // Hash of scope referenced by textual name
|
||||
bool m_forPrearray; // Compress cell__[array] refs
|
||||
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
|
||||
public:
|
||||
static int debug() { return V3Error::debugDefault(); }
|
||||
// static int debug() { return 9; }
|
||||
|
||||
// CONSTRUCTORS
|
||||
LinkDotState(bool forScopeCreation) {
|
||||
LinkDotState(bool forPrearray, bool forScopeCreation) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_forPrearray = forPrearray;
|
||||
m_forScopeCreation = forScopeCreation;
|
||||
//VV***** We reset all userp() on each netlist!!!
|
||||
AstNode::userClearTree();
|
||||
AstNode::user2ClearTree();
|
||||
m_forScopeCreation = forScopeCreation;
|
||||
}
|
||||
~LinkDotState() {}
|
||||
|
||||
@@ -234,18 +242,26 @@ public:
|
||||
leftname = "";
|
||||
}
|
||||
baddot = ident; // So user can see where they botched it
|
||||
string altIdent = "";
|
||||
if (m_forPrearray) {
|
||||
// Cell foo__[array] before we've expanded arrays is just foo.
|
||||
if ((pos = ident.find("__")) != string::npos) {
|
||||
altIdent = ident.substr(0,pos);
|
||||
}
|
||||
}
|
||||
UINFO(8," id "<<ident<<" left "<<leftname<<" at "<<cellVxp<<endl);
|
||||
// Spec says; Look at exiting module (cellnames then modname),
|
||||
// then look up (inst name or modname)
|
||||
if (firstId) {
|
||||
// Check this module - subcellnames
|
||||
if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident)) {
|
||||
if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident, altIdent)) {
|
||||
cellVxp = findVxp;
|
||||
}
|
||||
// Check this module - cur modname
|
||||
else if (cellVxp->modName() == ident) {}
|
||||
// Check this module - cur cellname
|
||||
else if (cellVxp->cellName() == ident) {}
|
||||
else if (cellVxp->cellName() == altIdent) {}
|
||||
// Move up and check cellname + modname
|
||||
else {
|
||||
while (cellVxp) {
|
||||
@@ -253,10 +269,11 @@ public:
|
||||
if (cellVxp) {
|
||||
UINFO(9,"\t\tUp to "<<cellVxp<<endl);
|
||||
if (cellVxp->modName() == ident
|
||||
|| cellVxp->cellName() == ident) {
|
||||
|| cellVxp->cellName() == ident
|
||||
|| cellVxp->cellName() == altIdent) {
|
||||
break;
|
||||
}
|
||||
else if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident)) {
|
||||
else if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident, altIdent)) {
|
||||
cellVxp = findVxp;
|
||||
break;
|
||||
}
|
||||
@@ -265,7 +282,7 @@ public:
|
||||
if (!cellVxp) return NULL; // Not found
|
||||
}
|
||||
} else { // Searching for middle submodule, must be a cell name
|
||||
if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident)) {
|
||||
if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident, altIdent)) {
|
||||
cellVxp = findVxp;
|
||||
} else {
|
||||
return NULL; // Not found
|
||||
@@ -529,7 +546,8 @@ private:
|
||||
string baddot;
|
||||
LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope
|
||||
if (nodep->inlinedDots()!="") { // Correct for current scope
|
||||
dotVxp = m_statep->findDotted(dotVxp, nodep->inlinedDots(), baddot);
|
||||
string inl = AstNode::prettyName(nodep->inlinedDots());
|
||||
dotVxp = m_statep->findDotted(dotVxp, inl, baddot);
|
||||
if (!dotVxp) nodep->v3fatalSrc("Couldn't resolve inlined scope '"<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot); // Maybe NULL
|
||||
@@ -572,7 +590,9 @@ private:
|
||||
string baddot;
|
||||
LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope
|
||||
if (nodep->inlinedDots()!="") { // Correct for current scope
|
||||
dotVxp = m_statep->findDotted(dotVxp, nodep->inlinedDots(), baddot);
|
||||
string inl = AstNode::prettyName(nodep->inlinedDots());
|
||||
UINFO(8,"\t\tInlined "<<inl<<endl);
|
||||
dotVxp = m_statep->findDotted(dotVxp, inl, baddot);
|
||||
if (!dotVxp) nodep->v3fatalSrc("Couldn't resolve inlined scope '"<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot); // Maybe NULL
|
||||
@@ -606,22 +626,15 @@ public:
|
||||
//######################################################################
|
||||
// Link class functions
|
||||
|
||||
void V3LinkDot::linkDot(AstNetlist* rootp) {
|
||||
void V3LinkDot::linkDotGuts(AstNetlist* rootp, bool prearray, bool scoped) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
if (LinkDotState::debug()>=5) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree"));
|
||||
LinkDotState state (false);
|
||||
LinkDotState state (prearray,scoped);
|
||||
LinkDotFindVisitor visitor(rootp,&state);
|
||||
state.dump();
|
||||
LinkDotResolveVisitor visitorb(rootp,&state);
|
||||
}
|
||||
|
||||
void V3LinkDot::linkDotScope(AstNetlist* rootp) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
if (LinkDotState::debug()>=5) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree"));
|
||||
LinkDotState state (true);
|
||||
LinkDotFindVisitor visitor(rootp,&state);
|
||||
// Process AstScope's. This needs to be separate pass after whole hiearchy graph created.
|
||||
LinkDotScopeVisitor visitors(rootp,&state);
|
||||
if (scoped) {
|
||||
// Process AstScope's. This needs to be separate pass after whole hierarchy graph created.
|
||||
LinkDotScopeVisitor visitors(rootp,&state);
|
||||
}
|
||||
state.dump();
|
||||
LinkDotResolveVisitor visitorb(rootp,&state);
|
||||
}
|
||||
|
||||
+5
-2
@@ -28,9 +28,12 @@
|
||||
//============================================================================
|
||||
|
||||
class V3LinkDot {
|
||||
private:
|
||||
static void linkDotGuts(AstNetlist* nodep, bool preparam, bool scoped);
|
||||
public:
|
||||
static void linkDot(AstNetlist* nodep);
|
||||
static void linkDotScope(AstNetlist* nodep);
|
||||
static void linkDotPrearrayed(AstNetlist* nodep) { linkDotGuts(nodep,true,false); }
|
||||
static void linkDotArrayed(AstNetlist* nodep) { linkDotGuts(nodep,false,false); }
|
||||
static void linkDotScope(AstNetlist* nodep) { linkDotGuts(nodep,false,true); }
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) {
|
||||
AstVar* varp = oldvarp->cloneTree(false)->castVar();
|
||||
newmodp->addStmtp(varp);
|
||||
varp->sigPublic(true); // User needs to be able to get to it...
|
||||
if (oldvarp->isInput() || oldvarp->isOutput()) {
|
||||
if (oldvarp->isIO()) {
|
||||
oldvarp->primaryIO(true);
|
||||
varp->primaryIO(true);
|
||||
}
|
||||
|
||||
@@ -104,14 +104,14 @@ private:
|
||||
nodep->v3error("Arrayed variables may not be inputs nor outputs");
|
||||
}
|
||||
if (m_ftaskp) nodep->funcLocal(true);
|
||||
if (nodep && nodep->isSigPublic()) m_modp->modPublic(true); // Avoid flattening if signals are exposed
|
||||
if (nodep->isSigPublic()) m_modp->modPublic(true); // Avoid flattening if signals are exposed
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
|
||||
// VarRef: Resolve its reference
|
||||
if (nodep->varp()) {
|
||||
nodep->varp()->usedParam(true);
|
||||
if (nodep->lvalue() && nodep->varp()->isInput()) {
|
||||
if (nodep->lvalue() && nodep->varp()->isInOnly()) {
|
||||
nodep->v3error("Assigning to input variable: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -62,7 +62,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*) ( ((uint8_t*)newbasep) + offset);
|
||||
return (V3ListEnt*) ( ((vluint8_t*)newbasep) + offset);
|
||||
}
|
||||
public:
|
||||
V3ListEnt()
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
// METHODS
|
||||
void pushBack (V3List<T>& listr, T newp) {
|
||||
// "this" must be a element inside of *newp
|
||||
uint32_t offset = (uint8_t*)(this) - (uint8_t*)(newp);
|
||||
uint32_t offset = (vluint8_t*)(this) - (vluint8_t*)(newp);
|
||||
m_nextp = NULL;
|
||||
if (!listr.m_headp) listr.m_headp = newp;
|
||||
m_prevp = listr.m_tailp;
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
}
|
||||
void pushFront (V3List<T>& listr, T newp) {
|
||||
// "this" must be a element inside of *newp
|
||||
uint32_t offset = (uint8_t*)(this) - (uint8_t*)(newp);
|
||||
uint32_t offset = (vluint8_t*)(this) - (vluint8_t*)(newp);
|
||||
m_nextp = listr.m_headp;
|
||||
if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = newp;
|
||||
listr.m_headp = newp;
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
// Unlink from side
|
||||
void unlink (V3List<T>& listr, T oldp) {
|
||||
// "this" must be a element inside of *oldp
|
||||
uint32_t offset = (uint8_t*)(this) - (uint8_t*)(oldp);
|
||||
uint32_t offset = (vluint8_t*)(this) - (vluint8_t*)(oldp);
|
||||
if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = m_prevp;
|
||||
else listr.m_tailp = m_prevp;
|
||||
if (m_prevp) baseToListEnt(m_prevp,offset)->m_nextp = m_nextp;
|
||||
|
||||
+6
-2
@@ -115,9 +115,7 @@ V3Number::V3Number (FileLine* fileline, const char* sourcep) {
|
||||
// Otherwise...
|
||||
else if (!m_sized) {
|
||||
width(32, false); // Says the spec.
|
||||
#ifndef VL_UNSIGNED
|
||||
if (unbased) isSigned(true); // Also says the spec.
|
||||
#endif
|
||||
}
|
||||
|
||||
// Ignore leading blanks
|
||||
@@ -430,6 +428,12 @@ uint32_t V3Number::countOnes() const {
|
||||
return n;
|
||||
}
|
||||
|
||||
uint32_t V3Number::mostSetBitP1() const {
|
||||
for (int bit=this->width()-1; bit>=0; bit--) {
|
||||
if (bitIs1(bit)) return bit+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//======================================================================
|
||||
|
||||
V3Number& V3Number::opBitsNonX (const V3Number& lhs) { // 0/1->1, X/Z->0
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
uint32_t asHash() const;
|
||||
uint32_t dataWord(int word) const;
|
||||
uint32_t countOnes() const;
|
||||
uint32_t mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0.
|
||||
|
||||
// STATICS
|
||||
static int log2b(uint32_t num);
|
||||
|
||||
@@ -366,6 +366,7 @@ void V3Options::parseOptsList(FileLine* fl, int argc, char** argv) {
|
||||
else if ( onoff (sw, "-coverage-line", flag/*ref*/) ){ m_coverageLine = flag; }
|
||||
else if ( onoff (sw, "-coverage-user", flag/*ref*/) ){ m_coverageUser = flag; }
|
||||
else if ( onoff (sw, "-covsp", flag/*ref*/) ) { } // TBD
|
||||
else if ( onoff (sw, "-debug-check", flag/*ref*/) ){ m_debugCheck = flag; }
|
||||
else if ( onoff (sw, "-dump-tree", flag/*ref*/) ) { m_dumpTree = flag; }
|
||||
else if ( onoff (sw, "-exe", flag/*ref*/) ) { m_exe = flag; }
|
||||
else if ( onoff (sw, "-ignc", flag/*ref*/) ) { m_ignc = flag; }
|
||||
@@ -557,6 +558,7 @@ V3Options::V3Options() {
|
||||
|
||||
m_coverageLine = false;
|
||||
m_coverageUser = false;
|
||||
m_debugCheck = false;
|
||||
m_dumpTree = false;
|
||||
m_exe = false;
|
||||
m_ignc = false;
|
||||
@@ -603,6 +605,7 @@ void V3Options::setDebugMode(int level) {
|
||||
V3Error::debugDefault(level);
|
||||
m_dumpTree = true;
|
||||
m_stats = true;
|
||||
m_debugCheck = true;
|
||||
cout << "Starting "<<version()<<endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ class V3Options {
|
||||
bool m_assert; // main switch: --assert
|
||||
bool m_coverageLine; // main switch: --coverage-block
|
||||
bool m_coverageUser; // main switch: --coverage-func
|
||||
bool m_debugCheck; // main switch: --debug-check
|
||||
bool m_dumpTree; // main switch: --dump-tree
|
||||
bool m_exe; // main switch: --exe
|
||||
bool m_ignc; // main switch: --ignc
|
||||
@@ -134,11 +135,13 @@ class V3Options {
|
||||
bool coverage() const { return m_coverageUser || m_coverageLine; }
|
||||
bool coverageLine() const { return m_coverageLine; }
|
||||
bool coverageUser() const { return m_coverageUser; }
|
||||
bool debugCheck() const { return m_debugCheck; }
|
||||
bool dumpTree() const { return m_dumpTree; }
|
||||
bool exe() const { return m_exe; }
|
||||
bool trace() const { return m_trace; }
|
||||
bool traceDups() const { return m_traceDups; }
|
||||
bool outFormatOk() const { return m_outFormatOk; }
|
||||
bool keepTempFiles() const { return (V3Error::debugDefault()!=0); }
|
||||
bool pins64() const { return m_pins64; }
|
||||
bool profileCFuncs() const { return m_profileCFuncs; }
|
||||
bool psl() const { return m_psl; }
|
||||
@@ -181,6 +184,7 @@ class V3Options {
|
||||
static string version();
|
||||
static string argString(int argc, char** argv); ///< Return list of arguments as simple string
|
||||
string allArgsString(); ///< Return all passed arguments as simple string
|
||||
void bin(const string& flag) { m_bin = flag; }
|
||||
void parseOpts(FileLine* fl, int argc, char** argv);
|
||||
void parseOptsList (FileLine* fl, int argc, char** argv);
|
||||
void parseOptsFile (FileLine* fl, const string& filename);
|
||||
|
||||
+7
-3
@@ -127,10 +127,14 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
// After expanding the generate, all statements under it can be moved
|
||||
// up, and the generate block deleted as it's not relevant
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
nodep->replaceWith(stmtsp);
|
||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->replaceWith(stmtsp);
|
||||
if (debug()>=9) stmtsp->dumpTree(cout,"-genout: ");
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
if (debug()>=9) stmtsp->dumpTree(cout,"-genout: ");
|
||||
}
|
||||
virtual void visit(AstGenIf* nodep, AstNUser*) {
|
||||
V3Width::widthParams(nodep); // Param typed widthing will NOT recurse the body
|
||||
|
||||
+24
-24
@@ -58,8 +58,9 @@ static void pslMoreNeeded(bool flag) { V3PreLex::s_currentLexp->m_pslMoreNeeded
|
||||
%x ARGMODE
|
||||
%x INCMODE
|
||||
|
||||
ws [ \t\r\f]
|
||||
newline [\n]
|
||||
ws [ \t\f\r]
|
||||
wsn [ \t\f]
|
||||
crnl [\r]*[\n]
|
||||
quote [\"]
|
||||
backslash [\\]
|
||||
symb [a-zA-Z_][a-zA-Z0-9_$]*
|
||||
@@ -68,7 +69,7 @@ psl [p]sl
|
||||
/**************************************************************/
|
||||
%%
|
||||
|
||||
<INITIAL>^{ws}*"`line"{ws}+.*{newline} { V3PreLex::s_currentLexp->lineDirective(yytext); }
|
||||
<INITIAL>^{ws}*"`line"{ws}+.*{crnl} { V3PreLex::s_currentLexp->lineDirective(yytext); }
|
||||
|
||||
/* Special directives we recognise */
|
||||
<INITIAL>"`include" { return(VP_INCLUDE); }
|
||||
@@ -95,7 +96,7 @@ psl [p]sl
|
||||
/* Pass-through strings */
|
||||
<INITIAL,PSLMULM,PSLONEM>{quote} { yy_push_state(STRMODE); yymore(); }
|
||||
<STRMODE><<EOF>> { linenoInc(); yyerror("EOF in unterminated string"); yyleng=0; yyterminate(); }
|
||||
<STRMODE>{newline} { linenoInc(); yyerror("Unterminated string"); BEGIN(INITIAL); }
|
||||
<STRMODE>{crnl} { linenoInc(); yyerror("Unterminated string"); BEGIN(INITIAL); }
|
||||
<STRMODE>[^\"\\] { yymore(); }
|
||||
<STRMODE>{backslash}. { yymore(); }
|
||||
<STRMODE>{quote} { yy_pop_state();
|
||||
@@ -104,28 +105,26 @@ psl [p]sl
|
||||
|
||||
/* Pass-through include <> filenames */
|
||||
<INCMODE><<EOF>> { linenoInc(); yyerror("EOF in unterminated include filename"); yyleng=0; yyterminate(); }
|
||||
<INCMODE>{newline} { linenoInc(); yyerror("Unterminated include filename"); BEGIN(INITIAL); }
|
||||
<INCMODE>{crnl} { linenoInc(); yyerror("Unterminated include filename"); BEGIN(INITIAL); }
|
||||
<INCMODE>[^\>\\] { yymore(); }
|
||||
<INCMODE>{backslash}. { yymore(); }
|
||||
<INCMODE>[\>] { yy_pop_state(); return (VP_STRING); }
|
||||
|
||||
/* Reading definition */
|
||||
<DEFMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
|
||||
<DEFMODE>"//"[^\n]* { return (VP_COMMENT);}
|
||||
<DEFMODE>"//"[^\n\r]* { return (VP_COMMENT);}
|
||||
<DEFMODE><<EOF>> { linenoInc(); yyerror("EOF (missing return?) in define value"); yyleng=0; yyterminate(); }
|
||||
<DEFMODE>{newline} { linenoInc();
|
||||
yy_pop_state();
|
||||
return (VP_DEFVALUE); } /* Note contains a return */
|
||||
<DEFMODE>[^\/\*\n\m\\]+ |
|
||||
<DEFMODE>[\\][^\n] |
|
||||
<DEFMODE>{crnl} { linenoInc(); yy_pop_state(); yytext="\n"; yyleng=1; return (VP_DEFVALUE); }
|
||||
<DEFMODE>[\\]{crnl} { linenoInc(); appendDefValue("\n",1); } /* Include return so can maintain output line count */
|
||||
<DEFMODE>[^\/\*\n\r\\]+ |
|
||||
<DEFMODE>[\\][^\n\r] |
|
||||
<DEFMODE>. { appendDefValue(yytext,yyleng); }
|
||||
<DEFMODE>[\\]\n { linenoInc(); appendDefValue("\n",1); }
|
||||
|
||||
/* Define arguments */
|
||||
<ARGMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
|
||||
<ARGMODE>"//"[^\n]* { return (VP_COMMENT);}
|
||||
<ARGMODE>"//"[^\n\r]* { return (VP_COMMENT);}
|
||||
<ARGMODE><<EOF>> { yyerror("EOF in define argument list\n"); yyleng = 0; yyterminate(); }
|
||||
<ARGMODE>{newline} { linenoInc(); yytext="\n"; yyleng=1; return(VP_WHITE); }
|
||||
<ARGMODE>{crnl} { linenoInc(); yytext="\n"; yyleng=1; return(VP_WHITE); }
|
||||
<ARGMODE>{quote} { yy_push_state(STRMODE); yymore(); }
|
||||
<ARGMODE>[(] { V3PreLex::s_currentLexp->m_parenLevel++; appendDefValue(yytext,yyleng); }
|
||||
<ARGMODE>[,)] { if (V3PreLex::s_currentLexp->m_parenLevel>1) {
|
||||
@@ -134,26 +133,26 @@ psl [p]sl
|
||||
} else {
|
||||
unput(yytext[0]); yy_pop_state(); return (VP_DEFARG);
|
||||
}}
|
||||
<ARGMODE>[^\/\*\n\m\\(,)\"]+ |
|
||||
<ARGMODE>[^\/\*\n\r\\(,)\"]+ |
|
||||
<ARGMODE>. { appendDefValue(yytext,yyleng); }
|
||||
|
||||
/* One line comments. */
|
||||
<INITIAL>"//"{ws}*{psl} { if (optPsl()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); }
|
||||
else { yy_push_state(CMTONEM); yymore(); } }
|
||||
<INITIAL>"//"{newline} { linenoInc(); yytext="\n"; yyleng=1; return (VP_WHITE); }
|
||||
<INITIAL>"//"{ws}*{crnl} { linenoInc(); yytext="\n"; yyleng=1; return (VP_WHITE); }
|
||||
<INITIAL>"//" { if (pslMoreNeeded()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); }
|
||||
else { yy_push_state(CMTONEM); yymore(); } }
|
||||
<CMTONEM>[^\n]* { yy_pop_state(); return (VP_COMMENT); }
|
||||
<CMTONEM>[^\n\r]* { yy_pop_state(); return (VP_COMMENT); }
|
||||
|
||||
/* Psl oneline comments */
|
||||
<PSLONEM>[{(] { pslParenLevelInc(); return (VP_TEXT); }
|
||||
<PSLONEM>[})] { pslParenLevelDec(); return (VP_TEXT); }
|
||||
<PSLONEM>[;] { if (!pslParenLevel()) {BEGIN PSLONEE; pslMoreNeeded(false);} return (VP_TEXT); }
|
||||
<PSLONEM><<EOF>> { yyerror("EOF in '/* ... */' psl comment\n"); yyleng=0; yyterminate(); }
|
||||
<PSLONEM>{newline} { linenoInc(); yy_pop_state(); return(VP_WHITE); }
|
||||
<PSLONEM>{crnl} { linenoInc(); yy_pop_state(); yytext="\n"; yyleng=1; return(VP_WHITE); }
|
||||
|
||||
/* Completed psl oneline comments */
|
||||
<PSLONEE>{newline} { linenoInc(); yy_pop_state(); return(VP_WHITE); }
|
||||
<PSLONEE>{crnl} { linenoInc(); yy_pop_state(); yytext="\n"; yyleng=1; return(VP_WHITE); }
|
||||
<PSLONEE>{ws}+ { yymore(); }
|
||||
<PSLONEE>. { yyerror("Unexpected text following psl assertion\n"); }
|
||||
|
||||
@@ -163,24 +162,25 @@ psl [p]sl
|
||||
<CMTBEGM>{psl} { yyleng -= 3; BEGIN PSLMUL1; return (VP_COMMENT); }
|
||||
<CMTBEGM>{ws}+ { yymore(); }
|
||||
<CMTBEGM,CMTMODE>"*/" { yy_pop_state(); return(VP_COMMENT); }
|
||||
<CMTBEGM,CMTMODE>{newline} { linenoInc(); yymore(); }
|
||||
<CMTBEGM,CMTMODE>{crnl} { linenoInc(); yymore(); }
|
||||
<CMTBEGM,CMTMODE><<EOF>> { yyerror("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); }
|
||||
<CMTBEGM>. { BEGIN CMTMODE; yymore(); } /* Non 'psl' beginning in comment */
|
||||
<CMTMODE>. { yymore(); }
|
||||
|
||||
/* Psl C-style comments. */
|
||||
<PSLMUL1>.|{newline} { yyless(0); BEGIN PSLMULM; return(VP_PSL); }
|
||||
<PSLMUL1>.|{crnl} { yyless(0); BEGIN PSLMULM; return(VP_PSL); }
|
||||
<PSLMULM>"*/" { yy_pop_state(); return(VP_COMMENT); }
|
||||
<PSLMULM>"//"[^\n]* { return (VP_COMMENT); } /* Comments inside block comments get literal inclusion (later removal) */
|
||||
<PSLMULM>"//"[^\n\r]* { return (VP_COMMENT); } /* Comments inside block comments get literal inclusion (later removal) */
|
||||
<PSLMULM><<EOF>> { yyerror("EOF in '/* ... */' psl comment\n"); yyleng=0; yyterminate(); }
|
||||
|
||||
/* Define calls */
|
||||
<INITIAL,PSLMULM,PSLONEM>"`"{symb} { return (VP_DEFREF); }
|
||||
|
||||
/* Generics */
|
||||
<INITIAL,PSLMULM>{newline} { linenoInc(); return(VP_WHITE); }
|
||||
<INITIAL,PSLMULM>{crnl} { linenoInc(); yytext="\n"; yyleng=1; return(VP_WHITE); }
|
||||
<INITIAL,PSLMULM,PSLONEM>{symb} { return (VP_SYMBOL); }
|
||||
<INITIAL,PSLMULM,PSLONEM>{ws}+ { return (VP_WHITE); }
|
||||
<INITIAL,PSLMULM,PSLONEM>[\r] { }
|
||||
<INITIAL,PSLMULM,PSLONEM>{wsn}+ { return (VP_WHITE); }
|
||||
<INITIAL,PSLMULM,PSLONEM>. { return (VP_TEXT); }
|
||||
%%
|
||||
|
||||
|
||||
+10
-8
@@ -537,10 +537,12 @@ int V3PreProcImp::getRawToken() {
|
||||
int tok = yylex();
|
||||
|
||||
if (debug()>4) {
|
||||
char buf[10000]; strncpy(buf, yytext, yyleng); buf[yyleng] = '\0';
|
||||
for (char* cp=buf; *cp; cp++) if (*cp=='\n') *cp='$';
|
||||
string buf = string (yytext, yyleng);
|
||||
string::size_type pos;
|
||||
while ((pos=buf.find("\n")) != string::npos) { buf.replace(pos, 1, "\\n"); }
|
||||
while ((pos=buf.find("\r")) != string::npos) { buf.replace(pos, 1, "\\r"); }
|
||||
fprintf (stderr, "%d: RAW %d %d: %-10s: %s\n",
|
||||
fileline()->lineno(), m_off, m_state, tokenName(tok), buf);
|
||||
fileline()->lineno(), m_off, m_state, tokenName(tok), buf.c_str());
|
||||
}
|
||||
|
||||
// On EOF, try to pop to upper level includes, as needed.
|
||||
@@ -867,12 +869,12 @@ string V3PreProcImp::getline() {
|
||||
while (NULL==(rtnp=strchr(m_lineChars.c_str(),'\n'))) {
|
||||
int tok = getToken();
|
||||
if (debug()>4) {
|
||||
char buf[100000];
|
||||
strncpy(buf, yytext, yyleng);
|
||||
buf[yyleng] = '\0';
|
||||
for (char* cp=buf; *cp; cp++) if (*cp=='\n') *cp='$';
|
||||
string buf = string (yytext, yyleng);
|
||||
string::size_type pos;
|
||||
while ((pos=buf.find("\n")) != string::npos) { buf.replace(pos, 1, "\\n"); }
|
||||
while ((pos=buf.find("\r")) != string::npos) { buf.replace(pos, 1, "\\r"); }
|
||||
fprintf (stderr,"%d: GETFETC: %-10s: %s\n",
|
||||
fileline()->lineno(), tokenName(tok), buf);
|
||||
fileline()->lineno(), tokenName(tok), buf.c_str());
|
||||
}
|
||||
if (tok==VP_EOF) {
|
||||
// Add a final newline, in case the user forgot the final \n.
|
||||
|
||||
@@ -79,6 +79,8 @@ protected:
|
||||
ostream* osp;
|
||||
if (v3Global.opt.preprocOnly()) {
|
||||
osp = &cout;
|
||||
} else if (!v3Global.opt.keepTempFiles()) { // Must match unlink rule in V3Read.cpp
|
||||
osp = ofp = V3File::new_ofstream_nodepend(vppFilename);
|
||||
} else {
|
||||
osp = ofp = V3File::new_ofstream(vppFilename);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ void V3Read::readFile(FileLine* fileline, const string& modfilename, bool inLibr
|
||||
if (!v3Global.opt.preprocOnly()) {
|
||||
lexFile (vppfilename, modfilename);
|
||||
}
|
||||
|
||||
if (!v3Global.opt.keepTempFiles()) { // Must match new_ofstream_nodepend rule in V3PreShell.cpp
|
||||
unlink (vppfilename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void V3Read::lexFile(const string& vppfilename, const string& modname) {
|
||||
|
||||
@@ -284,9 +284,7 @@ private:
|
||||
|
||||
// VISITORS - defaults
|
||||
virtual void visit(AstNodeMath* nodep, AstNUser*) {
|
||||
#ifndef VL_UNSIGNED
|
||||
nodep->v3fatalSrc("Visit function missing? Signedness unknown for this node: "<<nodep);
|
||||
#endif
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
@@ -347,12 +345,6 @@ public:
|
||||
|
||||
class SignedRemoveVisitor : public AstNVisitor {
|
||||
private:
|
||||
// METHODS
|
||||
void supportCheck(AstNode* nodep) {
|
||||
#ifdef VL_UNSIGNED
|
||||
if (nodep->isSigned()) nodep->v3error("Unsupported: signed numbers");
|
||||
#endif
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstSigned* nodep, AstNUser*) {
|
||||
replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL;
|
||||
@@ -362,13 +354,11 @@ private:
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
supportCheck(nodep);
|
||||
}
|
||||
void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) {
|
||||
UINFO(6," Replace "<<nodep<<" w/ "<<newp<<endl);
|
||||
nodep->replaceWith(newp);
|
||||
newp->widthSignedFrom(nodep);
|
||||
supportCheck(nodep);
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
public:
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public:
|
||||
~V3Double0() {}
|
||||
|
||||
// Implicit conversion operators:
|
||||
inline V3Double0 (const uint64_t v) : m_d(v) { };
|
||||
inline V3Double0 (const vluint64_t v) : m_d(v) { };
|
||||
inline operator const double () const { return m_d; };
|
||||
|
||||
// Explicit operators:
|
||||
|
||||
+153
-52
@@ -19,10 +19,11 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Subst's Transformations:
|
||||
//
|
||||
//
|
||||
// Each module:
|
||||
// Search all ASSIGN(WORDSEL(...)) and build what it's assigned to
|
||||
// Later usages of that word may then be replaced
|
||||
// Later usages of that word may then be replaced as long as
|
||||
// the RHS hasn't changed value.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -44,76 +45,97 @@
|
||||
|
||||
class SubstBaseVisitor : public AstNVisitor {
|
||||
public:
|
||||
static int debug() { return 0; }
|
||||
static int debug() { return V3Error::debugDefault(); }
|
||||
// static int debug() { return 9; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Subst state, as a visitor of each AstNode
|
||||
// Class for each word of a multi-word variable
|
||||
|
||||
class SubstVarWord {
|
||||
protected:
|
||||
// MEMBERS
|
||||
AstNodeAssign* m_assignp; // Last assignment to each word of this var
|
||||
int m_step; // Step number of last assignment
|
||||
bool m_use; // True if each word was consumed
|
||||
bool m_complex; // True if each word is complex
|
||||
friend class SubstVarEntry;
|
||||
// METHODS
|
||||
void clear() {
|
||||
m_assignp = NULL;
|
||||
m_step = 0;
|
||||
m_use = false;
|
||||
m_complex = false;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Class for every variable we may process
|
||||
|
||||
class SubstVarEntry {
|
||||
// MEMBERS
|
||||
AstVar* m_varp; // Variable this tracks
|
||||
bool m_complexAssign;// True if assigned multiple times or in complex mannor
|
||||
AstNodeAssign* m_wholeAssignp; // Last assignment to entire array
|
||||
bool m_wholeUse; // True if used as entire wide array
|
||||
bool m_wordAssign; // True if any word assignments
|
||||
bool m_wordUse; // True if any individual word usage
|
||||
vector<AstNodeAssign*> m_wordAssignps; // Last assignment to each word of this var
|
||||
vector<bool> m_wordUses; // True if each word was consumed
|
||||
vector<bool> m_wordComplex; // True if each word is complex
|
||||
SubstVarWord m_whole; // Data for whole vector used at once
|
||||
vector<SubstVarWord> m_words; // Data for every word, if multi word variable
|
||||
int debug() { return SubstBaseVisitor::debug(); }
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
SubstVarEntry (AstVar* varp) { // Construction for when a var is used
|
||||
m_varp = varp;
|
||||
m_complexAssign = false;
|
||||
m_wholeAssignp = NULL;
|
||||
m_wholeUse = false;
|
||||
m_whole.m_use = false;
|
||||
m_wordAssign = false;
|
||||
m_wordUse = false;
|
||||
m_wordAssignps.resize(varp->widthWords());
|
||||
m_wordUses.resize(varp->widthWords());
|
||||
m_wordComplex.resize(varp->widthWords());
|
||||
m_words.resize(varp->widthWords());
|
||||
m_whole.clear();
|
||||
for (int i=0; i<varp->widthWords(); i++) {
|
||||
m_wordAssignps[i] = NULL;
|
||||
m_wordUses[i] = false;
|
||||
m_wordComplex[i] = false;
|
||||
m_words[i].clear();
|
||||
}
|
||||
}
|
||||
bool wordNumOk(int word) {
|
||||
~SubstVarEntry() {}
|
||||
private:
|
||||
// METHODS
|
||||
bool wordNumOk(int word) const {
|
||||
return word < m_varp->widthWords();
|
||||
}
|
||||
AstNodeAssign* getWordAssignp(int word) {
|
||||
AstNodeAssign* getWordAssignp(int word) const {
|
||||
if (!wordNumOk(word)) return NULL;
|
||||
else return m_wordAssignps[word];
|
||||
else return m_words[word].m_assignp;
|
||||
}
|
||||
void assignWhole (AstNodeAssign* assp) {
|
||||
if (m_wholeAssignp) m_complexAssign = true;
|
||||
m_wholeAssignp = assp;
|
||||
public:
|
||||
void assignWhole (int step, AstNodeAssign* assp) {
|
||||
if (m_whole.m_assignp) m_whole.m_complex = true;
|
||||
m_whole.m_assignp = assp;
|
||||
m_whole.m_step = step;
|
||||
}
|
||||
void assignWord (int word, AstNodeAssign* assp) {
|
||||
if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true;
|
||||
void assignWord (int step, int word, AstNodeAssign* assp) {
|
||||
if (!wordNumOk(word) || getWordAssignp(word) || m_words[word].m_complex) m_whole.m_complex = true;
|
||||
m_wordAssign = true;
|
||||
if (wordNumOk(word)) m_wordAssignps[word] = assp;
|
||||
if (wordNumOk(word)) {
|
||||
m_words[word].m_assignp = assp;
|
||||
m_words[word].m_step = step;
|
||||
}
|
||||
}
|
||||
void assignWordComplex (int word) {
|
||||
if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true;
|
||||
m_wordComplex[word] = true;
|
||||
void assignWordComplex (int step, int word) {
|
||||
if (!wordNumOk(word) || getWordAssignp(word) || m_words[word].m_complex) m_whole.m_complex = true;
|
||||
m_words[word].m_complex = true;
|
||||
}
|
||||
void assignComplex() {
|
||||
m_complexAssign = true;
|
||||
void assignComplex(int step) {
|
||||
m_whole.m_complex = true;
|
||||
}
|
||||
void consumeWhole() { //==consumeComplex as we don't know the difference
|
||||
m_wholeUse = true;
|
||||
m_whole.m_use = true;
|
||||
}
|
||||
void consumeWord(int word) {
|
||||
m_wordUses[word] = true;
|
||||
m_words[word].m_use = true;
|
||||
m_wordUse = true;
|
||||
}
|
||||
// ACCESSORS
|
||||
AstNode* substWhole(AstNode* errp) {
|
||||
if (!m_varp->isWide()
|
||||
&& !m_complexAssign && m_wholeAssignp && !m_wordAssign) {
|
||||
AstNodeAssign* assp = m_wholeAssignp;
|
||||
&& !m_whole.m_complex && m_whole.m_assignp && !m_wordAssign) {
|
||||
AstNodeAssign* assp = m_whole.m_assignp;
|
||||
if (!assp) errp->v3fatalSrc("Reading whole that was never assigned");
|
||||
return (assp->rhsp());
|
||||
} else {
|
||||
@@ -121,7 +143,7 @@ public:
|
||||
}
|
||||
}
|
||||
AstNode* substWord(AstNode* errp, int word) { // Return what to substitute given word number for
|
||||
if (!m_complexAssign && !m_wholeAssignp && !m_wordComplex[word]) {
|
||||
if (!m_whole.m_complex && !m_whole.m_assignp && !m_words[word].m_complex) {
|
||||
AstNodeAssign* assp = getWordAssignp(word);
|
||||
if (!assp) errp->v3fatalSrc("Reading a word that was never assigned, or bad word #");
|
||||
return (assp->rhsp());
|
||||
@@ -129,34 +151,90 @@ public:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
int getWholeStep() const {
|
||||
return m_whole.m_step;
|
||||
}
|
||||
int getWordStep(int word) const {
|
||||
if (!wordNumOk(word)) return 0; else return m_words[word].m_step;
|
||||
}
|
||||
void deleteAssign (AstNodeAssign* nodep) {
|
||||
UINFO(5, "Delete "<<nodep<<endl);
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
}
|
||||
void deleteUnusedAssign() {
|
||||
// If there are unused assignments in this var, kill them
|
||||
if (!m_wholeUse && !m_wordUse && m_wholeAssignp) {
|
||||
deleteAssign (m_wholeAssignp); m_wholeAssignp=NULL;
|
||||
if (!m_whole.m_use && !m_wordUse && m_whole.m_assignp) {
|
||||
deleteAssign (m_whole.m_assignp); m_whole.m_assignp=NULL;
|
||||
}
|
||||
for (unsigned i=0; i<m_wordUses.size(); i++) {
|
||||
if (!m_wholeUse && !m_wordUses[i] && m_wordAssignps[i] && !m_wordComplex[i]) {
|
||||
deleteAssign (m_wordAssignps[i]); m_wordAssignps[i]=NULL;
|
||||
for (unsigned i=0; i<m_words.size(); i++) {
|
||||
if (!m_whole.m_use && !m_words[i].m_use && m_words[i].m_assignp && !m_words[i].m_complex) {
|
||||
deleteAssign (m_words[i].m_assignp); m_words[i].m_assignp=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// See if any variables have changed value since we determined subst value, as a visitor of each AstNode
|
||||
|
||||
class SubstUseVisitor : public SubstBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// See SubstVisitor
|
||||
//
|
||||
// STATE
|
||||
int m_origStep; // Step number where subst was recorded
|
||||
bool m_ok; // No misassignments found
|
||||
|
||||
// METHODS
|
||||
SubstVarEntry* findEntryp(AstVarRef* nodep) {
|
||||
return (SubstVarEntry*)(nodep->varp()->userp()); // Might be NULL
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
SubstVarEntry* entryp = findEntryp (nodep);
|
||||
if (entryp) {
|
||||
// Don't sweat it. We assign a new temp variable for every new assignment,
|
||||
// so there's no way we'd ever replace a old value.
|
||||
} else {
|
||||
// A simple variable; needs checking.
|
||||
if (m_origStep < nodep->varp()->user2()) {
|
||||
if (m_ok) UINFO(9," RHS variable changed since subst recorded: "<<nodep<<endl);
|
||||
m_ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConst* nodep, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
SubstUseVisitor(AstNode* nodep, int origStep) {
|
||||
UINFO(9, " SubstUseVisitor "<<origStep<<" "<<nodep<<endl);
|
||||
m_ok = true;
|
||||
m_origStep = origStep;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~SubstUseVisitor() {}
|
||||
// METHODS
|
||||
bool ok() const { return m_ok; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Subst state, as a visitor of each AstNode
|
||||
|
||||
class SubstVisitor : public SubstBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Passed to SubstElimVisitor
|
||||
// Passed to SubstUseVisitor
|
||||
// AstVar::userp -> SubstVar* for usage var, 0=not set yet
|
||||
// AstVar::user2 -> int step number for last assignment, 0=not set yet
|
||||
//
|
||||
// STATE
|
||||
vector<SubstVarEntry*> m_entryps; // Nodes to delete when we are finished
|
||||
int m_ops; // Number of operators on assign rhs
|
||||
int m_assignStep; // Assignment number to determine var lifetime
|
||||
V3Double0 m_statSubsts; // Statistic tracking
|
||||
|
||||
enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in
|
||||
@@ -178,6 +256,7 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
m_ops = 0;
|
||||
m_assignStep++;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
bool hit=false;
|
||||
if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) {
|
||||
@@ -186,10 +265,10 @@ private:
|
||||
hit = true;
|
||||
if (m_ops > SUBST_MAX_OPS_SUBST) {
|
||||
UINFO(8," ASSIGNtooDeep "<<varrefp<<endl);
|
||||
entryp->assignComplex();
|
||||
entryp->assignComplex(m_assignStep);
|
||||
} else {
|
||||
UINFO(8," ASSIGNwhole "<<varrefp<<endl);
|
||||
entryp->assignWhole(nodep);
|
||||
entryp->assignWhole(m_assignStep, nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,10 +281,10 @@ private:
|
||||
hit = true;
|
||||
if (m_ops > SUBST_MAX_OPS_SUBST) {
|
||||
UINFO(8," ASSIGNtooDeep "<<varrefp<<endl);
|
||||
entryp->assignWordComplex(word);
|
||||
entryp->assignWordComplex(m_assignStep, word);
|
||||
} else {
|
||||
UINFO(8," ASSIGNword"<<word<<" "<<varrefp<<endl);
|
||||
entryp->assignWord(word, nodep);
|
||||
entryp->assignWord(m_assignStep, word, nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +317,13 @@ private:
|
||||
UINFO(8," USEword"<<word<<" "<<varrefp<<endl);
|
||||
SubstVarEntry* entryp = getEntryp(varrefp);
|
||||
if (AstNode* substp = entryp->substWord (nodep, word)) {
|
||||
replaceSubstEtc(nodep, substp); nodep=NULL;
|
||||
// Check that the RHS hasn't changed value since we recorded it.
|
||||
SubstUseVisitor visitor (substp, entryp->getWordStep(word));
|
||||
if (visitor.ok()) {
|
||||
replaceSubstEtc(nodep, substp); nodep=NULL;
|
||||
} else {
|
||||
entryp->consumeWord(word);
|
||||
}
|
||||
} else {
|
||||
entryp->consumeWord(word);
|
||||
}
|
||||
@@ -247,14 +332,27 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// Any variable
|
||||
if (nodep->lvalue()) {
|
||||
m_assignStep++;
|
||||
nodep->varp()->user2(m_assignStep);
|
||||
UINFO(9, " ASSIGNstep u2="<<nodep->varp()->user2()<<" "<<nodep<<endl);
|
||||
}
|
||||
if (nodep->varp()->isStatementTemp()) {
|
||||
SubstVarEntry* entryp = getEntryp (nodep);
|
||||
if (nodep->lvalue()) {
|
||||
UINFO(8," ASSIGNcpx "<<nodep<<endl);
|
||||
entryp->assignComplex();
|
||||
entryp->assignComplex(m_assignStep);
|
||||
} else if (AstNode* substp = entryp->substWhole(nodep)) {
|
||||
UINFO(8," USEwhole "<<nodep<<endl);
|
||||
replaceSubstEtc(nodep, substp); nodep=NULL;
|
||||
// Check that the RHS hasn't changed value since we recorded it.
|
||||
SubstUseVisitor visitor (substp, entryp->getWholeStep());
|
||||
if (visitor.ok()) {
|
||||
UINFO(8," USEwhole "<<nodep<<endl);
|
||||
replaceSubstEtc(nodep, substp); nodep=NULL;
|
||||
} else {
|
||||
UINFO(8," USEwholeButChg "<<nodep<<endl);
|
||||
entryp->consumeWhole();
|
||||
}
|
||||
} else { // Consumed w/o substitute
|
||||
UINFO(8," USEwtf "<<nodep<<endl);
|
||||
entryp->consumeWhole();
|
||||
@@ -262,6 +360,7 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {}
|
||||
virtual void visit(AstConst* nodep, AstNUser*) {}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
m_ops++;
|
||||
if (!nodep->isSubstOptimizable()) {
|
||||
@@ -273,7 +372,9 @@ public:
|
||||
// CONSTUCTORS
|
||||
SubstVisitor(AstNode* nodep) {
|
||||
AstNode::userClearTree(); // userp() used on entire tree
|
||||
AstNode::user2ClearTree(); // user2p() used on entire tree
|
||||
m_ops = 0;
|
||||
m_assignStep = 0;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~SubstVisitor() {
|
||||
|
||||
+402
-133
@@ -39,6 +39,54 @@
|
||||
#include "V3Inst.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
//######################################################################
|
||||
// Graph subclasses
|
||||
|
||||
class TaskBaseVertex : public V3GraphVertex {
|
||||
AstNode* m_impurep; // Node causing impure function w/ outside references
|
||||
bool m_noInline; // Marked with pragma
|
||||
public:
|
||||
TaskBaseVertex(V3Graph* graphp)
|
||||
: V3GraphVertex(graphp), m_impurep(NULL), m_noInline(false) {}
|
||||
virtual ~TaskBaseVertex() {}
|
||||
bool pure() const { return m_impurep==NULL; }
|
||||
AstNode* impureNode() const { return m_impurep; }
|
||||
void impure(AstNode* nodep) { m_impurep = nodep; }
|
||||
bool noInline() const { return m_noInline; }
|
||||
void noInline(bool flag) { m_noInline = flag; }
|
||||
};
|
||||
|
||||
class TaskFTaskVertex : public TaskBaseVertex {
|
||||
// Every task gets a vertex, and we link tasks together based on funcrefs.
|
||||
AstNodeFTask* m_nodep;
|
||||
public:
|
||||
TaskFTaskVertex(V3Graph* graphp, AstNodeFTask* nodep)
|
||||
: TaskBaseVertex(graphp), m_nodep(nodep) {}
|
||||
virtual ~TaskFTaskVertex() {}
|
||||
AstNodeFTask* nodep() const { return m_nodep; }
|
||||
virtual string name() const { return nodep()->name(); }
|
||||
virtual string dotColor() const { return pure() ? "black" : "red"; }
|
||||
};
|
||||
|
||||
class TaskCodeVertex : public TaskBaseVertex {
|
||||
// Top vertex for all calls not under another task
|
||||
public:
|
||||
TaskCodeVertex(V3Graph* graphp)
|
||||
: TaskBaseVertex(graphp) {}
|
||||
virtual ~TaskCodeVertex() {}
|
||||
virtual string name() const { return "*CODE*"; }
|
||||
virtual string dotColor() const { return "green"; }
|
||||
};
|
||||
|
||||
class TaskEdge : public V3GraphEdge {
|
||||
public:
|
||||
TaskEdge(V3Graph* graphp, TaskBaseVertex* fromp, TaskBaseVertex* top)
|
||||
: V3GraphEdge(graphp, fromp, top, 1, false) {}
|
||||
virtual ~TaskEdge() {}
|
||||
virtual string dotLabel() const { return "w"+cvtToStr(weight()); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -47,11 +95,17 @@ private:
|
||||
// NODE STATE
|
||||
// Output:
|
||||
// AstNodeFTask::user3p // AstScope* this FTask is under
|
||||
// AstNodeFTask::user4p // GraphFTaskVertex* this FTask is under
|
||||
// AstVar::user4p // GraphFTaskVertex* this variable is declared in
|
||||
|
||||
// TYPES
|
||||
typedef std::map<pair<AstScope*,AstVar*>,AstVarScope*> VarToScopeMap;
|
||||
// MEMBERS
|
||||
VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings
|
||||
AstAssignW* m_assignwp; // Current assignment
|
||||
V3Graph m_callGraph; // Task call graph
|
||||
TaskBaseVertex* m_curVxp; // Current vertex we're adding to
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
AstScope* getScope(AstNodeFTask* nodep) {
|
||||
@@ -64,7 +118,30 @@ public:
|
||||
if (iter == m_varToScopeMap.end()) nodep->v3fatalSrc("No scope for var");
|
||||
return iter->second;
|
||||
}
|
||||
bool ftaskNoInline(AstNodeFTask* nodep) {
|
||||
return (getFTaskVertex(nodep)->noInline());
|
||||
}
|
||||
void checkPurity(AstNodeFTask* nodep) {
|
||||
checkPurity(nodep, getFTaskVertex(nodep));
|
||||
}
|
||||
void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) {
|
||||
if (!vxp->pure()) {
|
||||
nodep->v3warn(IMPURE,"Unsupported: External variable referenced by non-inlined function/task: "<<nodep->prettyName());
|
||||
vxp->impureNode()->v3warn(IMPURE,"... Location of the external reference: "<<vxp->impureNode()->prettyName());
|
||||
}
|
||||
// And, we need to check all tasks this task calls
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
checkPurity(nodep, static_cast<TaskBaseVertex*>(edgep->top()));
|
||||
}
|
||||
}
|
||||
private:
|
||||
TaskBaseVertex* getFTaskVertex(AstNodeFTask* nodep) {
|
||||
if (!nodep->user4p()) {
|
||||
nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep));
|
||||
}
|
||||
return static_cast<TaskBaseVertex*>(nodep->user4p()->castGraphVertex());
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||
// Each FTask is unique per-scope, so AstNodeFTaskRefs do not need
|
||||
@@ -85,10 +162,56 @@ private:
|
||||
taskp->user3p(nodep);
|
||||
}
|
||||
}
|
||||
// No iterateChildren for speed
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
m_assignwp = nodep;
|
||||
nodep->iterateChildren(*this); nodep=NULL; // May delete nodep.
|
||||
m_assignwp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
if (m_assignwp) {
|
||||
// Wire assigns must become always statements to deal with insertion
|
||||
// of multiple statements. Perhaps someday make all wassigns into always's?
|
||||
UINFO(5," IM_WireRep "<<m_assignwp<<endl);
|
||||
AstNode* lhsp = m_assignwp->lhsp()->unlinkFrBack();
|
||||
AstNode* rhsp = m_assignwp->rhsp()->unlinkFrBack();
|
||||
AstNode* assignp = new AstAssign (m_assignwp->fileline(), lhsp, rhsp);
|
||||
AstNode* alwaysp = new AstAlways (m_assignwp->fileline(), NULL, assignp);
|
||||
m_assignwp->replaceWith(alwaysp); pushDeletep(m_assignwp); m_assignwp=NULL;
|
||||
}
|
||||
// We make multiple edges if a task is called multiple times from another task.
|
||||
new TaskEdge (&m_callGraph, m_curVxp, getFTaskVertex(nodep->taskp()));
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
UINFO(9," TASK "<<nodep<<endl);
|
||||
TaskBaseVertex* lastVxp = m_curVxp;
|
||||
m_curVxp = getFTaskVertex(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
m_curVxp = lastVxp;
|
||||
}
|
||||
virtual void visit(AstPragma* nodep, AstNUser*) {
|
||||
if (nodep->pragType() == AstPragmaType::NO_INLINE_TASK) {
|
||||
// Just mark for the next steps, and we're done with it.
|
||||
m_curVxp->noInline(true);
|
||||
nodep->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
else {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->user4p(m_curVxp); // Remember what task it's under
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->varp()->user4p() != m_curVxp) {
|
||||
if (m_curVxp->pure()) {
|
||||
m_curVxp->impure(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Speedup
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
@@ -96,9 +219,16 @@ private:
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
TaskStateVisitor(AstNode* nodep) {
|
||||
TaskStateVisitor(AstNetlist* nodep) {
|
||||
m_assignwp = NULL;
|
||||
m_curVxp = new TaskCodeVertex(&m_callGraph);
|
||||
AstNode::user3ClearTree();
|
||||
AstNode::user4ClearTree();
|
||||
//
|
||||
nodep->iterateAndNext(*this, NULL);
|
||||
//
|
||||
m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue);
|
||||
m_callGraph.dumpDotFilePrefixed("task_call");
|
||||
}
|
||||
virtual ~TaskStateVisitor() {}
|
||||
};
|
||||
@@ -148,11 +278,21 @@ private:
|
||||
// AstNodeFTask::user // True if its been expanded
|
||||
// Each funccall
|
||||
// AstVar::user2p // AstVarScope* to replace varref with
|
||||
// AstNodeFTask::user5p // AstCFunc* created for non-inlined tasks
|
||||
|
||||
// TYPES
|
||||
enum InsertMode {
|
||||
IM_BEFORE, // Pointing at statement ref is in, insert before this
|
||||
IM_AFTER, // Pointing at last inserted stmt, insert after
|
||||
IM_WHILE_PRECOND // Pointing to for loop, add to body end
|
||||
};
|
||||
|
||||
// STATE
|
||||
TaskStateVisitor* m_statep; // Common state between visitors
|
||||
AstModule* m_modp; // Current module
|
||||
AstScope* m_scopep; // Current scope
|
||||
AstNode* m_lastStmtp; // Proceeding statement
|
||||
InsertMode m_insMode; // How to insert
|
||||
AstNode* m_insStmtp; // Where to insert statement
|
||||
int m_modNCalls; // Incrementing func # for making symbols
|
||||
//int debug() { return 9; }
|
||||
|
||||
@@ -181,73 +321,81 @@ private:
|
||||
//
|
||||
// Create input variables
|
||||
AstNode::user2ClearTree();
|
||||
AstNode* pinp = refp->pinsp();
|
||||
AstNode* nextpinp = pinp;
|
||||
AstNode* nextstmtp;
|
||||
for (AstNode* stmtp = newbodysp; stmtp; pinp=nextpinp, stmtp=nextstmtp) {
|
||||
nextstmtp = stmtp->nextp();
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
portp->unlinkFrBack(); // Remove it from the clone (not original)
|
||||
pushDeletep(portp);
|
||||
if (portp->isIO()) {
|
||||
if (pinp==NULL) {
|
||||
refp->v3error("Too few arguments in function call");
|
||||
pinp = new AstConst(refp->fileline(), 0);
|
||||
m_modp->addStmtp(pinp); // For below unlink
|
||||
}
|
||||
UINFO(9, " Port "<<portp<<endl);
|
||||
UINFO(9, " pin "<<pinp<<endl);
|
||||
//
|
||||
nextpinp = pinp->nextp();
|
||||
pinp->unlinkFrBack(); // Relinked to assignment below
|
||||
//
|
||||
if (portp->isTristate()) {
|
||||
refp->v3error("Unsupported: Inouts in functions/tasks");
|
||||
}
|
||||
else if (portp->isOutput() && outvscp) {
|
||||
refp->v3error("Outputs not allowed in function declarations");
|
||||
}
|
||||
else if (portp->isOutput()) {
|
||||
// Make output variables
|
||||
// Correct lvalue; we didn't know when we linked
|
||||
if (AstVarRef* varrefp = pinp->castVarRef()) {
|
||||
varrefp->lvalue(true);
|
||||
} else {
|
||||
pinp->v3error("Unsupported: Task output pin connected to non-variable");
|
||||
{
|
||||
AstNode* pinp = refp->pinsp();
|
||||
AstNode* nextpinp = pinp;
|
||||
AstNode* nextstmtp;
|
||||
for (AstNode* stmtp = newbodysp; stmtp; pinp=nextpinp, stmtp=nextstmtp) {
|
||||
nextstmtp = stmtp->nextp();
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
portp->unlinkFrBack(); // Remove it from the clone (not original)
|
||||
pushDeletep(portp);
|
||||
if (portp->isIO()) {
|
||||
if (pinp==NULL) {
|
||||
refp->v3error("Too few arguments in function call");
|
||||
pinp = new AstConst(refp->fileline(), 0);
|
||||
m_modp->addStmtp(pinp); // For below unlink
|
||||
}
|
||||
// Even if it's referencing a varref, we still make a temporary
|
||||
// Else task(x,x,x) might produce incorrect results
|
||||
AstVarScope* outvscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(outvscp);
|
||||
AstAssign* assp = new AstAssign (pinp->fileline(),
|
||||
pinp,
|
||||
new AstVarRef(outvscp->fileline(), outvscp, false));
|
||||
// Put assignment BEHIND of all other statements
|
||||
beginp->addNext(assp);
|
||||
}
|
||||
else if (portp->isInput()) {
|
||||
// Make input variable
|
||||
AstVarScope* inVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(inVscp);
|
||||
AstAssign* assp = new AstAssign (pinp->fileline(),
|
||||
new AstVarRef(inVscp->fileline(), inVscp, true),
|
||||
pinp);
|
||||
// Put assignment in FRONT of all other statements
|
||||
if (AstNode* afterp = beginp->nextp()) {
|
||||
afterp->unlinkFrBackWithNext();
|
||||
assp->addNext(afterp);
|
||||
UINFO(9, " Port "<<portp<<endl);
|
||||
UINFO(9, " pin "<<pinp<<endl);
|
||||
//
|
||||
nextpinp = pinp->nextp();
|
||||
pinp->unlinkFrBack(); // Relinked to assignment below
|
||||
//
|
||||
if (portp->isInout()) {
|
||||
if (AstVarRef* varrefp = pinp->castVarRef()) {
|
||||
// Connect to this exact variable
|
||||
AstVarScope* localVscp = varrefp->varScopep(); if (!localVscp) varrefp->v3fatalSrc("Null var scope");
|
||||
portp->user2p(localVscp);
|
||||
} else {
|
||||
pinp->v3warn(TASKNSVAR,"Unsupported: Function/task input argument is not simple variable");
|
||||
}
|
||||
}
|
||||
else if (portp->isOutput() && outvscp) {
|
||||
refp->v3error("Outputs not allowed in function declarations");
|
||||
}
|
||||
else if (portp->isOutput()) {
|
||||
// Make output variables
|
||||
// Correct lvalue; we didn't know when we linked
|
||||
if (AstVarRef* varrefp = pinp->castVarRef()) {
|
||||
varrefp->lvalue(true);
|
||||
} else {
|
||||
pinp->v3warn(TASKNSVAR,"Unsupported: Task output pin connected to non-variable");
|
||||
}
|
||||
// Even if it's referencing a varref, we still make a temporary
|
||||
// Else task(x,x,x) might produce incorrect results
|
||||
AstVarScope* outvscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(outvscp);
|
||||
AstAssign* assp = new AstAssign (pinp->fileline(),
|
||||
pinp,
|
||||
new AstVarRef(outvscp->fileline(), outvscp, false));
|
||||
// Put assignment BEHIND of all other statements
|
||||
beginp->addNext(assp);
|
||||
}
|
||||
else if (portp->isInput()) {
|
||||
// Make input variable
|
||||
AstVarScope* inVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(inVscp);
|
||||
AstAssign* assp = new AstAssign (pinp->fileline(),
|
||||
new AstVarRef(inVscp->fileline(), inVscp, true),
|
||||
pinp);
|
||||
// Put assignment in FRONT of all other statements
|
||||
if (AstNode* afterp = beginp->nextp()) {
|
||||
afterp->unlinkFrBackWithNext();
|
||||
assp->addNext(afterp);
|
||||
}
|
||||
beginp->addNext(assp);
|
||||
}
|
||||
beginp->addNext(assp);
|
||||
}
|
||||
}
|
||||
else { // Var is not I/O
|
||||
// Move it to a new localized variable
|
||||
AstVarScope* localVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(localVscp);
|
||||
else { // Var is not I/O
|
||||
// Move it to a new localized variable
|
||||
AstVarScope* localVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
||||
portp->user2p(localVscp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pinp!=NULL) refp->v3error("Too many arguments in function call");
|
||||
}
|
||||
if (pinp!=NULL) refp->v3error("Too many arguments in function call");
|
||||
// Create function output variables
|
||||
if (outvscp) {
|
||||
//UINFO(0, "setflag on "<<funcp->fvarp()<<" to "<<outvscp<<endl);
|
||||
@@ -260,9 +408,71 @@ private:
|
||||
return beginp;
|
||||
}
|
||||
|
||||
AstNode* createNonInlinedFTask(AstNodeFTaskRef* refp, string namePrefix, AstVarScope* outvscp) {
|
||||
// outvscp is the variable for functions only, if NULL, it's a task
|
||||
if (!refp->taskp()) refp->v3fatalSrc("Unlinked?");
|
||||
AstCFunc* cfuncp = refp->taskp()->user5p()->castNode()->castCFunc();
|
||||
|
||||
AstCFunc* makeUserFunc(AstNodeFTask* nodep) {
|
||||
// Given a already cloned node, make a public C function.
|
||||
if (!cfuncp) refp->v3fatalSrc("No non-inline task associated with this task call?");
|
||||
//
|
||||
AstNode* beginp = new AstComment(refp->fileline(), (string)("Function: ")+refp->name());
|
||||
AstCCall* ccallp = new AstCCall(refp->fileline(), cfuncp, NULL);
|
||||
beginp->addNext(ccallp);
|
||||
// Convert complicated outputs to temp signals
|
||||
{
|
||||
AstNode* pinp = refp->pinsp();
|
||||
AstNode* nextpinp = pinp;
|
||||
AstNode* nextstmtp;
|
||||
for (AstNode* stmtp = refp->taskp()->stmtsp(); stmtp; pinp=nextpinp, stmtp=nextstmtp) {
|
||||
nextstmtp = stmtp->nextp();
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (portp->isIO()) {
|
||||
UINFO(9, " Port "<<portp<<endl);
|
||||
UINFO(9, " pin "<<pinp<<endl);
|
||||
//
|
||||
nextpinp = pinp->nextp();
|
||||
//
|
||||
if (portp->isInout()) {
|
||||
if (pinp->castVarRef()) {
|
||||
// Connect to this exact variable
|
||||
} else {
|
||||
pinp->v3warn(TASKNSVAR,"Unsupported: Function/task input argument is not simple variable");
|
||||
}
|
||||
}
|
||||
else if (portp->isOutput()) {
|
||||
// Make output variables
|
||||
// Correct lvalue; we didn't know when we linked
|
||||
if (AstVarRef* varrefp = pinp->castVarRef()) {
|
||||
varrefp->lvalue(true);
|
||||
} else {
|
||||
pinp->v3warn(TASKNSVAR,"Unsupported: Task output pin connected to non-variable");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pinp!=NULL) refp->v3error("Too many arguments in function call");
|
||||
}
|
||||
// First argument is symbol table, then output if a function
|
||||
ccallp->argTypes("vlSymsp");
|
||||
if (outvscp) {
|
||||
ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true));
|
||||
}
|
||||
// Create connections
|
||||
AstNode* nextpinp;
|
||||
for (AstNode* pinp = refp->pinsp(); pinp; pinp=nextpinp) {
|
||||
nextpinp = pinp->nextp();
|
||||
// Move pin to the CCall
|
||||
pinp->unlinkFrBack();
|
||||
ccallp->addArgsp(pinp);
|
||||
}
|
||||
if (debug()>=9) { beginp->dumpTree(cout,"-nitask: "); }
|
||||
return beginp;
|
||||
}
|
||||
|
||||
|
||||
AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool forUser) {
|
||||
// Given a already cloned node, make a public C function, or a non-inline C function
|
||||
// Probably some of this work should be done later, but...
|
||||
// should the type of the function be bool/uint32/64 etc (based on lookup) or IData?
|
||||
AstNode::user2ClearTree();
|
||||
@@ -272,7 +482,8 @@ private:
|
||||
AstVar* portp = NULL;
|
||||
if (NULL!=(portp = nodep->castFunc()->fvarp()->castVar())) {
|
||||
if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var");
|
||||
if (portp->isWide()) nodep->v3error("Unsupported: Public functions/tasks with inputs or outputs > 64 bits wide.");
|
||||
if (portp->isWide()) nodep->v3error("Unsupported: Public functions with return > 64 bits wide. (Make it a output instead.)");
|
||||
if (!forUser) portp->funcReturn(false); // Converting return to 'outputs'
|
||||
portp->unlinkFrBack();
|
||||
rtnvarp = portp;
|
||||
rtnvarp->funcLocal(true);
|
||||
@@ -283,19 +494,28 @@ private:
|
||||
nodep->v3fatalSrc("function without function output variable");
|
||||
}
|
||||
}
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), nodep->name(),
|
||||
m_scopep,
|
||||
(rtnvarp?rtnvarp->cType():""));
|
||||
if (rtnvarp) funcp->addArgsp(rtnvarp);
|
||||
funcp->dontCombine(true);
|
||||
funcp->funcPublic(true);
|
||||
funcp->isStatic(false);
|
||||
AstCFunc* cfuncp = new AstCFunc(nodep->fileline(),
|
||||
string(forUser?"":"__VnoInFunc_") + nodep->name(),
|
||||
m_scopep,
|
||||
((forUser && rtnvarp)?rtnvarp->cType():""));
|
||||
cfuncp->dontCombine(true);
|
||||
cfuncp->entryPoint(true);
|
||||
cfuncp->funcPublic(forUser);
|
||||
cfuncp->isStatic(!forUser);
|
||||
|
||||
// We need to get a pointer to all of our variables (may have eval'ed something else earlier)
|
||||
funcp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(),
|
||||
" "+EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline()," "+EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
if (forUser) {
|
||||
// We need to get a pointer to all of our variables (may have eval'ed something else earlier)
|
||||
cfuncp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(),
|
||||
" "+EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
} else {
|
||||
// Need symbol table
|
||||
cfuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
}
|
||||
// Fake output variable if was a function
|
||||
if (rtnvarp) cfuncp->addArgsp(rtnvarp);
|
||||
|
||||
cfuncp->addInitsp(new AstCStmt(nodep->fileline()," "+EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
|
||||
// Create list of arguments and move to function
|
||||
for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) {
|
||||
@@ -305,12 +525,7 @@ private:
|
||||
// Move it to new function
|
||||
portp->unlinkFrBack();
|
||||
portp->funcLocal(true);
|
||||
funcp->addArgsp(portp);
|
||||
if (portp->isWide()) {
|
||||
// As we don't want to export our WData arrays to users,
|
||||
// and casting to sc_bv's is ugly, we'll just...
|
||||
nodep->v3error("Unsupported: Public functions/tasks with inputs or outputs > 64 bits wide.");
|
||||
}
|
||||
cfuncp->addArgsp(portp);
|
||||
} else {
|
||||
// "Normal" variable, mark inside function
|
||||
portp->funcLocal(true);
|
||||
@@ -322,18 +537,19 @@ private:
|
||||
}
|
||||
// Move body
|
||||
AstNode* bodysp = nodep->stmtsp();
|
||||
if (bodysp) { bodysp->unlinkFrBackWithNext(); funcp->addStmtsp(bodysp); }
|
||||
if (bodysp) { bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); }
|
||||
// Return statement
|
||||
if (rtnvscp) {
|
||||
funcp->addFinalsp(new AstCReturn(rtnvscp->fileline(),
|
||||
new AstVarRef(rtnvscp->fileline(), rtnvscp, false)));
|
||||
if (rtnvscp && forUser) {
|
||||
cfuncp->addFinalsp(new AstCReturn(rtnvscp->fileline(),
|
||||
new AstVarRef(rtnvscp->fileline(), rtnvscp, false)));
|
||||
}
|
||||
// Replace variable refs
|
||||
TaskRelinkVisitor visit (funcp);
|
||||
TaskRelinkVisitor visit (cfuncp);
|
||||
// Delete rest of cloned task and return new func
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
if (debug()>=9) { funcp->dumpTree(cout,"-userFunc: "); }
|
||||
return funcp;
|
||||
if (debug()>=9 && forUser) { cfuncp->dumpTree(cout,"-userFunc: "); }
|
||||
if (debug()>=9 && !forUser) { cfuncp->dumpTree(cout,"-noInFunc: "); }
|
||||
return cfuncp;
|
||||
}
|
||||
|
||||
void iterateIntoFTask(AstNodeFTask* nodep) {
|
||||
@@ -344,18 +560,46 @@ private:
|
||||
nodep->accept(*this);
|
||||
m_scopep = oldscopep;
|
||||
}
|
||||
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
||||
if (debug()>=9) { nodep->dumpTree(cout,"-newstmt:"); }
|
||||
if (!m_insStmtp) nodep->v3fatalSrc("Function not underneath a statement");
|
||||
if (m_insMode == IM_BEFORE) {
|
||||
// Add the whole thing before insertAt
|
||||
UINFO(5," IM_Before "<<m_insStmtp<<endl);
|
||||
AstNRelinker handle;
|
||||
m_insStmtp->unlinkFrBackWithNext(&handle);
|
||||
if (debug()>=9) { newp->dumpTree(cout,"-newfunc:"); }
|
||||
newp->addNext(m_insStmtp);
|
||||
handle.relink(newp);
|
||||
}
|
||||
else if (m_insMode == IM_AFTER) {
|
||||
UINFO(5," IM_After "<<m_insStmtp);
|
||||
m_insStmtp->addNextHere(newp);
|
||||
}
|
||||
else if (m_insMode == IM_WHILE_PRECOND) {
|
||||
UINFO(5," IM_While_Precond "<<m_insStmtp);
|
||||
AstWhile* whilep = m_insStmtp->castWhile();
|
||||
if (!whilep) nodep->v3fatalSrc("Insert should be under WHILE");
|
||||
whilep->addPrecondsp(newp);
|
||||
}
|
||||
else {
|
||||
nodep->v3fatalSrc("Unknown InsertMode");
|
||||
}
|
||||
m_insMode = IM_AFTER;
|
||||
m_insStmtp = newp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstModule* nodep, AstNUser*) {
|
||||
m_modp = nodep;
|
||||
m_lastStmtp = NULL;
|
||||
m_insStmtp = NULL;
|
||||
m_modNCalls = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||
m_scopep = nodep;
|
||||
m_lastStmtp = NULL;
|
||||
m_insStmtp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
m_scopep = NULL;
|
||||
}
|
||||
@@ -365,34 +609,24 @@ private:
|
||||
if (debug()>=9) { nodep->dumpTree(cout,"-inltask:"); }
|
||||
// Create cloned statements
|
||||
string namePrefix = "__Vtask_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++);
|
||||
AstNode* beginp = createInlinedFTask(nodep, namePrefix, NULL);
|
||||
AstNode* beginp;
|
||||
if (m_statep->ftaskNoInline(nodep->taskp())) {
|
||||
beginp = createNonInlinedFTask(nodep, namePrefix, NULL);
|
||||
} else {
|
||||
beginp = createInlinedFTask(nodep, namePrefix, NULL);
|
||||
}
|
||||
// Replace the ref
|
||||
nodep->replaceWith(beginp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
virtual void visit(AstFuncRef* nodep, AstNUser*) {
|
||||
UINFO(4," Func REF "<<nodep<<endl);
|
||||
if (debug()>=9) { m_lastStmtp->dumpTree(cout,"-prestmt:"); }
|
||||
if (debug()>=9) { nodep->dumpTree(cout,"-preref:"); }
|
||||
// First, do hierarchical funcs
|
||||
AstFunc* funcp = nodep->taskp()->castFunc();
|
||||
if (!funcp) nodep->v3fatalSrc("unlinked");
|
||||
AstNode* insertAtp = m_lastStmtp;
|
||||
if (AstAssignW* awp = insertAtp->castAssignW()) {
|
||||
// Wire assigns must become always statements to deal with insertion
|
||||
// of multiple statements. Perhaps someday make all wassigns into always's?
|
||||
AstNode* lhsp = awp->lhsp()->unlinkFrBack();
|
||||
AstNode* rhsp = awp->rhsp()->unlinkFrBack();
|
||||
AstNode* assignp = new AstAssign (awp->fileline(), lhsp, rhsp);
|
||||
AstNode* alwaysp = new AstAlways (awp->fileline(), NULL, assignp);
|
||||
m_lastStmtp = assignp; insertAtp = assignp;
|
||||
awp->replaceWith(alwaysp); pushDeletep(awp); awp=NULL;
|
||||
}
|
||||
// Inline func refs in the function
|
||||
iterateIntoFTask(funcp);
|
||||
// Inline this reference
|
||||
if (debug()>=9) { m_lastStmtp->dumpTree(cout,"-inlstmt:"); }
|
||||
if (!insertAtp) nodep->v3fatalSrc("Function not underneath a statement");
|
||||
UINFO(5," Under "<<insertAtp<<endl);
|
||||
// Create output variabls
|
||||
string namePrefix = "__Vfunc_"+funcp->shortName()+"__"+cvtToStr(m_modNCalls++);
|
||||
AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(),
|
||||
@@ -401,37 +635,48 @@ private:
|
||||
if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); }
|
||||
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?");
|
||||
|
||||
AstNode* beginp = createInlinedFTask(nodep, namePrefix, outvscp);
|
||||
// Add the whole thing before insertAt
|
||||
AstNRelinker handle;
|
||||
insertAtp->unlinkFrBackWithNext(&handle);
|
||||
if (debug()>=9) { beginp->dumpTree(cout,"-newfunc:"); }
|
||||
beginp->addNext(insertAtp);
|
||||
handle.relink(beginp);
|
||||
AstNode* beginp;
|
||||
if (m_statep->ftaskNoInline(nodep->taskp())) {
|
||||
// This may share VarScope's with a public task, if any. Yuk.
|
||||
beginp = createNonInlinedFTask(nodep, namePrefix, outvscp);
|
||||
} else {
|
||||
beginp = createInlinedFTask(nodep, namePrefix, outvscp);
|
||||
}
|
||||
// Replace the ref
|
||||
AstVarRef* outrefp = new AstVarRef (nodep->fileline(), outvscp, false);
|
||||
nodep->replaceWith(outrefp);
|
||||
// Insert new statements
|
||||
insertBeforeStmt(nodep, beginp);
|
||||
// Cleanup
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
if (debug()>=9) { insertAtp->dumpTree(cout,"-newstmt:"); }
|
||||
UINFO(4," Done.\n");
|
||||
UINFO(4," Func REF Done.\n");
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
AstNode* prevLastStmtp = m_lastStmtp;
|
||||
m_lastStmtp = nodep->stmtsp();
|
||||
UINFO(4," Inline "<<nodep<<endl);
|
||||
InsertMode prevInsMode = m_insMode;
|
||||
AstNode* prevInsStmtp = m_insStmtp;
|
||||
m_insMode = IM_BEFORE;
|
||||
m_insStmtp = nodep->stmtsp(); // Might be null if no statements, but we won't use it
|
||||
if (!nodep->user()) {
|
||||
// Expand functions in it & Mark for later delete
|
||||
// Expand functions in it
|
||||
nodep->user(true);
|
||||
if (!nodep->taskPublic()) {
|
||||
nodep->unlinkFrBack();
|
||||
} else {
|
||||
if (nodep->taskPublic()) {
|
||||
// Clone it first, because we may have later FTaskRef's that still need
|
||||
// the original version.
|
||||
AstNodeFTask* clonedFuncp = nodep->cloneTree(false)->castNodeFTask();
|
||||
AstCFunc* cfuncp = makeUserFunc(clonedFuncp);
|
||||
// Replace it
|
||||
nodep->replaceWith(cfuncp);
|
||||
AstCFunc* cfuncp = makeUserFunc(clonedFuncp, true);
|
||||
nodep->addNextHere(cfuncp);
|
||||
iterateIntoFTask(clonedFuncp); // Do the clone too
|
||||
}
|
||||
if (m_statep->ftaskNoInline(nodep)) {
|
||||
m_statep->checkPurity(nodep);
|
||||
AstNodeFTask* clonedFuncp = nodep->cloneTree(false)->castNodeFTask();
|
||||
AstCFunc* cfuncp = makeUserFunc(clonedFuncp, false);
|
||||
nodep->user5p(cfuncp);
|
||||
nodep->addNextHere(cfuncp);
|
||||
iterateIntoFTask(clonedFuncp); // Do the clone too
|
||||
}
|
||||
|
||||
// Any variables inside the function still have varscopes pointing to them.
|
||||
// We're going to delete the vars, so delete the varscopes.
|
||||
if (nodep->castFunc()) {
|
||||
@@ -450,13 +695,35 @@ private:
|
||||
}
|
||||
}
|
||||
// Just push, as other references to func may remain until visitor exits
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
m_lastStmtp = prevLastStmtp;
|
||||
m_insMode = prevInsMode;
|
||||
m_insStmtp = prevInsStmtp;
|
||||
}
|
||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Preconditions insert first just before themselves (the normal rule for other statement types)
|
||||
m_insStmtp = NULL; // First thing should be new statement
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
// Conditions insert first at end of precondsp.
|
||||
m_insMode = IM_WHILE_PRECOND;
|
||||
m_insStmtp = nodep;
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
// Body insert just before themselves
|
||||
m_insStmtp = NULL; // First thing should be new statement
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
// Done the loop
|
||||
m_insStmtp = NULL; // Next thing should be new statement
|
||||
}
|
||||
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
||||
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin.cpp\n");
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
|
||||
m_lastStmtp = nodep;
|
||||
m_insMode = IM_BEFORE;
|
||||
m_insStmtp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_insStmtp = NULL; // Next thing should be new statement
|
||||
}
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
@@ -470,7 +737,9 @@ public:
|
||||
: m_statep(statep) {
|
||||
m_modp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_insStmtp = NULL;
|
||||
AstNode::userClearTree();
|
||||
AstNode::user5ClearTree();
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~TaskVisitor() {}
|
||||
|
||||
+140
-80
@@ -50,6 +50,7 @@ private:
|
||||
AstVar* m_forVarp; // Iterator variable
|
||||
AstVarScope* m_forVscp; // Iterator variable scope (NULL for generate pass)
|
||||
AstConst* m_varValuep; // Current value of loop
|
||||
AstNode* m_ignoreIncp; // Increment node to ignore
|
||||
bool m_varModeCheck; // Just checking RHS assignments
|
||||
bool m_varModeReplace; // Replacing varrefs
|
||||
bool m_varAssignHit; // Assign var hit
|
||||
@@ -66,7 +67,7 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
bool cantUnroll(AstNodeFor* nodep, const char* reason) {
|
||||
bool cantUnroll(AstNode* nodep, const char* reason) {
|
||||
if (m_generate) {
|
||||
nodep->v3error("Unsupported: Can't unroll generate for; "<<reason);
|
||||
}
|
||||
@@ -75,36 +76,44 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool forUnrollCheck(AstNodeFor* nodep) {
|
||||
// Do only the body; ignore the loop variable as a dependency.
|
||||
// Return if we did the replacement or not
|
||||
if (m_varModeCheck || m_varModeReplace) return false;
|
||||
// See if we can make it simple enough to process
|
||||
if (nodep->initsp()) V3Const::constifyTree(nodep->initsp()); // May change what is under init, leave here
|
||||
V3Const::constifyTree(nodep->condp());
|
||||
if (nodep->assignsp()) V3Const::constifyTree(nodep->assignsp());
|
||||
|
||||
AstAssign* initp = nodep->initsp()->castAssign();
|
||||
if (!initp) nodep->v3fatalSrc("no initial assignment");
|
||||
m_forVarp = initp->lhsp()->castVarRef()->varp();
|
||||
m_forVscp = initp->lhsp()->castVarRef()->varScopep();
|
||||
bool forUnrollCheck(AstNode* nodep,
|
||||
AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp)
|
||||
AstNode* precondsp, AstNode* condp,
|
||||
AstNode* incp, // Maybe under nodep or in bodysp
|
||||
AstNode* bodysp) {
|
||||
// To keep the IF levels low, we return as each test fails.
|
||||
UINFO(4, " FOR Check "<<nodep<<endl);
|
||||
if (initp) UINFO(6, " Init "<<initp<<endl);
|
||||
if (precondsp) UINFO(6, " Pcon "<<precondsp<<endl);
|
||||
if (condp) UINFO(6, " Cond "<<condp<<endl);
|
||||
if (incp) UINFO(6, " Inc "<<incp<<endl);
|
||||
// Initial value check
|
||||
AstAssign* initAssp = initp->castAssign();
|
||||
if (!initAssp) return cantUnroll(nodep, "no initial assignment");
|
||||
if (initp->nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list");
|
||||
m_forVarp = initAssp->lhsp()->castVarRef()->varp();
|
||||
m_forVscp = initAssp->lhsp()->castVarRef()->varScopep();
|
||||
if (nodep->castGenFor() && !m_forVarp->isGenVar()) {
|
||||
nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->name()<<endl);
|
||||
}
|
||||
AstNodeBiop* condp = nodep->condp()->castNodeBiop();
|
||||
AstAssign* assignp = nodep->assignsp()->castAssign();
|
||||
AstNodeBiop* incInstrp = assignp->rhsp()->castNodeBiop();
|
||||
if (!assignp) nodep->v3fatalSrc("no increment assignment");
|
||||
UINFO(4, " FOR Check "<<nodep<<endl);
|
||||
if (m_generate) V3Const::constifyParam(initAssp->rhsp());
|
||||
AstConst* constInitp = initAssp->rhsp()->castConst();
|
||||
if (!constInitp) return cantUnroll(nodep, "non-constant initializer");
|
||||
//
|
||||
// Condition check
|
||||
if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list");
|
||||
//
|
||||
// Assignment of next value check
|
||||
AstAssign* incAssp = incp->castAssign();
|
||||
if (!incAssp) return cantUnroll(nodep, "no increment assignment");
|
||||
if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list");
|
||||
AstNodeBiop* incInstrp = incAssp->rhsp()->castNodeBiop();
|
||||
//
|
||||
if (m_forVscp) { UINFO(8, " Loop Variable: "<<m_forVscp<<endl); }
|
||||
else { UINFO(8, " Loop Variable: "<<m_forVarp<<endl); }
|
||||
if (debug()>=9) nodep->dumpTree(cout," for: ");
|
||||
if (debug()>=9) nodep->dumpTree(cout,"- for: ");
|
||||
//
|
||||
// Extract the constant loop bounds
|
||||
// To keep the IF levels low, we return as each test fails.
|
||||
if (m_generate) V3Const::constifyParam(initp->rhsp());
|
||||
AstConst* constInitp = initp->rhsp()->castConst();
|
||||
if (!constInitp) return cantUnroll(nodep, "non-constant initializer");
|
||||
bool subtract = incInstrp->castSub();
|
||||
{
|
||||
if (!subtract && !incInstrp->castAdd()) return cantUnroll(nodep, "missing add/sub for incrementer");
|
||||
@@ -134,13 +143,14 @@ private:
|
||||
bool gte = condp->castGte() || condp->castGteS();
|
||||
if (!lt && !lte && !gt && !gte)
|
||||
return cantUnroll(nodep, "condition not <= or <");
|
||||
if (!condp->lhsp()->castVarRef())
|
||||
AstNodeBiop* condBip = condp->castNodeBiop();
|
||||
if (!condBip->lhsp()->castVarRef())
|
||||
return cantUnroll(nodep, "no variable on lhs of condition");
|
||||
if (condp->lhsp()->castVarRef()->varp() != m_forVarp
|
||||
|| condp->lhsp()->castVarRef()->varScopep() != m_forVscp)
|
||||
if (condBip->lhsp()->castVarRef()->varp() != m_forVarp
|
||||
|| condBip->lhsp()->castVarRef()->varScopep() != m_forVscp)
|
||||
return cantUnroll(nodep, "different variable in condition");
|
||||
if (m_generate) V3Const::constifyParam(condp->rhsp());
|
||||
AstConst* constStopp = condp->rhsp()->castConst();
|
||||
if (m_generate) V3Const::constifyParam(condBip->rhsp());
|
||||
AstConst* constStopp = condBip->rhsp()->castConst();
|
||||
if (!constStopp) return cantUnroll(nodep, "non-constant final value");
|
||||
UINFO(8, " Stop expr ok: "<<constStopp<<endl);
|
||||
//
|
||||
@@ -162,7 +172,10 @@ private:
|
||||
|
||||
// Less then 10 statements in the body?
|
||||
int bodySize = 0;
|
||||
for (AstNode* bodp = nodep->bodysp(); bodp; bodp=bodp->nextp()) {
|
||||
for (AstNode* bodp = precondsp; bodp; bodp=bodp->nextp()) {
|
||||
bodySize++;
|
||||
}
|
||||
for (AstNode* bodp = bodysp; bodp; bodp=bodp->nextp()) {
|
||||
bodySize++;
|
||||
}
|
||||
if (bodySize > v3Global.opt.unrollStmts())
|
||||
@@ -172,30 +185,52 @@ private:
|
||||
// Now, make sure there's no assignment to this variable in the loop
|
||||
m_varModeCheck = true;
|
||||
m_varAssignHit = false;
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
m_ignoreIncp = incp;
|
||||
precondsp->iterateAndNext(*this);
|
||||
bodysp->iterateAndNext(*this);
|
||||
m_varModeCheck = false;
|
||||
m_ignoreIncp = NULL;
|
||||
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
||||
//
|
||||
// Finally, we can do it
|
||||
forUnroller(nodep, constInitp->num(),
|
||||
condp, constStopp->num(),
|
||||
forUnroller(nodep, initp, precondsp, condp, incp, bodysp,
|
||||
constInitp->num(),
|
||||
condBip, constStopp->num(),
|
||||
incInstrp, constIncp->num()); nodep = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void forUnroller(AstNodeFor* nodep, const V3Number& numInit,
|
||||
void forUnroller(AstNode* nodep,
|
||||
AstNode* initp,
|
||||
AstNode* precondsp, AstNode* condp,
|
||||
AstNode* incp, AstNode* bodysp,
|
||||
const V3Number& numInit,
|
||||
AstNodeBiop* cmpInstrp, const V3Number& numStop,
|
||||
AstNodeBiop* incInstrp, const V3Number& numInc) {
|
||||
UINFO(4, " Unroll for var="<<numInit<<"; var<"<<numStop<<"; var+="<<numInc<<endl);
|
||||
AstNode* bodysp = nodep->bodysp(); // Maybe null if no body
|
||||
if (bodysp) bodysp->unlinkFrBackWithNext();
|
||||
|
||||
AstNode* newbodysp = NULL;
|
||||
UINFO(6, " cmpI "<<cmpInstrp<<endl);
|
||||
UINFO(6, " IncI "<<incInstrp<<endl);
|
||||
AstNode* stmtsp = NULL;
|
||||
if (initp) {
|
||||
initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep
|
||||
// Don't add to list, we do it once, and setting loop index isn't needed as we're constant propagating it
|
||||
}
|
||||
if (precondsp) {
|
||||
precondsp->unlinkFrBackWithNext();
|
||||
stmtsp = stmtsp->addNextNull(precondsp);
|
||||
}
|
||||
if (bodysp) {
|
||||
bodysp->unlinkFrBackWithNext();
|
||||
stmtsp = stmtsp->addNextNull(bodysp); // Maybe null if no body
|
||||
}
|
||||
// If it's a While, then incp is already part of bodysp.
|
||||
V3Number loopValue(nodep->fileline(), m_forVarp->width()); // May differ in size from numInitp
|
||||
loopValue.opAssign(numInit);
|
||||
|
||||
AstNode* newbodysp = NULL;
|
||||
AstNode* clonedIncsp = NULL; // Last cloned incp() statements
|
||||
m_statLoops++;
|
||||
if (bodysp) {
|
||||
if (stmtsp) {
|
||||
int times = 0;
|
||||
while (1) {
|
||||
UINFO(8," Looping "<<loopValue<<endl);
|
||||
@@ -205,7 +240,21 @@ private:
|
||||
if (contin.isEqZero()) {
|
||||
break; // Done with the loop
|
||||
} else {
|
||||
AstNode* oneloopp = bodysp->cloneTree(true);
|
||||
// Replace iterator values with constant.
|
||||
AstNode* oneloopp = stmtsp->cloneTree(true);
|
||||
|
||||
// A nicer way to propage the loop constant would be to set the variable to the value
|
||||
// and call a constant-propagator like V3Table, so temp values
|
||||
// that are calculated propagate down.
|
||||
// If we do this, we can remove the below
|
||||
if (nodep->castWhile() && incp) {
|
||||
if (clonedIncsp) {
|
||||
// Previous iteration of loop set the variable.
|
||||
// This set is redundant with this next iteration and can be removed.
|
||||
clonedIncsp->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
clonedIncsp = incp->clonep(); if (!clonedIncsp) nodep->v3fatalSrc("inc failed");
|
||||
}
|
||||
|
||||
m_varValuep = new AstConst(nodep->fileline(), loopValue);
|
||||
m_varModeReplace = true;
|
||||
@@ -228,55 +277,60 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And, leave the iterator at the right final value.
|
||||
if (!nodep->castGenFor()) {
|
||||
AstVarRef* newrefp = (m_forVscp
|
||||
? new AstVarRef(nodep->fileline(), m_forVscp, true)
|
||||
: new AstVarRef(nodep->fileline(), m_forVarp, true));
|
||||
AstAssign* finalAssignp = new AstAssign
|
||||
(nodep->fileline(),
|
||||
newrefp,
|
||||
new AstConst(nodep->fileline(), loopValue));
|
||||
if (newbodysp) newbodysp->addNext(finalAssignp);
|
||||
else newbodysp = finalAssignp;
|
||||
}
|
||||
|
||||
// Leaving the iterator at the final value is handled by the increment statements being left the final body
|
||||
// Replace the FOR()
|
||||
if (newbodysp) nodep->replaceWith(newbodysp);
|
||||
else nodep->unlinkFrBack();
|
||||
if (debug()>=9) newbodysp->dumpTree(cout," _new: ");
|
||||
if (debug()>=9) newbodysp->dumpTree(cout,"- _new: ");
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
||||
if (!m_generate || m_varModeReplace) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (m_varModeCheck || m_varModeReplace) {
|
||||
} else {
|
||||
if (forUnrollCheck(nodep)) {
|
||||
// Constify before unroll call, as it may change what is underneath.
|
||||
if (nodep->precondsp()) V3Const::constifyTree(nodep->precondsp());
|
||||
if (nodep->condp()) V3Const::constifyTree(nodep->condp());
|
||||
// Grab initial value
|
||||
AstNode* initp = NULL; // Should be statement before the while.
|
||||
if (nodep->backp()->nextp() == nodep) initp=nodep->backp();
|
||||
if (initp) V3Const::constifyTree(initp);
|
||||
initp = NULL; if (nodep->backp()->nextp() == nodep) initp=nodep->backp();
|
||||
// Grab assignment
|
||||
AstNode* incp = NULL; // Should be last statement
|
||||
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
||||
if (incp) V3Const::constifyTree(incp);
|
||||
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed
|
||||
// And check it
|
||||
if (forUnrollCheck(nodep, initp,
|
||||
nodep->precondsp(), nodep->condp(),
|
||||
incp, nodep->bodysp())) {
|
||||
nodep=NULL; // Did replacement
|
||||
} else if (m_generate || nodep->castGenFor()) {
|
||||
nodep->v3error("For loop doesn't have genvar index, or is misformed");
|
||||
} else {
|
||||
// So later optimizations don't need to deal with them,
|
||||
// convert leftover FOR's:
|
||||
// FOR(init,cond,assign,body) -> init,WHILE(cond) { body, assign }
|
||||
AstNode* initsp = nodep->initsp(); if (initsp) initsp->unlinkFrBackWithNext();
|
||||
AstNode* condp = nodep->condp(); if (condp) condp->unlinkFrBackWithNext();
|
||||
AstNode* assignsp = nodep->assignsp(); if (assignsp) assignsp->unlinkFrBackWithNext();
|
||||
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
||||
bodysp = bodysp->addNext(assignsp);
|
||||
AstNode* newp = new AstWhile(nodep->fileline(),
|
||||
condp,
|
||||
bodysp);
|
||||
initsp = initsp->addNext(newp);
|
||||
newp = initsp;
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstGenFor* nodep, AstNUser*) {
|
||||
if (!m_generate || m_varModeReplace) {
|
||||
nodep->iterateChildren(*this);
|
||||
} // else V3Param will recursively call each for loop to be unrolled for us
|
||||
if (m_varModeCheck || m_varModeReplace) {
|
||||
} else {
|
||||
// Constify before unroll call, as it may change what is underneath.
|
||||
if (nodep->initsp()) V3Const::constifyTree(nodep->initsp());
|
||||
if (nodep->condp()) V3Const::constifyTree(nodep->condp());
|
||||
if (nodep->incsp()) V3Const::constifyTree(nodep->incsp());
|
||||
if (forUnrollCheck(nodep, nodep->initsp(),
|
||||
NULL, nodep->condp(),
|
||||
nodep->incsp(), nodep->bodysp())) {
|
||||
nodep=NULL; // Did replacement
|
||||
} else {
|
||||
nodep->v3error("For loop doesn't have genvar index, or is misformed");
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
||||
nodep->v3error("V3Task should have removed standard FORs");
|
||||
}
|
||||
|
||||
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||||
// Naming inside loop body; must have been a generate for.
|
||||
@@ -307,7 +361,8 @@ private:
|
||||
}
|
||||
if (m_varModeReplace
|
||||
&& nodep->varp() == m_forVarp
|
||||
&& nodep->varScopep() == m_forVscp) {
|
||||
&& nodep->varScopep() == m_forVscp
|
||||
&& !nodep->lvalue()) {
|
||||
AstNode* newconstp = m_varValuep->cloneTree(false);
|
||||
nodep->replaceWith(newconstp);
|
||||
}
|
||||
@@ -316,7 +371,11 @@ private:
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (m_varModeCheck && nodep == m_ignoreIncp) {
|
||||
// Ignore subtree that is the increment
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -324,6 +383,7 @@ public:
|
||||
UnrollVisitor(AstNode* nodep, bool generate) {
|
||||
m_forVarp = NULL;
|
||||
m_forVscp = NULL;
|
||||
m_ignoreIncp = NULL;
|
||||
m_varModeCheck = false;
|
||||
m_varModeReplace = false;
|
||||
m_inBegin = false;
|
||||
|
||||
+1
-1
@@ -492,7 +492,7 @@ private:
|
||||
nodep->initsp()->iterateAndNext(*this);
|
||||
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
||||
if (!nodep->castGenFor()) nodep->bodysp()->iterateAndNext(*this);
|
||||
nodep->assignsp()->iterateAndNext(*this);
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like a if() condition.
|
||||
}
|
||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||
|
||||
+21
-5
@@ -109,7 +109,7 @@ void process () {
|
||||
// Cross-link signal names
|
||||
V3Link::link(v3Global.rootp());
|
||||
// Cross-link dotted hierarchical references
|
||||
V3LinkDot::linkDot(v3Global.rootp());
|
||||
V3LinkDot::linkDotPrearrayed(v3Global.rootp());
|
||||
// Correct state we couldn't know at parse time, repair SEL's, set lvalue's
|
||||
V3LinkResolve::linkResolve(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree"));
|
||||
@@ -120,7 +120,7 @@ void process () {
|
||||
// Remove parameters by cloning modules to de-parameterized versions
|
||||
// This requires some width calculations and constant propagation
|
||||
V3Param::param(v3Global.rootp());
|
||||
V3LinkDot::linkDot(v3Global.rootp()); // Cleanup as made new modules
|
||||
V3LinkDot::linkDotPrearrayed(v3Global.rootp()); // Cleanup as made new modules
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param.tree"));
|
||||
V3Error::abortIfErrors();
|
||||
|
||||
@@ -169,7 +169,7 @@ void process () {
|
||||
V3Const::constifyAllLint(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||
|
||||
// Remove cell arrays (must be between V3Width and tasking)
|
||||
// Remove cell arrays (must be between V3Width and scoping)
|
||||
V3Inst::dearrayAll(v3Global.rootp());
|
||||
//v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dearray.tree"));
|
||||
|
||||
@@ -189,7 +189,7 @@ void process () {
|
||||
if (v3Global.opt.oInline()) {
|
||||
V3Inline::inlineAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inline.tree"));
|
||||
V3LinkDot::linkDot(v3Global.rootp()); // Cleanup as made new modules
|
||||
V3LinkDot::linkDotArrayed(v3Global.rootp()); // Cleanup as made new modules
|
||||
//v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree"));
|
||||
}
|
||||
|
||||
@@ -248,7 +248,13 @@ void process () {
|
||||
V3Case::caseAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("case.tree"));
|
||||
|
||||
// Push constants across variables and remove redundant assignments
|
||||
V3Const::constifyAll(v3Global.rootp());
|
||||
if (v3Global.opt.oLife()) {
|
||||
V3Life::lifeAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("life.tree"));
|
||||
}
|
||||
|
||||
//v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||
|
||||
// Make large low-fanin logic blocks into lookup tables
|
||||
@@ -286,8 +292,14 @@ void process () {
|
||||
if (v3Global.opt.oGate()) {
|
||||
V3Gate::gateAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("gate.tree"));
|
||||
// V3Gate calls constant propagation itself.
|
||||
}
|
||||
|
||||
// Remove unused vars
|
||||
V3Const::constifyAll(v3Global.rootp());
|
||||
V3Dead::deadifyAll(v3Global.rootp(), true);
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||
|
||||
// Reorder assignments in pipelined blocks
|
||||
if (v3Global.opt.oReorder()) {
|
||||
V3Split::splitReorderAll(v3Global.rootp());
|
||||
@@ -321,7 +333,9 @@ void process () {
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clock.tree"));
|
||||
|
||||
// Cleanup any dly vars or other temps that are simple assignments
|
||||
// Life must be done before Subst, as it assumes each CFunc under _eval is called only once.
|
||||
if (v3Global.opt.oLife()) {
|
||||
V3Const::constifyAll(v3Global.rootp());
|
||||
V3Life::lifeAll(v3Global.rootp());
|
||||
}
|
||||
if (v3Global.opt.oLifePost()) {
|
||||
@@ -333,7 +347,7 @@ void process () {
|
||||
|
||||
// Remove unused vars
|
||||
V3Const::constifyAll(v3Global.rootp());
|
||||
V3Dead::deadifyAll(v3Global.rootp(), false);
|
||||
V3Dead::deadifyAll(v3Global.rootp(), true);
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||
|
||||
#ifndef NEW_ORDERING
|
||||
@@ -453,9 +467,11 @@ int main(int argc, char** argv, char** env) {
|
||||
srand( (int) randseed);
|
||||
|
||||
// Preprocessor
|
||||
// Before command parsing so we can handle -Ds on command line.
|
||||
V3PreShell::boot(env);
|
||||
|
||||
// Command option parsing
|
||||
v3Global.opt.bin(argv[0]);
|
||||
string argString = V3Options::argString(argc-1, argv+1);
|
||||
v3Global.opt.parseOpts(new FileLine("COMMAND_LINE",0), argc-1, argv+1);
|
||||
if (v3Global.opt.coverage() && !v3Global.opt.systemPerl()) {
|
||||
|
||||
+13
-10
@@ -324,6 +324,7 @@ sub tree_line {
|
||||
match_func => "match_${type}_${n}",
|
||||
match_if => $mif,
|
||||
exec_func => $exec_func,
|
||||
uinfo_level => ($to =~ /^!/ ? 0:7),
|
||||
};
|
||||
($typefunc->{uinfo} = $func) =~ s/[ \t\"\{\}]+/ /g;
|
||||
push @{$self->{treeop}{$type}}, $typefunc;
|
||||
@@ -373,8 +374,11 @@ sub _exec_new_recurse {
|
||||
sub treeop_exec_func {
|
||||
my $self = shift;
|
||||
my $func = shift;
|
||||
my $out = "";
|
||||
$func =~ s/^!//;
|
||||
if ($func =~ /^\s*[a-zA-Z0-9]+\s*\(/) { # Function call
|
||||
$func =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g;
|
||||
(my $outl = $func) =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g;
|
||||
$out .= $outl;
|
||||
}
|
||||
elsif ($func =~ /^\s*Ast([a-zA-Z0-9]+) \s*\{\s* (.*) \s* \}$/x) {
|
||||
|
||||
@@ -419,23 +423,22 @@ sub treeop_exec_func {
|
||||
$_Exec_Nsyms = 0;
|
||||
_exec_syms_recurse($aref);
|
||||
|
||||
$func = "";
|
||||
foreach my $sym (sort {$_Exec_Syms{$a} cmp $_Exec_Syms{$b}} (keys %_Exec_Syms)) {
|
||||
my $argnp = $_Exec_Syms{$sym};
|
||||
my $arg = add_nodep($sym);
|
||||
$func .= "AstNode* ${argnp} = ${arg}->unlinkFrBack();\n";
|
||||
$out .= "AstNode* ${argnp} = ${arg}->unlinkFrBack();\n";
|
||||
}
|
||||
|
||||
$func .= "AstNode* newp = " . _exec_new_recurse($aref).";\n";
|
||||
$func .= "nodep->replaceWith(newp);";
|
||||
$func .= "nodep->deleteTree(); nodep=NULL;";
|
||||
#print "FF $func\n" if $Debug;
|
||||
$out .= "AstNode* newp = " . _exec_new_recurse($aref).";\n";
|
||||
$out .= "nodep->replaceWith(newp);";
|
||||
$out .= "nodep->deleteTree(); nodep=NULL;";
|
||||
#print "FF $out\n" if $Debug;
|
||||
} elsif ($func eq "NEVER") {
|
||||
$func = "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");";
|
||||
$out .= "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");";
|
||||
} else {
|
||||
$self->error("Unknown execution function format: $func\n");
|
||||
}
|
||||
return $func;
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub tree_match {
|
||||
@@ -448,7 +451,7 @@ sub tree_match {
|
||||
$self->print(" bool $typefunc->{match_func}(Ast${base}* nodep) {\n",
|
||||
"\t// $typefunc->{comment}\n",);
|
||||
$self->print( "\tif ($typefunc->{match_if}) {\n");
|
||||
$self->print( "\t UINFO(7,(void*)(nodep)<<\" $typefunc->{uinfo}\\n\");\n");
|
||||
$self->print( "\t UINFO($typefunc->{uinfo_level},(void*)(nodep)<<\" $typefunc->{uinfo}\\n\");\n");
|
||||
$self->print( "\t $typefunc->{exec_func};\n");
|
||||
$self->print( "\t return true;\n");
|
||||
$self->print( "\t}\n");
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
//**********************************************************************
|
||||
//**** Version and host name
|
||||
|
||||
#define DTVERSION "Verilator 3.600 08/28/2006"
|
||||
#define DTVERSION "Verilator 3.622 10/17/2006"
|
||||
|
||||
//**********************************************************************
|
||||
//**** Functions
|
||||
|
||||
@@ -429,6 +429,7 @@ escid \\[^ \t\f\r\n]+
|
||||
<VLG,PSL>"/*verilator full_case*/" {yylval.fileline = CRELINE(); return yVL_FULL_CASE;}
|
||||
<VLG,PSL>"/*verilator inline_module*/" {yylval.fileline = CRELINE(); return yVL_INLINE_MODULE;}
|
||||
<VLG,PSL>"/*verilator no_inline_module*/" {yylval.fileline = CRELINE(); return yVL_NO_INLINE_MODULE;}
|
||||
<VLG,PSL>"/*verilator no_inline_task*/" {yylval.fileline = CRELINE(); return yVL_NO_INLINE_TASK;}
|
||||
<VLG,PSL>"/*verilator one_cold*/" {yylval.fileline = CRELINE(); return yVL_ONE_COLD;}
|
||||
<VLG,PSL>"/*verilator one_hot*/" {yylval.fileline = CRELINE(); return yVL_ONE_HOT;}
|
||||
<VLG,PSL>"/*verilator parallel_case*/" {yylval.fileline = CRELINE(); return yVL_PARALLEL_CASE;}
|
||||
|
||||
+9
-9
@@ -168,6 +168,7 @@ class AstSenTree;
|
||||
%token<fileline> yVL_FULL_CASE "/*verilator full_case*/"
|
||||
%token<fileline> yVL_INLINE_MODULE "/*verilator inline_module*/"
|
||||
%token<fileline> yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/"
|
||||
%token<fileline> yVL_NO_INLINE_TASK "/*verilator no_inline_task*/"
|
||||
%token<fileline> yVL_ONE_COLD "/*verilator one_cold*/"
|
||||
%token<fileline> yVL_ONE_HOT "/*verilator one_hot*/"
|
||||
%token<fileline> yVL_PARALLEL_CASE "/*verilator parallel_case*/"
|
||||
@@ -222,7 +223,7 @@ class AstSenTree;
|
||||
%type<nodep> v2kPort ioDecl varDecl
|
||||
%type<nodep> modParDecl modParList modParE
|
||||
%type<nodep> modItem modItemList modItemListOrNone modOrGenItem
|
||||
%type<nodep> genItem genItemList genItemBegin genItemBlock genItemsBlock genCaseList
|
||||
%type<nodep> genItem genItemList genItemBegin genItemBlock genTopBlock genCaseList
|
||||
%type<nodep> dterm
|
||||
%type<varp> onesig sigId sigIdRange paramId sigList regsig regsigList regSigId
|
||||
%type<varp> netSig netSigList
|
||||
@@ -399,7 +400,7 @@ modItemList: modItem { $$ = $1; }
|
||||
;
|
||||
|
||||
modItem: modOrGenItem { $$ = $1; }
|
||||
| yGENERATE genItemsBlock yENDGENERATE { $$ = new AstGenerate($1, $2); }
|
||||
| yGENERATE genTopBlock yENDGENERATE { $$ = new AstGenerate($1, $2); }
|
||||
| ySCHDR { $$ = new AstScHdr(CRELINE(),*$1); }
|
||||
| ySCINT { $$ = new AstScInt(CRELINE(),*$1); }
|
||||
| ySCIMP { $$ = new AstScImp(CRELINE(),*$1); }
|
||||
@@ -437,7 +438,7 @@ genItemBlock: genItem { $$ = new AstBegin(CRELINE(),"genblk",$1); }
|
||||
| genItemBegin { $$ = $1; }
|
||||
;
|
||||
|
||||
genItemsBlock: genItemList { $$ = new AstBegin(CRELINE(),"genblk",$1); }
|
||||
genTopBlock: genItemList { $$ = new AstBegin(CRELINE(),"genblk",$1); }
|
||||
| genItemBegin { $$ = $1; }
|
||||
;
|
||||
|
||||
@@ -627,11 +628,11 @@ senList: senitem { $$ = $1; }
|
||||
;
|
||||
|
||||
senitem: senitemEdge { $$ = $1; }
|
||||
| idVarRef ignoreRangeE { $$ = new AstSenItem(CRELINE(),AstEdgeType::ANYEDGE,$1); }
|
||||
| idVarXRef ignoreRangeE { $$ = new AstSenItem(CRELINE(),AstEdgeType::ANYEDGE,$1); }
|
||||
;
|
||||
|
||||
senitemEdge: yPOSEDGE idVarRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::POSEDGE,$2); }
|
||||
| yNEGEDGE idVarRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::NEGEDGE,$2); }
|
||||
senitemEdge: yPOSEDGE idVarXRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::POSEDGE,$2); }
|
||||
| yNEGEDGE idVarXRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::NEGEDGE,$2); }
|
||||
;
|
||||
|
||||
ignoreRangeE: /* empty */ { $$ = NULL; } /* ignored */
|
||||
@@ -729,7 +730,8 @@ funcVarList: funcVar { $$ = $1; }
|
||||
|
||||
funcVar: ioDecl { $$ = $1; }
|
||||
| varDecl { $$ = $1; }
|
||||
| yVL_PUBLIC { $$ = new AstPragma ($1,AstPragmaType::PUBLIC_TASK); }
|
||||
| yVL_PUBLIC { $$ = new AstPragma($1,AstPragmaType::PUBLIC_TASK); }
|
||||
| yVL_NO_INLINE_TASK { $$ = new AstPragma($1,AstPragmaType::NO_INLINE_TASK); }
|
||||
;
|
||||
|
||||
constExpr: expr { $$ = $1; }
|
||||
@@ -1086,9 +1088,7 @@ AstVar* V3Parse::createVariable(FileLine* fileline, string name, AstRange* array
|
||||
rangep->cloneTree(false),
|
||||
arrayp);
|
||||
nodep->isSigned(V3Parse::s_varSigned);
|
||||
#ifndef VL_UNSIGNED
|
||||
if (type == AstVarType::INTEGER) nodep->isSigned(true);
|
||||
#endif
|
||||
if (V3Parse::s_varDecl != AstVarType::UNKNOWN) nodep->combineType(V3Parse::s_varDecl);
|
||||
if (V3Parse::s_varIO != AstVarType::UNKNOWN) nodep->combineType(V3Parse::s_varIO);
|
||||
|
||||
|
||||
+5
-3
@@ -17,6 +17,8 @@ VERILATOR_ROOT := $(shell pwd)/..
|
||||
export VERILATOR_ROOT
|
||||
PWD := $(shell pwd)
|
||||
|
||||
PERL = perl
|
||||
|
||||
DEBUG_ON = --debug --trace-dups
|
||||
#DEBUG = $(DEBUG_ON)
|
||||
VALGRIND_ON = $(DEBUG_ON) --gdb "valgrind -v --leak-check=yes"
|
||||
@@ -31,11 +33,11 @@ VERILATOR_FLAGS = --cc -f $(PWD)/../test_v/input.vc top.v
|
||||
#VERILATOR_FLAGS += --trace
|
||||
|
||||
prep:
|
||||
perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG) $(VERILATOR_FLAGS)
|
||||
$(PERL) $(VERILATOR_ROOT)/bin/verilator $(DEBUG) $(VERILATOR_FLAGS)
|
||||
prep_dbg:
|
||||
perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS)
|
||||
$(PERL) $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS)
|
||||
prep_vg:
|
||||
perl $(VERILATOR_ROOT)/bin/verilator $(VALGRIND_ON) $(VERILATOR_FLAGS)
|
||||
$(PERL) $(VERILATOR_ROOT)/bin/verilator $(VALGRIND_ON) $(VERILATOR_FLAGS)
|
||||
|
||||
compile:
|
||||
cd obj_dir ; $(MAKE) -j 3 -f ../Makefile_obj
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ include Vtop.mk
|
||||
CPPFLAGS += -DVL_DEBUG=1
|
||||
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
||||
CPPFLAGS += -DVL_THREADED=1
|
||||
CPPFLAGS += -W
|
||||
CPPFLAGS += -Werror
|
||||
CPPFLAGS += -W -Werror -Wall
|
||||
#CPPFLAGS += -Wno-unused-variable # Instead, add VL_ATTR_UNUSED in gen'ed code
|
||||
#CPPFLAGS += -pedantic-errors
|
||||
endif
|
||||
|
||||
|
||||
+21
-11
@@ -123,7 +123,8 @@ sub one_test {
|
||||
} else {
|
||||
$test->oprint("FAILED: ","*"x60,"\n");
|
||||
push @fails, "\t#".$test->soprint("%Error: $test->{errors}\n");
|
||||
push @fails, "\t\tmake && ( cd test_regress ; "
|
||||
my $j = ($opt_jobs>1?" -j 2":"");
|
||||
push @fails, "\t\tmake$j && ( cd test_regress ; "
|
||||
.$test->{pl_filename}." ".join(' ',@Orig_ARGV_Sw)." )\n";
|
||||
$failcnt++;
|
||||
if ($opt_stop) { die "%Error: --stop and errors found\n"; }
|
||||
@@ -186,7 +187,7 @@ sub new {
|
||||
make_top_shell => 1, # Make a default __top.v file
|
||||
make_main => 1, # Make __main.cpp
|
||||
# All compilers
|
||||
v_flags => [split(/\s+/," -f input.vc")],
|
||||
v_flags => [split(/\s+/," -f input.vc --debug-check")],
|
||||
v_flags2 => [], # Overridden in some sim files
|
||||
v_other_filenames => [], # After the filename so we can spec multiple files
|
||||
# VCS
|
||||
@@ -252,7 +253,7 @@ sub read {
|
||||
sub write_status {
|
||||
my $self = shift;
|
||||
my $filename = $self->{status_filename};
|
||||
my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,";
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
||||
print $fh Dumper($self);
|
||||
print $fh "1;";
|
||||
$fh->close();
|
||||
@@ -372,7 +373,7 @@ sub execute {
|
||||
my %param = (%{$self}, @_); # Default arguments are from $self
|
||||
$self->oprint("Run\n");
|
||||
if ($param{vcs}) {
|
||||
#my $fh = IO::File->new("simv.key","w") or die "%Error: $! simv.key,";
|
||||
#my $fh = IO::File->new(">simv.key") or die "%Error: $! simv.key,";
|
||||
#$fh->print("quit\n"); $fh->close;
|
||||
$self->_run(logfile=>"obj_dir/".$self->{name}."_simv.log",
|
||||
cmd=>["./simv",],
|
||||
@@ -461,7 +462,7 @@ sub _run {
|
||||
sleep 1 if ($try!=7);
|
||||
my $moretry = $try!=0;
|
||||
|
||||
my $fh = IO::File->new($param{logfile},"r");
|
||||
my $fh = IO::File->new("<$param{logfile}");
|
||||
next if !$fh && $moretry;
|
||||
local $/; undef $/;
|
||||
my $wholefile = <$fh>;
|
||||
@@ -507,7 +508,7 @@ sub _make_main {
|
||||
$self->_read_inputs();
|
||||
|
||||
my $filename = "obj_dir/$self->{VM_PREFIX}__main.cpp";
|
||||
my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,";
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
||||
|
||||
my $VM_PREFIX = $self->{VM_PREFIX};
|
||||
print $fh "#include \"$VM_PREFIX.h\"\n";
|
||||
@@ -586,7 +587,7 @@ sub _make_top {
|
||||
|
||||
$self->_read_inputs();
|
||||
|
||||
my $fh = IO::File->new($self->{top_shell_filename},"w") or die "%Error: $! $self->{top_shell_filename},";
|
||||
my $fh = IO::File->new(">$self->{top_shell_filename}") or die "%Error: $! $self->{top_shell_filename},";
|
||||
print $fh "module top;\n";
|
||||
foreach my $inp (sort (keys %{$self->{inputs}})) {
|
||||
print $fh " reg ${inp};\n";
|
||||
@@ -639,7 +640,7 @@ sub _read_inputs {
|
||||
my $self = shift;
|
||||
my $filename = $self->{top_filename};
|
||||
$filename = "t/$filename" if !-r $filename;
|
||||
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
|
||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) {
|
||||
$self->{inputs}{$1} = $1;
|
||||
@@ -672,8 +673,8 @@ sub verilator_version {
|
||||
sub files_identical {
|
||||
my $fn1 = shift;
|
||||
my $fn2 = shift;
|
||||
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
|
||||
my $f2 = IO::File->new ($fn2) or die "%Error: $! $fn2,";
|
||||
my $f1 = IO::File->new ("<$fn1"); if (!$f1) { warn "%Error: $! $fn1\n"; return 0; }
|
||||
my $f2 = IO::File->new ("<$fn2"); if (!$f2) { warn "%Error: $! $fn2\n"; return 0; }
|
||||
my @l1 = $f1->getlines();
|
||||
my @l2 = $f2->getlines();
|
||||
my $nl = $#l1; $nl = $#l2 if ($#l2 > $nl);
|
||||
@@ -719,7 +720,7 @@ sub file_contents {
|
||||
my $filename = shift;
|
||||
|
||||
if (!$_File_Contents_Cache{$filename}) {
|
||||
my $fh = IO::File->new($filename,"r");
|
||||
my $fh = IO::File->new("<$filename");
|
||||
if (!$fh) {
|
||||
$_File_Contents_Cache{$filename} = "_Already_Errored_";
|
||||
$self->error("File_grep file not found: ".$filename."\n");
|
||||
@@ -734,6 +735,15 @@ sub file_contents {
|
||||
return $_File_Contents_Cache{$filename};
|
||||
}
|
||||
|
||||
sub write_wholefile {
|
||||
my $self = (ref $_[0]? shift : $Last_Self);
|
||||
my $filename = shift;
|
||||
my $contents = shift;
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! writing $filename,";
|
||||
print $fh $contents;
|
||||
$fh->close;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,109 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
|
||||
reg out1;
|
||||
sub sub (.in(crc[23:0]), .out1(out1));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x out=%x\n",$time, cyc, crc, sum, out1);
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
sum <= {sum[62:0], sum[63]^sum[2]^sum[0]} ^ {63'h0,out1};
|
||||
if (cyc==1) begin
|
||||
// Setup
|
||||
crc <= 64'h00000000_00000097;
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc==90) begin
|
||||
if (sum != 64'h2e5cb972eb02b8a0) $stop;
|
||||
end
|
||||
else if (cyc==91) begin
|
||||
end
|
||||
else if (cyc==92) begin
|
||||
end
|
||||
else if (cyc==93) begin
|
||||
end
|
||||
else if (cyc==94) begin
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module sub (/*AUTOARG*/
|
||||
// Outputs
|
||||
out1,
|
||||
// Inputs
|
||||
in
|
||||
);
|
||||
|
||||
input [23:0] in;
|
||||
output reg [0:0] out1;
|
||||
|
||||
parameter [1023:0] RANDOM = 1024'b101011010100011011100111101001000000101000001111111111100110000110011011010110011101000100110000110101111101000111100100010111001001110001010101000111000100010000010011100001100011110110110000101100011111000110111110010110011000011111111010101110001101010010001111110111100000110111101100110101110001110110000010000110101110111001111001100001101110001011100111001001110101001010000110101010100101111000010000010110100101110100110000110110101000100011101111100011000110011001100010010011001101100100101110010100110101001110011111110010000111001111000010001101100101101110111110001000010110010011100101001011111110011010110111110000110010011110001110110011010011010110011011111001110100010110100011100001011000101111000010011111010111001110110011101110101011111001100011000101000001000100111110010100111011101010101011001101000100000101111110010011010011010001111010001110000110010100011110110011001010000011001010010110111101010010011111111010001000101100010100100010011001100110000111111000001000000001001111101110000100101;
|
||||
|
||||
always @* begin
|
||||
casez (in[17:16])
|
||||
2'b00: casez (in[2:0])
|
||||
3'h0: out1[0] = in[0]^RANDOM[0];
|
||||
3'h1: out1[0] = in[0]^RANDOM[1];
|
||||
3'h2: out1[0] = in[0]^RANDOM[2];
|
||||
3'h3: out1[0] = in[0]^RANDOM[3];
|
||||
3'h4: out1[0] = in[0]^RANDOM[4];
|
||||
3'h5: out1[0] = in[0]^RANDOM[5];
|
||||
3'h6: out1[0] = in[0]^RANDOM[6];
|
||||
3'h7: out1[0] = in[0]^RANDOM[7];
|
||||
endcase
|
||||
2'b01: casez (in[2:0])
|
||||
3'h0: out1[0] = RANDOM[10];
|
||||
3'h1: out1[0] = RANDOM[11];
|
||||
3'h2: out1[0] = RANDOM[12];
|
||||
3'h3: out1[0] = RANDOM[13];
|
||||
3'h4: out1[0] = RANDOM[14];
|
||||
3'h5: out1[0] = RANDOM[15];
|
||||
3'h6: out1[0] = RANDOM[16];
|
||||
3'h7: out1[0] = RANDOM[17];
|
||||
endcase
|
||||
2'b1?: casez (in[4])
|
||||
1'b1: casez (in[2:0])
|
||||
3'h0: out1[0] = RANDOM[20];
|
||||
3'h1: out1[0] = RANDOM[21];
|
||||
3'h2: out1[0] = RANDOM[22];
|
||||
3'h3: out1[0] = RANDOM[23];
|
||||
3'h4: out1[0] = RANDOM[24];
|
||||
3'h5: out1[0] = RANDOM[25];
|
||||
3'h6: out1[0] = RANDOM[26];
|
||||
3'h7: out1[0] = RANDOM[27];
|
||||
endcase
|
||||
1'b0: casez (in[2:0])
|
||||
3'h0: out1[0] = RANDOM[30];
|
||||
3'h1: out1[0] = RANDOM[31];
|
||||
3'h2: out1[0] = RANDOM[32];
|
||||
3'h3: out1[0] = RANDOM[33];
|
||||
3'h4: out1[0] = RANDOM[34];
|
||||
3'h5: out1[0] = RANDOM[35];
|
||||
3'h6: out1[0] = RANDOM[36];
|
||||
3'h7: out1[0] = RANDOM[37];
|
||||
endcase
|
||||
endcase
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,93 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
|
||||
reg out1;
|
||||
reg [4:0] out2;
|
||||
sub sub (.in(crc[23:0]), .out1(out1), .out2(out2));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
//$write("[%0t] cyc==%0d crc=%x sum=%x out=%x,%x\n",$time, cyc, crc, sum, out1,out2);
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
sum <= {sum[62:0], sum[63]^sum[2]^sum[0]} ^ {58'h0,out1,out2};
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
crc <= 64'h00000000_00000097;
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc==90) begin
|
||||
if (sum != 64'hf0afc2bfa78277c5) $stop;
|
||||
end
|
||||
else if (cyc==91) begin
|
||||
end
|
||||
else if (cyc==92) begin
|
||||
end
|
||||
else if (cyc==93) begin
|
||||
end
|
||||
else if (cyc==94) begin
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module sub (/*AUTOARG*/
|
||||
// Outputs
|
||||
out1, out2,
|
||||
// Inputs
|
||||
in
|
||||
);
|
||||
|
||||
input [23:0] in;
|
||||
output reg out1;
|
||||
output reg [4:0] out2;
|
||||
|
||||
always @* begin
|
||||
casez (in)
|
||||
24'b0000_0000_0000_0000_0000_0000 : {out1,out2} = {1'b0,5'h00};
|
||||
24'b????_????_????_????_????_???1 : {out1,out2} = {1'b1,5'h00};
|
||||
24'b????_????_????_????_????_??10 : {out1,out2} = {1'b1,5'h01};
|
||||
24'b????_????_????_????_????_?100 : {out1,out2} = {1'b1,5'h02};
|
||||
24'b????_????_????_????_????_1000 : {out1,out2} = {1'b1,5'h03};
|
||||
24'b????_????_????_????_???1_0000 : {out1,out2} = {1'b1,5'h04};
|
||||
24'b????_????_????_????_??10_0000 : {out1,out2} = {1'b1,5'h05};
|
||||
24'b????_????_????_????_?100_0000 : {out1,out2} = {1'b1,5'h06};
|
||||
24'b????_????_????_????_1000_0000 : {out1,out2} = {1'b1,5'h07};
|
||||
// Same pattern, but reversed to test we work OK.
|
||||
24'b1000_0000_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h17};
|
||||
24'b?100_0000_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h16};
|
||||
24'b??10_0000_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h15};
|
||||
24'b???1_0000_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h14};
|
||||
24'b????_1000_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h13};
|
||||
24'b????_?100_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h12};
|
||||
24'b????_??10_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h11};
|
||||
24'b????_???1_0000_0000_0000_0000 : {out1,out2} = {1'b1,5'h10};
|
||||
24'b????_????_1000_0000_0000_0000 : {out1,out2} = {1'b1,5'h0f};
|
||||
24'b????_????_?100_0000_0000_0000 : {out1,out2} = {1'b1,5'h0e};
|
||||
24'b????_????_??10_0000_0000_0000 : {out1,out2} = {1'b1,5'h0d};
|
||||
24'b????_????_???1_0000_0000_0000 : {out1,out2} = {1'b1,5'h0c};
|
||||
24'b????_????_????_1000_0000_0000 : {out1,out2} = {1'b1,5'h0b};
|
||||
24'b????_????_????_?100_0000_0000 : {out1,out2} = {1'b1,5'h0a};
|
||||
24'b????_????_????_??10_0000_0000 : {out1,out2} = {1'b1,5'h09};
|
||||
24'b????_????_????_???1_0000_0000 : {out1,out2} = {1'b1,5'h08};
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,89 @@
|
||||
[2] crc=0000000000000097 1410
|
||||
[3] crc=000000000000012e 1410
|
||||
[4] crc=000000000000025d 1410
|
||||
[5] crc=00000000000004ba 1410
|
||||
[6] crc=0000000000000974 1410
|
||||
[7] crc=00000000000012e9 1410
|
||||
[8] crc=00000000000025d3 1410
|
||||
[9] crc=0000000000004ba7 1410
|
||||
[10] crc=000000000000974e 1410
|
||||
[11] crc=0000000000012e9d 1410
|
||||
[12] crc=0000000000025d3a 1410
|
||||
[13] crc=000000000004ba74 1410
|
||||
[14] crc=00000000000974e9 1410
|
||||
[15] crc=000000000012e9d3 1410
|
||||
[16] crc=000000000025d3a7 1410
|
||||
[17] crc=00000000004ba74e 1410
|
||||
[18] crc=0000000000974e9d 1410
|
||||
[19] crc=00000000012e9d3a 1410
|
||||
[20] crc=00000000025d3a74 1410
|
||||
[21] crc=0000000004ba74e9 1410
|
||||
[22] crc=000000000974e9d3 1304a:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002031303039;17 1304b:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002031303039203233 1304c:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020313030392032332031333033;4 1304d:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002031303039203233203133303320313338 1304e:203130303920323320313330332031333820202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020 1304: 1009 23 1303 138
|
||||
[23] crc=0000000012e9d3a7 1313: 1009 46 1309 1311 143 1312
|
||||
[24] crc=0000000025d3a74e 1129: 1009 172 407 175 408 409 410 1106
|
||||
[25] crc=000000004ba74e9d 1017: 1009 223 1014 880 885 1015 1016 1007
|
||||
[26] crc=00000000974e9d3a 1231: 1229 967 1230 718
|
||||
[27] crc=000000012e9d3a74 1410
|
||||
[28] crc=000000025d3a74e9 1370: 1009 58 1369 19
|
||||
[29] crc=00000004ba74e9d3 1036: 1009 194 1033 1034 1008 1035 880
|
||||
[30] crc=0000000974e9d3a7 1409:i
|
||||
[31] crc=00000012e9d3a74e 1321: 1009 29 1320 137 144 141 138 148
|
||||
[32] crc=00000025d3a74e9d 1383:§
|
||||
[33] crc=0000004ba74e9d3a 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[34] crc=000000974e9d3a74 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[35] crc=0000012e9d3a74e9 1231: 1228 979 1230 713
|
||||
[36] crc=0000025d3a74e9d3 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[37] crc=000004ba74e9d3a7 1409:i
|
||||
[38] crc=00000974e9d3a74e 1321: 1009 29 1320 137 144 141 138 148
|
||||
[39] crc=000012e9d3a74e9d 1383:§
|
||||
[40] crc=000025d3a74e9d3a 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[41] crc=00004ba74e9d3a74 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[42] crc=0000974e9d3a74e9 1231: 1228 979 1230 713
|
||||
[43] crc=00012e9d3a74e9d3 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[44] crc=00025d3a74e9d3a7 1409:i
|
||||
[45] crc=0004ba74e9d3a74e 1321: 1009 29 1320 137 144 141 138 148
|
||||
[46] crc=000974e9d3a74e9d 1383:§
|
||||
[47] crc=0012e9d3a74e9d3a 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[48] crc=0025d3a74e9d3a74 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[49] crc=004ba74e9d3a74e9 1231: 1228 979 1230 713
|
||||
[50] crc=00974e9d3a74e9d3 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[51] crc=012e9d3a74e9d3a7 1409:i
|
||||
[52] crc=025d3a74e9d3a74e 1321: 1009 29 1320 137 144 141 138 148
|
||||
[53] crc=04ba74e9d3a74e9d 1383:§
|
||||
[54] crc=0974e9d3a74e9d3a 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[55] crc=12e9d3a74e9d3a74 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[56] crc=25d3a74e9d3a74e9 1231: 1228 979 1230 713
|
||||
[57] crc=4ba74e9d3a74e9d3 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[58] crc=974e9d3a74e9d3a7 1409:i
|
||||
[59] crc=2e9d3a74e9d3a74f 1321: 1009 29 1320 137 144 141 138 149
|
||||
[60] crc=5d3a74e9d3a74e9e 1383:§
|
||||
[61] crc=ba74e9d3a74e9d3d 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[62] crc=74e9d3a74e9d3a7b 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[63] crc=e9d3a74e9d3a74f7 1231: 1228 979 1230 713
|
||||
[64] crc=d3a74e9d3a74e9ef 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[65] crc=a74e9d3a74e9d3df 1409:i
|
||||
[66] crc=4e9d3a74e9d3a7bf 1321: 1009 29 1320 137 144 141 145 149
|
||||
[67] crc=9d3a74e9d3a74f7e 1383:§
|
||||
[68] crc=3a74e9d3a74e9efc 1021: 1009 216 1018 882 884 1019 1020 1007
|
||||
[69] crc=74e9d3a74e9d3df9 1017: 1009 197 1014 882 883 1015 1016 1008
|
||||
[70] crc=e9d3a74e9d3a7bf3 1231: 1228 979 1230 713
|
||||
[71] crc=d3a74e9d3a74f7e6 1013: 1009 194 1011 1006 1008 1012 880
|
||||
[72] crc=a74e9d3a74e9efcc 1409:i
|
||||
[73] crc=4e9d3a74e9d3df98 1321: 1009 29 1320 137 147 149 143 142
|
||||
[74] crc=9d3a74e9d3a7bf30 1383:§
|
||||
[75] crc=3a74e9d3a74f7e61 1021: 1009 216 1018 882 885 1019 1020 1007
|
||||
[76] crc=74e9d3a74e9efcc3 1017: 1009 197 1014 882 884 1015 1016 1008
|
||||
[77] crc=e9d3a74e9d3df987 1231: 1228 982 1230 713
|
||||
[78] crc=d3a74e9d3a7bf30f 1013: 1009 194 1011 1006 1008 1012 881 885
|
||||
[79] crc=a74e9d3a74f7e61f 1409:w
|
||||
[80] crc=4e9d3a74e9efcc3f 1321: 1009 30 1320 149 146 146 137 149
|
||||
[81] crc=9d3a74e9d3df987e 1383:ß
|
||||
[82] crc=3a74e9d3a7bf30fc 1021: 1009 225 1018 882 885 1019 1020 1008
|
||||
[83] crc=74e9d3a74f7e61f9 1017: 1009 218 1014 882 884 1015 1016 1008
|
||||
[84] crc=e9d3a74e9efcc3f3 1231: 1228 981 1230 708
|
||||
[85] crc=d3a74e9d3df987e6 1013: 1009 232 1011 1005 1008 1012 881 883
|
||||
[86] crc=a74e9d3a7bf30fcc 1409:s
|
||||
[87] crc=4e9d3a74f7e61f98 1262: 1009 1006 1258 846 1259 1006 1260 833 1261
|
||||
[88] crc=9d3a74e9efcc3f30 1321: 1009 124 1320 146 137 149 137 134
|
||||
[89] crc=3a74e9d3df987e61 1383:˜
|
||||
[90] crc=74e9d3a7bf30fcc3 1036: 1009 215 1033 1034 1008 1035 879
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
$golden_out ||= "t/$Last_Self->{name}.out";
|
||||
|
||||
compile (
|
||||
v_flags2 => [$Last_Self->{v3}?"--stats --O3 -x-assign 0":""],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(files_identical("obj_dir/$Last_Self->{name}_logger.log", $golden_out));
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,44 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
`include "verilated.v"
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
reg [63:0] crc;
|
||||
`verilator_file_descriptor fd;
|
||||
|
||||
t_case_write1_tasks tasks ();
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
$fwrite(fd, "[%0d] crc=%x ", cyc, crc);
|
||||
tasks.big_case(fd, crc[31:0]);
|
||||
$fwrite(fd, "\n");
|
||||
end
|
||||
|
||||
always @ (posedge clk) begin
|
||||
//$write("[%0t] cyc==%0d crc=%x\n",$time, cyc, crc);
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
if (cyc==1) begin
|
||||
crc <= 64'h00000000_00000097;
|
||||
$write("Open obj_dir/t_case_write1_logger.log\n");
|
||||
fd = $fopen("obj_dir/t_case_write1_logger.log", "w");
|
||||
end
|
||||
if (cyc==90) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
[2] crc=0000000000000097 1009 1410
|
||||
[3] crc=000000000000012e 1009 1410
|
||||
[4] crc=000000000000025d 1009 1410
|
||||
[5] crc=00000000000004ba 1009 1410
|
||||
[6] crc=0000000000000974 1009 1410
|
||||
[7] crc=00000000000012e9 1009 1410
|
||||
[8] crc=00000000000025d3 1009 1410
|
||||
[9] crc=0000000000004ba7 1009 1410
|
||||
[10] crc=000000000000974e 1009 1410
|
||||
[11] crc=0000000000012e9d 1009 1410
|
||||
[12] crc=0000000000025d3a 1009 1410
|
||||
[13] crc=000000000004ba74 1009 1410
|
||||
[14] crc=00000000000974e9 1009 1410
|
||||
[15] crc=000000000012e9d3 1009 1410
|
||||
[16] crc=000000000025d3a7 1009 1410
|
||||
[17] crc=00000000004ba74e 1009 1410
|
||||
[18] crc=0000000000974e9d 1009 1410
|
||||
[19] crc=00000000012e9d3a 1009 1410
|
||||
[20] crc=00000000025d3a74 1009 1410
|
||||
[21] crc=0000000004ba74e9 1009 1410
|
||||
[22] crc=000000000974e9d3 1009 23 1303 138 dude 1304
|
||||
[23] crc=0000000012e9d3a7 1009 46 1309 1311 143 1312 dude 1313
|
||||
[24] crc=0000000025d3a74e 1009 172 407 175 408 409 410 1106 dude 1129
|
||||
[25] crc=000000004ba74e9d 1009 223 1014 880 885 1015 1016:0 1007 dude 1017
|
||||
[26] crc=00000000974e9d3a 1009 1229 967 1230 718 dude 1231
|
||||
[27] crc=000000012e9d3a74 1009 1410
|
||||
[28] crc=000000025d3a74e9 1009 58 1369 19 dude 1370
|
||||
[29] crc=00000004ba74e9d3 1009 194 1033 1034 1008 1035 880 dude 1036
|
||||
[30] crc=0000000974e9d3a7 1009 1409:69
|
||||
[31] crc=00000012e9d3a74e 1009 29 1320 137 144 141 138 148 dude 1321
|
||||
[32] crc=00000025d3a74e9d 1009 1383:3a7
|
||||
[33] crc=0000004ba74e9d3a 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[34] crc=000000974e9d3a74 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[35] crc=0000012e9d3a74e9 1009 1228 979 1230 713 dude 1231
|
||||
[36] crc=0000025d3a74e9d3 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[37] crc=000004ba74e9d3a7 1009 1409:69
|
||||
[38] crc=00000974e9d3a74e 1009 29 1320 137 144 141 138 148 dude 1321
|
||||
[39] crc=000012e9d3a74e9d 1009 1383:3a7
|
||||
[40] crc=000025d3a74e9d3a 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[41] crc=00004ba74e9d3a74 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[42] crc=0000974e9d3a74e9 1009 1228 979 1230 713 dude 1231
|
||||
[43] crc=00012e9d3a74e9d3 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[44] crc=00025d3a74e9d3a7 1009 1409:69
|
||||
[45] crc=0004ba74e9d3a74e 1009 29 1320 137 144 141 138 148 dude 1321
|
||||
[46] crc=000974e9d3a74e9d 1009 1383:3a7
|
||||
[47] crc=0012e9d3a74e9d3a 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[48] crc=0025d3a74e9d3a74 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[49] crc=004ba74e9d3a74e9 1009 1228 979 1230 713 dude 1231
|
||||
[50] crc=00974e9d3a74e9d3 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[51] crc=012e9d3a74e9d3a7 1009 1409:69
|
||||
[52] crc=025d3a74e9d3a74e 1009 29 1320 137 144 141 138 148 dude 1321
|
||||
[53] crc=04ba74e9d3a74e9d 1009 1383:3a7
|
||||
[54] crc=0974e9d3a74e9d3a 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[55] crc=12e9d3a74e9d3a74 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[56] crc=25d3a74e9d3a74e9 1009 1228 979 1230 713 dude 1231
|
||||
[57] crc=4ba74e9d3a74e9d3 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[58] crc=974e9d3a74e9d3a7 1009 1409:69
|
||||
[59] crc=2e9d3a74e9d3a74f 1009 29 1320 137 144 141 138 149 dude 1321
|
||||
[60] crc=5d3a74e9d3a74e9e 1009 1383:3a7
|
||||
[61] crc=ba74e9d3a74e9d3d 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[62] crc=74e9d3a74e9d3a7b 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[63] crc=e9d3a74e9d3a74f7 1009 1228 979 1230 713 dude 1231
|
||||
[64] crc=d3a74e9d3a74e9ef 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[65] crc=a74e9d3a74e9d3df 1009 1409:69
|
||||
[66] crc=4e9d3a74e9d3a7bf 1009 29 1320 137 144 141 145 149 dude 1321
|
||||
[67] crc=9d3a74e9d3a74f7e 1009 1383:3a7
|
||||
[68] crc=3a74e9d3a74e9efc 1009 216 1018 882 884 1019 1020 1007 dude 1021
|
||||
[69] crc=74e9d3a74e9d3df9 1009 197 1014 882 883 1015 1016:1 1008 dude 1017
|
||||
[70] crc=e9d3a74e9d3a7bf3 1009 1228 979 1230 713 dude 1231
|
||||
[71] crc=d3a74e9d3a74f7e6 1009 194 1011 1006 1008 1012 880 dude 1013
|
||||
[72] crc=a74e9d3a74e9efcc 1009 1409:69
|
||||
[73] crc=4e9d3a74e9d3df98 1009 29 1320 137 147 149 143 142 dude 1321
|
||||
[74] crc=9d3a74e9d3a7bf30 1009 1383:3a7
|
||||
[75] crc=3a74e9d3a74f7e61 1009 216 1018 882 885 1019 1020 1007 dude 1021
|
||||
[76] crc=74e9d3a74e9efcc3 1009 197 1014 882 884 1015 1016:1 1008 dude 1017
|
||||
[77] crc=e9d3a74e9d3df987 1009 1228 982 1230 713 dude 1231
|
||||
[78] crc=d3a74e9d3a7bf30f 1009 194 1011 1006 1008 1012 881 885 dude 1013
|
||||
[79] crc=a74e9d3a74f7e61f 1009 1409:77
|
||||
[80] crc=4e9d3a74e9efcc3f 1009 30 1320 149 146 146 137 149 dude 1321
|
||||
[81] crc=9d3a74e9d3df987e 1009 1383:3df
|
||||
[82] crc=3a74e9d3a7bf30fc 1009 225 1018 882 885 1019 1020 1008 dude 1021
|
||||
[83] crc=74e9d3a74f7e61f9 1009 218 1014 882 884 1015 1016:1 1008 dude 1017
|
||||
[84] crc=e9d3a74e9efcc3f3 1009 1228 981 1230 708 dude 1231
|
||||
[85] crc=d3a74e9d3df987e6 1009 232 1011 1005 1008 1012 881 883 dude 1013
|
||||
[86] crc=a74e9d3a7bf30fcc 1009 1409:73
|
||||
[87] crc=4e9d3a74f7e61f98 1009 1006 1258 846 1259 1006 1260 833 1261 dude 1262
|
||||
[88] crc=9d3a74e9efcc3f30 1009 124 1320 146 137 149 137 134 dude 1321
|
||||
[89] crc=3a74e9d3df987e61 1009 1383:f98
|
||||
[90] crc=74e9d3a7bf30fcc3 1009 215 1033 1034 1008 1035 879 dude 1036
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
$golden_out ||= "t/$Last_Self->{name}.out";
|
||||
|
||||
compile (
|
||||
v_flags2 => [$Last_Self->{v3}?"--stats --O3 -x-assign 0":""],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(files_identical("obj_dir/$Last_Self->{name}_logger.log", $golden_out));
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,44 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
`include "verilated.v"
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
reg [63:0] crc;
|
||||
`verilator_file_descriptor fd;
|
||||
|
||||
t_case_write2_tasks tasks ();
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
$fwrite(fd, "[%0d] crc=%x ", cyc, crc);
|
||||
tasks.big_case(fd, crc[31:0]);
|
||||
$fwrite(fd, "\n");
|
||||
end
|
||||
|
||||
always @ (posedge clk) begin
|
||||
//$write("[%0t] cyc==%0d crc=%x\n",$time, cyc, crc);
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
if (cyc==1) begin
|
||||
crc <= 64'h00000000_00000097;
|
||||
$write("Open obj_dir/t_case_write2_logger.log\n");
|
||||
fd = $fopen("obj_dir/t_case_write2_logger.log", "w");
|
||||
end
|
||||
if (cyc==90) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
if ($Last_Self->{v3}) {
|
||||
compile ();
|
||||
|
||||
my $outfile = "obj_dir/V".$Last_Self->{name}.".cpp";
|
||||
my @oldstats = stat($outfile);
|
||||
print "Old mtime=",$oldstats[9],"\n";
|
||||
$oldstats[9] or $Last_Self->error("No output file found: $outfile\n");
|
||||
|
||||
sleep (1); # Or else it might take < 1 second to compile and see no diff.
|
||||
|
||||
compile ();
|
||||
|
||||
my @newstats = stat($outfile);
|
||||
print "New mtime=",$newstats[9],"\n";
|
||||
|
||||
($oldstats[9] == $newstats[9])
|
||||
or $Last_Self->error("--skip-identical was ignored -- recompiled\n");
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,9 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
endmodule
|
||||
@@ -13,7 +13,7 @@ compile (
|
||||
fails=>$Last_Self->{v3},
|
||||
verilator_flags=> [qw(-sp -Werror-WIDTH)],
|
||||
expect=>
|
||||
'%Error: t/t_flag_werror.v:\d+: Operator ASSIGNW expects 4 bits on the Assign RHS, but Assign RHS.s CONST generates 6 bits.
|
||||
'%Error-WIDTH: t/t_flag_werror.v:\d+: Operator ASSIGNW expects 4 bits on the Assign RHS, but Assign RHS.s CONST generates 6 bits.
|
||||
%Error: Exiting due to',
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2004 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
expect=>
|
||||
'[10] hello
|
||||
[20] world
|
||||
',
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,81 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2006 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
integer j;
|
||||
integer hit_count;
|
||||
reg [63:0] cam_lookup_hit_vector;
|
||||
|
||||
strings strings ();
|
||||
|
||||
task show;
|
||||
input [8*8-1:0] str;
|
||||
reg [7:0] char;
|
||||
integer loc;
|
||||
begin
|
||||
$write("[%0t] ",$time);
|
||||
strings.stringStart(8*8-1);
|
||||
for (char = strings.stringByte(str); !strings.isNull(char); char = strings.stringByte(str)) begin
|
||||
$write("%c",char);
|
||||
end
|
||||
$write("\n");
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
integer cyc; initial cyc=1;
|
||||
always @ (posedge clk) begin
|
||||
if (cyc!=0) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==1) begin
|
||||
show("hello\000xx");
|
||||
end
|
||||
if (cyc==2) begin
|
||||
show("world\000xx");
|
||||
end
|
||||
if (cyc==4) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module strings;
|
||||
// **NOT** reentrant, just a test!
|
||||
integer index;
|
||||
task stringStart;
|
||||
input [31:0] bits;
|
||||
begin
|
||||
index = (bits-1)/8;
|
||||
end
|
||||
endtask
|
||||
|
||||
function isNull;
|
||||
input [7:0] chr;
|
||||
isNull = (chr == 8'h0);
|
||||
endfunction
|
||||
|
||||
function [7:0] stringByte;
|
||||
input [8*8-1:0] str;
|
||||
begin
|
||||
if (index<=0) stringByte=8'h0;
|
||||
else stringByte = str[index*8 +: 8];
|
||||
index = index - 1;
|
||||
end
|
||||
endfunction
|
||||
endmodule
|
||||
|
||||
// Local Variables:
|
||||
// compile-command: "./vlint __FILE__"
|
||||
// End:
|
||||
+35
-1
@@ -1,4 +1,4 @@
|
||||
// $Id:$
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
@@ -22,6 +22,15 @@ module t;
|
||||
if (global != 32'h17) $stop;
|
||||
nop(32'h11);
|
||||
|
||||
global = 32'h00000001;
|
||||
flipupperbit(global,4'd4);
|
||||
flipupperbit(global,4'd12);
|
||||
if (global !== 32'h10100001) $stop;
|
||||
|
||||
if (nil_func(32'h12,32'h12) != 32'h24) $stop;
|
||||
nil_task(32'h012,32'h112,global);
|
||||
if (global !== 32'h124) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
@@ -70,4 +79,29 @@ module t;
|
||||
end
|
||||
endtask
|
||||
|
||||
task flipupperbit;
|
||||
inout [31:0] vector;
|
||||
input [3:0] bitnum;
|
||||
reg [4:0] bitnum2;
|
||||
begin
|
||||
bitnum2 = {1'b1, bitnum}; // A little math to test constant propagation
|
||||
vector[bitnum2] = vector[bitnum2] ^ 1'b1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task nil_task;
|
||||
input [31:0] a;
|
||||
input [31:0] b;
|
||||
output [31:0] q;
|
||||
// verilator no_inline_task
|
||||
q = nil_func(a, b);
|
||||
endtask
|
||||
|
||||
function [31:0] nil_func;
|
||||
input [31:0] fa;
|
||||
input [31:0] fb;
|
||||
// verilator no_inline_task
|
||||
nil_func = fa + fb;
|
||||
endfunction
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id:$
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
@@ -13,7 +13,7 @@ compile (
|
||||
'%Error: t/t_func_bad.v:\d+: Too few arguments in function call
|
||||
%Error: t/t_func_bad.v:\d+: Too many arguments in function call
|
||||
%Error: t/t_func_bad.v:\d+: Too few arguments in function call
|
||||
%Error: t/t_func_bad.v:\d+: Unsupported: Task output pin connected to non-variable
|
||||
%Error-TASKNSVAR: t/t_func_bad.v:\d+: Unsupported: Task output pin connected to non-variable
|
||||
%Error: t/t_func_bad.v:\d+: Outputs not allowed in function declarations
|
||||
%Error: Exiting due to',
|
||||
);
|
||||
|
||||
@@ -37,6 +37,11 @@ module t (/*AUTOARG*/
|
||||
if (ma0.mb0.mc0.getP3(1'b0) !== 32'h0) $stop;
|
||||
if (ma0.mb0.mc1.getP3(1'b0) !== 32'h1) $stop;
|
||||
end
|
||||
if (cyc==5) begin
|
||||
ma0. checkName(ma0. getName(1'b0));
|
||||
ma0.mb0. checkName(ma0.mb0. getName(1'b0));
|
||||
ma0.mb0.mc0.checkName(ma0.mb0.mc0.getName(1'b0));
|
||||
end
|
||||
if (cyc==9) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
@@ -72,7 +77,9 @@ module ma ();
|
||||
`INLINE_MODULE
|
||||
|
||||
mb #(0) mb0 ();
|
||||
reg [31:0] gName; initial gName = "ma ";
|
||||
function [31:0] getName; input fake; getName = "ma "; endfunction
|
||||
task checkName; input [31:0] name; if (name !== "ma ") $stop; endtask
|
||||
|
||||
initial begin
|
||||
if (ma.getName(1'b0) !== "ma ") $stop;
|
||||
@@ -89,13 +96,23 @@ module mb ();
|
||||
mc #(P2,1) mc1 ();
|
||||
global_mod #(32'hf33d) global_cell2 ();
|
||||
|
||||
reg [31:0] gName; initial gName = "mb ";
|
||||
function [31:0] getName; input fake; getName = "mb "; endfunction
|
||||
function [31:0] getP2 ; input fake; getP2 = P2; endfunction
|
||||
task checkName; input [31:0] name; if (name !== "mb ") $stop; endtask
|
||||
|
||||
initial begin
|
||||
if (ma.getName(1'b0) !== "ma ") $stop;
|
||||
if (getName(1'b0) !== "mb ") $stop;
|
||||
`ifndef verilator #1; `endif
|
||||
if (ma. getName(1'b0) !== "ma ") $stop;
|
||||
if ( getName(1'b0) !== "mb ") $stop;
|
||||
if (mc1.getName(1'b0) !== "mc ") $stop;
|
||||
|
||||
ma. checkName (ma. gName);
|
||||
/**/checkName ( gName);
|
||||
mc1.checkName (mc1.gName);
|
||||
ma. checkName (ma. getName(1'b0));
|
||||
/**/checkName ( getName(1'b0));
|
||||
mc1.checkName (mc1.getName(1'b0));
|
||||
end
|
||||
endmodule
|
||||
|
||||
@@ -104,12 +121,21 @@ module mc ();
|
||||
parameter P2 = 0;
|
||||
parameter P3 = 0;
|
||||
|
||||
reg [31:0] gName; initial gName = "mc ";
|
||||
function [31:0] getName; input fake; getName = "mc "; endfunction
|
||||
function [31:0] getP3 ; input fake; getP3 = P3; endfunction
|
||||
task checkName; input [31:0] name; if (name !== "mc ") $stop; endtask
|
||||
|
||||
initial begin
|
||||
`ifndef verilator #1; `endif
|
||||
if (ma.getName(1'b0) !== "ma ") $stop;
|
||||
if (mb.getName(1'b0) !== "mb ") $stop;
|
||||
if (mc.getName(1'b0) !== "mc ") $stop;
|
||||
ma.checkName (ma.gName);
|
||||
mb.checkName (mb.gName);
|
||||
mc.checkName (mc.gName);
|
||||
ma.checkName (ma.getName(1'b0));
|
||||
mb.checkName (mb.getName(1'b0));
|
||||
mc.checkName (mc.getName(1'b0));
|
||||
end
|
||||
endmodule
|
||||
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
compile (
|
||||
v_flags2 => ['-v', 't/t_func_lib_sub.v'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,13 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2003-2006 by Wilson Snyder.
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
compile (
|
||||
);
|
||||
# No execute
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,101 @@
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2003-2006 by Wilson Snyder.
|
||||
|
||||
`define zednkw 200
|
||||
|
||||
module BreadAddrDP (zfghtn, cjtmau, knquim, kqxkkr);
|
||||
input zfghtn;
|
||||
input [4:0] cjtmau;
|
||||
input vipmpg;
|
||||
input [7:0] knquim;
|
||||
input [7:0] kqxkkr;
|
||||
|
||||
reg covfok;
|
||||
|
||||
reg [15:0] xwieqw;
|
||||
reg [2:0] ofnjjt;
|
||||
|
||||
reg [37:0] hdsejo[0:1];
|
||||
|
||||
reg wxxzgd, tceppr, ratebp, fjizkr, iwwrnq;
|
||||
reg vrqrih, ryyjxy;
|
||||
reg fgzsox;
|
||||
|
||||
wire xdjikl = ~wxxzgd & ~tceppr & ~ratebp & fjizkr;
|
||||
wire iytyol = ~wxxzgd & ~tceppr & ratebp & ~fjizkr & ~xwieqw[10];
|
||||
wire dywooz = ~wxxzgd & ~tceppr & ratebp & ~fjizkr & xwieqw[10];
|
||||
wire qnpfus = ~wxxzgd & ~tceppr & ratebp & fjizkr;
|
||||
wire fqlkrg = ~wxxzgd & tceppr & ~ratebp & ~fjizkr;
|
||||
|
||||
wire ktsveg = hdsejo[0][6] | (hdsejo[0][37:34] == 4'h1);
|
||||
wire smxixw = vrqrih | (ryyjxy & ktsveg);
|
||||
|
||||
wire [7:0] grvsrs, kyxrft, uxhkka;
|
||||
|
||||
wire [7:0] eianuv = 8'h01 << ofnjjt;
|
||||
wire [7:0] jvpnxn = {8{qnpfus}} & eianuv;
|
||||
wire [7:0] zlnzlj = {8{fqlkrg}} & eianuv;
|
||||
wire [7:0] nahzat = {8{iytyol}} & eianuv;
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for (i=0;i<8;i=i+1)
|
||||
begin : dnlpyw
|
||||
DecCountReg4 bzpytc (zfghtn, fgzsox, zlnzlj[i],
|
||||
knquim[3:0], covfok, grvsrs[i]);
|
||||
DecCountReg4 oghukp (zfghtn, fgzsox, zlnzlj[i],
|
||||
knquim[7:4], covfok, kyxrft[i]);
|
||||
DecCountReg4 ttvjoo (zfghtn, fgzsox, nahzat[i],
|
||||
kqxkkr[3:0], covfok, uxhkka[i]);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
module DecCountReg4 (clk, fgzsox, fckiyr, uezcjy, covfok, juvlsh);
|
||||
input clk, fgzsox, fckiyr, covfok;
|
||||
input [3:0] uezcjy;
|
||||
output juvlsh;
|
||||
|
||||
task Xinit;
|
||||
begin
|
||||
`ifdef TEST_HARNESS
|
||||
khgawe = 1'b0;
|
||||
`endif
|
||||
end
|
||||
endtask
|
||||
function X;
|
||||
input vrdejo;
|
||||
begin
|
||||
`ifdef TEST_HARNESS
|
||||
if ((vrdejo & ~vrdejo) !== 1'h0) khgawe = 1'b1;
|
||||
`endif
|
||||
X = vrdejo;
|
||||
end
|
||||
endfunction
|
||||
task Xcheck;
|
||||
input vzpwwy;
|
||||
begin
|
||||
end
|
||||
endtask
|
||||
|
||||
reg [3:0] udbvtl;
|
||||
|
||||
assign juvlsh = |udbvtl;
|
||||
wire [3:0] mppedc = {4{fgzsox}} & (fckiyr ? uezcjy : (udbvtl - 4'h1));
|
||||
|
||||
wire qqibou = ((juvlsh | fckiyr) & covfok) | ~fgzsox;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
Xinit;
|
||||
if (X(qqibou))
|
||||
udbvtl <= #`zednkw mppedc;
|
||||
|
||||
Xcheck(fgzsox);
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -103,8 +103,8 @@ module tpub (
|
||||
if (24'h11bca != got_long) $stop;
|
||||
$c("{ uint64_t qq; publicGetQuad(qq); got_quad=qq; }");
|
||||
if (60'haaaa_bbbb_cccc != got_quad) $stop;
|
||||
//Unsupported: $c("publicGetWide(got_wide);");
|
||||
//Unsupported: if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop;
|
||||
$c("{ WData gw[3]; publicGetWide(gw); VL_ASSIGN_W(72,got_wide,gw); }");
|
||||
if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop;
|
||||
//Below doesn't work, because we're calling it inside the loop that sets var_flop
|
||||
// if (12'h321 != var_flop) $stop;
|
||||
end
|
||||
@@ -154,7 +154,7 @@ module tpub (
|
||||
endtask
|
||||
|
||||
task publicSetWide;
|
||||
// Can't be public, as no wide return types in C++
|
||||
// verilator public
|
||||
input [71:0] in_wide;
|
||||
var_wide = in_wide;
|
||||
endtask
|
||||
@@ -178,7 +178,7 @@ module tpub (
|
||||
endtask
|
||||
|
||||
task publicGetWide;
|
||||
// Can't be public, as no wide return types in C++
|
||||
// verilator public
|
||||
output [71:0] out_wide;
|
||||
out_wide = var_wide;
|
||||
endtask
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// $Id:$
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
@@ -11,6 +11,7 @@ module t (/*AUTOARG*/
|
||||
|
||||
input clk;
|
||||
integer cyc; initial cyc=1;
|
||||
parameter ONE = 1;
|
||||
|
||||
wire [17:10] bitout;
|
||||
reg [7:0] allbits;
|
||||
@@ -18,6 +19,8 @@ module t (/*AUTOARG*/
|
||||
|
||||
sub sub [7:0] (allbits, onebit, bitout);
|
||||
|
||||
integer x;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
//$write("%x\n", bitout);
|
||||
if (cyc!=0) begin
|
||||
@@ -33,6 +36,14 @@ module t (/*AUTOARG*/
|
||||
end
|
||||
if (cyc==3) begin
|
||||
if (bitout !== 8'h41) $stop;
|
||||
`ifdef verilator // Hacky array subscripting
|
||||
if (sub__0.bitout !== 1'b1) $stop;
|
||||
if (sub__1.bitout !== 1'b0) $stop;
|
||||
`else
|
||||
if (sub[0].bitout !== 1'b1) $stop;
|
||||
if (sub[1].bitout !== 1'b0) $stop;
|
||||
if (sub[ONE].bitout !== 1'b0) $stop;
|
||||
`endif
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
@@ -40,7 +51,14 @@ module t (/*AUTOARG*/
|
||||
end
|
||||
endmodule
|
||||
|
||||
`ifdef USE_INLINE
|
||||
`define INLINE_MODULE /*verilator inline_module*/
|
||||
`else
|
||||
`define INLINE_MODULE /*verilator public_module*/
|
||||
`endif
|
||||
|
||||
module sub (input [7:0] allbits, input [1:0] onebit, output bitout);
|
||||
`INLINE_MODULE
|
||||
wire bitout = (^ onebit) ^ (^ allbits);
|
||||
endmodule
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
top_filename("t/t_inst_array.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+NOUSE_INLINE',],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -1,13 +1,16 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id:$
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2005 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
top_filename("t/t_inst_array.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+USE_INLINE',],
|
||||
);
|
||||
|
||||
execute (
|
||||
@@ -39,7 +39,6 @@ module t (/*AUTOARG*/
|
||||
a <= 256'h0e17c88f3d5fe51a982646c8e2bd68c3e236ddfddddbdad20a48e039c9f395b8;
|
||||
divisor <= 61'h1238123771;
|
||||
a[60] <= 1'b0; divisor[60] <= 1'b0; // Unsigned
|
||||
//$display("FIX");
|
||||
if (qq!==61'h00000403ad81c0da) $stop;
|
||||
if (rq!==61'h00000000000090ec) $stop;
|
||||
if (qqs!==61'h00000403ad81c0da) $stop;
|
||||
|
||||
@@ -75,6 +75,7 @@ module t (/*AUTOARG*/
|
||||
endfunction
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
wire [31:0] ucyc = cyc;
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
$write("%x %x %x %x %x %x %x\n", cyc, sr,srs,sl,sls, b_s,b_us);
|
||||
@@ -88,9 +89,24 @@ module t (/*AUTOARG*/
|
||||
end
|
||||
2: begin
|
||||
a <= 16'sh8b1b; b <= 5'sh1e; // shift AMOUNT is really unsigned
|
||||
if (ucyc / 1 != 32'd2) $stop;
|
||||
if (ucyc / 2 != 32'd1) $stop;
|
||||
if (ucyc * 1 != 32'd2) $stop;
|
||||
if (ucyc * 2 != 32'd4) $stop;
|
||||
if (ucyc * 3 != 32'd6) $stop;
|
||||
if (cyc * 32'sd1 != 32'sd2) $stop;
|
||||
if (cyc * 32'sd2 != 32'sd4) $stop;
|
||||
if (cyc * 32'sd3 != 32'sd6) $stop;
|
||||
end
|
||||
3: begin
|
||||
a <= 16'sh0048; b <= 5'sh1f;
|
||||
if (ucyc * 1 != 32'd3) $stop;
|
||||
if (ucyc * 2 != 32'd6) $stop;
|
||||
if (ucyc * 3 != 32'd9) $stop;
|
||||
if (ucyc * 4 != 32'd12) $stop;
|
||||
if (cyc * 32'sd1 != 32'sd3) $stop;
|
||||
if (cyc * 32'sd2 != 32'sd6) $stop;
|
||||
if (cyc * 32'sd3 != 32'sd9) $stop;
|
||||
end
|
||||
4: begin
|
||||
a <= 16'sh4154; b <= 5'sh02;
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
// $Id:$
|
||||
// $Id$
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Use this file as a template for submitting bugs, etc.
|
||||
// This module takes a single clock input, and should either
|
||||
// $write("*-* All Finished *-*\n");
|
||||
// $finish
|
||||
// on success, or $stop.
|
||||
//
|
||||
// **If you do not wish for your code to be released to the public
|
||||
// please note it here**
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2005 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
$golden_out ||= "t/$Last_Self->{name}.out";
|
||||
my $stdout_filename = "obj_dir/$Last_Self->{name}__test.vpp";
|
||||
|
||||
if (!$Last_Self->{v3}) {
|
||||
@@ -17,8 +18,8 @@ if (!$Last_Self->{v3}) {
|
||||
verilator_make_gcc=>0,
|
||||
stdout_filename => $stdout_filename,
|
||||
);
|
||||
ok(preproc_check("t/$Last_Self->{name}.v", $stdout_filename)
|
||||
&& files_identical($stdout_filename, "t/$Last_Self->{name}.out"));
|
||||
ok(preproc_check($Last_Self->{top_filename}, $stdout_filename)
|
||||
&& files_identical($stdout_filename, $golden_out));
|
||||
}
|
||||
|
||||
sub preproc_check {
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; }
|
||||
# $Id$
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2006-2006 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# General Public License or the Perl Artistic License.
|
||||
|
||||
top_filename("obj_dir/$Last_Self->{name}.v");
|
||||
|
||||
# Rather then having to maintain a new .v and .out, simply add returns
|
||||
# to all lines of the existing t_preproc test.
|
||||
|
||||
$golden_out ||= "obj_dir/$Last_Self->{name}.out";
|
||||
|
||||
{
|
||||
my $wholefile = file_contents("t/t_preproc.v");
|
||||
$wholefile =~ s/\n/\r\n/og;
|
||||
write_wholefile("obj_dir/$Last_Self->{name}.v", $wholefile);
|
||||
}
|
||||
{
|
||||
my $wholefile = file_contents("t/t_preproc.out");
|
||||
$wholefile =~ s!t/t_preproc.v!obj_dir/t_preproc_dos.v!og; # Fix `line's
|
||||
write_wholefile($golden_out, $wholefile);
|
||||
}
|
||||
|
||||
require 't/t_preproc.pl';
|
||||
|
||||
1;
|
||||
@@ -141,10 +141,9 @@ module mc (output reg [31:0] out);
|
||||
end
|
||||
|
||||
// Can we look from the top module name down?
|
||||
wire reach_up_clk = t.clk;
|
||||
wire [31:0] reach_up_cyc = t.cyc;
|
||||
|
||||
always @ (posedge reach_up_clk) begin
|
||||
always @ (posedge t.clk) begin
|
||||
//$write("[%0t] %m: Got reachup, cyc=%0d\n", $time, reach_up_cyc);
|
||||
if (reach_up_cyc==2) begin
|
||||
if (global_cell.global != 32'hf00d) $stop;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user