Compare commits

...
57 Commits
Author SHA1 Message Date
Wilson Snyder 44b6c371db Version bump 2010-05-01 14:18:56 -04:00
Wilson Snyder 00970be996 Fix bit reductions on multi-packed dimensions, bug227/patch0004. 2010-04-22 09:40:53 -04:00
Wilson Snyder aca4ab015f Fix removing if assigned to unused var, bug248. 2010-04-19 19:38:22 -04:00
Wilson Snyder 68b63d5783 Increase unrollStmts, bug247, due to unreleased change. 2010-04-19 13:59:15 -04:00
Wilson Snyder 4b499f2bb1 Commentary, bug246 2010-04-19 09:44:48 -04:00
Wilson Snyder ed17581f92 Fix loop unroller out of memory; change --unroll-stmts. 2010-04-17 08:01:22 -04:00
Wilson Snyder a46c4ec912 Accelerate and reduce memory usage of V3Expand 2010-04-15 21:05:21 -04:00
Wilson Snyder 930cbeec17 Internals: Create user#Inc routines 2010-04-15 20:56:54 -04:00
Wilson Snyder 10689ffaba Fix carrage return purging, broke in pre-release only 2010-04-10 09:11:52 -04:00
Wilson Snyder 29b0ea0af9 Debug: Allow --debugi-V3PreShell to turn on flex debug 2010-04-10 08:20:28 -04:00
Wilson Snyder 57d00946be Fix MSVC compile issues 2010-04-10 06:46:24 -04:00
Wilson Snyder ef51de72c9 Fix word size to match uint64_t on -m64 systems, bug238. 2010-04-09 21:51:15 -04:00
Wilson Snyder c807bf1e0e Internals: Fix useless rule and flex warning 2010-04-09 21:06:16 -04:00
Byron Bradley 2525b3fb05 Fix bit reductions on multi-packed dimensions, bug227
Signed-off-by: Wilson Snyder <[email protected]>
2010-04-09 21:05:46 -04:00
Byron Bradley 9163ce0f6e Internals: modify AstVar::dimensions() to return a pair, bug227
Signed-off-by: Wilson Snyder <[email protected]>
2010-04-09 20:43:25 -04:00
Byron Bradley d776638f53 Internals: Mark packed dimensions in arrays, bug227
Signed-off-by: Wilson Snyder <[email protected]>
2010-04-09 20:40:41 -04:00
Wilson Snyder 83fff41312 Fix installing data files as non-executable, bug168. 2010-04-09 20:21:00 -04:00
Wilson Snyder dd7d9bc95b Fix some spelling mistakes in verilator executable & manpage, bug240 2010-04-09 19:45:46 -04:00
Wilson Snyder f8eabbc100 From Verilog-Perl: Fix parsing single files > 2GB. 2010-04-06 20:20:44 -04:00
Wilson Snyder 7ed18ac62d Internals: Track Verilog-Perl 3.240 parser changes 2010-04-06 18:55:54 -04:00
Wilson Snyder 3ac707c645 Tests: Fix vpi_user addition failure 2010-04-06 18:43:24 -04:00
Wilson Snyder 8e9a1e0bf3 Add standard vpi_user.h file 2010-04-05 20:08:13 -04:00
Wilson Snyder bc31b5d27a Commentary 2010-04-05 20:08:07 -04:00
Wilson Snyder 936738b750 Add /*verilator public_flat_rw*/ for timing-specific public access. 2010-04-05 20:01:17 -04:00
Wilson Snyder 7c3048ab9c Report errors when extra underscores used in meta-comments. 2010-03-24 22:08:59 -04:00
Wilson Snyder 64719f3ace Makefile: Add local install-cadtools rule 2010-03-24 16:09:29 -04:00
Wilson Snyder a11a5f04d5 Default Verilated::debug() to off 2010-03-23 20:31:22 -04:00
Wilson Snyder eb5c035dde Internals: Fix some missing branch prediction 2010-03-22 19:07:03 -04:00
Wilson Snyder c3887efbf3 Internals: Remove dead code 2010-03-22 19:06:30 -04:00
Wilson Snyder 495585830d Fix trace files with empty modules crashing some viewers. 2010-03-22 18:38:24 -04:00
Wilson Snyder 6715cb9880 Improve error handling on slices of arrays, bug226. 2010-03-20 21:29:16 -04:00
Wilson Snyder 41b167d23c Fix DPI display bogus error 2010-03-20 07:13:52 -04:00
Wilson Snyder 7ea8b54210 Tests: Support atsim and cleanup verilator-only tests 2010-03-18 12:03:08 -04:00
Wilson Snyder bcea39a858 Fix "make install" with configure outside srcdir. 2010-03-17 20:20:40 -04:00
Wilson Snyder fb560abac4 Commentary 2010-03-17 19:57:38 -04:00
Wilson Snyder ba93a08b40 Support runtime access to public signal names 2010-03-17 08:22:49 -04:00
Wilson Snyder 2be6b3481c Pre-version 2010-03-17 08:05:07 -04:00
Wilson Snyder 6ef44a6fbb Version bump 2010-03-17 07:59:12 -04:00
Wilson Snyder 2afe40a0b5 verilated: Add debug pointers to scopeDump() 2010-03-16 19:28:29 -04:00
Wilson Snyder e57d004718 Fix clock-gates with non-AND complex logic, bug220. 2010-03-16 18:50:26 -04:00
Wilson Snyder d780d0aabb Fix flushing VCD buffers on . 2010-03-12 20:00:08 -05:00
Wilson Snyder 0f9e3f0767 Cleanup flex compiles again - remove yyleng from preproc.cpp 2010-03-05 12:02:56 -05:00
Wilson Snyder 381972c923 Fix Mac OS-X compile issues, bug217. 2010-02-26 19:50:44 -05:00
Wilson Snyder 845d7ad718 Commentary 2010-02-26 18:57:34 -05:00
Wilson Snyder f1b6c0c559 Support "`default_nettype none|wire". 2010-02-23 09:27:16 -05:00
Wilson Snyder e39eddf3fe Fix parametrized defines with empty arguments. 2010-02-21 07:20:39 -05:00
Wilson Snyder 72218fb475 Defines with // comments still need continuation - spec ambiguity 2010-02-18 20:57:46 -05:00
Wilson Snyder 9a3e497c22 Fix specparam defaults 2010-02-18 08:43:16 -05:00
Wilson Snyder 85d510cd48 Fix vl_mc_scan_plusarg returning stack based pointer 2010-02-16 11:54:39 -05:00
Wilson Snyder 786c65ea5f Makefile: reconfigure when src/*.in changes 2010-02-15 07:34:55 -05:00
Wilson Snyder 10e6f61125 Commentary 2010-02-14 19:23:29 -05:00
Wilson Snyder cdd06e7236 Support "break", "continue", "return". 2010-02-14 10:01:21 -05:00
Wilson Snyder 48603c0ee2 Commentary 2010-02-10 08:50:41 -05:00
Wilson Snyder 63f30492be Skip SystemC tests if not installed. 2010-02-08 20:51:43 -05:00
Wilson Snyder 0e8772290d Fix make uninstall, bug216 2010-02-08 09:28:40 -05:00
Wilson Snyder 6cfc5798b0 Tests: Move sc_main.cpp as there's more sc than sp users 2010-02-08 08:10:07 -05:00
Wilson Snyder 6be275f5d4 Post release version bump 2010-02-08 06:27:07 -05:00
146 changed files with 4521 additions and 675 deletions
+44
View File
@@ -3,6 +3,50 @@ 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.802 2010/05/01
*** Support runtime access to public signal names.
*** Add /*verilator public_flat_rw*/ for timing-specific public access.
*** Fix word size to match uint64_t on -m64 systems, bug238. [Joe Eiler]
**** Improve error handling on slices of arrays, bug226. [by Byron Bradley]
**** Report errors when extra underscores used in meta-comments.
**** Fix bit reductions on multi-packed dimensions, bug227. [by Byron Bradley]
**** Fix removing $fscanf if assigned to unused var, bug248. [Ashutosh Das]
**** Fix "make install" with configure outside srcdir. [Stefan Wallentowitz]
**** Fix loop unroller out of memory; change --unroll-stmts. [Ashutosh Das]
**** Fix trace files with empty modules crashing some viewers.
**** Fix parsing single files > 2GB. [Jeffrey Short]
**** Fix installing data files as non-executable, bug168. [by Ahmed El-Mahmoudy]
* Verilator 3.801 2010/03/17
*** Support "break", "continue", "return".
*** Support "`default_nettype none|wire". [Dominic Plunkett]
**** Skip SystemC tests if not installed. [Iztok Jeras]
**** Fix clock-gates with non-AND complex logic, bug220. [Ashutosh Das]
**** Fix flushing VCD buffers on $stop. [Ashutosh Das]
**** Fix Mac OS-X compile issues, bug217. [Joshua Wise, Trevor Williams]
**** Fix make uninstall, bug216. [Iztok Jeras]
**** Fix parametrized defines with empty arguments.
* Verilator 3.800 2010/02/07
Application visible changes:
+95 -39
View File
@@ -134,10 +134,9 @@ INST_PROJ_FILES = \
bin/verilator \
bin/verilator_includer \
bin/verilator_profcfunc \
include/verilated.[chv]* \
include/verilated.mk \
include/verilatedos.[chv]* \
include/verilated_*.[chv]* \
include/*.[chv]* \
include/vltstd/*.[chv]* \
INST_PROJ_BIN_FILES = \
verilator_bin \
@@ -145,8 +144,6 @@ INST_PROJ_BIN_FILES = \
DISTFILES := $(DISTFILES_INC)
VL_INST_MAN_FILES = verilator.1
ifeq ($(OBJCACHE_JOBS),)
ifneq ($(OBJCACHE_HOSTS),)
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
@@ -238,11 +235,29 @@ internals.txt: internals.pod
-rm -f $@
$(POD2TEXT) --loose $< > $@
# See uninstall also
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg \
verilator_includer verilator_profcfunc
# Some scripts go into both the search path and pkgdatadir,
# so they can be found by the user, and under $VERILATOR_ROOT.
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_MAN_FILES = verilator.1
VL_INST_INC_BLDDIR_FILES = \
include/verilated.mk \
# Files under srcdir, instead of build time
VL_INST_INC_SRCDIR_FILES = \
include/*.[chv]* \
include/vltstd/*.[chv]* \
VL_INST_DATA_SRCDIR_FILES = \
test_v/*.[chv]* \
test_c/*.[chv]* test_c/Makefile test_c/Makefile_obj \
test_sc/*.[chv]* test_sc/Makefile test_sc/Makefile_obj \
test_sp/*.[chv]* test_sp/Makefile test_sp/Makefile_obj \
installbin:
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
@@ -255,26 +270,52 @@ installbin:
installman: $(VL_INST_MAN_FILES)
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(mandir)/man1
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(mandir)/man1/$$p; \
$(INSTALL_DATA) $$p $(DESTDIR)$(mandir)/man1/$$p; \
done
installdata:
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/include/vltstd $(DESTDIR)$(pkgdatadir)/examples
cp -r include/*.* $(DESTDIR)$(pkgdatadir)/include
cp -r ${srcdir}/include/*.* $(DESTDIR)$(pkgdatadir)/include
cp -r ${srcdir}/include/vltstd/*.* $(DESTDIR)$(pkgdatadir)/include/vltstd
cp -r ${srcdir}/test_c $(DESTDIR)$(pkgdatadir)/examples
cp -r ${srcdir}/test_sc $(DESTDIR)$(pkgdatadir)/examples
cp -r ${srcdir}/test_sp $(DESTDIR)$(pkgdatadir)/examples
cp -r ${srcdir}/test_v $(DESTDIR)$(pkgdatadir)/examples
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/include/vltstd
for p in $(VL_INST_INC_BLDDIR_FILES) ; do \
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
done
cd $(srcdir) \
; for p in $(VL_INST_INC_SRCDIR_FILES) ; do \
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
done
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/test_c
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/test_sc
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/test_sp
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/test_v
cd $(srcdir) \
; for p in $(VL_INST_DATA_SRCDIR_FILES) ; do \
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/examples/$$p; \
done
# We don't trust rm -rf, so rmdir instead as it will fail if user put in other files
uninstall:
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_BIN_FILES)
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_BIN_FILES)
-cd $(DESTDIR)$(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_BLDDIR_FILES)
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_SRCDIR_FILES)
-cd $(DESTDIR)$(pkgdatadir)/examples && rm -f $(VL_INST_DATA_SRCDIR_FILES)
-rmdir $(DESTDIR)$(pkgdatadir)/bin
-rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd
-rmdir $(DESTDIR)$(pkgdatadir)/include
-rmdir $(DESTDIR)$(pkgdatadir)/examples/test_c
-rmdir $(DESTDIR)$(pkgdatadir)/examples/test_sc
-rmdir $(DESTDIR)$(pkgdatadir)/examples/test_sp
-rmdir $(DESTDIR)$(pkgdatadir)/examples/test_v
-rmdir $(DESTDIR)$(pkgdatadir)/examples
-rmdir $(DESTDIR)$(pkgdatadir)
VL_INST_DATA_FILES = verilator.1
install: all_nomsg installbin installman installdata install-msg
install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DIRPROJECT := $(shell project_dir --project)
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
INST_PROJ_CVS = cp_if_cvs_diff
install-project: dist
@@ -282,7 +323,7 @@ install-project: dist
strip verilator_bin*
$(MAKE) install-project-quick
for p in verilator.1 ; do \
$(INSTALL_PROGRAM) -m 0666 $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
$(INSTALL_DATA) $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(DIRPROJECT)/hw/utils/verilator/verilator.tgz
rm $(DISTNAME).tgz
@@ -299,6 +340,34 @@ endif
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p-$(DIRPROJECT_ARCH); \
done
install-cadtools: dist
@echo "Install-project to $(CAD_DIR)"
strip verilator_bin*
$(MAKE) install-cadtools-quick
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/man/man1
for p in verilator.1 ; do \
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
rm $(DISTNAME).tgz
install-cadtools-quick:
ifeq ($(CFG_WITH_DEFENV),yes)
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
false
endif
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/include/vltstd
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/bin
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
# VERILATOR_AUTHOR_SITE
endif
ftp: info
@@ -306,33 +375,19 @@ ftp: info
install-msg:
@echo "Installed!"
@echo
@echo "Add to your startup file (for bash or csh, as appropriate):"
@echo " export VERILATOR_ROOT="`pwd`
@echo " setenv VERILATOR_ROOT "`pwd` " ; export VERILATOR_ROOT "
@echo
@echo "See 'verilator.txt' for documentation."
@echo "Fod documentation see 'man verilator' or 'verilator --help'"
@echo "For forums and to report bugs see http://www.veripool.org/verilator"
@echo
uninstall:
-cd $(DESTDIR)$(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_BIN_FILES)
-rm -f $(DESTDIR)$(pkgdatadir)
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
# autoheader might not change config_build.h.in, so touch a stamp file.
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in ${srcdir}/*/*/*.in \
*.in */*.in */*.in
${srcdir}/config_build.h.in: stamp-h.in
${srcdir}/stamp-h.in: configure.ac $(wildcard $(IN_WILD))
# autoheader might not change config_build.h.in, so touch it
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
cd ${srcdir} && autoheader
echo timestamp > ${srcdir}/stamp-h.in
config_build.h: stamp-h
stamp-h: config_build.h.in config.status
./config.status
Makefile: Makefile.in config.status
./config.status
src/Makefile: src/Makefile.in config.status
touch $@
Makefile: Makefile.in config.status $(IN_WILD)
./config.status
src/Makefile: src/Makefile.in Makefile
config.status: configure
./config.status --recheck
configure: configure.ac
@@ -382,6 +437,7 @@ DISTCONFIG = ${srcdir}/src/config_build.h.in
DISTTITLE := $(shell sed -e '/DTVERSION/!d' -e 's/[^0-9]*\([0-9.a-z]*\).*/verilator-\1/' -e 's/v/V/' -e q $(DISTCONFIG))
DISTNAME := $(shell sed -e '/DTVERSION/!d' -e 's/[^0-9]*\([0-9.a-z]*\).*/verilator-\1/' -e q $(DISTCONFIG))
DISTDATEPRE := $(shell sed -e '/DTVERSION/!d' -e 's/.*\([1-2][0-9][0-9][0-9].[0-9]*[0-9].[0-9]*[0-9]\).*/\1/' -e q $(DISTCONFIG))
DISTNAMEREV := $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q src/config_rev.h)
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
DISTDATE := $(subst /,-,$(DISTDATEPRE))
+94 -89
View File
@@ -6,129 +6,134 @@
// Version 2.0.
Features:
Latch optimizations {Need here}
Task I/Os connecting to non-simple variables.
Fix ordering of each bit separately in a signal (mips)
Language support:
* Fix ordering of each bit separately in a signal (mips)
assign b[3:0] = b[7:4]; assign b[7:4] = in;
Support gate primitives/ cell libraries from xilinx, etc
Assign dont_care value to an 1'bzzz assignment
Function to eval combo logic after /*verilator public*/ functions [gwaters]
Support generated clocks (correctness)
?gcov coverage
Selectable SystemC types based on widths (see notes below)
Coverage
Points should be per-scope like everything else rather then per-module
Expression coverage (see notes)
Constant functions for widths, etc, IE "input [log2(PARAM):0] xx;"
More Verilog 2001 Support
(* *) Attributes (just ignore -- preprocessor?)
Real numbers (NEVER)
Recursive functions (NEVER)
Verilog configuration files (NEVER)
DPI to define C/C++ calls from Verilog
* Support UDP gate primitives/ cell libraries
(have code for combos - problem is sequential udps)
* Function to eval combo logic after /*verilator public*/ functions [gwaters]
* Support generated clocks (correctness)
* Real numbers
* Recursive functions
* Verilog configuration files
* Structs/unions (have starting point)
* DPI to define C/C++ calls from Verilog
* Expression coverage (see notes)
* Better tristate support
Long-term Features
Assertions
VHDL parser [Philips]
Tristate support
SystemPerl integration
Multithreaded execution
* Assertions
* Tristate support
* Multithreaded execution
Configure/Make/Install
* Full MSVC++ compilation (does scons support this?) (4.000?)
* Distribute with flex/bison already expanded?
Flex library not needed. Probably too difficult to be worth it.
* Integrate SystemPerl coverage
(Note in /usr/include there are no upper cased include files.)
Coverage.pm -- Need all functionality, but in C?
Coverage/Item.pm -- Need all functionality, but in C?
Coverage/ItemKey.pm -- Need all functionality, but in C?
sp_preproc -- Some steps in here need to be moved to generated C
src/Sp.cpp -- n/a
src/SpCommon.h -- mostly overlaps verilatedos.h
src/SpCoverage.cpp/h -- All needed
src/SpFunctor.cpp/h -- No longer used
src/SpTraceVcd.cpp/h -- MOVED
src/SpTraceVcdC.cpp/h -- MOVED
src/sp_log.cpp/h -- Not needed
src/systemperl.h -- some stuff may be cut
vcoverage -- Need all functionality, but in C?
Testing:
Capture all inputs into global "rerun it" file
Code to make wrapper that sets signals, so can do comparison checks
New random program generator
Better graph viewer with search and zoom
Port and test against opencores.org code
* Move test_c/sp/v/verilated into test_regress format (4.000?)
* Capture all inputs into global "rerun it" file
* Code to make wrapper that sets signals, so can do comparison checks
* New random program generator
* Better graph viewer with search and zoom
* Port and test against opencores.org code
Usability:
Better reporting of unopt problems, including what lines of code
Report more errors (all of them?) before exiting [Eugene Weber]
* Detect and pre-remove most UNOPTFLATs (4.000)
* Better reporting of unopt problems, including what lines of code
* Report more errors (all of them?) before exiting [Eugene Weber]
* Auto-create scons config files
* Print version/etc message at runtime. (4.000?)
Include number of lines of code, percent comments, code complexity measurement
<-80chars------------------------------------------------------------------->
Verilator 3.600 - fast, free, open-sourced. Copyright 2001-2010.
Verilated #### modules, #### instances, ##### sigs,
#### non-comment lines, ##### ops, ### KB model size
* Default the --l2name to remove extra "v" level of hierarchy (flag to make "top")
Internal Code:
Eliminate the AstNUser* passed to all visitors; its only needed in V3Width,
and removing it will speed up and simplify all the other code.
V3Graph should be templated container type, taking in Vertex + Edge types
* Eliminate the AstNUser* passed to all visitors; its only needed in V3Width,
and removing it will speed up and simplify all the other code.
* V3Graph should be templated container type, taking in Vertex + Edge types
* Rename V3PreLex etc to match VerilogPerl filenames
* Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
things, to reduce bugs. Also add _20 trailing space to \ encoded names. (4.000)
Runtime:
* New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
* Remove all private internal functions from top level wrapper header, move
to new level (4.000?)
* Completely standalone simulation (4.000)
main() records arguments for $test$plusvars
instantiates top,
does tracing (support $dump?)
calls top->simulateForever()
exits
Performance:
Constant propagation
* Latch optimizations
* Constant propagation
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
* Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
Could propagate the AND into pos/negedges and let domaining optimize.
Negedge reset
* Negedge reset
Switch to remove negedges that don't matter
Can't remove async resets from control flops (like in syncronizers)
If all references to array have a constant index, blow up into separate signals-per-index
Multithreaded execution
Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
Move _last sets and all other combo logic inside master
* If all references to array have a constant index, blow up into separate signals-per-index
* Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
* Move _last sets and all other combo logic inside master
if() that triggers on all possible sense items
Rewrite and combine V3Life, V3Subst
* Rewrite and combine V3Life, V3Subst
If block temp only ever set in one place to constant, propagate it
Used in t_mem for array delayed assignments
Replace variables if set later in same cfunc branch
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
Same assignment on both if branches
* Same assignment on both if branches
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
Careful though, as b could appear in the statement or multiple times in statement
(Could just require exatly two 'b's in statement)
Simplify XOR/XNOR/AND/OR bit selection trees
* Simplify XOR/XNOR/AND/OR bit selection trees
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
Combine variables into wider elements
* Combine variables into wider elements
Parallel statements on different bits should become single signal
Variables that are always consumed in "parallel" can be joined
Duplicate assignments in gate optimization
* Duplicate assignments in gate optimization
Common to have many separate posedge blocks, each with identical
reset_r <= rst_in
*If signal is used only once (not counting trace), always gate substitute
* If signal is used only once (not counting trace), always gate substitute
Don't merge if any combining would form circ logic (out goes back to in)
Multiple assignments each bit can become single assign with concat
* Multiple assignments each bit can become single assign with concat
Make sure a SEL of a CONCAT can get the single bit back.
Usually blocks/values
* Usually blocks/values
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
Allow Split of case statements without a $display/$stop
I-cache packing improvements (what/how?)
Data cache organization (order of vars in class)
* Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
* Allow Split of case statements without a $display/$stop
* I-cache packing improvements (what/how?)
* Data cache organization (order of vars in class)
First have clocks,
then bools instead of uint32_t's
then based on what sense list they come from, all outputs, then all inputs
finally have any signals part of a "usually" block, or constant.
Rather then tracking widths, have a MSB...LSB of this expression
* Rather then tracking widths, have a MSB...LSB of this expression
(or better, a bitmask of bits relevant in this expression)
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)
All of the temp vars that get set, exp pre_ vars and never feedback
(not flops) don't need to be stored in the structs, but instead can
be per-invocation, and even better register-colored-like to reuse
the space. This will greatly reduce the data footprint.
//**********************************************************************
//* Eventual tristate bus Stuff allowed (old verilator)
1) Tristate assignments must be continuous assignments
The RHS of a tristate assignment can be the following
a) a node (tristate or non-tristate)
b) a constant (must be all or no z's)
x'b0, x'bz, x{x'bz}, x{x'b0} -> are allowed
c) a conditional whose possible values are (a) or (b)
2) One can lose that fact that a node is a tristate node. This happens
if a tristate node is assigned to a 'standard' node, or is used on
RHS of a conditional. The following infer tristate signals:
a) inout <SIGNAL>
b) tri <SIGNAL>
c) assigning to 'Z' (maybe through a conditional)
Note: tristate-ness of an output port determined only by
statements in the module (not the instances it calls)
4) Tristate variables can't be multidimensional arrays
5) Only check tristate contention between modules (not within!)
6) Only simple compares with 'Z' are allowed (===)
* 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)
+44 -23
View File
@@ -878,7 +878,7 @@ section below for descriptions of some of the files that were created.
We then can compile it
cd obj_dir
make -f Vour.mk Vour
make -j -f Vour.mk Vour
(Verilator included a default compile rule and link rule, since we used
--exe and passed a .cpp file on the Verilator command line. You can also
@@ -944,9 +944,8 @@ Verilator by replacing the verilator --sp flag in the previous step with
We then can compile it
make -f Vour.mk Vour__ALL.a
make -f Vour.mk ../sc_main.o
make -f Vour.mk verilated.o
make -j -f Vour.mk Vour__ALL.a
make -j -f Vour.mk ../sc_main.o verilated.o
And link with SystemC. Note your path to the libraries may vary,
depending on the operating system.
@@ -1390,20 +1389,20 @@ Verilator configuration commands.
=item coverage_off [-file "<filename>" [-lines <line> [ - <line> ]]]
Disable coverage for the specified filename (or all files if ommitted) and
range of line numbers (or all lines if ommitted). Often used to ignore an
Disable coverage for the specified filename (or all files if omitted) and
range of line numbers (or all lines if omitted). Often used to ignore an
entire module for coverage analysis purposes.
=item lint_off -msg <message> [-file "<filename>" [-lines <line> [ - <line>]]]
Disables the specified lint warning in the specified filename (or all files
if ommitted) and range of line numbers (or all lines if ommitted).
if omitted) and range of line numbers (or all lines if omitted).
=item tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
Disable waveform tracing for all future signals declared in the specified
filename (or all files if ommitted) and range of line numbers (or all lines
if ommitted).
filename (or all files if omitted) and range of line numbers (or all lines
if omitted).
=back
@@ -1630,7 +1629,8 @@ Used after a signal declaration to indicate the signal is used to gate a
clock, and the user takes responsibility for insuring there are no races
related to it. (Typically by adding a latch, and running static timing
analysis.) This will cause the clock gate to be ignored in the scheduling
algorithm, improving performance.
algorithm, improving performance. It's also a good idea to enable the
IMPERFECTSCH warning, to insure all clock enables are properly recognized.
=item /*verilator coverage_block_off*/
@@ -1644,7 +1644,7 @@ Often used to ignore an entire module for coverage analysis purposes.
=item /*verilator coverage_on*/
Specifies that following lines of code should have coverage reenabled (if
Specifies that following lines of code should have coverage re-enabled (if
appropriate --coverage flags are passed) after being disabled earlier with
/*verilator coverage_off*/.
@@ -1770,6 +1770,18 @@ signal. This will not declare this module public, which means the name of
the signal or path to it may change based upon the module inlining which
takes place.
=item /*verilator public_flat_rd*/ (variable)
Used after a input, output, register, or wire declaration to indicate the
signal should be declared public_flat (see above), but read-only.
=item /*verilator public_flat_rw @(<edge_list>) */ (variable)
Used after a input, output, register, or wire declaration to indicate the
signal should be declared public_flat_rd (see above), and also writable,
where writes should be considered to have the timing specified by the given
sensitivity edge list.
=item /*verilator public_module*/
Used after a module statement to indicate the module should not be inlined
@@ -1819,14 +1831,17 @@ program will find for you.
Verilator supports only the Synthesis subset with a few minor additions
such as $stop, $finish and $display. That is, you cannot use hierarchical
references, events or similar features of the Verilog language. It also
simulates as Synopsys's Design Compiler would; namely a block of the form
simulates as Synopsys's Design Compiler would; namely a block of the form:
always @ (x) y = x & z;
will recompute y when there is a change in x or a change in z, which is
what Design Compiler will synthesize. A compliant simulator would only
calculate y if x changes. (Use verilog-mode's /*AS*/ or Verilog 2001's
always @* to prevent these issues.)
This will recompute y when there is even a potential for change in x or a
change in z, that is when the flops computing x or z evaluate (which is
what Design Compiler will synthesize.) A compliant simulator would only
calculate y if x changes. Use verilog-mode's /*AS*/ or Verilog 2001's
always @* to reduce missing activity items. Avoid putting $displays in
combo blocks, as they may print multiple times when not desired, even on
compliant simulators as event ordering is not specified.
=head2 Dotted cross-hierarchy references
@@ -2597,8 +2612,8 @@ allows you to modify Verilator for internal use without distributing the
modified version. But please contribute back to the community!
One limit is that you cannot under either license release a commercial
simulation product incorporating Verilator without making the source code
available.
Verilog simulation product incorporating Verilator without making the
source code available.
=item Why is Verilation so slow?
@@ -2620,8 +2635,10 @@ in your main loop call "trace_object->dump(time)" every time step, and
finally call "trace_object->close()". For an example, see below and the
test_c/sim_main.cpp file of the distribution.
You also need to compile verilated_vcd_c.cpp and add it to your link. This
is done for you if using the Verilator --exe flag.
You also need to compile verilated_vcd_c.cpp and add it to your link,
preferably by adding the dependancies in $(VK_GLOBAL_OBJS) to your
Makefile's link rule. This is done for you if using the Verilator --exe
flag.
Note you can also call ->trace on multiple Verilated objects with the same
trace file if you want all data to land in the same output file.
@@ -2658,8 +2675,10 @@ Alternatively you may use the C++ trace mechanism described in the previous
question, however the timescale and timeprecision will not inherited from
your SystemC settings.
You also need to compile verilated_vcd_sc.cpp and add it to your link.
This is done for you if using the Verilator --exe flag.
You also need to compile verilated_vcd_sc.cpp and verilated_vcd_c.cpp and
add them to your link, preferably by adding the dependancies in
$(VK_GLOBAL_OBJS) to your Makefile's link rule. This is done for you if
using the Verilator --exe flag.
Note you can also call ->trace on multiple Verilated objects with the same
trace file if you want all data to land in the same output file.
@@ -2765,7 +2784,9 @@ simulator knows the current time. See the "CONNECTING TO C++" examples.
=item Why do I get "undefined reference to `VL_RAND_RESET_I' or `Verilated::...'"?
You need to link your compiled Verilated code against the verilated.cpp
file found in the include directory of the Verilator kit.
file found in the include directory of the Verilator kit. This is one
target in the $(VK_GLOBAL_OBJS) make variable, which should be part of your
Makefile's link rule.
=item Is the PLI supported?
+101 -10
View File
@@ -33,8 +33,12 @@
//===========================================================================
// Global variables
// Slow path variables
int Verilated::s_randReset = 0;
int Verilated::s_debug = 1;
VerilatedVoidCb Verilated::s_flushCb = NULL;
// Keep below together in one cache line
int Verilated::s_debug = 0;
bool Verilated::s_calcUnusedSigs = false;
bool Verilated::s_gotFinish = false;
bool Verilated::s_assertOn = true;
@@ -53,6 +57,7 @@ void vl_finish (const char* filename, int linenum, const char* hier) {
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) {
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
Verilated::flushCall();
exit(0);
}
Verilated::gotFinish(true);
@@ -62,6 +67,7 @@ void vl_finish (const char* filename, int linenum, const char* hier) {
#ifndef VL_USER_STOP // Define this to override this function
void vl_stop (const char* filename, int linenum, const char* hier) {
Verilated::gotFinish(true);
Verilated::flushCall();
vl_fatal (filename,linenum,hier,"Verilog $stop");
}
#endif
@@ -71,6 +77,7 @@ void vl_fatal (const char* filename, int linenum, const char* hier, const char*
if (0 && hier) {}
Verilated::gotFinish(true);
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
Verilated::flushCall();
abort();
}
#endif
@@ -826,7 +833,7 @@ void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
} else {
needinc = true;
//printf(" Value width=%d @%x = %c\n", width, addr, c);
if (addr >= (IData)(depth+array_lsb) || addr < (IData)(array_lsb)) {
if (VL_UNLIKELY(addr >= (IData)(depth+array_lsb) || addr < (IData)(array_lsb))) {
vl_fatal (ofilenamez, linenum, "", "$readmem file address beyond bounds of array");
} else {
int entry = addr - array_lsb;
@@ -854,7 +861,7 @@ void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
_VL_SHIFTL_INPLACE_W(width, datap, (IData)shift);
datap[0] |= value;
}
if (value>=(1<<shift)) {
if (VL_UNLIKELY(value>=(1<<shift))) {
vl_fatal (ofilenamez, linenum, "", "$readmemb (binary) file contains hex characters");
}
}
@@ -871,7 +878,7 @@ void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
// Final checks
fclose(fp);
if (end != VL_UL(0xffffffff) && addr != (end+1)) {
if (VL_UNLIKELY(end != VL_UL(0xffffffff) && addr != (end+1))) {
vl_fatal (ofilenamez, linenum, "", "$readmem file ended before specified ending-address");
}
}
@@ -920,8 +927,12 @@ IData VL_VALUEPLUSARGS_IW(int rbits, const char* prefixp, char fmt, WDataOutP rw
const char* vl_mc_scan_plusargs(const char* prefixp) {
string match = VerilatedImp::argPlusMatch(prefixp);
if (match == "") return 0;
else return match.c_str()+strlen(prefixp)+1; // +1 to skip the "+"
static VL_THREAD char outstr[VL_VALUE_STRING_MAX_WIDTH];
if (match == "") return NULL;
strncpy(outstr, match.c_str()+strlen(prefixp)+1, // +1 to skip the "+"
VL_VALUE_STRING_MAX_WIDTH);
outstr[VL_VALUE_STRING_MAX_WIDTH-1] = '\0';
return outstr;
}
//===========================================================================
@@ -967,6 +978,15 @@ const char* Verilated::catName(const char* n1, const char* n2) {
return strp;
}
void Verilated::flushCb(VerilatedVoidCb cb) {
if (s_flushCb == cb) {} // Ok - don't duplicate
else if (!s_flushCb) { s_flushCb=cb; }
else {
// Someday we may allow multiple callbacks ala atexit(), but until then
vl_fatal("unknown",0,"", "Verilated::flushCb called twice with different callbacks");
}
}
void Verilated::commandArgs(int argc, const char** argv) {
VerilatedImp::commandArgs(argc,argv);
}
@@ -975,6 +995,10 @@ void Verilated::scopesDump() {
VerilatedImp::scopesDump();
}
const VerilatedScope* Verilated::scopeFind(const char* namep) {
return VerilatedImp::scopeFind(namep);
}
int Verilated::exportFuncNum(const char* namep) {
return VerilatedImp::exportFind(namep);
}
@@ -990,6 +1014,23 @@ VerilatedModule::~VerilatedModule() {
if (m_namep) free((void*)m_namep); m_namep=NULL;
}
//======================================================================
// VerilatedVar:: Methods
vluint32_t VerilatedVar::entSize() const {
vluint32_t size = 1;
switch (vltype()) {
case VLVT_PTR: size=sizeof(void*); break;
case VLVT_UINT8: size=sizeof(CData); break;
case VLVT_UINT16: size=sizeof(SData); break;
case VLVT_UINT32: size=sizeof(IData); break;
case VLVT_UINT64: size=sizeof(QData); break;
case VLVT_WDATA: size=VL_WORDS_I(range().bits())*sizeof(IData); break;
default: size=0; break;
}
return size;
}
//======================================================================
// VerilatedScope:: Methods
@@ -997,12 +1038,14 @@ VerilatedScope::VerilatedScope() {
m_callbacksp = NULL;
m_namep = NULL;
m_funcnumMax = 0;
m_varsp = NULL;
}
VerilatedScope::~VerilatedScope() {
VerilatedImp::scopeErase(this);
if (m_namep) { delete [] m_namep; m_namep = NULL; }
if (m_callbacksp) { delete [] m_callbacksp; m_callbacksp = NULL; }
if (m_varsp) { delete m_varsp; m_varsp = NULL; }
m_funcnumMax = 0; // Force callback table to empty
}
@@ -1027,10 +1070,10 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) {
// Alternative is to dynamically stretch the array, which is more code, and slower.
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
} else {
if (funcnum >= m_funcnumMax) {
if (VL_UNLIKELY(funcnum >= m_funcnumMax)) {
vl_fatal(__FILE__,__LINE__,"","Internal: Bad funcnum vs. pre-finalize maximum");
}
if (!m_callbacksp) { // First allocation
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
m_callbacksp = new void* [m_funcnumMax];
memset(m_callbacksp, 0, m_funcnumMax*sizeof(void*));
}
@@ -1038,6 +1081,47 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) {
}
}
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
VerilatedVarType vltype, int vlflags, int dims, ...) {
// Grab dimensions
// In the future we may just create a large table at emit time and statically construct from that.
if (!finalize) return;
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
VerilatedVar var (namep, datap, vltype, (VerilatedVarFlags)vlflags, dims);
va_list ap;
va_start(ap,dims);
for (int i=0; i<dims; ++i) {
int msb = va_arg(ap,int);
int lsb = va_arg(ap,int);
if (i==0) {
var.m_range.m_lhs = msb;
var.m_range.m_rhs = lsb;
} else if (i==1) {
var.m_array.m_lhs = msb;
var.m_array.m_rhs = lsb;
} else {
// We could have a linked list of ranges, but really this whole thing needs
// to be generalized to support structs and unions, etc.
vl_fatal(__FILE__,__LINE__,"",(string("Unsupported multi-dimensional public varInsert: ")+namep).c_str());
}
}
va_end(ap);
m_varsp->insert(make_pair(namep,var));
}
VerilatedVar* VerilatedScope::varFind(const char* namep) const {
if (VL_LIKELY(m_varsp)) {
VerilatedVarNameMap::iterator it = m_varsp->find(namep);
if (VL_LIKELY(it != m_varsp->end())) {
return &(it->second);
}
}
return NULL;
}
void* VerilatedScope::exportFindNullError(int funcnum) const {
// Slowpath - Called only when find has failed
string msg = (string("Testbench C called '")
@@ -1058,10 +1142,17 @@ void* VerilatedScope::exportFindError(int funcnum) const {
}
void VerilatedScope::scopeDump() const {
VL_PRINTF(" SCOPE: %s\n", name());
VL_PRINTF(" SCOPE %p: %s\n", this, name());
for (int i=0; i<m_funcnumMax; i++) {
if (m_callbacksp && m_callbacksp[i]) {
VL_PRINTF(" DPI-EXPORT: %s\n", VerilatedImp::exportName(i));
VL_PRINTF(" DPI-EXPORT %p: %s\n",
m_callbacksp[i], VerilatedImp::exportName(i));
}
}
if (varsp()) {
for (VerilatedVarNameMap::const_iterator it = varsp()->begin();
it != varsp()->end(); ++it) {
VL_PRINTF(" VAR %p: %s\n", &(it->second), it->first);
}
}
}
+48 -4
View File
@@ -62,11 +62,37 @@ typedef vluint32_t WData; ///< Verilated pack data, >64 bits, as an array
typedef const WData* WDataInP; ///< Array input to a function
typedef WData* WDataOutP; ///< Array output from a function
typedef void (*VerilatedVoidCb)(void);
class SpTraceVcd;
class SpTraceVcdCFile;
class VerilatedVar;
class VerilatedVarNameMap;
class VerilatedVcd;
class VerilatedVcdC;
enum VerilatedVarType {
VLVT_UNKNOWN=0,
VLVT_PTR, // Pointer to something
VLVT_UINT8, // AKA CData
VLVT_UINT16, // AKA SData
VLVT_UINT32, // AKA IData
VLVT_UINT64, // AKA QData
VLVT_WDATA, // AKA WData
VLVT_STRING // C++ string
};
enum VerilatedVarFlags {
VLVD_IN=1, // == vpiInput
VLVD_OUT=2, // == vpiOutput
VLVD_INOUT=3, // == vpiInOut
VLVD_NODIR=5, // == vpiNoDirection
VLVF_MASK_DIR=7, // Bit mask for above directions
// Flags
VLVF_PUB_RD=(1<<8), // Public readable
VLVF_PUB_RW=(1<<9) // Public writable
};
//=========================================================================
/// Base class for all Verilated module classes
@@ -136,6 +162,9 @@ public:
#ifndef VL_PRINTF
# define VL_PRINTF printf ///< Print ala printf; may redefine if desired
#endif
#ifndef VL_VPRINTF
# define VL_VPRINTF vprintf ///< Print ala vprintf; may redefine if desired
#endif
//===========================================================================
/// Verilator symbol table base class
@@ -149,19 +178,25 @@ class VerilatedSyms {
class VerilatedScope {
// Fastpath:
VerilatedSyms* m_symsp; ///< Symbol table
void** m_callbacksp; ///< Callback table pointer (Fastpath)
int m_funcnumMax; ///< Maxium function number stored (Fastpath)
VerilatedSyms* m_symsp; ///< Symbol table
void** m_callbacksp; ///< Callback table pointer (Fastpath)
int m_funcnumMax; ///< Maxium function number stored (Fastpath)
// 4 bytes padding (on -m64), for rent.
const char* m_namep; ///< Scope name (Slowpath)
VerilatedVarNameMap* m_varsp; ///< Variable map
const char* m_namep; ///< Scope name (Slowpath)
public: // But internals only - called from VerilatedModule's
VerilatedScope();
~VerilatedScope();
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp);
void exportInsert(int finalize, const char* namep, void* cb);
void varInsert(int finalize, const char* namep, void* datap,
VerilatedVarType vltype, int vlflags, int dims, ...);
// ACCESSORS
const char* name() const { return m_namep; }
inline VerilatedSyms* symsp() const { return m_symsp; }
VerilatedVar* varFind(const char* namep) const;
VerilatedVarNameMap* varsp() const { return m_varsp; }
void* exportFindError(int funcnum) const;
void* exportFindNullError(int funcnum) const;
void scopeDump() const;
@@ -182,7 +217,11 @@ public: // But internals only - called from VerilatedModule's
struct Verilated {
// MEMBERS
private:
// Slow path variables
static int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random
static VerilatedVoidCb s_flushCb; ///< Flush callback function
// Fast path
static int s_debug; ///< See accessors... only when VL_DEBUG set
static bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated
static bool s_gotFinish; ///< A $finish statement executed
@@ -224,6 +263,9 @@ public:
/// Enable/disable assertions
static void assertOn(bool flag) { s_assertOn=flag; }
static bool assertOn() { return s_assertOn; }
/// Flush callback for VCD waves
static void flushCb(VerilatedVoidCb cb);
static void flushCall() { if (s_flushCb) (*s_flushCb)(); }
/// Record command line arguments, for retrieval by $test$plusargs/$value$plusargs
static void commandArgs(int argc, const char** argv);
@@ -237,6 +279,8 @@ public:
// METHODS - INTERNAL USE ONLY
// Internal: Create a new module name by concatenating two strings
static const char* catName(const char* n1, const char* n2); // Returns new'ed data
// Internal: Find scope
static const VerilatedScope* scopeFind(const char* namep);
// Internal: Get and set DPI context
static const VerilatedScope* dpiScope() { return t_dpiScopep; }
static void dpiScope(const VerilatedScope* scopep) { t_dpiScopep=scopep; }
+1 -7
View File
@@ -31,6 +31,7 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_heavy.h"
#include "verilated_syms.h"
#include <map>
#include <vector>
@@ -41,13 +42,6 @@ class VerilatedScope;
//======================================================================
// Types
struct VerilatedCStrCmp {
// For ordering maps keyed by const char*'s
bool operator() (const char *a, const char *b) const {
return std::strcmp(a, b) < 0;
}
};
class VerilatedImp {
// Whole class is internal use only - Global information shared between verilated*.cpp files.
+100
View File
@@ -0,0 +1,100 @@
// -*- C++ -*-
//*************************************************************************
//
// Copyright 2003-2010 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
///
/// \file
/// \brief Verilator: Include to allow symbol inspection
///
/// This file is for inclusion by files that need to inspect
/// the symbol table. It is not included in verilated.h
/// as it requires some heavyweight C++ classes.
///
/// Code available from: http://www.veripool.org/verilator
///
//*************************************************************************
#ifndef _VERILATED_SYMS_H_
#define _VERILATED_SYMS_H_ 1 ///< Header Guard
#include "verilated_heavy.h"
#include <map>
//======================================================================
// Types
struct VerilatedCStrCmp {
/// Ordering maps keyed by const char*'s
bool operator() (const char *a, const char *b) const {
return std::strcmp(a, b) < 0;
}
};
//===========================================================================
/// Verilator range
class VerilatedRange {
int m_lhs;
int m_rhs;
protected:
friend class VerilatedVar;
friend class VerilatedScope;
VerilatedRange() : m_lhs(0), m_rhs(0) {}
void sets(int lhs, int rhs) { m_lhs=lhs; m_rhs=rhs; }
public:
~VerilatedRange() {}
int lhs() const { return m_lhs; }
int rhs() const { return m_rhs; }
int bits() const { return (VL_LIKELY(m_lhs>=m_rhs)?(m_lhs-m_rhs+1):(m_rhs-m_lhs+1)); }
};
//===========================================================================
/// Verilator variable
class VerilatedVar {
void* m_datap; // Location of data
VerilatedVarType m_vltype; // Data type
VerilatedVarFlags m_vlflags; // Direction
VerilatedRange m_range; // First range
VerilatedRange m_array; // Array
int m_dims; // Dimensions
const char* m_namep; // Name - slowpath
protected:
friend class VerilatedScope;
VerilatedVar(const char* namep, void* datap,
VerilatedVarType vltype, VerilatedVarFlags vlflags, int dims)
: m_datap(datap), m_vltype(vltype), m_vlflags(vlflags), m_dims(dims), m_namep(namep) {}
public:
~VerilatedVar() {}
void* datap() const { return m_datap; }
VerilatedVarType vltype() const { return m_vltype; }
VerilatedVarFlags vldir() const { return (VerilatedVarFlags)((int)m_vlflags & VLVF_MASK_DIR); }
vluint32_t entSize() const;
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
const VerilatedRange& range() const { return m_range; }
const VerilatedRange& array() const { return m_array; }
const char* name() const { return m_namep; }
int dims() const { return m_dims; }
};
//======================================================================
/// Types
struct VerilatedVarNameMap : public map<const char*, VerilatedVar, VerilatedCStrCmp> {
VerilatedVarNameMap() {}
~VerilatedVarNameMap() {}
};
#endif // Guard
+44 -12
View File
@@ -88,6 +88,11 @@ void VerilatedVcd::open (const char* filename) {
m_filename = filename;
s_vcdVecp.push_back(this);
// SPDIFF_OFF
// Set callback so an early exit will flush us
Verilated::flushCb(&flush_all);
// SPDIFF_ON
openNext (m_rolloverMB!=0);
if (!isOpen()) return;
@@ -149,6 +154,39 @@ void VerilatedVcd::openNext (bool incFilename) {
m_wroteBytes = 0;
}
void VerilatedVcd::makeNameMap() {
// Take signal information from each module and build m_namemapp
m_namemapp = new NameMap;
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
VerilatedVcdCallInfo *cip = m_callbacks[ent];
cip->m_code = nextCode();
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
}
// Though not speced, it's illegal to generate a vcd with signals
// not under any module - it crashes at least two viewers.
// If no scope was specified, prefix everything with a "top"
// This comes from user instantiations with no name - IE Vtop("").
bool nullScope = false;
for (NameMap::iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
const char* hiername = (*it).first.c_str();
if (hiername[0] == '\t') nullScope=true;
}
if (nullScope) {
NameMap* newmapp = new NameMap;
for (NameMap::iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
const string& hiername = it->first;
const string& decl = it->second;
string newname = string("top");
if (hiername[0] != '\t') newname += ' ';
newname += hiername;
newmapp->insert(make_pair(newname,decl));
}
delete m_namemapp; m_namemapp=NULL;
m_namemapp = newmapp;
}
}
VerilatedVcd::~VerilatedVcd() {
close();
if (m_wrBufp) { delete[] m_wrBufp; m_wrBufp=NULL; }
@@ -196,7 +234,7 @@ void VerilatedVcd::printStr (const char* str) {
void VerilatedVcd::printQuad (vluint64_t n) {
char buf [100];
sprintf(buf,"%" VL_PRI64 "u",(long long unsigned)n);
sprintf(buf,"%" VL_PRI64 "u", n);
printStr(buf);
}
@@ -222,10 +260,10 @@ void VerilatedVcd::bufferFlush () {
if (VL_UNLIKELY(!isOpen())) return;
char* wp = m_wrBufp;
while (1) {
size_t remaining = (m_writep - wp);
ssize_t remaining = (m_writep - wp);
if (remaining==0) break;
errno = 0;
int got = write (m_fd, wp, remaining);
ssize_t got = write (m_fd, wp, remaining);
if (got>0) {
wp += got;
m_wroteBytes += got;
@@ -309,13 +347,7 @@ void VerilatedVcd::dumpHeader () {
printStr(doubleToTimescale(m_timeRes).c_str());
printStr(" $end\n");
// Take signal information from each module and build m_namemapp
m_namemapp = new NameMap;
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
VerilatedVcdCallInfo *cip = m_callbacks[ent];
cip->m_code = nextCode();
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
}
makeNameMap();
// Signal header
assert (m_modDepth==0);
@@ -341,7 +373,7 @@ void VerilatedVcd::dumpHeader () {
// Skip common prefix, it must break at a space or tab
for (; *np && (*np == *lp); np++, lp++) {}
while (np!=hiername && *np && *np!=' ' && *np!='\t') { np--; lp--; }
//cout <<"hier "<<hiername<<endl<<" lp "<<lp<<endl<<" np "<<np<<endl;
//printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
// Any extra spaces in last name are scope ups we need to do
bool first = true;
@@ -389,7 +421,7 @@ void VerilatedVcd::module (string name) {
}
void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep,
int arraynum, bool tri, bool bussed, int msb, int lsb) {
int arraynum, bool tri, bool bussed, int msb, int lsb) {
if (!code) { vl_fatal(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
int bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
+1
View File
@@ -99,6 +99,7 @@ private:
void closePrev();
void closeErr();
void openNext();
void makeNameMap();
void printIndent (int levelchange);
void printStr (const char* str);
void printQuad (vluint64_t n);
+24 -2
View File
@@ -126,8 +126,13 @@ typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibili
typedef long vlsint32_t; ///< 32-bit signed type
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
# endif
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
typedef long vlsint64_t; ///< 64-bit signed type
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
# else
typedef long long vlsint64_t; ///< 64-bit signed type
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
# endif
#elif defined(_WIN32) && defined(_MSC_VER)
@@ -141,6 +146,14 @@ typedef unsigned __int32 vluint32_t; ///< 32-bit unsigned type
typedef signed __int64 vlsint64_t; ///< 64-bit signed type
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
# ifndef _SSIZE_T_DEFINED
# ifdef _WIN64
typedef signed __int64 ssize_t; ///< signed size_t; returned from read()
# else
typedef signed __int32 ssize_t; ///< signed size_t; returned from read()
# endif
# endif
#else // Linux or compliant Unix flavors, -m64
# include <stdint.h> // Linux and most flavors
@@ -149,8 +162,13 @@ 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
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
typedef long vlsint64_t; ///< 64-bit signed type
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
# else
typedef long long vlsint64_t; ///< 64-bit signed type
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
# endif
#endif
//=========================================================================
@@ -160,7 +178,11 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
#ifdef _WIN32
# define VL_PRI64 "I64"
#else // Linux or compliant Unix flavors
# define VL_PRI64 "ll"
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
# define VL_PRI64 "l"
# else
# define VL_PRI64 "ll"
# endif
#endif
//=========================================================================
File diff suppressed because it is too large Load Diff
+143 -54
View File
@@ -40,7 +40,148 @@ Modify the later visitor functions to process the new feature as needed.
=back
=head1 DEBUG OUTPUT/ TREE FILES
=head1 CODE FLOWS
=head2 Verilator Flow
The main flow of Verilator can be followed by reading the Verilator.cpp
process() function:
First, the files specified on the command line are read. Reading involves
preprocessing, then lexical analysis with Flex and parsing with Bison.
This produces an abstract syntax tree (AST) representation of the design,
which is what is visible in the .tree files described below.
Cells are then linked, which will read and parse additional files as above.
Functions, variable and other references are linked to their definitions.
Parameters are resolved and the design is elaborated.
Verilator then performs many additional edits and optimizations on the
hierarchical design. This includes coverage, assertions, X elimination,
inlining, constant propagation, and dead code elimination.
References in the design are then psudo-flattened. Each module's variables
and functions get "Scope" references. A scope reference is an occurrence of
that un-flattened variable in the flattened hierarchy. A module that occurs
only once in the hierarchy will have a single scope and single VarScope for
each variable. A module that occurs twice will have a scope for each
occurrence, and two VarScopes for each variable. This allows optimizations
to proceed across the flattened design, while still preserving the
hierarchy.
Additional edits and optimizations proceed on the psudo-flat design. These
include module references, function inlining, loop unrolling, variable
lifetime analysis, lookup table creation, always splitting, and logic gate
simplifications (pushing inverters, etc).
Verilator orders the code. Best case, this results in a single "eval"
function which has all always statements flowing from top to bottom with no
loops.
Verilator mostly removes the flattening, so that code may be shared between
multiple invocations of the same module. It localizes variables, combines
identical functions, expands macros to C primitives, adds branch prediction
hints, and performs additional constant propagation.
Verilator finally writes the C++ modules.
=head2 Verilated Flow
The evaluation loop outputted by Verilator is designed to allow a single
function to perform evaluation under most situations.
On the first evaluation, the Verilated code calls initial blocks, and then
"settles" the modules, by evaluating functions (from always statements)
until all signals are stable.
On other evaluations, the Verilated code detects what input signals have
changes. If any are clocks, it calls the appropriate sequential functions
(from always @ posedge statements). Interspersed with sequential functions
it calls combo functions (from always @*). After this is complete, it
detects any changes due to combo loops or internally generated clocks, and
if one is found must reevaluate the model again.
For SystemC code, the eval() function is wrapped in a SystemC SC_METHOD,
sensitive to all inputs. (Ideally it would only be sensitive to clocks and
combo inputs, but tracing requires all signals to cause evaluation, and the
performance difference is small.)
If tracing is enabled, a callback examines all variables in the design for
changes, and writes the trace for each change. To accelerate this process
the evaluation process records a bitmask of variables that might have
changed; if clear, checking those signals for changes may be skipped.
=head1 CODING CONVENTIONS
=head2 Indentation style
To match the indentation of Verilator C++ sources, use 4 spaces per level,
and leave tabs at 8 columns, so every other indent level is a tab stop.
In Emacs, use in your ~/.emacs
(add-hook 'c-mode-common-hook '(lambda ()
(c-set-style "cc-mode"))))
This sets indentation to the cc-mode defaults. (Verilator predates a
CC-mode change of several years ago which overrides the defaults with GNU
style indentation; the c-set-style undoes that.)
=head2 Visitor Functions
There's three ways data is passed between visitor functions.
1. A visitor-class member variable. This is generally for passing "parent"
information down to children. m_modp is a common example. It's set to
NULL in the constructor, where that node (AstModule visitor) sets it, then
the children are iterated, then it's cleared. Children under an AstModule
will see it set, while nodes elsewhere will see it clear. If there can be
nested items (for example an AstFor under an AstFor) the variable needs to
be save-set-restored in the AstFor visitor, otherwise exiting the lower for
will loose the upper for's setting.
2. User() attributes. Each node has 5 ->user() number or ->userp() pointer
utility values (a common technique lifted from graph traversal packages).
A visitor first clears the one it wants to use by calling
AstNode::user#ClearTree(), then it can mark any node's user() with whatever
data it wants. Readers just call nodep->user(), but may need to cast
appropriately, so you'll often see nodep->userp()->castSOMETYPE(). At the
top of each visitor are comments describing how the user() stuff applies to
that visitor class. For example:
// NODE STATE
// Cleared entire netlist
// AstModule::user1p() // bool. True to inline this module
This says that at the AstNetlist user1ClearTree() is called. Each
AstModule's is user1() is used to indicate if we're going to inline it.
These comments are important to make sure a user#() on a given AstNode type
is never being used for two different purposes.
Note that calling user#ClearTree is fast, it doesn't walk the tree, so it's
ok to call fairly often. For example, it's commonly called on every
module.
3. Parameters can be passed between the visitors in close to the "normal"
function caller to callee way. This is the second "vup" parameter that is
ignored on most of the visitor functions. V3Width does this, but it proved
more messy than the above and is deprecated. (V3Width was nearly the first
module written. Someday this scheme may be removed, as it slows the
program down to have to pass vup everywhere.)
=head1 TESTING
To write a test see notes in the forum and in the verilator.txt manual.
Note you can run the regression tests in parallel; see the
test_regress/driver.pl script -j flag.
=head1 DEBUGGING
=head2 --debug
When you run with --debug there are two primary output file types placed into
the obj_dir, .tree and .dot files.
@@ -94,59 +235,7 @@ variable is an output.
=back
=head1 TESTING
To write a test see notes in the forum and in the verilator.txt manual.
Note you can run the regression tests in parallel; see the
test_regress/driver.pl script -j flag.
=head1 VISITOR FUNCTIONS
=head2 Passing Variables
There's three ways data is passed between visitor functions.
1. A visitor-class member variable. This is generally for passing "parent"
information down to children. m_modp is a common example. It's set to
NULL in the constructor, where that node (AstModule visitor) sets it, then
the children are iterated, then it's cleared. Children under an AstModule
will see it set, while nodes elsewhere will see it clear. If there can be
nested items (for example an AstFor under an AstFor) the variable needs to
be save-set-restored in the AstFor visitor, otherwise exiting the lower for
will loose the upper for's setting.
2. User() attributes. Each node has 5 ->user() number or ->userp() pointer
utility values (a common technique lifted from graph traversal packages).
A visitor first clears the one it wants to use by calling
AstNode::user#ClearTree(), then it can mark any node's user() with whatever
data it wants. Readers just call nodep->user(), but may need to cast
appropriately, so you'll often see nodep->userp()->castSOMETYPE(). At the
top of each visitor are comments describing how the user() stuff applies to
that visitor class. For example:
// NODE STATE
// Cleared entire netlist
// AstModule::user1p() // bool. True to inline this module
This says that at the AstNetlist user1ClearTree() is called. Each
AstModule's is user1() is used to indicate if we're going to inline it.
These comments are important to make sure a user#() on a given AstNode type
is never being used for two different purposes.
Note that calling user#ClearTree is fast, it doesn't walk the tree, so it's
ok to call fairly often. For example, it's commonly called on every
module.
3. Parameters can be passed between the visitors in close to the "normal"
function caller to callee way. This is the second "vup" parameter that is
ignored on most of the visitor functions. V3Width does this, but it proved
more messy than the above and is deprecated. (V3Width was nearly the first
module written. Someday this scheme may be removed, as it slows the
program down to have to pass vup everywhere.)
=head1 DEBUGGING WITH GDB
=head2 Debugging with GDB
The test_regress/driver.pl script accepts --debug --gdb to start Verilator
under gdb. You can also use --debug --gdbbt to just backtrace and then
+2 -2
View File
@@ -59,9 +59,9 @@ sub test {
my $bin1 = $prefix."/bin";
my $bin2 = $prefix."/share/bin";
IO::File->new(">$testdir/foo.v")->print('module t; initial begin $display("HELLO WORLD"); end endmodule'."\n");
run("cd $testdir && PATH=$bin1:$bin2:\$PATH verilator --cc --exe foo.v");
run("cd $testdir && PATH=$bin1:$bin2:\$PATH verilator --cc foo.v");
run("cd $testdir/obj_dir && PATH=$bin1:$bin2:\$PATH make -f Vfoo.mk");
#Need .c file for this to work
#Need .c file, and --exe for this to work
#run("cd $testdir && PATH=$bin1:$bin2:\$PATH obj_dir/Vfoo");
}
}
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/perl -w
# See copyright, etc in below POD section.
######################################################################
require 5.006_001;
use strict;
#======================================================================
# main
delete $ENV{MODULE_VERSION};
_setup_modules();
module('add','axiom-athdl');
exec('atsim',@ARGV);
#######################################################################
# Modules package
sub _setup_modules {
# Load the 'module' command into the environment
my $init = "$ENV{MODULESHOME}/init/perl";
(-f $init) or die "%Error: Script not found: $init,";
require $init;
}
#######################################################################
__END__
=pod
=head1 NAME
invoke_atsim - Invoke tool under "modules" command
=head1 SYNOPSIS
invoke_atsim {ncv arguments}
=head1 DESCRIPTION
=head1 DISTRIBUTION
Copyright 2005-2010 by Wilson Snyder. This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
=head1 AUTHORS
Wilson Snyder <[email protected]>
=head1 SEE ALSO
=cut
######################################################################
### Local Variables:
### compile-command: "./invoke_atsim -help"
### End:
+1
View File
@@ -126,6 +126,7 @@ sub prep {
#
$wholefile =~ s/\b(uint[0-9]+_t)/vl$1/g;
$wholefile =~ s/%ll/%" VL_PRI64 "/g;
$wholefile =~ s/\(long long unsigned\)n/ n/g; # printQuad
#
$wholefile =~ s/\bSP_SC_BV/VL_SC_BV/g;
$wholefile =~ s/\bSP_UNLIKELY/VL_UNLIKELY/g;
+7 -5
View File
@@ -121,6 +121,7 @@ HEADERS = $(wildcard V*.h v*.h)
ASTGEN = $(srcdir)/astgen
BISONPRE = $(srcdir)/bisonpre
FLEXFIX = $(srcdir)/flexfix
######################################################################
#### Top level
@@ -194,8 +195,9 @@ RAW_OBJS = \
V3Link.o \
V3LinkCells.o \
V3LinkDot.o \
V3LinkLevel.o \
V3LinkJump.o \
V3LinkLValue.o \
V3LinkLevel.o \
V3LinkParse.o \
V3LinkResolve.o \
V3Localize.o \
@@ -295,15 +297,15 @@ V3Lexer_pregen.yy.cpp: verilog.l V3ParseBison.h $(HEADERS)
${LEX} --version
${LEX} ${LFLAGS} -o$@ $<
V3Lexer.yy.cpp: V3Lexer_pregen.yy.cpp
$(PERL) $(srcdir)/flexfix <$< >$@
V3Lexer.yy.cpp: V3Lexer_pregen.yy.cpp $(FLEXFIX)
$(PERL) $(FLEXFIX) V3Lexer <$< >$@
V3PreLex_pregen.yy.cpp: V3PreLex.l $(HEADERS)
${LEX} --version
${LEX} ${LFLAGS} -o$@ $<
V3PreLex.yy.cpp: V3PreLex_pregen.yy.cpp
$(PERL) $(srcdir)/flexfix <$< >$@
V3PreLex.yy.cpp: V3PreLex_pregen.yy.cpp $(FLEXFIX)
$(PERL) $(FLEXFIX) V3PreLex <$< >$@
######################################################################
######################################################################
+29 -23
View File
@@ -197,7 +197,6 @@ private:
// STATE
ActiveNamer m_namer; // Tracking of active names
AstCFunc* m_scopeFinalp; // Final function for this scope
AstAlways* m_alwaysp; // Under always
bool m_itemCombo; // Found a SenItem combo
bool m_itemSequent; // Found a SenItem sequential
@@ -264,32 +263,22 @@ private:
}
// METHODS
virtual void visit(AstAlways* nodep, AstNUser*) {
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp) {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4," ALW "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
if (!nodep->bodysp()) {
// Empty always. Kill it.
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
if (nodep->sensesp()
&& nodep->sensesp()->sensesp()
&& nodep->sensesp()->sensesp()->castSenItem()
&& nodep->sensesp()->sensesp()->castSenItem()->isNever()) {
if (oldsensesp
&& oldsensesp->sensesp()
&& oldsensesp->sensesp()->castSenItem()
&& oldsensesp->sensesp()->castSenItem()->isNever()) {
// Never executing. Kill it.
if (nodep->sensesp()->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
// Read sensitivitues
m_alwaysp = nodep;
m_itemCombo = false;
m_itemSequent = false;
nodep->sensesp()->iterateAndNext(*this);
m_alwaysp = NULL;
oldsensesp->iterateAndNext(*this);
bool combo = m_itemCombo;
bool sequent = m_itemSequent;
@@ -311,15 +300,15 @@ private:
// 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
if (combo) oldsensesp->addSensesp
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
#endif
wantactivep = m_namer.getActive(nodep->fileline(), nodep->sensesp());
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
}
// Delete sensitivity list
if (AstNode* oldsense = nodep->sensesp()) {
oldsense->unlinkFrBackWithNext()->deleteTree(); oldsense=NULL;
if (oldsensesp) {
oldsensesp->unlinkFrBackWithNext()->deleteTree(); oldsensesp=NULL;
}
// Move node to new active
@@ -331,6 +320,24 @@ private:
ActiveDlyVisitor dlyvisitor (nodep);
}
}
virtual void visit(AstAlways* nodep, AstNUser*) {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4," ALW "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
if (!nodep->bodysp()) {
// Empty always. Kill it.
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
visitAlways(nodep, nodep->sensesp());
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4," ALWPub "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
visitAlways(nodep, nodep->sensesp());
}
virtual void visit(AstSenGate* nodep, AstNUser*) {
AstSenItem* subitemp = nodep->sensesp();
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
@@ -366,7 +373,6 @@ public:
// CONSTUCTORS
ActiveVisitor(AstNetlist* nodep) {
m_scopeFinalp = NULL;
m_alwaysp = NULL;
m_itemCombo = false;
m_itemSequent = false;
nodep->accept(*this);
+3
View File
@@ -133,6 +133,9 @@ private:
virtual void visit(AstAlways* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstFinal* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been deleted");
}
+1 -2
View File
@@ -177,8 +177,7 @@ private:
// VISITORS //========== Case assertions
virtual void visit(AstCase* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->user1()) {
nodep->user1(true);
if (!nodep->user1Inc()) {
bool has_default=false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
if (itemp->isDefault()) has_default=true;
+11 -1
View File
@@ -179,6 +179,8 @@ public:
VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn
VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic
VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic
VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic
VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic
VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign
VAR_SFORMAT // V3LinkParse moves to AstVar::attrSFormat
};
@@ -186,7 +188,8 @@ public:
const char* ascii() const {
static const char* names[] = {
"EXPR_BITS", "VAR_BASE",
"VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT",
"VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW",
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SFORMAT"
};
return names[m_e];
@@ -761,6 +764,7 @@ public:
AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); }
AstNode* firstAbovep() const { return ((backp() && backp()->nextp()!=this) ? backp() : NULL); } // Returns NULL when second or later in list
bool brokeExists() const;
bool brokeExistsAbove() const;
// CONSTRUCTORS
virtual ~AstNode();
@@ -818,6 +822,7 @@ public:
void user1p(void* userp) { m_user1p=(AstNUser*)(userp); m_user1Cnt=AstUser1InUse::s_userCntGbl; }
int user1() const { return user1p()->castInt(); }
void user1(int val) { user1p(AstNUser::fromInt(val)); }
int user1Inc() { int v=user1(); user1(v+1); return v; }
static void user1ClearTree() { AstUser1InUse::clear(); } // Clear userp()'s across the entire tree
AstNUser* user2p() const {
@@ -826,6 +831,7 @@ public:
void user2p(void* userp) { m_user2p=(AstNUser*)(userp); m_user2Cnt=AstUser2InUse::s_userCntGbl; }
int user2() const { return user2p()->castInt(); }
void user2(int val) { user2p(AstNUser::fromInt(val)); }
int user2Inc() { int v=user2(); user2(v+1); return v; }
static void user2ClearTree() { AstUser2InUse::clear(); }
AstNUser* user3p() const {
@@ -834,6 +840,7 @@ public:
void user3p(void* userp) { m_user3p=(AstNUser*)(userp); m_user3Cnt=AstUser3InUse::s_userCntGbl; }
int user3() const { return user3p()->castInt(); }
void user3(int val) { user3p(AstNUser::fromInt(val)); }
int user3Inc() { int v=user3(); user3(v+1); return v; }
static void user3ClearTree() { AstUser3InUse::clear(); }
AstNUser* user4p() const {
@@ -842,6 +849,7 @@ public:
void user4p(void* userp) { m_user4p=(AstNUser*)(userp); m_user4Cnt=AstUser4InUse::s_userCntGbl; }
int user4() const { return user4p()->castInt(); }
void user4(int val) { user4p(AstNUser::fromInt(val)); }
int user4Inc() { int v=user4(); user4(v+1); return v; }
static void user4ClearTree() { AstUser4InUse::clear(); }
AstNUser* user5p() const {
@@ -850,6 +858,7 @@ public:
void user5p(void* userp) { m_user5p=(AstNUser*)(userp); m_user5Cnt=AstUser5InUse::s_userCntGbl; }
int user5() const { return user5p()->castInt(); }
void user5(int val) { user5p(AstNUser::fromInt(val)); }
int user5Inc() { int v=user5(); user5(v+1); return v; }
static void user5ClearTree() { AstUser5InUse::clear(); }
vluint64_t editCount() const { return m_editCount; }
@@ -870,6 +879,7 @@ public:
AstNode* addNext(AstNode* newp); // Returns this, adds to end of list
AstNode* addNextNull(AstNode* newp); // Returns this, adds to end of list, NULL is OK
void addNextHere(AstNode* newp); // Adds after speced node
void addPrev(AstNode* newp) { replaceWith(newp); newp->addNext(this); }
void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next
void replaceWith(AstNode* newp); // Replace current node in tree with new node
void v3errorEnd(ostringstream& str) const;
+56 -5
View File
@@ -137,6 +137,43 @@ string AstVar::vlArgType(bool named, bool forReturn) const {
return arg;
}
string AstVar::vlEnumType() const {
string arg;
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
return "VLVT_PTR";
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
return "VLVT_PTR";
} else if (strtype) {
arg += "VLVT_STRING";
} else if (widthMin() <= 8) {
arg += "VLVT_UINT8";
} else if (widthMin() <= 16) {
arg += "VLVT_UINT16";
} else if (widthMin() <= VL_WORDSIZE) {
arg += "VLVT_UINT32";
} else if (isQuad()) {
arg += "VLVT_UINT64";
} else if (isWide()) {
arg += "VLVT_WDATA";
}
// else return "VLVT_UNKNOWN"
return arg;
}
string AstVar::vlEnumDir() const {
if (isInout()) {
return "VLVD_INOUT";
} else if (isInOnly()) {
return "VLVD_IN";
} else if (isOutOnly()) {
return "VLVD_OUT";
} else {
return "VLVD_NODIR";
}
}
string AstVar::cPubArgType(bool named, bool forReturn) const {
if (forReturn) named=false;
string arg;
@@ -258,13 +295,15 @@ uint32_t AstVar::arrayElements() const {
return entries;
}
uint32_t AstVar::dimensions() const {
// How many array dimensions does this Var have?
uint32_t dim = 0;
pair<uint32_t,uint32_t> AstVar::dimensions() const {
// How many array dimensions (packed,unpacked) does this Var have?
uint32_t packed = 0;
uint32_t unpacked = 0;
for (AstNodeDType* dtypep=this->dtypep(); dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstArrayDType* adtypep = dtypep->castArrayDType()) {
dim += 1;
if (adtypep->isPacked()) packed += 1;
else unpacked += 1;
dtypep = adtypep->dtypep();
}
else {
@@ -272,7 +311,7 @@ uint32_t AstVar::dimensions() const {
break;
}
}
return dim;
return make_pair(packed, unpacked);
}
// Special operators
@@ -348,6 +387,8 @@ string AstScopeName::scopeSymName() const {
out += textp->text();
}
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
if (out.substr(0,1) == ".") out.replace(0,1,"");
string::size_type pos;
while ((pos=out.find(".")) != string::npos) {
out.replace(pos, 1, "__");
@@ -464,6 +505,10 @@ void AstNode::dump(ostream& os) {
if (name()!="") os<<" "<<AstNode::quoteName(name());
}
void AstArrayDType::dump(ostream& str) {
this->AstNode::dump(str);
if (isPacked()) str<<" [PACKED]";
}
void AstArraySel::dump(ostream& str) {
this->AstNode::dump(str);
str<<" [start:"<<start()<<"] [length:"<<length()<<"]";
@@ -494,6 +539,12 @@ void AstDisplay::dump(ostream& str) {
this->AstNode::dump(str);
//str<<" "<<displayType().ascii();
}
void AstJumpGo::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (labelp()) { labelp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstEnumItemRef::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
+119 -15
View File
@@ -204,13 +204,17 @@ public:
struct AstArrayDType : public AstNodeDType {
// Array data type, ie "some_dtype var_name [2:0]"
AstArrayDType(FileLine* fl, AstNodeDType* dtypep, AstRange* rangep)
: AstNodeDType(fl) {
private:
bool m_packed;
public:
AstArrayDType(FileLine* fl, AstNodeDType* dtypep, AstRange* rangep, bool isPacked=false)
: AstNodeDType(fl), m_packed(isPacked) {
setOp1p(dtypep);
setOp2p(rangep);
widthSignedFrom(dtypep);
}
ASTNODE_NODE_FUNCS(ArrayDType, ARRAYDTYPE)
virtual void dump(ostream& str);
AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable
void dtypep(AstNodeDType* nodep) { setOp1p(nodep); }
@@ -225,6 +229,7 @@ struct AstArrayDType : public AstNodeDType {
int lsb() const { return arrayp()->lsbConst(); }
int elementsConst() const { return arrayp()->elementsConst(); }
int msbMaxSelect() const { return (lsb()<0 ? msb()-lsb() : msb()); } // Maximum value a [] select may index
bool isPacked() const { return m_packed; }
};
struct AstBasicDType : public AstNodeDType {
@@ -542,8 +547,10 @@ private:
bool m_sc:1; // SystemC variable
bool m_scClocked:1; // SystemC sc_clk<> needed
bool m_scSensitive:1;// SystemC sensitive() needed
bool m_sigPublic:1; // User C code accesses this signal
bool m_sigPublic:1; // User C code accesses this signal or is top signal
bool m_sigModPublic:1;// User C code accesses this signal and module
bool m_sigUserRdPublic:1; // User C code accesses this signal, read only
bool m_sigUserRWPublic:1; // User C code accesses this signal, read-write
bool m_usedClock:1; // Signal used as a clock
bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup)
bool m_funcLocal:1; // Local variable for a function
@@ -561,7 +568,7 @@ private:
m_primaryIO=false;
m_sc=false; m_scClocked=false; m_scSensitive=false;
m_usedClock=false; m_usedParam=false;
m_sigPublic=false; m_sigModPublic=false;
m_sigPublic=false; m_sigModPublic=false; m_sigUserRdPublic=false; m_sigUserRWPublic=false;
m_funcLocal=false; m_funcReturn=false;
m_attrClockEn=false; m_attrIsolateAssign=false; m_attrSFormat=false;
m_fileDescr=false; m_isConst=false; m_isStatic=false;
@@ -607,12 +614,14 @@ public:
string cPubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc.
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
string vlArgType(bool named, bool forReturn) const; // Return Verilator internal type for argument: CData, SData, IData, WData
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
void combineType(AstVarType type);
AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType)
AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType)
AstNodeDType* dtypeDimensionp(int depth) const;
uint32_t dimensions() const;
pair<uint32_t,uint32_t> dimensions() const;
AstNode* valuep() const { return op3p()->castNode(); } // op3 = Initial value that never changes (static const)
void valuep(AstNode* nodep) { setOp3p(nodep); } // It's valuep, not constp, as may be more complicated than a AstConst
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
@@ -628,6 +637,8 @@ public:
void usedParam(bool flag) { m_usedParam = flag; }
void sigPublic(bool flag) { m_sigPublic = flag; }
void sigModPublic(bool flag) { m_sigModPublic = flag; }
void sigUserRdPublic(bool flag) { m_sigUserRdPublic = flag; if (flag) sigPublic(true); }
void sigUserRWPublic(bool flag) { m_sigUserRWPublic = flag; if (flag) sigUserRdPublic(true); }
void sc(bool flag) { m_sc = flag; }
void scSensitive(bool flag) { m_scSensitive = flag; }
void primaryIO(bool flag) { m_primaryIO = flag; }
@@ -670,6 +681,8 @@ public:
bool isScSensitive() const { return m_scSensitive; }
bool isSigPublic() const;
bool isSigModPublic() const { return m_sigModPublic; }
bool isSigUserRdPublic() const { return m_sigUserRdPublic; }
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
bool isTrace() const { return m_trace; }
bool isConst() const { return m_isConst; }
bool isStatic() const { return m_isStatic; }
@@ -699,6 +712,8 @@ public:
combineType(typevarp->varType());
if (typevarp->isSigPublic()) sigPublic(true);
if (typevarp->isSigModPublic()) sigModPublic(true);
if (typevarp->isSigUserRdPublic()) sigUserRdPublic(true);
if (typevarp->isSigUserRWPublic()) sigUserRWPublic(true);
if (typevarp->attrScClocked()) attrScClocked(true);
}
void inlineAttrReset(const string& name) {
@@ -1265,6 +1280,24 @@ struct AstAlways : public AstNode {
bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); }
};
struct AstAlwaysPublic : public AstNodeStmt {
// "Fake" sensitivity created by /*verilator public_flat_rw @(edgelist)*/
// Body statements are just AstVarRefs to the public signals
AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp)
: AstNodeStmt(fl) {
addNOp1p(sensesp); addNOp2p(bodysp);
}
ASTNODE_NODE_FUNCS(AlwaysPublic, ALWAYSPUBLIC)
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
//
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p()->castNode(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
// Special accessors
bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); }
};
struct AstAlwaysPost : public AstNode {
// Like always but post assignments for memory assignment IFs
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp)
@@ -1846,14 +1879,6 @@ struct AstGenFor : public AstNodeFor {
ASTNODE_NODE_FUNCS(GenFor, GENFOR)
};
struct AstFor : public AstNodeFor {
AstFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
AstNode* incsp, AstNode* bodysp)
: AstNodeFor(fileline, initsp, condp, incsp, bodysp) {
}
ASTNODE_NODE_FUNCS(For, FOR)
};
struct AstRepeat : public AstNodeStmt {
AstRepeat(FileLine* fileline, AstNode* countp, AstNode* bodysp)
: AstNodeStmt(fileline) {
@@ -1869,16 +1894,18 @@ struct AstRepeat : public AstNodeStmt {
};
struct AstWhile : public AstNodeStmt {
AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp)
AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp, AstNode* incsp=NULL)
: AstNodeStmt(fileline) {
setOp2p(condp); addNOp3p(bodysp);
setOp2p(condp); addNOp3p(bodysp); addNOp4p(incsp);
}
ASTNODE_NODE_FUNCS(While, WHILE)
AstNode* precondsp() const { return op1p()->castNode(); } // op1= prepare statements for condition (exec every loop)
AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue
AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop
AstNode* incsp() const { return op4p()->castNode(); } // op4= increment (if from a FOR loop)
void addPrecondsp(AstNode* newp) { addOp1p(newp); }
void addBodysp(AstNode* newp) { addOp3p(newp); }
void addIncsp(AstNode* newp) { addOp4p(newp); }
virtual bool isGateOptimizable() const { return false; }
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(); }
@@ -1887,6 +1914,36 @@ struct AstWhile : public AstNodeStmt {
virtual void addNextStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
};
struct AstBreak : public AstNodeStmt {
AstBreak(FileLine* fileline)
: AstNodeStmt (fileline) {}
ASTNODE_NODE_FUNCS(Break, BREAK)
virtual string verilogKwd() const { return "break"; };
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
};
struct AstContinue : public AstNodeStmt {
AstContinue(FileLine* fileline)
: AstNodeStmt (fileline) {}
ASTNODE_NODE_FUNCS(Continue, CONTINUE)
virtual string verilogKwd() const { return "continue"; };
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
};
struct AstReturn : public AstNodeStmt {
AstReturn(FileLine* fileline, AstNode* lhsp=NULL)
: AstNodeStmt (fileline) {
setNOp1p(lhsp);
}
ASTNODE_NODE_FUNCS(Return, RETURN)
virtual string verilogKwd() const { return "return"; };
virtual V3Hash sameHash() const { return V3Hash(); }
AstNode* lhsp() const { return op1p(); }
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
};
struct AstGenIf : public AstNodeIf {
AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
: AstNodeIf(fileline, condp, ifsp, elsesp) {
@@ -1901,6 +1958,53 @@ struct AstIf : public AstNodeIf {
ASTNODE_NODE_FUNCS(If, IF)
};
struct AstJumpLabel : public AstNodeStmt {
// Jump point declaration
// Separate from AstJumpGo; as a declaration can't be deleted
// Parents: {statement list}
// Children: {statement list, with JumpGo below}
private:
int m_labelNum; // Set by V3EmitCSyms to tell final V3Emit what to increment
public:
AstJumpLabel(FileLine* fl, AstNode* stmtsp)
: AstNodeStmt(fl) ,m_labelNum(0) {
addNOp1p(stmtsp);
}
virtual int instrCount() const { return 0; }
ASTNODE_NODE_FUNCS(JumpLabel, JUMPLABEL)
virtual bool maybePointedTo() const { return true; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
// op1 = Statements
AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
int labelNum() const { return m_labelNum; }
void labelNum(int flag) { m_labelNum=flag; }
};
struct AstJumpGo : public AstNodeStmt {
// Jump point; branch up to the JumpLabel
// Parents: {statement list}
private:
AstJumpLabel* m_labelp; // [After V3Jump] Pointer to declaration
public:
AstJumpGo(FileLine* fl, AstJumpLabel* labelp)
: AstNodeStmt(fl) {
m_labelp = labelp;
}
ASTNODE_NODE_FUNCS(JumpGo, JUMPGO)
virtual bool broken() const { return !labelp()->brokeExistsAbove(); }
virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep()->castJumpLabel(); }
virtual void dump(ostream& str);
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(labelp()); }
virtual bool same(AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
return labelp()==samep->castJumpGo()->labelp(); }
virtual bool isGateOptimizable() const { return false; }
virtual bool isSplittable() const { return false; } // SPECIAL: We don't process code after breaks
AstJumpLabel* labelp() const { return m_labelp; }
};
struct AstUntilStable : public AstNodeStmt {
// Quasi-while loop until given signals are stable
// Parents: CFUNC (generally)
-47
View File
@@ -135,53 +135,6 @@ 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(AstRepeat* nodep, AstNUser*) {
// So later optimizations don't need to deal with them,
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
// Note var can be signed or unsigned based on original number.
AstNode* countp = nodep->countp()->unlinkFrBackWithNext();
string name = string("__Vrepeat")+cvtToStr(m_repeatNum++);
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, AstLogicPacked(), countp->width());
m_modp->addStmtp(varp);
AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
countp);
AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
new AstConst(nodep->fileline(), 1)));
AstNode* condp;
if (countp->isSigned()) {
condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
new AstConst(nodep->fileline(), 0));
} else {
condp = new AstGt (nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
new AstConst(nodep->fileline(), 0));
}
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
bodysp = bodysp->addNext(decp);
AstNode* newp = new AstWhile(nodep->fileline(),
condp,
bodysp);
initsp = initsp->addNext(newp);
newp = initsp;
nodep->replaceWith(newp);
nodep->deleteTree(); nodep=NULL;
}
virtual void visit(AstScopeName* nodep, AstNUser*) {
// If there's a %m in the display text, we add a special node that will contain the name()
// Similar code in V3Inline
+24
View File
@@ -52,6 +52,7 @@ private:
static const int FLAG_IN_TREE = 0x02; // Is in netlist tree
static const int FLAG_LINKABLE = 0x04; // Is in netlist tree, can be linked to
static const int FLAG_LEAKED = 0x08; // Known to have been leaked
static const int FLAG_UNDER_NOW = 0x10; // Is in tree as parent of current node
public:
// METHODS
static void deleted(const AstNode* nodep) {
@@ -74,6 +75,15 @@ public:
s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED));
}
}
static void setUnder(const AstNode* nodep, bool flag) {
// Called by BrokenCheckVisitor when each node entered/exited
if (!okIfLinkedTo(nodep)) return;
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter!=s_nodes.end()) {
iter->second &= ~FLAG_UNDER_NOW;
if (flag) iter->second |= FLAG_UNDER_NOW;
}
}
static void addInTree(AstNode* nodep, bool linkable) {
#ifndef VL_LEAK_CHECKS
if (!linkable) return; // save some time, else the map will get huge!
@@ -111,6 +121,14 @@ public:
if (!(iter->second & FLAG_LINKABLE)) return false;
return true;
}
static bool okIfBelow(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static void prepForTree() {
#ifndef VL_LEAK_CHECKS
s_nodes.clear();
@@ -152,6 +170,10 @@ bool AstNode::brokeExists() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfLinkedTo(this);
}
bool AstNode::brokeExistsAbove() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfBelow(this);
}
//######################################################################
@@ -180,6 +202,7 @@ public:
class BrokenCheckVisitor : public AstNVisitor {
private:
virtual void visit(AstNode* nodep, AstNUser*) {
BrokenTable::setUnder(nodep,true);
if (nodep->broken()) {
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)\n");
}
@@ -192,6 +215,7 @@ private:
}
}
nodep->iterateChildren(*this);
BrokenTable::setUnder(nodep,false);
}
public:
// CONSTUCTORS
+3
View File
@@ -642,6 +642,9 @@ private:
virtual void visit(AstAlways* nodep, AstNUser*) {
iterateNewStmt(nodep);
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
// CDC doesn't care about public variables
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
iterateNewStmt(nodep);
}
+1 -2
View File
@@ -128,8 +128,7 @@ private:
virtual void visit(AstVarScope* nodep, AstNUser*) {
if (nodep->isCircular()) {
UINFO(8," CIRC "<<nodep<<endl);
if (!nodep->user1()) {
nodep->user1(true);
if (!nodep->user1Inc()) {
genChangeDet(nodep);
}
}
+1 -2
View File
@@ -704,8 +704,7 @@ class GaterVisitor : public GaterBaseVisitor {
virtual void visit(AstAlways* nodep, AstNUser*) {
if (debug()>=9) cout<<endl<<endl<<endl;
UINFO(5, "Gater: ALWAYS: "<<nodep<<endl);
if (nodep->user4()) return;
nodep->user4(1);
if (nodep->user4Inc()) return;
clear();
+27 -1
View File
@@ -96,6 +96,7 @@ private:
// ** only when m_warn/m_expensive is set. If state is needed other times,
// ** must track down everywhere V3Const is called and make sure no overlaps.
// AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
// STATE
bool m_params; // If true, propogate parameterized and true numbers only
@@ -1487,7 +1488,8 @@ private:
nodep->iterateChildren(*this);
if (nodep->condp()->isZero()) {
UINFO(4,"WHILE(0) => nop "<<nodep<<endl);
nodep->unlinkFrBack();
if (nodep->precondsp()) nodep->replaceWith(nodep->precondsp());
else nodep->unlinkFrBack();
nodep->deleteTree(); nodep=NULL;
}
else if (operandBoolShift(nodep->condp())) {
@@ -1499,6 +1501,30 @@ private:
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
virtual void visit(AstNodePreSel* nodep, AstNUser*) {}
//-----
// Jump elimination
virtual void visit(AstJumpGo* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (m_expensive) { nodep->labelp()->user4(true); }
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
// Because JumpLabels disable many optimizations,
// remove JumpLabels that are not pointed to by any AstJumpGos
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
nodep->iterateChildren(*this);
// AstJumpGo's below here that point to this node will set user4
if (m_expensive && !nodep->user4()) {
UINFO(4,"JUMPLABEL => unused "<<nodep<<endl);
AstNode* underp = NULL;
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
if (underp) nodep->replaceWith(underp);
else nodep->unlinkFrBack();
nodep->deleteTree(); nodep=NULL;
}
}
//-----
// Below lines are magic expressions processed by astgen
// "AstNODETYPE { # bracket not paren
+8 -11
View File
@@ -95,31 +95,31 @@ private:
// VISITORS
virtual void visit(AstCell* nodep, AstNUser*) {
nodep->iterateChildren(*this);
nodep->modp()->user1(nodep->modp()->user1() + 1);
nodep->modp()->user1Inc();
}
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->varScopep()) {
nodep->varScopep()->user1(nodep->varScopep()->user1() + 1);
nodep->varScopep()->varp()->user1(nodep->varScopep()->varp()->user1() + 1);
nodep->varScopep()->user1Inc();
nodep->varScopep()->varp()->user1Inc();
}
if (nodep->varp()) {
nodep->varp()->user1(nodep->varp()->user1() + 1);
nodep->varp()->user1Inc();
}
if (nodep->packagep()) {
nodep->packagep()->user1(nodep->packagep()->user1() + 1);
nodep->packagep()->user1Inc();
}
}
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->packagep()) {
nodep->packagep()->user1(nodep->packagep()->user1() + 1);
nodep->packagep()->user1Inc();
}
}
virtual void visit(AstRefDType* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->packagep()) {
nodep->packagep()->user1(nodep->packagep()->user1() + 1);
nodep->packagep()->user1Inc();
}
}
virtual void visit(AstVarScope* nodep, AstNUser*) {
@@ -145,13 +145,10 @@ private:
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*) {
if (nodep->isOutputter()) m_sideEffect=true;
nodep->iterateChildren(*this);
}
+18 -5
View File
@@ -149,6 +149,8 @@ public:
if (decind) ofp()->blockDec();
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAlwaysPublic*, AstNUser*) {
}
virtual void visit(AstCCall* nodep, AstNUser*) {
puts(nodep->hiername());
puts(nodep->funcp()->name());
@@ -360,12 +362,22 @@ public:
puts(")); }\n");
}
}
virtual void visit(AstJumpGo* nodep, AstNUser*) {
puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n");
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
puts("{\n");
nodep->stmtsp()->iterateAndNext(*this);
puts("}\n");
puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n");
}
virtual void visit(AstWhile* nodep, AstNUser*) {
nodep->precondsp()->iterateAndNext(*this);
puts("while (");
nodep->condp()->iterateAndNext(*this);
puts(") {\n");
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
puts("}\n");
}
@@ -531,17 +543,18 @@ public:
}
for (int word=VL_WORDS_I(nodep->num().minWidth())-1; word>0; word--) {
// Only 32 bits - llx + long long here just to appease CPP format warning
ofp()->printf(",0x%08" VL_PRI64 "x", (long long)(nodep->num().dataWord(word)));
ofp()->printf(",0x%08" VL_PRI64 "x", (vluint64_t)(nodep->num().dataWord(word)));
}
ofp()->printf(",0x%08" VL_PRI64 "x)", (long long)(nodep->num().dataWord(0)));
ofp()->printf(",0x%08" VL_PRI64 "x)", (vluint64_t)(nodep->num().dataWord(0)));
} else if (nodep->isQuad()) {
vluint64_t num = nodep->toUQuad();
if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "d)", (long long)num);
else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", (long long)num);
if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "d)", num);
else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", num);
} else {
uint32_t num = nodep->toUInt();
// Only 32 bits - llx + long long here just to appease CPP format warning
if (num<10) puts(cvtToStr(num));
else ofp()->printf("0x%" VL_PRI64 "x", (long long)num);
else ofp()->printf("0x%" VL_PRI64 "x", (vluint64_t)num);
//Unneeded-Causes %lx format warnings:
// if (!nodep->num().isSigned() && (num & (1UL<<31))) puts("U");
}
+141 -6
View File
@@ -45,13 +45,24 @@ class EmitCSyms : EmitCBaseVisitor {
AstUser1InUse m_inuser1;
// TYPES
struct ScopeNameData { string m_symName; string m_prettyName;
ScopeNameData(const string& symName, const string& prettyName)
: m_symName(symName), m_prettyName(prettyName) {}
};
struct ScopeFuncData { AstScopeName* m_scopep; AstCFunc* m_funcp; AstNodeModule* m_modp;
ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp)
: m_scopep(scopep), m_funcp(funcp), m_modp(modp) {}
};
struct ScopeVarData { string m_scopeName; string m_varBasePretty; AstVar* m_varp;
AstNodeModule* m_modp; AstScope* m_scopep;
ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, AstNodeModule* modp, AstScope* scopep)
: m_scopeName(scopeName), m_varBasePretty(varBasePretty), m_varp(varp), m_modp(modp), m_scopep(scopep) {}
};
typedef map<string,ScopeFuncData> ScopeFuncs;
typedef map<string,AstScopeName*> ScopeNames;
typedef map<string,ScopeVarData> ScopeVars;
typedef map<string,ScopeNameData> ScopeNames;
typedef pair<AstScope*,AstNodeModule*> ScopeModPair;
typedef pair<AstNodeModule*,AstVar*> ModVarPair;
struct CmpName {
inline bool operator () (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const {
return lhsp.first->name() < rhsp.first->name();
@@ -71,10 +82,13 @@ class EmitCSyms : EmitCBaseVisitor {
AstNodeModule* m_modp; // Current module
vector<ScopeModPair> m_scopes; // Every scope by module
vector<AstCFunc*> m_dpis; // DPI functions
vector<ModVarPair> m_modVars; // Each public {mod,var}
ScopeNames m_scopeNames; // Each unique AstScopeName
ScopeFuncs m_scopeFuncs; // Each {scope,dpiexportfunc}
ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func}
ScopeVars m_scopeVars; // Each {scope,public-var}
V3LanguageWords m_words; // Reserved word detector
int m_coverBins; // Coverage bin number
int m_labelNum; // Next label number
// METHODS
void emitSymHdr();
@@ -94,10 +108,65 @@ class EmitCSyms : EmitCBaseVisitor {
}
}
void varsExpand() {
// We didn'e have all m_scopes loaded when we encountered variables, so expand them now
// It would be less code if each module inserted its own variables.
// Someday. For now public isn't common.
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* smodp = it->second;
for (vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) {
AstNodeModule* modp = it->first;
if (modp == smodp) {
AstVar* varp = it->second;
// Need to split the module + var name into the original-ish full scope and variable name under that scope.
// The module instance name is included later, when we know the scopes this module is under
string whole = scopep->name()+"__DOT__"+varp->name();
string scpName;
string varBase;
if (whole.substr(0,10) == "__DOT__TOP") whole.replace(0,10,"");
string::size_type pos = whole.rfind("__DOT__");
if (pos != string::npos) {
scpName = whole.substr(0,pos);
varBase = whole.substr(pos+strlen("__DOT__"));
} else {
varBase = whole;
}
//UINFO(9,"For "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<" Var "<<varBase<<endl);
string varBasePretty = AstNode::prettyName(varBase);
string scpPretty = AstNode::prettyName(scpName);
string scpSym;
{
string out = scpName;
string::size_type pos;
while ((pos=out.find("__PVT__")) != string::npos) {
out.replace(pos, 7, "");
}
if (out.substr(0,10) == "TOP__DOT__") out.replace(0,10,"");
if (out.substr(0,4) == "TOP.") out.replace(0,4,"");
while ((pos=out.find(".")) != string::npos) {
out.replace(pos, 1, "__");
}
while ((pos=out.find("__DOT__")) != string::npos) {
out.replace(pos, 7, "__");
}
scpSym = out;
}
//UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl);
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
m_scopeNames.insert(make_pair(scpSym, ScopeNameData(scpSym, scpPretty)));
}
m_scopeVars.insert(make_pair(scpSym + " " + varp->name(),
ScopeVarData(scpSym, varBasePretty, varp, modp, scopep)));
}
}
}
}
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
// Collect list of scopes
nodep->iterateChildren(*this);
varsExpand();
// Sort by names, so line/process order matters less
sort(m_scopes.begin(), m_scopes.end(), CmpName());
@@ -114,6 +183,7 @@ class EmitCSyms : EmitCBaseVisitor {
virtual void visit(AstNodeModule* nodep, AstNUser*) {
nameCheck(nodep);
m_modp = nodep;
m_labelNum = 0;
nodep->iterateChildren(*this);
m_modp = NULL;
}
@@ -123,8 +193,9 @@ class EmitCSyms : EmitCBaseVisitor {
}
virtual void visit(AstScopeName* nodep, AstNUser*) {
string name = nodep->scopeSymName();
//UINFO(9,"scnameins sp "<<nodep->name()<<" sp "<<nodep->scopePrettyName()<<" ss "<<name<<endl);
if (m_scopeNames.find(name) == m_scopeNames.end()) {
m_scopeNames.insert(make_pair(name, nodep));
m_scopeNames.insert(make_pair(name, ScopeNameData(name, nodep->scopePrettyName())));
}
if (nodep->dpiExport()) {
if (!m_funcp) nodep->v3fatalSrc("ScopeName not under DPI function");
@@ -132,12 +203,22 @@ class EmitCSyms : EmitCBaseVisitor {
ScopeFuncData(nodep, m_funcp, m_modp)));
}
}
virtual void visit(AstVar* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->isSigUserRdPublic()) {
m_modVars.push_back(make_pair(m_modp, nodep));
}
}
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
// Assign numbers to all bins, so we know how big of an array to use
if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for
nodep->binNum(m_coverBins++);
}
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
nodep->labelNum(++m_labelNum);
nodep->iterateChildren(*this);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
m_dpis.push_back(nodep);
@@ -160,6 +241,7 @@ public:
m_funcp = NULL;
m_modp = NULL;
m_coverBins = 0;
m_labelNum = 0;
nodep->accept(*this);
}
};
@@ -248,7 +330,7 @@ void EmitCSyms::emitSymHdr() {
puts("\n// SCOPE NAMES\n");
for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) {
puts("VerilatedScope __Vscope_"+it->second->scopeSymName()+";\n");
puts("VerilatedScope __Vscope_"+it->second.m_symName+";\n");
}
puts("\n// CREATORS\n");
@@ -338,8 +420,8 @@ void EmitCSyms::emitSymImp() {
puts("// Setup scope names\n");
for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) {
puts("__Vscope_"+it->second->scopeSymName()+".configure(this,name(),");
putsQuoted(it->second->scopePrettyName());
puts("__Vscope_"+it->second.m_symName+".configure(this,name(),");
putsQuoted(it->second.m_prettyName);
puts(");\n");
}
@@ -360,6 +442,59 @@ void EmitCSyms::emitSymImp() {
puts("));\n");
}
}
// It would be less code if each module inserted its own variables.
// Someday. For now public isn't common.
for (ScopeVars::iterator it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) {
AstNodeModule* modp = it->second.m_modp;
AstScope* scopep = it->second.m_scopep;
AstVar* varp = it->second.m_varp;
//
int dim=0;
string bounds;
if (AstBasicDType* basicp = varp->basicp()) {
// Range is always first, it's not in "C" order
if (basicp->rangep()) {
bounds += " ,"; bounds += cvtToStr(basicp->rangep()->msbConst());
bounds += ","; bounds += cvtToStr(basicp->rangep()->lsbConst());
dim++;
}
for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstArrayDType* adtypep = dtypep->castArrayDType()) {
bounds += " ,"; bounds += cvtToStr(adtypep->arrayp()->msbConst());
bounds += ","; bounds += cvtToStr(adtypep->arrayp()->lsbConst());
dim++;
dtypep = adtypep->dtypep();
}
else break; // AstBasicDType - nothing below, 1
}
}
//
if (dim>2) {
puts("//UNSUP "); // VerilatedImp can't deal with >2d arrays
}
puts("__Vscope_"+it->second.m_scopeName+".varInsert(__Vfinal,");
putsQuoted(it->second.m_varBasePretty);
puts(", &(");
if (modp->isTop()) {
puts(scopep->nameDotless());
puts("p->");
} else {
puts(scopep->nameDotless());
puts(".");
}
puts(varp->name());
puts("), ");
puts(varp->vlEnumType()); // VLVT_UINT32 etc
puts(",");
puts(varp->vlEnumDir()); // VLVD_IN etc
if (varp->isSigUserRWPublic()) puts("|VLVF_PUB_RW");
else if (varp->isSigUserRdPublic()) puts("|VLVF_PUB_RD");
puts(",");
puts(cvtToStr(dim));
puts(bounds);
puts(");\n");
}
puts("}\n");
}
+30
View File
@@ -109,6 +109,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
nodep->bodysp()->iterateAndNext(*this);
putqs(nodep,"end\n");
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
putfs(nodep,"/*verilator public_flat_rw ");
if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active
else nodep->sensesp()->iterateAndNext(*this);
putqs(nodep," ");
nodep->bodysp()->iterateAndNext(*this);
putqs(nodep,"*/\n");
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
nodep->lhsp()->iterateAndNext(*this);
putfs(nodep," "+nodep->verilogKwd()+" ");
@@ -135,6 +143,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
nodep->rhsp()->iterateAndNext(*this);
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstBreak* nodep, AstNUser*) {
putbs("break");
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstSenTree* nodep, AstNUser*) {
// AstSenItem is called for dumping in isolation by V3Order
putfs(nodep,"@(");
@@ -180,6 +192,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
puts((string)"// "+nodep->name()+"\n");
nodep->iterateChildren(*this);
}
virtual void visit(AstContinue* nodep, AstNUser*) {
putbs("continue");
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstCoverDecl*, AstNUser*) {} // N/A
virtual void visit(AstCoverInc*, AstNUser*) {} // N/A
virtual void visit(AstCoverToggle*, AstNUser*) {} // N/A
@@ -236,6 +252,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
puts(");\n");
}
virtual void visit(AstJumpGo* nodep, AstNUser*) {
putbs("disable "+cvtToStr((void*)(nodep->labelp()))+";\n");
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
putbs("begin : "+cvtToStr((void*)(nodep))+"\n");
if (nodep->stmtsp()) nodep->stmtsp()->iterateChildren(*this);
puts("end\n");
}
virtual void visit(AstReadMem* nodep, AstNUser*) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
@@ -272,6 +296,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
nodep->condp()->iterateAndNext(*this);
puts(") begin\n");
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
putfs(nodep,"end\n");
}
@@ -287,6 +312,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
putqs(nodep,"end\n");
}
virtual void visit(AstReturn* nodep, AstNUser*) {
putfs(nodep,"return ");
nodep->lhsp()->iterateAndNext(*this);
puts(";\n");
}
virtual void visit(AstStop* nodep, AstNUser*) {
putfs(nodep,"$stop;\n");
}
+2 -1
View File
@@ -44,6 +44,7 @@ public:
// Boolean information we track per-line, but aren't errors
I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE)
// Error codes:
E_MULTITOP, // Error: Multiple top level modules
E_TASKNSVAR, // Error: Task I/O not simple
@@ -91,7 +92,7 @@ public:
// Leading spaces indicate it can't be disabled.
" MIN", " SUPPRESS", " INFO", " FATAL", " FATALSRC", " ERROR",
// Boolean
" I_COVERAGE", " I_TRACING",
" I_COVERAGE", " I_TRACING", " I_DEF_NETTYPE_WIRE",
// Errors
"MULTITOP", "TASKNSVAR", "BLKLOOPINIT",
// Warnings
+16
View File
@@ -47,6 +47,8 @@
class ExpandVisitor : public AstNVisitor {
private:
// NODE STATE
// AstNode::user1() -> bool. Processed
AstUser1InUse m_inuser1;
// STATE
AstNode* m_stmtp; // Current statement
@@ -76,12 +78,14 @@ private:
}
void insertBefore (AstNode* placep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
AstNRelinker linker;
placep->unlinkFrBack(&linker);
newp->addNext(placep);
linker.relink(newp);
}
void replaceWithDelete (AstNode* nodep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
nodep->replaceWith(newp);
nodep->deleteTree(); nodep=NULL;
}
@@ -306,6 +310,7 @@ private:
// VISITORS
virtual void visit(AstExtend* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(EXTEND)
@@ -344,6 +349,7 @@ private:
}
virtual void visit(AstSel* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
if (nodep->widthMin()!=(int)nodep->widthConst()) nodep->v3fatalSrc("Width mismatch");
@@ -640,6 +646,7 @@ private:
}
virtual void visit(AstConcat* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
@@ -679,6 +686,7 @@ private:
}
virtual void visit(AstReplicate* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
@@ -740,6 +748,7 @@ private:
}
virtual void visit(AstChangeXor* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
UINFO(8," Wordize ChangeXor "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
@@ -754,6 +763,7 @@ private:
}
void visitEqNeq(AstNodeBiop* nodep) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize EQ/NEQ "<<nodep<<endl);
@@ -779,6 +789,7 @@ private:
virtual void visit(AstNeq* nodep, AstNUser*) { visitEqNeq (nodep); }
virtual void visit(AstRedOr* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDOR "<<nodep<<endl);
@@ -802,6 +813,7 @@ private:
}
}
virtual void visit(AstRedAnd* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDAND "<<nodep<<endl);
@@ -830,6 +842,7 @@ private:
}
}
virtual void visit(AstRedXor* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDXOR "<<nodep<<endl);
@@ -840,6 +853,7 @@ private:
newp = (newp==NULL) ? eqp : (new AstXor (nodep->fileline(), newp, eqp));
}
newp = new AstRedXor (nodep->fileline(), newp);
UINFO(8," Wordize REDXORnew "<<newp<<endl);
replaceWithDelete(nodep, newp); nodep=NULL;
}
// We don't reduce non-wide XORs, as its more efficient to use a temp register,
@@ -847,11 +861,13 @@ private:
}
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
m_stmtp = nodep;
nodep->iterateChildren(*this);
m_stmtp = NULL;
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
if (nodep->user1Inc()) return; // Process once
m_stmtp = nodep;
nodep->iterateChildren(*this);
bool did = false;
+47 -24
View File
@@ -44,6 +44,8 @@
#include "V3PreShell.h"
#include "V3Ast.h"
// If change this code, run a test with the below size set very small
//#define INFILTER_IPC_BUFSIZ 16
#define INFILTER_IPC_BUFSIZ 64*1024 // For debug, try this as a small number
#define INFILTER_CACHE_MAX 64*1024 // Maximum bytes to cache if same file read twice
@@ -271,6 +273,7 @@ void V3File::createMakeDir() {
class V3InFilterImp {
typedef map<string,string> FileContentsMap;
typedef V3InFilter::StrList StrList;
FileContentsMap m_contentsMap; // Cache of file contents
bool m_readEof; // Received EOF on read
@@ -292,27 +295,27 @@ private:
return level;
}
bool readContents(const string& filename, string& out) {
if (m_pid) return readContentsFilter(filename,out);
else return readContentsFile(filename,out);
bool readContents(const string& filename, StrList& outl) {
if (m_pid) return readContentsFilter(filename,outl);
else return readContentsFile(filename,outl);
}
bool readContentsFile(const string& filename, string& out) {
bool readContentsFile(const string& filename, StrList& outl) {
int fd = open (filename.c_str(), O_RDONLY);
if (!fd) return false;
m_readEof = false;
out = readBlocks(fd, -1);
readBlocks(fd, -1, outl);
close(fd);
return true;
}
bool readContentsFilter(const string& filename, string& out) {
if (filename!="" || out!="") {} // Prevent unused
bool readContentsFilter(const string& filename, StrList& outl) {
if (filename!="" || outl.empty()) {} // Prevent unused
#ifdef INFILTER_PIPE
writeFilter("read \""+filename+"\"\n");
string line = readFilterLine();
if (line.find("Content-Length") != string::npos) {
int len = 0;
sscanf(line.c_str(), "Content-Length: %d\n", &len);
out = readBlocks(m_readFd, len);
readBlocks(m_readFd, len, outl);
return true;
} else {
if (line!="") v3error("--pipe-filter protocol error, unexpected: "<<line);
@@ -334,15 +337,19 @@ private:
#endif
}
string readBlocks(int fd, int size=-1) {
string readBlocks(int fd, int size, StrList& outl) {
string out;
char buf[INFILTER_IPC_BUFSIZ];
while (!m_readEof && (size<0 || size>(int)out.length())) {
int todo = INFILTER_IPC_BUFSIZ;
if (size>0 && size<INFILTER_IPC_BUFSIZ) todo = size;
int got = read (fd, buf, todo);
ssize_t sizegot = 0;
while (!m_readEof && (size<0 || size>sizegot)) {
ssize_t todo = INFILTER_IPC_BUFSIZ;
if (size>0 && size<todo) todo = size;
ssize_t got = read (fd, buf, todo);
//UINFO(9,"RD GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl); usleep(50*1000);
if (got>0) out.append(buf, got);
if (got>0) {
outl.push_back(string(buf, got));
sizegot += got;
}
else if (errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
@@ -358,9 +365,11 @@ private:
UINFO(9,"readFilterLine\n");
string line;
while (!m_readEof) {
string c = readBlocks(m_readFd, 1);
line += c;
if (c == "\n") {
StrList outl;
readBlocks(m_readFd, 1, outl);
string onechar = listString(outl);
line += onechar;
if (onechar == "\n") {
if (line == "\n") { line=""; continue; }
else break;
}
@@ -477,21 +486,35 @@ private:
protected:
friend class V3InFilter;
// Read file contents and return it
bool readWholefile(const string& filename, string& out) {
bool readWholefile(const string& filename, StrList& outl) {
FileContentsMap::iterator it = m_contentsMap.find(filename);
if (it != m_contentsMap.end()) {
out = it->second;
outl.push_back(it->second);
return true;
}
if (!readContents(filename, out)) return false;
if (out.length() < INFILTER_CACHE_MAX) {
if (!readContents(filename, outl)) return false;
if (listSize(outl) < INFILTER_CACHE_MAX) {
// Cache small files (only to save space)
// It's quite common to `include "timescale" thousands of times
// This isn't so important if it's just a open(), but filtering can be slow
m_contentsMap.insert(make_pair(filename,out));
m_contentsMap.insert(make_pair(filename,listString(outl)));
}
return true;
}
size_t listSize(StrList& sl) {
size_t out = 0;
for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) {
out += it->length();
}
return out;
}
string listString(StrList& sl) {
string out;
for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) {
out += *it;
}
return out;
}
// CONSTRUCTORS
V3InFilterImp(const string& command) {
m_readEof = false;
@@ -512,9 +535,9 @@ protected:
V3InFilter::V3InFilter(const string& command) { m_impp = new V3InFilterImp(command); }
V3InFilter::~V3InFilter() { if (m_impp) delete m_impp; m_impp=NULL; }
bool V3InFilter::readWholefile(const string& filename, string& out) {
bool V3InFilter::readWholefile(const string& filename, V3InFilter::StrList& outl) {
if (!m_impp) v3fatalSrc("readWholefile on invalid filter");
return m_impp->readWholefile(filename, out);
return m_impp->readWholefile(filename, outl);
}
//######################################################################
+5 -1
View File
@@ -28,6 +28,7 @@
#include <cstdio>
#include <stack>
#include <set>
#include <list>
#include <fstream>
//============================================================================
@@ -79,9 +80,12 @@ class V3InFilterImp;
class V3InFilter {
V3InFilterImp* m_impp;
public:
// TYPES
typedef list<string> StrList;
// METHODS
// Read file contents and return it. Return true on success.
bool readWholefile(const string& filename, string& out);
bool readWholefile(const string& filename, StrList& outl);
// CONSTRUCTORS
V3InFilter(const string& command);
+6
View File
@@ -357,6 +357,12 @@ private:
virtual void visit(AstAlways* nodep, AstNUser*) {
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL);
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
bool lastslow = m_inSlow;
m_inSlow = true;
iterateNewStmt(nodep, "AlwaysPublic", NULL);
m_inSlow = lastslow;
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
iterateNewStmt(nodep, "User C Function", "User C Function");
}
+19 -3
View File
@@ -57,6 +57,7 @@ private:
// AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor)
// Cleared each cell
// AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to
// AstVar::user3() // bool Don't alias the user4, keep it as signal
// STATE
AstNodeModule* m_modp; // Current module
@@ -151,6 +152,10 @@ private:
UINFO(6,"One-to-one "<<connectRefp<<endl);
UINFO(6," -to "<<pinNewVarp<<endl);
pinNewVarp->user2p(connectRefp);
// Public output inside the cell must go via an assign rather than alias
// Else the public logic will set the alias, loosing the value to be propagated up
// (InOnly isn't a problem as the AssignAlias will create the assignment for us)
pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly());
}
// Cleanup var names, etc, to not conflict
m_cellp = nodep;
@@ -175,6 +180,7 @@ private:
// user2p is either a const or a var.
AstConst* exprconstp = nodep->user2p()->castNode()->castConst();
AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef();
UINFO(1,"connectto: "<<nodep->user2p()->castNode()<<endl);
if (!exprconstp && !exprvarrefp) {
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n");
}
@@ -182,6 +188,15 @@ private:
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
new AstVarRef(nodep->fileline(), nodep, true),
exprconstp->cloneTree(true)));
} else if (nodep->user3()) {
// Public variable at the lower module end - we need to make sure we propagate
// the logic changes up and down; if we aliased, we might remove the change detection
// on the output variable.
UINFO(9,"public pin assign: "<<exprvarrefp<<endl);
if (nodep->isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias");
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true),
new AstVarRef(nodep->fileline(), nodep, false)));
} else {
m_modp->addStmtp(new AstAssignAlias(nodep->fileline(),
new AstVarRef(nodep->fileline(), nodep, true),
@@ -216,8 +231,9 @@ private:
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (m_cellp) {
if (nodep->varp()->user2p() // It's being converted to a alias.
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases
if (nodep->varp()->user2p() // It's being converted to an alias.
&& !nodep->varp()->user3()
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made)
AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst();
AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef();
if (exprconstp) {
@@ -355,7 +371,7 @@ private:
m_modp = NULL;
}
virtual void visit(AstCell* nodep, AstNUser*) {
nodep->modp()->user3( nodep->modp()->user3() + 1);
nodep->modp()->user3Inc();
nodep->iterateChildren(*this);
}
virtual void visit(AstPragma* nodep, AstNUser*) {
+1 -2
View File
@@ -89,8 +89,7 @@ private:
if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst())
nodep->v3error("Output port is connected to a constant pin, electrical short");
// Use user1p on the PIN to indicate we created an assign for this pin
if (!nodep->user1()) {
nodep->user1(1);
if (!nodep->user1Inc()) {
// Simplify it
V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp);
// Make a ASSIGNW (expr, pin)
+23 -1
View File
@@ -288,6 +288,7 @@ private:
// STATE
LifeState* m_statep; // Current state
bool m_sideEffect; // Side effects discovered in assign RHS
bool m_noopt; // Disable optimization of variables in this block
// LIFE MAP
// For each basic block, we'll make a new map of what variables that if/else is changing
@@ -327,7 +328,7 @@ private:
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
}
// Has to be direct assignment without any EXTRACTing.
if (nodep->lhsp()->castVarRef() && !m_sideEffect) {
if (nodep->lhsp()->castVarRef() && !m_sideEffect && !m_noopt) {
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
m_lifep->simpleAssign(vscp, nodep);
@@ -388,6 +389,7 @@ private:
{
m_lifep = bodyLifep;
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
m_lifep = prevLifep;
}
UINFO(4," joinfor"<<endl);
@@ -397,6 +399,25 @@ private:
delete condLifep;
delete bodyLifep;
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
// As with While's we can't predict if a JumpGo will kill us or not
// It's worse though as an IF(..., JUMPGO) may change the control flow.
// Just don't optimize blocks with labels; they're rare - so far.
LifeBlock* prevLifep = m_lifep;
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
bool prev_noopt = m_noopt;
{
m_lifep = bodyLifep;
m_noopt = true;
nodep->stmtsp()->iterateAndNext(*this);
m_lifep = prevLifep;
m_noopt = prev_noopt;
}
UINFO(4," joinjump"<<endl);
// For the next assignments, clear any variables that were read or written in the block
bodyLifep->lifeToAbove();
delete bodyLifep;
}
virtual void visit(AstCCall* nodep, AstNUser*) {
//UINFO(4," CCALL "<<nodep<<endl);
nodep->iterateChildren(*this);
@@ -432,6 +453,7 @@ public:
UINFO(4," LifeVisitor on "<<nodep<<endl);
m_statep = statep;
m_sideEffect = false;
m_noopt = false;
{
m_lifep = new LifeBlock (NULL, m_statep);
nodep->accept(*this);
+7 -1
View File
@@ -185,7 +185,13 @@ private:
// Create implicit after warning
if (linkVarName(forrefp)) { forrefp=NULL; return; }
if (!forrefp->varp()) {
if (!noWarn) forrefp->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<<forrefp->prettyName());
if (!noWarn) {
if (forrefp->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
forrefp->v3error("Signal definition not found, and implicit disabled with `default_nettype: "<<forrefp->prettyName());
} else {
forrefp->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<<forrefp->prettyName());
}
}
AstVar* newp = new AstVar (forrefp->fileline(), AstVarType::WIRE,
forrefp->name(), AstLogicPacked(), 1);
+227
View File
@@ -0,0 +1,227 @@
//*************************************************************************
// DESCRIPTION: Verilator: Replace return/continue with jumps
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2010 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3LinkJump's Transformations:
//
// Each module:
// Look for BEGINs
// BEGIN(VAR...) -> VAR ... {renamed}
// FOR -> WHILEs
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3LinkJump.h"
#include "V3Ast.h"
//######################################################################
class LinkJumpVisitor : public AstNVisitor {
private:
// TYPES
typedef vector<AstBegin*> BeginStack;
// STATE
AstModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Current function/task
AstWhile* m_loopp; // Current loop
int m_repeatNum; // Repeat counter
BeginStack m_beginStack; // All begin blocks above current node
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
// Put label under given node, and if WHILE optionally at end of iteration
UINFO(4,"Create label for "<<nodep<<endl);
if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done
AstNode* underp = NULL;
bool under_and_next = true;
if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp();
else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp();
else if (nodep->castWhile()) {
if (endOfIter) {
// Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip
underp = nodep->castWhile()->bodysp();
} else {
underp = nodep; under_and_next=false; // IE we skip the entire while
}
}
else {
nodep->v3fatalSrc("Unknown jump point for break/disable/continue");
return NULL;
}
if (!underp) {
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
return NULL;
} else if (underp->castJumpLabel()) {
return underp->castJumpLabel();
} else { // Move underp stuff to be under a new label
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
AstNRelinker repHandle;
if (under_and_next) underp->unlinkFrBackWithNext(&repHandle);
else underp->unlinkFrBack(&repHandle);
repHandle.relink(labelp);
labelp->addStmtsp(underp);
// Keep any AstVars under the function not under the new JumpLabel
for (AstNode* nextp, *varp=underp; varp; varp = nextp) {
nextp = varp->nextp();
if (varp->castVar()) {
labelp->addPrev(varp->unlinkFrBack());
}
}
return labelp;
}
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
m_repeatNum = 0;
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
m_ftaskp = nodep;
nodep->iterateChildren(*this);
m_ftaskp = NULL;
}
virtual void visit(AstBegin* nodep, AstNUser*) {
UINFO(8," "<<nodep<<endl);
m_beginStack.push_back(nodep);
nodep->iterateChildren(*this);
m_beginStack.pop_back();
}
virtual void visit(AstRepeat* nodep, AstNUser*) {
// So later optimizations don't need to deal with them,
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
// Note var can be signed or unsigned based on original number.
AstNode* countp = nodep->countp()->unlinkFrBackWithNext();
string name = string("__Vrepeat")+cvtToStr(m_repeatNum++);
// Spec says value is integral, if negative is ignored
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name,
AstLogicPacked(), 32);
varp->isSigned(true);
varp->dtypep()->isSigned(true);
m_modp->addStmtp(varp);
AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
countp);
AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
new AstConst(nodep->fileline(), 1)));
AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->isSigned(true);
AstNode* condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
zerosp);
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
AstNode* newp = new AstWhile(nodep->fileline(),
condp,
bodysp,
decp);
initsp = initsp->addNext(newp);
newp = initsp;
nodep->replaceWith(newp);
nodep->deleteTree(); nodep=NULL;
}
virtual void visit(AstWhile* nodep, AstNUser*) {
// Don't need to track AstRepeat/AstFor as they have already been converted
AstWhile* lastLoopp = m_loopp;
m_loopp = nodep;
nodep->iterateChildren(*this);
m_loopp = lastLoopp;
}
virtual void visit(AstReturn* nodep, AstNUser*) {
nodep->iterateChildren(*this);
AstFunc* funcp = m_ftaskp->castFunc();
if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); }
else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); }
else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); }
else {
if (funcp && nodep->lhsp()) {
// Set output variable to return value
nodep->addPrev(new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), funcp->fvarp()->castVar(), true),
nodep->lhsp()->unlinkFrBackWithNext()));
}
// Jump to the end of the function call
AstJumpLabel* labelp = findAddLabel(m_ftaskp, false);
nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp));
}
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstBreak* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); }
else {
// Jump to the end of the loop
AstJumpLabel* labelp = findAddLabel(m_loopp, false);
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
}
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstContinue* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); }
else {
// Jump to the end of this iteration
// If a "for" loop then need to still do the post-loop increment
AstJumpLabel* labelp = findAddLabel(m_loopp, true);
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
}
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
LinkJumpVisitor(AstNetlist* nodep) {
m_modp = NULL;
m_ftaskp = NULL;
m_loopp = NULL;
m_repeatNum = 0;
nodep->accept(*this);
}
virtual ~LinkJumpVisitor() {}
};
//######################################################################
// Task class functions
void V3LinkJump::linkJump(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
LinkJumpVisitor bvisitor (nodep);
}
+37
View File
@@ -0,0 +1,37 @@
// -*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Replace return/continue with jumps
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2010 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3LINKJUMP_H_
#define _V3LINKJUMP_H_ 1
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3LinkJump {
public:
static void linkJump(AstNetlist* nodep);
};
#endif // Guard
+30 -10
View File
@@ -77,8 +77,7 @@ private:
// VISITs
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
if (!nodep->user1()) {
nodep->user1(true); // Process only once.
if (!nodep->user1Inc()) { // Process only once.
UINFO(5," "<<nodep<<endl);
checkExpected(nodep);
// Due to a need to get the arguments, the ParseRefs are under here,
@@ -165,8 +164,7 @@ private:
}
}
virtual void visit(AstSelBit* nodep, AstNUser*) {
if (!nodep->user1()) {
nodep->user1(true); // Process only once.
if (!nodep->user1Inc()) { // Process only once.
if (m_inModDot) { // Already under dot, so this is {modulepart} DOT {modulepart}
m_dotText = "";
nodep->lhsp()->iterateAndNext(*this);
@@ -194,8 +192,7 @@ private:
}
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
// Excludes simple AstSel, see above
if (!nodep->user1()) {
nodep->user1(true); // Process only once.
if (!nodep->user1Inc()) { // Process only once.
if (m_inModDot) { // Already under dot, so this is {modulepart} DOT {modulepart}
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference");
} else if (m_exp==AstParseRefExp::PX_FUNC) {
@@ -217,8 +214,7 @@ private:
}
}
virtual void visit(AstText* nodep, AstNUser*) {
if (!nodep->user1()) {
nodep->user1(true); // Process only once.
if (!nodep->user1Inc()) { // Process only once.
if (m_exp != AstParseRefExp::PX_NONE) {
UINFO(7," "<<nodep<<endl);
if (m_inModDot) { // Dotted part, just pass up
@@ -274,12 +270,22 @@ private:
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigPublic(true); m_varp->sigModPublic(true);
m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigPublic(true);
m_varp->sigUserRWPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRdPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) {
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
m_varp->sigUserRWPublic(true);
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) {
@@ -294,6 +300,20 @@ private:
}
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
// AlwaysPublic was attached under a var, but it's a statement that should be
// at the same level as the var
nodep->iterateChildren(*this);
if (m_varp) {
nodep->unlinkFrBack();
m_varp->addNext(nodep);
// lvalue is true, because we know we have a verilator public_flat_rw
// but someday we may be more general
bool lvalue = m_varp->isSigUserRWPublic();
nodep->addStmtp(new AstVarRef(nodep->fileline(), m_varp, lvalue));
}
}
virtual void visit(AstDefImplicitDType* nodep, AstNUser*) {
UINFO(8," DEFIMPLICIT "<<nodep<<endl);
// Must remember what names we've already created, and combine duplicates
+7
View File
@@ -75,6 +75,13 @@ private:
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstInitial* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Initial assignments under function/tasks can just be simple assignments without the initial
if (m_ftaskp) {
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); nodep=NULL;
}
}
virtual void visit(AstVAssert* nodep, AstNUser*) {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
m_assertp = nodep;
+1 -2
View File
@@ -109,8 +109,7 @@ private:
}
}
virtual void visit(AstScope* nodep, AstNUser*) {
if (!nodep->user1()) {
nodep->user1(1);
if (!nodep->user1Inc()) {
if (nodep->aboveScopep()) nodep->aboveScopep()->iterate(*this);
if (nodep->aboveCellp()) nodep->aboveCellp()->iterate(*this);
// Always recompute name (as many level above scope may have changed)
+1 -1
View File
@@ -1023,7 +1023,7 @@ V3Options::V3Options() {
m_outputSplitCTrace = 0;
m_traceDepth = 0;
m_unrollCount = 64;
m_unrollStmts = 100;
m_unrollStmts = 30000;
m_compLimitParens = 0;
m_compLimitBlocks = 0;
+42 -16
View File
@@ -375,7 +375,7 @@ private:
OrderLoopId processMoveLoopCurrent();
string cfuncName(AstNodeModule* modp, AstSenTree* domainp, AstScope* scopep, AstNode* forWhatp) {
modp->user3(1+modp->user3());
modp->user3Inc();
int funcnum = modp->user3();
string name = (domainp->hasCombo() ? "_combo"
: (domainp->hasInitial() ? "_initial"
@@ -666,6 +666,9 @@ private:
iterateNewStmt(nodep);
m_inPost = false;
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
iterateNewStmt(nodep);
}
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
iterateNewStmt(nodep);
}
@@ -941,28 +944,51 @@ void OrderVisitor::processBrokeLoop() {
// Clock propagation
void OrderVisitor::processInputs() {
m_graph.userClearVertices(); // Vertex::user() // true if added as begin/end
m_graph.userClearVertices(); // Vertex::user() // true if processed
// Start at input vertex, process from input-to-output order
m_inputsVxp->isFromInput(true); // By definition
processInputsIterate(m_inputsVxp);
}
void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) {
// Propagate PrimaryIn through simple assignments
if (vertexp->user()) return; // Already processed
//UINFO(9," InIt "<<vertexp<<endl);
vertexp->user(true);
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(vertexp)) {
vvertexp->isFromInput(true);
}
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top();
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(toVertexp)) {
processInputsIterate(vvertexp);
if (0 && debug()>=9) {
UINFO(9," InIt "<<vertexp<<endl);
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
vvertexp->nodep()->dumpTree(cout,"- TT: ");
}
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(toVertexp)) {
if (AstNodeAssign* nodep = vvertexp->nodep()->castNodeAssign()) {
if (nodep->lhsp()->castVarRef()
&& nodep->rhsp()->castVarRef()) {
UINFO(9," Input reassignment: "<<vvertexp<<endl);
}
vertexp->user(true); // Processing
// First handle all inputs to this vertex, in most cases they'll be already processed earlier
// Also, determine if this vertex is an input
int inonly = 1; // 0=no, 1=maybe, 2=yes until a no
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
OrderEitherVertex* frVertexp = (OrderEitherVertex*)edgep->fromp();
processInputsIterate(frVertexp);
if (frVertexp->isFromInput()) {
if (inonly==1) inonly = 2;
} else if (dynamic_cast<OrderVarPostVertex*>(frVertexp)) {
// Ignore post assignments, just for ordering
} else {
//UINFO(9," InItStopDueTo "<<frVertexp<<endl);
inonly = 0;
break;
}
}
if (inonly == 2) { // Set it. Note may have already been set earlier, too
UINFO(9," Input reassignment: "<<vertexp<<endl);
vertexp->isFromInput(true);
}
// If we're still an input, process all targets of this vertex
if (vertexp->isFromInput()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top();
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(toVertexp)) {
processInputsIterate(vvertexp);
}
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(toVertexp)) {
if (vvertexp->nodep()->castNodeAssign()) {
processInputsIterate(vvertexp);
}
}
+8 -6
View File
@@ -132,10 +132,11 @@ class OrderEitherVertex : public V3GraphVertex {
AstScope* m_scopep; // Scope the vertex is in
AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate)
OrderLoopId m_inLoop; // Loop number vertex is in
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
public:
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
: V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp)
, m_inLoop(LOOPID_UNKNOWN) {
, m_inLoop(LOOPID_UNKNOWN), m_isFromInput(false) {
}
virtual ~OrderEitherVertex() {}
// Methods
@@ -148,12 +149,16 @@ public:
AstSenTree* domainp() const { return m_domainp; }
OrderLoopId inLoop() const { return m_inLoop; }
void inLoop(OrderLoopId inloop) { m_inLoop = inloop; }
void isFromInput(bool flag) { m_isFromInput=flag; }
bool isFromInput() const { return m_isFromInput; }
};
class OrderInputsVertex : public OrderEitherVertex {
public:
OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp)
: OrderEitherVertex(graphp, NULL, domainp) {}
: OrderEitherVertex(graphp, NULL, domainp) {
isFromInput(true); // By definition
}
virtual ~OrderInputsVertex() {}
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; }
virtual string name() const { return "*INPUTS*"; }
@@ -195,11 +200,10 @@ class OrderVarVertex : public OrderEitherVertex {
AstVarScope* m_varScp;
OrderVarVertex* m_pilNewVertexp; // for processInsLoopNewVar
bool m_isClock; // Used as clock
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
public:
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp)
, m_pilNewVertexp(NULL), m_isClock(false), m_isFromInput(false)
, m_pilNewVertexp(NULL), m_isClock(false)
{}
virtual ~OrderVarVertex() {}
virtual OrderVarVertex* clone (V3Graph* graphp) const = 0;
@@ -208,8 +212,6 @@ public:
AstVarScope* varScp() const { return m_varScp; }
void isClock(bool flag) { m_isClock=flag; }
bool isClock() const { return m_isClock; }
void isFromInput(bool flag) { m_isFromInput=flag; }
bool isFromInput() const { return m_isFromInput; }
OrderVarVertex* pilNewVertexp() const { return m_pilNewVertexp; }
void pilNewVertexp (OrderVarVertex* vertexp) { m_pilNewVertexp = vertexp; }
};
+3 -3
View File
@@ -66,12 +66,12 @@ V3ParseImp::~V3ParseImp() {
if (debug()>=9) { UINFO(0,"~V3ParseImp\n"); symp()->dump(cout, "-vpi: "); }
}
int V3ParseImp::ppInputToLex(char* buf, int max_size) {
int got = 0;
size_t V3ParseImp::ppInputToLex(char* buf, size_t max_size) {
size_t got = 0;
while (got < max_size // Haven't got enough
&& !m_ppBuffers.empty()) { // And something buffered
string front = m_ppBuffers.front(); m_ppBuffers.pop_front();
int len = front.length();
size_t len = front.length();
if (len > (max_size-got)) { // Front string too big
string remainder = front.substr(max_size-got);
front = front.substr(0, max_size-got);
+3 -3
View File
@@ -260,7 +260,7 @@ public:
static const char* tokenName(int tok);
void ppPushText(const string& text) { m_ppBuffers.push_back(text); }
int ppInputToLex(char* buf, int max_size);
size_t ppInputToLex(char* buf, size_t max_size);
static V3ParseImp* parsep() { return s_parsep; }
@@ -278,7 +278,7 @@ public:
m_stringps.push_back(strp);
return strp;
}
string* newString(const char* text, int length) {
string* newString(const char* text, size_t length) {
string* strp = new string (text, length);
m_stringps.push_back(strp);
return strp;
@@ -307,7 +307,7 @@ public:
void statePushVlg(); // Parser -> lexer communication
void statePop(); // Parser -> lexer communication
int stateVerilogRecent(); // Parser -> lexer communication
int flexPpInputToLex(char* buf, int max_size) { return ppInputToLex(buf,max_size); }
size_t flexPpInputToLex(char* buf, size_t max_size) { return ppInputToLex(buf,max_size); }
//==== Symbol tables
V3ParseSym* symp() { return &m_sym; }
+23 -5
View File
@@ -27,6 +27,7 @@
#ifndef _VPREPROCLEX_H_ // Guard
#define _VPREPROCLEX_H_ 1
#include <deque>
#include <stack>
#include "V3Error.h"
@@ -84,6 +85,11 @@
# define yyerrorf V3PreLexerrorf
#endif
#ifndef yyourleng
# define yyourleng V3PreLexourleng
# define yyourtext V3PreLexourtext
#endif
#ifndef YY_BUFFER_STATE
struct yy_buffer_state;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
@@ -92,23 +98,30 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern int yylex();
extern void yyrestart(FILE*);
extern char* yytext;
extern int yyleng;
// Accessors, because flex keeps changing the type of yyleng
extern char* yyourtext();
extern size_t yyourleng();
extern void yyourtext(const char* textp, size_t size); // Must call with static
YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size );
void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer );
void yy_delete_buffer( YY_BUFFER_STATE b );
//======================================================================
// Class entry for each per-lexter state
#define KEEPCMT_SUB 2
//======================================================================
// Class entry for each per-lexer state
class V3PreLex {
public: // Used only by V3PreLex.cpp and V3PreProc.cpp
FileLine* m_curFilelinep; // Current processing point
// Parse state
stack<YY_BUFFER_STATE> m_bufferStack; // Stack of inserted text above current point
deque<string> m_buffers; ///< Buffer of characters to process
// State to lexer
static V3PreLex* s_currentLexp; // Current lexing point
@@ -132,13 +145,15 @@ class V3PreLex {
m_defCmtSlash = false;
m_pslParenLevel = 0;
m_pslMoreNeeded = false;
initFirstBuffer();
}
~V3PreLex() {
while (!m_bufferStack.empty()) { yy_delete_buffer(m_bufferStack.top()); m_bufferStack.pop(); }
}
void initFirstBuffer();
// Called by V3PreLex.l from lexer
void appendDefValue(const char* text, int len);
void appendDefValue(const char* text, size_t len);
void lineDirective(const char* text);
void incLineno() { m_curFilelinep->incLineno(); }
// Called by V3PreProc.cpp to inform lexer
@@ -146,10 +161,13 @@ class V3PreLex {
void pushStateDefForm();
void pushStateDefValue();
void pushStateIncFilename();
void scanBytes(const string& strg);
void scanBytes(const char* strp, size_t len);
void scanBytesBack(const string& str);
size_t inputToLex(char* buf, size_t max_size);
/// Called by VPreproc.cpp to get data from lexer
YY_BUFFER_STATE currentBuffer();
int currentStartState();
void dumpSummary();
void dumpStack();
};
+69 -4
View File
@@ -33,13 +33,21 @@ V3PreLex* V3PreLex::s_currentLexp = NULL; // Current lexing point
#define LEXP V3PreLex::s_currentLexp
#define YY_INPUT(buf,result,max_size) \
result = LEXP->inputToLex(buf,max_size);
// Accessors, because flex keeps changing the type of yyleng
char* yyourtext() { return yytext; }
size_t yyourleng() { return yyleng; }
void yyourtext(const char* textp, size_t size) { yytext=(char*)textp; yyleng=size; }
// Prevent conflicts from perl version
static void linenoInc() {LEXP->incLineno();}
static bool optPsl() { return V3PreProc::optPsl(); }
static bool pedantic() { return LEXP->m_pedantic; }
static void yyerror(char* msg) { LEXP->m_curFilelinep->v3error(msg); }
static void yyerrorf(const char* msg) { LEXP->m_curFilelinep->v3error(msg); }
static void appendDefValue(const char* t,int l) { LEXP->appendDefValue(t,l); }
static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t,l); }
static int pslParenLevel() { return LEXP->m_pslParenLevel; }
static void pslParenLevelInc() { LEXP->m_pslParenLevel++; }
static void pslParenLevelDec() { if (pslParenLevel()) LEXP->m_pslParenLevel--; }
@@ -73,6 +81,7 @@ crnl [\r]*[\n]
quote [\"]
backslash [\\]
symb ([a-zA-Z_][a-zA-Z0-9_$]*|\\[^ \t\f\r\n]+)
word [a-zA-Z0-9_]+
drop [\032]
psl [p]sl
@@ -108,6 +117,7 @@ psl [p]sl
<INITIAL,PSLMULM,PSLONEM>{quote} { yy_push_state(STRMODE); yymore(); }
<STRMODE><<EOF>> { linenoInc(); yyerrorf("EOF in unterminated string"); yyleng=0; yyterminate(); }
<STRMODE>{crnl} { linenoInc(); yyerrorf("Unterminated string"); BEGIN(INITIAL); }
<STRMODE>{word} { yymore(); }
<STRMODE>[^\"\\] { yymore(); }
<STRMODE>{backslash}{crnl} { linenoInc(); yymore(); }
<STRMODE>{backslash}. { yymore(); }
@@ -154,6 +164,7 @@ psl [p]sl
/* Reading definition value */
<DEFVAL>"/*" { LEXP->m_defCmtSlash=false; yy_push_state(DEFCMT); yymore(); } /* Special comment parser */
<DEFVAL>"//"[^\n\r]*[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Spec says // not part of define value */
<DEFVAL>"//"[^\n\r]* { return (VP_COMMENT);}
<DEFVAL>{drop} { }
<DEFVAL><<EOF>> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */
@@ -171,6 +182,7 @@ psl [p]sl
appendDefValue(yytext,yyleng-2); appendDefValue((char*)"\n",1); } /* Return but not \ */
<DEFCMT>{crnl} { linenoInc(); yymore(); if (LEXP->m_defCmtSlash) yyerrorf("One line of /* ... */ is missing \\ before newline");
BEGIN(CMTMODE); }
<DEFCMT>{word} { yymore(); }
<DEFCMT>. { yymore(); }
<DEFCMT><<EOF>> { yyerrorf("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); }
@@ -235,6 +247,7 @@ psl [p]sl
<CMTBEGM,CMTMODE>"*/" { yy_pop_state(); return(VP_COMMENT); }
<CMTBEGM,CMTMODE>{crnl} { linenoInc(); yymore(); }
<CMTBEGM,CMTMODE><<EOF>> { yyerrorf("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); }
<CMTMODE>{word} { yymore(); }
<CMTBEGM>. { BEGIN CMTMODE; yymore(); } /* Non 'psl' beginning in comment */
<CMTMODE>. { yymore(); }
@@ -284,12 +297,57 @@ void V3PreLex::pushStateIncFilename() {
yymore();
}
void V3PreLex::scanBytes(const string& strg) {
yy_scan_bytes(strg.c_str(), strg.length());
void V3PreLex::initFirstBuffer() {
// Called from constructor to make first buffer
// yy_create_buffer also sets yy_fill_buffer=1 so reads from YY_INPUT
yy_switch_to_buffer(yy_create_buffer(NULL, YY_BUF_SIZE));
m_bufferStack.push(currentBuffer());
yyrestart(NULL);
}
size_t V3PreLex::inputToLex(char* buf, size_t max_size) {
// We need a custom YY_INPUT because we can't use flex buffers.
// Flex buffers are limited to 2GB, and we can't chop into 2G pieces
// because buffers can't end in the middle of tokens.
// m_buffers only applies to the "base" buffer when there's no scanBytes outstanding
// It won't be called on scan_buffers as they don't have yy_fill_buffer set.
//
//if (debug()) { cout<<"- pp:inputToLex ITL s="<<max_size<<" bs="<<m_bufferStack.size()<<endl; dumpSummary(); }
// For testing, use really small chunks
//if (max_size > 13) max_size=13;
size_t got = 0;
while (got < max_size // Haven't got enough
&& !m_buffers.empty()) { // And something buffered
string front = m_buffers.front(); m_buffers.pop_front();
size_t len = front.length();
if (len > (max_size-got)) { // Front string too big
string remainder = front.substr(max_size-got);
front = front.substr(0, max_size-got);
m_buffers.push_front(remainder); // Put back remainder for next time
len = (max_size-got);
}
strncpy(buf+got, front.c_str(), len);
got += len;
}
//if (debug()) { cout<<"- pp::inputToLex got="<<got<<" '"<<string(buf,got)<<"'"<<endl; }
return got;
}
void V3PreLex::scanBytes(const char* strp, size_t len) {
// Note buffers also appended in ::scanBytesBack
// Not "m_buffers.push_front(string(strp,len))" as we need a `define
// to take effect immediately, in the middle of the current buffer
yy_scan_bytes(strp, len);
m_bufferStack.push(currentBuffer()); // yy_scan_bytes makes new buffer
}
void V3PreLex::appendDefValue(const char* textp, int len) {
void V3PreLex::scanBytesBack(const string& str) {
// Initial creation, that will pull from YY_INPUT==inputToLex
// Note buffers also appended in ::scanBytes
m_buffers.push_back(str);
}
void V3PreLex::appendDefValue(const char* textp, size_t len) {
// Append given text to current definition value being formed
m_defValue.append(textp,len);
}
@@ -308,8 +366,15 @@ void V3PreLex::lineDirective(const char* textp) {
V3File::addSrcDepend(m_curFilelinep->filename());
}
void V3PreLex::dumpSummary() {
cout<<"- pp::dumpSummary curBuf="<<(void*)(currentBuffer())
<<" nBuf="<<m_bufferStack.size()
<<" yyfill="<<currentBuffer()->yy_fill_buffer<<endl;
}
void V3PreLex::dumpStack() {
// For debug use
dumpSummary();
stack<YY_BUFFER_STATE> tmpstack = m_bufferStack;
printf(" bufferStack[%p]:",this);
while (!tmpstack.empty()) {
+78 -40
View File
@@ -23,12 +23,14 @@
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <fstream>
#include <stack>
#include <vector>
#include <map>
#include <list>
#include "V3Error.h"
#include "V3Global.h"
@@ -71,7 +73,7 @@ class V3DefineRef {
string m_name; // Define last name being defined
string m_params; // Define parameter list for next expansion
string m_nextarg; // String being built for next argument
int m_parenLevel; // Parenthesis counting inside def args
int m_parenLevel; // Parenthesis counting inside def args (for PARENT not child)
vector<string> m_args; // List of define arguments
public:
@@ -80,9 +82,10 @@ public:
string nextarg() const { return m_nextarg; }
void nextarg(const string& value) { m_nextarg = value; }
int parenLevel() const { return m_parenLevel; }
void parenLevel(int value) { m_parenLevel = value; }
vector<string>& args() { return m_args; }
V3DefineRef(const string& name, const string& params, int pl)
: m_name(name), m_params(params), m_parenLevel(pl) {}
V3DefineRef(const string& name, const string& params)
: m_name(name), m_params(params), m_parenLevel(0) {}
~V3DefineRef() {}
};
@@ -107,6 +110,7 @@ public:
struct V3PreProcImp : public V3PreProc {
// TYPES
typedef std::map<string,V3Define> DefinesMap;
typedef V3InFilter::StrList StrList;
// debug() -> see V3PreShellImp::debug
@@ -164,7 +168,7 @@ private:
bool commentTokenMatch(string& cmdr, const char* strg);
string trimWhitespace(const string& strg, bool trailing);
void unputString(const string& strg, bool first=false);
void unputString(const string& strg);
void parsingOn() {
m_off--;
@@ -322,9 +326,11 @@ void V3PreProcImp::comment(const string& text) {
if ((cp[0]=='v' || cp[0]=='V')
&& 0==(strncmp(cp+1,"erilator",8))) {
cp+=strlen("verilator");
if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment; use /*verilator {...}*/ not /*verilator_{...}*/");
} else if (0==(strncmp(cp,"synopsys",strlen("synopsys")))) {
cp+=strlen("synopsys");
synth = true;
if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment; use /*synopsys {...}*/ not /*synopsys_{...}*/");
} else if (0==(strncmp(cp,"cadence",strlen("cadence")))) {
cp+=strlen("cadence");
synth = true;
@@ -368,6 +374,13 @@ void V3PreProcImp::comment(const string& text) {
//}
// else ignore the comment we don't recognize
} // else no assertions
} else if ((pos=cmd.find("public_flat_rw")) != string::npos) {
// "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)"
cmd = cmd.substr(pos+strlen("public_flat_rw"));
while (isspace(cmd[0])) cmd = cmd.substr(1);
if ((pos=cmd.find("*/")) != string::npos)
cmd.replace(pos, 2, "");
insertUnreadback ("/*verilator public_flat_rw*/ "+cmd+" /**/");
} else {
insertUnreadback ("/*verilator "+cmd+"*/");
}
@@ -404,18 +417,19 @@ const char* V3PreProcImp::tokenName(int tok) {
}
}
void V3PreProcImp::unputString(const string& strg, bool first) {
void V3PreProcImp::unputString(const string& strg) {
// Note: The preliminary call in ::openFile bypasses this function
// We used to just m_lexp->unputString(strg.c_str());
// However this can lead to "flex scanner push-back overflow"
// so instead we scan from a temporary buffer, then on EOF return.
// This is also faster than the old scheme, amazingly.
if (!first) { // Else the initial creation
if (1) {
if (m_lexp->m_bufferStack.empty() || m_lexp->m_bufferStack.top()!=m_lexp->currentBuffer()) {
fileline()->v3fatalSrc("bufferStack missing current buffer; will return incorrectly");
// Hard to debug lost text as won't know till much later
}
}
m_lexp->scanBytes(strg);
m_lexp->scanBytes(strg.c_str(), strg.length());
}
string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) {
@@ -510,7 +524,9 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) {
if (*cp=='"') quote=!quote;
if (*cp) token += *cp;
}
if (refp->args().size() > numArgs) {
if (refp->args().size() > numArgs
// `define X() is ok to call with nothing
&& !(refp->args().size()==1 && numArgs==0 && trimWhitespace(refp->args()[0],false)=="")) {
fileline()->v3error("Define passed too many arguments: "+refp->name()+"\n");
return " `"+refp->name()+" ";
}
@@ -588,22 +604,14 @@ void V3PreProcImp::openFile(FileLine* fl, V3InFilter* filterp, const string& fil
V3File::addSrcDepend(filename);
string wholefile;
// Read a list<string> with the whole file.
StrList wholefile;
bool ok = filterp->readWholefile(filename, wholefile/*ref*/);
if (!ok) {
fileline()->v3error("File not found: "+filename+"\n");
return;
}
// Filter all DOS CR's en-mass. This avoids bugs with lexing CRs in the wrong places.
// This will also strip them from strings, but strings aren't supposed to be multi-line without a "\"
string wholefilecr;
size_t wholesize = wholefile.length();
for (size_t i=0; i<wholesize; i++) { // Not a c_str(), as we keep '\0's for now.
if (wholefile[i] != '\r' && wholefile[i] != '\0') wholefilecr += wholefile[i];
}
wholefile.resize(0); // free memory
if (m_lexp) {
// We allow the same include file twice, because occasionally it pops
// up, with guards preventing a real recursion.
@@ -624,7 +632,35 @@ void V3PreProcImp::openFile(FileLine* fl, V3InFilter* filterp, const string& fil
addLineComment(1); // Enter
yy_flex_debug = (debug()>4)?1:0;
unputString(wholefilecr,true);
// Filter all DOS CR's en-mass. This avoids bugs with lexing CRs in the wrong places.
// This will also strip them from strings, but strings aren't supposed to be multi-line without a "\"
for (StrList::iterator it=wholefile.begin(); it!=wholefile.end(); ++it) {
// We don't end-loop at \0 as we allow and strip mid-string '\0's (for now).
bool strip = false;
const char* sp = it->data();
const char* ep = sp + it->length();
// Only process if needed, as saves extra string allocations
for (const char* cp=sp; cp<ep; cp++) {
if (VL_UNLIKELY(*cp == '\r' || *cp == '\0')) {
strip = true; break;
}
}
if (strip) {
string out; out.reserve(it->length());
for (const char* cp=sp; cp<ep; cp++) {
if (!(*cp == '\r' || *cp == '\0')) {
out += *cp;
}
}
*it = out;
}
// Push the data to an internal buffer.
m_lexp->scanBytesBack(*it);
// Reclaim memory; the push saved the string contents for us
*it = "";
}
}
void V3PreProcImp::insertUnreadbackAtBol(const string& text) {
@@ -680,7 +716,7 @@ int V3PreProcImp::getRawToken() {
if (m_lineAdd) {
m_lineAdd--;
m_rawAtBol = true;
yytext=(char*)"\n"; yyleng=1;
yyourtext("\n",1);
if (debug()) debugToken(VP_WHITE, "LNA");
return (VP_WHITE);
}
@@ -692,11 +728,11 @@ int V3PreProcImp::getRawToken() {
if (!m_rawAtBol) rtncmt = "\n"+rtncmt;
m_lineCmtNl = false;
}
yytext=(char*)rtncmt.c_str(); yyleng=rtncmt.length();
yyourtext(rtncmt.c_str(), rtncmt.length());
m_lineCmt = "";
if (yyleng) m_rawAtBol = (yytext[yyleng-1]=='\n');
if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n');
if (m_state==ps_DEFVALUE) {
V3PreLex::s_currentLexp->appendDefValue(yytext,yyleng);
V3PreLex::s_currentLexp->appendDefValue(yyourtext(),yyourleng());
goto next_tok;
} else {
if (debug()) debugToken(VP_TEXT, "LCM");
@@ -718,14 +754,14 @@ int V3PreProcImp::getRawToken() {
goto next_tok; // Parse parent, or find the EOF.
}
if (yyleng) m_rawAtBol = (yytext[yyleng-1]=='\n');
if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n');
return tok;
}
}
void V3PreProcImp::debugToken(int tok, const char* cmtp) {
if (debug()>4) {
string buf = string (yytext, yyleng);
string buf = string (yyourtext(), yyourleng());
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"); }
@@ -750,7 +786,7 @@ int V3PreProcImp::getToken() {
if (tok==VP_COMMENT) {
if (!m_off) {
if (m_lexp->m_keepComments == KEEPCMT_SUB) {
string rtn; rtn.assign(yytext,yyleng);
string rtn; rtn.assign(yyourtext(),yyourleng());
comment(rtn);
} else {
return (tok);
@@ -759,7 +795,7 @@ int V3PreProcImp::getToken() {
// We're off or processed the comment specially. If there are newlines
// in it, we also return the newlines as TEXT so that the linenumber
// count is maintained for downstream tools
for (int len=0; len<yyleng; len++) { if (yytext[len]=='\n') m_lineAdd++; }
for (size_t len=0; len<yyourleng(); len++) { if (yyourtext()[len]=='\n') m_lineAdd++; }
goto next_tok;
}
if (tok==VP_LINE) {
@@ -774,7 +810,7 @@ int V3PreProcImp::getToken() {
case ps_DEFNAME: {
if (tok==VP_SYMBOL) {
m_state = ps_TOP;
m_lastSym.assign(yytext,yyleng);
m_lastSym.assign(yyourtext(),yyourleng());
if (m_stateFor==VP_IFDEF
|| m_stateFor==VP_IFNDEF) {
bool enable = defExists(m_lastSym);
@@ -876,11 +912,11 @@ int V3PreProcImp::getToken() {
m_state = ps_TOP;
// DEFVALUE is terminated by a return, but lex can't return both tokens.
// Thus, we emit a return here.
yytext=(char*)(newlines.c_str()); yyleng=newlines.length();
yyourtext(newlines.c_str(), newlines.length());
return(VP_WHITE);
}
case ps_DEFPAREN: {
if (tok==VP_TEXT && yyleng==1 && yytext[0]=='(') {
if (tok==VP_TEXT && yyourleng()==1 && yyourtext()[0]=='(') {
m_state = ps_DEFARG;
goto next_tok;
} else {
@@ -896,13 +932,13 @@ int V3PreProcImp::getToken() {
V3DefineRef* refp = &(m_defRefs.top());
refp->nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue="";
UINFO(4,"defarg++ "<<refp->nextarg()<<endl);
if (tok==VP_DEFARG && yyleng==1 && yytext[0]==',') {
if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==',') {
refp->args().push_back(refp->nextarg());
m_state = ps_DEFARG;
m_lexp->pushStateDefArg(1);
refp->nextarg("");
goto next_tok;
} else if (tok==VP_DEFARG && yyleng==1 && yytext[0]==')') {
} else if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==')') {
refp->args().push_back(refp->nextarg());
string out = defineSubst(refp);
// Substitute in and prepare for next action
@@ -929,7 +965,7 @@ int V3PreProcImp::getToken() {
// we'll append it when we push the argument.
break;
} else if (tok==VP_SYMBOL || tok==VP_STRING || VP_TEXT || VP_WHITE || VP_PSL) {
string rtn; rtn.assign(yytext,yyleng);
string rtn; rtn.assign(yyourtext(),yyourleng());
refp->nextarg(refp->nextarg()+rtn);
goto next_tok;
} else {
@@ -941,7 +977,7 @@ int V3PreProcImp::getToken() {
case ps_INCNAME: {
if (tok==VP_STRING) {
m_state = ps_TOP;
m_lastSym.assign(yytext,yyleng);
m_lastSym.assign(yyourtext(),yyourleng());
UINFO(4,"Include "<<m_lastSym<<endl);
// Drop leading and trailing quotes.
m_lastSym.erase(0,1);
@@ -949,7 +985,7 @@ int V3PreProcImp::getToken() {
include(m_lastSym);
goto next_tok;
}
else if (tok==VP_TEXT && yyleng==1 && yytext[0]=='<') {
else if (tok==VP_TEXT && yyourleng()==1 && yyourtext()[0]=='<') {
// include <filename>
m_state = ps_INCNAME; // Still
m_lexp->pushStateIncFilename();
@@ -969,7 +1005,7 @@ int V3PreProcImp::getToken() {
if (tok==VP_STRING) {
m_state = ps_TOP;
if (!m_off) {
m_lastSym.assign(yytext,yyleng);
m_lastSym.assign(yyourtext(),yyourleng());
fileline()->v3error(m_lastSym);
}
goto next_tok;
@@ -1022,7 +1058,7 @@ int V3PreProcImp::getToken() {
case VP_DEFREF: {
if (!m_off) {
string name; name.append(yytext+1,yyleng-1);
string name; name.append(yyourtext()+1,yyourleng()-1);
UINFO(4,"DefRef "<<name<<endl);
if (m_defDepth++ > V3PreProc::DEFINE_RECURSION_LEVEL_MAX) {
fileline()->v3error("Recursive `define substitution: `"+name);
@@ -1054,7 +1090,9 @@ int V3PreProcImp::getToken() {
}
else { // Found, with parameters
UINFO(4,"Defref `"<<name<<" => parameterized"<<endl);
m_defRefs.push(V3DefineRef(name, params, m_lexp->m_parenLevel));
// The CURRENT macro needs the paren saved, it's not a property of the child macro
if (!m_defRefs.empty()) m_defRefs.top().parenLevel(m_lexp->m_parenLevel);
m_defRefs.push(V3DefineRef(name, params));
m_state = ps_DEFPAREN; m_stateFor = tok;
m_lexp->pushStateDefArg(0);
goto next_tok;
@@ -1107,7 +1145,7 @@ string V3PreProcImp::getline() {
while (NULL==(rtnp=strchr(m_lineChars.c_str(),'\n')) && !gotEof) {
int tok = getToken();
if (debug()>4) {
string buf = string (yytext, yyleng);
string buf = string (yyourtext(), yyourleng());
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"); }
@@ -1126,7 +1164,7 @@ string V3PreProcImp::getline() {
m_lineChars.append(" psl ");
}
else {
m_lineChars.append(yytext,0,yyleng);
m_lineChars.append(yyourtext(),0,yyourleng());
}
}
+1
View File
@@ -29,6 +29,7 @@
#include <string>
#include <map>
#include <list>
#include <iostream>
class V3InFilter;
+7 -2
View File
@@ -48,9 +48,12 @@ protected:
//---------------------------------------
// METHODS
static int debug() {
static int debug(bool reset=false) {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
if (VL_UNLIKELY(level < 0) || reset) {
level = v3Global.opt.debugSrcLevel(__FILE__);
if (s_preprocp) s_preprocp->debug(debug());
}
return level;
}
@@ -72,6 +75,8 @@ protected:
}
void preproc (FileLine* fl, const string& modname, V3InFilter* filterp, V3ParseImp* parsep) {
debug(true); // Recheck if debug on - first check was before command line passed
// Preprocess the given module, putting output in vppFilename
UINFONL(1," Preprocessing "<<modname<<endl);
+2 -2
View File
@@ -170,6 +170,7 @@ private:
m_inWhilep = NULL;
startStatement(nodep);
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
m_stmtp = NULL;
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
@@ -195,9 +196,8 @@ private:
}
void visitShift (AstNodeBiop* nodep) {
// Shifts of > 32/64 bits in C++ will wrap-around and generate non-0s
if (!nodep->user2()) {
if (!nodep->user2Inc()) {
UINFO(4," ShiftFix "<<nodep<<endl);
nodep->user2(true);
if (nodep->widthMin()<=64 // Else we'll use large operators which work right
// C operator's width must be < maximum shift which is based on Verilog width
&& nodep->width() < (1LL<<nodep->rhsp()->widthMin())) {
+11
View File
@@ -165,6 +165,14 @@ private:
m_scopep->addActivep(clonep);
clonep->iterateChildren(*this); // We iterate under the *clone*
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
// Add to list of blocks under this scope
UINFO(4," Move "<<nodep<<endl);
AstNode* clonep = nodep->cloneTree(false);
nodep->user2p(clonep);
m_scopep->addActivep(clonep);
clonep->iterateChildren(*this); // We iterate under the *clone*
}
virtual void visit(AstCoverToggle* nodep, AstNUser*) {
// Add to list of blocks under this scope
UINFO(4," Move "<<nodep<<endl);
@@ -291,6 +299,9 @@ private:
virtual void visit(AstAlways* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}
virtual void visit(AstAlwaysPublic* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}
virtual void visit(AstCoverToggle* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}
+1
View File
@@ -143,6 +143,7 @@ private:
//=======
// These have proper signedness set when they were created.
virtual void visit(AstReturn* nodep, AstNUser*) { nodep->iterateChildren(*this); }
virtual void visit(AstNodeDType* nodep, AstNUser*) { nodep->iterateChildren(*this); }
// Inherit from others
+60 -8
View File
@@ -88,6 +88,7 @@ private:
bool m_inDlyAssign; ///< Under delayed assignment
int m_instrCount; ///< Number of nodes
int m_dataCount; ///< Bytes of data
AstJumpGo* m_jumpp; ///< Jump label we're branching from
// Simulating:
deque<V3Number*> m_numFreeps; ///< List of all numbers free and not in use
deque<V3Number*> m_numAllps; ///< List of all numbers free and in use
@@ -230,8 +231,14 @@ private:
: v3Global.opt.unrollCount();
}
bool jumpingOver(AstNode* nodep) {
// True to jump over this node - all visitors must call this up front
return (m_jumpp && m_jumpp->labelp()!=nodep);
}
// VISITORS
virtual void visit(AstAlways* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
}
@@ -239,6 +246,7 @@ private:
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate
AstNode* vscp = varOrScope(nodep);
@@ -285,16 +293,19 @@ private:
}
}
virtual void visit(AstVarXRef* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
if (m_scoped) { badNodeType(nodep); return; }
else { clearOptimizable(nodep,"Language violation: Dotted hierarchical references not allowed in constant functions"); }
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
if (!m_params) { badNodeType(nodep); return; }
if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); }
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeIf* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
UINFO(5," IF "<<nodep<<endl);
checkNodeInfo(nodep);
if (m_checkOnly) {
@@ -364,6 +375,7 @@ private:
}
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate
if (nodep->castAssignDly()) {
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
@@ -400,6 +412,7 @@ private:
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeCase* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
UINFO(5," CASE "<<nodep<<endl);
checkNodeInfo(nodep);
if (m_checkOnly) {
@@ -436,12 +449,30 @@ private:
virtual void visit(AstCaseItem* nodep, AstNUser*) {
// Real handling is in AstNodeCase
if (jumpingOver(nodep)) return;
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
}
virtual void visit(AstComment*, AstNUser*) {}
virtual void visit(AstJumpGo* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
checkNodeInfo(nodep);
if (!m_checkOnly) {
UINFO(5," JUMP GO "<<nodep<<endl);
m_jumpp = nodep;
}
}
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
checkNodeInfo(nodep);
nodep->iterateChildren(*this);
if (m_jumpp && m_jumpp->labelp() == nodep) {
UINFO(5," JUMP DONE "<<nodep<<endl);
m_jumpp = NULL;
}
}
virtual void visit(AstStop* nodep, AstNUser*) {
if (m_params) { // This message seems better than an obscure $stop
// The spec says $stop is just ignored, it seems evil to ignore assertions
@@ -479,6 +510,7 @@ private:
virtual void visit(AstWhile* nodep, AstNUser*) {
// Doing lots of Whiles is slow, so only for parameters
if (jumpingOver(nodep)) return;
UINFO(5," WHILE "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
checkNodeInfo(nodep);
@@ -489,12 +521,19 @@ private:
while (1) {
UINFO(5," WHILE-ITER "<<nodep<<endl);
nodep->precondsp()->iterateAndNext(*this);
if (jumpingOver(nodep)) break;
nodep->condp()->iterateAndNext(*this);
if (jumpingOver(nodep)) break;
if (!optimizable()) break;
if (!fetchNumber(nodep->condp())->isNeqZero()) {
break;
}
nodep->bodysp()->iterateAndNext(*this);
if (jumpingOver(nodep)) break;
nodep->incsp()->iterateAndNext(*this);
if (jumpingOver(nodep)) break;
// Prep for next loop
if (loops++ > unrollCount()*16) {
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "+cvtToStr(unrollCount()));
break;
@@ -504,6 +543,7 @@ private:
}
virtual void visit(AstFuncRef* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
UINFO(5," FUNCREF "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
AstNodeFTask* funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked");
@@ -540,6 +580,7 @@ private:
}
virtual void visit(AstVar* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
if (!m_params) { badNodeType(nodep); return; }
}
@@ -548,20 +589,30 @@ private:
// AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish,
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
virtual void visit(AstNode* nodep, AstNUser*) {
if (jumpingOver(nodep)) return;
badNodeType(nodep);
}
private:
// MEMBERS - called by constructor
void setMode(bool scoped, bool checkOnly, bool params) {
m_checkOnly = checkOnly;
m_scoped = scoped;
m_params = params;
}
void mainGuts(AstNode* nodep) {
nodep->accept(*this);
if (m_jumpp) {
m_jumpp->v3fatalSrc("JumpGo branched to label that wasn't found");
m_jumpp = NULL;
}
}
public:
// CONSTRUCTORS
SimulateVisitor() {
setMode(false,false,false);
clear(); // We reuse this structure in the main loop, so put initializers inside clear()
}
void setMode(bool scoped, bool checkOnly, bool params) {
m_checkOnly = checkOnly;
m_scoped = scoped;
m_params = params;
}
void clear() {
m_whyNotOptimizable = "";
m_whyNotNodep = NULL;
@@ -570,6 +621,7 @@ public:
m_inDlyAssign = false;
m_instrCount = 0;
m_dataCount = 0;
m_jumpp = NULL;
AstNode::user1ClearTree(); // user1p() used on entire tree
AstNode::user2ClearTree(); // user2p() used on entire tree
@@ -580,15 +632,15 @@ public:
}
void mainTableCheck (AstNode* nodep) {
setMode(true/*scoped*/,true/*checking*/, false/*params*/);
nodep->accept(*this);
mainGuts(nodep);
}
void mainTableEmulate (AstNode* nodep) {
setMode(true/*scoped*/,false/*checking*/, false/*params*/);
nodep->accept(*this);
mainGuts(nodep);
}
void mainParamEmulate (AstNode* nodep) {
setMode(false/*scoped*/,false/*checking*/, true/*params*/);
nodep->accept(*this);
mainGuts(nodep);
}
virtual ~SimulateVisitor() {
for (deque<V3Number*>::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) {
+176 -34
View File
@@ -54,6 +54,14 @@ class SliceCloneVisitor : public AstNVisitor {
// AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to
// AstNodeAssign::user2() -> int. The number of clones needed for this assign
// ENUMS
enum RedOp { // The type of unary operation to be expanded
REDOP_UNKNOWN, // Unknown/Unsupported
REDOP_OR, // Or Reduction
REDOP_AND, // And Reduction
REDOP_XOR, // Xor Reduction
REDOP_XNOR}; // Xnor Reduction
// STATE
vector<vector<unsigned> > m_selBits; // Indexes of the ArraySel we are expanding
int m_vecIdx; // Current vector index
@@ -76,8 +84,9 @@ class SliceCloneVisitor : public AstNVisitor {
if (m_vecIdx == (int)m_selBits.size()) {
m_selBits.push_back(vector<unsigned>());
AstVar* varp = m_refp->varp();
int dimensions = varp->dimensions();
for (int i = 0; i < dimensions; ++i) {
pair<uint32_t,uint32_t> arrDim = varp->dimensions();
uint32_t dimensions = arrDim.first + arrDim.second;
for (uint32_t i = 0; i < dimensions; ++i) {
m_selBits[m_vecIdx].push_back(0);
}
}
@@ -112,6 +121,7 @@ class SliceCloneVisitor : public AstNVisitor {
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
if (nodep->user2() < 2) return; // Don't need clones
m_selBits.clear();
UINFO(4, "Cloning "<<nodep->user2()<<" times: "<<nodep<<endl);
for (int i = 0; i < nodep->user2(); ++i) {
@@ -124,13 +134,77 @@ class SliceCloneVisitor : public AstNVisitor {
nodep->unlinkFrBack()->deleteTree(); nodep = NULL;
}
// Not all Uniop nodes should be cloned down to a single bit
void cloneUniop(AstNodeUniop* nodep) {
if (nodep->user2() < 2) return; // Don't need clones
m_selBits.clear();
UINFO(4, "Cloning "<<nodep->user2()<<" times: "<<nodep<<endl);
// Figure out what type of operation this is so we don't have to cast on
// every clone.
RedOp redOpType = REDOP_UNKNOWN;
if (nodep->castRedOr()) redOpType = REDOP_OR;
else if (nodep->castRedAnd()) redOpType = REDOP_AND;
else if (nodep->castRedXor()) redOpType = REDOP_XOR;
else if (nodep->castRedXnor()) redOpType = REDOP_XNOR;
AstNode* lhsp = NULL;
AstNode* rhsp = NULL;
for (int i = 0; i < nodep->user2(); ++i) {
// Clone the node and iterate over the clone
m_vecIdx = -1;
AstNodeUniop* clonep = nodep->cloneTree(false)->castNodeUniop();
clonep->iterateChildren(*this);
if (!lhsp) lhsp = clonep;
else rhsp = clonep;
if (lhsp && rhsp) {
switch (redOpType) {
case REDOP_OR:
lhsp = new AstLogOr(nodep->fileline(), lhsp, rhsp);
break;
case REDOP_AND:
lhsp = new AstLogAnd(nodep->fileline(), lhsp, rhsp);
break;
case REDOP_XOR:
lhsp = new AstXor(nodep->fileline(), lhsp, rhsp);
break;
case REDOP_XNOR:
lhsp = new AstXnor(nodep->fileline(), lhsp, rhsp);
break;
default: // REDOP_UNKNOWN
nodep->v3fatalSrc("Unsupported: Unary operation on multiple packed dimensions");
break;
}
rhsp = NULL;
}
}
nodep->addNextHere(lhsp);
nodep->unlinkFrBack()->deleteTree(); nodep = NULL;
}
virtual void visit(AstRedOr* nodep, AstNUser*) {
cloneUniop(nodep);
}
virtual void visit(AstRedAnd* nodep, AstNUser*) {
cloneUniop(nodep);
}
virtual void visit(AstRedXor* nodep, AstNUser*) {
cloneUniop(nodep);
}
virtual void visit(AstRedXnor* nodep, AstNUser*) {
cloneUniop(nodep);
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
SliceCloneVisitor(AstNodeAssign* assignp) {
SliceCloneVisitor(AstNode* assignp) {
assignp->accept(*this);
}
virtual ~SliceCloneVisitor() {}
@@ -142,15 +216,20 @@ class SliceVisitor : public AstNVisitor {
// NODE STATE
// Cleared on netlist
// AstNodeAssign::user1() -> bool. True if find is complete
// AstNodeAssign::user2() -> int. The number of clones needed for this assign
// AstUniop::user1() -> bool. True if find is complete
// AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to
// AstNode::user2() -> int. The number of clones needed for this node
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
// TYPEDEFS
typedef pair<uint32_t, uint32_t> ArrayDimensions; // Array Dimensions (packed, unpacked)
// STATE
AstNode* m_assignp; // Assignment we are under
AstNodeVarRef* m_lhsVarRefp; // Var on the LHS
bool m_extend; // We have found an extend node
bool m_assignError; // True if the current assign already has an error
// METHODS
static int debug() {
@@ -178,15 +257,13 @@ class SliceVisitor : public AstNVisitor {
return dim;
}
AstNode* insertImplicit(AstVarRef* nodep, unsigned start, unsigned count) {
AstArraySel* insertImplicit(AstNode* nodep, unsigned start, unsigned count) {
// Insert any implicit slices as explicit slices (ArraySel nodes).
// Return a new pointer to replace fromp() in the ArraySel.
AstVarRef* fromp = nodep;
if (!fromp) nodep->v3fatalSrc("NULL VarRef passed to insertImplicit");
AstVar* varp = fromp->varp();
// Get the DType and insert a new ArraySel
AstArraySel* topp = NULL;
AstArraySel* bottomp = NULL;
// Return a new pointer to replace nodep() in the ArraySel.
AstVarRef* refp = nodep->user1p()->castNode()->castVarRef();
if (!refp) nodep->v3fatalSrc("No VarRef in user1 of node "<<nodep);
AstVar* varp = refp->varp();
AstNode* topp = nodep;
for (unsigned i = start; i < start + count; ++i) {
AstNodeDType* dtypep = varp->dtypeDimensionp(i-1);
AstArrayDType* adtypep = dtypep->castArrayDType();
@@ -197,17 +274,13 @@ class SliceVisitor : public AstNVisitor {
// Below code assumes big bit endian; just works out if we swap
int x = msb; msb = lsb; lsb = x;
}
AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, new AstConst(nodep->fileline(),lsb));
AstArraySel* newp = new AstArraySel(nodep->fileline(), topp, new AstConst(nodep->fileline(),lsb));
newp->user1p(refp);
newp->start(lsb);
newp->length(msb - lsb + 1);
if (!topp) topp = newp;
fromp = newp->fromp()->unlinkFrBack()->castVarRef();
if (bottomp) bottomp->fromp(newp);
bottomp = newp;
topp = newp->castNode();
}
bottomp->fromp(fromp);
return topp;
return topp->castArraySel();
}
int countClones(AstArraySel* nodep) {
@@ -228,9 +301,12 @@ class SliceVisitor : public AstNVisitor {
// The LHS/RHS of an Assign may be to a Var that is an array. In this
// case we need to create a slice accross the entire Var
if (m_assignp && !nodep->backp()->castArraySel()) {
uint32_t dimensions = nodep->varp()->dimensions();
pair<uint32_t,uint32_t> arrDim = nodep->varp()->dimensions();
uint32_t dimensions = arrDim.first + arrDim.second;
if (dimensions > 0) {
AstNode* newp = insertImplicit(nodep->cloneTree(false), 1, dimensions);
AstVarRef* clonep = nodep->cloneTree(false);
clonep->user1p(nodep);
AstNode* newp = insertImplicit(clonep, 1, dimensions);
nodep->replaceWith(newp); nodep = NULL;
newp->accept(*this);
}
@@ -239,8 +315,18 @@ class SliceVisitor : public AstNVisitor {
virtual void visit(AstExtend* nodep, AstNUser*) {
m_extend = true;
if (m_assignp && m_assignp->user2() > 1) {
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions");
m_assignError = true;
}
nodep->iterateChildren(*this);
}
virtual void visit(AstConst* nodep, AstNUser*) {
m_extend = true;
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
m_assignp->v3error("Unsupported: Assignment between a constant and an array slice");
m_assignError = true;
}
nodep->iterateChildren(*this);
}
@@ -249,22 +335,24 @@ class SliceVisitor : public AstNVisitor {
if (!m_assignp) return;
unsigned dim = explicitDimensions(nodep);
AstVarRef* refp = nodep->user1p()->castNode()->castVarRef();
unsigned implicit = refp->varp()->dimensions() - dim;
pair<uint32_t,uint32_t> arrDim = refp->varp()->dimensions();
uint32_t implicit = (arrDim.first + arrDim.second) - dim;
if (implicit > 0) {
AstNode* backp = refp->backp();
AstNode* newp = insertImplicit(refp->cloneTree(false), dim+1, implicit);
backp->castArraySel()->fromp()->replaceWith(newp);
AstArraySel* newp = insertImplicit(nodep->cloneTree(false), dim+1, implicit);
nodep->replaceWith(newp); nodep = newp;
}
int clones = countClones(nodep);
if (m_assignp->user2() > 0 && m_assignp->user2() != clones) {
m_assignp->v3error("Slices of arrays in assignments must have the same unpacked dimensions");
} else if (m_assignp->user2() == 0) {
} else if (m_assignp->user2() == 0 && !m_assignError) {
if (m_extend && clones > 1) {
m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions");
m_assignError = true;
}
if (clones > 1 && !refp->lvalue() && refp->varp() == m_lhsVarRefp->varp() && !m_assignp->castAssignDly()) {
// LHS Var != RHS Var for a non-delayed assignment
// LHS Var != RHS Var for a non-delayed assignment
m_assignp->v3error("Unsupported: Slices in a non-delayed assignment with the same Var on both sides");
m_assignError = true;
}
m_assignp->user2(clones);
}
@@ -272,8 +360,9 @@ class SliceVisitor : public AstNVisitor {
virtual void visit(AstSel* nodep, AstNUser*) {
m_extend = true;
if (m_assignp && m_assignp->user2() > 1) {
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions");
m_assignError = true;
}
nodep->iterateChildren(*this);
}
@@ -314,6 +403,7 @@ class SliceVisitor : public AstNVisitor {
void findImplicit(AstNodeAssign* nodep) {
if (m_assignp) nodep->v3fatalSrc("Found a NodeAssign under another NodeAssign");
m_assignp = nodep;
m_assignError = false;
m_extend = false;
nodep->user1(true);
// Record the LHS Var so we can check if the Var on the RHS is the same
@@ -322,9 +412,7 @@ class SliceVisitor : public AstNVisitor {
// Iterate children looking for ArraySel nodes. From that we get the number of elements
// in the array so we know how many times we need to clone this assignment.
nodep->iterateChildren(*this);
if (nodep->user2() > 1) {
SliceCloneVisitor scv(nodep);
}
if (nodep->user2() > 1) SliceCloneVisitor scv(nodep);
m_assignp = NULL;
}
@@ -335,6 +423,60 @@ class SliceVisitor : public AstNVisitor {
}
}
void expandUniOp(AstNodeUniop* nodep) {
nodep->user1(true);
unsigned dim = 0;
if (AstArraySel* selp = nodep->lhsp()->castArraySel()) {
// We have explicit dimensions, either packed or unpacked
dim = explicitDimensions(selp);
}
if (dim == 0 && !nodep->lhsp()->castVarRef()) {
// No ArraySel or VarRef, not something we can expand
nodep->iterateChildren(*this);
} else {
AstVarRef* refp = findVarRefRecurse(nodep->lhsp());
ArrayDimensions varDim = refp->varp()->dimensions();
if ((int)(dim - varDim.second) < 0) {
// Unpacked dimensions are referenced first, make sure we have them all
nodep->v3error("Unary operator used across unpacked dimensions");
} else if ((int)(dim - (varDim.first + varDim.second)) < 0) {
// Implicit packed dimensions are allowed, make them explicit
uint32_t newDim = (varDim.first + varDim.second) - dim;
AstNode* clonep = nodep->lhsp()->cloneTree(false);
clonep->user1p(refp);
AstNode* newp = insertImplicit(clonep, dim+1, newDim);
nodep->lhsp()->replaceWith(newp); refp = NULL;
int clones = countClones(nodep->lhsp()->castArraySel());
nodep->user2(clones);
SliceCloneVisitor scv(nodep);
}
}
}
virtual void visit(AstRedOr* nodep, AstNUser*) {
if (!nodep->user1()) {
expandUniOp(nodep);
}
}
virtual void visit(AstRedAnd* nodep, AstNUser*) {
if (!nodep->user1()) {
expandUniOp(nodep);
}
}
virtual void visit(AstRedXor* nodep, AstNUser*) {
if (!nodep->user1()) {
expandUniOp(nodep);
}
}
virtual void visit(AstRedXnor* nodep, AstNUser*) {
if (!nodep->user1()) {
expandUniOp(nodep);
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
@@ -354,6 +496,6 @@ public:
// Link class functions
void V3Slice::sliceAll(AstNetlist* rootp) {
UINFO(4,__FUNCTION__<<": "<<endl);
UINFO(2,__FUNCTION__<<": "<<endl);
SliceVisitor visitor(rootp);
}
+3 -4
View File
@@ -1039,7 +1039,7 @@ private:
// Create output variable
string namePrefix = "__Vfunc_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++);
AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(),
namePrefix+"__out");
namePrefix+"__Vfuncout");
// Create cloned statements
if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); }
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?");
@@ -1066,10 +1066,8 @@ private:
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->user1()) { // Just one creation needed per function
if (!nodep->user1Inc()) { // Just one creation needed per function
// Expand functions in it
nodep->user1(true);
int modes = 0;
if (nodep->dpiImport()) modes++;
if (nodep->dpiExport()) modes++;
@@ -1127,6 +1125,7 @@ private:
// Body insert just before themselves
m_insStmtp = NULL; // First thing should be new statement
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
// Done the loop
m_insStmtp = NULL; // Next thing should be new statement
}
+2 -4
View File
@@ -322,8 +322,7 @@ private:
void visit(AstSel* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->user1()) {
nodep->user1(1);
if (!nodep->user1Inc()) {
// Guard against reading/writing past end of bit vector array
int maxmsb = 0;
bool lvalue = false;
@@ -373,8 +372,7 @@ private:
virtual void visit(AstArraySel* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->user1()) {
nodep->user1(1);
if (!nodep->user1Inc()) {
if (debug()==9) nodep->dumpTree(cout,"-in: ");
// Guard against reading/writing past end of arrays
AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp());
+35 -13
View File
@@ -82,6 +82,20 @@ private:
: v3Global.opt.unrollCount();
}
bool bodySizeOverRecurse(AstNode* nodep, int& bodySize, int bodyLimit) {
if (!nodep) return false;
bodySize++;
// Exit once exceeds limits, rather than always total
// so don't go O(n^2) when can't unroll
if (bodySize > bodyLimit) return true;
if (bodySizeOverRecurse(nodep->op1p(), bodySize, bodyLimit)) return true;
if (bodySizeOverRecurse(nodep->op2p(), bodySize, bodyLimit)) return true;
if (bodySizeOverRecurse(nodep->op3p(), bodySize, bodyLimit)) return true;
if (bodySizeOverRecurse(nodep->op4p(), bodySize, bodyLimit)) return true;
// Tail recurse.
return bodySizeOverRecurse(nodep->nextp(), bodySize, bodyLimit);
}
bool forUnrollCheck(AstNode* nodep,
AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp)
AstNode* precondsp, AstNode* condp,
@@ -174,19 +188,19 @@ private:
//
if (!m_generate) {
UINFO(8, " ~Iters: "<<((valStop - valInit)/valInc)<<" c="<<unrollCount()<<endl);
if (((valStop - valInit)/valInc) > unrollCount())
int loops = ((valStop - valInit)/valInc);
if (loops > unrollCount())
return cantUnroll(nodep, "too many iterations");
// Less than 10 statements in the body?
int bodySize = 0;
for (AstNode* bodp = precondsp; bodp; bodp=bodp->nextp()) {
bodySize++;
}
for (AstNode* bodp = bodysp; bodp; bodp=bodp->nextp()) {
bodySize++;
}
if (bodySize > v3Global.opt.unrollStmts())
int bodyLimit = v3Global.opt.unrollStmts();
if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops;
if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit)
|| bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit)
|| bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) {
return cantUnroll(nodep, "too many statements");
}
}
//
// Now, make sure there's no assignment to this variable in the loop
@@ -195,6 +209,7 @@ private:
m_ignoreIncp = incp;
precondsp->iterateAndNext(*this);
bodysp->iterateAndNext(*this);
incp->iterateAndNext(*this);
m_varModeCheck = false;
m_ignoreIncp = NULL;
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
@@ -231,6 +246,10 @@ private:
bodysp->unlinkFrBackWithNext();
stmtsp = stmtsp->addNextNull(bodysp); // Maybe null if no body
}
if (incp && !nodep->castGenFor()) { // Generates don't need to increment loop index
incp->unlinkFrBackWithNext();
stmtsp = stmtsp->addNextNull(incp); // 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);
@@ -279,7 +298,6 @@ private:
}
}
}
// 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();
@@ -303,9 +321,13 @@ private:
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::constifyEdit(incp); incp=NULL; }
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp());
if (nodep->incsp()) incp = nodep->incsp();
else {
for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {}
if (incp) { V3Const::constifyEdit(incp); incp=NULL; }
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(),
@@ -338,7 +360,7 @@ private:
nodep->incsp(), nodep->bodysp())) {
pushDeletep(nodep); nodep=NULL; // Did replacement
} else {
nodep->v3error("For loop doesn't have genvar index, or is misformed");
nodep->v3error("For loop doesn't have genvar index, or is malformed");
}
}
}
+20 -2
View File
@@ -84,6 +84,7 @@ private:
bool m_paramsOnly; // Computing parameter value; limit operation
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
AstNodeCase* m_casep; // Current case statement CaseItem is under
AstFunc* m_funcp; // Current function
// CLASSES
#define ANYSIZE 0
@@ -711,6 +712,7 @@ private:
nodep->precondsp()->iterateAndNext(*this);
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like an if() condition.
}
virtual void visit(AstNodeIf* nodep, AstNUser*) {
@@ -924,6 +926,20 @@ private:
nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width());
}
}
m_funcp = NULL;
}
virtual void visit(AstReturn* nodep, AstNUser* vup) {
if (!m_funcp) {
if (nodep->lhsp()) { // Return w/o value ok other places
nodep->v3error("Return with return value isn't underneath a function");
}
} else {
if (nodep->lhsp()) {
// Function hasn't been widthed, so make it so.
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->widthSignedFrom(m_funcp->fvarp());
}
}
}
virtual void visit(AstFuncRef* nodep, AstNUser* vup) {
visit(nodep->castNodeFTaskRef(), vup);
@@ -974,8 +990,9 @@ private:
lastloop = true; // so exit early; next loop will correct it
}
else if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING
&& !pinp->castCvtPackString()
&& !(pinp->castVarRef() && pinp->castVarRef()->varp()->basicp()->keyword()==AstBasicDTypeKwd::STRING)) {
&& !pinp->castCvtPackString()
&& !pinp->castSFormatF() // Already generates a string
&& !(pinp->castVarRef() && pinp->castVarRef()->varp()->basicp()->keyword()==AstBasicDTypeKwd::STRING)) {
UINFO(4," Add CvtPackString: "<<pinp<<endl);
AstNRelinker handle;
pinp->unlinkFrBack(&handle); // No next, that's the next pin
@@ -1028,6 +1045,7 @@ public:
m_taskDepth = 0;
m_cellRangep = NULL;
m_casep = NULL;
m_funcp = NULL;
}
AstNode* mainAcceptEdit(AstNode* nodep) {
return nodep->acceptSubtreeReturnEdits(*this, WidthVP(ANYSIZE,0,BOTH).p());
+5 -2
View File
@@ -52,8 +52,8 @@
#include "V3File.h"
#include "V3Cdc.h"
#include "V3Gate.h"
#include "V3Graph.h"
#include "V3GenClk.h"
#include "V3Graph.h"
#include "V3Inline.h"
#include "V3Inst.h"
#include "V3Life.h"
@@ -61,9 +61,10 @@
#include "V3Link.h"
#include "V3LinkCells.h"
#include "V3LinkDot.h"
#include "V3LinkJump.h"
#include "V3LinkLValue.h"
#include "V3LinkLevel.h"
#include "V3LinkParse.h"
#include "V3LinkLValue.h"
#include "V3LinkResolve.h"
#include "V3Localize.h"
#include "V3Name.h"
@@ -143,6 +144,8 @@ void process () {
V3LinkResolve::linkResolve(v3Global.rootp());
// Set Lvalue's in variable refs
V3LinkLValue::linkLValue(v3Global.rootp());
// Convert return/continue/disable to jumps
V3LinkJump::linkJump(v3Global.rootp());
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree"));
V3Error::abortIfErrors();
+1 -1
View File
@@ -25,7 +25,7 @@
//**********************************************************************
//**** Version and host name
#define DTVERSION "Verilator 3.800 2010/02/07"
#define DTVERSION "Verilator 3.802 2010/05/01"
//**********************************************************************
//**** Functions
+4
View File
@@ -15,6 +15,8 @@
# DESCRIPTION: Edits flex output to get around various broken flex issues.
my $Opt_Prefix = $ARGV[0] or die "%Error: No prefix specified,";
foreach my $line (<STDIN>) {
# Fix flex 2.5.4 namespace omission
$line =~ s/^class istream;/\#include <iostream>\nusing namespace std;\n/;
@@ -28,6 +30,8 @@ foreach my $line (<STDIN>) {
$line =~ s!for \( n = 0; n < max_size && !for ( n = 0; ((size_t)n < (size_t)max_size) && !g;
# Fix flex 2.5.4 and GCC 4.0.2 under FLEX_DEBUG
$line =~ s!--accepting rule at line %d !--accepting rule at line %ld !g;
# Fix compiler warning filenames
$line =~ s!(#line \d+ ".*)_pretmp!$1!;
print "$line";
}
+15 -6
View File
@@ -144,6 +144,7 @@ crnl [\r]*[\n]
id [a-zA-Z_][a-zA-Z0-9_$]*
/* escaped identifier */
escid \\[^ \t\f\r\n]+
word [a-zA-Z0-9_]+
%%
@@ -382,10 +383,12 @@ escid \\[^ \t\f\r\n]+
"always_ff" { FL; return yALWAYS; }
"always_latch" { FL; return yALWAYS; }
"bit" { FL; return yBIT; }
"break" { FL; return yBREAK; }
"byte" { FL; return yBYTE; }
"chandle" { FL; return yCHANDLE; }
"clocking" { FL; return yCLOCKING; }
"context" { FL; return yCONTEXT; }
"continue" { FL; return yCONTINUE; }
"do" { FL; return yDO; }
"endclocking" { FL; return yENDCLOCKING; }
"endpackage" { FL; return yENDPACKAGE; }
@@ -403,6 +406,7 @@ escid \\[^ \t\f\r\n]+
"priority" { FL; return yPRIORITY; }
"program" { FL; return yPROGRAM; }
"pure" { FL; return yPURE; }
"return" { FL; return yRETURN; }
"shortint" { FL; return ySHORTINT; }
"static" { FL; return ySTATIC; }
"string" { FL; return ySTRING; }
@@ -419,10 +423,8 @@ escid \\[^ \t\f\r\n]+
"bind" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"binsof" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"break" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"class" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"constraint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"continue" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"covergroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"coverpoint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"cross" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
@@ -457,7 +459,6 @@ escid \\[^ \t\f\r\n]+
"randomize" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"randsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"ref" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"return" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"shortreal" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"solve" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"struct" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
@@ -632,6 +633,8 @@ escid \\[^ \t\f\r\n]+
"/*verilator parallel_case*/" { FL; return yVL_PARALLEL_CASE; }
"/*verilator public*/" { FL; return yVL_PUBLIC; }
"/*verilator public_flat*/" { FL; return yVL_PUBLIC_FLAT; }
"/*verilator public_flat_rd*/" { FL; return yVL_PUBLIC_FLAT_RD; }
"/*verilator public_flat_rw*/" { FL; return yVL_PUBLIC_FLAT_RW; } // The @(edge) is converted by the preproc
"/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; }
"/*verilator sc_clock*/" { FL; return yVL_CLOCK; }
"/*verilator sformat*/" { FL; return yVL_SFORMAT; }
@@ -645,7 +648,8 @@ escid \\[^ \t\f\r\n]+
"/*verilator lint_restore*/" {PARSEP->verilatorCmtLintRestore(); }
"/*verilator lint_save*/" {PARSEP->verilatorCmtLintSave(); }
"/*"[^*]*"*/" {PARSEP->verilatorCmtBad(yytext); }
"/**/" { }
"/*"[^*]+"*/" {PARSEP->verilatorCmtBad(yytext); }
}
/************************************************************************/
@@ -829,12 +833,14 @@ escid \\[^ \t\f\r\n]+
<STRING>\" { yy_pop_state();
FL; yylval.strp = PARSEP->newString(yytext+1,yyleng-2);
return yaSTRING; }
<STRING>{word} { yymore(); }
<STRING>. { yymore(); }
/************************************************************************/
/* Attributes */
<ATTRMODE>{crnl} { yymore(); NEXTLINE(); }
<ATTRMODE>"*)" { yy_pop_state(); }
<ATTRMODE>{word} { yymore(); }
<ATTRMODE>. { yymore(); }
<ATTRMODE><<EOF>> { yyerrorf("EOF in (*");
yyleng = 0; yy_pop_state(); }
@@ -863,7 +869,10 @@ escid \\[^ \t\f\r\n]+
"`autoexpand_vectornets" { } // Verilog-XL compatibility
"`celldefine" { PARSEP->inCellDefine(true); }
"`default_decay_time"{ws}+[^\n\r]* { } // Verilog spec - delays only
"`default_nettype"{ws}+[a-zA-Z0-9]* { yyerrorf("Unsupported: Verilog 2001 directive not implemented: %s",yytext); } // Verilog 2001
"`default_nettype"{ws}+"wire" { PARSEP->fileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE,true); }
"`default_nettype"{ws}+"none" { PARSEP->fileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE,false); }
"`default_nettype"{ws}+[a-zA-Z0-9]* { yyerrorf("Unsupported: `default_nettype of other than none or wire: %s",yytext); }
"`default_trireg_strength"{ws}+[^\n\r]* { yyerrorf("Unsupported: Verilog optional directive not implemented: %s",yytext); }
"`delay_mode_distributed" { } // Verilog spec - delays only
"`delay_mode_path" { } // Verilog spec - delays only
"`delay_mode_unit" { } // Verilog spec - delays only
@@ -887,7 +896,7 @@ escid \\[^ \t\f\r\n]+
"`psl" { if (PARSEP->optPsl()) { BEGIN PSL; } else { BEGIN IGNORE; } }
"`remove_gatenames" { } // Verilog-XL compatibility
"`remove_netnames" { } // Verilog-XL compatibility
"`resetall" { }
"`resetall" { PARSEP->fileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE,true); } // Rest handled by preproc
"`suppress_faults" { } // Verilog-XL compatibility
"`timescale"{ws}+[^\n\r]* { } // Verilog spec - not supported
+56 -33
View File
@@ -84,7 +84,7 @@ public:
}
// METHODS
AstNodeDType* createArray(AstNodeDType* basep, AstRange* rangep);
AstNodeDType* createArray(AstNodeDType* basep, AstRange* rangep, bool isPacked);
AstVar* createVariable(FileLine* fileline, string name, AstRange* arrayp, AstNode* attrsp);
AstNode* createSupplyExpr(FileLine* fileline, string name, int value);
AstText* createTextQuoted(FileLine* fileline, string text) {
@@ -113,7 +113,7 @@ public:
}
return pkgp;
}
AstNodeDType* addRange(AstBasicDType* dtypep, AstRange* rangesp) {
AstNodeDType* addRange(AstBasicDType* dtypep, AstRange* rangesp, bool isPacked) {
// If dtypep isn't basic, don't use this, call createArray() instead
if (!rangesp) {
return dtypep;
@@ -134,7 +134,7 @@ public:
dtypep->rangep(finalp);
dtypep->implicit(false);
}
return createArray(dtypep, rangearraysp);
return createArray(dtypep, rangearraysp, isPacked);
}
}
string deQuote(FileLine* fileline, string text);
@@ -264,6 +264,7 @@ class AstSenTree;
%token<fl> yAUTOMATIC "automatic"
%token<fl> yBEGIN "begin"
%token<fl> yBIT "bit"
%token<fl> yBREAK "break"
%token<fl> yBUF "buf"
%token<fl> yBUFIF0 "bufif0"
%token<fl> yBUFIF1 "bufif1"
@@ -275,6 +276,7 @@ class AstSenTree;
%token<fl> yCLOCKING "clocking"
%token<fl> yCMOS "cmos"
%token<fl> yCONTEXT "context"
%token<fl> yCONTINUE "continue"
%token<fl> yCOVER "cover"
%token<fl> yDEFAULT "default"
%token<fl> yDEFPARAM "defparam"
@@ -340,6 +342,7 @@ class AstSenTree;
%token<fl> yRCMOS "rcmos"
%token<fl> yREG "reg"
%token<fl> yREPEAT "repeat"
%token<fl> yRETURN "return"
%token<fl> yRNMOS "rnmos"
%token<fl> yRPMOS "rpmos"
%token<fl> yRTRAN "rtran"
@@ -431,6 +434,8 @@ class AstSenTree;
%token<fl> yVL_PARALLEL_CASE "/*verilator parallel_case*/"
%token<fl> yVL_PUBLIC "/*verilator public*/"
%token<fl> yVL_PUBLIC_FLAT "/*verilator public_flat*/"
%token<fl> yVL_PUBLIC_FLAT_RD "/*verilator public_flat_rd*/"
%token<fl> yVL_PUBLIC_FLAT_RW "/*verilator public_flat_rw*/"
%token<fl> yVL_PUBLIC_MODULE "/*verilator public_module*/"
%token<fl> yP_TICK "'"
@@ -814,7 +819,7 @@ port<nodep>: // ==IEEE: port
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE
{ $$=$4; VARDTYPE($3); $$->addNextNull(VARDONEP($$,$5,$6)); }
| portDirNetE signingE rangeList portSig variable_dimensionListE sigAttrListE
{ $$=$4; VARDTYPE(GRAMMARP->addRange(new AstBasicDType($3->fileline(), LOGIC_IMPLICIT, $2), $3)); $$->addNextNull(VARDONEP($$,$5,$6)); }
{ $$=$4; VARDTYPE(GRAMMARP->addRange(new AstBasicDType($3->fileline(), LOGIC_IMPLICIT, $2), $3,false)); $$->addNextNull(VARDONEP($$,$5,$6)); }
| portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE
{ $$=$2; /*VARDTYPE-same*/ $$->addNextNull(VARDONEP($$,$3,$4)); }
//
@@ -1026,7 +1031,7 @@ port_declaration<nodep>: // ==IEEE: port_declaration
list_of_variable_decl_assignments { $$ = $6; }
| port_directionReset port_declNetE yVAR implicit_typeE { VARDTYPE($4); }
list_of_variable_decl_assignments { $$ = $6; }
| port_directionReset port_declNetE signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($4->fileline(), LOGIC_IMPLICIT, $3),$4)); }
| port_directionReset port_declNetE signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($4->fileline(), LOGIC_IMPLICIT, $3),$4,false)); }
list_of_variable_decl_assignments { $$ = $6; }
| port_directionReset port_declNetE signing { VARDTYPE(new AstBasicDType($<fl>3, LOGIC_IMPLICIT, $3)); }
list_of_variable_decl_assignments { $$ = $5; }
@@ -1076,7 +1081,7 @@ data_type<dtypep>: // ==IEEE: data_type
// // This expansion also replicated elsewhere, IE data_type__AndID
data_typeNoRef { $$ = $1; }
// // IEEE: [ class_scope | package_scope ] type_identifier { packed_dimension }
| ps_type packed_dimensionListE { $$ = GRAMMARP->createArray($1,$2); }
| ps_type packed_dimensionListE { $$ = GRAMMARP->createArray($1,$2,true); }
//UNSUP class_scope_type packed_dimensionListE { UNSUP }
// // IEEE: class_type
//UNSUP class_typeWithoutId { $$ = $1; }
@@ -1085,7 +1090,7 @@ data_type<dtypep>: // ==IEEE: data_type
;
data_typeBasic<dtypep>: // IEEE: part of data_type
integer_vector_type signingE rangeListE { $1->setSignedState($2); $$ = GRAMMARP->addRange($1,$3); }
integer_vector_type signingE rangeListE { $1->setSignedState($2); $$ = GRAMMARP->addRange($1,$3,true); }
| integer_atom_type signingE { $1->setSignedState($2); $$ = $1; }
//UNSUP non_integer_type { UNSUP }
;
@@ -1187,12 +1192,12 @@ enum_base_typeE<dtypep>: // IEEE: enum_base_type
/* empty */ { $$ = new AstBasicDType(CRELINE(),AstBasicDTypeKwd::INT); }
// // Not in spec, but obviously "enum [1:0]" should work
// // implicit_type expanded, without empty
| signingE rangeList { $$ = GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1),$2); }
| signingE rangeList { $$ = GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1),$2,false); }
| signing { $$ = new AstBasicDType($<fl>1, LOGIC_IMPLICIT, $1); }
//
| integer_atom_type signingE { $1->setSignedState($2); $$ = $1; }
| integer_vector_type signingE rangeListE { $1->setSignedState($2); $$ = GRAMMARP->addRange($1,$3); }
| yaID__aTYPE rangeListE { $$ = GRAMMARP->createArray(new AstRefDType($<fl>1, *$1), $2); }
| integer_vector_type signingE rangeListE { $1->setSignedState($2); $$ = GRAMMARP->addRange($1,$3,false); }
| yaID__aTYPE rangeListE { $$ = GRAMMARP->createArray(new AstRefDType($<fl>1, *$1), $2, false); }
;
enum_nameList<nodep>:
@@ -1241,7 +1246,7 @@ data_declarationVarFront: // IEEE: part of data_declaration
// // implicit_type expanded into /*empty*/ or "signingE rangeList"
constE yVAR lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE($4); }
| constE yVAR lifetimeE { /*VARRESET-in-ddVar*/ VARDTYPE(new AstBasicDType($<fl>2, LOGIC_IMPLICIT)); }
| constE yVAR lifetimeE signingE rangeList { /*VARRESET-in-ddVar*/ VARDTYPE(GRAMMARP->addRange(new AstBasicDType($<fl>2, LOGIC_IMPLICIT, $4),$5)); }
| constE yVAR lifetimeE signingE rangeList { /*VARRESET-in-ddVar*/ VARDTYPE(GRAMMARP->addRange(new AstBasicDType($<fl>2, LOGIC_IMPLICIT, $4),$5,false)); }
//
// // Expanded: "constE lifetimeE data_type"
| /**/ data_type { /*VARRESET-in-ddVar*/ VARDTYPE($1); }
@@ -1258,13 +1263,13 @@ constE: // IEEE: part of data_declaration
implicit_typeE<dtypep>: // IEEE: part of *data_type_or_implicit
// // Also expanded in data_declaration
/* empty */ { $$ = NULL; }
| signingE rangeList { $$ = GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1),$2); }
| signingE rangeList { $$ = GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1),$2,false); }
| signing { $$ = new AstBasicDType($<fl>1, LOGIC_IMPLICIT, $1); }
;
type_declaration<nodep>: // ==IEEE: type_declaration
// // Use idAny, as we can redeclare a typedef on an existing typedef
/*U*/ yTYPEDEF data_type idAny variable_dimensionListE ';' { $$ = new AstTypedef($<fl>1, *$3, GRAMMARP->createArray($2,$4)); SYMP->reinsert($$); }
yTYPEDEF data_type idAny variable_dimensionListE ';' { $$ = new AstTypedef($<fl>1, *$3, GRAMMARP->createArray($2,$4,false)); SYMP->reinsert($$); }
//UNSUP yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ ';' { $$ = NULL; $1->v3error("Unsupported: SystemVerilog 2005 typedef in this context"); } //UNSUP
// // Combines into above "data_type id" rule
// // Verilator: Not important what it is in the AST, just need to make sure the yaID__aTYPE gets returned
@@ -1436,6 +1441,7 @@ genvar_iteration<nodep>: // ==IEEE: genvar_iteration
| varRefBase yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); }
// // inc_or_dec_operator
// When support ++ as a real AST type, maybe AstWhile::precondsp() becomes generic AstMathStmt?
| yP_PLUSPLUS varRefBase { $$ = new AstAssign($1,$2,new AstAdd ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
| yP_MINUSMINUS varRefBase { $$ = new AstAssign($1,$2,new AstSub ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
| varRefBase yP_PLUSPLUS { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
@@ -1537,6 +1543,10 @@ sigAttr<nodep>:
| yVL_CLOCK_ENABLE { $$ = new AstAttrOf($1,AstAttrType::VAR_CLOCK_ENABLE); }
| yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC); }
| yVL_PUBLIC_FLAT { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT); }
| yVL_PUBLIC_FLAT_RD { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RD); }
| yVL_PUBLIC_FLAT_RW { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); }
| yVL_PUBLIC_FLAT_RW attr_event_control { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW);
$$ = $$->addNext(new AstAlwaysPublic($1,$2,NULL)); }
| yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); }
| yVL_SFORMAT { $$ = new AstAttrOf($1,AstAttrType::VAR_SFORMAT); }
;
@@ -1553,7 +1563,7 @@ rangeList<rangep>: // IEEE: {packed_dimension}
wirerangeE<dtypep>:
/* empty */ { $$ = new AstBasicDType(CRELINE(), LOGIC); } // not implicit
| rangeList { $$ = GRAMMARP->addRange(new AstBasicDType(CRELINE(), LOGIC),$1); } // not implicit
| rangeList { $$ = GRAMMARP->addRange(new AstBasicDType(CRELINE(), LOGIC),$1,false); } // not implicit
;
// IEEE: select
@@ -1661,13 +1671,18 @@ cellpinItemE<pinp>: // IEEE: named_port_connection + named_parameter_assignment
| '.' idSVKwd { $$ = NULL; PINNUMINC(); }
| '.' idAny { $$ = new AstPin($1,PINNUMINC(),*$2,new AstVarRef($1,*$2,false)); $$->svImplicit(true);}
| '.' idAny '(' ')' { $$ = NULL; PINNUMINC(); }
// // mintypmax is expanded here, as it might be a UDP or gate primitive
| '.' idAny '(' expr ')' { $$ = new AstPin($1,PINNUMINC(),*$2,$4); }
//UNSUP '.' idAny '(' expr ':' expr ')' { }
//UNSUP '.' idAny '(' expr ':' expr ':' expr ')' { }
// // For parameters
//UNSUP '.' idAny '(' data_type ')' { PINDONE($1,$2,$4); GRAMMARP->pinNumInc(); }
// // For parameters
//UNSUP data_type { PINDONE($1->fileline(),"",$1); GRAMMARP->pinNumInc(); }
//
| expr { $$ = new AstPin($1->fileline(),PINNUMINC(),"",$1); }
//UNSUP expr ':' expr { }
//UNSUP expr ':' expr ':' expr { }
// // Floatnum should only occur with UDPs, but since ports aren't floats, it's legal to round always
| yaFLOATNUM { $$ = new AstPin($<fl>1,PINNUMINC(),"",new AstConst($<fl>1,AstConst::Unsized32(),(int)(($1<0)?($1-0.5):($1+0.5)))); }
;
@@ -1675,6 +1690,12 @@ cellpinItemE<pinp>: // IEEE: named_port_connection + named_parameter_assignment
//************************************************
// EventControl lists
attr_event_control<sentreep>: // ==IEEE: event_control
'@' '(' event_expression ')' { $$ = new AstSenTree($1,$3); }
| '@' '(' '*' ')' { $$ = NULL; }
| '@' '*' { $$ = NULL; }
;
event_controlE<sentreep>:
/* empty */ { $$ = NULL; }
| event_control { $$ = $1; }
@@ -1831,7 +1852,7 @@ statement_item<nodep>: // IEEE: statement_item
| statementVerilatorPragmas { $$ = $1; }
//
// // IEEE: disable_statement
//UNSUP yDISABLE hierarchical_identifier/*task_or_block*/ ';' { UNSUP }
//UNSUP yDISABLE idAny/*hierarchical_identifier-task_or_block*/ ';' { UNSUP }
//UNSUP yDISABLE yFORK ';' { UNSUP }
// // IEEE: event_trigger
//UNSUP yP_MINUSGT hierarchical_identifier/*event*/ ';' { UNSUP }
@@ -1842,15 +1863,15 @@ statement_item<nodep>: // IEEE: statement_item
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile($1,$3,$5);}
// // for's first ';' is in for_initalization
| yFOR '(' for_initialization expr ';' for_stepE ')' stmtBlock
{ $$ = new AstBegin($1,"",$3); $3->addNext(new AstFor($1,NULL,$4,$6,$8));}
{ $$ = new AstBegin($1,"",$3); $3->addNext(new AstWhile($1, $4,$8,$6)); }
| yDO stmtBlock yWHILE '(' expr ')' { $$ = $2->cloneTree(true); $$->addNext(new AstWhile($1,$5,$2));}
//UNSUP yFOREACH '(' idClassForeach/*array_id[loop_variables]*/ ')' stmt { UNSUP }
//
// // IEEE: jump_statement
//UNSUP yRETURN ';' { UNSUP }
//UNSUP yRETURN expr ';' { UNSUP }
//UNSUP yBREAK ';' { UNSUP }
//UNSUP yCONTINUE ';' { UNSUP }
| yRETURN ';' { $$ = new AstReturn($1); }
| yRETURN expr ';' { $$ = new AstReturn($1,$2); }
| yBREAK ';' { $$ = new AstBreak($1); }
| yCONTINUE ';' { $$ = new AstContinue($1); }
//
//UNSUP par_block { $$ = $1; }
// // IEEE: procedural_timing_control_statement + procedural_timing_control
@@ -2141,7 +2162,7 @@ funcId<ftaskp>: // IEEE: function_data_type_or_implicit + part of function_bod
SYMP->pushNewUnder($$, NULL); }
| signingE rangeList tfIdScoped
{ $$ = new AstFunc ($<fl>3,*$<strp>3,NULL,
GRAMMARP->addRange(new AstBasicDType($<fl>3, LOGIC_IMPLICIT, $1), $2));
GRAMMARP->addRange(new AstBasicDType($<fl>3, LOGIC_IMPLICIT, $1), $2,false));
SYMP->pushNewUnder($$, NULL); }
| signing tfIdScoped
{ $$ = new AstFunc ($<fl>2,*$<strp>2,NULL,
@@ -2211,14 +2232,14 @@ tf_port_item<nodep>: // ==IEEE: tf_port_item
tf_port_itemFront: // IEEE: part of tf_port_item, which has the data type
data_type { VARDTYPE($1); }
| signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1), $2)); }
| signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($2->fileline(), LOGIC_IMPLICIT, $1), $2, false)); }
| signing { VARDTYPE(new AstBasicDType($<fl>1, LOGIC_IMPLICIT, $1)); }
| yVAR data_type { VARDTYPE($2); }
| yVAR implicit_typeE { VARDTYPE($2); }
//
| tf_port_itemDir /*implicit*/ { VARDTYPE(NULL); /*default_nettype-see spec*/ }
| tf_port_itemDir data_type { VARDTYPE($2); }
| tf_port_itemDir signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($3->fileline(), LOGIC_IMPLICIT, $2),$3)); }
| tf_port_itemDir signingE rangeList { VARDTYPE(GRAMMARP->addRange(new AstBasicDType($3->fileline(), LOGIC_IMPLICIT, $2),$3,false)); }
| tf_port_itemDir signing { VARDTYPE(new AstBasicDType($<fl>2, LOGIC_IMPLICIT, $2)); }
| tf_port_itemDir yVAR data_type { VARDTYPE($3); }
| tf_port_itemDir yVAR implicit_typeE { VARDTYPE($3); }
@@ -2738,9 +2759,14 @@ specifyJunk:
;
specparam_declaration<nodep>: // ==IEEE: specparam_declaration
ySPECPARAM junkToSemi ';' { $$ = NULL; }
ySPECPARAM junkToSemiList ';' { $$ = NULL; }
;
junkToSemiList:
junkToSemi { } /* ignored */
| junkToSemiList junkToSemi { } /* ignored */
;
junkToSemi:
BISONPRE_NOT(';',yENDSPECIFY,yENDMODULE) { }
| error {}
@@ -2882,16 +2908,13 @@ concurrent_assertion_statement<nodep>: // ==IEEE: concurrent_assertion_statement
property_spec<nodep>: // IEEE: property_spec
//UNSUP: This rule has been super-specialized to what is supported now
'@' '(' senitemEdge ')' property_specDisable expr { $$ = new AstPslClocked($1,$3,$5,$6); }
'@' '(' senitemEdge ')' yDISABLE yIFF '(' expr ')' expr
{ $$ = new AstPslClocked($1,$3,$8,$10); }
| '@' '(' senitemEdge ')' expr { $$ = new AstPslClocked($1,$3,NULL,$5); }
| property_specDisable expr { $$ = new AstPslClocked($2->fileline(),NULL,$1,$2); }
| yDISABLE yIFF '(' expr ')' expr { $$ = new AstPslClocked($4->fileline(),NULL,$4,$6); }
| expr { $$ = new AstPslClocked($1->fileline(),NULL,NULL,$1); }
;
property_specDisable<nodep>: // IEEE: part of property_spec
yDISABLE yIFF '(' expr ')' { $$ = $4; }
;
immediate_assert_statement<nodep>: // ==IEEE: immediate_assert_statement
// // action_block expanded here, for compatibility with AstVAssert
yASSERT '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE { $$ = new AstVAssert($1,$3,$5, GRAMMARP->createDisplayError($1)); }
@@ -3045,7 +3068,7 @@ AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, string name, int v
return nodep;
}
AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstRange* rangep) {
AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstRange* rangep, bool isPacked) {
// Split RANGE0-RANGE1-RANGE2 into ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3),RANGE),RANGE)
AstNodeDType* arrayp = basep;
if (rangep) { // Maybe no range - return unmodified base type
@@ -3053,7 +3076,7 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstRange* rangep)
while (rangep) {
AstRange* prevp = rangep->backp()->castRange();
if (prevp) rangep->unlinkFrBack();
arrayp = new AstArrayDType(rangep->fileline(), arrayp, rangep);
arrayp = new AstArrayDType(rangep->fileline(), arrayp, rangep, isPacked);
rangep = prevp;
}
}
@@ -3085,7 +3108,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name, AstRange
}
// Split RANGE0-RANGE1-RANGE2 into ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3),RANGE),RANGE)
AstNodeDType* arrayDTypep = createArray(dtypep,arrayp);
AstNodeDType* arrayDTypep = createArray(dtypep,arrayp,false);
AstVar* nodep = new AstVar(fileline, type, name, arrayDTypep);
nodep->addAttrsp(attrsp);
+40 -4
View File
@@ -40,6 +40,7 @@ our @Orig_ARGV = @ARGV;
our @Orig_ARGV_Sw; foreach (@Orig_ARGV) { push @Orig_ARGV_Sw, $_ if /^-/ && !/^-j/; }
$Debug = 0;
my $opt_atsim;
my $opt_benchmark;
my @opt_tests;
my $opt_gdb;
@@ -61,6 +62,7 @@ Getopt::Long::config ("pass_through");
if (! GetOptions (
"benchmark:i" => sub { $opt_benchmark = $_[1] ? $_[1] : 1; },
"debug" => \&debug,
"atsim|athdl!"=> \$opt_atsim,
"gdb!" => \$opt_gdb,
"gdbbt!" => \$opt_gdbbt,
"gdbsim!" => \$opt_gdbsim,
@@ -86,7 +88,7 @@ $opt_jobs = calc_jobs() if defined $opt_jobs && $opt_jobs==0;
$Fork->max_proc($opt_jobs);
if (!$opt_iv && !$opt_vcs && !$opt_nc && !$opt_vlt) {
if (!$opt_atsim && !$opt_iv && !$opt_vcs && !$opt_nc && !$opt_vlt) {
$opt_vlt = 1;
}
@@ -101,6 +103,7 @@ my $leftcnt=0; my $okcnt=0; my $failcnt=0; my $skcnt=0;
my @fails;
foreach my $testpl (@opt_tests) {
one_test(pl_filename => $testpl, atsim=>1) if $opt_atsim;
one_test(pl_filename => $testpl, iv=>1) if $opt_iv;
one_test(pl_filename => $testpl, nc=>1) if $opt_nc;
one_test(pl_filename => $testpl, vcs=>1) if $opt_vcs;
@@ -183,7 +186,7 @@ sub parameter {
elsif ($param =~ /\.pl/) {
push @opt_tests, $param;
}
elsif ($param =~ /^--debugi/) {
elsif ($param =~ /^-?-debugi/) {
push @Opt_Driver_Verilator_Flags, $param;
$_Parameter_Next_Level = $param;
}
@@ -261,6 +264,12 @@ sub new {
v_flags2 => [], # Overridden in some sim files
v_other_filenames => [], # After the filename so we can spec multiple files
all_run_flags => [],
# ATSIM
atsim => 0,
atsim_flags => [split(/\s+/,"-c +sv +define+atsim"),
"+sv_dir+$self->{obj_dir}/.athdl_compile"],
atsim_flags2 => [], # Overridden in some sim files
atsimrun_flags => [],
# IV
iv => 0,
iv_flags => [split(/\s+/,"+define+iverilog -o $self->{obj_dir}/simiv")],
@@ -288,6 +297,7 @@ sub new {
%$self};
bless $self, $class;
$self->{mode} ||= "atsim" if $self->{atsim};
$self->{mode} ||= "vcs" if $self->{vcs};
$self->{mode} ||= "vlt" if $self->{vlt};
$self->{mode} ||= "nc" if $self->{nc};
@@ -387,6 +397,20 @@ sub compile {
$self->{trace} = 1 if ($opt_trace || $checkflags =~ /-trace\b/);
$self->{coverage} = 1 if ($checkflags =~ /-coverage\b/);
if ($param{atsim}) {
$self->_make_top();
$self->_run(logfile=>"$self->{obj_dir}/atsim_compile.log",
fails=>$param{fails},
cmd=>[($ENV{VERILATOR_ATSIM}||"atsim"),
@{$param{atsim_flags}},
@{$param{atsim_flags2}},
@{$param{v_flags}},
@{$param{v_flags2}},
$param{top_filename},
$param{top_shell_filename},
@{$param{v_other_filenames}},
]);
}
if ($param{vcs}) {
$self->_make_top();
$self->_run(logfile=>"$self->{obj_dir}/vcs_compile.log",
@@ -511,6 +535,14 @@ sub execute {
my $run_env = $param{run_env};
$run_env .= ' ' if $run_env;
if ($param{atsim}) {
$self->_run(logfile=>"$self->{obj_dir}/atsim_sim.log",
fails=>$param{fails},
cmd=>["echo q | ".$run_env."$self->{obj_dir}/athdl_sv",
@{$param{atsimrun_flags}},
@{$param{all_run_flags}},
]);
}
if ($param{iv}) {
$self->_run(logfile=>"$self->{obj_dir}/iv_sim.log",
fails=>$param{fails},
@@ -522,7 +554,7 @@ sub execute {
if ($param{nc}) {
$self->_run(logfile=>"$self->{obj_dir}/nc_sim.log",
fails=>$param{fails},
cmd=>[$run_env.($ENV{VERILATOR_NCVERILOG}||"ncverilog"),
cmd=>["echo q | ".$run_env.($ENV{VERILATOR_NCVERILOG}||"ncverilog"),
@{$param{ncrun_flags}},
@{$param{all_run_flags}},
]);
@@ -531,7 +563,7 @@ sub execute {
#my $fh = IO::File->new(">simv.key") or die "%Error: $! simv.key,";
#$fh->print("quit\n"); $fh->close;
$self->_run(logfile=>"$self->{obj_dir}/vcs_sim.log",
cmd=>[$run_env."./simv",
cmd=>["echo q | ".$run_env."./simv",
@{$param{all_run_flags}},
],
%param,
@@ -1166,6 +1198,10 @@ driver.pl invokes Verilator or another simulator on each little test file.
=over 4
=item --atsim
Run using ATSIM.
=item --benchmark [<cycles>]
Show execution times of each step. If an optional number is given,
+1 -1
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags2 => ["--stats"],
verilator_flags2 => ["--stats"],
);
if ($Self->{v3}) {
+1 -1
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags2 => ["--stats"],
verilator_flags2 => ["--stats"],
);
if ($Self->{v3}) {
+1 -1
View File
@@ -16,7 +16,7 @@ compile (
);
execute (
fails => 1,
fails => $Self->{vlt},
);
ok(1);
+1 -1
View File
@@ -16,7 +16,7 @@ compile (
execute (
check_finished=>0,
fails=>1,
fails=> $Self->{vlt},
expect=>
'%Error: t_assert_synth.v:\d+: Assertion failed in top.v: synthesis full_case'
);
+1 -1
View File
@@ -16,7 +16,7 @@ compile (
execute (
check_finished=>0,
fails=>1,
fails => $Self->{v3},
expect=>
'%Error: t_assert_synth.v:\d+: Assertion failed in top.v: synthesis parallel_case'
);
+1 -1
View File
@@ -10,7 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
$Self->{golden_out} ||= "t/$Self->{name}.out";
compile (
v_flags2 => [$Self->{v3}?"--stats --O3 -x-assign fast":""],
verilator_flags2 => ["--stats --O3 -x-assign fast"],
);
execute (
+1 -1
View File
@@ -10,7 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
$Self->{golden_out} ||= "t/$Self->{name}.out";
compile (
v_flags2 => [$Self->{v3}?"--stats --O3 -x-assign fast":""],
verilator_flags2 => ["--stats --O3 -x-assign fast"],
);
execute (
+1 -1
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags2 => ["--x-assign 0"],
verilator_flags2 => ["--x-assign 0"],
);
execute (
+1 -1
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags2 => ["--stats"],
verilator_flags2 => ["--stats"],
);
execute (
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# 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
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;
+178
View File
@@ -0,0 +1,178 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2010 by Wilson Snyder.
//
// --------------------------------------------------------
// Bug Description:
//
// Issue: The gated clock gclk_vld[0] toggles but dvld[0]
// input to the flop does not propagate to the output
// signal entry_vld[0] correctly. The value that propagates
// is the new value of dvld[0] not the one just before the
// posedge of gclk_vld[0].
// --------------------------------------------------------
// Define to see the bug with test failing with gated clock 'gclk_vld'
// Comment out the define to see the test passing with ungated clock 'clk'
`define GATED_CLK_TESTCASE 1
// A side effect of the problem is this warning, disabled by default
//verilator lint_on IMPERFECTSCH
// Test Bench
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
reg [63:0] crc;
// Take CRC data and apply to testblock inputs
wire [7:0] dvld = crc[7:0];
wire [7:0] ff_en_e1 = crc[15:8];
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire [7:0] entry_vld; // From test of Test.v
wire [7:0] ff_en_vld; // From test of Test.v
// End of automatics
Test test (/*AUTOINST*/
// Outputs
.ff_en_vld (ff_en_vld[7:0]),
.entry_vld (entry_vld[7:0]),
// Inputs
.clk (clk),
.dvld (dvld[7:0]),
.ff_en_e1 (ff_en_e1[7:0]));
reg err_code;
reg ffq_clk_active;
reg [7:0] prv_dvld;
initial begin
err_code = 0;
ffq_clk_active = 0;
end
always @ (posedge clk) begin
prv_dvld = test.dvld;
end
always @ (negedge test.ff_entry_dvld_0.clk) begin
ffq_clk_active = 1;
if (test.entry_vld[0] !== prv_dvld[0]) err_code = 1;
end
// Test loop
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d crc=%x ",$time, cyc, crc);
$display(" en=%b fen=%b d=%b ev=%b",
test.flop_en_vld[0], test.ff_en_vld[0],
test.dvld[0], test.entry_vld[0]);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
if (cyc<3) begin
crc <= 64'h5aef0c8d_d70a4497;
end
else if (cyc==99) begin
$write("[%0t] cyc==%0d crc=%x\n",$time, cyc, crc);
if (ffq_clk_active == 0) begin
$display ("----");
$display ("%%Error: TESTCASE FAILED with no Clock arriving at FFQs");
$display ("----");
$stop;
end
else if (err_code) begin
$display ("----");
$display ("%%Error: TESTCASE FAILED with invalid propagation of 'd' to 'q' of FFQs");
$display ("----");
$stop;
end
else begin
$write("*-* All Finished *-*\n");
$finish;
end
end
end
endmodule
module llq (clk, d, q);
parameter WIDTH = 32;
input clk;
input [WIDTH-1:0] d;
output [WIDTH-1:0] q;
reg [WIDTH-1:0] qr;
/* verilator lint_off COMBDLY */
always @(clk or d)
if (clk == 1'b0)
qr <= d;
/* verilator lint_on COMBDLY */
assign q = qr;
endmodule
module ffq (clk, d, q);
parameter WIDTH = 32;
input clk;
input [WIDTH-1:0] d;
output [WIDTH-1:0] q;
reg [WIDTH-1:0] qr;
always @(posedge clk)
qr <= d;
assign q = qr;
endmodule
// DUT module
module Test (/*AUTOARG*/
// Outputs
ff_en_vld, entry_vld,
// Inputs
clk, dvld, ff_en_e1
);
input clk;
input [7:0] dvld;
input [7:0] ff_en_e1;
output [7:0] ff_en_vld;
output wire [7:0] entry_vld;
wire [7:0] gclk_vld;
wire [7:0] ff_en_vld /*verilator clock_enable*/;
reg [7:0] flop_en_vld;
always @(posedge clk) flop_en_vld <= ff_en_e1;
// clock gating
`ifdef GATED_CLK_TESTCASE
assign gclk_vld = {8{clk}} & ff_en_vld;
`else
assign gclk_vld = {8{clk}};
`endif
// latch for avoiding glitch on the clock gating control
llq #(8) dp_ff_en_vld (.clk(clk), .d(flop_en_vld), .q(ff_en_vld));
// flops that use the gated clock signal
ffq #(1) ff_entry_dvld_0 (.clk(gclk_vld[0]), .d(dvld[0]), .q(entry_vld[0]));
ffq #(1) ff_entry_dvld_1 (.clk(gclk_vld[1]), .d(dvld[1]), .q(entry_vld[1]));
ffq #(1) ff_entry_dvld_2 (.clk(gclk_vld[2]), .d(dvld[2]), .q(entry_vld[2]));
ffq #(1) ff_entry_dvld_3 (.clk(gclk_vld[3]), .d(dvld[3]), .q(entry_vld[3]));
ffq #(1) ff_entry_dvld_4 (.clk(gclk_vld[4]), .d(dvld[4]), .q(entry_vld[4]));
ffq #(1) ff_entry_dvld_5 (.clk(gclk_vld[5]), .d(dvld[5]), .q(entry_vld[5]));
ffq #(1) ff_entry_dvld_6 (.clk(gclk_vld[6]), .d(dvld[6]), .q(entry_vld[6]));
ffq #(1) ff_entry_dvld_7 (.clk(gclk_vld[7]), .d(dvld[7]), .q(entry_vld[7]));
endmodule
+1 -1
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
verilator_flags2 => [$Self->{v3}?'--sp --coverage-line':''],
verilator_flags2 => ['--sp --coverage-line'],
);
execute (
+4
View File
@@ -48,7 +48,11 @@ module t (/*AUTOARG*/
toggle <= '1;
end
else if (cyc==5) begin
`ifdef VERILATOR
$c("call_task();");
`else
call_task();
`endif
end
else if (cyc==10) begin
$write("*-* All Finished *-*\n");
+3 -2
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags2 => [$Self->{v3}?'--assert --sp --coverage-user':''],
verilator_flags2 => ['--assert --sp --coverage-user'],
);
execute (
@@ -20,7 +20,8 @@ execute (
# Allow old Perl format dump, or new binary dump
# Check that the hierarchy doesn't include __PVT__
# Otherwise our coverage reports would look really ugly
file_grep ($Self->{coverage_filename}, qr/(top\.v\.sub.*.cyc_eq_5)/);
file_grep ($Self->{coverage_filename}, qr/(top\.v\.sub.*.cyc_eq_5)/)
if $Self->{vlt};
ok(1);
1;
+3 -2
View File
@@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
verilator_flags2 => [$Self->{v3}?'--sp --coverage-toggle --stats':''],
verilator_flags2 => ['--sp --coverage-toggle --stats'],
);
execute (
@@ -18,7 +18,8 @@ execute (
# Read the input .v file and do any CHECK_COVER requests
inline_checks();
file_grep ($Self->{stats}, qr/Coverage, Toggle points joined\s+25/i);
file_grep ($Self->{stats}, qr/Coverage, Toggle points joined\s+25/i)
if $Self->{vlt};
ok(1);
1;
+1
View File
@@ -22,6 +22,7 @@ if (!-r "$root/.git") {
my $cmd = "cd $root && fgrep -n include $files | sort";
my $grep = `$cmd`;
foreach my $line (split /\n/, $grep) {
next if $line =~ /vpi_user.h/; # IEEE Standard file - can't change it
my $hit;
$hit = 1 if $line =~ /\bassert\.h/;
$hit = 1 if $line =~ /\bctype\.h/;
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2010 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
use Cwd;
my $root = "..";
my $Debug;
if (!-r "$root/.git") {
$Self->skip("Not in a git repository");
} else {
my $cwd = getcwd();
my $destdir = "$cwd/".$Self->{obj_dir};
# Start clean
$Self->_run(cmd=>["rm -rf $destdir && mkdir -p $destdir"],
check_finished=>0);
# Install into temp area
print "Install...\n";
$Self->_run (cmd=>["cd $root && make DESTDIR=$destdir install"],
check_finished=>0);
# Check we can run a test
# Unfortunately the prefix was hardcoded in the exec at a different place,
# so we can't do much here.
#print "Check install...\n";
# Uninstall
print "Uninstall...\n";
$Self->_run (cmd=>["cd $root && make DESTDIR=$destdir uninstall"],
check_finished=>0);
# Check empty
my @files;
$finds = `find $destdir -type f -print`;
foreach my $f (split /\n/, $finds) {
print "\tLEFT: $f\n";
$f =~ s!^$cwd!.!;
push @files, $f;
}
if ($#files >= 0) {
$Self->error("Uninstall missed files: ",join(' ',@files));
}
}
ok(1);
1;
+1 -1
View File
@@ -14,7 +14,7 @@
//*************************************************************************
#include <cstdio>
#include <svdpi.h>
#include "svdpi.h"
//======================================================================
+1 -1
View File
@@ -14,7 +14,7 @@
//*************************************************************************
#include <cstdio>
#include <svdpi.h>
#include "svdpi.h"
//======================================================================
+1 -1
View File
@@ -14,8 +14,8 @@
//*************************************************************************
#include <cstdio>
#include <svdpi.h>
#include <cstring>
#include "svdpi.h"
#ifdef _WIN32
# define T_PRI64 "I64"
+1 -1
View File
@@ -15,7 +15,7 @@
#include <cstdio>
#include <cstring>
#include <svdpi.h>
#include "svdpi.h"
//======================================================================
+1 -1
View File
@@ -14,7 +14,7 @@
//*************************************************************************
#include <cstdio>
#include <svdpi.h>
#include "svdpi.h"
//======================================================================
+147
View File
@@ -0,0 +1,147 @@
// -*- C++ -*-
//*************************************************************************
//
// Copyright 2010-2010 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "Vt_dpi_var.h"
#include "verilated.h"
#include "svdpi.h"
#include "verilated_syms.h"
//======================================================================
struct MyMon {
vluint32_t* sigsp[2];
MyMon() { sigsp[0]=NULL; sigsp[1]=NULL; }
};
MyMon mons[2];
void mon_register_a(const char* namep, void* sigp, bool isOut) {
// Callback from initial block in monitor
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_register_a(\"%s\", %p, %d);\n", namep, sigp, isOut);
#endif
mons[0].sigsp[isOut] = (vluint32_t*)sigp;
}
void mon_do(MyMon* monp) {
if (!monp->sigsp[0]) vl_fatal(__FILE__,__LINE__,"","never registered");
if (!monp->sigsp[1]) vl_fatal(__FILE__,__LINE__,"","never registered");
*monp->sigsp[1] = (*(monp->sigsp[0]))+1;
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_do(%08x(&%p) -> %08x(&%p));\n",
*(monp->sigsp[0]), monp->sigsp[0], *(monp->sigsp[1]), monp->sigsp[1]);
#endif
}
void mon_class_name(const char* namep) {
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_class_name(\"%s\");\n", namep);
#endif
// Check the C's calling name of "" doesn't lead to extra dots in the name()
if (namep && namep[0]=='.') vl_fatal(__FILE__,__LINE__,"", (string("Unexp class name ")+namep).c_str());
}
extern "C" void mon_scope_name(const char* namep);
void mon_scope_name(const char* namep) {
const char* modp = svGetNameFromScope(svGetScope());
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_scope_name('%s', \"%s\");\n", modp, namep);
#endif
if (strcmp(namep,"t.sub")) vl_fatal(__FILE__,__LINE__,"", (string("Unexp scope name ")+namep).c_str());
if (strcmp(modp,"t.sub")) vl_fatal(__FILE__,__LINE__,"", (string("Unexp dpiscope name ")+modp).c_str());
}
extern "C" void mon_register_b(const char* namep, int isOut);
void mon_register_b(const char* namep, int isOut) {
const char* modp = svGetNameFromScope(svGetScope());
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_register_b('%s', \"%s\", %d);\n", modp, namep, isOut);
#endif
// Use scope to get pointer and size of signal
const VerilatedScope* scopep = Verilated::dpiScope();
const VerilatedVar* varp = scopep->varFind(namep);
if (!varp) {
VL_PRINTF("%%Warning: mon_register_b signal not found: \"%s\"\n", namep);
} else if (varp->vltype() != VLVT_UINT32) {
VL_PRINTF("%%Warning: wrong type for signal: \"%s\"\n", namep);
} else {
vluint32_t* datap = (vluint32_t*)(varp->datap());
VL_PRINTF("- mon_register_b('%s', \"%s\", %p, %d);\n", modp, namep, datap, isOut);
mons[1].sigsp[isOut] = (vluint32_t*)(varp->datap());
}
}
extern "C" void mon_register_done();
void mon_register_done() {
const char* modp = svGetNameFromScope(svGetScope());
#ifdef TEST_VERBOSE
VL_PRINTF("- mon_register_done('%s');\n", modp);
#endif
// Print list of all signals - if we didn't register2 anything we'd pick them off here
const VerilatedScope* scopep = Verilated::dpiScope();
if (VerilatedVarNameMap* varsp = scopep->varsp()) {
for (VerilatedVarNameMap::const_iterator it = varsp->begin();
it != varsp->end(); ++it) {
VL_PRINTF("- mon2: %s\n", it->first);
}
}
}
extern "C" void mon_eval();
void mon_eval() {
// Callback from always@ negedge
mon_do(&mons[0]);
mon_do(&mons[1]);
}
//======================================================================
unsigned int main_time = false;
double sc_time_stamp () {
return main_time;
}
int main(int argc, char **argv, char **env) {
double sim_time = 1100;
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
VM_PREFIX* topp = new VM_PREFIX (""); // Note null name - we're flattening it out
#ifdef VERILATOR
# ifdef TEST_VERBOSE
Verilated::scopesDump();
# endif
#endif
topp->eval();
topp->clk = 0;
main_time += 10;
while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) {
main_time += 1;
topp->eval();
topp->clk = !topp->clk;
//mon_do();
}
if (!Verilated::gotFinish()) {
vl_fatal(__FILE__,__LINE__,"main", "%Error: Timeout; never got a $finish");
}
topp->final();
delete topp; topp=NULL;
exit(0L);
}
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# 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
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
compile (
make_top_shell => 0,
make_main => 0,
verilator_flags2 => ["--exe --no-l2name $Self->{t_dir}/t_dpi_var.cpp"],
);
execute (
check_finished=>1,
);
ok(1);
1;
+91
View File
@@ -0,0 +1,91 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Copyright 2010 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
wire monclk = ~clk;
int in;
int fr_a;
int fr_b;
int fr_chk;
sub sub (.*);
// Test loop
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d in=%x fr_a=%x b=%x fr_chk=%x\n",$time, cyc, in, fr_a, fr_b, fr_chk);
`endif
cyc <= cyc + 1;
in <= {in[30:0], in[31]^in[2]^in[0]};
if (cyc==0) begin
// Setup
in <= 32'hd70a4497;
end
else if (cyc<3) begin
end
else if (cyc<10) begin
if (fr_chk != fr_a) $stop;
if (fr_chk != fr_b) $stop;
end
else if (cyc==10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(posedge t.monclk) begin
mon_eval();
end
endmodule
import "DPI-C" context function void mon_scope_name (input string formatted /*verilator sformat*/ );
import "DPI-C" context function void mon_register_b(string name, int isOut);
import "DPI-C" context function void mon_register_done();
import "DPI-C" context function void mon_eval();
module sub (/*AUTOARG*/
// Outputs
fr_a, fr_b, fr_chk,
// Inputs
in
);
`systemc_imp_header
void mon_class_name(const char* namep);
void mon_register_a(const char* namep, void* sigp, bool isOut);
`verilog
input int in /*verilator public_flat_rd*/;
output int fr_a /*verilator public_flat_rw @(posedge t.monclk)*/;
output int fr_b /*verilator public_flat_rw @(posedge t.monclk)*/;
output int fr_chk;
always @* fr_chk = in + 1;
initial begin
// Test the naming
$c("mon_class_name(name());");
mon_scope_name("%m");
// Scheme A - pass pointer directly
$c("mon_register_a(\"in\",&",in,",false);");
$c("mon_register_a(\"fr_a\",&",fr_a,",true);");
// Scheme B - use VPIish callbacks to see what signals exist
mon_register_b("in", 0);
mon_register_b("fr_b", 1);
mon_register_done();
end
endmodule
+2
View File
@@ -7,6 +7,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} or $Self->skip("Verilator only test");
$Self->_run (cmd=>["cd $Self->{obj_dir}"
." && g++ -c ../../t/t_flag_ldflags_a.cpp"
." && ar r t_flag_ldflags_a.a t_flag_ldflags_a.o"
+1 -1
View File
@@ -14,7 +14,7 @@
//*************************************************************************
#include <cstdio>
#include <svdpi.h>
#include "svdpi.h"
//======================================================================
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# 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
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

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