Compare commits

...
165 Commits
Author SHA1 Message Date
Wilson Snyder 35fe205fd6 Version bump 2019-08-29 18:58:38 -04:00
Todd Strader 70cf2582d6 Add XSim support to driver.pl, bug1493. 2019-08-29 17:00:49 -04:00
Todd Strader ec620387af Add --dpi-hdr-only, bug1491. 2019-08-27 21:36:59 -04:00
Wilson Snyder e1e4bde125 Remove old V3ClkGater code 2019-08-27 17:51:06 -04:00
Todd Strader b947391662 Retain widthSized when simulating 2019-08-12 15:38:58 -04:00
Wilson Snyder edc0f442e4 Tests: Add tests for bug1487. 2019-08-09 05:45:44 -04:00
Wilson Snyder 562b9551bf Internals: Remove some unused arguments. No functional change intended. 2019-08-09 04:12:49 -04:00
Wilson Snyder f74af949da Remove trailing spaces. 2019-08-08 17:53:49 -04:00
Wilson Snyder 81e806e895 Fix elaboration time errors, bug1429. 2019-08-04 22:34:54 -04:00
Wilson Snyder 7d4958264a Support arbitrary-argument $warning/$info etc 2019-08-04 21:50:08 -04:00
Wilson Snyder 88b87429dc Tests: Add elaboration info test. 2019-08-04 21:31:06 -04:00
Wilson Snyder 429f463032 Don't check SYSTEMC environment, for part of CMake support, bug1363. 2019-08-04 20:21:58 -04:00
Wilson Snyder af4a39603c Makefile: Run hello world before other examples. 2019-08-04 20:20:29 -04:00
Wilson Snyder a13d230d8c Tests: Less verbosity 2019-08-04 15:04:34 -04:00
Wilson Snyder b7bd5956ea Concats should be unsigned 2019-08-04 14:48:17 -04:00
Wilson Snyder 1a36e05cc6 Fix core dump on v3Number replace'ed assertions. 2019-08-04 14:14:28 -04:00
Wilson Snyder 71265a8ec9 Internals: Rename unsized functions. No functional change. 2019-08-04 12:39:35 -04:00
Wilson Snyder 3a720204c2 Internals: Remove m_logicMap as m_detailedMap covers it. No functional change intended. 2019-08-04 08:16:24 -04:00
Wilson Snyder a95f58749f Fix internal error on gate optimization of assign, bug1475. 2019-08-03 21:49:39 -04:00
Wilson Snyder 9e58ede480 Internals: Rename V3Hashed functions for future commit. No functional change. 2019-08-03 19:05:59 -04:00
Wilson Snyder cf2ac30f66 Fix date. 2019-08-02 21:39:32 -04:00
Wilson Snyder 68b22a4a3d Tests: Summarize any skips. 2019-07-30 22:20:37 -04:00
Wilson Snyder 875dc9a27e --debug doesn't take an arg 2019-07-30 18:40:38 -04:00
Maarten De Braekeleer 1332446f2a Spelling fixes.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-30 18:27:36 -04:00
Wilson Snyder 6b798830c9 Fix spacing 2019-07-29 21:07:37 -04:00
Wilson Snyder ffa23b59b8 Tests: Add iff test, bug1482. 2019-07-26 19:31:02 -04:00
Maarten De Braekeleer 7d27461054 Spelling fixes.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-26 12:58:13 -04:00
Maarten De Braekeleer b3f318d8ab Add contributor certification. 2019-07-26 12:57:42 -04:00
Todd Strader 4a14788c9b When showing an error, show the instance location 2019-07-26 12:52:38 -04:00
Wilson Snyder 5560da2934 Commentary 2019-07-26 06:24:26 -04:00
Todd Strader 43ce048f9e Add rr support 2019-07-25 21:34:09 -04:00
Todd Strader b045111a67 Refactor SimulateVisitor to use AstConst 2019-07-23 13:58:17 -04:00
Todd Strader 654571bd1c V3Number cleanup 2019-07-22 15:07:08 -04:00
Todd Strader a3fd105acb Add more Travis coverage 2019-07-18 05:31:02 -04:00
Wilson Snyder 97561bf064 When showing an error, show source code. 2019-07-14 21:42:03 -04:00
Wilson Snyder a4820fc700 Misc minor error cleanups from show-source branch. 2019-07-14 20:59:56 -04:00
Wilson Snyder ee469eedaf Fix some errors reporting wrong objects. 2019-07-14 15:06:49 -04:00
Wilson Snyder a49d819437 Tests: Default test_regress to quiet 2019-07-14 15:04:19 -04:00
Wilson Snyder cbb9288cb1 Fix some errors reporting wrong objects. 2019-07-14 12:21:39 -04:00
Wilson Snyder 173efbc829 Offer suggestions on bad identifier errors. 2019-07-14 10:08:53 -04:00
Wilson Snyder 0fef3b02ec Fix some parse tokens having wrong fileline. 2019-07-13 12:01:26 -04:00
Wilson Snyder d1ee6689c4 Internals: Make V3LanguageWords a singleton. 2019-07-13 08:57:42 -04:00
Wilson Snyder b3eab9d78a Tests: Move t_math_synmul to extended tests. 2019-07-12 19:35:28 -04:00
Wilson Snyder defd05bdd7 Tests: Fix driver checking threads/gcc for extended tests. 2019-07-12 19:35:10 -04:00
Wilson Snyder a7e5cccf33 In errors, single quote signals and other from-user data. 2019-07-11 22:36:32 -04:00
Wilson Snyder 0f857ce423 In errors, single quote signals and other from-user data. 2019-07-11 22:09:30 -04:00
Wilson Snyder 83cf1882de Quote reserved word messages, and related internal cleanups. 2019-07-11 20:49:35 -04:00
Wilson Snyder e17543d6d8 Tests: Fill in some missing error coverage holes. 2019-07-11 20:18:36 -04:00
Wilson Snyder dacf45fea9 Fix error message continuations to avoid linenumber repetition 2019-07-11 19:15:40 -04:00
Wilson Snyder 13a20c5ee9 Internals: Fix inserted tabs. 2019-07-11 07:05:33 -04:00
Wilson Snyder 58dfe9d071 Internals: Detab and fix spacing style issues. No functional change.
When diff, recommend using "git diff --ignore-all-space"
When merging, recommend using "git merge -Xignore-all-space"
2019-07-11 06:57:49 -04:00
Wilson Snyder 6c5cc885a6 Internals: Cleanup FileLine constructors. No functional change intended. 2019-07-10 22:04:58 -04:00
Sebastien Van Cauwenberghe 2c44ab61ac Fix GDB returning wrong error code on Fedora 30
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-10 06:47:20 -04:00
Sebastien Van Cauwenberghe 0847ea57ae Docs: Add name to contributors
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-10 06:46:28 -04:00
Wilson Snyder 9af84c71f5 Internals: More UASSERT_OBJ conversions. No functional change intended. 2019-07-07 09:01:36 -04:00
Wilson Snyder 4ce77d3e68 Header mentions public use. 2019-07-07 09:00:57 -04:00
Philipp Wagner 3c67791624 docs: Document parse-order-dependency for config files, bug1474
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 18:09:23 -04:00
Philipp Wagner a281c00752 docs: Contributor signoff
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 18:08:22 -04:00
Wilson Snyder 2ca418288d Fix enum values not being sized based on parent, bug1442. 2019-07-06 16:26:44 -04:00
Wilson Snyder bea34d0ced Internals: Misc enum cleanups. No functional change. 2019-07-06 15:50:25 -04:00
Wilson Snyder a5f2ce0dad Tests: Disable t_leak on Travis-CI. 2019-07-06 13:47:17 -04:00
Gianfranco Costamagna 07ae7146e3 Honour system flags, this makes debug flags being injected correctly.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 13:02:36 -04:00
Wilson Snyder 8548ecfdac Internals: Add UASSERT_OBJ macro to replace hand-done ifs. No functional change intended.
This makes it easier to filter out correctly zero code-coverage lines.
2019-07-06 12:57:50 -04:00
Wilson Snyder 7e54ff1b37 Additional lcov code coverage. 2019-07-05 22:30:19 -04:00
Wilson Snyder a4b9745e6e Fix lifetime opt dropping 2019-07-05 22:30:19 -04:00
Wilson Snyder 8612ba2b8d Internals: Commentary and fix gate debug glibc warning 2019-07-05 22:30:19 -04:00
Todd Strader fea6e047e2 Add Travis badge 2019-07-05 05:26:08 -04:00
Wilson Snyder 01ef7122e9 Internals: Add lcov code coverage markers. 2019-06-30 22:37:03 -04:00
Wilson Snyder ac43e7322b Internals: Remove dead code. 2019-06-30 22:11:56 -04:00
Wilson Snyder 02a3f2089f Include filenames in savable error. 2019-06-30 22:01:53 -04:00
Wilson Snyder 33ad834106 Tests: Close some test coverage holes. 2019-06-30 22:00:18 -04:00
Wilson Snyder efe739c52f Tests: Cover --error-limit and update docs (found via coverage). 2019-06-30 21:36:57 -04:00
Wilson Snyder 5cd531a5e0 Tests: Fix glib warning 2019-06-30 21:35:55 -04:00
Wilson Snyder 0e8dfdbb71 Tests: Less verbosity. 2019-06-30 17:45:42 -04:00
Wilson Snyder ec32a9b976 Tests: Close some test coverage holes. 2019-06-30 17:38:41 -04:00
Wilson Snyder 1aae6df669 Internals: Run graph self tests on --debug-self-test 2019-06-30 17:36:58 -04:00
Wilson Snyder f7641d2ecc Change MULTITOP to warning to help linting, see manual. 2019-06-30 16:46:48 -04:00
Wilson Snyder 5f27c41ee3 Test driver: Cleanup rerun flags & status. 2019-06-29 21:36:33 -04:00
Wilson Snyder 51df77099a Test driver: With --quiet print summary every 15sec or Travis barfs. 2019-06-29 15:58:00 -04:00
Wilson Snyder b8b7e5c978 Test driver: Add --rerun and --quiet options 2019-06-29 15:24:56 -04:00
Wilson Snyder dddcfeb482 Test driver: Move one_test, no functional change. 2019-06-29 13:37:01 -04:00
Wilson Snyder 8d63f83177 Tests: Reduce verbosity unless use --verbose 2019-06-29 13:11:30 -04:00
Wilson Snyder d43e9aa2c5 Fix not in array context on non-power-of-two slices, msg2946. 2019-06-29 11:30:38 -04:00
Wilson Snyder a2c1b7f827 Tests: Fix t_flag_csplit skip on old make. 2019-06-29 08:25:50 -04:00
Wilson Snyder 2fff92fe78 Tests: Add array MDA test. 2019-06-29 08:12:00 -04:00
Wilson Snyder ba9af4aabf For internal messages, use <command-line> and <built-in> to match GCC. 2019-06-29 07:39:34 -04:00
Wilson Snyder ef02678305 tests: Fix default make test, broke earlier commit. 2019-06-29 07:22:19 -04:00
Wilson Snyder 6990db70fa Avoid unique internal fileline in selftest. 2019-06-28 22:34:01 -04:00
Wilson Snyder af5616c077 Fix line number in dup vars. 2019-06-28 22:34:01 -04:00
Todd Strader 009f053d6e Add initial Travis CI config 2019-06-27 11:26:25 -04:00
Wilson Snyder 6903c52ef7 Tests: Add coverage of missing include error. 2019-06-26 19:12:57 -04:00
Wilson Snyder aa65dfb1bc Tests: Use only 2 threads for t_gantt. 2019-06-26 19:06:52 -04:00
Wilson Snyder 84a2cd0ec7 Remove some old DIRPROJECT dead code. 2019-06-26 18:50:38 -04:00
Wilson Snyder 8e7559c6c9 Show included-from filenames in warnings, bug1439. 2019-06-22 17:01:39 -04:00
Wilson Snyder 951521320a Warnings: Suppress dup lines, and use lowercase note: to match gcc 2019-06-22 16:12:17 -04:00
Wilson Snyder abc0eb92a0 Debug: Show only first line of suppressed warnings 2019-06-22 14:25:31 -04:00
Wilson Snyder c514bb26e8 Always internal module wrapper TOP. 2019-06-22 13:43:49 -04:00
Wilson Snyder 5cb6474cc6 Fix not reporting some duplicate signals/ports, bug1462. 2019-06-22 12:43:48 -04:00
Wilson Snyder b8eb6368e9 Tests: Fix up duplicate var declarations. 2019-06-22 12:32:13 -04:00
Wilson Snyder 6239deb1df Tests: Disable t_flag_csplit with old make. 2019-06-19 08:44:40 -04:00
Wilson Snyder 89c2a6070a devel release 2019-06-16 10:01:09 -04:00
Wilson Snyder c53ea10c75 Version bump 2019-06-16 09:30:56 -04:00
Wilson Snyder 8fa54b1c4d Add .gitattributes 2019-06-15 21:49:49 -04:00
Wilson Snyder e5fae03364 docs: Move license files back to top out of docs to appease github. 2019-06-15 21:41:38 -04:00
Wilson Snyder 081fd3b789 Rename to LICENSE to appease github 2019-06-15 17:57:40 -04:00
Wilson Snyder 0edd3c9e95 Tests: Support remote test obj_ 2019-06-15 16:37:58 -04:00
Wilson Snyder 659c7b78d5 Warn only once if too few CPUs. 2019-06-15 09:17:51 -04:00
Wilson Snyder 34ef3c9c38 Internals: Assert V3Number ops have different pointers. 2019-06-15 08:10:17 -04:00
Wilson Snyder 90af180ec1 Fix constant function return of function var, bug1467. 2019-06-15 08:03:39 -04:00
Wilson Snyder 0d71c1154d Fix overshift error to have user's source line. 2019-06-15 07:44:03 -04:00
Alex Chadwick 5da5e32e86 Fix --savable invalid C++ on packed arrays, bug1465.
Signed-off-by: Wilson Snyder <[email protected]>
2019-06-14 18:42:27 -04:00
Wilson Snyder ea59a39661 Commentary 2019-06-13 20:53:39 -04:00
Wilson Snyder f5ca1a04cc Docs: Rewrite README 2019-06-13 20:51:06 -04:00
Wilson Snyder b6c905dffc Move files into docs directory. 2019-06-13 07:19:44 -04:00
Wilson Snyder e4a3f9b38c Tests: Remove tabs from golden-output tests. No functional change. 2019-06-12 22:41:51 -04:00
Wilson Snyder b3e2d26e35 Tests: Remove tabs from golden-output tests. No functional change. 2019-06-12 22:22:36 -04:00
Wilson Snyder 79e9fbaeb7 Tests: Add additional .out files instead of expects. 2019-06-12 21:49:40 -04:00
Wilson Snyder f5f5cd522b Tests: Make standard lint() to replace inconsistent compile() 2019-06-12 21:05:02 -04:00
Wilson Snyder e713c8ce57 Fix not reporting some duplicate signals, bug1462. 2019-06-12 19:17:10 -04:00
Wilson Snyder 94ed817897 Fix debug over-verbosity. 2019-06-12 19:11:26 -04:00
Wilson Snyder ff360738b5 XML: Remove extranious space on dtypes. 2019-06-12 07:19:14 -04:00
Wilson Snyder f7f73a0825 Internals: Standardize internal FileLine filenames. 2019-06-12 07:00:56 -04:00
Wilson Snyder 2cedd14d43 Fix build error on MinGW, bug1460. 2019-06-11 21:38:17 -04:00
Wilson Snyder 77c2d49d1a Internals: Move code out of verilog.y. No functional change. 2019-06-11 21:19:44 -04:00
Wilson Snyder dd8d5ef687 Internals: Move code out of verilog.l. No functional change. 2019-06-11 21:03:03 -04:00
Wilson Snyder cfb05cd70a Whitespace cleanup. No functional change. 2019-06-11 20:20:04 -04:00
Todd Strader 6f2f668449 Fix dotted references to type parameter sizes, bug1458.
Signed-off-by: Wilson Snyder <[email protected]>
2019-06-11 19:00:24 -04:00
Wilson Snyder 6ffbb7cabf Internals: Remove extra semicolons. No functional change. 2019-06-11 18:31:06 -04:00
Wilson Snyder 6c1782e4de Add contributors and test. 2019-06-10 20:29:58 -04:00
Todd Strader 10e3cc6e3b Tests: Better checking for type param hash fix.
Signed-off-by: Wilson Snyder <[email protected]>
2019-06-06 19:57:31 -04:00
Todd Strader 34424e70d8 Fix sameHash error on type parameters, bug1456.
Signed-off-by: Wilson Snyder <[email protected]>
2019-06-05 20:39:42 -04:00
Wilson Snyder 38ad8727af Tests: Remove CRs. 2019-06-04 20:37:16 -04:00
Wilson Snyder 4e115d4b69 Fix performance when mulithreaded on 1 CPU, bug1455. 2019-06-03 19:13:03 -04:00
Wilson Snyder f6f8073058 Support logical equivalence operator <->. 2019-06-01 19:40:06 -04:00
Wilson Snyder 902ba752a3 Move many unsupported errors from lex to parser so can --bbox-unsup ignore them. 2019-05-31 21:05:50 -04:00
Wilson Snyder 97d9de3dad Support deferred assertions, bug1449. 2019-05-31 07:33:57 -04:00
Todd Strader 97fef7c60b Tests: Fix t_dist_whitespace.pl --golden, bug1451
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-30 20:36:13 -04:00
Wilson Snyder a58e7d94ec Error continuation lines no longer have %Error prefix. 2019-05-30 20:30:59 -04:00
Wilson Snyder c0be8bcefb Internals: Remove some pointless V3Number temporaries. No functional change intended. 2019-05-29 23:18:47 -04:00
Wilson Snyder 8846b365f4 Fix some memory leaks in V3Const/V3Unroll. 2019-05-29 22:43:26 -04:00
Kanad Kanhere 72eb361131 Fix invalid XML output due to special chars, bug1444.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-29 18:41:03 -04:00
Wilson Snyder b83b606267 Internals: Detab and fix spacing style issues. No functional change.
When diff, recommend using "git diff --ignore-all-space"
When merging, recommend using "git merge -Xignore-all-space"
2019-05-19 16:13:13 -04:00
Wilson Snyder 59d7d9e8c3 Fix real parameter assignment, bug1427. 2019-05-17 20:50:57 -04:00
Kanad Kanhere 3411279294 Internals: Relocate quoteNameControls, part of bug1444.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-16 21:44:01 -04:00
Wilson Snyder 8d6bea8975 nodist: Fix git_untabify --narrow. 2019-05-16 21:32:50 -04:00
Wilson Snyder 01725f662f Fix $display with string without %s, bug1441. 2019-05-16 21:21:38 -04:00
Wilson Snyder d841e68f4f Fix parameter function string returns, bug1441. 2019-05-16 21:16:20 -04:00
Wilson Snyder 13ecb8e177 Fix fault on with %t, bug1443. 2019-05-16 19:35:10 -04:00
Todd Strader 26436cf4dd Tests: Fix files ignored by t_dist_manifest, bug1438.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-15 19:02:17 -04:00
Wilson Snyder 96c70ea2df Internals: Fix some long lines in include files. No functional change.
When merging, recommend using "git merge -Xignore-all-space"
2019-05-14 22:49:32 -04:00
Wilson Snyder c8849094fc Fix spacing of last commit. 2019-05-14 22:05:37 -04:00
Wilson Snyder afea6d84e3 Mark infrequently called functions with GCC cold attribute. 2019-05-14 22:03:50 -04:00
Wilson Snyder 1fb0af7fba Internals: Fix some -Wsuggest-attribute=const suggestions. 2019-05-14 21:46:19 -04:00
Wilson Snyder 8ad1a68420 Fix OSX compile issue with -Winvalid-noreturn, bug1440. 2019-05-14 20:51:28 -04:00
Wilson Snyder 6ef7d5f0fd Internals: Fix spacing of last commit 2019-05-14 06:56:20 -04:00
Todd Strader d0fbdfac07 Add --quiet-exit, bug1436.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-14 06:47:51 -04:00
Sergey Kvachonok 16997d153f Fix sign-compare warning in verilated.cpp, bug1437.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-14 06:07:16 -04:00
Wilson Snyder 7777d10e9b Tests: Check for and remove trailing newlines 2019-05-13 19:47:52 -04:00
Wilson Snyder 00214f738f Tests: New scope-relevant tests, towards bug1305. 2019-05-13 19:31:24 -04:00
Wilson Snyder 07e6bc17db Fix error message showing pointer 2019-05-13 19:31:00 -04:00
Wilson Snyder afdfa4df87 Support VerilatedFstC set_time_unit, bug1433. 2019-05-12 08:19:49 -04:00
Wilson Snyder 1f714c6813 Commentary: Spelling fixes. 2019-05-11 18:42:27 -04:00
Wilson Snyder 5df3032b11 gtkwave: Merge from upstream. 2019-05-09 20:38:48 -04:00
Todd Strader eac3458647 Internals: V3Number tracks node information, part of bug1305.
Signed-off-by: Wilson Snyder <[email protected]>
2019-05-09 20:03:19 -04:00
Wilson Snyder 00efa05b50 devel release 2019-05-08 21:39:23 -04:00
815 changed files with 43607 additions and 41401 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-modernize*,-hicpp*,-and
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none
CheckOptions:
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '2'
- key: google-readability-namespace-comments.SpacesBeforeComments
+4
View File
@@ -0,0 +1,4 @@
*.v linguist-language=SystemVerilog
*.vh linguist-language=SystemVerilog
*.sv linguist-language=SystemVerilog
nodist linguist-detectable=false
+111
View File
@@ -0,0 +1,111 @@
# DESCRIPTION: Travis-CI config
#
# Copyright 2003-2019 by Todd Strader. Verilator 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.
language: cpp
matrix:
include:
# Non-cron build will just run on whatever linux flavor we get
- if: type != cron
os: linux
compiler: gcc
env:
- SCENARIOS=--dist
- if: type != cron
os: linux
compiler: gcc
env:
- SCENARIOS=--vlt
- if: type != cron
os: linux
compiler: gcc
env:
- SCENARIOS=--vltmt
# Cron builds try different OS/compiler combinations
- if: type = cron
dist: xenial
compiler: gcc
env:
- SCENARIOS=--dist
- if: type = cron
dist: xenial
compiler: gcc
env:
- SCENARIOS=--vlt
- if: type = cron
dist: xenial
compiler: gcc
env:
- SCENARIOS=--vltmt
- if: type = cron
dist: xenial
compiler: clang
env:
- SCENARIOS=--dist
- if: type = cron
dist: xenial
compiler: clang
env:
- SCENARIOS=--vlt
- if: type = cron
dist: xenial
compiler: clang
env:
- SCENARIOS=--vltmt
# - if: type = cron
# os: osx
# compiler: gcc
# env:
# - SCENARIOS=--dist
# - if: type = cron
# os: osx
# compiler: gcc
# env:
# - SCENARIOS=--vlt
# - if: type = cron
# os: osx
# compiler: gcc
# env:
# - SCENARIOS=--vltmt
- if: type = cron
dist: trusty
compiler: gcc
env:
- SCENARIOS=--dist
- if: type = cron
dist: trusty
compiler: gcc
env:
- SCENARIOS=--vlt
- if: type = cron
dist: trusty
compiler: gcc
env:
- SCENARIOS=--vltmt
cache: ccache
# Some tests require gdb
addons:
apt:
packages:
- gdb
before_install:
# Perl modules needed for testing
- yes yes | sudo cpan -fi Unix::Processors Parallel::Forker Bit::Vector
before_script:
- export VLT_JOBS=$((`nproc` + 1))
- export OBJCACHE=ccache
- export VERILATOR_ROOT=`pwd`
- autoconf
script:
- >
./configure --enable-maintainer-mode --enable-longtests &&
make -j $VLT_JOBS &&
make test_regress DRIVER_FLAGS="-j 0 --quiet --rerun"
after_script:
- ccache -s
-674
View File
@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+66
View File
@@ -2,6 +2,72 @@ Revision history for Verilator
The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.018 2019-08-29
** When showing an error, show source code and offer suggestions of replacements.
** When showing an error, show the instance location, bug1305. [Todd Strader]
*** Add --rr, bug1481. [Todd Strader]
*** Change MULTITOP to warning to help linting, see manual.
*** Add XSim support to driver.pl, bug1493. [Todd Strader]
**** Show included-from filenames in warnings, bug1439. [Todd Strader]
**** Fix elaboration time errors, bug1429. [Udi Finkelstein]
**** Fix not reporting some duplicate signals/ports, bug1462. [Peter Gerst]
**** Fix not in array context on non-power-of-two slices, msg2946. [Yu Sheng Lin]
**** Fix system compile flags injection. [Gianfranco Costamagna]
**** Fix enum values not being sized based on parent, bug1442. [Dan Petrisko]
**** Fix internal error on gate optimization of assign, bug1475. [Oyvind Harboe]
**** Add --dpi-hdr-only, bug1491. [Todd Strader]
* Verilator 4.016 2019-06-16
*** Add --quiet-exit, bug1436. [Todd Strader]
**** Error continuation lines no longer have %Error prefix.
**** Support logical equivalence operator <->.
**** Support VerilatedFstC set_time_unit, bug1433. [Pieter Kapsenberg]
**** Support deferred assertions, bug1449. [Charles Eddleston]
**** Mark infrequently called functions with GCC cold attribute.
**** Fix sign-compare warning in verilated.cpp, bug1437. [Sergey Kvachonok]
**** Fix fault on $realtime with %t, bug1443. [Julien Margetts]
**** Fix $display with string without %s, bug1441. [Denis Rystsov]
**** Fix parameter function string returns, bug1441. [Denis Rystsov]
**** Fix invalid XML output due to special chars, bug1444. [Kanad Kanhere]
**** Fix performance when mulithreaded on 1 CPU, bug1455. [Stefan Wallentowitz]
**** Fix type and real parameter issues, bug1427, bug1456, bug1458. [Todd Strader]
**** Fix build error on MinGW, bug1460. [Richard Myers]
**** Fix not reporting some duplicate signals, bug1462. [Peter Gerst]
**** Fix --savable invalid C++ on packed arrays, bug1465. [Alex Chadwick]
**** Fix constant function return of function var, bug1467. [Roman Popov]
* Verilator 4.014 2019-05-08
*** Add --trace-fst-thread.
+1 -1
View File
@@ -1,7 +1,7 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
+3 -1
View File
@@ -14,6 +14,7 @@
.*\.key
.*\.vcd
.*\.1
\.travis\.yml
/obj_dir/
/obj_dbg/
/obj_nc/
@@ -28,6 +29,8 @@
^Makefile$
bin/verilator_bin.*
bin/verilator_coverage_bin.*
docs/Makefile$
docs/doxygen-doc/.*
src/Makefile$
src/Makefile_obj$
include/verilated.mk$
@@ -47,7 +50,6 @@ nodist/
/simv.daidir/
/vc_hdrs.h$
/csrc/
doxygen-doc/.*
obj_dir/.*
TAGS
.*~
+25 -33
View File
@@ -83,7 +83,7 @@ infodir = @infodir@
pkgdatadir = @pkgdatadir@
# Directory in which to install pkgconfig file
# Generall ${prefix}/share/pkgconfig
# Generally ${prefix}/share/pkgconfig
pkgconfigdir = @pkgconfigdir@
# Directory in which to install data across multiple architectures
@@ -114,9 +114,11 @@ INFOS = README README.html README.pdf internals.txt internals.html \
# Files that can be generated, but should be up to date for a distribution.
DISTDEP = info Makefile
DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
DISTFILES_INC = $(INFOS) .gitignore \
*.in *.ac \
Changes TODO \
Artistic \
Changes \
LICENSE \
MANIFEST.SKIP \
bin/verilator \
bin/verilator_coverage \
@@ -124,7 +126,13 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
doxygen-mainpage doxygen.config verilator_logo.png \
docs/.gitignore \
docs/CONTRIBUTORS \
docs/Makefile.in \
docs/TODO \
docs/doxygen-mainpage \
docs/doxygen.config \
docs/verilator_logo.png \
install-sh configure *.pod \
include/*.[chv]* \
include/*.in \
@@ -172,6 +180,12 @@ INST_PROJ_BIN_FILES = \
DISTFILES := $(DISTFILES_INC)
EXAMPLES_FIRST = \
examples/hello_world_c \
examples/hello_world_sc \
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
ifeq ($(OBJCACHE_JOBS),)
ifneq ($(OBJCACHE_HOSTS),)
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
@@ -218,13 +232,15 @@ test_regress: all_nomsg
$(MAKE) -C test_regress
examples: all_nomsg
for p in examples/* ; do \
for p in $(EXAMPLES) ; do \
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
done
.PHONY: docs
docs: info
info: $(INFOS)
# Use --no-split to avoid creating filenames > 14 chars.
%.1: ${srcdir}/bin/%
pod2man $< $@
@@ -367,34 +383,9 @@ install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
DIRPROJECT := $(shell project_dir --project)
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
INST_PROJ_CVS = cp_if_cvs_diff
install-project: dist
@echo "Install-project to $(DIRPROJECT)"
strip bin/verilator_bin*
strip bin/verilator_coverage_bin*
$(MAKE) install-project-quick
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_DATA) $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(DIRPROJECT)/hw/utils/verilator/verilator.tgz
rm $(DISTNAME).tgz
install-project-quick:
ifeq ($(CFG_WITH_DEFENV),yes)
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
false
endif
@echo "Install-project-quick (no strip) to $(DIRPROJECT)"
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p; \
done
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 bin/verilator_bin*
@@ -511,8 +502,9 @@ clean mostlyclean distclean maintainer-clean::
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
rm -f *.tex
rm -rf test_*/obj_dir
rm -rf examples/*/obj_dir examples/*/logs
rm -rf test_*/obj_dir
rm -rf nodist/obj_dir
distclean maintainer-clean::
rm -f *.info* *.1 $(INFOS)
@@ -530,7 +522,7 @@ TAGS: $(TAGFILES)
.PHONY: doxygen
doxygen:
$(DOXYGEN) doxygen.config
$(MAKE) -C docs doxygen
######################################################################
# Test targets
+145 -72
View File
@@ -3,32 +3,19 @@
=pod
=for html <a href="https://travis-ci.com/verilator/verilator"><img src="https://travis-ci.com/verilator/verilator.svg?branch=master"></a>
=head1 NAME
This is the Verilator package README file.
Welcome to Verilator. This is the Verilator package's README file.
=head1 DISTRIBUTION
http://www.veripool.org/verilator
This package is Copyright 2003-2019 by Wilson Snyder. (Report bugs to
L<http://www.veripool.org/>.)
Verilator 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. (See the documentation for more
details.)
This program 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.
This document describes how to initially install Verilator. For more
general information please see L<http://verilator.org>.
=head1 DESCRIPTION
Verilator converts synthesizable (generally not behavioral) Verilog code
into C++ or SystemC code. It is not a complete simulator, just a
translator.
Verilator is a simulator which "Verilates" synthesizable (generally not
behavioral) Verilog code into "Verilated" C++ or SystemC code.
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
reads the specified Verilog code, lints it, and optionally adds coverage
@@ -47,25 +34,75 @@ Verilator is developed and has primary testing on Ubuntu. Versions have
also built on Redhat Linux, Macs OS-X, HPUX and Solaris. It should run
with minor porting on any Linix-ish platform. Verilator also works on
Windows under Cygwin, and Windows under MinGW (gcc -mno-cygwin). Verilated
output (not Verilator itself) compiles under MSVC++ 2008 and newer.
output (not Verilator itself) compiles under all the options above, plus
MSVC++ 2008 and newer.
=head1 INSTALLATION
For more details see
The following are detailed installation instructions.
Alternatively, for a quick summary please see
L<http://www.veripool.org/projects/verilator/wiki/Installing>.
If you will be modifying Verilator, you should use the "git" method as it
will let you track changes.
=over 4
=item
The latest version is available at L<http://www.veripool.org/verilator>.
Obtain binary or sources:
Download the latest package from that site, and decompress.
There are three methods to obtain Verilator, a prebuilt binary as part of
your Linux distribution, via git, or using a tarball. If you will be
modifying Verilator, you should use the "git" method as it will let you
track changes and hopefully contribute in the future.
tar xvzf verilator_version.tgz
=over 4
=item
Prebuilt binary:
You may install a binary on Ubuntu or other distributions using a package
manager. This is unlikely to be the most recent version.
apt-get install verilator
You may now skip the remaining installation steps.
=item
Git:
Get the sources from the repository.
git clone http://git.veripool.org/git/verilator # Only first time
## Note the URL above is not a page you can see with a browser, it's for git only
=item
Tarball:
Get a recent tarball package from L<http://www.veripool.org/verilator>.
Click the "Download" tab, scroll down to the latest package
(i.e. verilator-#.###.tgz), download it, and decompress with:
tar xvzf verilator_#-###.tgz
=back
=item
Install prerequisites:
=over 4
=item
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
C<g++> (or C<clang>) packages. To compile Verilator in addition to the above you need the C<flex>,
C<bison> and C<texi2html> packages installed.
sudo apt-get install git make autoconf g++ flex bisonz # First time prerequisites
sudo apt-get install libgz # Non-Ubuntu (ignore if gives error)
sudo apt-get install libfl2 libfl-dev zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
=item
@@ -78,22 +115,32 @@ instead.)
=item
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
C<g++> (or C<clang>) packages.
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (on
To use Verilator FST tracing you will need the C<gtkwave> and C<libgz> (and on
Ubuntu C<zlibc> C<zlib1g> C<zlib1g-dev>) packages installed.
To compile Verilator in addition to the above you need the C<flex>,
C<bison> and C<texi2html> packages installed.
=back
=item
C<cd> to the Verilator directory containing this README.
Prepare for building:
cd verilator # Needed if not already in the package
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash; ignore error if on bash
# If using git:
git pull # Make sure we're up-to-date
git tag # See what versions exist
#git checkout master # Use development branch (e.g. recent bug fix)
#git checkout stable # Use most recent release
#git checkout v{version} # Switch to specified release version
#
autoconf # Create ./configure script
=item
You now have to decide how you're going to eventually install the kit.
Installation Choices
You have to decide how you're going to eventually install the kit.
Note Verilator builds the current value of VERILATOR_ROOT, SYSTEMC_INCLUDE,
and SYSTEMC_LIBDIR as defaults into the executable, so try to have them
@@ -103,25 +150,32 @@ correct before configuring.
=item 1.
Our personal favorite is to always run Verilator from the kit directory.
This allows the easiest experimentation and upgrading. It's also how most
EDA tools operate; to run you point to the tarball, no install is needed.
Our personal favorite is to always run Verilator from its git directory.
This allows the easiest experimentation and upgrading, and allows many
versions of Verilator to co-exist on a system. To run you point to the
program's files, no install is needed.
export VERILATOR_ROOT=`pwd` # if your shell is bash
setenv VERILATOR_ROOT `pwd` # if your shell is csh
./configure
Note after installing (below steps), a calling program should set the
environment variable VERILATOR_ROOT to point to this git directory, then
execute $VERILATOR_ROOT/bin/verilator, which will find the path to all
needed files.
=item 2.
To install globally onto a "cad" disk with multiple versions of every tool,
and add it to path using Modules/modulecmd:
You may eventually be instaling onto a project/company-wide "CAD" tools
disk that may support multiple versions of every tool.
unset VERILATOR_ROOT # if your shell is bash
unsetenv VERILATOR_ROOT # if your shell is csh
# For the tarball, use the version number instead of git describe
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
After installing you'll want a module file like the following:
Note after installing (below steps), if you use C<modulecmd>, you'll want a
module file like the following:
set install_root /CAD_DISK/verilator/{version-number-used-above}
unsetenv VERILATOR_ROOT
@@ -131,50 +185,49 @@ and add it to path using Modules/modulecmd:
=item 3.
The next option is to install it globally, using the normal system paths:
The next option is to eventually install it globally, using the normal system paths:
unset VERILATOR_ROOT # if your shell is bash
unsetenv VERILATOR_ROOT # if your shell is csh
./configure
Then after installing (below) the binary directories should already be in
your PATH.
=item 4.
Alternatively you can configure a prefix that install will populate, as
most GNU tools support:
Finally, you may eventually install it into a specific installation prefix,
as most GNU tools support:
unset VERILATOR_ROOT # if your shell is bash
unsetenv VERILATOR_ROOT # if your shell is csh
./configure --prefix /opt/verilator-VERSION
Then after installing you will need to add /opt/verilator-VERSION/bin to
Then after installing (below steps) you will need to add /opt/verilator-VERSION/bin to
PATH.
=back
Note all of the options above did:
./configure ... some options ...
Add to this line C<--enable-longtests> for more complete developer tests.
Additional packages may be required for these tests.
=item
Type C<make> to compile Verilator.
=item
Type C<make test> to check the compilation.
Configure with C<--enable-longtests> for more complete developer tests.
Additional packages may be required for these tests.
You may get a error about a typedef conflict for uint32_t. Edit
verilated.h to change the typedef to work, probably to @samp{typedef
unsigned long uint32_t;}.
=item
If you used the VERILATOR_ROOT scheme you're done. Programs should set the
environment variable VERILATOR_ROOT to point to this distribution, then
execute $VERILATOR_ROOT/bin/verilator, which will find the path to all
needed files.
If you used the prefix scheme, now do a C<make install>.
If you used the prefix scheme, now do a C<make install>. To run verilator,
have the verilator binary directory in your PATH (this should already be
true if using the default configure), and make sure VERILATOR_ROOT is not
set.
=item
You may now wish to consult the examples directory. Type C<make> inside any
example directory to run the example.
@@ -187,25 +240,45 @@ Detailed documentation and the man page can be seen by running:
bin/verilator --help
or reading verilator.txt in the same directory as this README.
or reading verilator.pdf in the same directory as this README.
=head1 DIRECTORY STRUCTURE
or see L<https://www.veripool.org/ftp/verilator_doc.pdf> (which is the most
recent version and thus may differ in some respects from the version you installed).
The directories in the kit after de-taring are as follows:
=head1 PACKAGE DIRECTORY STRUCTURE
The directories in the package directory are as follows:
Changes => Version history
bin/verilator => Compiler Wrapper invoked to Verilate code
include/ => Files that should be in your -I compiler path
include/verilated*.cpp => Global routines to link into your simulator
include/verilated*.h => Global headers
include/verilated.v => Stub defines for linting
include/verilated.mk => Common makefile
src/ => Translator source code
docs/ => Additional documentation
examples/hello_world_c => Example simple Verilog->C++ conversion
examples/hello_world_sc => Example simple Verilog->SystemC conversion
examples/tracing_c => Example Verilog->C++ with tracing
examples/tracing_sc => Example Verilog->SystemC with tracing
include/ => Files that should be in your -I compiler path
include/verilated*.cpp => Global routines to link into your simulator
include/verilated*.h => Global headers
include/verilated.mk => Common Makefile
include/verilated.v => Stub defines for linting
src/ => Translator source code
test_regress => Internal tests
verilator.pdf => Primary documentation
verilator.txt => Primary documentation (text)
=head1 LIMITATIONS
For files created after Verilation, see the manual.
See verilator.txt (or execute C<bin/verilator --help>) for limitations.
=head1 DISTRIBUTION
This package is Copyright 2003-2019 by Wilson Snyder. (Report bugs to
L<http://www.veripool.org/>.)
Verilator 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. (See the documentation for more
details.)
This program 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.
+86 -40
View File
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
require 5.006_001;
use warnings;
BEGIN {
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
# Magic to allow author testing of perl packages in local directory
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
}
}
use Getopt::Long;
use FindBin qw($RealBin $RealScript);
use IO::File;
@@ -41,7 +34,9 @@ autoflush STDERR 1;
$Debug = 0;
my $opt_gdb;
my $opt_rr;
my $opt_gdbbt;
my $opt_quiet_exit;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
@@ -61,11 +56,13 @@ Getopt::Long::config("no_auto_abbrev","pass_through");
if (! GetOptions(
# Major operating modes
"help" => \&usage,
"debug:s" => \&debug,
"debug" => \&debug,
# "version!" => \&version, # Also passthru'ed
# Switches
"gdb!" => \$opt_gdb,
"gdbbt!" => \$opt_gdbbt,
"quiet-exit!" => \$opt_quiet_exit,
"rr!" => \$opt_rr,
# Additional parameters
"<>" => sub {}, # Ignored
)) {
@@ -98,6 +95,10 @@ if ($opt_gdb) {
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt'");
} elsif ($opt_rr) {
# Record with rr
run ("rr record ".verilator_bin()
." ".join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
# Run under GDB to get gdbbt
run ("gdb"
@@ -105,7 +106,7 @@ if ($opt_gdb) {
." --batch --quiet --return-child-result"
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt'");
." -ex 'bt' -ex 'quit'");
} else {
# Normal, non gdb
run(verilator_bin()." ".join(' ',@quoted_sw));
@@ -164,7 +165,8 @@ sub gdb_works {
." --batch-silent --quiet --return-child-result"
." -ex 'run -n'" # `echo -n`
." -ex 'set width 0'"
." -ex 'bt'");
." -ex 'bt'"
." -ex 'quit'");
my $status = $?;
return $status==0;
}
@@ -185,9 +187,9 @@ sub run {
warn "%Error: $command\n";
}
if ($status & 127) {
if (($status & 127) == 4 # SIGILL
|| ($status & 127) == 8 # SIGFPA
|| ($status & 127) == 11) { # SIGSEGV
if (($status & 127) == 4 # SIGILL
|| ($status & 127) == 8 # SIGFPA
|| ($status & 127) == 11) { # SIGSEGV
warn "%Error: Verilator internal fault, sorry. Consider trying --debug --gdbbt\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator aborted. Consider trying --debug --gdbbt\n" if !$Debug;
@@ -195,7 +197,14 @@ sub run {
warn "%Error: Verilator threw signal $status. Consider trying --debug --gdbbt\n" if !$Debug;
}
}
die "%Error: Command Failed $command\n";
if ($opt_quiet_exit) {
# Same return code as die
exit $! if $!; # errno
exit $? >> 8 if $? >> 8; # child exit status
exit 255; # last resort
} else {
die "%Error: Command Failed $command\n";
}
}
}
@@ -289,6 +298,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--debugi-<srcfile> <level> Enable debugging a source file at a level
--default-language <lang> Default language to parse
+define+<var>=<value> Set preprocessor define
--dpi-hdr-only Only produce the DPI header file
--dump-defines Show preprocessor defines with -E
--dump-tree Enable dumping .tree files
--dump-treei <level> Enable dumping .tree files at a level
@@ -346,9 +356,11 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--private Debugging; see docs
--public Debugging; see docs
-pvalue+<name>=<value> Overwrite toplevel parameter
--quiet-exit Don't print the command on failure
--relative-includes Resolve includes relative to current file
--no-relative-cfuncs Disallow 'this->' in generated functions
--report-unoptflat Extra diagnostics for UNOPTFLAT
--rr Run Verilator and record with rr
--savable Enable model save-restore
--sc Create SystemC output
--stats Create statistics file
@@ -730,6 +742,11 @@ Defines the given preprocessor symbol, or multiple symbols if separated by
plusses. Similar to -D; +define is fairly standard across Verilog tools
while -D is an alias for GCC compatibility.
=item --dpi-hdr-only
Only generate the DPI header file. This option has no effect on the name
or location of the file.
=item --dump-defines
With -E, suppress normal output, and instead print a list of all defines
@@ -765,8 +782,8 @@ will also go to standard out.
=item --error-limit I<value>
After this number of errors or warnings are encountered, exit. Defaults to
50.
After this number of errors are encountered, exit. Warnings are not
counted in this limit. Defaults to 50.
=item --exe
@@ -1024,15 +1041,15 @@ developers.
=item --output-split I<bytes>
Enables splitting the output .cpp files into multiple outputs. When a
C++ file exceeds the specified number of operations, a new file will be
created at the next function boundary. In addition, any slow routines will
be placed into __Slow files. This accelerates compilation by as
optimization can be disabled on the slow routines, and the remaining files
can be compiled on parallel machines. Using --output-split should have
only a trivial impact on performance. With GCC 3.3 on a 2GHz Opteron,
--output-split 20000 will result in splitting into approximately
one-minute-compile chunks.
Enables splitting the output .cpp files into multiple outputs. When a C++
file exceeds the specified number of operations, a new file will be created
at the next function boundary. In addition, any infrequently executed
"cold" routines will be placed into __Slow files. This accelerates
compilation by as optimization can be disabled on the routines in __Slow,
and the remaining files can be compiled on parallel machines. Using
--output-split should have only a trivial impact on performance. With GCC
3.3 on a 2GHz Opteron, --output-split 20000 will result in splitting into
approximately one-minute-compile chunks.
=item --output-split-cfuncs I<statements>
@@ -1157,6 +1174,10 @@ inlining. This will also turn off inlining as if all modules had a
Overwrites the given parameter(s) of the toplevel module. See -G for a
detailed description.
=item --quiet-exit
When exiting due to an error, do not display the "Command Failed" message.
=item --relative-includes
When a file references an include file, resolve the filename relative to
@@ -1184,6 +1205,10 @@ printing. For example:
will generate a PDF Vt_unoptflat_simple_2_35_unoptflat.dot.pdf from the DOT
file.
=item --rr
Run Verilator and record with rr. See: rr-project.org.
=item --savable
Enable including save and restore functions in the generated model.
@@ -1273,7 +1298,7 @@ good value.
When the input Verilog contains more than one top level module, specifies
the name of the top level Verilog module to become the top, and sets the
default for if --prefix is not used. This is not needed with standard
designs with only one top.
designs with only one top. See also the MULTITOP warning section.
=item --trace
@@ -1533,7 +1558,7 @@ ensure that X E<rarr> 1 triggers a C<posedge>.
B<Note.> Some users have reported that using this option can affect
convergence, and that it may be necessary to use --converge-limit to
increase the number of convergence iterations. This may be another
indication of problems with the modelled design that should be addressed.
indication of problems with the modeled design that should be addressed.
=item --xml-only
@@ -1604,7 +1629,7 @@ posedge eval() and one negedge eval().
=item +verilator+rand+reset+I<value>
When a model was Verilated using "-x-inital unique", sets the
When a model was Verilated using "-x-initial unique", sets the
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
Randomize. See L</"Unknown states">.
@@ -1857,7 +1882,7 @@ For -cc and -sc mode, it also creates:
{prefix}.cpp // Top level C++ file
{prefix}.h // Top level header
{prefix}__Slow{__n}.cpp // Constructors and infrequent routines
{prefix}__Slow{__n}.cpp // Constructors and infrequent cold routines
{prefix}{__n}.cpp // Additional top C++ files (--output-split)
{prefix}{each_verilog_module}.cpp // Lower level internal C++ files
{prefix}{each_verilog_module}.h // Lower level internal header files
@@ -2353,7 +2378,7 @@ provides one of those threads, and the generated model will create and
manage the other N-1 threads. It's the client's responsibility not to
oversubscribe the available CPU cores. Under CPU oversubscription, the
Verilated model should not livelock nor deadlock, however, you can expect
performance to be far worse than it would be with proper stoichiometry of
performance to be far worse than it would be with proper ratio of
threads and CPU cores.
With --trace-fst-thread, tracing occurs in a separate thread from the main
@@ -2404,7 +2429,7 @@ completing after the $stop or $finish.
If using --coverage, the coverage routines are fully thread safe.
If using --dpi, Verilator assumes pure DPI imports are thread safe,
balancing performance versus saftey. See --threads-dpi.
balancing performance versus safety. See --threads-dpi.
If using --savable, the save/restore classes are not multithreaded and are
must be called only by the eval thread.
@@ -2437,6 +2462,10 @@ Configuration files are parsed after the normal Verilog preprocessing, so
`ifdefs, `defines, and comments may be used as if it were normal Verilog
code.
Note that file or line-specific configuration only applies to files read
after the configuration file. It is therefore recommended to pass the
configuration file to Verilator as first file.
The grammar of configuration commands is as follows:
=over 4
@@ -2909,7 +2938,7 @@ clock pins to be sc_clocks and this is no longer needed.
Used after a port declaration. It sets the port to be of sc_bv<I<width>>
type, instead of bool, vluint32_t or vluint64_t. This may be useful if
the port width is parametrized and different of such modules interface
the port width is parameterized and different of such modules interface
a templated module (such as a transactor) or for other reasons. In general
you should avoid using this attribute when not necessary as with increasing
usage of sc_bv the performance increases significantly.
@@ -3300,7 +3329,7 @@ $value$plusargs.
Not supported as Verilator needs to determine all formatting at compile
time. Generally you can just ifdef them out for no ill effect. Note also
VL_TIME_MULTIPLER can be defined at compile time to move the decimal point
VL_TIME_MULTIPLIER can be defined at compile time to move the decimal point
when displaying all times, model wide.
=back
@@ -3711,14 +3740,31 @@ correctly.
=item MULTITOP
Error that there are multiple top level modules, that is modules not
instantiated by any other module. Verilator only supports a single top
level, if you need more, create a module that wraps all of the top modules.
Warns that there are multiple top level modules, that is modules not
instantiated by any other module, and both modules were put on the command
line (not in a library). Three likely cases:
Often this error is because some low level cell is being read in, but is
not really needed. The best solution is to insure that each module is in a
unique file by the same name. Otherwise, make sure all library files are
read in as libraries with -v, instead of automatically with -y.
1. A single module is intended to be the top. This warning then occurs
because some low level cell is being read in, but is not really needed as
part of the design. The best solution for this situation is to ensure that
only the top module is put on the command line without any flags, and all
remaining library files are read in as libraries with -v, or are
automatically resolved by having filenames that match the module names.
2. A single module is intended to be the top, the name of it is known, and
all other modules should be ignored if not part of the design. The best
solution is to use the --top-module option to specify the top module's
name. All other modules that are not part of the design will be for the
most part ignored (they must be clean in syntax and their contents will be
removed as part of the Verilog module elaboration process.)
3. Multiple modules are intended to be design tops, e.g. when linting a
library file. As multiple modules are desired, disable the MULTITOP
warning. All input/outputs will go uniquely to each module, with any
conflicting and identical signal names being uniquified by adding a prefix
based on the top module name followed by __02E (a Verilator-encoded ASCII
".'). This renaming is done even if the two modules' signals seem
identical, e.g. multiple modules with a "clk" input.
=item PINCONNECTEMPTY
-7
View File
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
require 5.006_001;
use warnings;
BEGIN {
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
# Magic to allow author testing of perl packages in local directory
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
}
}
use Getopt::Long;
use FindBin qw($RealBin $RealScript);
use IO::File;
+1 -1
View File
@@ -212,7 +212,7 @@ verilator_profcfunc - Read gprof report created with --prof-cfuncs
=head1 DESCRIPTION
Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used verilator's
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc,
in each Verilog block.
+9 -7
View File
@@ -6,14 +6,14 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.014 2019-05-08],
AC_INIT([Verilator],[4.018 2019-08-29],
[https://www.veripool.org/verilator],
[verilator],[https://www.veripool.org/verilator])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
AC_CONFIG_HEADER(src/config_build.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc)
AC_CONFIG_FILES(Makefile docs/Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc)
AC_MSG_RESULT([configuring for $PACKAGE_STRING])
@@ -91,10 +91,10 @@ AC_SUBST(CFG_WITH_PREC11)
AC_MSG_RESULT($CFG_WITH_PREC11)
# Compiler flags
CFLAGS=-I${includedir}
CPPFLAGS=-I${includedir}
CXXFLAGS=-I${includedir}
LDFLAGS=-L${libdir}
CFLAGS+=" -I${includedir}"
CPPFLAGS+=" -I${includedir}"
CXXFLAGS+=" -I${includedir}"
LDFLAGS+=" -L${libdir}"
# Checks for programs.
AC_PROG_CC
@@ -273,7 +273,9 @@ AC_SUBST(CFG_CXXFLAGS_PARSER)
# without this flag, even though there's a conditional to prevent the divide.
# We still don't add no-div-by-zero as it throws message to stdout, though doesn't die.
#_MY_CXX_CHECK_OPT(-Wno-div-by-zero)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)
# For some reason -faligned-new does not work under Travis w/ clang but the
# configure test doesn't catch this either
AS_IF([test "x$TRAVIS_COMPILER" != xclang], [_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)])
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-fbracket-depth=4096)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Qunused-arguments)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-bool-operation)
+1
View File
@@ -0,0 +1 @@
Makefile
+17
View File
@@ -0,0 +1,17 @@
The contributors listed below have certified their Verilator contributions
under the Developer Certificate of Origin
(https://developercertificate.org/).
Please see the Verilator manual for additional contributors.
Alex Chadwick
Gianfranco Costamagna
Jeremy Bennett
John Coiner
Kanad Kanhere
Maarten De Braekeleer
Philipp Wagner
Richard Myers
Sebastien Van Cauwenberghe
Stefan Wallentowitz
Wilson Snyder
+50
View File
@@ -0,0 +1,50 @@
#*****************************************************************************
# DESCRIPTION: Verilator documentation: Makefile pre-configure version
#
# This file is part of Verilator.
#
# Code available from: http://www.veripool.org/verilator
#
#*****************************************************************************
#
# Copyright 2003-2019 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.
#
#****************************************************************************/
#
# This file is intended only to be called from the top-level Verilator Makefile.
#### Start of system configuration section. ####
DOXYGEN = doxygen
#### End of system configuration section. ####
######################################################################
.SUFFIXES:
default:
@echo "error: make not supported here, run 'make docs' from Verilator top-level"
clean mostlyclean distclean maintainer-clean::
rm -f $(SCRIPTS) *.tmp
rm -f *.aux *.cp *.cps *.dvi *.fn *.fns *.ky *.kys *.log
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
rm -f *.tex
distclean maintainer-clean::
rm -f *.info* *.1 $(INFOS)
rm -f Makefile
.PHONY: doxygen
doxygen:
$(DOXYGEN) doxygen.config
View File
+1 -1
View File
@@ -45,7 +45,7 @@ PROJECT_BRIEF = "Verilog to C translator"
# exceed 55 pixels and the maximum width should not exceed 200 pixels.
# Doxygen will copy the logo to the output directory.
PROJECT_LOGO = veripool-logo.png
PROJECT_LOGO = verilator_logo.png
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

+1 -1
View File
@@ -24,7 +24,7 @@ endif
# e.g. examples/tracing_c.
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package inatall, and verilator is in your path. Otherwise find the
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
+1 -1
View File
@@ -24,7 +24,7 @@ endif
# e.g. examples/tracing_sc.
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package inatall, and verilator is in your path. Otherwise find the
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
+1 -1
View File
@@ -21,7 +21,7 @@ endif
# Set up variables
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package inatall, and verilator is in your path. Otherwise find the
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
+6 -6
View File
@@ -14,18 +14,18 @@ module top
input fastclk,
input reset_l,
output [1:0] out_small,
output [39:0] out_quad,
output [69:0] out_wide,
output wire [1:0] out_small,
output wire [39:0] out_quad,
output wire [69:0] out_wide,
input [1:0] in_small,
input [39:0] in_quad,
input [69:0] in_wide
);
// Connect up the outputs, using some trivial logic
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
// And an example sub module. The submodule will print stuff.
sub sub (/*AUTOINST*/
+1 -1
View File
@@ -21,7 +21,7 @@ endif
# Set up variables
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package inatall, and verilator is in your path. Otherwise find the
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
+6 -6
View File
@@ -14,18 +14,18 @@ module top
input fastclk,
input reset_l,
output [1:0] out_small,
output [39:0] out_quad,
output [69:0] out_wide,
output wire [1:0] out_small,
output wire [39:0] out_quad,
output wire [69:0] out_wide,
input [1:0] in_small,
input [39:0] in_quad,
input [69:0] in_wide
);
// Connect up the outputs, using some trivial logic
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
// And an example sub module. The submodule will print stuff.
sub sub (/*AUTOINST*/
+7 -1
View File
@@ -663,6 +663,7 @@ return(rc);
}
#ifndef FST_DYNAMIC_ALIAS2_DISABLE
static int fstWriterSVarint(FILE *handle, int64_t v)
{
unsigned char buf[15]; /* ceil(64/7) = 10 + sign byte padded way up */
@@ -688,6 +689,7 @@ len = pnt-buf;
fstFwrite(buf, len, 1, handle);
return(len);
}
#endif
/***********************/
@@ -4810,6 +4812,7 @@ uint32_t cur_blackout = 0;
int packtype;
unsigned char *mc_mem = NULL;
uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */
int dumpvars_state = 0;
if(!xc) return(0);
@@ -4819,7 +4822,6 @@ length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t));
if(fv)
{
fprintf(fv, "$dumpvars\n");
#ifndef FST_WRITEX_DISABLE
fflush(fv);
setvbuf(fv, (char *) NULL, _IONBF, 0); /* even buffered IO is slow so disable it and use our own routines that don't need seeking */
@@ -4966,8 +4968,10 @@ for(;;)
if(beg_tim)
{
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
}
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
@@ -5400,8 +5404,10 @@ for(;;)
}
}
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
+112 -53
View File
@@ -71,9 +71,11 @@ VerilatedImp VerilatedImp::s_s;
#ifndef VL_USER_FINISH ///< Define this to override this function
void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
if (0 && hier) {}
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) {
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum); // Not VL_PRINTF_MT, already on main thread
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
Verilated::flushCall();
exit(0);
}
@@ -93,7 +95,8 @@ void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
if (0 && hier) {}
Verilated::gotFinish(true);
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); // Not VL_PRINTF_MT, already on main thread
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"%%Error: %s:%d: %s\n", filename, linenum, msg);
Verilated::flushCall();
VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread
@@ -167,7 +170,8 @@ vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
#ifdef VL_THREADED
// Alternative is to use std::this_thread::get_id, but that returns a hard-to-read number and is very slow
// Alternative is to use std::this_thread::get_id, but that returns a
// hard-to-read number and is very slow
static std::atomic<vluint32_t> s_nextId(0);
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
return t_myId;
@@ -281,6 +285,7 @@ IData VL_RANDOM_I(int obits) VL_MT_SAFE {
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
return vl_rand64() & VL_MASK_Q(obits);
}
// VL_RANDOM_W currently unused as $random always 32 bits
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
for (int i=0; i<VL_WORDS_I(obits); ++i) {
if (i<(VL_WORDS_I(obits)-1)) {
@@ -338,7 +343,8 @@ void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp) VL_MT_SAFE {
//===========================================================================
// Slow math
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus) VL_MT_SAFE {
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
bool is_modulus) VL_MT_SAFE {
// See Knuth Algorithm D. Computes u/v = q.r
// This isn't massively tuned, as wide division is rare
// for debug see V3Number version
@@ -398,7 +404,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
// Main loop
for (int j = uw - vw; j >= 0; --j) {
// Estimate
vluint64_t unw64 = (static_cast<vluint64_t>(un[j+vw])<<VL_ULL(32) | static_cast<vluint64_t>(un[j+vw-1]));
vluint64_t unw64 = (static_cast<vluint64_t>(un[j+vw])<<VL_ULL(32)
| static_cast<vluint64_t>(un[j+vw-1]));
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw-1]);
vluint64_t rhat = unw64 - qhat*static_cast<vluint64_t>(vn[vw-1]);
@@ -447,7 +454,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
}
}
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
WDataOutP VL_POW_WWW(int obits, int, int rbits,
WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
// obits==lbits, rbits can be different
owp[0] = 1;
for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
@@ -469,7 +477,8 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDa
}
return owp;
}
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs) VL_MT_SAFE {
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits,
WDataOutP owp, WDataInP lwp, QData rhs) VL_MT_SAFE {
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
}
@@ -512,7 +521,8 @@ WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits,
WData rhsw[2]; VL_SET_WQ(rhsw, rhs);
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
}
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
QData VL_POWSS_QQW(int obits, int, int rbits,
QData lhs, WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
// Skip check for rhs == 0, as short-circuit doesn't save time
if (rsign && VL_SIGN_W(rbits, rwp)) {
if (lhs==0) return 0; // "X"
@@ -633,14 +643,28 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
case 'e':
case 'f':
case 'g': {
case 'g':
case '^': { // Realtime
const int lbits = va_arg(ap, int);
double d = va_arg(ap, double);
if (lbits) {} // UNUSED - always 64
strncpy(tmpf, pctp, pos-pctp+1);
tmpf[pos-pctp+1] = '\0';
sprintf(tmp, tmpf, d);
output += tmp;
switch (fmt) {
case '^': { // Realtime
int digits = sprintf(tmp, "%g", d/VL_TIME_MULTIPLIER);
int needmore = width-digits;
if (needmore>0) output.append(needmore, ' '); // Pre-pad spaces
output += tmp;
break;
}
default: {
strncpy(tmpf, pctp, pos-pctp+1);
tmpf[pos-pctp+1] = '\0';
sprintf(tmp, tmpf, d);
output += tmp;
break;
}
break;
} // switch
break;
}
default: {
@@ -805,7 +829,8 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
if (fp) fgetc(fp);
else floc -= 8;
}
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
// Get a character without advancing
if (fp) {
int data = fgetc(fp);
@@ -822,7 +847,8 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp, const std:
}
}
}
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
static inline void _vl_vsss_skipspace(FILE* fp, int& floc,
WDataInP fromp, const std::string& fstr) VL_MT_SAFE {
while (1) {
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
if (c==EOF || !isspace(c)) return;
@@ -845,16 +871,19 @@ static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp, const std:
*cp++ = '\0';
//VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
}
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb,
int nbits, IData ld) VL_MT_SAFE {
for (; nbits && lsb<obits; nbits--, lsb++, ld>>=1) {
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
}
}
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2,
const char* strp, size_t posstart, size_t posend) VL_MT_SAFE {
const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
int lsb = 0;
for (int i=0, pos=static_cast<int>(posend)-1; i<obits && pos>=static_cast<int>(posstart); --pos) {
for (int i=0, pos = static_cast<int>(posend)-1;
i<obits && pos>=static_cast<int>(posstart); --pos) {
switch (tolower (strp[pos])) {
case 'x': case 'z': case '?': // FALLTHRU
case '0': lsb += baseLog2; break;
@@ -1065,8 +1094,8 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
IData bytes = VL_BYTES_I(obits);
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
if (VL_UNLIKELY(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun");
if (VL_UNCOVERABLE(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
}
// We don't use fgets, as we must read \0s.
@@ -1289,7 +1318,7 @@ const char* memhFormat(int nBits) {
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
default: assert(false); break;
default: assert(false); break; // LCOV_EXCL_LINE
}
return buf;
}
@@ -1303,35 +1332,52 @@ void VL_WRITEMEM_N(
const std::string& filename, // Output file name
const void* memp, // Array state
IData start, // First array row address to write
IData end // Last address to write
IData end // Last address to write, or ~0 when not specified
) VL_MT_SAFE {
if (VL_UNLIKELY(!hex)) {
VL_FATAL_MT(filename.c_str(), 0, "",
"VL_WRITEMEM_N only supports hex format for now, sorry!");
return;
}
// Calculate row address limits
size_t row_min = array_lsb;
size_t row_max = row_min + depth - 1;
// Normalize the last address argument: ~0 => row_max
size_t nend = (end == ~0u) ? row_max : end;
// Bounds check the write address range
if (VL_UNLIKELY((start < row_min) || (start > row_max)
|| (nend < row_min) || (nend > row_max))) {
VL_FATAL_MT(filename.c_str(), 0, "",
"$writemem specified address out-of-bounds");
return;
}
if (VL_UNLIKELY(start > nend)) {
VL_FATAL_MT(filename.c_str(), 0, "",
"$writemem invalid address range");
return;
}
// Calculate row offset range
size_t row_start = start - row_min;
size_t row_end = nend - row_min;
// Bail out on possible 32-bit size_t overflow
if (VL_UNLIKELY(row_end + 1 == 0)) {
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem address is too large");
return;
}
FILE* fp = fopen(filename.c_str(), "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
return;
}
for (int row_addr = start; row_addr <= end; ++row_addr) {
if ((row_addr < array_lsb)
|| (row_addr > array_lsb + depth - 1)) {
vluint32_t endmax = ~0;
if (end != endmax) {
VL_FATAL_MT(filename.c_str(), 0, "",
"$writemem specified address out-of-bounds");
}
// else, it's not an error to overflow due to end == endmax,
// just return cleanly.
goto cleanup;
}
// Compute the offset into the memp array.
int row_offset = row_addr - array_lsb;
for (size_t row_offset = row_start; row_offset <= row_end; ++row_offset) {
if (width <= 8) {
const CData* datap
= &(reinterpret_cast<const CData*>(memp))[row_offset];
@@ -1380,7 +1426,6 @@ void VL_WRITEMEM_N(
}
}
cleanup:
fclose(fp);
}
@@ -1485,8 +1530,15 @@ void VL_READMEM_N(
if (VL_UNLIKELY(c==EOF)) break;
//printf("%d: Got '%c' Addr%x IN%d IgE%d IgC%d ninc%d\n",
// linenum, c, addr, innum, ignore_to_eol, ignore_to_cmt, needinc);
if (c=='\n') { linenum++; ignore_to_eol=false; if (innum) reading_addr=false; innum=false; }
else if (c=='\t' || c==' ' || c=='\r' || c=='\f') { if (innum) reading_addr=false; innum=false; }
if (c=='\n') {
linenum++; ignore_to_eol = false;
if (innum) reading_addr = false;
innum = false;
}
else if (c=='\t' || c==' ' || c=='\r' || c=='\f') {
if (innum) reading_addr = false;
innum = false;
}
// Skip // comments and detect /* comments
else if (ignore_to_cmt && lastc=='*' && c=='/') {
ignore_to_cmt = false; if (innum) reading_addr=false; innum=false;
@@ -1537,7 +1589,8 @@ void VL_READMEM_N(
*datap = ((*datap << static_cast<QData>(shift))
+ static_cast<QData>(value)) & VL_MASK_Q(width);
} else {
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))[ entry*VL_WORDS_I(width) ];
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))
[ entry*VL_WORDS_I(width) ];
if (!innum) { VL_ZERO_RESET_W(width, datap); }
_VL_SHIFTL_INPLACE_W(width, datap, static_cast<IData>(shift));
datap[0] |= value;
@@ -1722,9 +1775,11 @@ void Verilated::debug(int level) VL_MT_SAFE {
s_s.s_debug = level;
if (level) {
#ifdef VL_DEBUG
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on. Message prefix indicates {<thread>,<sequence_number>}.\n"););
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on."
" Message prefix indicates {<thread>,<sequence_number>}.\n"););
#else
VL_PRINTF_MT("- Verilated::debug attempted, but compiled without VL_DEBUG, so messages suppressed.\n");
VL_PRINTF_MT("- Verilated::debug attempted,"
" but compiled without VL_DEBUG, so messages suppressed.\n");
#endif
}
}
@@ -1788,9 +1843,9 @@ void Verilated::flushCb(VerilatedVoidCb cb) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
if (s_flushCb == cb) {} // Ok - don't duplicate
else if (!s_flushCb) { s_flushCb=cb; }
else {
else { // LCOV_EXCL_LINE
// Someday we may allow multiple callbacks ala atexit(), but until then
VL_FATAL_MT("unknown", 0, "",
VL_FATAL_MT("unknown", 0, "", // LCOV_EXCL_LINE
"Verilated::flushCb called twice with different callbacks");
}
}
@@ -1951,11 +2006,13 @@ void VerilatedImp::commandArgVl(const std::string& arg) {
}
else if (arg == "+verilator+V") {
versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
}
else if (arg == "+verilator+version") {
versionDump();
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
}
else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
@@ -2058,7 +2115,8 @@ VerilatedScope::~VerilatedScope() {
m_funcnumMax = 0; // Force callback table to empty
}
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE {
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp,
const char* suffixp) VL_MT_UNSAFE {
// Slowpath - called once/scope at construction
// We don't want the space and reference-count access overhead of strings.
m_symsp = symsp;
@@ -2079,8 +2137,8 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
// Alternative is to dynamically stretch the array, which is more code, and slower.
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
} else {
if (VL_UNLIKELY(funcnum >= m_funcnumMax)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) {
VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE
"Internal: Bad funcnum vs. pre-finalize maximum");
}
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
@@ -2094,7 +2152,8 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
// Grab dimensions
// In the future we may just create a large table at emit time and statically construct from that.
// 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();
+18 -11
View File
@@ -189,8 +189,8 @@ public:
// METHODS
/// Check that the current thread ID is the same as the construction thread ID
void check() VL_MT_UNSAFE_ONE {
if (!VL_LIKELY(m_threadid == VL_THREAD_ID())) {
fatal_different();
if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) {
fatal_different(); // LCOV_EXCL_LINE
}
}
static void fatal_different() VL_MT_SAFE;
@@ -318,8 +318,8 @@ public: // But internals only - called from VerilatedModule's
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
// m_callbacksp must be declared, as Max'es are > 0
return scopep->m_callbacksp[funcnum];
} else {
return scopep->exportFindError(funcnum);
} else { // LCOV_EXCL_LINE
return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE
}
}
};
@@ -404,7 +404,8 @@ public:
static void debug(int level) VL_MT_SAFE;
#ifdef VL_DEBUG
/// Return debug level
/// When multithreaded this may not immediately react to another thread changing the level (no mutex)
/// When multithreaded this may not immediately react to another thread
/// changing the level (no mutex)
static inline int debug() VL_MT_SAFE { return s_s.s_debug; }
#else
static inline int debug() VL_PURE { return 0; } ///< Return constant 0 debug level, so C++'s optimizer rips up
@@ -581,7 +582,8 @@ inline QData VL_RDTSC_Q() { vluint64_t val; VL_RDTSC(val); return val; }
#endif
/// Math
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus);
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp,
WDataInP lwp, WDataInP rwp, bool is_modulus);
/// File I/O
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi);
@@ -1292,7 +1294,8 @@ static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP lwp, WDataIn
static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
QData carry = 0;
for (int i=0; i<words; ++i) {
carry = carry + static_cast<QData>(lwp[i]) + static_cast<QData>(static_cast<IData>(~rwp[i]));
carry = (carry + static_cast<QData>(lwp[i])
+ static_cast<QData>(static_cast<IData>(~rwp[i])));
if (i==0) ++carry; // Negation of temp2
owp[i] = (carry & VL_ULL(0xffffffff));
carry = (carry >> VL_ULL(32)) & VL_ULL(0xffffffff);
@@ -1344,7 +1347,8 @@ static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PU
return lhs_signed * rhs_signed;
}
static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
static inline WDataOutP VL_MULS_WWW(int, int lbits, int,
WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE {
int words = VL_WORDS_I(lbits);
// cppcheck-suppress variableScope
WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
@@ -1774,7 +1778,8 @@ static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_P
return ret;
}
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int,
WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
VL_ZERO_W(lbits, owp);
// Slice size should never exceed the lhs width
int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
@@ -1888,7 +1893,8 @@ static inline void _VL_SHIFTL_INPLACE_W(int obits, WDataOutP iowp, IData rd/*1 o
// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean;
// Important: Unlike most other funcs, the shift might well be a computed
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int,
WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE {
int word_shift = VL_BITWORD_I(rd);
int bit_shift = VL_BITBIT_I(rd);
if (rd >= static_cast<IData>(obits)) { // rd may be huge with MSB set
@@ -2142,7 +2148,8 @@ static inline QData VL_SEL_QWII(int, int lbits, int, int,
}
static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int,
WDataOutP owp, WDataInP lwp, IData lsb, IData width) VL_MT_SAFE {
WDataOutP owp, WDataInP lwp,
IData lsb, IData width) VL_MT_SAFE {
int msb = lsb+width-1;
int word_shift = VL_BITWORD_I(lsb);
if (VL_UNLIKELY(msb>lbits)) { // Outside bounds,
+3 -1
View File
@@ -316,7 +316,9 @@ public:
m_insertp->m_vals[addKeynum] = valueIndex(val);
addKeynum++;
if (!legalKey(key)) {
std::string msg = "%Error: Coverage keys of one character, or letter+digit are illegal: "+key;
std::string msg
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
+key);
VL_FATAL_MT("", 0, "", msg.c_str());
}
}
+10 -5
View File
@@ -320,7 +320,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
return;
}
default:
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
varp->vltype());
return;
}
}
@@ -348,7 +349,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
return;
}
default:
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
varp->vltype());
return;
}
}
@@ -370,7 +372,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
return;
}
default:
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
varp->vltype());
return;
}
}
@@ -707,11 +710,13 @@ void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) {
// Verilator doesn't support X/Z so can just call Bit version
svPutBitArrElem1(d, value, indx1);
}
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) {
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value,
int indx1, int indx2) {
// Verilator doesn't support X/Z so can just call Bit version
svPutBitArrElem2(d, value, indx1, indx2);
}
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2, int indx3) {
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value,
int indx1, int indx2, int indx3) {
// Verilator doesn't support X/Z so can just call Bit version
svPutBitArrElem3(d, value, indx1, indx2, indx3);
}
+3 -2
View File
@@ -63,7 +63,7 @@ protected:
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
VerilatedFstCallback_t changecb,
void* ut, vluint32_t code)
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {}
~VerilatedFstCallInfo() {}
};
@@ -174,7 +174,8 @@ void VerilatedFst::addCallback(
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(isOpen())) {
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
std::string msg = (std::string("Internal: ")+__FILE__+"::"+__FUNCTION__
+" called with already open file");
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
}
VerilatedFstCallInfo* vci = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis, 1);
+18 -12
View File
@@ -77,11 +77,11 @@ public:
fstWriterClose(m_fst);
m_fst = NULL;
}
// void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
// void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
void set_time_unit(const char* unitp) { fstWriterSetTimescaleFromString(m_fst, unitp); }
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
// void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
// void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
void set_time_resolution(const char* unitp) { if (unitp) {} }
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
// double timescaleToDouble(const char* unitp);
// std::string doubleToTimescale(double value);
@@ -157,12 +157,18 @@ public:
fstWriterEmitValueChange(m_fst, m_code2symbol[code], array2Str(newval, bits));
}
void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); }
void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); }
void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); }
void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); }
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); }
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); }
void fullBit(vluint32_t code, const vluint32_t newval) {
chgBit(code, newval); }
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
chgBus(code, newval, bits); }
void fullDouble(vluint32_t code, const double newval) {
chgDouble(code, newval); }
void fullFloat(vluint32_t code, const float newval) {
chgFloat(code, newval); }
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
chgQuad(code, newval, bits); }
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
chgArray(code, newval, bits); }
void declTriBit (vluint32_t code, const char* name, int arraynum);
void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
@@ -216,11 +222,11 @@ public:
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// Set time units (s/ms, defaults to ns)
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
void set_time_unit(const char* unit) { /* TODO */ }
void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); }
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
/// Set time resolution (s/ms, defaults to ns)
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
void set_time_resolution(const char* unit) { /* TODO */ }
void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); }
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
/// Internal class access
+4 -2
View File
@@ -70,8 +70,10 @@ extern void VL_READMEM_N(bool hex, int width, int depth, int array_lsb,
extern void VL_WRITEMEM_N(bool hex, int width, int depth, int array_lsb,
const std::string& filename,
const void* memp, IData start, IData end) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld,
const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output,
const char* formatp, ...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
+8 -4
View File
@@ -61,10 +61,12 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap, size_t size
return (miss!=0);
}
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
size_t size) VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(readDiffers(datap, size))) {
std::string fn = filename();
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
std::string msg = "Can't deserialize save-restore file as was made from different model:"
+filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
close();
}
@@ -85,7 +87,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
std::string fn = filename();
std::string msg = std::string("Can't deserialize; file has wrong header signature");
std::string msg = std::string("Can't deserialize; file has wrong header signature: ")
+filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
close();
}
@@ -102,7 +105,8 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
std::string fn = filename();
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
+filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
close();
}
+12 -6
View File
@@ -142,22 +142,28 @@ public:
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
// DPI accessors
int left(int dim) const {
return dim==0 ? m_packed.left() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].left() : 0;
return dim==0 ? m_packed.left()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].left() : 0;
}
int right(int dim) const {
return dim==0 ? m_packed.right() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].right() : 0;
return dim==0 ? m_packed.right()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].right() : 0;
}
int low(int dim) const {
return dim==0 ? m_packed.low() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].low() : 0;
return dim==0 ? m_packed.low()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].low() : 0;
}
int high(int dim) const {
return dim==0 ? m_packed.high() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].high() : 0;
return dim==0 ? m_packed.high()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].high() : 0;
}
int increment(int dim) const {
return dim==0 ? m_packed.increment() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].increment() : 0;
return dim==0 ? m_packed.increment()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].increment() : 0;
}
int elements(int dim) const {
return dim==0 ? m_packed.elements() : VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].elements() : 0;
return dim==0 ? m_packed.elements()
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].elements() : 0;
}
/// Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const;
+6 -4
View File
@@ -119,8 +119,11 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads+1) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n", cpus, nThreads+1);
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n", cpus, nThreads+1);
}
}
// Create'em
for (int i=0; i<nThreads; ++i) {
@@ -218,8 +221,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
eit->m_cpu,
thread_id);
break;
default: assert(false);
break;
default: assert(false); break; // LCOV_EXCL_LINE
}
}
}
+12 -3
View File
@@ -52,6 +52,10 @@ public:
// METHODS
static vluint64_t yields() { return s_yields; }
static void yieldThread() {
++s_yields; // Statistics
std::this_thread::yield();
}
// Block until notify() has occurred, then return.
// If notify() has already occurred, return immediately.
@@ -63,11 +67,10 @@ public:
unsigned ct = 0;
while (VL_UNLIKELY(!notified())) {
VL_CPU_RELAX();
ct++;
++ct;
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
ct = 0;
++s_yields; // Statistics
std::this_thread::yield();
yieldThread();
}
}
}
@@ -149,8 +152,14 @@ public:
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
unsigned ct = 0;
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
VL_CPU_RELAX();
++ct;
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
ct = 0;
VlNotification::yieldThread();
}
}
}
};
+15 -9
View File
@@ -68,14 +68,17 @@ public:
}
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
VerilatedLockGuard lock(singleton().s_vcdMutex);
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(),
singleton().s_vcdVecp.end(), vcdp);
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
}
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
// Thread safety: Although this function is protected by a mutex so perhaps
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
// Thread safety: Although this function is protected by a mutex so
// perhaps in the future we can allow tracing in separate threads,
// vcdp->flush() assumes call from single thread
VerilatedLockGuard lock(singleton().s_vcdMutex);
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
for (VcdVec::const_iterator it = singleton().s_vcdVecp.begin();
it != singleton().s_vcdVecp.end(); ++it) {
VerilatedVcd* vcdp = *it;
vcdp->flush();
}
@@ -102,7 +105,7 @@ protected:
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
VerilatedVcdCallback_t changecb,
void* ut, vluint32_t code)
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {}
~VerilatedVcdCallInfo() {}
};
@@ -523,7 +526,8 @@ void VerilatedVcd::module(const std::string& name) {
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
int arraynum, bool tri, bool bussed, int msb, int lsb) {
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
if (!code) { VL_FATAL_MT(__FILE__, __LINE__, "",
"Internal: internal trace problem, code 0 is illegal"); }
int bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
int codesNeeded = 1+int(bits/32);
@@ -644,10 +648,12 @@ void VerilatedVcd::addCallback(
{
m_assertOne.check();
if (VL_UNLIKELY(isOpen())) {
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__
+" called with already open file";
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
}
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
VerilatedVcdCallInfo* vci
= new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
m_callbacks.push_back(vci);
}
+19 -11
View File
@@ -162,7 +162,7 @@ public:
// ACCESSORS
/// Set size in megabytes after which new file should be created
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB=rolloverMB; };
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB=rolloverMB; }
/// Is file open?
bool isOpen() const { return m_isOpen; }
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
@@ -268,7 +268,8 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
void fullTriQuad(vluint32_t code, const vluint64_t newval,
const vluint32_t newtri, int bits) {
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
*m_writep++='b';
@@ -279,7 +280,8 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
void fullTriArray(vluint32_t code, const vluint32_t* newvalp,
const vluint32_t* newtrip, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
m_sigs_oldvalp[code+word*2] = newvalp[word];
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
@@ -319,7 +321,8 @@ public:
inline void chgBit(vluint32_t code, const vluint32_t newval) {
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
fullBit(code, newval);
}
@@ -349,17 +352,20 @@ public:
}
}
}
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
inline void chgTriBit(vluint32_t code, const vluint32_t newval,
const vluint32_t newtri) {
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
| (m_sigs_oldvalp[code+1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
fullTriBit(code, newval, newtri);
}
}
}
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
inline void chgTriBus(vluint32_t code, const vluint32_t newval,
const vluint32_t newtri, int bits) {
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
| (m_sigs_oldvalp[code+1] ^ newtri));
if (VL_UNLIKELY(diff)) {
@@ -368,7 +374,8 @@ public:
}
}
}
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
inline void chgTriQuad(vluint32_t code, const vluint64_t newval,
const vluint32_t newtri, int bits) {
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
if (VL_UNLIKELY(diff)) {
@@ -377,7 +384,8 @@ public:
}
}
}
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp,
const vluint32_t* newtrip, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
@@ -432,7 +440,7 @@ public:
/// "cat" to be used to combine the header plus any number of data files.
void openNext(bool incFilename=true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); }
/// Set size in megabytes after which new file should be created
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); };
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); }
/// Close dump
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
/// Flush dump
@@ -454,7 +462,7 @@ public:
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
/// Internal class access
inline VerilatedVcd* spTrace() { return &m_sptrace; };
inline VerilatedVcd* spTrace() { return &m_sptrace; }
};
#endif // guard
+4 -2
View File
@@ -47,7 +47,8 @@ public:
sc_get_curr_simcontext()->add_trace_file(this);
# if (SYSTEMC_VERSION>=20060505)
// We want to avoid a depreciated warning, but still be back compatible.
// Turning off the message just for this still results in an annoying "to turn off" message.
// Turning off the message just for this still results in an
// annoying "to turn off" message.
sc_time t1sec(1,SC_SEC);
if (t1sec.to_default_time_units()!=0) {
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
@@ -69,7 +70,8 @@ public:
# if (SYSTEMC_VERSION>20011000)
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
# else
// VCD files must have integer timestamps, so we write all times in increments of time_resolution
// VCD files must have integer timestamps, so we write all times in
// increments of time_resolution
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
# endif
}
+67 -41
View File
@@ -74,7 +74,7 @@ public:
// We reserve word zero for the next pointer, as that's safer in case a
// dangling reference to the original remains around.
static const size_t chunk = 96;
if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_UNCOVERABLE(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* newp = t_freeHead;
t_freeHead = *((vluint8_t**)newp);
@@ -348,7 +348,7 @@ public:
varop->createPrevDatap();
}
}
if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) {
if (VL_UNCOVERABLE(vop->reason() >= CB_ENUM_MAX_VALUE)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
}
s_s.m_cbObjLists[vop->reason()].push_back(vop);
@@ -417,8 +417,9 @@ public:
void* newDatap = varop->varDatap();
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap),
newDatap, prevDatap););
varop->fullname(),
*((CData*)newDatap), *((CData*)prevDatap),
newDatap, prevDatap););
if (memcmp(prevDatap, newDatap, varop->entSize())) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %p %s v[0]=%d\n",
vop, varop->fullname(), *((CData*)newDatap)););
@@ -454,8 +455,9 @@ class VerilatedVpiError {
// Stop on vpi error/unsupported
vpi_unsupported();
}
// We need to run above code first because in the case that the callback executes further vpi
// functions we will loose the error as it will be overwritten.
// We need to run above code first because in the case that the
// callback executes further vpi functions we will loose the error
// as it will be overwritten.
VerilatedVpiImp::callCbs(cbPLIError);
}
public:
@@ -937,16 +939,16 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
VerilatedVpiImp::cbTimedAdd(vop);
return vop->castVpiHandle();
}
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp
case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp
case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
case cbReadWriteSynch: // FALLTHRU // Supported via vlt_main.cpp
case cbReadOnlySynch: // FALLTHRU // Supported via vlt_main.cpp
case cbNextSimTime: // FALLTHRU // Supported via vlt_main.cpp
case cbStartOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
case cbEndOfSimulation: // FALLTHRU // Supported via vlt_main.cpp
case cbValueChange: // FALLTHRU // Supported via vlt_main.cpp
case cbPLIError: // FALLTHRU // NOP, but need to return handle, so make object
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
VerilatedVpioCb* vop = new VerilatedVpioCb(cb_data_p, 0);
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb %d %p\n", cb_data_p->reason, vop););
VerilatedVpiImp::cbReasonAdd(vop);
@@ -1099,7 +1101,8 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (VL_UNLIKELY(!vop)) return 0;
if (vop->varp()->dims() < 2) return 0;
if (vop->varp()->dims() > 2) {
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
_VL_VPI_WARNING(__FILE__, __LINE__,
"%s: %s, object %s has unsupported number of indices (%d)",
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
vop->fullname() , vop->varp()->dims());
}
@@ -1111,7 +1114,8 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (vop->varp()->dims() < 2) return 0;
// Unsupported is multidim list
if (vop->varp()->dims() > 2) {
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
_VL_VPI_WARNING(__FILE__, __LINE__,
"%s: %s, object %s has unsupported number of indices (%d)",
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type),
vop->fullname() , vop->varp()->dims());
}
@@ -1251,8 +1255,9 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
return;
case VLVT_WDATA: {
int words = VL_WORDS_I(vop->varp()->packed().elements());
if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
}
WDataInP datap = (reinterpret_cast<IData*>(vop->varDatap()));
for (int i=0; i<words; ++i) {
@@ -1271,7 +1276,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
}
default: {
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return;
}
}
@@ -1354,7 +1360,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
default:
strcpy(outStr, "0");
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return;
}
} else if (value_p->format == vpiDecStrVal) {
@@ -1363,19 +1370,24 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
// outStrSz does not include NULL termination so add one
case VLVT_UINT8:
VL_SNPRINTF(outStr, outStrSz+1, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(vop->varDatap())))); return;
static_cast<unsigned char>(*(reinterpret_cast<CData*>(vop->varDatap()))));
return;
case VLVT_UINT16:
VL_SNPRINTF(outStr, outStrSz+1, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(vop->varDatap())))); return;
static_cast<unsigned short>(*(reinterpret_cast<SData*>(vop->varDatap()))));
return;
case VLVT_UINT32:
VL_SNPRINTF(outStr, outStrSz+1, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(vop->varDatap())))); return;
static_cast<unsigned int>(*(reinterpret_cast<IData*>(vop->varDatap()))));
return;
case VLVT_UINT64:
VL_SNPRINTF(outStr, outStrSz+1, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(vop->varDatap())))); return;
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(vop->varDatap()))));
return;
default:
strcpy(outStr, "-1");
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
_VL_VPI_ERROR(__FILE__, __LINE__,
"%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
return;
}
@@ -1416,7 +1428,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
}
default:
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return;
}
} else if (value_p->format == vpiStringVal) {
@@ -1448,7 +1461,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
}
default:
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return;
}
} else if (value_p->format == vpiIntVal) {
@@ -1467,7 +1481,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
default:
value_p->value.integer = 0;
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return;
}
} else if (value_p->format == vpiSuppressVal) {
@@ -1505,7 +1520,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
vop->varp()->datap(), vop->varDatap()););
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
_VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value to signal marked read-only,"
_VL_VPI_WARNING(__FILE__, __LINE__,
"Ignoring vpi_put_value to signal marked read-only,"
" use public_flat_rw instead: ",
vop->fullname());
return 0;
@@ -1544,7 +1560,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
}
default: {
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return NULL;
}
}
@@ -1572,7 +1589,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
}
default:
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return 0;
}
} else if (value_p->format == vpiOctStrVal) {
@@ -1600,9 +1618,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
if (digit >= '0' && digit <= '7') {
val.half = digit-'0';
} else {
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Non octal character '%c' in '%s' as value %s for %s",
_VL_VPI_WARNING(__FILE__, __LINE__,
"%s: Non octal character '%c' in '%s' as value %s for %s",
VL_FUNC, digit, value_p->value.str,
VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
val.half = 0;
}
} else {
@@ -1634,7 +1654,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
}
default:
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return 0;
}
} else if (value_p->format == vpiDecStrVal) {
@@ -1642,12 +1663,15 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
unsigned long long val;
int success = sscanf(value_p->value.str, "%30llu%15s", &val, remainder);
if (success < 1) {
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
VL_FUNC, value_p->value.str, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
_VL_VPI_ERROR(__FILE__, __LINE__,
"%s: Parsing failed for '%s' as value %s for %s",
VL_FUNC, value_p->value.str,
VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
return 0;
}
if (success > 1) {
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s",
_VL_VPI_WARNING(__FILE__, __LINE__,
"%s: Trailing garbage '%s' in '%s' as value %s for %s",
VL_FUNC, remainder, value_p->value.str,
VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
@@ -1663,8 +1687,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
(reinterpret_cast<IData*>(vop->varDatap()))[1] &= vop->mask(); break;
case VLVT_WDATA:
default:
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
_VL_VPI_ERROR(__FILE__, __LINE__,
"%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format),
vop->fullname());
return 0;
}
return object;
+15 -5
View File
@@ -36,6 +36,8 @@
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
# define VL_ATTR_ALWINLINE __attribute__ ((always_inline))
# define VL_ATTR_COLD __attribute__ ((cold))
# define VL_ATTR_HOT __attribute__ ((hot))
# define VL_ATTR_NORETURN __attribute__ ((noreturn))
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
# define VL_ATTR_PURE __attribute__ ((pure))
@@ -70,6 +72,12 @@
#ifndef VL_ATTR_ALWINLINE
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
#endif
#ifndef VL_ATTR_COLD
# define VL_ATTR_COLD ///< Function is rarely executed
#endif
#ifndef VL_ATTR_HOT
# define VL_ATTR_HOT ///< Function is highly executed
#endif
#ifndef VL_ATTR_NORETURN
# define VL_ATTR_NORETURN ///< Function does not ever return
#endif
@@ -102,6 +110,7 @@
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
#endif
#define VL_UNCOVERABLE(x) VL_UNLIKELY(x) ///< Boolean expression never hit by users (no coverage)
#ifndef VL_UNREACHABLE
# define VL_UNREACHABLE ///< Point that may never be reached
#endif
@@ -198,6 +207,11 @@
//=========================================================================
// Basic integer types
#ifdef __MINGW32__
# define __USE_MINGW_ANSI_STDIO 1 // Force old MinGW (GCC 5 and older) to use C99 formats
# define __STDC_FORMAT_MACROS 1 // Otherwise MinGW doesn't get PRId64 for fstapi.c
#endif
#if defined(__CYGWIN__)
# include <stdint.h>
@@ -270,10 +284,6 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
#ifdef _MSC_VER
# define VL_PRI64 "I64"
#else // use standard C99 format specifiers
# ifdef __MINGW32__
# define __USE_MINGW_ANSI_STDIO 1 // Force old MinGW (GCC 5 and older) to use C99 formats
# define __STDC_FORMAT_MACROS 1 // Otherwise MinGW doesn't get PRId64 for fstapi.c
# endif
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
# define VL_PRI64 "l"
# else
@@ -397,4 +407,4 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
//=========================================================================
#endif /*guard*/
#endif // Guard
+2 -2
View File
@@ -302,7 +302,7 @@ serial mode; that is a possible area for improvement.)
=item C<Wave Scheduling>
To allow the verilated model to run in parallel with the testbench, it
To allow the Verilated model to run in parallel with the testbench, it
might be nice to support "wave" scheduling, in which work on a cycle begins
before eval() is called or continues after eval() returns. For now all
work on a cycle happens during the eval() call, leaving Verilator's threads
@@ -826,7 +826,7 @@ level. For example after inlining:
The textual name of this node AST type (always in capitals). Many of these
correspond directly to Verilog entities (for example C<MODULE> and
C<TASK>), but others are internal to Verialtor (for example C<NETLIST> and
C<TASK>), but others are internal to Verilator (for example C<NETLIST> and
C<BASICDTYPE>).
=item Address of the node
+263
View File
@@ -0,0 +1,263 @@
#!/usr/bin/perl -w
# See copyright, etc in below POD section.
######################################################################
use Cwd;
use File::Copy qw(cp);
use File::Path qw(mkpath);
use Getopt::Long;
use IO::File;
use Pod::Usage;
use strict;
use vars qw($Debug);
our $Exclude_Line_Regexp;
our @Remove_Sources;
our @Source_Globs;
#======================================================================
# main
our $Opt_Stage = 0;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"debug" => sub { $Debug = 1; },
"<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; },
"stage=i" => \$Opt_Stage,
)) {
die "%Error: Bad usage, try 'install_test --help'\n";
}
test();
exit(0);
#######################################################################
sub test {
-r "nodist/code_coverage.dat" or die "%Error: Run from the top of the verilator kit,";
require "./nodist/code_coverage.dat";
if ($Opt_Stage <= 0) {
print "Stage 0: configure (coverage on)\n";
run("make distclean");
run("./configure --enable-longtests CXX='g++ --coverage'");
run("make -k");
# The optimized versions will not collect good coverage, overwrite them
run("cp bin/verilator_bin_dbg bin/verilator_bin");
run("cp bin/verilator_coverage_bin_dbg bin/verilator_coverage_bin");
}
if ($Opt_Stage <= 1) {
print "Stage 1: make examples (with coverage on)\n";
run("make examples");
run("make test_regress");
}
my $cc_dir = "nodist/obj_dir/coverage";
if ($Opt_Stage <= 2) {
print "Stage 2: Create info files under $cc_dir\n";
mkpath($cc_dir);
mkpath("$cc_dir/info");
my $dats = `find . -print | grep .gcda`;
my %dirs;
foreach my $dat (split '\n', $dats) {
$dat =~ s!/[^/]+$!!;
$dirs{$dat} = 1;
}
foreach my $dir (sort keys %dirs) {
(my $outname = $dir) =~ s![^a-zA-Z0-9]+!_!g;
run("cd $cc_dir/info ; lcov -c -d ../../../../$dir -o app_test_${outname}.info");
}
}
if ($Opt_Stage <= 3) {
# lcov doesn't have a control file to override single lines, so replicate the sources
print "Stage 3: Clone sources under $cc_dir\n";
clone_sources($cc_dir);
}
if ($Opt_Stage <= 4) {
print "Stage 4: Copy .gcno files\n";
my $dats = `find . -print | grep .gcno`;
foreach my $dat (split '\n', $dats) {
next if $dat =~ /$cc_dir/;
my $outdat = $cc_dir."/".$dat;
#print "cp $dat, $outdat);\n";
cp($dat, $outdat);
}
}
if ($Opt_Stage <= 5) {
print "Stage 5: Combine data files\n";
run("cd $cc_dir ; lcov -c -i -d src/obj_dbg -o app_base.info");
run("cd $cc_dir ; lcov -a app_base.info -o app_total.info");
my $infos = `cd $cc_dir ; find info -print | grep .info`;
my $comb = "";
my @infos = (split /\n/, $infos);
foreach my $info (@infos) {
$comb .= " -a $info";
# Need to batch them to avoid overrunning shell command length limit
if (length($comb) > 10000 || $info eq $infos[$#infos]) {
run("cd $cc_dir ; lcov -a app_total.info $comb -o app_total.info");
$comb = "";
}
}
}
if ($Opt_Stage <= 6) {
print "Stage 6: Filter processed source files\n";
my $cmd = '';
foreach my $glob (@Remove_Sources) {
$cmd .= " '$glob'";
}
run("cd $cc_dir ; lcov --remove app_total.info $cmd -o app_total.info");
}
if ($Opt_Stage <= 7) {
print "Stage 7: Create HTML\n";
cleanup_abs_paths($cc_dir, "$cc_dir/app_total.info", "$cc_dir/app_total.info");
run("cd $cc_dir ; genhtml app_total.info --demangle-cpp"
." --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html");
}
if ($Opt_Stage <= 9) {
print "*-* All Finished *-*\n";
}
}
sub clone_sources {
my $cc_dir = shift;
my $excluded_lines = 0;
foreach my $glob (@Source_Globs) {
foreach my $infile (glob $glob) {
$infile !~ m!^/!
or die "%Error: source globs should be relative not absolute filenames,";
my $outfile = $cc_dir."/".$infile;
(my $outpath = $outfile) =~ s!/[^/]*$!!;
mkpath($outpath);
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
my $lineno = 0;
while (defined(my $line = $fh->getline)) {
$lineno++;
chomp $line;
if ($line !~ m!// LCOV_EXCL_LINE!
&& $line =~ /$Exclude_Line_Regexp/) {
$line .= " //code_coverage: // LCOV_EXCL_LINE";
$excluded_lines++;
#print "$infile:$lineno: $line";
} else {
}
$ofh->print("$line\n");
}
}
}
print "Source code lines automatically LCOV_EXCL_LINE'ed: $excluded_lines\n";
}
sub cleanup_abs_paths {
my $cc_dir = shift;
my $infile = shift;
my $outfile = shift;
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
my @lines;
while (defined(my $line = $fh->getline)) {
if ($line =~ m!^SF:!) {
$line =~ s!$ENV{VERILATOR_ROOT}/!!;
$line =~ s!$cc_dir/!!;
$line =~ s!obj_dbg/verilog.y$!verilog.y!;
#print "Remaining SF: ",$line;
}
push @lines, $line;
}
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
$ofh->print(@lines);
}
#######################################################################
# .dat file callbacks
sub exclude_line_regexp {
$Exclude_Line_Regexp = shift;
}
sub remove_source {
my @srcs = @_;
push @Remove_Sources, @srcs;
}
sub source_globs {
my @dirs = @_;
push @Source_Globs, @dirs;
}
#######################################################################
sub run {
# Run a system command, check errors
my $command = shift;
print "\t$command\n";
system "$command";
my $status = $?;
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
}
#######################################################################
__END__
=pod
=head1 NAME
code_coverage - Build and collect Verilator coverage
=head1 SYNOPSIS
cd $VERILATOR_ROOT
nodist/code_coverage
=head1 DESCRIPTION
code_coverage builds Verilator with C++ coverage support and runs tests
with coverage enabled.
This will rebuild the current object files.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=item -stage I<stage>
Runs a specific stage (see the script).
=back
=head1 DISTRIBUTION
Copyright 2019-2019 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
C<lcov>
=cut
######################################################################
### Local Variables:
### compile-command: "cd .. ; nodist/code_coverage "
### End:
+42
View File
@@ -0,0 +1,42 @@
# -*- Perl -*-
# DESCRIPTION: Verilator: Internal C++ code lcov control file
#
# Copyright 2019-2019 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.
source_globs("src/*.cpp",
"src/*.h",
"src/*.l",
"src/*.y",
"src/obj_dbg/*.h",
"src/obj_dbg/*.cpp",
"include/*.c",
"include/*.cpp",
"include/*.h",
"include/*/*.h",
"include/*/*.cpp",
"include/*/*.c",
);
remove_source("/usr/include/*");
remove_source("*/include/sysc/*");
remove_source("*/V3ClkGater.cpp");
remove_source("*/V3ClkGater.h");
remove_source("*/V3GraphDfa.cpp");
remove_source("*/V3GraphDfa.h");
remove_source("*/V3Lexer_pregen.yy.cpp");
remove_source("*/V3PreLex_pregen.yy.cpp");
remove_source("*/verilog.c");
remove_source("*include/gtkwave/*");
# Something wrong in generation, unfortunately as would like this
#genhtml: ERROR: cannot read /svaha/wsnyder/SandBox/homecvs/v4/verilator/src/obj_dbg/verilog.y
#remove_source("*/src/obj_dbg/verilog.y");
remove_source("*test_regress/*");
remove_source("*examples/*");
exclude_line_regexp(qr/(\bv3fatalSrc\b|\bVL_UNCOVERABLE\b)/);
1;
+23 -20
View File
@@ -87,27 +87,30 @@ sub find_edits {
my $hunk = $editlines->{$lineno}{hunk};
$hunk_lastlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
}
# Expand to include }'s that finish basic block
foreach my $hunk (keys %hunk_lastlines) {
while (my $lineno = $hunk_lastlines{$hunk}) {
++$lineno;
if (($editlines->{$lineno}{line} || "")
=~ /^[+ ]\s+}[ \t};]*$/) {
$hunk_lastlines{$hunk} = $lineno;
} else {
last;
if ($Opt_Widen) {
# Expand to include }'s that finish basic block
foreach my $hunk (keys %hunk_lastlines) {
while (my $lineno = $hunk_lastlines{$hunk}) {
++$lineno;
if (($editlines->{$lineno}{line} || "")
=~ /^[+ ]\s+}[ \t};]*$/) {
$hunk_lastlines{$hunk} = $lineno;
} else {
last;
}
}
}
}
# Expand to always untabify at least 3 lines (so that future diff will
# have non-tabs within a edit hunk distance
foreach my $hunk (keys %hunk_firstlines) {
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
--$hunk_firstlines{$hunk};
++$hunk_lastlines{$hunk};
}
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
++$hunk_lastlines{$hunk};
# Expand to always untabify at least 3 lines (so that future diff will
# have non-tabs within a edit hunk distance
foreach my $hunk (keys %hunk_firstlines) {
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
--$hunk_firstlines{$hunk};
++$hunk_lastlines{$hunk};
}
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
++$hunk_lastlines{$hunk};
}
}
}
@@ -129,7 +132,7 @@ sub edit_file {
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
push @editits, $lineno if $editlines->{$lineno}{editit};
}
return if $#editits < 0;
if (ignore($filename)) {
print "%Warning: Ignoring $filename\n";
+9 -8
View File
@@ -60,6 +60,8 @@ pkgdatadir = @pkgdatadir@
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CPPFLAGS += @CPPFLAGS@
CFLAGS += @CFLAGS@
CXXFLAGS += @CXXFLAGS@
LDFLAGS += @LDFLAGS@
EXEEXT = @EXEEXT@
CFG_CXXFLAGS_SRC = @CFG_CXXFLAGS_SRC@
@@ -135,7 +137,7 @@ VLCOVGEN = $(srcdir)/vlcovgen
all: make_info $(TGT)
make_info:
@echo " Compile flags: " $(CXX) ${CPPFLAGS}
@echo " Compile flags: " $(CXX) ${CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
clean mostlyclean distclean maintainer-clean::
-rm -f *.o *.d perlxsi.c *_gen_*
@@ -167,7 +169,6 @@ RAW_OBJS = \
V3Cdc.o \
V3Changed.o \
V3Clean.o \
V3ClkGater.o \
V3Clock.o \
V3Combine.o \
V3Config.o \
@@ -280,21 +281,21 @@ V3Number_test: V3Number_test.o
$(PERL) $(ASTGEN) -I$(srcdir) $*.cpp
%.o: %.cpp
$(OBJCACHE) ${CXX} ${CPPFLAGSWALL} -c $<
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $<
%.o: %.c
$(OBJCACHE) ${CC} ${CPPFLAGSWALL} -c $<
$(OBJCACHE) ${CC} ${CFLAGS} ${CPPFLAGSWALL} -c $<
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
#### Generated files
+235 -228
View File
@@ -21,12 +21,12 @@
//
// Note this can be called multiple times.
// Create a IACTIVE(initial), SACTIVE(combo)
// ALWAYS: Remove any-edges from sense list
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
// Else fold into SACTIVE(sequent).
// OPTIMIZE: When support async clocks, fold into that active if possible
// INITIAL: Move into IACTIVE
// WIRE: Move into SACTIVE(combo)
// ALWAYS: Remove any-edges from sense list
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
// Else fold into SACTIVE(sequent).
// OPTIMIZE: When support async clocks, fold into that active if possible
// INITIAL: Move into IACTIVE
// WIRE: Move into SACTIVE(combo)
//
//*************************************************************************
@@ -56,66 +56,68 @@ class ActiveNamer : public ActiveBaseVisitor {
private:
typedef std::map<string,AstActive*> ActiveNameMap;
// STATE
AstScope* m_scopep; // Current scope to add statement to
AstActive* m_iActivep; // For current scope, the IActive we're building
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
AstScope* m_scopep; // Current scope to add statement to
AstActive* m_iActivep; // For current scope, the IActive we're building
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
SenTreeSet m_activeSens; // Sen lists for each active we've made
SenTreeSet m_activeSens; // Sen lists for each active we've made
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
ActiveMap m_activeMap; // Map sentree to active, for folding.
// METHODS
void addActive(AstActive* nodep) {
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
m_scopep->addActivep(nodep);
UASSERT_OBJ(m_scopep, nodep, "NULL scope");
m_scopep->addActivep(nodep);
}
// VISITORS
virtual void visit(AstScope* nodep) {
m_scopep = nodep;
m_iActivep = NULL;
m_cActivep = NULL;
m_scopep = nodep;
m_iActivep = NULL;
m_cActivep = NULL;
m_activeSens.clear();
m_activeMap.clear();
iterateChildren(nodep);
// Don't clear scopep, the namer persists beyond this visit
// Don't clear scopep, the namer persists beyond this visit
}
virtual void visit(AstSenTree* nodep) {
// Simplify sensitivity list
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
// Simplify sensitivity list
V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep);
}
// Empty visitors, speed things up
virtual void visit(AstNodeStmt* nodep) { }
//--------------------
// Default
virtual void visit(AstNode* nodep) {
// Default: Just iterate
// Default: Just iterate
iterateChildren(nodep);
}
// METHODS
public:
AstScope* scopep() { return m_scopep; }
AstActive* getCActive(FileLine* fl) {
if (!m_cActivep) {
m_cActivep = new AstActive(fl, "combo",
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Combo())));
m_cActivep->sensesStorep(m_cActivep->sensesp());
addActive(m_cActivep);
}
return m_cActivep;
if (!m_cActivep) {
m_cActivep = new AstActive(
fl, "combo",
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
m_cActivep->sensesStorep(m_cActivep->sensesp());
addActive(m_cActivep);
}
return m_cActivep;
}
AstActive* getIActive(FileLine* fl) {
if (!m_iActivep) {
m_iActivep = new AstActive(fl, "initial",
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Initial())));
m_iActivep->sensesStorep(m_iActivep->sensesp());
addActive(m_iActivep);
}
return m_iActivep;
if (!m_iActivep) {
m_iActivep = new AstActive(
fl, "initial",
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
m_iActivep->sensesStorep(m_iActivep->sensesp());
addActive(m_iActivep);
}
return m_iActivep;
}
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
// Return a sentree in this scope that matches given sense list.
// Return a sentree in this scope that matches given sense list.
AstActive* activep = NULL;
AstActive* activep = NULL;
AstSenTree* activeSenp = m_activeSens.find(sensesp);
if (activeSenp) {
ActiveMap::iterator it = m_activeMap.find(activeSenp);
@@ -123,26 +125,26 @@ public:
activep = it->second;
}
// Not found, form a new one
if (!activep) {
AstSenTree* newsenp = sensesp->cloneTree(false);
activep = new AstActive(fl, "sequent", newsenp);
activep->sensesStorep(activep->sensesp());
UINFO(8," New ACTIVE "<<activep<<endl);
// Form the sensitivity list
addActive(activep);
// Not found, form a new one
if (!activep) {
AstSenTree* newsenp = sensesp->cloneTree(false);
activep = new AstActive(fl, "sequent", newsenp);
activep->sensesStorep(activep->sensesp());
UINFO(8," New ACTIVE "<<activep<<endl);
// Form the sensitivity list
addActive(activep);
m_activeMap[newsenp] = activep;
m_activeSens.add(newsenp);
// Note actives may have also been added above in the Active visitor
}
return activep;
// Note actives may have also been added above in the Active visitor
}
return activep;
}
public:
// CONSTUCTORS
ActiveNamer() {
m_scopep = NULL;
m_iActivep = NULL;
m_cActivep = NULL;
m_scopep = NULL;
m_iActivep = NULL;
m_cActivep = NULL;
}
virtual ~ActiveNamer() {}
void main(AstScope* nodep) {
@@ -157,55 +159,55 @@ class ActiveDlyVisitor : public ActiveBaseVisitor {
public:
enum CheckType { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
private:
CheckType m_check; // Combo logic or other
AstNode* m_alwaysp; // Always we're under
AstNode* m_assignp; // In assign
CheckType m_check; // Combo logic or other
AstNode* m_alwaysp; // Always we're under
AstNode* m_assignp; // In assign
// VISITORS
virtual void visit(AstAssignDly* nodep) {
if (m_check != CT_SEQ) {
// Convert to a non-delayed assignment
UINFO(5," ASSIGNDLY "<<nodep<<endl);
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial"
" or final block; suggest blocking assignments (=).");
} else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races
} else {
if (m_check != CT_SEQ) {
// Convert to a non-delayed assignment
UINFO(5," ASSIGNDLY "<<nodep<<endl);
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
} else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races
} else {
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
" (non flop or latch) block; suggest blocking assignments (=).");
}
" (non flop or latch) block\n"
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
}
AstNode* newp = new AstAssign(nodep->fileline(),
nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
nodep->replaceWith(newp);
nodep->deleteTree(); VL_DANGLING(nodep);
}
nodep->replaceWith(newp);
nodep->deleteTree(); VL_DANGLING(nodep);
}
}
virtual void visit(AstAssign* nodep) {
if (m_check == CT_SEQ) {
AstNode* las = m_assignp;
m_assignp = nodep;
if (m_check == CT_SEQ) {
AstNode* las = m_assignp;
m_assignp = nodep;
iterateAndNextNull(nodep->lhsp());
m_assignp = las;
}
m_assignp = las;
}
}
virtual void visit(AstVarRef* nodep) {
AstVar* varp=nodep->varp();
if (m_check == CT_SEQ
&& m_assignp
&& !varp->isUsedLoopIdx() // Ignore loop indicies
&& !varp->isTemp()
) {
// Allow turning off warnings on the always, or the variable also
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
) {
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
nodep->v3warn(BLKSEQ,"Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=).");
}
}
AstVar* varp = nodep->varp();
if (m_check == CT_SEQ
&& m_assignp
&& !varp->isUsedLoopIdx() // Ignore loop indicies
&& !varp->isTemp()) {
// Allow turning off warnings on the always, or the variable also
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
m_assignp->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block\n"
<<m_assignp->warnMore()<<"... Suggest delayed assignments (<=)");
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
}
}
}
//--------------------
virtual void visit(AstNode* nodep) {
@@ -214,9 +216,9 @@ private:
public:
// CONSTUCTORS
ActiveDlyVisitor(AstNode* nodep, CheckType check) {
m_alwaysp = nodep;
m_check = check;
m_assignp = NULL;
m_alwaysp = nodep;
m_check = check;
m_assignp = NULL;
iterate(nodep);
}
virtual ~ActiveDlyVisitor() {}
@@ -229,183 +231,188 @@ class ActiveVisitor : public ActiveBaseVisitor {
private:
// NODE STATE
// Each call to V3Const::constify
// AstNode::user4() Used by V3Const::constify, called below
// AstNode::user4() Used by V3Const::constify, called below
// STATE
ActiveNamer m_namer; // Tracking of active names
AstCFunc* m_scopeFinalp; // Final function for this scope
bool m_itemCombo; // Found a SenItem combo
bool m_itemSequent; // Found a SenItem sequential
ActiveNamer m_namer; // Tracking of active names
AstCFunc* m_scopeFinalp; // Final function for this scope
bool m_itemCombo; // Found a SenItem combo
bool m_itemSequent; // Found a SenItem sequential
// VISITORS
virtual void visit(AstScope* nodep) {
// Create required actives and add to scope
UINFO(4," SCOPE "<<nodep<<endl);
// Clear last scope's names, and collect this scope's existing names
m_namer.main(nodep);
m_scopeFinalp = NULL;
// Create required actives and add to scope
UINFO(4," SCOPE "<<nodep<<endl);
// Clear last scope's names, and collect this scope's existing names
m_namer.main(nodep);
m_scopeFinalp = NULL;
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) {
// Actives are being formed, so we can ignore any already made
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitial* nodep) {
// Relink to IACTIVE, unless already under it
UINFO(4," INITIAL "<<nodep<<endl);
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Relink to IACTIVE, unless already under it
UINFO(4," INITIAL "<<nodep<<endl);
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* nodep) {
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignW* nodep) {
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstCoverToggle* nodep) {
// Relink to CACTIVE, unless already under it
UINFO(4," COVERTOGGLE "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Relink to CACTIVE, unless already under it
UINFO(4," COVERTOGGLE "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstFinal* nodep) {
// Relink to CFUNC for the final
UINFO(4," FINAL "<<nodep<<endl);
if (!nodep->bodysp()) { // Empty, Kill it.
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), m_namer.scopep());
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
m_scopeFinalp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->formCallTree(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}
nodep->unlinkFrBack();
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
nodep->deleteTree(); VL_DANGLING(nodep);
// Relink to CFUNC for the final
UINFO(4," FINAL "<<nodep<<endl);
if (!nodep->bodysp()) { // Empty, Kill it.
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(),
m_namer.scopep());
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
m_scopeFinalp->addInitsp(
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->formCallTree(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}
nodep->unlinkFrBack();
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
nodep->deleteTree(); VL_DANGLING(nodep);
}
// METHODS
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
// Move always to appropriate ACTIVE based on its sense list
if (oldsensesp
&& oldsensesp->sensesp()
// Move always to appropriate ACTIVE based on its sense list
if (oldsensesp
&& oldsensesp->sensesp()
&& VN_IS(oldsensesp->sensesp(), SenItem)
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
// Never executing. Kill it.
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
// Never executing. Kill it.
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
"Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
// Read sensitivitues
m_itemCombo = false;
m_itemSequent = false;
// Read sensitivitues
m_itemCombo = false;
m_itemSequent = false;
iterateAndNextNull(oldsensesp);
bool combo = m_itemCombo;
bool sequent = m_itemSequent;
bool combo = m_itemCombo;
bool sequent = m_itemSequent;
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
if (combo && sequent) {
if (!v3Global.opt.bboxUnsup()) {
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
}
sequent = false;
}
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
if (combo && sequent) {
if (!v3Global.opt.bboxUnsup()) {
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
}
sequent = false;
}
AstActive* wantactivep = NULL;
if (combo && !sequent) {
// Combo: Relink to ACTIVE(combo)
wantactivep = m_namer.getCActive(nodep->fileline());
} else {
// Sequential: Build a ACTIVE(name)
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
// always (posedge RESET) { if (RESET).... } we know RESET is true.
// Summarize a long list of combo inputs as just "combo"
#ifndef __COVERITY__ // Else dead code on next line.
if (combo) oldsensesp->addSensesp
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
AstActive* wantactivep = NULL;
if (combo && !sequent) {
// Combo: Relink to ACTIVE(combo)
wantactivep = m_namer.getCActive(nodep->fileline());
} else {
// Sequential: Build a ACTIVE(name)
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
// always (posedge RESET) { if (RESET).... } we know RESET is true.
// Summarize a long list of combo inputs as just "combo"
#ifndef __COVERITY__ // Else dead code on next line.
if (combo) oldsensesp->addSensesp
(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
#endif
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
}
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
}
// Delete sensitivity list
if (oldsensesp) {
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
}
// Delete sensitivity list
if (oldsensesp) {
oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp);
}
// Move node to new active
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Move node to new active
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
} else {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
}
}
else if (!combo && sequent) {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
}
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
} else {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
}
}
else if (!combo && sequent) {
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
}
}
virtual void visit(AstAlways* nodep) {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4," ALW "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
// 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(); VL_DANGLING(nodep);
return;
}
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
if (!nodep->bodysp()) {
// Empty always. Kill it.
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
}
virtual void visit(AstAlwaysPublic* nodep) {
// 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(), VAlwaysKwd::ALWAYS);
// 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(), VAlwaysKwd::ALWAYS);
}
virtual void visit(AstSenGate* nodep) {
AstSenItem* subitemp = nodep->sensesp();
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
&& subitemp->edgeType() != AstEdgeType::ET_POSEDGE
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
nodep->v3fatalSrc("Strange activity type under SenGate");
}
AstSenItem* subitemp = nodep->sensesp();
UASSERT_OBJ(subitemp->edgeType() == AstEdgeType::ET_ANYEDGE
|| subitemp->edgeType() == AstEdgeType::ET_POSEDGE
|| subitemp->edgeType() == AstEdgeType::ET_NEGEDGE,
nodep, "Strange activity type under SenGate");
iterateChildren(nodep);
}
virtual void visit(AstSenItem* nodep) {
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
m_itemCombo = true;
// Delete the sensitivity
// We'll add it as a generic COMBO SenItem in a moment.
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->varrefp()) {
// V3LinkResolve should have cleaned most of these up
if (!nodep->varrefp()->width1()) nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
<<nodep->varrefp()->prettyName());
m_itemSequent = true;
nodep->varrefp()->varp()->usedClock(true);
}
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
m_itemCombo = true;
// Delete the sensitivity
// We'll add it as a generic COMBO SenItem in a moment.
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->varrefp()) {
// V3LinkResolve should have cleaned most of these up
if (!nodep->varrefp()->width1()) {
nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
<<nodep->varrefp()->prettyNameQ());
}
m_itemSequent = true;
nodep->varrefp()->varp()->usedClock(true);
}
}
// Empty visitors, speed things up
@@ -418,9 +425,9 @@ private:
public:
// CONSTUCTORS
explicit ActiveVisitor(AstNetlist* nodep) {
m_scopeFinalp = NULL;
m_itemCombo = false;
m_itemSequent = false;
m_scopeFinalp = NULL;
m_itemCombo = false;
m_itemSequent = false;
iterate(nodep);
}
virtual ~ActiveVisitor() {}
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void activeAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+66 -64
View File
@@ -21,9 +21,9 @@
//
// Note this can be called multiple times.
// Across all ACTIVES
// SenTrees are now under each ACTIVE statement, we want them global:
// Find SenTree in under global TopScope, or create it there
// Move SenTree the global SenTree
// SenTrees are now under each ACTIVE statement, we want them global:
// Find SenTree in under global TopScope, or create it there
// Move SenTree the global SenTree
//
//*************************************************************************
@@ -43,91 +43,93 @@ class ActiveTopVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist
// AstNode::user() bool. True if processed
// AstNode::user() bool. True if processed
// Each call to V3Const::constify
// AstNode::user4() Used by V3Const::constify, called below
AstUser1InUse m_inuser1;
// AstNode::user4() Used by V3Const::constify, called below
AstUser1InUse m_inuser1;
// STATE
AstTopScope* m_topscopep; // Top scope for adding sentrees under
SenTreeFinder m_finder; // Find global sentree's and add them
AstTopScope* m_topscopep; // Top scope for adding sentrees under
SenTreeFinder m_finder; // Find global sentree's and add them
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstTopScope* nodep) {
m_topscopep = nodep;
m_finder.main(m_topscopep);
m_topscopep = nodep;
m_finder.main(m_topscopep);
iterateChildren(nodep);
m_topscopep = NULL;
m_topscopep = NULL;
}
virtual void visit(AstNodeModule* nodep) {
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4," MOD "<<nodep<<endl);
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4," MOD "<<nodep<<endl);
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) {
UINFO(4," ACTIVE "<<nodep<<endl);
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
AstSenTree* sensesp = nodep->sensesp();
if (!sensesp) nodep->v3fatalSrc("NULL");
if (sensesp->sensesp()
UINFO(4," ACTIVE "<<nodep<<endl);
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
AstSenTree* sensesp = nodep->sensesp();
UASSERT_OBJ(sensesp, nodep, "NULL");
if (sensesp->sensesp()
&& VN_IS(sensesp->sensesp(), SenItem)
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
// Never executing. Kill it.
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* newsentreep
// Never executing. Kill it.
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
"Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* newsentreep
= new AstSenTree(nodep->fileline(),
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
UINFO(4," lookdone\n");
if (wantp != sensesp) {
// Move the active's contents to the other active
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
if (nodep->sensesStorep()) {
if (sensesp != nodep->sensesStorep()) nodep->v3fatalSrc("sensesStore should have been deleted earlier if different");
sensesp->unlinkFrBack();
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
}
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
UINFO(4," lookdone\n");
if (wantp != sensesp) {
// Move the active's contents to the other active
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
if (nodep->sensesStorep()) {
UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep,
"sensesStore should have been deleted earlier if different");
sensesp->unlinkFrBack();
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
}
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
//iterateChildren(nodep);
}
virtual void visit(AstInitial* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
virtual void visit(AstInitial* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignAlias* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
virtual void visit(AstAssignAlias* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignW* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
virtual void visit(AstAssignW* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlways* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
virtual void visit(AstAlways* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlwaysPublic* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
virtual void visit(AstAlwaysPublic* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstFinal* nodep) {
nodep->v3fatalSrc("Node should have been deleted");
virtual void visit(AstFinal* nodep) { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been deleted");
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep) {}
@@ -139,7 +141,7 @@ private:
public:
// CONSTUCTORS
explicit ActiveTopVisitor(AstNetlist* nodep) {
m_topscopep = NULL;
m_topscopep = NULL;
iterate(nodep);
}
virtual ~ActiveTopVisitor() {}
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void activeTopAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+208 -202
View File
@@ -37,46 +37,46 @@ class AssertVisitor : public AstNVisitor {
private:
// NODE STATE/TYPES
// Cleared on netlist
// AstNode::user() -> bool. True if processed
AstUser1InUse m_inuser1;
// AstNode::user() -> bool. True if processed
AstUser1InUse m_inuser1;
// STATE
AstNodeModule* m_modp; // Last module
AstBegin* m_beginp; // Last begin
AstNodeModule* m_modp; // Last module
AstBegin* m_beginp; // Last begin
unsigned m_modPastNum; // Module past numbering
V3Double0 m_statAsCover; // Statistic tracking
V3Double0 m_statAsPsl; // Statistic tracking
V3Double0 m_statAsFull; // Statistic tracking
V3Double0 m_statAsSV; // Statistic tracking
V3Double0 m_statAsCover; // Statistic tracking
V3Double0 m_statAsPsl; // Statistic tracking
V3Double0 m_statAsFull; // Statistic tracking
V3Double0 m_statAsSV; // Statistic tracking
// METHODS
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
+":"+cvtToStr(nodep->fileline()->lineno())
+": Assertion failed in %m"
+((message != "")?": ":"")+message
+"\n");
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
+":"+cvtToStr(nodep->fileline()->lineno())
+": Assertion failed in %m"
+((message != "")?": ":"")+message
+"\n");
}
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
nodep->displayType(AstDisplayType::DT_WRITE);
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
// cppcheck-suppress nullPointer
AstNode* timenewp = new AstTime(nodep->fileline());
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
timesp->unlinkFrBackWithNext();
timenewp->addNext(timesp);
}
nodep->fmtp()->addExprsp(timenewp);
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
}
nodep->displayType(AstDisplayType::DT_WRITE);
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
// cppcheck-suppress nullPointer
AstNode* timenewp = new AstTime(nodep->fileline());
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
timesp->unlinkFrBackWithNext();
timenewp->addNext(timesp);
}
nodep->fmtp()->addExprsp(timenewp);
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
AstNode* newIfAssertOn(AstNode* nodep) {
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
FileLine* fl = nodep->fileline();
AstNode* newp
AstNode* newp
= new AstIf(fl,
// If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
@@ -84,46 +84,47 @@ private:
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
nodep, NULL);
newp->user1(true); // Don't assert/cover this if
return newp;
newp->user1(true); // Don't assert/cover this if
return newp;
}
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
// Like newFireAssert() but omits the asserts-on check
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
AstNode* bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
AstDisplay* dispp = new AstDisplay(nodep->fileline(),
AstDisplayType::DT_ERROR, message, NULL, NULL);
AstNode* bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
bodysp->addNext(new AstStop(nodep->fileline()));
return bodysp;
}
AstNode* newFireAssert(AstNode* nodep, const string& message) {
AstNode* bodysp = newFireAssertUnchecked(nodep, message);
bodysp = newIfAssertOn(bodysp);
return bodysp;
bodysp = newIfAssertOn(bodysp);
return bodysp;
}
void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep,
AstNode* stmtsp, const string& message) {
propp->unlinkFrBack();
sentreep->unlinkFrBack();
if (stmtsp) stmtsp->unlinkFrBack();
//
AstNode* bodysp = NULL;
bool selfDestruct = false;
AstNode* stmtsp, const string& message) {
propp->unlinkFrBack();
sentreep->unlinkFrBack();
if (stmtsp) stmtsp->unlinkFrBack();
//
AstNode* bodysp = NULL;
bool selfDestruct = false;
AstIf* ifp = NULL;
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
++m_statAsCover;
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
covincp->unlinkFrBack();
if (message!="") covincp->declp()->comment(message);
bodysp = covincp;
}
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
covincp->unlinkFrBack();
if (message!="") covincp->declp()->comment(message);
bodysp = covincp;
}
if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
@@ -148,163 +149,167 @@ private:
// IEEE says simulator ignores these
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
} else {
nodep->v3fatalSrc("Unknown node type");
}
} else {
nodep->v3fatalSrc("Unknown node type");
}
AstNode* newp = new AstAlways(nodep->fileline(),
VAlwaysKwd::ALWAYS, sentreep, bodysp);
// Install it
if (selfDestruct) {
// Delete it after making the tree. This way we can tell the user
// if it wasn't constructed nicely or has other errors without needing --coverage.
newp->deleteTree();
nodep->unlinkFrBack();
} else {
nodep->replaceWith(newp);
}
// Bye
pushDeletep(nodep); VL_DANGLING(nodep);
// Install it
if (selfDestruct) {
// Delete it after making the tree. This way we can tell the user
// if it wasn't constructed nicely or has other errors without needing --coverage.
newp->deleteTree();
nodep->unlinkFrBack();
} else {
nodep->replaceWith(newp);
}
// Bye
pushDeletep(nodep); VL_DANGLING(nodep);
}
void newVAssertion(AstVAssert* nodep, AstNode* propp) {
propp->unlinkFrBackWithNext();
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
//
propp->unlinkFrBackWithNext();
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
//
if (VN_IS(nodep, VAssert)) {
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
} else {
nodep->v3fatalSrc("Unknown node type");
}
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
} else {
nodep->v3fatalSrc("Unknown node type");
}
AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
AstNode* newp = ifp;
AstNode* newp = ifp;
if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
//
// Install it
nodep->replaceWith(newp);
// Bye
pushDeletep(nodep); VL_DANGLING(nodep);
//
// Install it
nodep->replaceWith(newp);
// Bye
pushDeletep(nodep); VL_DANGLING(nodep);
}
// VISITORS
virtual void visit(AstIf* nodep) {
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
AstNodeIf* ifp = nodep;
AstNode* propp = NULL;
bool hasDefaultElse = false;
do {
// If this statement ends with 'else if', then nextIf will point to the
// nextIf statement. Otherwise it will be null.
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
AstNodeIf* ifp = nodep;
AstNode* propp = NULL;
bool hasDefaultElse = false;
do {
// If this statement ends with 'else if', then nextIf will point to the
// nextIf statement. Otherwise it will be null.
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
iterateAndNextNull(ifp->condp());
// Recurse into the true case.
// Recurse into the true case.
iterateAndNextNull(ifp->ifsp());
// If the last else is not an else if, recurse into that too.
if (ifp->elsesp() && !nextifp) {
// If the last else is not an else if, recurse into that too.
if (ifp->elsesp() && !nextifp) {
iterateAndNextNull(ifp->elsesp());
}
}
// Build a bitmask of the true predicates
AstNode* predp = ifp->condp()->cloneTree(false);
if (propp) {
propp = new AstConcat(nodep->fileline(), predp, propp);
} else {
propp = predp;
}
// Build a bitmask of the true predicates
AstNode* predp = ifp->condp()->cloneTree(false);
if (propp) {
propp = new AstConcat(nodep->fileline(), predp, propp);
} else {
propp = predp;
}
// Record if this ends with an 'else' that does not have an if
if (ifp->elsesp() && !nextifp) {
hasDefaultElse = true;
}
// Record if this ends with an 'else' that does not have an if
if (ifp->elsesp() && !nextifp) {
hasDefaultElse = true;
}
ifp = nextifp;
} while (ifp);
ifp = nextifp;
} while (ifp);
AstNode *newifp = nodep->cloneTree(false);
bool allow_none = nodep->unique0Pragma();
AstNode *newifp = nodep->cloneTree(false);
bool allow_none = nodep->unique0Pragma();
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
// Note: if this ends with an 'else', then we don't need to validate that one of the
// predicates evaluates to true.
AstNode* ohot = ((allow_none || hasDefaultElse)
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
// Note: if this ends with an 'else', then we don't need to validate that one of the
// predicates evaluates to true.
AstNode* ohot = ((allow_none || hasDefaultElse)
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* checkifp = new AstIf(nodep->fileline(),
new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep, "'unique if' statement violated"),
newifp);
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp);
pushDeletep(nodep);
} else {
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp);
pushDeletep(nodep);
} else {
iterateChildren(nodep);
}
}
}
//========== Case assertions
virtual void visit(AstCase* nodep) {
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default=false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) has_default=true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
// Simply need to add a default if there isn't one already
++m_statAsFull;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
newFireAssert(nodep, "synthesis full_case, but non-match found")));
}
}
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
// Need to check that one, and only one of the case items match at any moment
// If there's a default, we allow none to match, else exactly one must match
++m_statAsFull;
if (!has_default && !nodep->itemsp()) {
// Not parallel, but harmlessly so.
} else {
AstNode* propp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
AstNode* onep;
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
onep = AstEqWild::newTyped(itemp->fileline(),
nodep->exprp()->cloneTree(false),
icondp->cloneTree(false));
} else {
onep = AstEq::newTyped(icondp->fileline(),
nodep->exprp()->cloneTree(false),
icondp->cloneTree(false));
}
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
else propp = onep;
}
}
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
if (!nodep->user1SetOnce()) {
bool has_default = false;
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) has_default = true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
// Simply need to add a default if there isn't one already
++m_statAsFull;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
newFireAssert(nodep, "synthesis full_case, but non-match found")));
}
}
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
// Need to check that one, and only one of the case items match at any moment
// If there's a default, we allow none to match, else exactly one must match
++m_statAsFull;
if (!has_default && !nodep->itemsp()) {
// Not parallel, but harmlessly so.
} else {
AstNode* propp = NULL;
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp();
icondp!=NULL; icondp=icondp->nextp()) {
AstNode* onep;
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
onep = AstEqWild::newTyped(itemp->fileline(),
nodep->exprp()->cloneTree(false),
icondp->cloneTree(false));
} else {
onep = AstEq::newTyped(icondp->fileline(),
nodep->exprp()->cloneTree(false),
icondp->cloneTree(false));
}
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
else propp = onep;
}
}
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
bool allow_none = has_default || nodep->unique0Pragma();
AstNode* ohot = (allow_none
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
bool allow_none = has_default || nodep->unique0Pragma();
AstNode* ohot
= (allow_none
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* ifp = new AstIf(nodep->fileline(),
new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
NULL);
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp);
}
}
}
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp);
}
}
}
}
//========== Past
@@ -312,10 +317,11 @@ private:
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
"Expected constant ticks, checked in V3Width");
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
}
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
UASSERT_OBJ(ticks>=1, nodep, "0 tick should have been checked in V3Width");
AstNode* inp = nodep->exprp()->unlinkFrBack();
AstVar* invarp = NULL;
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
@@ -341,46 +347,46 @@ private:
//========== Statements
virtual void visit(AstDisplay* nodep) {
iterateChildren(nodep);
// Replace the special types with standard text
if (nodep->displayType()==AstDisplayType::DT_INFO) {
replaceDisplay(nodep, "-Info");
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
replaceDisplay(nodep, "%%Warning");
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
replaceDisplay(nodep, "%%Error");
}
// Replace the special types with standard text
if (nodep->displayType()==AstDisplayType::DT_INFO) {
replaceDisplay(nodep, "-Info");
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
replaceDisplay(nodep, "%%Warning");
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
replaceDisplay(nodep, "%%Error");
}
}
virtual void visit(AstNodePslCoverOrAssert* nodep) {
iterateChildren(nodep);
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
}
virtual void visit(AstVAssert* nodep) {
iterateChildren(nodep);
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
++m_statAsSV;
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
++m_statAsSV;
}
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_modp = nodep;
m_modPastNum = 0;
//
//
iterateChildren(nodep);
// Reset defaults
m_modp = NULL;
// Reset defaults
m_modp = NULL;
}
virtual void visit(AstBegin* nodep) {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
AstBegin* lastp = m_beginp;
{
m_beginp = nodep;
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
AstBegin* lastp = m_beginp;
{
m_beginp = nodep;
iterateChildren(nodep);
}
m_beginp = lastp;
}
m_beginp = lastp;
}
virtual void visit(AstNode* nodep) {
@@ -389,17 +395,17 @@ private:
public:
// CONSTRUCTORS
explicit AssertVisitor(AstNetlist* nodep) {
m_beginp = NULL;
m_modp = NULL;
m_beginp = NULL;
m_modp = NULL;
m_modPastNum = 0;
// Process
// Process
iterate(nodep);
}
virtual ~AssertVisitor() {
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
}
};
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void assertAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+46 -46
View File
@@ -18,7 +18,7 @@
//
//*************************************************************************
// Pre steps:
// Attach clocks to each assertion
// Attach clocks to each assertion
//*************************************************************************
#include "config_build.h"
@@ -51,37 +51,37 @@ private:
VL_DEBUG_FUNC; // Declare debug()
AstSenTree* newSenTree(AstNode* nodep) {
// Create sentree based on clocked or default clock
// Return NULL for always
AstSenTree* newp = NULL;
// Create sentree based on clocked or default clock
// Return NULL for always
AstSenTree* newp = NULL;
AstNodeSenItem* senip = m_senip;
if (!senip) senip = m_seniDefaultp;
if (!senip) senip = m_seniAlwaysp;
if (!senip) {
nodep->v3error("Unsupported: Unclocked assertion");
newp = new AstSenTree(nodep->fileline(), NULL);
} else {
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
}
return newp;
if (!senip) {
nodep->v3error("Unsupported: Unclocked assertion");
newp = new AstSenTree(nodep->fileline(), NULL);
} else {
newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
}
return newp;
}
void clearAssertInfo() {
m_senip = NULL;
m_senip = NULL;
}
// VISITORS
//========== Statements
virtual void visit(AstClocking* nodep) {
UINFO(8," CLOCKING"<<nodep<<endl);
// Store the new default clock, reset on new module
m_seniDefaultp = nodep->sensesp();
// Trash it, keeping children
if (nodep->bodysp()) {
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
} else {
nodep->unlinkFrBack();
}
pushDeletep(nodep); VL_DANGLING(nodep);
UINFO(8," CLOCKING"<<nodep<<endl);
// Store the new default clock, reset on new module
m_seniDefaultp = nodep->sensesp();
// Trash it, keeping children
if (nodep->bodysp()) {
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
} else {
nodep->unlinkFrBack();
}
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstAlways* nodep) {
iterateAndNextNull(nodep->sensesp());
@@ -93,12 +93,12 @@ private:
}
virtual void visit(AstNodePslCoverOrAssert* nodep) {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
// Find PslClocking's burried under nodep->exprsp
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
}
virtual void visit(AstPast* nodep) {
if (nodep->sentreep()) return; // Already processed
@@ -108,26 +108,26 @@ private:
virtual void visit(AstPslClocked* nodep) {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
}
// Block is the new expression to evaluate
AstNode* blockp = nodep->propp()->unlinkFrBack();
if (nodep->disablep()) {
blockp = new AstAnd(nodep->disablep()->fileline(),
new AstNot(nodep->disablep()->fileline(),
nodep->disablep()->unlinkFrBack()),
blockp);
}
// Unlink and just keep a pointer to it, convert to sentree as needed
m_senip = nodep->sensesp();
nodep->replaceWith(blockp);
pushDeletep(nodep); VL_DANGLING(nodep);
if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
}
// Block is the new expression to evaluate
AstNode* blockp = nodep->propp()->unlinkFrBack();
if (nodep->disablep()) {
blockp = new AstAnd(nodep->disablep()->fileline(),
new AstNot(nodep->disablep()->fileline(),
nodep->disablep()->unlinkFrBack()),
blockp);
}
// Unlink and just keep a pointer to it, convert to sentree as needed
m_senip = nodep->sensesp();
nodep->replaceWith(blockp);
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstNodeModule* nodep) {
iterateChildren(nodep);
// Reset defaults
m_seniDefaultp = NULL;
// Reset defaults
m_seniDefaultp = NULL;
}
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
@@ -136,10 +136,10 @@ private:
public:
// CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep) {
m_seniDefaultp = NULL;
m_seniDefaultp = NULL;
m_seniAlwaysp = NULL;
clearAssertInfo();
// Process
clearAssertInfo();
// Process
iterate(nodep);
}
virtual ~AssertPreVisitor() {}
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void assertPreAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+454 -408
View File
File diff suppressed because it is too large Load Diff
+968 -927
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -30,4 +30,4 @@
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
#endif // Guard
#endif // Guard
+208 -235
View File
@@ -57,7 +57,7 @@ void AstNodeVarRef::cloneRelink() {
}
int AstNodeSel::bitConst() const {
AstConst* constp=VN_CAST(bitp(), Const);
AstConst* constp = VN_CAST(bitp(), Const);
return (constp ? constp->toSInt() : 0);
}
@@ -65,25 +65,30 @@ void AstNodeClassDType::repairMemberCache() {
clearCache();
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
if (m_members.find(itemp->name())!=m_members.end()) {
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
else m_members.insert(make_pair(itemp->name(), itemp));
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyNameQ()); }
else m_members.insert(make_pair(itemp->name(), itemp));
}
}
const char* AstNodeClassDType::broken() const {
vl_unordered_set<AstMemberDType*> exists;
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
exists.insert(itemp);
exists.insert(itemp);
}
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
this->v3error("Internal: Structure member broken: "<<it->first);
return "member broken";
}
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
this->v3error("Internal: Structure member broken: "<<it->first);
return "member broken";
}
}
return NULL;
}
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs,
const V3Number& rhs, const V3Number& ths) {
if (lhs.isNeqZero()) out.opAssign(rhs); else out.opAssign(ths);
}
int AstBasicDType::widthAlignBytes() const {
if (width()<=8) return 1;
else if (width()<=16) return 2;
@@ -116,37 +121,37 @@ int AstNodeClassDType::widthAlignBytes() const {
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstEqD(fl, lhsp, rhsp);
return new AstEqD(fl, lhsp, rhsp);
} else {
return new AstEq(fl, lhsp, rhsp);
return new AstEq(fl, lhsp, rhsp);
}
}
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstGteD(fl, lhsp, rhsp);
return new AstGteD(fl, lhsp, rhsp);
} else if (lhsp->isSigned() && rhsp->isSigned()) {
return new AstGteS(fl, lhsp, rhsp);
return new AstGteS(fl, lhsp, rhsp);
} else {
return new AstGte(fl, lhsp, rhsp);
return new AstGte(fl, lhsp, rhsp);
}
}
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstLteD(fl, lhsp, rhsp);
return new AstLteD(fl, lhsp, rhsp);
} else if (lhsp->isSigned() && rhsp->isSigned()) {
return new AstLteS(fl, lhsp, rhsp);
return new AstLteS(fl, lhsp, rhsp);
} else {
return new AstLte(fl, lhsp, rhsp);
return new AstLte(fl, lhsp, rhsp);
}
}
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstEqD(fl, lhsp, rhsp);
return new AstEqD(fl, lhsp, rhsp);
} else {
return new AstEqWild(fl, lhsp, rhsp);
return new AstEqWild(fl, lhsp, rhsp);
}
}
@@ -168,10 +173,12 @@ bool AstVar::isScBv() const {
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
}
bool AstVar::isScUint() const {
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
return ((isSc() && v3Global.opt.pinsScUint()
&& width() >= 2 && width() <= 64) && !isScBv());
}
bool AstVar::isScBigUint() const {
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
return ((isSc() && v3Global.opt.pinsScBigUint()
&& width() >= 65 && width() <= 512) && !isScBv());
}
void AstVar::combineType(AstVarType type) {
@@ -208,8 +215,8 @@ string AstVar::verilogKwd() const {
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
if (forReturn) named=false;
if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string otype;
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
@@ -279,21 +286,21 @@ string AstVar::vlEnumType() const {
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
return "VLVT_PTR";
return "VLVT_PTR";
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
return "VLVT_PTR";
return "VLVT_PTR";
} else if (strtype) {
arg += "VLVT_STRING";
arg += "VLVT_STRING";
} else if (widthMin() <= 8) {
arg += "VLVT_UINT8";
arg += "VLVT_UINT8";
} else if (widthMin() <= 16) {
arg += "VLVT_UINT16";
arg += "VLVT_UINT16";
} else if (widthMin() <= VL_WORDSIZE) {
arg += "VLVT_UINT32";
arg += "VLVT_UINT32";
} else if (isQuad()) {
arg += "VLVT_UINT64";
arg += "VLVT_UINT64";
} else if (isWide()) {
arg += "VLVT_WDATA";
arg += "VLVT_WDATA";
}
// else return "VLVT_UNKNOWN"
return arg;
@@ -339,28 +346,28 @@ string AstVar::vlPropInit() const {
out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right());
dtp = adtypep->subDTypep();
}
else break; // AstBasicDType - nothing below
else break; // AstBasicDType - nothing below
}
return out;
}
string AstVar::cPubArgType(bool named, bool forReturn) const {
if (forReturn) named=false;
if (forReturn) named = false;
string arg;
if (isWide() && isReadOnly()) arg += "const ";
if (widthMin() == 1) {
arg += "bool";
arg += "bool";
} else if (widthMin() <= VL_WORDSIZE) {
arg += "uint32_t";
arg += "uint32_t";
} else if (isWide()) {
arg += "uint32_t"; // []'s added later
arg += "uint32_t"; // []'s added later
} else {
arg += "vluint64_t";
arg += "vluint64_t";
}
if (isWide()) {
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
arg += " (& "+name();
arg += ")["+cvtToStr(widthWords())+"]";
if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead");
arg += " (& "+name();
arg += ")["+cvtToStr(widthWords())+"]";
} else {
if (!forReturn && (isWritable()
|| direction().isRefOrConstRef())) arg += "&";
@@ -370,25 +377,25 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
}
string AstVar::dpiArgType(bool named, bool forReturn) const {
if (forReturn) named=false;
if (forReturn) named = false;
string arg;
if (isDpiOpenArray()) {
arg = "const svOpenArrayHandle";
} else if (!basicp()) {
arg = "UNKNOWN";
} else if (basicp()->keyword().isDpiBitVal()) {
if (widthMin() == 1) {
arg = "unsigned char";
if (widthMin() == 1) {
arg = "unsigned char";
if (!forReturn && isWritable()) arg += "*";
} else {
if (forReturn) {
arg = "svBitVecVal";
} else if (isReadOnly()) {
arg = "const svBitVecVal*";
} else {
arg = "svBitVecVal*";
}
}
arg = "const svBitVecVal*";
} else {
arg = "svBitVecVal*";
}
}
} else if (basicp()->keyword().isDpiLogicVal()) {
if (widthMin() == 1) {
arg = "unsigned char";
@@ -403,9 +410,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
}
}
} else {
arg = basicp()->keyword().dpiType();
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
arg = "unsigned "+arg;
arg = basicp()->keyword().dpiType();
if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) {
arg = "unsigned "+arg;
}
if (!forReturn && isWritable()) arg += "*";
}
@@ -415,23 +422,23 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
string AstVar::scType() const {
if (isScBigUint()) {
return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
} else if (isScUint()) {
return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
} else if (isScBv()) {
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
} else if (widthMin() == 1) {
return "bool";
return "bool";
} else if (widthMin() <= VL_WORDSIZE) {
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
return "uint8_t";
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
return "uint16_t";
} else {
return "uint32_t";
}
if (widthMin() <= 8 && v3Global.opt.pinsUint8()) {
return "uint8_t";
} else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) {
return "uint16_t";
} else {
return "uint32_t";
}
} else {
return "vluint64_t";
return "vluint64_t";
}
}
@@ -440,18 +447,18 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
// Historically sc variables are identified by a variable
// attribute. TODO it would better be a data type attribute.
if (AstVar* anodep = VN_CAST(nodep, Var)) {
if (anodep->isSc()) return anodep;
else return NULL;
if (anodep->isSc()) return anodep;
else return NULL;
}
else if (VN_IS(nodep, VarRef)) {
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
else return NULL;
else return NULL;
}
else if (VN_IS(nodep, ArraySel)) {
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
}
return NULL;
}
@@ -473,58 +480,58 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
// ref order: a[1][2][3][4]
// Created as: reg [4] a [1][2][3];
// *or* reg a [1][2][3][4];
// // The bit select is optional; used only if "leftover" []'s
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
// SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0
// // The bit select is optional; used only if "leftover" []'s
// SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a))))
// DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3))))
// *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT))))
// SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0
// TODO this function should be removed in favor of recursing the dtype(),
// as that allows for more complicated data types.
int dim = 0;
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if ((dim++)==dimension) {
return dtypep;
}
dtypep = adtypep->subDTypep();
continue;
}
if ((dim++)==dimension) {
return dtypep;
}
dtypep = adtypep->subDTypep();
continue;
}
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
// AstBasicDType - nothing below, return null
if (adtypep->isRanged()) {
if ((dim++) == dimension) {
return adtypep;
}
}
return NULL;
}
// AstBasicDType - nothing below, return null
if (adtypep->isRanged()) {
if ((dim++) == dimension) {
return adtypep;
}
}
return NULL;
}
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
if (adtypep->packed()) {
if ((dim++) == dimension) {
return adtypep;
}
}
return NULL;
}
// Node no ->next in loop; use continue where necessary
break;
if (adtypep->packed()) {
if ((dim++) == dimension) {
return adtypep;
}
}
return NULL;
}
// Node no ->next in loop; use continue where necessary
break;
}
return NULL;
}
uint32_t AstNodeDType::arrayUnpackedElements() {
uint32_t entries=1;
uint32_t entries = 1;
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
entries *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
}
else {
// AstBasicDType - nothing below, 1
break;
}
entries *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
}
else {
// AstBasicDType - nothing below, 1
break;
}
}
return entries;
}
@@ -534,17 +541,17 @@ std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
uint32_t packed = 0;
uint32_t unpacked = 0;
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if (VN_IS(adtypep, PackArrayDType)) packed++;
else unpacked++;
dtypep = adtypep->subDTypep();
continue;
}
else unpacked++;
dtypep = adtypep->subDTypep();
continue;
}
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
if (includeBasic && adtypep->isRanged()) packed++;
}
break;
if (includeBasic && adtypep->isRanged()) packed++;
}
break;
}
return make_pair(packed, unpacked);
}
@@ -553,7 +560,7 @@ int AstNodeDType::widthPow2() const {
// I.e. width 30 returns 32, width 32 returns 32.
uint32_t width = this->width();
for (int p2=30; p2>=0; p2--) {
if (width > (1UL<<p2)) return (1UL<<(p2+1));
if (width > (1UL<<p2)) return (1UL<<(p2+1));
}
return 1;
}
@@ -572,9 +579,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variabl
} else {
nodep = VN_CAST(nodep, NodePreSel)->lhsp();
}
continue;
}
else break;
continue;
}
else break;
}
return nodep;
}
@@ -599,7 +606,7 @@ string AstScope::nameDotless() const {
string out = shortName();
string::size_type pos;
while ((pos = out.find('.')) != string::npos) {
out.replace(pos, 1, "__");
out.replace(pos, 1, "__");
}
return out;
}
@@ -607,58 +614,58 @@ string AstScope::nameDotless() const {
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
out += textp->text();
out += textp->text();
}
// TOP will be replaced by top->name()
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,"");
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, "");
return AstNode::prettyName(out);
}
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
out += textp->text();
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,"");
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, "__");
out.replace(pos, 1, "__");
}
while ((pos=out.find("__DOT__")) != string::npos) {
out.replace(pos, 7, "__");
while ((pos = out.find("__DOT__")) != string::npos) {
out.replace(pos, 7, "__");
}
return out;
}
bool AstSenTree::hasClocked() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isClocked()) return true;
if (senp->isClocked()) return true;
}
return false;
}
bool AstSenTree::hasSettle() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isSettle()) return true;
if (senp->isSettle()) return true;
}
return false;
}
bool AstSenTree::hasInitial() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isInitial()) return true;
if (senp->isInitial()) return true;
}
return false;
}
bool AstSenTree::hasCombo() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isCombo()) return true;
if (senp->isCombo()) return true;
}
return false;
}
@@ -667,20 +674,14 @@ void AstTypeTable::clearCache() {
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
// Just clear out the maps; the search functions will be used to rebuild the map
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
m_basicps[i] = NULL;
}
for (int isbit=0; isbit<_IDX0_MAX; ++isbit) {
for (int numer=0; numer<AstNumeric::_ENUM_MAX; ++numer) {
LogicMap& mapr = m_logicMap[isbit][numer];
mapr.clear();
}
m_basicps[i] = NULL;
}
m_detailedMap.clear();
// Clear generic()'s so dead detection will work
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
bdtypep->generic(false);
}
bdtypep->generic(false);
}
}
}
@@ -689,8 +690,8 @@ void AstTypeTable::repairCache() {
clearCache();
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
(void)findInsertSameDType(bdtypep);
}
(void)findInsertSameDType(bdtypep);
}
}
}
@@ -710,30 +711,16 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
}
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
int width, int widthMin, AstNumeric numeric) {
int idx = IDX0_LOGIC;
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
std::pair<int,int> widths = make_pair(width,widthMin);
LogicMap& mapr = m_logicMap[idx][static_cast<int>(numeric)];
LogicMap::const_iterator it = mapr.find(widths);
if (it != mapr.end()) return it->second;
//
int width, int widthMin, AstNumeric numeric) {
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
// Because the detailed map doesn't update this map,
// check the detailed map for this same node, and if found update this map
// Also adds this new node to the detailed map
AstBasicDType* newp = findInsertSameDType(new1p);
if (newp != new1p) new1p->deleteTree();
else addTypesp(newp);
//
mapr.insert(make_pair(widths,newp));
return newp;
}
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
VNumRange range, int widthMin, AstNumeric numeric) {
VNumRange range, int widthMin, AstNumeric numeric) {
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
AstBasicDType* newp = findInsertSameDType(new1p);
if (newp != new1p) new1p->deleteTree();
@@ -743,11 +730,11 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(),
nodep->keyword(), nodep->nrange());
nodep->keyword(), nodep->nrange());
DetailedMap& mapr = m_detailedMap;
DetailedMap::const_iterator it = mapr.find(key);
if (it != mapr.end()) return it->second;
mapr.insert(make_pair(key,nodep));
mapr.insert(make_pair(key, nodep));
nodep->generic(true);
// No addTypesp; the upper function that called new() is responsible for adding
return nodep;
@@ -757,12 +744,12 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
// Special walking tree inserters
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
if (!backp()) newp->v3fatalSrc("Can't find current statement to addBeforeStmt");
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
// Look up; virtual call will find where to put it
this->backp()->addBeforeStmt(newp, this);
}
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
if (!backp()) newp->v3fatalSrc("Can't find current statement to addNextStmt");
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
// Look up; virtual call will find where to put it
this->backp()->addNextStmt(newp, this);
}
@@ -779,39 +766,41 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
// Preconditions insert first just before themselves (the normal rule for other statement types)
// Preconditions insert first just before themselves (the normal rule
// for other statement types)
if (belowp == precondsp()) {
// Must have been first statement in precondsp list, so newp is new first statement
belowp->addHereThisAsNext(newp);
// Must have been first statement in precondsp list, so newp is new first statement
belowp->addHereThisAsNext(newp);
} else if (belowp == condp()) {
// Goes before condition, IE in preconditions
addPrecondsp(newp);
// Goes before condition, IE in preconditions
addPrecondsp(newp);
} else if (belowp == bodysp()) {
// Was first statement in body, so new front
belowp->addHereThisAsNext(newp);
// Was first statement in body, so new front
belowp->addHereThisAsNext(newp);
} else {
belowp->v3fatalSrc("Doesn't look like this was really under the while");
belowp->v3fatalSrc("Doesn't look like this was really under the while");
}
}
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
// Preconditions insert first just before themselves (the normal rule for other statement types)
// Preconditions insert first just before themselves (the normal rule
// for other statement types)
if (belowp == precondsp()) {
// Next in precond list
belowp->addNextHere(newp);
// Next in precond list
belowp->addNextHere(newp);
} else if (belowp == condp()) {
// Becomes first statement in body, body may have been empty
if (bodysp()) {
bodysp()->addHereThisAsNext(newp);
} else {
addBodysp(newp);
}
// Becomes first statement in body, body may have been empty
if (bodysp()) {
bodysp()->addHereThisAsNext(newp);
} else {
addBodysp(newp);
}
} else if (belowp == bodysp()) {
// Next statement in body
belowp->addNextHere(newp);
// Next statement in body
belowp->addNextHere(newp);
} else {
belowp->v3fatalSrc("Doesn't look like this was really under the while");
belowp->v3fatalSrc("Doesn't look like this was really under the while");
}
}
@@ -823,7 +812,7 @@ void AstNode::dump(std::ostream& str) {
//<<" "<<cvtToHex(this)->m_backp
<<" <e"<<std::dec<<editCount()
<<((editCount()>=editCountLast())?"#>":">")
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lastLineno()<<"}";
if (user1p()) str<<" u1="<<cvtToHex(user1p());
if (user2p()) str<<" u2="<<cvtToHex(user2p());
if (user3p()) str<<" u3="<<cvtToHex(user3p());
@@ -831,17 +820,17 @@ void AstNode::dump(std::ostream& str) {
if (user5p()) str<<" u5="<<cvtToHex(user5p());
if (hasDType()) {
// Final @ so less likely to by accident read it as a nodep
if (dtypep()==this) str<<" @dt="<<"this@";
if (dtypep()==this) str<<" @dt="<<"this@";
else str<<" @dt="<<cvtToHex(dtypep())<<"@";
if (AstNodeDType* dtp = dtypep()) {
dtp->dumpSmall(str);
}
} else { // V3Broken will throw an error
if (AstNodeDType* dtp = dtypep()) {
dtp->dumpSmall(str);
}
} else { // V3Broken will throw an error
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<cvtToHex(dtypep());
}
if (name()!="") {
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
else str<<" "<<V3Number::quoteNameControls(name());
else str<<" "<<V3OutFormatter::quoteNameControls(name());
}
}
@@ -951,7 +940,7 @@ void AstNodeDType::dump(std::ostream& str) {
if (generic()) str<<" [GENERIC]";
if (AstNodeDType* dtp = virtRefDTypep()) {
str<<" refdt="<<cvtToHex(dtp);
dtp->dumpSmall(str);
dtp->dumpSmall(str);
}
}
void AstNodeDType::dumpSmall(std::ostream& str) {
@@ -962,7 +951,7 @@ void AstNodeDType::dumpSmall(std::ostream& str) {
<<(isDouble()?"d":"")
<<(isString()?"str":"");
if (!isDouble() && !isString()) {
str<<"w"<<(widthSized()?"":"u")<<width();
str<<"w"<<(widthSized()?"":"u")<<width();
}
if (!widthSized()) str<<"/"<<widthMin();
str<<")";
@@ -996,8 +985,8 @@ void AstPackageImport::dump(std::ostream& str) {
void AstSel::dump(std::ostream& str) {
this->AstNode::dump(str);
if (declRange().ranged()) {
str<<" decl"<<declRange()<<"]";
if (declElWidth()!=1) str<<"/"<<declElWidth();
str<<" decl"<<declRange()<<"]";
if (declElWidth()!=1) str<<"/"<<declElWidth();
}
}
void AstSliceSel::dump(std::ostream& str) {
@@ -1014,37 +1003,21 @@ void AstMTaskBody::dump(std::ostream& str) {
void AstTypeTable::dump(std::ostream& str) {
this->AstNode::dump(str);
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
if (AstBasicDType* subnodep=m_basicps[i]) {
str<<endl; // Newline from caller, so newline first
if (AstBasicDType* subnodep = m_basicps[i]) {
str<<endl; // Newline from caller, so newline first
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
str<<" -> ";
subnodep->dump(str);
}
}
for (int isbit=0; isbit<2; ++isbit) {
for (int issigned=0; issigned<AstNumeric::_ENUM_MAX; ++issigned) {
LogicMap& mapr = m_logicMap[isbit][issigned];
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
AstBasicDType* dtypep = it->second;
str<<endl; // Newline from caller, so newline first
std::stringstream nsstr;
nsstr<<(isbit?"bw":"lw")
<<it->first.first<<"/"<<it->first.second;
str<<"\t\t"<<std::setw(8)<<nsstr.str();
if (issigned) str<<" s"; else str<<" u";
str<<" -> ";
dtypep->dump(str);
}
}
str<<" -> ";
subnodep->dump(str);
}
}
{
DetailedMap& mapr = m_detailedMap;
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
AstBasicDType* dtypep = it->second;
str<<endl; // Newline from caller, so newline first
str<<"\t\tdetailed -> ";
dtypep->dump(str);
}
DetailedMap& mapr = m_detailedMap;
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
AstBasicDType* dtypep = it->second;
str<<endl; // Newline from caller, so newline first
str<<"\t\tdetailed -> ";
dtypep->dump(str);
}
}
// Note get newline from caller too.
}
@@ -1153,8 +1126,8 @@ void AstBegin::dump(std::ostream& str) {
void AstCoverDecl::dump(std::ostream& str) {
this->AstNode::dump(str);
if (this->dataDeclNullp()) {
str<<" -> ";
this->dataDeclNullp()->dump(str);
str<<" -> ";
this->dataDeclNullp()->dump(str);
} else {
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
}
@@ -1176,8 +1149,8 @@ void AstNodeText::dump(std::ostream& str) {
string out = text();
string::size_type pos;
if ((pos = out.find('\n')) != string::npos) {
out.erase(pos,out.length()-pos);
out += "...";
out.erase(pos, out.length()-pos);
out += "...";
}
str<<" \""<<out<<"\"";
}
@@ -1190,8 +1163,8 @@ void AstCFile::dump(std::ostream& str) {
void AstCCall::dump(std::ostream& str) {
this->AstNode::dump(str);
if (funcp()) {
str<<" "<<funcp()->name()<<" => ";
funcp()->dump(str);
str<<" "<<funcp()->name()<<" => ";
funcp()->dump(str);
}
}
void AstCFunc::dump(std::ostream& str) {
+2134 -1897
View File
File diff suppressed because it is too large Load Diff
+151 -149
View File
@@ -20,9 +20,9 @@
// V3Begin's Transformations:
//
// Each module:
// Look for BEGINs
// BEGIN(VAR...) -> VAR ... {renamed}
// FOR -> WHILEs
// Look for BEGINs
// BEGIN(VAR...) -> VAR ... {renamed}
// FOR -> WHILEs
//
// There are two scopes; named BEGINs change %m and variable scopes.
// Unnamed BEGINs change only variable, not $display("%m") scope.
@@ -47,17 +47,17 @@ class BeginState {
private:
// NODE STATE
//Entire netlist:
// AstNodeFTask::user1 -> bool, 1=processed
AstUser1InUse m_inuser1;
bool m_anyFuncInBegin;
// AstNodeFTask::user1 -> bool, 1=processed
AstUser1InUse m_inuser1;
bool m_anyFuncInBegin;
public:
BeginState() {
m_anyFuncInBegin = false;
m_anyFuncInBegin = false;
}
~BeginState() {}
void userMarkChanged(AstNode* nodep) {
nodep->user1(true);
m_anyFuncInBegin = true;
nodep->user1(true);
m_anyFuncInBegin = true;
}
bool anyFuncInBegin() const { return m_anyFuncInBegin; }
};
@@ -67,159 +67,161 @@ public:
class BeginVisitor : public AstNVisitor {
private:
// STATE
BeginState* m_statep; // Current global state
AstNodeModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Current function/task
string m_namedScope; // Name of begin blocks above us
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_repeatNum; // Repeat counter
int m_ifDepth; // Current if depth
BeginState* m_statep; // Current global state
AstNodeModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Current function/task
string m_namedScope; // Name of begin blocks above us
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_repeatNum; // Repeat counter
int m_ifDepth; // Current if depth
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_repeatNum = 0;
m_modp = nodep;
m_repeatNum = 0;
iterateChildren(nodep);
m_modp = NULL;
m_modp = NULL;
}
virtual void visit(AstNodeFTask* nodep) {
UINFO(8," "<<nodep<<endl);
// Rename it
if (m_unnamedScope != "") {
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
UINFO(8," rename to "<<nodep->name()<<endl);
m_statep->userMarkChanged(nodep);
}
// BEGIN wrapping a function rename that function, but don't affect the inside function's variables
// We then restart with empty naming; so that any begin's inside the function will rename inside the function
// Process children
string oldScope = m_namedScope;
string oldUnnamed = m_unnamedScope;
{
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
UINFO(8," "<<nodep<<endl);
// Rename it
if (m_unnamedScope != "") {
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
UINFO(8," rename to "<<nodep->name()<<endl);
m_statep->userMarkChanged(nodep);
}
// BEGIN wrapping a function rename that function, but don't affect
// the inside function's variables. We then restart with empty
// naming; so that any begin's inside the function will rename
// inside the function.
// Process children
string oldScope = m_namedScope;
string oldUnnamed = m_unnamedScope;
{
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
iterateChildren(nodep);
m_ftaskp = NULL;
}
m_namedScope = oldScope;
m_unnamedScope = oldUnnamed;
m_ftaskp = NULL;
}
m_namedScope = oldScope;
m_unnamedScope = oldUnnamed;
}
virtual void visit(AstBegin* nodep) {
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8," "<<nodep<<endl);
string oldScope = m_namedScope;
string oldUnnamed = m_unnamedScope;
{
UINFO(8,"nname "<<m_namedScope<<endl);
if (nodep->name() != "") { // Else unneeded unnamed block
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos=dottedname.find("__DOT__")) != string::npos) {
string ident = dottedname.substr(0,pos);
dottedname = dottedname.substr(pos+strlen("__DOT__"));
if (!nodep->unnamed()) {
if (m_namedScope=="") m_namedScope = ident;
else m_namedScope = m_namedScope + "__DOT__"+ident;
}
if (m_unnamedScope=="") m_unnamedScope = ident;
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
m_unnamedScope, "__BEGIN__");
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
}
}
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8," "<<nodep<<endl);
string oldScope = m_namedScope;
string oldUnnamed = m_unnamedScope;
{
UINFO(8,"nname "<<m_namedScope<<endl);
if (nodep->name() != "") { // Else unneeded unnamed block
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos=dottedname.find("__DOT__")) != string::npos) {
string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos+strlen("__DOT__"));
if (!nodep->unnamed()) {
if (m_namedScope=="") m_namedScope = ident;
else m_namedScope = m_namedScope + "__DOT__"+ident;
}
if (m_unnamedScope=="") m_unnamedScope = ident;
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
m_unnamedScope, "__BEGIN__");
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
}
}
// Remap var names and replace lower Begins
// Remap var names and replace lower Begins
iterateAndNextNull(nodep->stmtsp());
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
}
m_namedScope = oldScope;
m_unnamedScope = oldUnnamed;
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
}
m_namedScope = oldScope;
m_unnamedScope = oldUnnamed;
// Cleanup
AstNode* addsp = NULL;
if (AstNode* stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
}
if (addsp) {
nodep->replaceWith(addsp);
} else {
nodep->unlinkFrBack();
}
pushDeletep(nodep); VL_DANGLING(nodep);
// Cleanup
AstNode* addsp = NULL;
if (AstNode* stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
}
if (addsp) {
nodep->replaceWith(addsp);
} else {
nodep->unlinkFrBack();
}
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstVar* nodep) {
if (m_unnamedScope != "") {
// Rename it
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
else m_modp->addStmtp(nodep);
}
if (m_unnamedScope != "") {
// Rename it
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
else m_modp->addStmtp(nodep);
}
}
virtual void visit(AstCell* nodep) {
UINFO(8," CELL "<<nodep<<endl);
if (m_namedScope != "") {
m_statep->userMarkChanged(nodep);
// Rename it
nodep->name(m_namedScope+"__DOT__"+nodep->name());
UINFO(8," rename to "<<nodep->name()<<endl);
// Move to module
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
}
UINFO(8," CELL "<<nodep<<endl);
if (m_namedScope != "") {
m_statep->userMarkChanged(nodep);
// Rename it
nodep->name(m_namedScope+"__DOT__"+nodep->name());
UINFO(8," rename to "<<nodep->name()<<endl);
// Move to module
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
}
iterateChildren(nodep);
}
virtual void visit(AstVarXRef* nodep) {
UINFO(9, " VARXREF "<<nodep<<endl);
if (m_namedScope != "" && nodep->inlinedDots() == "") {
nodep->inlinedDots(m_namedScope);
UINFO(9, " rescope to "<<nodep<<endl);
}
UINFO(9, " VARXREF "<<nodep<<endl);
if (m_namedScope != "" && nodep->inlinedDots() == "") {
nodep->inlinedDots(m_namedScope);
UINFO(9, " rescope to "<<nodep<<endl);
}
}
virtual void visit(AstScopeName* nodep) {
// If there's a %m in the display text, we add a special node that will contain the name()
// Similar code in V3Inline
if (nodep->user1SetOnce()) return; // Don't double-add text's
if (m_namedScope != "") {
// To keep correct visual order, must add before other Text's
AstNode* afterp = nodep->scopeAttrp();
if (afterp) afterp->unlinkFrBackWithNext();
// If there's a %m in the display text, we add a special node that will contain the name()
// Similar code in V3Inline
if (nodep->user1SetOnce()) return; // Don't double-add text's
if (m_namedScope != "") {
// To keep correct visual order, must add before other Text's
AstNode* afterp = nodep->scopeAttrp();
if (afterp) afterp->unlinkFrBackWithNext();
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
if (afterp) nodep->scopeAttrp(afterp);
}
if (afterp) nodep->scopeAttrp(afterp);
}
iterateChildren(nodep);
}
virtual void visit(AstCoverDecl* nodep) {
// Don't need to fix path in coverage statements, they're not under
// any BEGINs, but V3Coverage adds them all under the module itself.
// Don't need to fix path in coverage statements, they're not under
// any BEGINs, but V3Coverage adds them all under the module itself.
iterateChildren(nodep);
}
// VISITORS - LINT CHECK
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
// Check IFDEPTH warning - could be in other transform files if desire
int prevIfDepth = m_ifDepth;
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
m_ifDepth = -1;
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
m_ifDepth = -1;
}
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
// Check IFDEPTH warning - could be in other transform files if desire
int prevIfDepth = m_ifDepth;
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
m_ifDepth = -1;
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
m_ifDepth = -1;
}
iterateChildren(nodep);
m_ifDepth = prevIfDepth;
m_ifDepth = prevIfDepth;
}
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
@@ -227,11 +229,11 @@ private:
public:
// CONSTUCTORS
BeginVisitor(AstNetlist* nodep, BeginState* statep) {
m_statep = statep;
m_modp = NULL;
m_ftaskp = NULL;
m_repeatNum = 0;
m_ifDepth = 0;
m_statep = statep;
m_modp = NULL;
m_ftaskp = NULL;
m_repeatNum = 0;
m_ifDepth = 0;
iterate(nodep);
}
virtual ~BeginVisitor() {}
@@ -244,29 +246,29 @@ class BeginRelinkVisitor : public AstNVisitor {
private:
// NODE STATE
// Input:
// AstNodeFTask::user1p // Node replaced, rename it
// AstNodeFTask::user1p // Node replaced, rename it
// VISITORS
virtual void visit(AstNodeFTaskRef* nodep) {
if (nodep->taskp()->user1()) { // It was converted
UINFO(9, " relinkFTask "<<nodep<<endl);
nodep->name(nodep->taskp()->name());
}
if (nodep->taskp()->user1()) { // It was converted
UINFO(9, " relinkFTask "<<nodep<<endl);
nodep->name(nodep->taskp()->name());
}
iterateChildren(nodep);
}
virtual void visit(AstVarRef* nodep) {
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef "<<nodep<<endl);
nodep->name(nodep->varp()->name());
}
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef "<<nodep<<endl);
nodep->name(nodep->varp()->name());
}
iterateChildren(nodep);
}
virtual void visit(AstIfaceRefDType* nodep) {
// May have changed cell names
// TypeTable is always after all modules, so names are stable
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
UINFO(8," rename to "<<nodep<<endl);
// May have changed cell names
// TypeTable is always after all modules, so names are stable
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
UINFO(8," rename to "<<nodep<<endl);
iterateChildren(nodep);
}
//--------------------
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void debeginAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+52 -52
View File
@@ -18,12 +18,12 @@
//
//*************************************************************************
// BRANCH TRANSFORMATIONS:
// At each IF/(IF else).
// Count underneath $display/$stop statements.
// If more on if than else, this branch is unlikely, or vice-versa.
// At each FTASKREF,
// Count calls into the function
// Then, if FTASK is called only once, add inline attribute
// At each IF/(IF else).
// Count underneath $display/$stop statements.
// If more on if than else, this branch is unlikely, or vice-versa.
// At each FTASKREF,
// Count calls into the function
// Then, if FTASK is called only once, add inline attribute
//
//*************************************************************************
@@ -44,89 +44,89 @@ class BranchVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstFTask::user1() -> int. Number of references
AstUser1InUse m_inuser1;
// AstFTask::user1() -> int. Number of references
AstUser1InUse m_inuser1;
// TYPES
typedef std::vector<AstCFunc*> CFuncVec;
// STATE
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
CFuncVec m_cfuncsp; // List of all tasks
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
CFuncVec m_cfuncsp; // List of all tasks
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void reset() {
m_likely = false;
m_unlikely = false;
m_likely = false;
m_unlikely = false;
}
void checkUnlikely(AstNode* nodep) {
if (nodep->isUnlikely()) {
UINFO(4," UNLIKELY: "<<nodep<<endl);
m_unlikely++;
}
if (nodep->isUnlikely()) {
UINFO(4," UNLIKELY: "<<nodep<<endl);
m_unlikely++;
}
}
// VISITORS
virtual void visit(AstNodeIf* nodep) {
UINFO(4," IF: "<<nodep<<endl);
int lastLikely = m_likely;
int lastUnlikely = m_unlikely;
{
// Do if
reset();
UINFO(4," IF: "<<nodep<<endl);
int lastLikely = m_likely;
int lastUnlikely = m_unlikely;
{
// Do if
reset();
iterateAndNextNull(nodep->ifsp());
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
// Do else
reset();
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
// Do else
reset();
iterateAndNextNull(nodep->elsesp());
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
// Compute
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
if (likeness>0) {
nodep->branchPred(AstBranchPred::BP_LIKELY);
} else if (likeness<0) {
nodep->branchPred(AstBranchPred::BP_UNLIKELY);
} // else leave unknown
}
m_likely = lastLikely;
m_unlikely = lastUnlikely;
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
// Compute
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
if (likeness>0) {
nodep->branchPred(AstBranchPred::BP_LIKELY);
} else if (likeness<0) {
nodep->branchPred(AstBranchPred::BP_UNLIKELY);
} // else leave unknown
}
m_likely = lastLikely;
m_unlikely = lastUnlikely;
}
virtual void visit(AstCCall* nodep) {
checkUnlikely(nodep);
nodep->funcp()->user1Inc();
checkUnlikely(nodep);
nodep->funcp()->user1Inc();
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
checkUnlikely(nodep);
m_cfuncsp.push_back(nodep);
checkUnlikely(nodep);
m_cfuncsp.push_back(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
checkUnlikely(nodep);
checkUnlikely(nodep);
iterateChildren(nodep);
}
// METHODS
void calc_tasks() {
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
AstCFunc* nodep = *it;
if (!nodep->dontInline()) {
nodep->isInline(true);
}
}
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
AstCFunc* nodep = *it;
if (!nodep->dontInline()) {
nodep->isInline(true);
}
}
}
public:
// CONSTUCTORS
explicit BranchVisitor(AstNetlist* nodep) {
reset();
reset();
iterateChildren(nodep);
calc_tasks();
calc_tasks();
}
virtual ~BranchVisitor() {}
};
+1 -1
View File
@@ -35,4 +35,4 @@ public:
static void branchAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+141 -129
View File
@@ -20,8 +20,8 @@
// V3Broken's Transformations:
//
// Entire netlist
// Mark all nodes
// Check all links point to marked nodes
// Mark all nodes
// Check all links point to marked nodes
//
//*************************************************************************
@@ -47,125 +47,136 @@ private:
// MEMBERS
// For each node, we keep if it exists or not.
typedef vl_unordered_map<const AstNode*,int> NodeMap; // Performance matters (when --debug)
static NodeMap s_nodes; // Set of all nodes that exist
static NodeMap s_nodes; // Set of all nodes that exist
// BITMASK
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
public:
// METHODS
static void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
// Called by operator delete on any node - only if VL_LEAK_CHECKS
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
reinterpret_cast<const AstNode*>(nodep)
->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
}
if (iter!=s_nodes.end()) s_nodes.erase(iter);
NodeMap::iterator iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
reinterpret_cast<const AstNode*>(nodep),
"Deleting AstNode object that was never tracked or already deleted");
if (iter!=s_nodes.end()) s_nodes.erase(iter);
}
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
static void addNewed(const AstNode* nodep) {
// Called by operator new on any node - only if VL_LEAK_CHECKS
// Called by operator new on any node - only if VL_LEAK_CHECKS
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) {
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
}
if (iter == s_nodes.end()) {
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.insert(make_pair(nodep,flags));
}
NodeMap::iterator iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)),
nodep, "Newing AstNode object that is already allocated");
if (iter == s_nodes.end()) {
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.insert(make_pair(nodep, flags));
}
}
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;
}
// 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!
if (!linkable) return; // save some time, else the map will get huge!
#endif
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) {
NodeMap::iterator iter = s_nodes.find(nodep);
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
#ifdef VL_LEAK_CHECKS
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
#endif
} else {
if (!(iter->second & FLAG_ALLOCATED)) {
} else {
#ifdef VL_LEAK_CHECKS
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
"AstNode is in tree, but not allocated");
#endif
}
if (iter->second & FLAG_IN_TREE) {
nodep->v3fatalSrc("AstNode is already in tree at another location");
}
}
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
if (iter == s_nodes.end()) {
s_nodes.insert(make_pair(nodep,or_flags));
} else {
iter->second |= or_flags;
}
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
"AstNode is already in tree at another location");
}
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
if (iter == s_nodes.end()) {
s_nodes.insert(make_pair(nodep, or_flags));
} else {
iter->second |= or_flags;
}
}
static bool isAllocated(const AstNode* nodep) {
// Some generic node has a pointer to this node. Is it allocated?
// Use this when might not be in tree; otherwise use okIfLinkedTo().
#ifdef VL_LEAK_CHECKS
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
return true;
}
static bool okIfLinkedTo(const AstNode* nodep) {
// Someone has a pointer to this node. Is it kosher?
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
// Some node in tree has a pointer to this node. Is it kosher?
NodeMap::iterator iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
#ifdef VL_LEAK_CHECKS
if (!(iter->second & FLAG_ALLOCATED)) return false;
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
if (!(iter->second & FLAG_IN_TREE)) return false;
if (!(iter->second & FLAG_LINKABLE)) return false;
return true;
if (!(iter->second & FLAG_IN_TREE)) return false;
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;
// 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();
s_nodes.clear();
#endif
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
it->second &= ~FLAG_IN_TREE;
it->second &= ~FLAG_LINKABLE;
}
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
it->second &= ~FLAG_IN_TREE;
it->second &= ~FLAG_LINKABLE;
}
}
static void doneWithTree() {
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
if ((it->second & FLAG_ALLOCATED)
&& !(it->second & FLAG_IN_TREE)
&& !(it->second & FLAG_LEAKED)
&& (it->first->backp() ? backs==1 : backs==0)) {
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
if ((it->second & FLAG_ALLOCATED)
&& !(it->second & FLAG_IN_TREE)
&& !(it->second & FLAG_LEAKED)
&& (it->first->backp() ? backs==1 : backs==0)) {
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
AstNode* rawp = const_cast<AstNode*>
(static_cast<const AstNode*>(it->first));
rawp->AstNode::dump(std::cerr);
std::cerr<<endl;
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
}
}
}
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
}
}
}
}
public:
// CONSTUCTORS
@@ -190,16 +201,16 @@ class BrokenMarkVisitor : public AstNVisitor {
// Mark every node in the tree
private:
// NODE STATE
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// METHODS
void processAndIterate(AstNode* nodep) {
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
iterateChildrenConst(nodep);
}
// VISITORS
virtual void visit(AstNode* nodep) {
processAndIterate(nodep);
processAndIterate(nodep);
}
public:
// CONSTUCTORS
@@ -215,49 +226,47 @@ public:
class BrokenCheckVisitor : public AstNVisitor {
private:
void checkWidthMin(const AstNode* nodep) {
if (nodep->width() != nodep->widthMin()
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
nodep->v3fatalSrc("Width != WidthMin");
}
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
nodep, "Width != WidthMin");
}
void processAndIterate(AstNode* nodep) {
BrokenTable::setUnder(nodep,true);
if (const char* whyp=nodep->broken()) {
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
}
if (nodep->dtypep()) {
if (!nodep->dtypep()->brokeExists()) {
nodep->v3fatalSrc("Broken link in node->dtypep() to "
<<cvtToHex(nodep->dtypep()));
} else if (!VN_IS(nodep->dtypep(), NodeDType)) {
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "
<<cvtToHex(nodep->dtypep()));
BrokenTable::setUnder(nodep, true);
const char* whyp = nodep->broken();
UASSERT_OBJ(!whyp, nodep,
"Broken link in node (or something without maybePointedTo): "<<whyp);
if (nodep->dtypep()) {
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
"Broken link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
"Non-dtype link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
}
if (v3Global.assertDTypesResolved()) {
if (nodep->hasDType()) {
UASSERT_OBJ(nodep->dtypep(), nodep,
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
} else {
UASSERT_OBJ(!nodep->dtypep(), nodep,
"DType on node without hasDType(): "<<nodep->prettyTypeName());
}
}
if (v3Global.assertDTypesResolved()) {
if (nodep->hasDType()) {
if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype on node with hasDType(): "<<nodep->prettyTypeName());
} else {
if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<<nodep->prettyTypeName());
}
if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed");
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
"childDTypep() non-null on node after should have removed");
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
}
checkWidthMin(nodep);
}
checkWidthMin(nodep);
iterateChildrenConst(nodep);
BrokenTable::setUnder(nodep,false);
BrokenTable::setUnder(nodep, false);
}
virtual void visit(AstNodeAssign* nodep) {
processAndIterate(nodep);
if (v3Global.assertDTypesResolved()
&& nodep->brokeLhsMustBeLvalue()
&& VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
}
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved()
&& nodep->brokeLhsMustBeLvalue()
&& VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()),
nodep, "Assignment LHS is not an lvalue");
}
virtual void visit(AstNode* nodep) {
processAndIterate(nodep);
processAndIterate(nodep);
}
public:
// CONSTUCTORS
@@ -273,16 +282,16 @@ public:
void V3Broken::brokenAll(AstNetlist* nodep) {
//UINFO(9,__FUNCTION__<<": "<<endl);
static bool inBroken = false;
if (inBroken) {
// A error called by broken can recurse back into broken; avoid this
UINFO(1,"Broken called under broken, skipping recursion.\n");
if (VL_UNCOVERABLE(inBroken)) {
// A error called by broken can recurse back into broken; avoid this
UINFO(1,"Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
} else {
inBroken = true;
BrokenTable::prepForTree();
BrokenMarkVisitor mvisitor (nodep);
BrokenCheckVisitor cvisitor (nodep);
BrokenTable::doneWithTree();
inBroken = false;
inBroken = true;
BrokenTable::prepForTree();
BrokenMarkVisitor mvisitor (nodep);
BrokenCheckVisitor cvisitor (nodep);
BrokenTable::doneWithTree();
inBroken = false;
}
}
@@ -292,3 +301,6 @@ void V3Broken::addNewed(AstNode* nodep) {
void V3Broken::deleted(AstNode* nodep) {
BrokenTable::deleted(nodep);
}
bool V3Broken::isAllocated(AstNode* nodep) {
return BrokenTable::isAllocated(nodep);
}
+2 -1
View File
@@ -34,6 +34,7 @@ public:
static void brokenAll(AstNetlist* nodep);
static void addNewed(AstNode* nodep);
static void deleted(AstNode* nodep);
static bool isAllocated(AstNode* nodep);
};
#endif // Guard
#endif // Guard
+80 -75
View File
@@ -18,14 +18,14 @@
//
//*************************************************************************
// V3CCtors's Transformations:
// Iterates over all modules and
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
// For each variable that needs reset, add a AstCReset node.
// Iterates over all modules and
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
// For each variable that needs reset, add a AstCReset node.
//
// For primary inputs, add _eval_debug_assertions.
// For primary inputs, add _eval_debug_assertions.
//
// This transformation honors outputSplitCFuncs.
// This transformation honors outputSplitCFuncs.
//*************************************************************************
#include "config_build.h"
@@ -43,59 +43,60 @@
class V3CCtorsVisitor {
private:
string m_basename;
string m_argsp;
string m_callargsp;
AstNodeModule* m_modp; // Current module
AstCFunc* m_tlFuncp; // Top level function being built
AstCFunc* m_funcp; // Current function
int m_numStmts; // Number of statements output
int m_funcNum; // Function number being built
string m_basename;
string m_argsp;
string m_callargsp;
AstNodeModule* m_modp; // Current module
AstCFunc* m_tlFuncp; // Top level function being built
AstCFunc* m_funcp; // Current function
int m_numStmts; // Number of statements output
int m_funcNum; // Function number being built
public:
void add(AstNode* nodep) {
if (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_funcp = NULL;
}
if (!m_funcp) {
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum), NULL, "void");
m_funcp->isStatic(false);
m_funcp->declPrivate(true);
m_funcp->slow(true);
m_funcp->argTypes(m_argsp);
m_modp->addStmtp(m_funcp);
if (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_funcp = NULL;
}
if (!m_funcp) {
m_funcp = new AstCFunc(m_modp->fileline(),
m_basename+"_"+cvtToStr(++m_funcNum), NULL, "void");
m_funcp->isStatic(false);
m_funcp->declPrivate(true);
m_funcp->slow(true);
m_funcp->argTypes(m_argsp);
m_modp->addStmtp(m_funcp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
callp->argTypes(m_callargsp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
callp->argTypes(m_callargsp);
m_tlFuncp->addStmtsp(callp);
m_numStmts = 0;
}
m_funcp->addStmtsp(nodep);
m_numStmts += 1;
m_tlFuncp->addStmtsp(callp);
m_numStmts = 0;
}
m_funcp->addStmtsp(nodep);
m_numStmts += 1;
}
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename,
const string& argsp="", const string& callargsp="",
const string& stmt="") {
m_basename = basename;
m_argsp = argsp;
m_callargsp = callargsp;
m_modp = nodep;
m_numStmts = 0;
m_funcNum = 0;
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
m_tlFuncp->declPrivate(true);
m_tlFuncp->isStatic(false);
m_tlFuncp->slow(true);
m_tlFuncp->argTypes(m_argsp);
if (stmt != "") {
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
m_funcp = m_tlFuncp;
m_modp->addStmtp(m_tlFuncp);
const string& stmt="") {
m_basename = basename;
m_argsp = argsp;
m_callargsp = callargsp;
m_modp = nodep;
m_numStmts = 0;
m_funcNum = 0;
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
m_tlFuncp->declPrivate(true);
m_tlFuncp->isStatic(false);
m_tlFuncp->slow(true);
m_tlFuncp->argTypes(m_argsp);
if (stmt != "") {
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
m_funcp = m_tlFuncp;
m_modp->addStmtp(m_tlFuncp);
}
~V3CCtorsVisitor() {}
private:
@@ -105,7 +106,7 @@ private:
//######################################################################
void V3CCtors::evalAsserts() {
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void");
funcp->declPrivate(true);
funcp->isStatic(false);
@@ -116,36 +117,38 @@ void V3CCtors::evalAsserts() {
if (AstVar* varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
int storedWidth = basicp->widthAlignBytes() * 8;
int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
if (varp->isWide()) {
newp = new AstWordSel(varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords()-1));
}
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
V3Number num (varp->fileline(), storedWidth, value);
newp = new AstAnd(varp->fileline(), newp,
new AstConst(varp->fileline(), num));
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
int storedWidth = basicp->widthAlignBytes() * 8;
int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstNode* newp = new AstVarRef(varp->fileline(), varp, false);
if (varp->isWide()) {
newp = new AstWordSel(
varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords()-1));
}
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd(varp->fileline(), newp,
new AstConst(varp->fileline(), AstConst::WidthedValue(),
storedWidth, value));
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
new AstCStmt(varp->fileline(),
"Verilated::overWidthError(\""+varp->prettyName()+"\");"));
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
newp = ifp;
funcp->addStmtsp(newp);
}
}
}
}
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
newp = ifp;
funcp->addStmtsp(newp);
}
}
}
}
}
}
void V3CCtors::cctorsAll() {
UINFO(2,__FUNCTION__<<": "<<endl);
evalAsserts();
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
for (AstNodeModule* modp = v3Global.rootp()->modulesp();
modp; modp = VN_CAST(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
@@ -153,14 +156,16 @@ void V3CCtors::cctorsAll() {
if (AstVar* varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef()
&& !varp->noReset()) {
var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true)));
var_reset.add(new AstCReset(varp->fileline(),
new AstVarRef(varp->fileline(), varp, true)));
}
}
}
}
if (v3Global.opt.coverage()) {
V3CCtorsVisitor configure_coverage
(modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
(modp, "_configure_coverage",
EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
"if (0 && vlSymsp && first) {} // Prevent unused\n");
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
+1 -1
View File
@@ -37,4 +37,4 @@ private:
};
#endif // Guard
#endif // Guard
+342 -332
View File
@@ -20,20 +20,21 @@
// V3Case's Transformations:
//
// Each module:
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
// Need __en in changed list if a signal is on the LHS of a assign
// Cases:
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
// (AND item1 mask))
// (other items))
// body
// Or, converts to a if/else tree.
// FUTURES:
// Large 16+ bit tables with constants and no masking (address muxes)
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
// Need __en in changed list if a signal is on the LHS of a assign
// Cases:
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
// (AND item1 mask))
// (other items))
// body
// Or, converts to a if/else tree.
// FUTURES:
// Large 16+ bit tables with constants and no masking (address muxes)
// Enter all into std::multimap, sort by value and use a tree of < and == compares.
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
// Ignoring mask, check each value is unique (using std::multimap as above?)
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
// Each branch is then mask-and-compare operation (IE
// <000000001_000000000 at midpoint.)
//
//*************************************************************************
@@ -48,60 +49,62 @@
#include <algorithm>
#include <cstdarg>
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
#define CASE_BARF 999999 // Magic width when non-constant
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
#define CASE_BARF 999999 // Magic width when non-constant
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
//######################################################################
class CaseLintVisitor : public AstNVisitor {
private:
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
// METHODS
VL_DEBUG_FUNC; // Declare debug()
virtual void visit(AstNodeCase* nodep) {
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
}
// Detect multiple defaults
bool hitDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) {
if (hitDefault) {
nodep->v3error("Multiple default statements in case statement.");
}
hitDefault = true;
}
}
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
}
// Detect multiple defaults
bool hitDefault = false;
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) {
if (hitDefault) {
itemp->v3error("Multiple default statements in case statement.");
}
hitDefault = true;
}
}
// Check for X/Z in non-casex statements
{
m_caseExprp = nodep;
// Check for X/Z in non-casex statements
{
m_caseExprp = nodep;
iterate(nodep->exprp());
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
iterateAndNextNull(itemp->condsp());
}
m_caseExprp = NULL;
}
}
m_caseExprp = NULL;
}
}
virtual void visit(AstConst* nodep) {
// See also neverItem
if (m_caseExprp && nodep->num().isFourState()) {
// See also neverItem
if (m_caseExprp && nodep->num().isFourState()) {
if (VN_IS(m_caseExprp, GenCase)) {
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
// Don't sweat it, we already complained about casex in general
// Don't sweat it, we already complained about casex in general
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
if (nodep->num().isUnknown()) {
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
}
} else {
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
}
}
if (nodep->num().isUnknown()) {
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
}
} else {
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
}
}
}
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
@@ -109,7 +112,7 @@ private:
public:
// CONSTUCTORS
explicit CaseLintVisitor(AstNodeCase* nodep) {
m_caseExprp = NULL;
m_caseExprp = NULL;
iterate(nodep);
}
virtual ~CaseLintVisitor() {}
@@ -122,340 +125,347 @@ class CaseVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each Case
// AstIf::user3() -> bool. Set true to indicate clone not needed
AstUser3InUse m_inuser3;
// AstIf::user3() -> bool. Set true to indicate clone not needed
AstUser3InUse m_inuser3;
// STATE
V3Double0 m_statCaseFast; // Statistic tracking
V3Double0 m_statCaseSlow; // Statistic tracking
V3Double0 m_statCaseFast; // Statistic tracking
V3Double0 m_statCaseSlow; // Statistic tracking
// Per-CASE
int m_caseWidth; // Width of valueItems
int m_caseItems; // Number of caseItem unique values
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
int m_caseWidth; // Width of valueItems
int m_caseItems; // Number of caseItem unique values
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool isCaseTreeFast(AstCase* nodep) {
int width = 0;
bool opaque = false;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = true;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
if (icondp->width() > width) width = icondp->width();
if (icondp->isDouble()) opaque = true;
int width = 0;
bool opaque = false;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = true;
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
if (icondp->width() > width) width = icondp->width();
if (icondp->isDouble()) opaque = true;
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
m_caseItems++;
}
}
m_caseWidth = width;
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
m_caseNoOverlapsAllCovered = false;
return false; // Too wide for analysis
}
UINFO(8,"Simple case statement: "<<nodep<<endl);
// Zero list of items for each value
m_caseItems++;
}
}
m_caseWidth = width;
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
m_caseNoOverlapsAllCovered = false;
return false; // Too wide for analysis
}
UINFO(8,"Simple case statement: "<<nodep<<endl);
// Zero list of items for each value
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
// Now pick up the values for each assignment
// We can cheat and use uint32_t's because we only support narrow case's
bool bitched = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
// Now pick up the values for each assignment
// We can cheat and use uint32_t's because we only support narrow case's
bool bitched = false;
for (AstCaseItem* itemp = nodep->itemsp();
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
//if (debug()>=9) icondp->dumpTree(cout, " caseitem: ");
AstConst* iconstp = VN_CAST(icondp, Const);
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
if (neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
} else {
V3Number nummask (itemp->fileline(), iconstp->width());
nummask.opBitsNonX(iconstp->num());
uint32_t mask = nummask.toUInt();
V3Number numval (itemp->fileline(), iconstp->width());
numval.opBitsOne(iconstp->num());
uint32_t val = numval.toUInt();
UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this");
if (neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
} else {
V3Number nummask (itemp, iconstp->width());
nummask.opBitsNonX(iconstp->num());
uint32_t mask = nummask.toUInt();
V3Number numval (itemp, iconstp->width());
numval.opBitsOne(iconstp->num());
uint32_t val = numval.toUInt();
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
} else if (!itemp->ignoreOverlap() && !bitched) {
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
bitched = true;
m_caseNoOverlapsAllCovered = false;
}
}
}
}
}
// Defaults were moved to last in the caseitem list by V3LinkDot
if (itemp->isDefault()) { // Case statement's default... Fill the table
if ((i & mask) == val) {
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
} else if (!itemp->ignoreOverlap() && !bitched) {
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
<<std::hex<<i<<")");
bitched = true;
m_caseNoOverlapsAllCovered = false;
}
}
}
}
}
// Defaults were moved to last in the caseitem list by V3LinkDot
if (itemp->isDefault()) { // Case statement's default... Fill the table
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if (!m_valueItem[i]) m_valueItem[i] = itemp;
}
}
}
if (!m_valueItem[i]) m_valueItem[i] = itemp;
}
}
}
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
m_caseNoOverlapsAllCovered = false;
return false;
}
}
if (m_caseItems <= 3) return false; // Not worth simplifing
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered (example pattern 0x"
<<std::hex<<i<<")");
m_caseNoOverlapsAllCovered = false;
return false;
}
}
if (m_caseItems <= 3) return false; // Not worth simplifing
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
}
return true; // All is fine
}
return true; // All is fine
}
AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) {
if (msb<0) {
// There's no space for a IF. We know upperValue is thus down to a specific
// exact value, so just return the tree value
// Note can't clone here, as we're going to check for equivelence above
return m_valueItem[upperValue];
}
else {
// Make left and right subtrees
// cexpr[msb:lsb] == 1
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
if (msb<0) {
// There's no space for a IF. We know upperValue is thus down to a specific
// exact value, so just return the tree value
// Note can't clone here, as we're going to check for equivelence above
return m_valueItem[upperValue];
}
else {
// Make left and right subtrees
// cexpr[msb:lsb] == 1
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
if (tree0p == tree1p) {
// Same logic on both sides, so we can just return one of 'em
return tree0p;
}
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
bool same = true;
for (uint32_t a=upperValue,
b=(upperValue|(1UL<<msb));
a < (upperValue|(1UL<<msb));
a++, b++) {
if (m_valueItem[a] != m_valueItem[b]) { same=false; break; }
}
if (same) {
tree1p->deleteTree(); VL_DANGLING(tree1p);
return tree0p;
}
if (tree0p == tree1p) {
// Same logic on both sides, so we can just return one of 'em
return tree0p;
}
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
bool same = true;
for (uint32_t a=upperValue,
b=(upperValue|(1UL<<msb));
a < (upperValue|(1UL<<msb));
a++, b++) {
if (m_valueItem[a] != m_valueItem[b]) { same = false; break; }
}
if (same) {
tree1p->deleteTree(); VL_DANGLING(tree1p);
return tree0p;
}
// Must have differing logic, so make a selection
// Must have differing logic, so make a selection
// Case expressions can't be linked twice, so clone them
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
// Case expressions can't be linked twice, so clone them
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
// Alternate scheme if we ever do multiple bits at a time:
//V3Number nummask (cexprp->fileline(), cexprp->width(), (1UL<<msb));
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
// new AstConst(cexprp->fileline(), nummask));
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
msb, 1);
AstNode* eqp = new AstNeq(cexprp->fileline(),
new AstConst(cexprp->fileline(), 0),
and1p);
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
ifp->user3(1); // So we don't bother to clone it
return ifp;
}
// Alternate scheme if we ever do multiple bits at a time:
//V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
// new AstConst(cexprp->fileline(), nummask));
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
msb, 1);
AstNode* eqp = new AstNeq(cexprp->fileline(),
new AstConst(cexprp->fileline(), 0),
and1p);
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
ifp->user3(1); // So we don't bother to clone it
return ifp;
}
}
void replaceCaseFast(AstCase* nodep) {
// CASEx(cexpr,....
// -> tree of IF(msb, IF(msb-1, 11, 10)
// IF(msb-1, 01, 00))
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
// CASEx(cexpr,....
// -> tree of IF(msb, IF(msb-1, 11, 10)
// IF(msb-1, 01, 00))
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
if (debug()>=9) {
if (debug()>=9) {
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if (AstNode* itemp = m_valueItem[i]) {
if (AstNode* itemp = m_valueItem[i]) {
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
}
}
}
}
}
}
// Handle any assertions
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
// Handle any assertions
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
AstNode::user3ClearTree();
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
// Case expressions can't be linked twice, so clone them
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
AstNode::user3ClearTree();
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
// Case expressions can't be linked twice, so clone them
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
if (ifrootp) nodep->replaceWith(ifrootp);
else nodep->unlinkFrBack();
nodep->deleteTree(); VL_DANGLING(nodep);
cexprp->deleteTree(); VL_DANGLING(cexprp);
if (debug()>=9) ifrootp->dumpTree(cout," _simp: ");
if (ifrootp) nodep->replaceWith(ifrootp);
else nodep->unlinkFrBack();
nodep->deleteTree(); VL_DANGLING(nodep);
cexprp->deleteTree(); VL_DANGLING(cexprp);
if (debug()>=9) ifrootp->dumpTree(cout, " _simp: ");
}
void replaceCaseComplicated(AstCase* nodep) {
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
// -> IF((cexpr==icond1),istmts1,
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
// ,istmts2, istmts3
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
// We'll do this in two stages. First stage, convert the conditions to
// the appropriate IF AND terms.
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
bool hadDefault = false;
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
// -> IF((cexpr==icond1),istmts1,
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
// ,istmts2, istmts3
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
// We'll do this in two stages. First stage, convert the conditions to
// the appropriate IF AND terms.
if (debug()>=9) nodep->dumpTree(cout, " _comp_IN: ");
bool hadDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (!itemp->condsp()) {
// Default clause. Just make true, we'll optimize it away later
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
hadDefault = true;
} else {
// Expressioned clause
AstNode* icondNextp = NULL;
AstNode* ifexprp = NULL; // If expression to test
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
icondNextp = icondp->nextp();
icondp->unlinkFrBack();
if (!itemp->condsp()) {
// Default clause. Just make true, we'll optimize it away later
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
hadDefault = true;
} else {
// Expressioned clause
AstNode* icondNextp = NULL;
AstNode* ifexprp = NULL; // If expression to test
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
icondNextp = icondp->nextp();
icondp->unlinkFrBack();
AstNode* condp = NULL; // Default is to use and1p/and2p
AstNode* condp = NULL; // Default is to use and1p/and2p
AstConst* iconstp = VN_CAST(icondp, Const);
if (iconstp && neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
// For simplicity, make expression that is not equal, and let later
// optimizations remove it
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
if (iconstp && neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
// For simplicity, make expression that is not equal, and let later
// optimizations remove it
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
// Similar logic in V3Width::visit(AstInside)
AstNode* ap = AstGte::newTyped(itemp->fileline(),
cexprp->cloneTree(false),
irangep->lhsp()->unlinkFrBack());
AstNode* bp = AstLte::newTyped(itemp->fileline(),
cexprp->cloneTree(false),
irangep->rhsp()->unlinkFrBack());
condp = new AstAnd(itemp->fileline(), ap, bp);
} else if (iconstp && iconstp->num().isFourState()
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
V3Number nummask (itemp->fileline(), iconstp->width());
nummask.opBitsNonX(iconstp->num());
V3Number numval (itemp->fileline(), iconstp->width());
numval.opBitsOne(iconstp->num());
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
new AstConst(itemp->fileline(), nummask));
AstNode* and2p = new AstAnd(itemp->fileline(),
new AstConst(itemp->fileline(), numval),
new AstConst(itemp->fileline(), nummask));
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
} else {
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
AstNode* and1p = cexprp->cloneTree(false);
AstNode* and2p = icondp;
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
}
if (!ifexprp) {
ifexprp = condp;
} else {
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
}
}
// Replace expression in tree
itemp->condsp(ifexprp);
}
}
cexprp->deleteTree(); VL_DANGLING(cexprp);
if (!hadDefault) {
// If there was no default, add a empty one, this greatly simplifies below code
// and constant propagation will just eliminate it for us later.
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
NULL));
}
if (debug()>=9) nodep->dumpTree(cout," _comp_COND: ");
// Now build the IF statement tree
// The tree can be quite huge. Pull ever group of 8 out, and make a OR tree.
// This reduces the depth for the bottom elements, at the cost of some of the top elements.
// If we ever have profiling data, we should pull out the most common item from here and
// instead make it the first IF branch.
int depth = 0;
AstNode* grouprootp = NULL;
AstIf* groupnextp = NULL;
AstIf* itemnextp = NULL;
// Similar logic in V3Width::visit(AstInside)
AstNode* ap = AstGte::newTyped(itemp->fileline(),
cexprp->cloneTree(false),
irangep->lhsp()->unlinkFrBack());
AstNode* bp = AstLte::newTyped(itemp->fileline(),
cexprp->cloneTree(false),
irangep->rhsp()->unlinkFrBack());
condp = new AstAnd(itemp->fileline(), ap, bp);
} else if (iconstp && iconstp->num().isFourState()
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
V3Number nummask (itemp, iconstp->width());
nummask.opBitsNonX(iconstp->num());
V3Number numval (itemp, iconstp->width());
numval.opBitsOne(iconstp->num());
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
new AstConst(itemp->fileline(), nummask));
AstNode* and2p = new AstAnd(itemp->fileline(),
new AstConst(itemp->fileline(), numval),
new AstConst(itemp->fileline(), nummask));
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
} else {
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
AstNode* and1p = cexprp->cloneTree(false);
AstNode* and2p = icondp;
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
}
if (!ifexprp) {
ifexprp = condp;
} else {
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
}
}
// Replace expression in tree
itemp->condsp(ifexprp);
}
}
cexprp->deleteTree(); VL_DANGLING(cexprp);
if (!hadDefault) {
// If there was no default, add a empty one, this greatly simplifies below code
// and constant propagation will just eliminate it for us later.
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
NULL));
}
if (debug()>=9) nodep->dumpTree(cout, " _comp_COND: ");
// Now build the IF statement tree
// The tree can be quite huge. Pull ever group of 8 out, and make a OR tree.
// This reduces the depth for the bottom elements, at the cost of
// some of the top elements. If we ever have profiling data, we
// should pull out the most common item from here and instead make
// it the first IF branch.
int depth = 0;
AstNode* grouprootp = NULL;
AstIf* groupnextp = NULL;
AstIf* itemnextp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
if (istmtsp) istmtsp->unlinkFrBackWithNext();
// Expressioned clause
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
{ // Prepare for next group
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
if (depth == 1) { // First group or starting new group
itemnextp = NULL;
AstIf* newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL);
if (groupnextp) groupnextp->addElsesp(newp);
else grouprootp = newp;
groupnextp = newp;
} else { // Continue group, modify if condition to OR in this new condition
AstNode* condp = groupnextp->condp()->unlinkFrBack();
groupnextp->condp(new AstOr(ifexprp->fileline(),
condp,
ifexprp->cloneTree(true)));
}
}
{ // Make the new lower IF and attach in the tree
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
itemexprp->deleteTree(); VL_DANGLING(itemexprp);
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
}
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
if (itemnextp) itemnextp->addElsesp(newp);
else groupnextp->addIfsp(newp); // First in a new group
itemnextp = newp;
}
}
if (debug()>=9) nodep->dumpTree(cout," _comp_TREE: ");
// Handle any assertions
replaceCaseParallel(nodep, false);
// Replace the CASE... with IF...
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout," _new: ");
if (grouprootp) nodep->replaceWith(grouprootp);
else nodep->unlinkFrBack();
nodep->deleteTree(); VL_DANGLING(nodep);
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
if (istmtsp) istmtsp->unlinkFrBackWithNext();
// Expressioned clause
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
{ // Prepare for next group
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
if (depth == 1) { // First group or starting new group
itemnextp = NULL;
AstIf* newp = new AstIf(itemp->fileline(),
ifexprp->cloneTree(true), NULL, NULL);
if (groupnextp) groupnextp->addElsesp(newp);
else grouprootp = newp;
groupnextp = newp;
} else { // Continue group, modify if condition to OR in this new condition
AstNode* condp = groupnextp->condp()->unlinkFrBack();
groupnextp->condp(new AstOr(ifexprp->fileline(),
condp,
ifexprp->cloneTree(true)));
}
}
{ // Make the new lower IF and attach in the tree
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
itemexprp->deleteTree(); VL_DANGLING(itemexprp);
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
}
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
if (itemnextp) itemnextp->addElsesp(newp);
else groupnextp->addIfsp(newp); // First in a new group
itemnextp = newp;
}
}
if (debug()>=9) nodep->dumpTree(cout, " _comp_TREE: ");
// Handle any assertions
replaceCaseParallel(nodep, false);
// Replace the CASE... with IF...
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout, " _new: ");
if (grouprootp) nodep->replaceWith(grouprootp);
else nodep->unlinkFrBack();
nodep->deleteTree(); VL_DANGLING(nodep);
}
void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) {
// Take the notParallelp tree under the case statement created by V3Assert
// If the statement was proven to have no overlaps and all cases covered, we're done with it.
// Else, convert to a normal statement parallel with the case statement.
if (nodep->notParallelp() && !noOverlapsAllCovered) {
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
nodep->addNextHere(parp);
}
// Take the notParallelp tree under the case statement created by V3Assert
// If the statement was proven to have no overlaps and all cases
// covered, we're done with it.
// Else, convert to a normal statement parallel with the case statement.
if (nodep->notParallelp() && !noOverlapsAllCovered) {
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
nodep->addNextHere(parp);
}
}
bool neverItem(AstCase* casep, AstConst* itemp) {
// Xs in case or casez are impossible due to two state simulations
if (casep->casex()) {
} else if (casep->casez() || casep->caseInside()) {
if (itemp->num().isUnknown()) return true;
} else {
if (itemp->num().isFourState()) return true;
}
return false;
// Xs in case or casez are impossible due to two state simulations
if (casep->casex()) {
} else if (casep->casez() || casep->caseInside()) {
if (itemp->num().isUnknown()) return true;
} else {
if (itemp->num().isFourState()) return true;
}
return false;
}
// VISITORS
virtual void visit(AstCase* nodep) {
V3Case::caseLint(nodep);
V3Case::caseLint(nodep);
iterateChildren(nodep);
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
// It's a simple priority encoder or complete statement
// we can make a tree of statements to avoid extra comparisons
++m_statCaseFast;
replaceCaseFast(nodep); VL_DANGLING(nodep);
} else {
++m_statCaseSlow;
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
}
if (debug()>=9) nodep->dumpTree(cout, " case_old: ");
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
// It's a simple priority encoder or complete statement
// we can make a tree of statements to avoid extra comparisons
++m_statCaseFast;
replaceCaseFast(nodep); VL_DANGLING(nodep);
} else {
++m_statCaseSlow;
replaceCaseComplicated(nodep); VL_DANGLING(nodep);
}
}
//--------------------
// Default: Just iterate
@@ -468,15 +478,15 @@ public:
explicit CaseVisitor(AstNetlist* nodep) {
m_caseWidth = 0;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = false;
m_caseNoOverlapsAllCovered = false;
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
m_valueItem[i] = NULL;
}
iterate(nodep);
}
virtual ~CaseVisitor() {
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
}
};
+1 -1
View File
@@ -35,4 +35,4 @@ public:
static void caseLint(AstNodeCase* nodep);
};
#endif // Guard
#endif // Guard
+72 -72
View File
@@ -20,24 +20,24 @@
// V3Cast's Transformations:
//
// Each module:
// For each math operator, if above operator requires 32 bits,
// and this isn't, cast to 32 bits.
// Likewise for 64 bit operators.
// For each math operator, if above operator requires 32 bits,
// and this isn't, cast to 32 bits.
// Likewise for 64 bit operators.
//
// C++ rules:
// Integral promotions allow conversion to larger int. Unsigned is only
// used if a int would not fit the value.
// Integral promotions allow conversion to larger int. Unsigned is only
// used if a int would not fit the value.
//
// Bools converts to int, not unsigned.
// Bools converts to int, not unsigned.
//
// Most operations return unsigned if either operand is unsigned.
// Most operations return unsigned if either operand is unsigned.
//
// Unsignedness can be lost on results of the below operations, as they
// may need the sign bit for proper operation:
// /, %, /=, %=, <, <=, >, or >=
// Unsignedness can be lost on results of the below operations, as they
// may need the sign bit for proper operation:
// /, %, /=, %=, <, <=, >, or >=
//
// Signed values are always sign extended on promotion or right shift,
// even if assigning to a unsigned.
// Signed values are always sign extended on promotion or right shift,
// even if assigning to a unsigned.
//
//*************************************************************************
@@ -58,8 +58,8 @@ class CastVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNode::user() // bool. Indicates node is of known size
AstUser1InUse m_inuser1;
// AstNode::user() // bool. Indicates node is of known size
AstUser1InUse m_inuser1;
// STATE
@@ -67,97 +67,97 @@ private:
VL_DEBUG_FUNC; // Declare debug()
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
UINFO(4," NeedCast "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
UINFO(4," NeedCast "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
relinkHandle.relink(castp);
//if (debug()>8) castp->dumpTree(cout,"-castins: ");
//
insureLower32Cast(castp);
nodep->user1(1); // Now must be of known size
relinkHandle.relink(castp);
//if (debug()>8) castp->dumpTree(cout, "-castins: ");
//
insureLower32Cast(castp);
nodep->user1(1); // Now must be of known size
}
int castSize(AstNode* nodep) {
if (nodep->isQuad()) return VL_QUADSIZE;
else if (nodep->width()<=8) return 8;
else if (nodep->width()<=16) return 16;
else return VL_WORDSIZE;
if (nodep->isQuad()) return VL_QUADSIZE;
else if (nodep->width()<=8) return 8;
else if (nodep->width()<=16) return 16;
else return VL_WORDSIZE;
}
void insureCast(AstNode* nodep) {
if (castSize(nodep->backp()) != castSize(nodep)
|| !nodep->user1()) {
insertCast(nodep, castSize(nodep->backp()));
}
if (castSize(nodep->backp()) != castSize(nodep)
|| !nodep->user1()) {
insertCast(nodep, castSize(nodep->backp()));
}
}
void insureLower32Cast(AstCCast* nodep) {
// If we have uint64 = CAST(uint64(x)) then the upcasting
// really needs to be CAST(uint64(CAST(uint32(x))).
// Otherwise a (uint64)(a>b) would return wrong value, as
// less than has undeterministic signedness.
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
// If we have uint64 = CAST(uint64(x)) then the upcasting
// really needs to be CAST(uint64(CAST(uint32(x))).
// Otherwise a (uint64)(a>b) would return wrong value, as
// less than has undeterministic signedness.
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
&& !VN_IS(nodep->lhsp(), CCast)) {
insertCast(nodep->lhsp(), VL_WORDSIZE);
}
insertCast(nodep->lhsp(), VL_WORDSIZE);
}
}
// VISITORS
virtual void visit(AstNodeUniop* nodep) {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
nodep->user1(nodep->lhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
}
virtual void visit(AstNodeBiop* nodep) {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
}
virtual void visit(AstNodeTriop* nodep) {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1()
| nodep->thsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1()
| nodep->thsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
}
virtual void visit(AstCCast* nodep) {
iterateChildren(nodep);
insureLower32Cast(nodep);
nodep->user1(1);
insureLower32Cast(nodep);
nodep->user1(1);
}
virtual void visit(AstNegate* nodep) {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->lhsp()->widthMin()==1) {
// We want to avoid a GCC "converting of negative value" warning
// from our expansion of
// out = {32{a<b}} => out = - (a<b)
insertCast(nodep->lhsp(), castSize(nodep));
} else {
insureCast(nodep->lhsp());
}
nodep->user1(nodep->lhsp()->user1());
if (nodep->lhsp()->widthMin()==1) {
// We want to avoid a GCC "converting of negative value" warning
// from our expansion of
// out = {32{a<b}} => out = - (a<b)
insertCast(nodep->lhsp(), castSize(nodep));
} else {
insureCast(nodep->lhsp());
}
}
virtual void visit(AstVarRef* nodep) {
if (!nodep->lvalue()
if (!nodep->lvalue()
&& !VN_IS(nodep->backp(), CCast)
&& VN_IS(nodep->backp(), NodeMath)
&& !VN_IS(nodep->backp(), ArraySel)
&& nodep->backp()->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
&& nodep->backp()->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
insertCast(nodep, castSize(nodep));
}
nodep->user1(1);
}
nodep->user1(1);
}
virtual void visit(AstConst* nodep) {
// Constants are of unknown size if smaller than 33 bits, becase
// we're too lazy to wrap every constant in the universe in
// ((IData)#).
nodep->user1(nodep->isQuad() || nodep->isWide());
// Constants are of unknown size if smaller than 33 bits, becase
// we're too lazy to wrap every constant in the universe in
// ((IData)#).
nodep->user1(nodep->isQuad() || nodep->isWide());
}
// NOPs
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void castAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+427 -418
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void cdcAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+158 -148
View File
@@ -20,13 +20,13 @@
// V3Changed's Transformations:
//
// Each module:
// Each combo block
// For each variable that comes from combo block and is generated AFTER a usage
// Add __Vlast_{var} to local section, init to current value (just use array?)
// Change = if any var != last.
// If a signal is used as a clock in this module or any
// module *below*, and it isn't a input to this module,
// we need to indicate a new clock has been created.
// Each combo block
// For each variable that comes from combo block and is generated AFTER a usage
// Add __Vlast_{var} to local section, init to current value (just use array?)
// Change = if any var != last.
// If a signal is used as a clock in this module or any
// module *below*, and it isn't a input to this module,
// we need to indicate a new clock has been created.
//
//*************************************************************************
@@ -47,56 +47,60 @@
class ChangedState {
public:
// STATE
AstNodeModule* m_topModp; // Top module
AstScope* m_scopetopp; // Scope under TOPSCOPE
AstCFunc* m_chgFuncp; // Change function we're building
AstCFunc* m_tlChgFuncp; // Top level change function we're building
int m_numStmts; // Number of statements added to m_chgFuncp
int m_funcNum; // Number of change functions emitted
AstNodeModule* m_topModp; // Top module
AstScope* m_scopetopp; // Scope under TOPSCOPE
AstCFunc* m_chgFuncp; // Change function we're building
AstCFunc* m_tlChgFuncp; // Top level change function we're building
int m_numStmts; // Number of statements added to m_chgFuncp
int m_funcNum; // Number of change functions emitted
ChangedState() {
m_topModp = NULL;
m_chgFuncp = NULL;
m_scopetopp = NULL;
m_tlChgFuncp = NULL;
m_numStmts = 0;
m_funcNum = 0;
m_topModp = NULL;
m_chgFuncp = NULL;
m_scopetopp = NULL;
m_tlChgFuncp = NULL;
m_numStmts = 0;
m_funcNum = 0;
}
~ChangedState() {}
void maybeCreateChgFuncp() {
// Don't create an extra function call if splitting is disabled
if (!v3Global.opt.outputSplitCFuncs()) {
m_chgFuncp = m_tlChgFuncp;
return;
}
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_chgFuncp = new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), m_scopetopp, "QData");
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
m_chgFuncp->symProlog(true);
m_chgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_chgFuncp);
// Don't create an extra function call if splitting is disabled
if (!v3Global.opt.outputSplitCFuncs()) {
m_chgFuncp = m_tlChgFuncp;
return;
}
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_chgFuncp = new AstCFunc(m_scopetopp->fileline(),
"_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData");
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
m_chgFuncp->symProlog(true);
m_chgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_chgFuncp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
callp->argTypes("vlSymsp");
// Add a top call to it
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
callp->argTypes("vlSymsp");
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
} else {
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
} else {
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
// This is currently using AstLogOr which will shortcut the evaluation if
// any function returns true. This is likely what we want and is similar to the logic already in use
// inside V3EmitC, however, it also means that verbose logging may miss to print change detect variables.
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
new AstLogOr(m_scopetopp->fileline(), callp,
returnp->lhsp()->unlinkFrBack()));
returnp->replaceWith(newp);
returnp->deleteTree(); VL_DANGLING(returnp);
}
m_numStmts = 0;
}
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
// This is currently using AstLogOr which will shortcut the
// evaluation if any function returns true. This is likely what
// we want and is similar to the logic already in use inside
// V3EmitC, however, it also means that verbose logging may
// miss to print change detect variables.
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
new AstLogOr(m_scopetopp->fileline(), callp,
returnp->lhsp()->unlinkFrBack()));
returnp->replaceWith(newp);
returnp->deleteTree(); VL_DANGLING(returnp);
}
m_numStmts = 0;
}
}
};
@@ -106,112 +110,116 @@ public:
class ChangedInsertVisitor : public AstNVisitor {
private:
// STATE
ChangedState* m_statep; // Shared state across visitors
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
AstNode* m_varEqnp; // Original var's equation to get var value
AstNode* m_newLvEqnp; // New var's equation to read value
AstNode* m_newRvEqnp; // New var's equation to set value
uint32_t m_detects; // # detects created
ChangedState* m_statep; // Shared state across visitors
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
AstNode* m_varEqnp; // Original var's equation to get var value
AstNode* m_newLvEqnp; // New var's equation to read value
AstNode* m_newRvEqnp; // New var's equation to set value
uint32_t m_detects; // # detects created
// CONSTANTS
enum MiscConsts {
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
// Ok to increase this, but may result in much slower model
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
// Ok to increase this, but may result in much slower model
};
void newChangeDet() {
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<<cvtToStr(DETECTARRAY_MAX_INDEXES)
<<" array indexes (probably with UNOPTFLAT warning suppressed): "<<m_vscp->prettyName()<<endl
<<m_vscp->warnMore()
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
return;
}
m_statep->maybeCreateChgFuncp();
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "
<<cvtToStr(DETECTARRAY_MAX_INDEXES)
<<" array indexes (probably with UNOPTFLAT warning suppressed): "
<<m_vscp->prettyName()<<endl
<<m_vscp->warnMore()
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
return;
}
m_statep->maybeCreateChgFuncp();
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
m_varEqnp->cloneTree(true),
m_newRvEqnp->cloneTree(true), false);
m_statep->m_chgFuncp->addStmtsp(changep);
m_statep->m_chgFuncp->addStmtsp(changep);
AstAssign* initp = new AstAssign(m_vscp->fileline(),
m_newLvEqnp->cloneTree(true),
m_varEqnp->cloneTree(true));
m_statep->m_chgFuncp->addFinalsp(initp);
EmitCBaseCounterVisitor visitor(initp);
m_statep->m_numStmts += visitor.count();
m_statep->m_chgFuncp->addFinalsp(initp);
EmitCBaseCounterVisitor visitor(initp);
m_statep->m_numStmts += visitor.count();
}
virtual void visit(AstBasicDType* nodep) {
newChangeDet();
newChangeDet();
}
virtual void visit(AstPackArrayDType* nodep) {
newChangeDet();
newChangeDet();
}
virtual void visit(AstUnpackArrayDType* nodep) {
for (int index=0; index < nodep->elementsConst(); ++index) {
AstNode* origVEp = m_varEqnp;
AstNode* origNLEp = m_newLvEqnp;
AstNode* origNREp = m_newRvEqnp;
for (int index=0; index < nodep->elementsConst(); ++index) {
AstNode* origVEp = m_varEqnp;
AstNode* origNLEp = m_newLvEqnp;
AstNode* origNREp = m_newRvEqnp;
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
iterate(nodep->subDTypep()->skipRefp());
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
m_varEqnp = origVEp;
m_newLvEqnp = origNLEp;
m_newRvEqnp = origNREp;
}
m_varEqnp = origVEp;
m_newLvEqnp = origNLEp;
m_newRvEqnp = origNREp;
}
}
virtual void visit(AstNodeClassDType* nodep) {
if (nodep->packedUnsup()) {
newChangeDet();
} else {
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-class-");
if (nodep->packedUnsup()) {
newChangeDet();
} else {
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
" (probably with UNOPTFLAT warning suppressed): "
<<m_vscp->varp()->prettyName());
}
<<m_vscp->varp()->prettyNameQ());
}
}
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-");
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
" (probably with UNOPTFLAT warning suppressed): "
<<m_vscp->varp()->prettyName());
<<m_vscp->varp()->prettyNameQ());
}
public:
// CONSTUCTORS
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
m_statep = statep;
m_vscp = vscp;
m_detects = 0;
{
AstVar* varp = m_vscp->varp();
string newvarname = "__Vchglast__"+m_vscp->scopep()->nameDotless()+"__"+varp->shortName();
// Create: VARREF(_last)
// ASSIGN(VARREF(_last), VARREF(var))
// ...
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
m_statep->m_topModp->addStmtp(newvarp);
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
m_statep->m_scopetopp->addVarp(m_newvscp);
m_statep = statep;
m_vscp = vscp;
m_detects = 0;
{
AstVar* varp = m_vscp->varp();
string newvarname = ("__Vchglast__"+m_vscp->scopep()->nameDotless()
+"__"+varp->shortName());
// Create: VARREF(_last)
// ASSIGN(VARREF(_last), VARREF(var))
// ...
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP,
newvarname, varp);
m_statep->m_topModp->addStmtp(newvarp);
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
m_statep->m_scopetopp->addVarp(m_newvscp);
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
}
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
}
iterate(vscp->dtypep()->skipRefp());
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
}
virtual ~ChangedInsertVisitor() {}
};
@@ -223,61 +231,63 @@ class ChangedVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstVarScope::user1() -> bool. True indicates processed
AstUser1InUse m_inuser1;
// AstVarScope::user1() -> bool. True indicates processed
AstUser1InUse m_inuser1;
// STATE
ChangedState* m_statep; // Shared state across visitors
ChangedState* m_statep; // Shared state across visitors
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void genChangeDet(AstVarScope* vscp) {
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
ChangedInsertVisitor visitor (vscp, m_statep);
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyNameQ());
ChangedInsertVisitor visitor (vscp, m_statep);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) {
UINFO(4," MOD "<<nodep<<endl);
if (nodep->isTop()) {
m_statep->m_topModp = nodep;
}
UINFO(4," MOD "<<nodep<<endl);
if (nodep->isTop()) {
m_statep->m_topModp = nodep;
}
iterateChildren(nodep);
}
virtual void visit(AstTopScope* nodep) {
UINFO(4," TS "<<nodep<<endl);
// Clearing
AstNode::user1ClearTree();
// Create the change detection function
AstScope* scopep = nodep->scopep();
if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
m_statep->m_scopetopp = scopep;
UINFO(4," TS "<<nodep<<endl);
// Clearing
AstNode::user1ClearTree();
// Create the change detection function
AstScope* scopep = nodep->scopep();
UASSERT_OBJ(scopep, nodep,
"No scope found on top level, perhaps you have no statements?");
m_statep->m_scopetopp = scopep;
// Create a wrapper change detection function that calls each change detection function
m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
m_statep->m_tlChgFuncp->symProlog(true);
m_statep->m_tlChgFuncp->declPrivate(true);
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
m_statep->maybeCreateChgFuncp();
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
// Create a wrapper change detection function that calls each change detection function
m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(),
"_change_request", scopep, "QData");
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
m_statep->m_tlChgFuncp->symProlog(true);
m_statep->m_tlChgFuncp->declPrivate(true);
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
m_statep->maybeCreateChgFuncp();
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
iterateChildren(nodep);
}
virtual void visit(AstVarScope* nodep) {
if (nodep->isCircular()) {
UINFO(8," CIRC "<<nodep<<endl);
if (!nodep->user1SetOnce()) {
genChangeDet(nodep);
}
}
if (nodep->isCircular()) {
UINFO(8," CIRC "<<nodep<<endl);
if (!nodep->user1SetOnce()) {
genChangeDet(nodep);
}
}
}
virtual void visit(AstNodeMath* nodep) {
// Short-circuit
// Short-circuit
}
//--------------------
// Default: Just iterate
@@ -288,7 +298,7 @@ private:
public:
// CONSTUCTORS
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
m_statep = statep;
m_statep = statep;
iterate(nodep);
}
virtual ~ChangedVisitor() {}
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void changedAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+105 -104
View File
@@ -20,10 +20,10 @@
// V3Clean's Transformations:
//
// Each module:
// For each math operator, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node.
// Resize operands to C++ 32/64/wide types.
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
// For each math operator, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node.
// Resize operands to C++ 32/64/wide types.
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
//
//*************************************************************************
@@ -44,12 +44,12 @@ class CleanVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
// AstNode::user2() -> bool. True indicates widthMin has been propagated
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
// AstNode::user2() -> bool. True indicates widthMin has been propagated
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
// TYPES
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
@@ -62,153 +62,154 @@ private:
// Width resetting
int cppWidth(AstNode* nodep) {
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
else return nodep->widthWords()*VL_WORDSIZE;
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
else return nodep->widthWords()*VL_WORDSIZE;
}
void setCppWidth(AstNode* nodep) {
nodep->user2(true); // Don't resize it again
AstNodeDType* old_dtypep = nodep->dtypep();
int width=cppWidth(nodep); // widthMin is unchanged
if (old_dtypep->width() != width) {
// Since any given dtype's cppWidth() is the same, we can just
// remember one convertion for each, and reuse it
nodep->user2(true); // Don't resize it again
AstNodeDType* old_dtypep = nodep->dtypep();
int width = cppWidth(nodep); // widthMin is unchanged
if (old_dtypep->width() != width) {
// Since any given dtype's cppWidth() is the same, we can just
// remember one convertion for each, and reuse it
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
nodep->dtypep(new_dtypep);
} else {
nodep->dtypeChgWidth(width, nodep->widthMin());
AstNodeDType* new_dtypep2 = nodep->dtypep();
if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed");
old_dtypep->user3p(new_dtypep2); // Remember for next time
}
}
nodep->dtypep(new_dtypep);
} else {
nodep->dtypeChgWidth(width, nodep->widthMin());
AstNodeDType* new_dtypep2 = nodep->dtypep();
UASSERT_OBJ(new_dtypep2 != old_dtypep, nodep,
"Dtype didn't change when width changed");
old_dtypep->user3p(new_dtypep2); // Remember for next time
}
}
}
void computeCppWidth(AstNode* nodep) {
if (!nodep->user2() && nodep->hasDType()) {
if (!nodep->user2() && nodep->hasDType()) {
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
} else {
setCppWidth(nodep);
}
}
} else {
setCppWidth(nodep);
}
}
}
// Store the clean state in the userp on each node
void setCleanState(AstNode* nodep, CleanState clean) {
nodep->user1(clean);
nodep->user1(clean);
}
CleanState getCleanState(AstNode* nodep) {
return static_cast<CleanState>(nodep->user1());
}
bool isClean(AstNode* nodep) {
CleanState clstate = getCleanState(nodep);
if (clstate==CS_CLEAN) return true;
if (clstate==CS_DIRTY) return false;
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
return false;
CleanState clstate = getCleanState(nodep);
if (clstate==CS_CLEAN) return true;
if (clstate==CS_DIRTY) return false;
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
return false;
}
void setClean(AstNode* nodep, bool isClean) {
computeCppWidth(nodep); // Just to be sure it's in widthMin
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,...
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); // 32,64,...
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
}
// Operate on nodes
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
UINFO(4," NeedClean "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
computeCppWidth(nodep);
V3Number mask (nodep->fileline(), cppWidth(nodep));
mask.setMask(nodep->widthMin());
UINFO(4," NeedClean "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
computeCppWidth(nodep);
V3Number mask (nodep, cppWidth(nodep));
mask.setMask(nodep->widthMin());
AstNode* cleanp = new AstAnd(nodep->fileline(),
new AstConst(nodep->fileline(), mask),
nodep);
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
relinkHandle.relink(cleanp);
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
relinkHandle.relink(cleanp);
}
void insureClean(AstNode* nodep) {
computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep);
computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep);
}
void insureCleanAndNext(AstNode* nodep) {
// Editing list, careful looping!
for (AstNode* exprp = nodep; exprp; ) {
AstNode* nextp = exprp->nextp();
insureClean(exprp);
exprp = nextp;
}
// Editing list, careful looping!
for (AstNode* exprp = nodep; exprp; ) {
AstNode* nextp = exprp->nextp();
insureClean(exprp);
exprp = nextp;
}
}
// Base type handling methods
void operandBiop(AstNodeBiop* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
//no setClean.. must do it in each user routine.
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
//no setClean.. must do it in each user routine.
}
void operandTriop(AstNodeTriop* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
if (nodep->cleanThs()) {
insureClean(nodep->thsp());
}
//no setClean.. must do it in each user routine.
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
if (nodep->cleanThs()) {
insureClean(nodep->thsp());
}
//no setClean.. must do it in each user routine.
}
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_modp = nodep;
iterateChildren(nodep);
m_modp = NULL;
m_modp = NULL;
}
virtual void visit(AstNodeUniop* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeBiop* nodep) {
operandBiop(nodep);
operandBiop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstAnd* nodep) {
operandBiop(nodep);
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
}
virtual void visit(AstXor* nodep) {
operandBiop(nodep);
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstOr* nodep) {
operandBiop(nodep);
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstNodeMath* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
computeCppWidth(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeAssign* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
computeCppWidth(nodep);
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
}
virtual void visit(AstText* nodep) {
setClean(nodep, true);
@@ -217,17 +218,17 @@ private:
setClean(nodep, true);
}
virtual void visit(AstSel* nodep) {
operandTriop(nodep);
operandTriop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstUCFunc* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
computeCppWidth(nodep);
setClean(nodep, false);
// We always clean, as we don't trust those pesky users.
// We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) {
insertClean(nodep);
}
insertClean(nodep);
}
insureCleanAndNext(nodep->bodysp());
}
virtual void visit(AstTraceDecl* nodep) {
@@ -239,32 +240,32 @@ private:
insureCleanAndNext(nodep->valuep());
}
virtual void visit(AstTypedef* nodep) {
// No cleaning, or would loose pointer to enum
// No cleaning, or would loose pointer to enum
iterateChildren(nodep);
}
virtual void visit(AstParamTypeDType* nodep) {
// No cleaning, or would loose pointer to enum
// No cleaning, or would loose pointer to enum
iterateChildren(nodep);
}
// Control flow operators
virtual void visit(AstNodeCond* nodep) {
iterateChildren(nodep);
insureClean(nodep->condp());
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
insureClean(nodep->condp());
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
}
virtual void visit(AstWhile* nodep) {
iterateChildren(nodep);
insureClean(nodep->condp());
insureClean(nodep->condp());
}
virtual void visit(AstNodeIf* nodep) {
iterateChildren(nodep);
insureClean(nodep->condp());
insureClean(nodep->condp());
}
virtual void visit(AstSFormatF* nodep) {
iterateChildren(nodep);
insureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
setClean(nodep, true); // generates a string, so not relevant
}
virtual void visit(AstUCStmt* nodep) {
iterateChildren(nodep);
@@ -280,7 +281,7 @@ private:
// Default: Just iterate
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
computeCppWidth(nodep);
}
public:
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void cleanAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
-921
View File
@@ -1,921 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity active domains
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2008-2019 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.
//
//*************************************************************************
// V3ClkGater's Transformations:
//
// Look for clock gaters
// Note there's overlap between this process and V3Split's
// ALWAYS: Build graph
// IF: Form vertex pointing to any IFs or ALWAYS above
// VARREF: Form vertex pointing to IFs it is under
// ASSIGN: If under normal assignment, disable optimization
// FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating.
// !splitable: Mark all VARREFs in this statement as comming from it
// Optimize graph so if signal is referenced under multiple IF branches it moves up
// Make ALWAYS for each new gating term, and move statements there
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Ast.h"
#include "V3Stats.h"
#include "V3Graph.h"
#include "V3ClkGater.h"
#include <algorithm>
#include <cstdarg>
#include <map>
#include <vector>
//######################################################################
// Base for debug
class GaterBaseVisitor : public AstNVisitor {
protected:
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
// Support classes
class GaterVarVertex;
class GaterVertex : public V3GraphVertex {
static uint32_t s_rankNum;
public:
explicit GaterVertex(V3Graph* graphp)
: V3GraphVertex(graphp) {
s_rankNum++; rank(s_rankNum);
}
virtual ~GaterVertex() {}
virtual int typeNum() const = 0;
static void clearRank() { s_rankNum=0; }
virtual int sortCmp(const V3GraphVertex* rhsp) const;
};
uint32_t GaterVertex::s_rankNum = 0;
class GaterHeadVertex : public GaterVertex {
public:
explicit GaterHeadVertex(V3Graph* graphp)
: GaterVertex(graphp) {}
virtual ~GaterHeadVertex() {}
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
virtual string name() const { return "*HEAD*"; }
virtual string dotColor() const { return "green"; }
};
class GaterPliVertex : public GaterVertex {
public:
explicit GaterPliVertex(V3Graph* graphp)
: GaterVertex(graphp) {}
virtual ~GaterPliVertex() {}
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
virtual string name() const { return "*PLI*"; }
virtual string dotColor() const { return "red"; }
};
class GaterIfVertex : public GaterVertex {
AstNodeIf* m_nodep;
public:
AstNodeIf* nodep() const { return m_nodep; }
GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep)
: GaterVertex(graphp), m_nodep(nodep) { }
virtual ~GaterIfVertex() {}
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
virtual string name() const {
return cvtToHex(m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
};
class GaterVarVertex : public GaterVertex {
AstVarScope* m_nodep;
public:
AstVarScope* nodep() const { return m_nodep; }
GaterVarVertex(V3Graph* graphp, AstVarScope* nodep)
: GaterVertex(graphp), m_nodep(nodep) { }
virtual ~GaterVarVertex() {}
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
virtual string name() const { return nodep()->name(); }
virtual string dotColor() const { return "skyblue"; }
};
//######################################################################
// Edge types
class GaterEdge : public V3GraphEdge {
uint32_t m_ifelse; // True branch of if
public:
// These are used as shift amounts into node's user()
#define VU_DEFINE enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8}
VU_DEFINE;
uint32_t ifelse() const { return m_ifelse; }
bool ifelseTrue() const { return m_ifelse & VU_IF; }
bool ifelseFalse() const { return m_ifelse & VU_ELSE; }
bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); }
GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse)
: V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {}
virtual ~GaterEdge() {}
virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red"
: (ifelseBoth() ? "blue"
: (ifelseTrue() ? "green"
: "darkgreen"))); }
virtual int sortCmp(const V3GraphEdge* rEdgep) const {
// Our master sort sorts by edges first, so we don't want to
// consider the upstream vertex::sortCmp when sorting by edges.
// We do want to order by something though; rank works
if (fromp()->rank() < rEdgep->fromp()->rank()) return -1;
if (fromp()->rank() > rEdgep->fromp()->rank()) return 1;
// If same, resolve by ifelse
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
if (m_ifelse < crEdgep->m_ifelse) return -1;
if (m_ifelse > crEdgep->m_ifelse) return 1;
return 0;
}
};
int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const {
const GaterVertex* crhsp = static_cast<const GaterVertex*>(rhsp);
// We really only care about ordering Var's together, but...
// First put same type together
if (typeNum() < crhsp->typeNum()) return -1;
if (typeNum() > crhsp->typeNum()) return 1;
// If variable, group by same input fanin
// (know they're the same type based on above compare)
if (dynamic_cast<const GaterVarVertex*>(this)) {
// We've already sorted by edges, so just see if same tree
// If this gets too slow, we could compute a hash up front
V3GraphEdge* lEdgep = this->inBeginp();
V3GraphEdge* rEdgep = rhsp->inBeginp();
while (lEdgep && rEdgep) {
const GaterEdge* clEdgep = static_cast<const GaterEdge*>(lEdgep);
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1;
if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1;
if (clEdgep->ifelse() < crEdgep->ifelse()) return -1;
if (clEdgep->ifelse() > crEdgep->ifelse()) return 1;
lEdgep = lEdgep->inNextp();
rEdgep = rEdgep->inNextp();
}
if (!lEdgep && !rEdgep) return 0;
return lEdgep ? -1 : 1;
}
// Finally by rank of this vertex
if (rank() < rhsp->rank()) return -1;
if (rank() > rhsp->rank()) return 1;
return 0;
}
//######################################################################
// Check for non-simple gating equations
class GaterCondVisitor : public GaterBaseVisitor {
private:
// RETURN STATE
bool m_isSimple; // Set false when we know it isn't simple
// METHODS
inline void okIterate(AstNode* nodep) {
if (m_isSimple) iterateChildren(nodep);
}
// VISITORS
virtual void visit(AstOr* nodep) { okIterate(nodep); }
virtual void visit(AstAnd* nodep) { okIterate(nodep); }
virtual void visit(AstNot* nodep) { okIterate(nodep); }
virtual void visit(AstLogOr* nodep) { okIterate(nodep); }
virtual void visit(AstLogAnd* nodep) { okIterate(nodep); }
virtual void visit(AstLogNot* nodep) { okIterate(nodep); }
virtual void visit(AstVarRef* nodep) { okIterate(nodep); }
// Other possibilities are equals, etc
// But, we don't want to get too complicated or it will take too much
// effort to calculate the gater
virtual void visit(AstNode* nodep) {
m_isSimple = false;
//iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit GaterCondVisitor(AstNode* nodep) {
m_isSimple = true;
iterate(nodep);
}
virtual ~GaterCondVisitor() {}
// PUBLIC METHODS
bool isSimple() const { return m_isSimple; }
};
//######################################################################
// Check for non-simple gating equations
class GaterBodyVisitor : public GaterBaseVisitor {
// NODE STATE
// Input state
// AstVarScope::user2p() -> AstAlways* moving this variable to
enum State {
// This is used as a bitmask
STATE_UNKNOWN=0,
STATE_KEEP=1, // Seen a variable we need, keep this statement
STATE_DELETE=2 // Seen a variable we need, delete this statement
// 3=keep & delete
};
bool m_original; // Deleting original statements, vs deleting new var statements
AstNode* m_exprp; // New gater expression we are building
bool m_cloning; // Clone this object 0=not sure yet, 1=do
uint32_t m_state; // Parsing state
// VISITORS
virtual void visit(AstVarRef* nodep) {
if (nodep->lvalue()) {
AstVarScope* vscp = nodep->varScopep();
if (vscp->user2p() == m_exprp) {
// This variable's block needs to move to the new always
if (m_original) {
UINFO(9," VARREF delete in old: "<<nodep<<endl);
m_state |= STATE_DELETE;
} else {
UINFO(9," VARREF stays in new: "<<nodep<<endl);
m_state |= STATE_KEEP;
}
} else {
if (m_original) {
UINFO(9," VARREF other stays in old\n");
m_state |= STATE_KEEP;
} else {
UINFO(9," VARREF other delete in new\n");
m_state |= STATE_DELETE;
}
}
}
}
//virtual void visit(AstNodeIf* nodep) { ... }
// Not needed, it's the same handling as any other statement. Cool, huh?
// (We may get empty IFs but the constant propagater will rip them up for us)
virtual void visit(AstNodeStmt* nodep) {
if (!nodep->isStatement()) {
iterateChildren(nodep);
return;
}
uint32_t oldstate = m_state;
// Find if children want to delete this or not.
// Note children may bicker, and want to both keep and delete (branches on a if)
uint32_t childstate;
{
m_state = STATE_UNKNOWN;
iterateChildren(nodep);
childstate = m_state;
}
m_state = oldstate;
UINFO(9," Did state="<<childstate<<" "<<nodep<<endl);
// Indeterminate (statement with no lvalue), so keep only in original
if (childstate == STATE_UNKNOWN) {
if (m_original) childstate |= STATE_KEEP;
else childstate |= STATE_DELETE;
}
if ((childstate & STATE_DELETE) && !(childstate & STATE_KEEP)) {
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
// Pass upwards we did delete
m_state |= STATE_DELETE;
} else {
// Pass upwards we must keep
m_state |= STATE_KEEP;
}
}
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
}
public:
// CONSTUCTORS
GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) {
m_exprp = exprp;
m_original = original;
m_state = STATE_UNKNOWN;
m_cloning = false;
if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: ");
iterateAndNextNull(nodep->bodysp());
if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: ");
// If there's no statements we shouldn't have had a resulting graph
// vertex asking for this creation
}
virtual ~GaterBodyVisitor() {}
};
//######################################################################
// Create clock gaters
class GaterVisitor : public GaterBaseVisitor {
// NODE STATE
// Cleared on Always
// AstVarScope::user1p() -> GaterVarVertex*
// AstAlways::user4() -> bool. True indicates processed
// Cleared on each new Always
// AstVarScope::user2p() -> AstAlways* moving this variable to
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser4InUse m_inuser4;
// GRAPH STATE
// Cleared on simplify
// Vertex::user() -> VarUsage: Mark of which if/else edges are hit
// TYPES
VU_DEFINE;
enum MiscConsts {
IF_DEPTH_MAX = 4, // IFs deep we bother to analyze
DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup
};
// MEMBERS
string m_nonopt; // Reason block is not optimizable
V3Double0 m_statGaters; // Statistic tracking
V3Double0 m_statBits; // Statistic tracking
bool m_directlyUnderAlw; // Immediately under Always or If
int m_ifDepth; // Depth of IF statements
int m_numIfs; // Number of IF statements
V3Graph m_graph; // Scoreboard of var usages/dependencies
GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering
GaterHeadVertex* m_headVertexp; // Top vertex
V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree
uint32_t m_aboveTrue; // Vertex above this point is true branch
AstVarScope* m_stmtVscp; // Current statement had variable assigned
bool m_stmtInPli; // Current statement has PLI
// METHODS
void nonOptimizable(AstNode* nodep, const char* reasonp) {
if (m_nonopt=="") {
UINFO(9," Nonopt: "<<reasonp<<": "<<nodep<<endl);
m_nonopt = reasonp;
}
}
void scoreboardPli(AstNode* nodep) {
// Order all PLI statements with other PLI statements
// This ensures $display's and such remain in proper order
// We don't prevent splitting out other non-pli statements, however,
// because it is common to have $uasserts sprinkled about.
if (!m_pliVertexp) {
m_pliVertexp = new GaterPliVertex(&m_graph);
}
if (m_stmtVscp) { // Already saw a variable, be sure to mark it!
GaterVarVertex* varVtxp = reinterpret_cast<GaterVarVertex*>(m_stmtVscp->user1p());
new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI);
}
m_stmtInPli = true; // Mark all followon variables too
}
// METHODS -- GRAPH stuff
void simplifyGraph() {
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false);
// We now have all PLI variables with edges FROM the pli vertex
simplifyPli();
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false);
// Any vertex that points along both true & false path to variable
// can be simplfied so parent points to that vertex. Any vertex
// that points to a (great...) grandparent of a variable can just
// point to the edge.
m_graph.userClearVertices(); // user() will contain VarUsage
simplifyIfElseRecurse(m_headVertexp);
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false);
m_graph.userClearVertices(); // user() will contain VarUsage
simplifyGrandRecurse(m_headVertexp, 1);
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false);
simplifyRemoveUngated(m_headVertexp);
// Give all signals with same gating term same color
graphColorSameFeeder();
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false);
}
void simplifyPli() {
// For now, we'll not gate any logic with PLI lvariables in it. In
// the future we may move PLI statements. One way to do so is to
// all below pli VARs to go IfVertex -> PliVertex -> VarVertex then
// they all must get colored the same. There may be a lot of
// duplicated edges to the PLI; they'll need cleanup. All of the
// later optimizations need to deal, and the GaterBodyVisitor needs
// to know how to move them.
if (m_pliVertexp) {
// Follow PLI out edges to find all relevant variables
for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) {
nextp = edgep->outNextp(); // We may edit the list
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep);
} else {
m_graph.dump();
v3fatalSrc("PLI vertex points to non-signal");
}
}
m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL;
}
}
void simplifyIfElseRecurse(V3GraphVertex* vertexp) {
// From bottom-up, propagate duplicate IF/ELSE branches to grandparent
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
simplifyIfElseRecurse(edgep->top());
}
//UINFO(9,"IERecurse "<<vertexp<<endl);
if (dynamic_cast<GaterIfVertex*>(vertexp)) {
// Clear indications
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->top()->user(VU_NONE);
}
// Mark nodes on to/from side
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
V3GraphVertex* toVxp = edgep->top();
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
// We may mark this twice in one pass - if so it's duplicated; no worries
if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF);
if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE);
//UINFO(9," mark "<<edgep<<" "<<toVxp<<endl);
}
// Any with both IF & ELSE mark get removal mark, and new parent edge
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
nextp = edgep->outNextp(); // We may edit the list
V3GraphVertex* toVxp = edgep->top();
//UINFO(9," to "<<toVxp->user()<<" "<<toVxp<<endl);
if ((toVxp->user() & VU_IF) && (toVxp->user() & VU_ELSE)) {
edgep->unlinkDelete(); VL_DANGLING(edgep);
if (!(toVxp->user() & VU_MADE)) { // Make an edge only once
toVxp->user(toVxp->user() | VU_MADE);
GaterEdge* inedgep = static_cast<GaterEdge*>(vertexp->inBeginp());
V3GraphVertex* grandparent = inedgep->fromp();
new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse());
}
}
}
}
}
void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) {
// From top-down delete any vars that grandparents source
// IE A -> B -> C -> VAR
// \----------^
//UINFO(9,"GRecurse "<<depth<<" "<<vertexp<<endl);
// Mark all variables to be swept
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp(); // We may edit the list
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
if (toVxp->user() && toVxp->user() < depth) {
// A recursion "above" us marked it,
// Remove this edge, it's redundant with the upper edge
edgep->unlinkDelete(); VL_DANGLING(edgep);
} else {
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
if (cedgep->ifelseBoth()) {
toVxp->user(depth);
}
}
}
}
// Recurse
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
simplifyGrandRecurse(edgep->top(), depth+1);
}
// Clean our marks
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
V3GraphVertex* toVxp = edgep->top();
if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it
} else {
toVxp->user(0); // We marked it originally, so unmark now
}
}
// Delete any If nodes with no children
// Last, as want bottom-up cleanup
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp(); // We may edit the list
if (GaterIfVertex* toVxp = dynamic_cast<GaterIfVertex*>(edgep->top())) {
if (!toVxp->outBeginp()) {
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
}
}
}
}
}
void simplifyRemoveUngated(V3GraphVertex* vertexp) {
// Remove variables that are ungated
// At this point, any variable under the head is ungated
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp(); // We may edit the list
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
}
}
}
}
void graphColorSameFeeder() {
// Assign same color to all destination vertices that have same
// subgraph feeding into them
// (I.E. all "from" nodes are common within each color)
// We could hash, but instead it's faster to sort edges, so we know
// the same edge list is always adjacent. The result is a
// O(vertices*edges) loop, but we'd need that to hash too.
if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); }
m_graph.sortEdges();
// Now sort vertices, so same inbound edge set ends up adjacent
m_graph.sortVertices();
// Now walk and assign colors; same color is adjacent
m_graph.clearColors();
m_graph.userClearEdges(); // Used by newExprFromGraph
uint32_t color = 1;
GaterVarVertex* lastVxp = NULL;
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
if (!vVxp->inBeginp()) {
// At this point, any variable not linked is an error
// (It should have at least landed under the Head node)
vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection");
}
if (!lastVxp || vVxp->sortCmp(lastVxp)) {
// Different sources for this new node
color++;
}
vVxp->color(color);
lastVxp = vVxp;
}
}
if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); }
}
AstNode* newExprFromGraph(GaterVarVertex* vertexp) {
// Recurse backwards, then form equation on return path
// We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation
// We get equations like "a | (!a & b)" which obviously could be reduced here,
// instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up
static uint32_t s_generation = 0;
++s_generation;
nafgMarkRecurse(vertexp, s_generation);
AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation);
if (debug()>=9) nodep->dumpTree(cout," GateExpr: ");
return nodep;
}
void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
// Backwards mark user() on the path we recurse
//UINFO(9," nafgMark: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
//UINFO(9," nafgMark: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
edgep->user(generation);
nafgMarkRecurse(edgep->fromp(), generation);
}
}
AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) {
// Forewards follow user() marked previously and build tree
AstNode* nodep = NULL;
// OR across all edges found at this level
//UINFO(9," nafgEnter: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
if (edgep->user() == generation) {
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
AstNode* eqnp = NULL;
//UINFO(9," nafgFollow: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
// Just OR in all lower terms
eqnp = nafgCreateRecurse(edgep->top(), generation);
} else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
// Edges from IFs represent a real IF branch in the equation tree
//UINFO(9," ifver "<<cvtToHex(edgep)<<" cc"<<edgep->dotColor()<<endl);
eqnp = cVxp->nodep()->condp()->cloneTree(true);
if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition");
if (cedgep->ifelseFalse()) {
eqnp = new AstNot(eqnp->fileline(),eqnp);
}
// We need to AND this term onto whatever was found below it
AstNode* belowp = nafgCreateRecurse(edgep->top(), generation);
if (belowp) eqnp = new AstAnd(eqnp->fileline(),eqnp,belowp);
}
// Top level we could choose to make multiple gaters, or ORs under the gater
// Right now we'll put OR lower down and let other optimizations deal
if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp);
else nodep = eqnp;
//if (debug()>=9) nodep->dumpTree(cout," followExpr: ");
}
}
//UINFO(9," nafgExit: "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
return nodep;
}
void newAlwaysTrees(AstAlways* nodep) {
// Across all variables we're moving
uint32_t lastColor = 0;
AstNode* lastExprp = NULL;
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
if (!lastExprp || lastColor != vVxp->color()) {
lastColor = vVxp->color();
// Create the block we've just finished
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below
// New expression for this color
lastExprp = newExprFromGraph(vVxp);
}
// Mark variable to move
if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters");
vVxp->nodep()->user2p(lastExprp);
m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more!
// There shouldn't be two possibilities we want to
// move to, IE {A,B} <= Z because we marked such
// things as unoptimizable
}
}
// Create the final block we've just finished
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above
}
void newAlwaysTree(AstAlways* nodep, AstNode* exprp) {
// Create new always with specified gating expression
// Relevant vars are already marked with user2p
// Should we put the gater under the always itself,
// or make a new signal?
// New signal: May cause replicated logic until we can combine
// Need way to toggle signal on complicated pos/negedge
// Under Always: More complicated, may not propagate logic with gateer
// Untill we get better gate optimization, we'll go this route.
//#define GATER_NOP
#ifdef GATER_NOP
// For testing only, don't clock gate but still make new always
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
#else
// Make a SenGate
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
new AstSenGate(nodep->fileline(),
oldsenitemsp->cloneTree(true),
exprp));
#endif
// Make new body; note clone will preserve user2p() indicating moving vars
AstNode* bodyp = nodep->bodysp()->cloneTree(true);
AstAlways* alwp = new AstAlways(nodep->fileline(),
nodep->keyword(),
sensesp,
bodyp);
alwp->user4(1); // No need to process the new always again!
nodep->addNextHere(alwp);
// Blow moved statements from old body
{ GaterBodyVisitor vis(nodep,exprp,true); }
// Blow old statements from new body
{ GaterBodyVisitor vis(alwp,exprp,false); }
++m_statGaters;
if (debug()>=9) alwp->dumpTree(cout," new: ");
}
// VISITORS
virtual void visit(AstAlways* nodep) {
if (debug()>=9) cout<<endl<<endl<<endl;
UINFO(5, "Gater: ALWAYS: "<<nodep<<endl);
if (nodep->user4SetOnce()) return;
clear();
if (debug()>=9) nodep->dumpTree(cout," Alwin: ");
// Look for constructs we can't optimize
// Form graph with Vertices at each IF, and each Variable
m_aboveTrue = VU_IF | VU_ELSE;
iterateChildrenAlw(nodep, true);
// Other reasons to not optimize
if (!m_numIfs) nonOptimizable(nodep, "No if statements");
// Something to optimize, oh my!
if (m_nonopt!="") {
UINFO(5, " Gater non-opt: "<<m_nonopt<<endl);
} else {
// Process it
simplifyGraph();
// See how much moves
uint32_t lastColor = 0;
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
if (lastColor < vVxp->color()) {
lastColor = vVxp->color();
}
}
}
if (lastColor == 0) { // Nothing we moved!
nonOptimizable(nodep, "Nothing moved");
}
else if (lastColor > DOMAINS_MAX) {
// Our move algorithm is fairly slow and if we're splitting
// up too much it'll get really nasty. It's probably a bad
// move for performance to split too much, anyhow, as the
// number of gaters will result in calling many small c functions.
nonOptimizable(nodep, "Too much moved");
}
if (m_nonopt=="") {
newAlwaysTrees(nodep);
if (debug()>=9) nodep->dumpTree(cout," Gaterout: ");
}
}
UINFO(5, " Gater done"<<endl);
}
virtual void visit(AstVarRef* nodep) {
if (nodep->lvalue()) {
AstVarScope* vscp = nodep->varScopep();
if (nodep->varp()->isSigPublic()) {
// Public signals shouldn't be changed, pli code might be messing with them
scoreboardPli(nodep);
}
// If another lvalue in this node, give up optimizing.
// We could just not optimize this variable, but we've already marked the
// other variable as optimizable, so we can instead pretend it's a PLI node.
if (m_stmtVscp) {
UINFO(5, " Multiple lvalues in one statement: "<<nodep<<endl);
scoreboardPli(nodep); // This will set m_stmtInPli
}
m_stmtVscp = vscp;
// Find, or make new Vertex
GaterVarVertex* vertexp = reinterpret_cast<GaterVarVertex*>(vscp->user1p());
if (!vertexp) {
vertexp = new GaterVarVertex(&m_graph, vscp);
vscp->user1p(vertexp);
}
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
if (m_stmtInPli) {
new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI);
}
}
}
virtual void visit(AstNodeIf* nodep) {
m_ifDepth++;
bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX;
if (allowGater) {
GaterCondVisitor condVisitor(nodep->condp());
if (!condVisitor.isSimple()) {
// Don't clear optimization, simply drop this IF as part of the gating
UINFO(5," IFnon-simple-condition: "<<nodep<<endl);
allowGater = false;
}
}
if (!allowGater) {
// IF burried under something complicated
iterateChildrenAlw(nodep, true);
} else {
UINFO(5, " IF: "<<nodep<<endl);
m_numIfs++;
// Recurse to next level of if.
V3GraphVertex* lastabovep = m_aboveVertexp;
uint32_t lasttrue = m_aboveTrue;
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
{
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
}
{
m_aboveVertexp = vertexp; // Vars will point at this edge
m_aboveTrue = VU_IF;
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
}
{
m_aboveVertexp = vertexp; // Vars will point at this edge
m_aboveTrue = VU_ELSE;
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
}
m_aboveVertexp = lastabovep;
m_aboveTrue = lasttrue;
}
m_ifDepth--;
}
virtual void visit(AstAssignDly* nodep) {
// iterateChildrenAlw will detect this is a statement for us
iterateChildrenAlw(nodep, false);
}
virtual void visit(AstNodeAssign* nodep) {
// Note NOT AssignDly; handled above, We'll just mark this block as
// not optimizable.
//
// A future alternative is to look for any lvalues that are also
// variables in the sensitivity list, or rvalues in this block. If
// any hit, disable optimization. Unlikely to be useful (For loops
// being an exception, but they're already unrolled.)
nonOptimizable(nodep, "Non-delayed assignment");
// No reason to iterate.
}
virtual void visit(AstSenItem* nodep) {
if (!nodep->isClocked()) {
nonOptimizable(nodep, "Non-clocked sensitivity");
}
iterateChildrenAlw(nodep, false);
}
//--------------------
virtual void visit(AstNode* nodep) {
if (m_nonopt=="") { // Else accelerate
iterateChildrenAlw(nodep, false);
}
}
inline void iterateChildrenAlw(AstNode* nodep, bool under) {
// **** USE THIS INSTEAD OF iterateChildren!
// Note If visitor doesn't call here; does it its own way
bool lastdua = m_directlyUnderAlw;
AstVarScope* lastvscp = m_stmtVscp;
bool lastpli = m_stmtInPli;
m_directlyUnderAlw = under;
if (VN_IS(nodep, NodeStmt)) { // Restored below
UINFO(9," Stmt: "<<nodep<<endl);
m_stmtVscp = NULL;
m_stmtInPli = false;
}
if (!nodep->isPure() || nodep->isBrancher()) {
// May also be a new statement (above if); if so we mark it immediately
UINFO(9," NotPure "<<nodep<<endl);
scoreboardPli(nodep);
}
{
iterateChildren(nodep);
}
m_directlyUnderAlw = lastdua;
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
m_stmtVscp = lastvscp;
m_stmtInPli = lastpli;
}
}
public:
// CONSTUCTORS
explicit GaterVisitor(AstNetlist* nodep) {
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
clear();
iterate(nodep);
}
void clear() {
m_nonopt = "";
m_directlyUnderAlw = false;
m_ifDepth = 0;
m_numIfs = 0;
AstNode::user1ClearTree();
AstNode::user2ClearTree();
// Prepare graph
m_graph.clear();
GaterVertex::clearRank();
m_pliVertexp = NULL;
m_headVertexp = new GaterHeadVertex(&m_graph);
m_aboveVertexp = m_headVertexp;
m_aboveTrue = false;
m_stmtVscp = NULL;
m_stmtInPli = false;
}
virtual ~GaterVisitor() {
V3Stats::addStat("Optimizations, Gaters inserted", m_statGaters);
V3Stats::addStat("Optimizations, Gaters impacted bits", m_statBits);
}
};
//######################################################################
// Active class functions
void V3ClkGater::clkGaterAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
{
// While the gater does well at some modules, it seems to slow down many others
UINFO(5,"ClkGater is disabled due to performance issues\n");
} // Destruct before checking
//GaterVisitor visitor (nodep);
V3Global::dumpCheckGlobalTree("clkgater", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
-37
View File
@@ -1,37 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into clock gated blocks
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2019 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 _V3CLKGATER_H_
#define _V3CLKGATER_H_ 1
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3ClkGater {
public:
static void clkGaterAll(AstNetlist* nodep);
};
#endif // Guard
+275 -267
View File
@@ -22,11 +22,11 @@
// Top Scope:
// Check created ACTIVEs
// Compress adjacent ACTIVEs with same sensitivity list
// Form master _eval function
// Add around the SENTREE a (IF POSEDGE(..))
// Add a __Vlast_{clock} for the comparison
// Set the __Vlast_{clock} at the end of the block
// Replace UNTILSTABLEs with loops until specified signals become const.
// Form master _eval function
// Add around the SENTREE a (IF POSEDGE(..))
// Add a __Vlast_{clock} for the comparison
// Set the __Vlast_{clock} at the end of the block
// Replace UNTILSTABLEs with loops until specified signals become const.
// Create global calling function for any per-scope functions. (For FINALs).
//
//*************************************************************************
@@ -49,22 +49,22 @@ class ClockVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each Module:
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
AstUser1InUse m_inuser1;
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
AstUser1InUse m_inuser1;
// TYPES
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
// STATE
AstNodeModule* m_modp; // Current module
AstTopScope* m_topScopep; // Current top scope
AstScope* m_scopep; // Current scope
AstCFunc* m_evalFuncp; // Top eval function we are creating
AstCFunc* m_initFuncp; // Top initial function we are creating
AstCFunc* m_finalFuncp; // Top final function we are creating
AstCFunc* m_settleFuncp; // Top settlement function we are creating
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
AstIf* m_lastIfp; // Last sensitivity if active to add more under
AstNodeModule* m_modp; // Current module
AstTopScope* m_topScopep; // Current top scope
AstScope* m_scopep; // Current scope
AstCFunc* m_evalFuncp; // Top eval function we are creating
AstCFunc* m_initFuncp; // Top initial function we are creating
AstCFunc* m_finalFuncp; // Top final function we are creating
AstCFunc* m_settleFuncp; // Top settlement function we are creating
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
AstIf* m_lastIfp; // Last sensitivity if active to add more under
AstMTaskBody* m_mtaskBodyp; // Current mtask body
// METHODS
@@ -72,15 +72,18 @@ private:
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
AstVar* varp = vscp->varp();
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName());
string newvarname = (string("__Vclklast__")+vscp->scopep()->nameDotless()+"__"+varp->name());
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
AstVar* varp = vscp->varp();
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "
<<varp->prettyNameQ());
string newvarname = (string("__Vclklast__")
+vscp->scopep()->nameDotless()+"__"+varp->name());
AstVar* newvarp = new AstVar(vscp->fileline(),
AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
newvarp->noReset(true); // Reset by below assign
m_modp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
vscp->user1p(newvscp);
m_scopep->addVarp(newvscp);
m_modp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
vscp->user1p(newvscp);
m_scopep->addVarp(newvscp);
// Add init
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
@@ -88,243 +91,246 @@ private:
new AstVarRef(newvarp->fileline(), newvscp, true),
fromp);
addToInitial(newinitp);
// At bottom, assign them
AstAssign* finalp
// At bottom, assign them
AstAssign* finalp
= new AstAssign(vscp->fileline(),
new AstVarRef(vscp->fileline(), newvscp, true),
new AstVarRef(vscp->fileline(), vscp, false));
m_evalFuncp->addFinalsp(finalp);
//
UINFO(4,"New Last: "<<newvscp<<endl);
return newvscp;
m_evalFuncp->addFinalsp(finalp);
//
UINFO(4,"New Last: "<<newvscp<<endl);
return newvscp;
}
AstNode* createSenItemEquation(AstSenItem* nodep) {
// We know the var is clean, and one bit, so we use binary ops
// for speed instead of logical ops.
// POSEDGE: var & ~var_last
// NEGEDGE: ~var & var_last
// BOTHEDGE: var ^ var_last
// HIGHEDGE: var
// LOWEDGE: ~var
AstNode* newp = NULL;
if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) {
if (!v3Global.opt.bboxUnsup()) {
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
}
return NULL;
}
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstAnd(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false),
new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
lastVscp, false)));
} else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstAnd(nodep->fileline(),
new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false)),
new AstVarRef(nodep->fileline(), lastVscp, false));
} else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstXor(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false),
new AstVarRef(nodep->fileline(), lastVscp, false));
} else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) {
newp = new AstVarRef(nodep->fileline(),
clkvscp, false);
} else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) {
newp = new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
clkvscp, false));
} else {
nodep->v3fatalSrc("Bad edge type");
}
return newp;
// We know the var is clean, and one bit, so we use binary ops
// for speed instead of logical ops.
// POSEDGE: var & ~var_last
// NEGEDGE: ~var & var_last
// BOTHEDGE: var ^ var_last
// HIGHEDGE: var
// LOWEDGE: ~var
AstNode* newp = NULL;
if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) {
if (!v3Global.opt.bboxUnsup()) {
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
}
return NULL;
}
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstAnd(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false),
new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
lastVscp, false)));
} else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstAnd(nodep->fileline(),
new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false)),
new AstVarRef(nodep->fileline(), lastVscp, false));
} else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
newp = new AstXor(nodep->fileline(),
new AstVarRef(nodep->fileline(),
nodep->varrefp()->varScopep(), false),
new AstVarRef(nodep->fileline(), lastVscp, false));
} else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) {
newp = new AstVarRef(nodep->fileline(),
clkvscp, false);
} else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) {
newp = new AstNot(nodep->fileline(),
new AstVarRef(nodep->fileline(),
clkvscp, false));
} else {
nodep->v3fatalSrc("Bad edge type");
}
return newp;
}
AstNode* createSenGateEquation(AstSenGate* nodep) {
AstNode* newp = new AstAnd(nodep->fileline(),
createSenseEquation(nodep->sensesp()),
nodep->rhsp()->cloneTree(true));
return newp;
AstNode* newp = new AstAnd(nodep->fileline(),
createSenseEquation(nodep->sensesp()),
nodep->rhsp()->cloneTree(true));
return newp;
}
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
// Nodep may be a list of elements; we need to walk it
AstNode* senEqnp = NULL;
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
AstNode* senOnep = NULL;
// Nodep may be a list of elements; we need to walk it
AstNode* senEqnp = NULL;
for (AstNodeSenItem* senp = nodesp; senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) {
AstNode* senOnep = NULL;
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
senOnep = createSenItemEquation(itemp);
senOnep = createSenItemEquation(itemp);
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
senOnep = createSenGateEquation(itemp);
} else {
senp->v3fatalSrc("Strange node under sentree");
}
if (senEqnp) {
// Add new OR to the sensitivity list equation
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
} else {
senEqnp = senOnep;
}
}
return senEqnp;
senOnep = createSenGateEquation(itemp);
} else {
senp->v3fatalSrc("Strange node under sentree");
}
if (senEqnp) {
// Add new OR to the sensitivity list equation
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
} else {
senEqnp = senOnep;
}
}
return senEqnp;
}
AstIf* makeActiveIf(AstSenTree* sensesp) {
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
return (newifp);
return (newifp);
}
void clearLastSen() {
m_lastSenp = NULL;
m_lastIfp = NULL;
m_lastSenp = NULL;
m_lastIfp = NULL;
}
// VISITORS
virtual void visit(AstTopScope* nodep) {
UINFO(4," TOPSCOPE "<<nodep<<endl);
m_topScopep=nodep;
m_scopep = nodep->scopep();
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
//VV***** We reset all user1p()
AstNode::user1ClearTree();
// Make top functions
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_evalFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_initFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
funcp->skipDecl(true);
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(false);
funcp->entryPoint(true);
funcp->addInitsp(new AstCStmt(nodep->fileline(),
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
m_scopep->addActivep(funcp);
m_finalFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(true);
funcp->symProlog(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_settleFuncp = funcp;
}
// Process the activates
UINFO(4," TOPSCOPE "<<nodep<<endl);
m_topScopep = nodep;
m_scopep = nodep->scopep();
UASSERT_OBJ(m_scopep, nodep,
"No scope found on top level, perhaps you have no statements?");
//VV***** We reset all user1p()
AstNode::user1ClearTree();
// Make top functions
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_evalFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_initFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep);
funcp->skipDecl(true);
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(false);
funcp->entryPoint(true);
funcp->addInitsp(new AstCStmt
(nodep->fileline(),
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
funcp->addInitsp(new AstCStmt(nodep->fileline(),
EmitCBaseVisitor::symTopAssign()+"\n"));
m_scopep->addActivep(funcp);
m_finalFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(true);
funcp->symProlog(true);
funcp->entryPoint(true);
m_scopep->addActivep(funcp);
m_settleFuncp = funcp;
}
// Process the activates
iterateChildren(nodep);
// Done, clear so we can detect errors
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
clearLastSen();
m_topScopep=NULL;
m_scopep = NULL;
// Done, clear so we can detect errors
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
clearLastSen();
m_topScopep = NULL;
m_scopep = NULL;
}
virtual void visit(AstNodeModule* nodep) {
//UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
//UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
iterateChildren(nodep);
m_modp= NULL;
m_modp= NULL;
}
virtual void visit(AstScope* nodep) {
//UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
//UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
iterateChildren(nodep);
if (AstNode* movep = nodep->finalClksp()) {
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
movep->unlinkFrBackWithNext();
m_evalFuncp->addFinalsp(movep);
}
m_scopep = NULL;
if (AstNode* movep = nodep->finalClksp()) {
UASSERT_OBJ(m_topScopep, nodep, "Final clocks under non-top scope");
movep->unlinkFrBackWithNext();
m_evalFuncp->addFinalsp(movep);
}
m_scopep = NULL;
}
virtual void visit(AstAlways* nodep) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstAlwaysPost* nodep) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstCoverToggle* nodep) {
//nodep->dumpTree(cout,"ct:");
//COVERTOGGLE(INC, ORIG, CHANGE) ->
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
AstNode* incp = nodep->incp()->unlinkFrBack();
AstNode* origp = nodep->origp()->unlinkFrBack();
AstNode* changep = nodep->changep()->unlinkFrBack();
AstIf* newp = new AstIf(nodep->fileline(),
new AstXor(nodep->fileline(),
origp,
changep),
incp, NULL);
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though verus a potential memory miss.
// We'll go with the miss.
newp->addIfsp(new AstAssign(nodep->fileline(),
changep->cloneTree(false),
origp->cloneTree(false)));
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
//nodep->dumpTree(cout, "ct:");
//COVERTOGGLE(INC, ORIG, CHANGE) ->
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
AstNode* incp = nodep->incp()->unlinkFrBack();
AstNode* origp = nodep->origp()->unlinkFrBack();
AstNode* changep = nodep->changep()->unlinkFrBack();
AstIf* newp = new AstIf(nodep->fileline(),
new AstXor(nodep->fileline(),
origp,
changep),
incp, NULL);
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though verus a potential memory miss.
// We'll go with the miss.
newp->addIfsp(new AstAssign(nodep->fileline(),
changep->cloneTree(false),
origp->cloneTree(false)));
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstInitial* nodep) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstCFunc* nodep) {
iterateChildren(nodep);
// Link to global function
if (nodep->formCallTree()) {
UINFO(4, " formCallTree "<<nodep<<endl);
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
callp->argTypes("vlSymsp");
m_finalFuncp->addStmtsp(callp);
}
// Link to global function
if (nodep->formCallTree()) {
UINFO(4, " formCallTree "<<nodep<<endl);
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
callp->argTypes("vlSymsp");
m_finalFuncp->addStmtsp(callp);
}
}
virtual void visit(AstSenTree* nodep) {
// Delete it later; Actives still pointing to it
nodep->unlinkFrBack();
pushDeletep(nodep);
// Delete it later; Actives still pointing to it
nodep->unlinkFrBack();
pushDeletep(nodep);
}
void addToEvalLoop(AstNode* stmtsp) {
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
@@ -336,18 +342,19 @@ private:
m_initFuncp->addStmtsp(stmtsp); // add to top level function
}
virtual void visit(AstActive* nodep) {
// Careful if adding variables here, ACTIVES can be under other ACTIVES
// Need to save and restore any member state in AstUntilStable block
if (!m_topScopep || !nodep->stmtsp()) {
// Not at the top or empty block...
// Only empty blocks should be leftover on the non-top. Killem.
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
// Careful if adding variables here, ACTIVES can be under other ACTIVES
// Need to save and restore any member state in AstUntilStable block
if (!m_topScopep || !nodep->stmtsp()) {
// Not at the top or empty block...
// Only empty blocks should be leftover on the non-top. Killem.
UASSERT_OBJ(!nodep->stmtsp(), nodep, "Non-empty lower active");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else if (m_mtaskBodyp) {
UINFO(4," TR ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
UASSERT_OBJ(!nodep->hasInitial(), nodep,
"Initial block should not have clock sensitivity");
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
UINFO(4," sameSenseTree\n");
} else {
@@ -367,39 +374,40 @@ private:
m_mtaskBodyp->addStmtsp(stmtsp);
}
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else {
UINFO(4," ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
// Remember the latest sensitivity so we can compare it next time
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
UINFO(4," sameSenseTree\n");
} else {
clearLastSen();
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
addToEvalLoop(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
} else if (nodep->hasInitial()) {
// Don't need to: clearLastSen();, as we're adding it to different cfunc
// Move statements to function
addToInitial(stmtsp);
} else if (nodep->hasSettle()) {
// Don't need to: clearLastSen();, as we're adding it to different cfunc
// Move statements to function
addToSettleLoop(stmtsp);
} else {
// Combo
clearLastSen();
// Move statements to function
addToEvalLoop(stmtsp);
}
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
} else {
UINFO(4," ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
// Remember the latest sensitivity so we can compare it next time
UASSERT_OBJ(!nodep->hasInitial(), nodep,
"Initial block should not have clock sensitivity");
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
UINFO(4," sameSenseTree\n");
} else {
clearLastSen();
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
addToEvalLoop(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
} else if (nodep->hasInitial()) {
// Don't need to: clearLastSen();, as we're adding it to different cfunc
// Move statements to function
addToInitial(stmtsp);
} else if (nodep->hasSettle()) {
// Don't need to: clearLastSen();, as we're adding it to different cfunc
// Move statements to function
addToSettleLoop(stmtsp);
} else {
// Combo
clearLastSen();
// Move statements to function
addToEvalLoop(stmtsp);
}
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
}
virtual void visit(AstExecGraph* nodep) {
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
@@ -426,16 +434,16 @@ public:
// CONSTUCTORS
explicit ClockVisitor(AstNetlist* nodep) {
m_modp = NULL;
m_evalFuncp = NULL;
m_evalFuncp = NULL;
m_initFuncp = NULL;
m_finalFuncp = NULL;
m_settleFuncp = NULL;
m_topScopep = NULL;
m_lastSenp = NULL;
m_lastIfp = NULL;
m_scopep = NULL;
m_lastIfp = NULL;
m_scopep = NULL;
m_mtaskBodyp = NULL;
//
//
iterate(nodep);
// Allow downstream modules to find _eval()
// easily without iterating through the tree.
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void clockAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+276 -270
View File
@@ -19,18 +19,18 @@
//*************************************************************************
// V3Combine's Transformations:
//
// For every function that we spit out
// Examine code to find largest common blocks
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
// GO through table
// Lookup in hash, while next of each statement match, grow that common block
// Foreach common block
// If common block large enough (> 20 statements) & used 2x or more
// Make new function
// Move common block to function
// Replace each common block ref with funccall
// For every function that we spit out
// Examine code to find largest common blocks
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
// GO through table
// Lookup in hash, while next of each statement match, grow that common block
// Foreach common block
// If common block large enough (> 20 statements) & used 2x or more
// Make new function
// Move common block to function
// Replace each common block ref with funccall
//
//*************************************************************************
@@ -50,7 +50,7 @@
//######################################################################
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
//######################################################################
@@ -79,57 +79,60 @@ class CombCallVisitor : CombBaseVisitor {
// Find all CCALLS of each CFUNC, so that we can later rename them
private:
// NODE STATE
bool m_find; // Find mode vs. delete mode
bool m_find; // Find mode vs. delete mode
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
CallMmap m_callMmap; // Associative array of {function}{call}
CallMmap m_callMmap; // Associative array of {function}{call}
// METHODS
public:
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
if (oldfuncp==newfuncp) return;
if (newfuncp) {
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
} else UINFO(4, " Remove "<<oldfuncp<<endl);
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (!callp->user3()) { // !already done
UINFO(4, " Called "<<callp<<endl);
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
if (newfuncp) {
AstCCall* newp = new AstCCall(callp, newfuncp);
// Special new AstCCall form above transfers children of callp to newfuncp
callp->replaceWith(newp);
addCall(newp); // Fix the table
} else { // Just deleting empty function
callp->unlinkFrBack();
}
callp->user3(true); // Dead now
pushDeletep(callp); VL_DANGLING(callp);
m_callMmap.erase(eqit); // Fix the table
}
}
if (oldfuncp==newfuncp) return;
if (newfuncp) {
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
} else UINFO(4, " Remove "<<oldfuncp<<endl);
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange
= m_callMmap.equal_range(oldfuncp);
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (!callp->user3()) { // !already done
UINFO(4, " Called "<<callp<<endl);
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
"Call list broken, points to call w/different func");
if (newfuncp) {
AstCCall* newp = new AstCCall(callp, newfuncp);
// Special new AstCCall form above transfers children of callp to newfuncp
callp->replaceWith(newp);
addCall(newp); // Fix the table
} else { // Just deleting empty function
callp->unlinkFrBack();
}
callp->user3(true); // Dead now
pushDeletep(callp); VL_DANGLING(callp);
m_callMmap.erase(eqit); // Fix the table
}
}
}
// METHODS
void addCall(AstCCall* nodep) {
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
}
void deleteCall(AstCCall* nodep) {
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (callp==nodep) {
m_callMmap.erase(eqit);
return;
}
}
nodep->v3fatalSrc("deleteCall node not found in table");
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange
= m_callMmap.equal_range(nodep->funcp());
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (callp==nodep) {
m_callMmap.erase(eqit);
return;
}
}
nodep->v3fatalSrc("deleteCall node not found in table");
}
private:
// VISITORS
virtual void visit(AstCCall* nodep) {
addCall(nodep);
addCall(nodep);
}
// Speed things up
virtual void visit(AstNodeAssign* nodep) {}
@@ -140,7 +143,7 @@ private:
public:
// CONSTRUCTORS
CombCallVisitor() {
m_find = false;
m_find = false;
}
virtual ~CombCallVisitor() {}
void main(AstNetlist* nodep) {
@@ -155,10 +158,10 @@ class CombMarkVisitor : CombBaseVisitor {
// Mark all nodes under specified one.
private:
// OUTPUT:
// AstNode::user3() -> bool. True to indicate duplicated
// AstNode::user3() -> bool. True to indicate duplicated
// VISITORS
virtual void visit(AstNode* nodep) {
nodep->user3(true);
nodep->user3(true);
iterateChildren(nodep);
}
public:
@@ -176,273 +179,276 @@ class CombineVisitor : CombBaseVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNodeStmt::user() -> bool. True if iterated already
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
AstUser1InUse m_inuser1;
AstUser3InUse m_inuser3;
//AstUser4InUse part of V3Hashed
// AstNodeStmt::user() -> bool. True if iterated already
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
AstUser1InUse m_inuser1;
AstUser3InUse m_inuser3;
//AstUser4InUse part of V3Hashed
// STATE
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
V3Double0 m_statCombs; // Statistic tracking
CombineState m_state; // Major state
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current function
V3Hash m_lowerHash; // Hash of the statement we're building
CombCallVisitor m_call; // Tracking of function call users
int m_modNFuncs; // Number of functions made
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
V3Hashed m_hashed; // Hash for every node in module
V3Double0 m_statCombs; // Statistic tracking
CombineState m_state; // Major state
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current function
V3Hash m_lowerHash; // Hash of the statement we're building
CombCallVisitor m_call; // Tracking of function call users
int m_modNFuncs; // Number of functions made
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
V3Hashed m_hashed; // Hash for every node in module
// METHODS
void hashStatement(AstNode* nodep) {
// Compute hash on entire tree of this statement
m_hashed.hashAndInsert(nodep);
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
// Compute hash on entire tree of this statement
m_hashed.hashAndInsert(nodep);
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
}
void hashFunctions(AstCFunc* nodep) {
// Compute hash of all statement trees in the function
CombineState oldState = m_state;
{
m_state = STATE_HASH;
// Compute hash of all statement trees in the function
CombineState oldState = m_state;
{
m_state = STATE_HASH;
iterate(nodep);
}
m_state = oldState;
}
m_state = oldState;
}
void walkEmptyFuncs() {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* node1p = it->second;
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* node1p = it->second;
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
if (oldfuncp
&& oldfuncp->emptyBody()
&& !oldfuncp->dontCombine()) {
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
// Mark user3p on entire old tree, so we don't process it more
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, NULL);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
}
}
if (oldfuncp
&& oldfuncp->emptyBody()
&& !oldfuncp->dontCombine()) {
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())
<<" "<<oldfuncp<<endl);
// Mark user3p on entire old tree, so we don't process it more
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, NULL);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
}
}
}
void walkDupFuncs() {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
V3Hash hashval = it->first;
AstNode* node1p = it->second;
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
V3Hash hashval = it->first;
AstNode* node1p = it->second;
if (!VN_IS(node1p, CFunc)) continue;
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
AstNode* node2p = eqit->second;
if (!(eqit->first == hashval)) break;
if (node1p==node2p) continue; // Identical iterator
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
// Replace AstCCall's that point here
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
AstNode* node2p = eqit->second;
if (!(eqit->first == hashval)) break;
if (node1p==node2p) continue; // Identical iterator
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
// Replace AstCCall's that point here
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
// Replacement may promote a slow routine to fast path
// Replacement may promote a slow routine to fast path
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
}
}
}
}
}
}
}
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
// Mark user3p on entire old tree, so we don't process it more
++m_statCombs;
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, newfuncp);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
// Mark user3p on entire old tree, so we don't process it more
++m_statCombs;
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, newfuncp);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
}
void replaceOnlyCallFunc(AstCCall* nodep) {
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
//oldfuncp->dumpTree(cout,"MAYDEL: ");
if (nodep->nextp()==NULL
&& oldfuncp->initsp()==NULL
&& oldfuncp->stmtsp()==nodep
&& oldfuncp->finalsp()==NULL) {
UINFO(9," Function only has call "<<oldfuncp<<endl);
m_call.deleteCall(nodep);
CombMarkVisitor visitor(oldfuncp);
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
}
}
//oldfuncp->dumpTree(cout, "MAYDEL: ");
if (nodep->nextp()==NULL
&& oldfuncp->initsp()==NULL
&& oldfuncp->stmtsp()==nodep
&& oldfuncp->finalsp()==NULL) {
UINFO(9," Function only has call "<<oldfuncp<<endl);
m_call.deleteCall(nodep);
CombMarkVisitor visitor(oldfuncp);
replaceFuncWFunc(oldfuncp, nodep->funcp()); VL_DANGLING(nodep);
}
}
}
void walkDupCodeStart(AstNode* node1p) {
V3Hash hashval(node1p->user4p());
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
//
int bestDepth = 0; // Best substitution found in the search
AstNode* bestNode2p = NULL;
AstNode* bestLast1p = NULL;
AstNode* bestLast2p = NULL;
//
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (node1p==node2p) continue;
//
// We need to mark iteration to prevent matching code inside code (abab matching in ababab)
AstNode::user1ClearTree(); // user1p() used on entire tree
m_walkLast1p = NULL;
m_walkLast2p = NULL;
int depth = walkDupCodeNext(node1p, node2p, 1);
if (depth>COMBINE_MIN_STATEMENTS
&& depth>bestDepth) {
bestDepth = depth;
bestNode2p = node2p;
bestLast1p = m_walkLast1p;
bestLast2p = m_walkLast2p;
}
}
if (bestDepth) {
// Found a replacement
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
UINFO(5," DupFunc "<<" "<<node1p<<endl);
UINFO(5," and "<<" "<<bestNode2p<<endl);
UINFO(5," Through "<<" "<<bestLast1p<<endl);
UINFO(5," and "<<" "<<bestLast2p<<endl);
//
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
}
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
//
int bestDepth = 0; // Best substitution found in the search
AstNode* bestNode2p = NULL;
AstNode* bestLast1p = NULL;
AstNode* bestLast2p = NULL;
//
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange
= m_hashed.mmap().equal_range(hashval);
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (node1p==node2p) continue;
//
// We need to mark iteration to prevent matching code inside
// code (abab matching in ababab)
AstNode::user1ClearTree(); // user1p() used on entire tree
m_walkLast1p = NULL;
m_walkLast2p = NULL;
int depth = walkDupCodeNext(node1p, node2p, 1);
if (depth>COMBINE_MIN_STATEMENTS
&& depth>bestDepth) {
bestDepth = depth;
bestNode2p = node2p;
bestLast1p = m_walkLast1p;
bestLast2p = m_walkLast2p;
}
}
if (bestDepth) {
// Found a replacement
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
UINFO(5," DupFunc "<<" "<<node1p<<endl);
UINFO(5," and "<<" "<<bestNode2p<<endl);
UINFO(5," Through "<<" "<<bestLast1p<<endl);
UINFO(5," and "<<" "<<bestLast2p<<endl);
//
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
}
}
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
// Find number of common statements between the two node1p_nextp's...
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
if (!m_hashed.sameNodes(node1p,node2p)) return 0; // walk of tree has same comparison
V3Hash hashval(node1p->user4p());
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
m_walkLast1p = node1p;
m_walkLast2p = node2p;
node1p->user1(true);
node2p->user1(true);
if (node1p->nextp() && node2p->nextp()) {
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
}
return hashval.depth();
// Find number of common statements between the two node1p_nextp's...
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
V3Hash hashval(node1p->user4p());
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
m_walkLast1p = node1p;
m_walkLast2p = node2p;
node1p->user1(true);
node2p->user1(true);
if (node1p->nextp() && node2p->nextp()) {
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
}
return hashval.depth();
}
void walkReplace(AstNode* node1p, AstNode* node2p,
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
// Make new function
string oldname = m_funcp->name();
string::size_type pos;
if ((pos=oldname.find("_common")) != string::npos) {
oldname.erase(pos);
}
if ((pos=oldname.find("__")) != string::npos) {
oldname.erase(pos);
}
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
oldname+"_common"+cvtToStr(++m_modNFuncs),
NULL);
m_modp->addStmtp(newfuncp);
// Create calls
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
// Grab statement bodies
AstNRelinker relink1Handle;
AstNRelinker relink2Handle;
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
if (walkp==last1p) break;
}
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
if (walkp==last2p) break;
}
// Move node1 statements to new function
newfuncp->addStmtsp(node1p);
//newfuncp->dumpTree(cout," newfunctree: ");
// Mark node2 statements as dead
CombMarkVisitor visitor(node2p);
pushDeletep(node2p); // Delete later
// Link in new function
relink1Handle.relink(call1p);
relink2Handle.relink(call2p);
// Hash the new function
hashFunctions(newfuncp);
m_call.addCall(call1p);
m_call.addCall(call2p);
// If either new statement makes a func with only a single call, replace
// the above callers to call it directly
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
// Make new function
string oldname = m_funcp->name();
string::size_type pos;
if ((pos = oldname.find("_common")) != string::npos) {
oldname.erase(pos);
}
if ((pos = oldname.find("__")) != string::npos) {
oldname.erase(pos);
}
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
oldname+"_common"+cvtToStr(++m_modNFuncs),
NULL);
m_modp->addStmtp(newfuncp);
// Create calls
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
// Grab statement bodies
AstNRelinker relink1Handle;
AstNRelinker relink2Handle;
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
if (walkp==last1p) break;
}
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
if (walkp==last2p) break;
}
// Move node1 statements to new function
newfuncp->addStmtsp(node1p);
//newfuncp->dumpTree(cout, " newfunctree: ");
// Mark node2 statements as dead
CombMarkVisitor visitor(node2p);
pushDeletep(node2p); // Delete later
// Link in new function
relink1Handle.relink(call1p);
relink2Handle.relink(call2p);
// Hash the new function
hashFunctions(newfuncp);
m_call.addCall(call1p);
m_call.addCall(call2p);
// If either new statement makes a func with only a single call, replace
// the above callers to call it directly
replaceOnlyCallFunc(call1p); VL_DANGLING(call1p);
replaceOnlyCallFunc(call2p); VL_DANGLING(call2p);
}
// VISITORS
virtual void visit(AstNetlist* nodep) {
// Track all callers of each function
m_call.main(nodep);
//
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
// Iterate modules backwards, in bottom-up order.
// Required so that a module instantiating another can benefit from collapsing.
// Track all callers of each function
m_call.main(nodep);
//
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
// Iterate modules backwards, in bottom-up order.
// Required so that a module instantiating another can benefit from collapsing.
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) {
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_modNFuncs = 0;
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_modNFuncs = 0;
m_walkLast2p = NULL;
m_hashed.clear();
// Compute hash of all statement trees in the function
m_state = STATE_HASH;
m_hashed.clear();
// Compute hash of all statement trees in the function
m_state = STATE_HASH;
iterateChildren(nodep);
m_state = STATE_IDLE;
if (debug()>=9) {
m_hashed.dumpFilePrefixed("combine");
}
// Walk the hashes removing empty functions
if (emptyFunctionDeletion()) {
walkEmptyFuncs();
}
// Walk the hashes looking for duplicate functions
if (duplicateFunctionCombine()) {
walkDupFuncs();
}
// Walk the statements looking for large replicated code sections
if (statementCombine()) {
m_state = STATE_DUP;
m_state = STATE_IDLE;
if (debug()>=9) {
m_hashed.dumpFilePrefixed("combine");
}
// Walk the hashes removing empty functions
if (emptyFunctionDeletion()) {
walkEmptyFuncs();
}
// Walk the hashes looking for duplicate functions
if (duplicateFunctionCombine()) {
walkDupFuncs();
}
// Walk the statements looking for large replicated code sections
if (statementCombine()) {
m_state = STATE_DUP;
iterateChildren(nodep);
m_state = STATE_IDLE;
}
m_modp = NULL;
m_state = STATE_IDLE;
}
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
m_funcp = nodep;
if (!nodep->dontCombine()) {
if (m_state == STATE_HASH) {
hashStatement(nodep); // Hash the entire function - it might be identical
} else if (m_state == STATE_DUP) {
m_funcp = nodep;
if (!nodep->dontCombine()) {
if (m_state == STATE_HASH) {
hashStatement(nodep); // Hash the entire function - it might be identical
} else if (m_state == STATE_DUP) {
iterateChildren(nodep);
}
}
m_funcp = NULL;
}
}
m_funcp = NULL;
}
virtual void visit(AstNodeStmt* nodep) {
if (!nodep->isStatement()) {
iterateChildren(nodep);
return;
}
if (m_state == STATE_HASH && m_funcp) {
hashStatement(nodep);
}
else if (m_state == STATE_DUP && m_funcp) {
walkDupCodeStart(nodep);
}
if (m_state == STATE_HASH && m_funcp) {
hashStatement(nodep);
}
else if (m_state == STATE_DUP && m_funcp) {
walkDupCodeStart(nodep);
}
}
//--------------------
@@ -457,7 +463,7 @@ private:
public:
// CONSTUCTORS
explicit CombineVisitor(AstNetlist* nodep) {
m_state = STATE_IDLE;
m_state = STATE_IDLE;
m_modp = NULL;
m_funcp = NULL;
m_modNFuncs = 0;
@@ -466,7 +472,7 @@ public:
iterate(nodep);
}
virtual ~CombineVisitor() {
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
}
};
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void combineAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+80 -77
View File
@@ -33,110 +33,113 @@
class V3ConfigLine {
public:
int m_lineno; // Line number to make change at
V3ErrorCode m_code; // Error code
bool m_on; // True to enaable message
int m_lineno; // Line number to make change at
V3ErrorCode m_code; // Error code
bool m_on; // True to enaable message
V3ConfigLine(V3ErrorCode code, int lineno, bool on)
: m_lineno(lineno), m_code(code), m_on(on) {}
: m_lineno(lineno), m_code(code), m_on(on) {}
~V3ConfigLine() {}
inline bool operator< (const V3ConfigLine& rh) const {
if (m_lineno<rh.m_lineno) return true;
if (m_lineno>rh.m_lineno) return false;
if (m_code<rh.m_code) return true;
if (m_code>rh.m_code) return false;
// Always turn "on" before "off" so that overlapping lines will end up finally with the error "off"
return (m_on>rh.m_on);
if (m_lineno<rh.m_lineno) return true;
if (m_lineno>rh.m_lineno) return false;
if (m_code<rh.m_code) return true;
if (m_code>rh.m_code) return false;
// Always turn "on" before "off" so that overlapping lines will end
// up finally with the error "off"
return (m_on>rh.m_on);
}
};
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) {
return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
class V3ConfigIgnores {
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
// MEMBERS
string m_lastFilename; // Last filename looked up
int m_lastLineno; // Last linenumber looked up
string m_lastFilename; // Last filename looked up
int m_lastLineno; // Last linenumber looked up
IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number
IgnLines::const_iterator m_lastEnd; // Point with end()
IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number
IgnLines::const_iterator m_lastEnd; // Point with end()
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename
IgnFiles m_ignWilds; // Ignores for each wildcarded filename
IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename
static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow)
static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow)
V3ConfigIgnores() { m_lastLineno = -1; }
~V3ConfigIgnores() {}
// METHODS
inline IgnLines* findWilds(const string& wildname) {
IgnFiles::iterator it = m_ignWilds.find(wildname);
if (it != m_ignWilds.end()) {
return &(it->second);
} else {
m_ignWilds.insert(make_pair(wildname, IgnLines()));
it = m_ignWilds.find(wildname);
return &(it->second);
}
IgnFiles::iterator it = m_ignWilds.find(wildname);
if (it != m_ignWilds.end()) {
return &(it->second);
} else {
m_ignWilds.insert(make_pair(wildname, IgnLines()));
it = m_ignWilds.find(wildname);
return &(it->second);
}
}
inline void absBuild(const string& filename) {
// Given a filename, find all wildcard matches against it and build
// hash with the specific filename. This avoids having to wildmatch
// more than once against any filename.
IgnFiles::iterator it = m_ignFiles.find(filename);
if (it == m_ignFiles.end()) {
// Haven't seen this filename before
m_ignFiles.insert(make_pair(filename, IgnLines()));
it = m_ignFiles.find(filename);
// Make new list for this file of all matches
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
for (IgnLines::iterator lit = fnit->second.begin(); lit != fnit->second.end(); ++lit) {
it->second.insert(*lit);
}
}
}
}
m_lastIt = it->second.begin();
m_lastEnd = it->second.end();
// Given a filename, find all wildcard matches against it and build
// hash with the specific filename. This avoids having to wildmatch
// more than once against any filename.
IgnFiles::iterator it = m_ignFiles.find(filename);
if (it == m_ignFiles.end()) {
// Haven't seen this filename before
m_ignFiles.insert(make_pair(filename, IgnLines()));
it = m_ignFiles.find(filename);
// Make new list for this file of all matches
for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) {
if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) {
for (IgnLines::iterator lit = fnit->second.begin();
lit != fnit->second.end(); ++lit) {
it->second.insert(*lit);
}
}
}
}
m_lastIt = it->second.begin();
m_lastEnd = it->second.end();
}
public:
inline static V3ConfigIgnores& singleton() { return s_singleton; }
void addIgnore(V3ErrorCode code, const string& wildname, int lineno, bool on) {
// Insert
IgnLines* linesp = findWilds(wildname);
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
linesp->insert(V3ConfigLine(code, lineno, on));
// Flush the match cache, due to a change in the rules.
m_ignFiles.clear();
m_lastFilename = " ";
// Insert
IgnLines* linesp = findWilds(wildname);
UINFO(9,"config addIgnore "<<wildname<<":"<<lineno<<", "<<code<<", "<<on<<endl);
linesp->insert(V3ConfigLine(code, lineno, on));
// Flush the match cache, due to a change in the rules.
m_ignFiles.clear();
m_lastFilename = " ";
}
inline void applyIgnores(FileLine* filelinep) {
// HOT routine, called each parsed token line
if (m_lastLineno != filelinep->lineno()
|| m_lastFilename != filelinep->filename()) {
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
absBuild(filelinep->filename());
m_lastFilename = filelinep->filename();
}
// Process all on/offs for lines up to and including the current line
int curlineno = filelinep->lineno();
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
if (m_lastIt->m_lineno > curlineno) break;
//UINFO(9," Hit "<<*m_lastIt<<endl);
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
}
if (0 && debug() >= 9) {
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
UINFO(9," NXT "<<*it<<endl);
}
}
m_lastLineno = filelinep->lineno();
}
// HOT routine, called each parsed token line
if (m_lastLineno != filelinep->lastLineno()
|| m_lastFilename != filelinep->filename()) {
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
absBuild(filelinep->filename());
m_lastFilename = filelinep->filename();
}
// Process all on/offs for lines up to and including the current line
int curlineno = filelinep->lastLineno();
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
if (m_lastIt->m_lineno > curlineno) break;
//UINFO(9," Hit "<<*m_lastIt<<endl);
filelinep->warnOn(m_lastIt->m_code, m_lastIt->m_on);
}
if (0 && debug() >= 9) {
for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) {
UINFO(9," NXT "<<*it<<endl);
}
}
m_lastLineno = filelinep->lastLineno();
}
}
};
@@ -147,10 +150,10 @@ V3ConfigIgnores V3ConfigIgnores::s_singleton;
void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
if (filename=="*") {
FileLine::globalWarnOff(code,!on);
FileLine::globalWarnOff(code,!on);
} else {
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
V3ConfigIgnores::singleton().addIgnore(code, filename, min, on);
if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on);
}
}
+1 -1
View File
@@ -35,4 +35,4 @@ public:
static void applyIgnores(FileLine* filelinep);
};
#endif // Guard
#endif // Guard
+1700 -1647
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -49,4 +49,4 @@ public:
static AstNode* constifyExpensiveEdit(AstNode* nodep);
};
#endif // Guard
#endif // Guard
+264 -251
View File
@@ -18,13 +18,13 @@
//
//*************************************************************************
// COVERAGE TRANSFORMATIONS:
// At each IF/(IF else)/CASEITEM,
// If there's no coverage off on the block below it,
// or a $stop
// Insert a COVERDECL node in the module.
// (V3Emit reencodes into per-module numbers for emitting.)
// Insert a COVERINC node at the end of the statement list
// for that if/else/case.
// At each IF/(IF else)/CASEITEM,
// If there's no coverage off on the block below it,
// or a $stop
// Insert a COVERDECL node in the module.
// (V3Emit reencodes into per-module numbers for emitting.)
// Insert a COVERINC node at the end of the statement list
// for that if/else/case.
//
//*************************************************************************
@@ -47,334 +47,347 @@ private:
typedef std::map<string,int> FileMap;
struct ToggleEnt {
string m_comment; // Comment for coverage dump
AstNode* m_varRefp; // How to get to this element
AstNode* m_chgRefp; // How to get to this element
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
: m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {}
~ToggleEnt() {}
void cleanup() {
m_varRefp->deleteTree(); m_varRefp=NULL;
m_chgRefp->deleteTree(); m_chgRefp=NULL;
}
string m_comment; // Comment for coverage dump
AstNode* m_varRefp; // How to get to this element
AstNode* m_chgRefp; // How to get to this element
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
: m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {}
~ToggleEnt() {}
void cleanup() {
m_varRefp->deleteTree(); m_varRefp = NULL;
m_chgRefp->deleteTree(); m_chgRefp = NULL;
}
};
// NODE STATE
// Entire netlist:
// AstIf::user1() -> bool. True indicates ifelse processed
AstUser1InUse m_inuser1;
// AstIf::user1() -> bool. True indicates ifelse processed
AstUser1InUse m_inuser1;
// STATE
bool m_checkBlock; // Should this block get covered?
AstNodeModule* m_modp; // Current module to add statement to
bool m_inToggleOff; // In function/task etc
bool m_inModOff; // In module with no coverage
FileMap m_fileps; // Column counts for each fileline
string m_beginHier; // AstBegin hier name for user coverage points
bool m_checkBlock; // Should this block get covered?
AstNodeModule* m_modp; // Current module to add statement to
bool m_inToggleOff; // In function/task etc
bool m_inModOff; // In module with no coverage
FileMap m_fileps; // Column counts for each fileline
string m_beginHier; // AstBegin hier name for user coverage points
// METHODS
VL_DEBUG_FUNC; // Declare debug()
const char* varIgnoreToggle(AstVar* nodep) {
// Return true if this shouldn't be traced
// See also similar rule in V3TraceDecl::varIgnoreTrace
if (!nodep->isToggleCoverable())
return "Not relevant signal type";
if (!v3Global.opt.coverageUnderscore()) {
string prettyName = nodep->prettyName();
if (prettyName[0] == '_')
return "Leading underscore";
if (prettyName.find("._") != string::npos)
return "Inlined leading underscore";
}
if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) return "Wide bus/array > 256 bits";
// We allow this, though tracing doesn't
// if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array";
return NULL;
// Return true if this shouldn't be traced
// See also similar rule in V3TraceDecl::varIgnoreTrace
if (!nodep->isToggleCoverable())
return "Not relevant signal type";
if (!v3Global.opt.coverageUnderscore()) {
string prettyName = nodep->prettyName();
if (prettyName[0] == '_')
return "Leading underscore";
if (prettyName.find("._") != string::npos)
return "Inlined leading underscore";
}
if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) {
return "Wide bus/array > 256 bits";
}
// We allow this, though tracing doesn't
// if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array";
return NULL;
}
AstCoverInc* newCoverInc(FileLine* fl, const string& hier,
const string& page_prefix, const string& comment) {
// For line coverage, we may have multiple if's on one line, so disambiguate if
// everything is otherwise identical
// (Don't set column otherwise as it may result in making bins not match up with
// different types of coverage enabled.)
string key = fl->filename()+"\001"+cvtToStr(fl->lineno())+"\001"+hier+"\001"+page_prefix+"\001"+comment;
int column = 0;
FileMap::iterator it = m_fileps.find(key);
if (it == m_fileps.end()) {
m_fileps.insert(make_pair(key,column+1));
} else {
column = (it->second)++;
}
const string& page_prefix, const string& comment) {
// For line coverage, we may have multiple if's on one line, so disambiguate if
// everything is otherwise identical
// (Don't set column otherwise as it may result in making bins not match up with
// different types of coverage enabled.)
string key = fl->filename()+"\001"+cvtToStr(fl->lineno())
+"\001"+hier+"\001"+page_prefix+"\001"+comment;
int column = 0;
FileMap::iterator it = m_fileps.find(key);
if (it == m_fileps.end()) {
m_fileps.insert(make_pair(key, column+1));
} else {
column = (it->second)++;
}
// We could use the basename of the filename to the page, but seems better for code
// from an include file to be listed under the module using it rather than the include file.
// Note the module name could have parameters appended, we'll consider this
// a feature as it allows for each parameterized block to be counted separately.
// Someday the user might be allowed to specify a different page suffix
string page = page_prefix + "/" + m_modp->prettyName();
// We could use the basename of the filename to the page, but seems
// better for code from an include file to be listed under the
// module using it rather than the include file.
// Note the module name could have parameters appended, we'll consider this
// a feature as it allows for each parameterized block to be counted separately.
// Someday the user might be allowed to specify a different page suffix
string page = page_prefix + "/" + m_modp->prettyName();
AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment);
declp->hier(hier);
m_modp->addStmtp(declp);
AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment);
declp->hier(hier);
m_modp->addStmtp(declp);
return new AstCoverInc(fl, declp);
return new AstCoverInc(fl, declp);
}
// VISITORS - BOTH
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
m_fileps.clear();
m_modp = nodep;
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
m_fileps.clear();
iterateChildren(nodep);
m_modp = NULL;
m_inModOff = true;
m_modp = NULL;
m_inModOff = true;
}
// VISITORS - TOGGLE COVERAGE
virtual void visit(AstNodeFTask* nodep) {
bool oldtog = m_inToggleOff;
{
m_inToggleOff = true;
bool oldtog = m_inToggleOff;
{
m_inToggleOff = true;
iterateChildren(nodep);
}
m_inToggleOff = oldtog;
}
m_inToggleOff = oldtog;
}
virtual void visit(AstVar* nodep) {
iterateChildren(nodep);
if (m_modp && !m_inModOff && !m_inToggleOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
const char* disablep = varIgnoreToggle(nodep);
if (disablep) {
UINFO(4, " Disable Toggle: "<<disablep<<" "<<nodep<<endl);
} else {
UINFO(4, " Toggle: "<<nodep<<endl);
// There's several overall ways to approach this
// Treat like tracing, where a end-of-timestamp action sees all changes
// Works ok, but would be quite slow as need to reform vectors before the calls
// Convert to "always @ (posedge signal[#]) coverinc"
// Would mark many signals as clocks, precluding many later optimizations
// Convert to "if (x & !lastx) CoverInc"
// OK, but we couldn't later detect them to schedule where the IFs get called
// Convert to "AstCoverInc(CoverInc...)"
// We'll do this, and make the if(...) coverinc later.
if (m_modp && !m_inModOff && !m_inToggleOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
const char* disablep = varIgnoreToggle(nodep);
if (disablep) {
UINFO(4, " Disable Toggle: "<<disablep<<" "<<nodep<<endl);
} else {
UINFO(4, " Toggle: "<<nodep<<endl);
// There's several overall ways to approach this
// Treat like tracing, where a end-of-timestamp action sees all changes
// Works ok, but would be quite slow as need to reform
// vectors before the calls
// Convert to "always @ (posedge signal[#]) coverinc"
// Would mark many signals as clocks, precluding many later optimizations
// Convert to "if (x & !lastx) CoverInc"
// OK, but we couldn't later detect them to schedule where the IFs get called
// Convert to "AstCoverInc(CoverInc...)"
// We'll do this, and make the if(...) coverinc later.
// Add signal to hold the old value
// Add signal to hold the old value
string newvarname = string("__Vtogcov__")+nodep->shortName();
AstVar* chgVarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
m_modp->addStmtp(chgVarp);
AstVar* chgVarp = new AstVar(nodep->fileline(),
AstVarType::MODULETEMP, newvarname, nodep);
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
m_modp->addStmtp(chgVarp);
// Create bucket for each dimension * bit.
// This is necessarily an O(n^2) expansion, which is why
// we limit coverage to signals with < 256 bits.
// Create bucket for each dimension * bit.
// This is necessarily an O(n^2) expansion, which is why
// we limit coverage to signals with < 256 bits.
ToggleEnt newvec (string(""),
new AstVarRef(nodep->fileline(), nodep, false),
new AstVarRef(nodep->fileline(), chgVarp, true));
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec,
nodep, chgVarp);
newvec.cleanup();
}
}
ToggleEnt newvec (string(""),
new AstVarRef(nodep->fileline(), nodep, false),
new AstVarRef(nodep->fileline(), chgVarp, true));
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec,
nodep, chgVarp);
newvec.cleanup();
}
}
}
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) {
AstCoverToggle* newp
AstCoverToggle* newp
= new AstCoverToggle(varp->fileline(),
newCoverInc(varp->fileline(), "", "v_toggle",
varp->name()+above.m_comment),
above.m_varRefp->cloneTree(true),
above.m_chgRefp->cloneTree(true));
m_modp->addStmtp(newp);
m_modp->addStmtp(newp);
}
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
const ToggleEnt& above,
AstVar* varp, AstVar* chgVarp) { // Constant
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
const ToggleEnt& above,
AstVar* varp, AstVar* chgVarp) { // Constant
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
if (bdtypep->isRanged()) {
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
int index_code = index_docs - bdtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code, 1),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code, 1));
if (bdtypep->isRanged()) {
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
int index_code = index_docs - bdtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstSel(varp->fileline(),
above.m_varRefp->cloneTree(true), index_code, 1),
new AstSel(varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code, 1));
toggleVarBottom(newent, varp);
newent.cleanup();
}
} else {
newent.cleanup();
}
} else {
toggleVarBottom(above, varp);
}
}
}
}
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
int index_code = index_docs - adtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstArraySel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code),
new AstArraySel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code));
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
int index_code = index_docs - adtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstArraySel(varp->fileline(),
above.m_varRefp->cloneTree(true), index_code),
new AstArraySel(varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code));
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
int index_code = index_docs - adtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code*subtypep->width(), subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code*subtypep->width(), subtypep->width()));
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
int index_code = index_docs - adtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code*subtypep->width(), subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code*subtypep->width(), subtypep->width()));
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
// For now it's packed, so similar to array
for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
int index_code = itemp->lsb();
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()));
toggleVarRecurse(subtypep, depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
// For now it's packed, so similar to array
for (AstMemberDType* itemp = adtypep->membersp();
itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
int index_code = itemp->lsb();
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()));
toggleVarRecurse(subtypep, depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
// Arbitrarially handle only the first member of the union
if (AstMemberDType* itemp = adtypep->membersp()) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
above.m_varRefp->cloneTree(true),
above.m_chgRefp->cloneTree(true));
toggleVarRecurse(subtypep, depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
else {
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "<<dtypep->prettyTypeName());
}
// Arbitrarially handle only the first member of the union
if (AstMemberDType* itemp = adtypep->membersp()) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
above.m_varRefp->cloneTree(true),
above.m_chgRefp->cloneTree(true));
toggleVarRecurse(subtypep, depth+1,
newent,
varp, chgVarp);
newent.cleanup();
}
}
else {
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
<<dtypep->prettyTypeName());
}
}
// VISITORS - LINE COVERAGE
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
UINFO(4," IF: "<<nodep<<endl);
if (m_checkBlock) {
// An else-if. When we iterate the if, use "elsif" marking
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
UINFO(4," IF: "<<nodep<<endl);
if (m_checkBlock) {
// An else-if. When we iterate the if, use "elsif" marking
bool elsif = (VN_IS(nodep->elsesp(), If)
&& !VN_CAST(nodep->elsesp(), If)->nextp());
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
//
//
iterateAndNextNull(nodep->ifsp());
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
if (nodep->user1()) {
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif"));
} else {
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if"));
}
}
// Don't do empty else's, only empty if/case's
if (nodep->elsesp()) {
m_checkBlock = true;
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
if (nodep->user1()) {
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif"));
} else {
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if"));
}
}
// Don't do empty else's, only empty if/case's
if (nodep->elsesp()) {
m_checkBlock = true;
iterateAndNextNull(nodep->elsesp());
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
if (!elsif) { // elsif done inside if()
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "else"));
}
}
}
m_checkBlock = true; // Reset as a child may have cleared it
}
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
if (!elsif) { // elsif done inside if()
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(),
"", "v_line", "else"));
}
}
}
m_checkBlock = true; // Reset as a child may have cleared it
}
}
virtual void visit(AstCaseItem* nodep) {
UINFO(4," CASEI: "<<nodep<<endl);
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
UINFO(4," CASEI: "<<nodep<<endl);
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
iterateAndNextNull(nodep->bodysp());
if (m_checkBlock) { // if the case body didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
}
m_checkBlock = true; // Reset as a child may have cleared it
}
if (m_checkBlock) { // if the case body didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
}
m_checkBlock = true; // Reset as a child may have cleared it
}
}
virtual void visit(AstPslCover* nodep) {
UINFO(4," PSLCOVER: "<<nodep<<endl);
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
UINFO(4," PSLCOVER: "<<nodep<<endl);
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
iterateChildren(nodep);
if (!nodep->coverincp()) {
// Note the name may be overridden by V3Assert processing
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
}
m_checkBlock = true; // Reset as a child may have cleared it
if (!nodep->coverincp()) {
// Note the name may be overridden by V3Assert processing
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
}
m_checkBlock = true; // Reset as a child may have cleared it
}
virtual void visit(AstStop* nodep) {
UINFO(4," STOP: "<<nodep<<endl);
m_checkBlock = false;
UINFO(4," STOP: "<<nodep<<endl);
m_checkBlock = false;
}
virtual void visit(AstPragma* nodep) {
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
// Skip all NEXT nodes under this block, and skip this if/case branch
UINFO(4," OFF: "<<nodep<<endl);
m_checkBlock = false;
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else {
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
// Skip all NEXT nodes under this block, and skip this if/case branch
UINFO(4," OFF: "<<nodep<<endl);
m_checkBlock = false;
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else {
if (m_checkBlock) iterateChildren(nodep);
}
}
}
virtual void visit(AstBegin* nodep) {
// Record the hierarchy of any named begins, so we can apply to user
// coverage points. This is because there may be cov points inside
// generate blocks; each point should get separate consideration.
// (Currently ignored for line coverage, since any generate iteration
// covers the code in that line.)
string oldHier = m_beginHier;
bool oldtog = m_inToggleOff;
{
m_inToggleOff = true;
if (nodep->name()!="") {
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
}
// Record the hierarchy of any named begins, so we can apply to user
// coverage points. This is because there may be cov points inside
// generate blocks; each point should get separate consideration.
// (Currently ignored for line coverage, since any generate iteration
// covers the code in that line.)
string oldHier = m_beginHier;
bool oldtog = m_inToggleOff;
{
m_inToggleOff = true;
if (nodep->name()!="") {
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
}
iterateChildren(nodep);
}
m_beginHier = oldHier;
m_inToggleOff = oldtog;
}
m_beginHier = oldHier;
m_inToggleOff = oldtog;
}
// VISITORS - BOTH
virtual void visit(AstNode* nodep) {
// Default: Just iterate
if (m_checkBlock) {
// Default: Just iterate
if (m_checkBlock) {
iterateChildren(nodep);
m_checkBlock = true; // Reset as a child may have cleared it
}
m_checkBlock = true; // Reset as a child may have cleared it
}
}
public:
// CONSTUCTORS
explicit CoverageVisitor(AstNetlist* rootp) {
// Operate on all modules
m_checkBlock = true;
// Operate on all modules
m_checkBlock = true;
m_modp = NULL;
m_beginHier = "";
m_inToggleOff = false;
m_inModOff = true;
m_beginHier = "";
m_inToggleOff = false;
m_inModOff = true;
iterateChildren(rootp);
}
virtual ~CoverageVisitor() {}
+1 -1
View File
@@ -35,4 +35,4 @@ public:
static void coverage(AstNetlist* rootp);
};
#endif // Guard
#endif // Guard
+51 -47
View File
@@ -18,7 +18,7 @@
//
//*************************************************************************
// COVERAGEJOIN TRANSFORMATIONS:
// If two COVERTOGGLEs have same VARSCOPE, combine them
// If two COVERTOGGLEs have same VARSCOPE, combine them
//*************************************************************************
@@ -40,72 +40,76 @@ class CoverageJoinVisitor : public AstNVisitor {
private:
// NODE STATE
// V3Hashed
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
//AstUser4InUse In V3Hashed
//AstUser4InUse In V3Hashed
// TYPES
typedef std::vector<AstCoverToggle*> ToggleList;
// STATE
ToggleList m_toggleps; // List of of all AstCoverToggle's
ToggleList m_toggleps; // List of of all AstCoverToggle's
V3Double0 m_statToggleJoins; // Statistic tracking
V3Double0 m_statToggleJoins; // Statistic tracking
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void detectDuplicates() {
UINFO(9,"Finding duplicates\n");
// Note uses user4
V3Hashed hashed; // Duplicate code detection
// Hash all of the original signals we toggle cover
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
AstCoverToggle* nodep = *it;
hashed.hashAndInsert(nodep->origp());
}
// Find if there are any duplicates
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
AstCoverToggle* nodep = *it;
if (nodep->backp()) { // nodep->backp() is null if we already detected it's a duplicate and unlinked it
// Want to choose a base node, and keep finding duplicates that are identical
// This prevents making chains where a->b, then c->d, then b->c, as we'll find a->b, a->c, a->d directly.
while (1) {
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp());
if (dupit == hashed.end()) break;
//
AstNode* duporigp = hashed.iteratorNodep(dupit);
// Note hashed will point to the original variable (what's duplicated), not the covertoggle,
// but we need to get back to the covertoggle which is immediately above, so:
UINFO(9,"Finding duplicates\n");
// Note uses user4
V3Hashed hashed; // Duplicate code detection
// Hash all of the original signals we toggle cover
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
AstCoverToggle* nodep = *it;
hashed.hashAndInsert(nodep->origp());
}
// Find if there are any duplicates
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
AstCoverToggle* nodep = *it;
// nodep->backp() is null if we already detected it's a duplicate and unlinked it.
if (nodep->backp()) {
// Want to choose a base node, and keep finding duplicates that are identical.
// This prevents making chains where a->b, then c->d, then b->c, as we'll
// find a->b, a->c, a->d directly.
while (1) {
V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp());
if (dupit == hashed.end()) break;
//
AstNode* duporigp = hashed.iteratorNodep(dupit);
// Note hashed will point to the original variable (what's
// duplicated), not the covertoggle, but we need to get back to the
// covertoggle which is immediately above, so:
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
// The CoverDecl the duplicate pointed to now needs to point to the original's data
// IE the duplicate will get the coverage number from the non-duplicate
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
UASSERT_OBJ(removep, nodep, "CoverageJoin duplicate of wrong type");
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
// The CoverDecl the duplicate pointed to now needs to point to the
// original's data. I.e. the duplicate will get the coverage number
// from the non-duplicate
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
removep->incp()->declp()->dataDeclp(datadeclp);
UINFO(8," new "<<removep->incp()->declp()<<endl);
// Mark the found node as a duplicate of the first node
// (Not vice-versa as we have the iterator for the found node)
removep->unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep);
// Remove node from comparison so don't hit it again
hashed.erase(dupit);
++m_statToggleJoins;
}
}
}
UINFO(8," new "<<removep->incp()->declp()<<endl);
// Mark the found node as a duplicate of the first node
// (Not vice-versa as we have the iterator for the found node)
removep->unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep);
// Remove node from comparison so don't hit it again
hashed.erase(dupit);
++m_statToggleJoins;
}
}
}
}
// VISITORS
virtual void visit(AstNetlist* nodep) {
// Find all Coverage's
// Find all Coverage's
iterateChildren(nodep);
// Simplify
detectDuplicates();
// Simplify
detectDuplicates();
}
virtual void visit(AstCoverToggle* nodep) {
m_toggleps.push_back(nodep);
m_toggleps.push_back(nodep);
iterateChildren(nodep);
}
//--------------------
@@ -120,7 +124,7 @@ public:
iterate(nodep);
}
virtual ~CoverageJoinVisitor() {
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
}
};
+1 -1
View File
@@ -35,4 +35,4 @@ public:
static void coverageJoin(AstNetlist* rootp);
};
#endif // Guard
#endif // Guard
+233 -229
View File
@@ -18,8 +18,8 @@
//
//*************************************************************************
// DEAD TRANSFORMATIONS:
// Remove any unreferenced modules
// Remove any unreferenced variables
// Remove any unreferenced modules
// Remove any unreferenced variables
//
// TODO: A graph would make the process of circular and interlinked
// dependencies easier to resolve.
@@ -59,7 +59,7 @@ private:
// VISITORS
virtual void visit(AstCell* nodep) {
iterateChildren(nodep);
nodep->modp()->user1Inc(-1);
nodep->modp()->user1Inc(-1);
}
//-----
virtual void visit(AstNodeMath* nodep) {} // Accelerate
@@ -81,337 +81,341 @@ class DeadVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire Netlist:
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
// AstVar::user1() -> int. Count of number of references
// AstVarScope::user1() -> int. Count of number of references
// AstNodeDType::user1() -> int. Count of number of references
AstUser1InUse m_inuser1;
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
// AstVar::user1() -> int. Count of number of references
// AstVarScope::user1() -> int. Count of number of references
// AstNodeDType::user1() -> int. Count of number of references
AstUser1InUse m_inuser1;
// TYPES
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
// STATE
AstNodeModule* m_modp; // Current module
AstNodeModule* m_modp; // Current module
std::vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
std::vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
std::vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
std::vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
std::vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
AssignMap m_assignMap; // List of all simple assignments for each variable
bool m_elimUserVars; // Allow removal of user's vars
bool m_elimDTypes; // Allow removal of DTypes
bool m_elimScopes; // Allow removal of Scopes
bool m_elimCells; // Allow removal of Cells
bool m_sideEffect; // Side effects discovered in assign RHS
AssignMap m_assignMap; // List of all simple assignments for each variable
bool m_elimUserVars; // Allow removal of user's vars
bool m_elimDTypes; // Allow removal of DTypes
bool m_elimScopes; // Allow removal of Scopes
bool m_elimCells; // Allow removal of Cells
bool m_sideEffect; // Side effects discovered in assign RHS
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void checkAll(AstNode* nodep) {
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
if (AstNode* subnodep = nodep->dtypep()) {
subnodep->user1Inc();
}
}
if (AstNode* subnodep = nodep->getChildDTypep()) {
subnodep->user1Inc();
}
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
if (AstNode* subnodep = nodep->dtypep()) {
subnodep->user1Inc();
}
}
if (AstNode* subnodep = nodep->getChildDTypep()) {
subnodep->user1Inc();
}
}
void checkDType(AstNodeDType* nodep) {
if (!nodep->generic() // Don't remove generic types
&& m_elimDTypes // dtypes stick around until post-widthing
if (!nodep->generic() // Don't remove generic types
&& m_elimDTypes // dtypes stick around until post-widthing
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
) {
m_dtypesp.push_back(nodep);
}
if (AstNode* subnodep = nodep->virtRefDTypep()) {
subnodep->user1Inc();
}
) {
m_dtypesp.push_back(nodep);
}
if (AstNode* subnodep = nodep->virtRefDTypep()) {
subnodep->user1Inc();
}
}
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
if (!nodep->dead()) {
m_modp = nodep;
if (!nodep->dead()) {
iterateChildren(nodep);
checkAll(nodep);
}
m_modp = NULL;
checkAll(nodep);
}
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
}
virtual void visit(AstScope* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
checkAll(nodep);
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) {
m_scopesp.push_back(nodep);
}
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) {
m_scopesp.push_back(nodep);
}
}
virtual void visit(AstCell* nodep) {
iterateChildren(nodep);
checkAll(nodep);
m_cellsp.push_back(nodep);
nodep->modp()->user1Inc();
checkAll(nodep);
m_cellsp.push_back(nodep);
nodep->modp()->user1Inc();
}
virtual void visit(AstNodeVarRef* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varScopep()) {
nodep->varScopep()->user1Inc();
nodep->varScopep()->varp()->user1Inc();
}
if (nodep->varp()) {
nodep->varp()->user1Inc();
}
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
checkAll(nodep);
if (nodep->varScopep()) {
nodep->varScopep()->user1Inc();
nodep->varScopep()->varp()->user1Inc();
}
if (nodep->varp()) {
nodep->varp()->user1Inc();
}
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
}
virtual void visit(AstNodeFTaskRef* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
}
virtual void visit(AstRefDType* nodep) {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
checkDType(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
}
virtual void visit(AstNodeDType* nodep) {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
checkDType(nodep);
checkAll(nodep);
}
virtual void visit(AstEnumItemRef* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
checkAll(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
else nodep->packagep()->user1Inc();
}
checkAll(nodep);
}
virtual void visit(AstModport* nodep) {
iterateChildren(nodep);
if (m_elimCells) {
if (!nodep->varsp()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
}
}
checkAll(nodep);
if (m_elimCells) {
if (!nodep->varsp()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
}
}
checkAll(nodep);
}
virtual void visit(AstTypedef* nodep) {
iterateChildren(nodep);
if (m_elimCells && !nodep->attrPublic()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
}
checkAll(nodep);
// Don't let packages with only public variables disappear
// Normal modules may disappear, e.g. if they are parameterized then removed
if (m_elimCells && !nodep->attrPublic()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
}
checkAll(nodep);
// Don't let packages with only public variables disappear
// Normal modules may disappear, e.g. if they are parameterized then removed
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
}
virtual void visit(AstVarScope* nodep) {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
if (mightElimVar(nodep->varp())) {
m_vscsp.push_back(nodep);
}
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
if (mightElimVar(nodep->varp())) {
m_vscsp.push_back(nodep);
}
}
virtual void visit(AstVar* nodep) {
iterateChildren(nodep);
checkAll(nodep);
checkAll(nodep);
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
if (mightElimVar(nodep)) {
m_varsp.push_back(nodep);
}
if (mightElimVar(nodep)) {
m_varsp.push_back(nodep);
}
}
virtual void visit(AstNodeAssign* nodep) {
// See if simple assignments to variables may be eliminated because that variable is never used.
// Similar code in V3Life
m_sideEffect = false;
// See if simple assignments to variables may be eliminated because
// that variable is never used.
// Similar code in V3Life
m_sideEffect = false;
iterateAndNextNull(nodep->rhsp());
checkAll(nodep);
// Has to be direct assignment without any EXTRACTing.
checkAll(nodep);
// Has to be direct assignment without any EXTRACTing.
AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef);
if (varrefp && !m_sideEffect
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
checkAll(varrefp); // Must track reference to dtype()
} else { // Track like any other statement
if (varrefp && !m_sideEffect
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
checkAll(varrefp); // Must track reference to dtype()
} else { // Track like any other statement
iterateAndNextNull(nodep->lhsp());
}
checkAll(nodep);
}
checkAll(nodep);
}
//-----
virtual void visit(AstNode* nodep) {
if (nodep->isOutputter()) m_sideEffect=true;
if (nodep->isOutputter()) m_sideEffect = true;
iterateChildren(nodep);
checkAll(nodep);
checkAll(nodep);
}
// METHODS
void deadCheckMod() {
// Kill any unused modules
// V3LinkCells has a graph that is capable of this too, but we need to do it
// after we've done all the generate blocks
for (bool retry=true; retry; ) {
retry=false;
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
// Kill any unused modules
// V3LinkCells has a graph that is capable of this too, but we need to do it
// after we've done all the generate blocks
for (bool retry=true; retry; ) {
retry = false;
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = VN_CAST(modp->nextp(), NodeModule);
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4," Dead module "<<modp<<endl);
// And its children may now be killable too; correct counts
// Recurse, as cells may not be directly under the module but in a generate
if (!modp->dead()) { // If was dead didn't increment user1's
DeadModVisitor visitor(modp);
}
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
retry = true;
}
}
}
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4," Dead module "<<modp<<endl);
// And its children may now be killable too; correct counts
// Recurse, as cells may not be directly under the module but in a generate
if (!modp->dead()) { // If was dead didn't increment user1's
DeadModVisitor visitor(modp);
}
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
retry = true;
}
}
}
}
bool mightElimVar(AstVar* nodep) {
return (!nodep->isSigPublic() // Can't elim publics!
&& !nodep->isIO()
&& (nodep->isTemp()
|| (nodep->isParam() && !nodep->isTrace())
|| m_elimUserVars)); // Post-Trace can kill most anything
return (!nodep->isSigPublic() // Can't elim publics!
&& !nodep->isIO()
&& (nodep->isTemp()
|| (nodep->isParam() && !nodep->isTrace())
|| m_elimUserVars)); // Post-Trace can kill most anything
}
void deadCheckScope() {
for (bool retry=true; retry; ) {
retry = false;
for (std::vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();++it) {
AstScope* scp = *it;
if (!scp)
continue;
if (scp->user1() == 0) {
for (bool retry=true; retry; ) {
retry = false;
for (std::vector<AstScope*>::iterator it = m_scopesp.begin();
it != m_scopesp.end();++it) {
AstScope* scp = *it;
if (!scp)
continue;
if (scp->user1() == 0) {
UINFO(4, " Dead AstScope " << scp << endl);
scp->aboveScopep()->user1Inc(-1);
if (scp->dtypep()) {
scp->dtypep()->user1Inc(-1);
}
scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp);
*it = NULL;
retry = true;
}
}
}
scp->aboveScopep()->user1Inc(-1);
if (scp->dtypep()) {
scp->dtypep()->user1Inc(-1);
}
scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp);
*it = NULL;
retry = true;
}
}
}
}
void deadCheckCells() {
for (std::vector<AstCell*>::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) {
AstCell* cellp = *it;
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
cellp->modp()->user1Inc(-1);
cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp);
}
}
AstCell* cellp = *it;
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
cellp->modp()->user1Inc(-1);
cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp);
}
}
}
void deadCheckVar() {
// Delete any unused varscopes
// Delete any unused varscopes
for (std::vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
AstVarScope* vscp = *it;
if (vscp->user1() == 0) {
UINFO(4," Dead "<<vscp<<endl);
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
AstNodeAssign* assp = itr->second;
AstVarScope* vscp = *it;
if (vscp->user1() == 0) {
UINFO(4," Dead "<<vscp<<endl);
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange
= m_assignMap.equal_range(vscp);
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
AstNodeAssign* assp = itr->second;
UINFO(4," Dead assign "<<assp<<endl);
assp->dtypep()->user1Inc(-1);
assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp);
}
if (vscp->scopep()) vscp->scopep()->user1Inc(-1);
vscp->dtypep()->user1Inc(-1);
vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp);
}
}
for (bool retry=true; retry; ) {
retry = false;
assp->dtypep()->user1Inc(-1);
assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp);
}
if (vscp->scopep()) vscp->scopep()->user1Inc(-1);
vscp->dtypep()->user1Inc(-1);
vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp);
}
}
for (bool retry=true; retry; ) {
retry = false;
for (std::vector<AstVar *>::iterator it = m_varsp.begin(); it != m_varsp.end();++it) {
AstVar* varp = *it;
if (!varp)
continue;
if (varp->user1() == 0) {
AstVar* varp = *it;
if (!varp)
continue;
if (varp->user1() == 0) {
UINFO(4, " Dead " << varp << endl);
if (varp->dtypep()) {
varp->dtypep()->user1Inc(-1);
}
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
*it = NULL;
retry = true;
}
}
}
if (varp->dtypep()) {
varp->dtypep()->user1Inc(-1);
}
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
*it = NULL;
retry = true;
}
}
}
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) {
if ((*it)->user1() == 0) {
AstNodeClassDType *classp;
// It's possible that there if a reference to each individual member, but
// not to the dtype itself. Check and don't remove the parent dtype if
// members are still alive.
if ((*it)->user1() == 0) {
AstNodeClassDType *classp;
// It's possible that there if a reference to each individual member, but
// not to the dtype itself. Check and don't remove the parent dtype if
// members are still alive.
if ((classp = VN_CAST((*it), NodeClassDType))) {
bool cont = true;
bool cont = true;
for (AstMemberDType *memberp = classp->membersp();
memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) {
if (memberp->user1() != 0) {
cont = false;
break;
}
}
if (!cont)
continue;
}
(*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it);
}
}
if (memberp->user1() != 0) {
cont = false;
break;
}
}
if (!cont)
continue;
}
(*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it);
}
}
}
public:
// CONSTRUCTORS
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, bool elimCells) {
m_modp = NULL;
m_elimCells = elimCells;
m_elimUserVars = elimUserVars;
m_elimDTypes = elimDTypes;
m_elimScopes = elimScopes;
m_sideEffect = false;
// Prepare to remove some datatypes
nodep->typeTablep()->clearCache();
// Operate on whole netlist
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes,
bool elimScopes, bool elimCells) {
m_modp = NULL;
m_elimCells = elimCells;
m_elimUserVars = elimUserVars;
m_elimDTypes = elimDTypes;
m_elimScopes = elimScopes;
m_sideEffect = false;
// Prepare to remove some datatypes
nodep->typeTablep()->clearCache();
// Operate on whole netlist
iterate(nodep);
deadCheckVar();
// We only elimate scopes when in a flattened structure
// Otherwise we have no easy way to know if a scope is used
if (elimScopes) deadCheckScope();
if (elimCells) deadCheckCells();
// Modules after vars, because might be vars we delete inside a mod we delete
deadCheckMod();
deadCheckVar();
// We only elimate scopes when in a flattened structure
// Otherwise we have no easy way to know if a scope is used
if (elimScopes) deadCheckScope();
if (elimCells) deadCheckCells();
// Modules after vars, because might be vars we delete inside a mod we delete
deadCheckMod();
// We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache();
// We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache();
}
virtual ~DeadVisitor() {}
};
+1 -1
View File
@@ -41,4 +41,4 @@ public:
static void deadifyAllScoped(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+313 -302
View File
@@ -20,34 +20,35 @@
// V3Delayed's Transformations:
//
// Each module:
// Replace ASSIGNDLY var, exp
// With ASSIGNDLY newvar, exp
// At top of block: VAR newvar
// At bottom of block: ASSIGNW var newvar
// Need _x_dly = x at top of active if "x" is not always set
// For now we'll say it's set if at top of block (not under IF, etc)
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
// in the active, and above rule applies too. (If so, use x on LHS, not _x_dly.)
// Replace ASSIGNDLY var, exp
// With ASSIGNDLY newvar, exp
// At top of block: VAR newvar
// At bottom of block: ASSIGNW var newvar
// Need _x_dly = x at top of active if "x" is not always set
// For now we'll say it's set if at top of block (not under IF, etc)
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
// in the active, and above rule applies too.
// (If so, use x on LHS, not _x_dly.)
//
// If a signal is set in multiple always blocks, we need a dly read & set with
// multiple clock sensitivities. We have 3 options:
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
// 2. Form unique ACTIVE for every multiple clocked assignment
// 3. Predetect signals from multiple always blocks and do #2 on them
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
// If a signal is set in multiple always blocks, we need a dly read & set with
// multiple clock sensitivities. We have 3 options:
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
// 2. Form unique ACTIVE for every multiple clocked assignment
// 3. Predetect signals from multiple always blocks and do #2 on them
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
//
// ASSIGNDLY (BITSEL(ARRAYSEL (VARREF(v), bits), selbits), rhs)
// -> VAR __Vdlyvset_x
// VAR __Vdlyvval_x
// VAR __Vdlyvdim_x
// VAR __Vdlyvlsb_x
// ASSIGNW (__Vdlyvset_x,0)
// ...
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
// ASSIGNW (__Vdlyvdim_x, dimension_number)
// ASSIGNW (__Vdlyvset_x, 1)
// ...
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
// -> VAR __Vdlyvset_x
// VAR __Vdlyvval_x
// VAR __Vdlyvdim_x
// VAR __Vdlyvlsb_x
// ASSIGNW (__Vdlyvset_x,0)
// ...
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
// ASSIGNW (__Vdlyvdim_x, dimension_number)
// ASSIGNW (__Vdlyvset_x, 1)
// ...
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
//
//*************************************************************************
@@ -71,76 +72,81 @@ class DelayedVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each module:
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal (valid when AstVarScope::user1p is valid)
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
// AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage
// AstVar::user2() -> bool. Set true if already made warning
// AstVarRef::user2() -> bool. Set true if already processed
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal (valid when AstAlwaysPost::user4p is valid)
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal
// (valid when AstVarScope::user1p is valid)
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
// AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage
// AstVar::user2() -> bool. Set true if already made warning
// AstVarRef::user2() -> bool. Set true if already processed
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal
// (valid when AstAlwaysPost::user4p is valid)
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
// Cleared each scope/active:
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
AstUser4InUse m_inuser4;
AstUser5InUse m_inuser5;
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
AstUser4InUse m_inuser4;
AstUser5InUse m_inuser5;
enum VarUsage { VU_NONE=0, VU_DLY=1, VU_NONDLY=2 };
// STATE
AstActive* m_activep; // Current activate
AstCFunc* m_cfuncp; // Current public C Function
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
bool m_inDly; // True in delayed assignments
bool m_inLoop; // True in for loops
bool m_inInitial; // True in intial blocks
AstActive* m_activep; // Current activate
AstCFunc* m_cfuncp; // Current public C Function
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
bool m_inDly; // True in delayed assignments
bool m_inLoop; // True in for loops
bool m_inInitial; // True in intial blocks
typedef std::map<std::pair<AstNodeModule*,string>,AstVar*> VarMap;
VarMap m_modVarMap; // Table of new var names created under module
V3Double0 m_statSharedSet;// Statistic tracking
VarMap m_modVarMap; // Table of new var names created under module
V3Double0 m_statSharedSet;// Statistic tracking
typedef std::map<AstVarScope*,int> ScopeVecMap;
ScopeVecMap m_scopeVecMap; // Next var number for each scope
ScopeVecMap m_scopeVecMap; // Next var number for each scope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void markVarUsage(AstVarScope* nodep, uint32_t flags) {
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
nodep->user5( nodep->user5() | flags );
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
nodep->v3warn(BLKANDNBLK,"Unsupported: Blocked and non-blocking assignments to same variable: "
<<nodep->varp()->prettyName());
}
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
nodep->user5( nodep->user5() | flags );
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
nodep->v3warn(BLKANDNBLK, "Unsupported: Blocked and non-blocking assignments to same variable: "
<<nodep->varp()->prettyNameQ());
}
}
AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name,
int width/*0==fromoldvar*/, AstNodeDType* newdtypep) {
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
AstVar* varp;
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
VarMap::iterator it = m_modVarMap.find(make_pair(addmodp,name));
if (it != m_modVarMap.end()) {
// Created module's AstVar earlier under some other scope
varp = it->second;
} else {
if (newdtypep) {
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep);
} else if (width==0) {
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, oldvarscp->varp());
varp->dtypeFrom(oldvarscp);
} else { // Used for vset and dimensions, so can zero init
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, VFlagBitPacked(), width);
}
addmodp->addStmtp(varp);
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
}
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
UASSERT_OBJ(oldvarscp->scopep(), oldvarscp, "Var unscoped");
AstVar* varp;
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
VarMap::iterator it = m_modVarMap.find(make_pair(addmodp, name));
if (it != m_modVarMap.end()) {
// Created module's AstVar earlier under some other scope
varp = it->second;
} else {
if (newdtypep) {
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
name, newdtypep);
} else if (width==0) {
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
name, oldvarscp->varp());
varp->dtypeFrom(oldvarscp);
} else { // Used for vset and dimensions, so can zero init
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
name, VFlagBitPacked(), width);
}
addmodp->addStmtp(varp);
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
}
AstVarScope* varscp = new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp);
oldvarscp->scopep()->addVarp(varscp);
return varscp;
oldvarscp->scopep()->addVarp(varscp);
return varscp;
}
AstActive* createActivePost(AstVarRef* varrefp) {
@@ -148,299 +154,304 @@ private:
m_activep->sensesp());
// Was addNext(), but addNextHere() avoids a linear search.
m_activep->addNextHere(newactp);
return newactp;
return newactp;
}
void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) {
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block");
if (oldactivep->sensesp() != m_activep->sensesp()) {
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
&& !varrefp->varp()->user2()) {
varrefp->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks with different clocking: "
<<varrefp->varp()->prettyName()<<endl
<<varrefp->warnMore()<<"... Location of first driving block"<<endl
<<oldactivep->warnMore()<<"... Location of other driving block");
varrefp->varp()->user2(true);
}
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
UINFO(4," Act: "<<m_activep<<endl);
UINFO(4," Act: "<<oldactivep<<endl);
// Make a new sensitivity list, which is the combination of both blocks
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
if (senb) treep->addSensesp(senb);
if (AstSenTree* storep = oldactivep->sensesStorep()) {
storep->unlinkFrBack();
pushDeletep(storep);
}
oldactivep->sensesStorep(treep);
oldactivep->sensesp(treep);
}
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
UASSERT_OBJ(oldactivep, varrefp, "<= old dly assignment not put under sensitivity block");
if (oldactivep->sensesp() != m_activep->sensesp()) {
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
&& !varrefp->varp()->user2()) {
varrefp->varp()->v3warn(
MULTIDRIVEN, "Signal has multiple driving blocks with different clocking: "
<<varrefp->varp()->prettyNameQ()<<endl
<<varrefp->warnOther()<<"... Location of first driving block"<<endl
<<varrefp->warnContextPrimary()<<endl
<<oldactivep->warnOther()<<"... Location of other driving block"<<endl
<<oldactivep->warnContextSecondary()
);
varrefp->varp()->user2(true);
}
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
UINFO(4," Act: "<<m_activep<<endl);
UINFO(4," Act: "<<oldactivep<<endl);
// Make a new sensitivity list, which is the combination of both blocks
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
if (senb) treep->addSensesp(senb);
if (AstSenTree* storep = oldactivep->sensesStorep()) {
storep->unlinkFrBack();
pushDeletep(storep);
}
oldactivep->sensesStorep(treep);
oldactivep->sensesp(treep);
}
}
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) {
// Create delayed assignment
// See top of this file for transformation
// Return the new LHS for the assignment, Null = unlink
// Find selects
AstNode* newlhsp = NULL; // NULL = unlink old assign
AstSel* bitselp = NULL;
AstArraySel* arrayselp = NULL;
// Create delayed assignment
// See top of this file for transformation
// Return the new LHS for the assignment, Null = unlink
// Find selects
AstNode* newlhsp = NULL; // NULL = unlink old assign
AstSel* bitselp = NULL;
AstArraySel* arrayselp = NULL;
if (VN_IS(lhsp, Sel)) {
bitselp = VN_CAST(lhsp, Sel);
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
} else {
} else {
arrayselp = VN_CAST(lhsp, ArraySel);
}
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) {
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
}
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
//
//=== Dimensions: __Vdlyvdim__
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
AstNode* dimselp=arrayselp;
UASSERT_OBJ(arrayselp, nodep, "No arraysel under bitsel?");
UASSERT_OBJ(!VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType), nodep,
"ArraySel with unpacked arrays should have been removed in V3Slice");
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
//
//=== Dimensions: __Vdlyvdim__
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
AstNode* dimselp = arrayselp;
for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) {
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
dimvalp.push_front(valp);
}
dimvalp.push_front(valp);
}
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
varrefp->unlinkFrBack();
AstVar* oldvarp = varrefp->varp();
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
//
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
AstNode* dimp = dimvalp[dimension];
UASSERT_OBJ(varrefp, nodep, "No var underneath arraysels");
UASSERT_OBJ(varrefp->varScopep(), varrefp, "Var didn't get varscoped in V3Scope.cpp");
varrefp->unlinkFrBack();
AstVar* oldvarp = varrefp->varp();
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
//
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
AstNode* dimp = dimvalp[dimension];
if (VN_IS(dimp, Const)) { // bit = const, can just use it
dimreadps.push_front(dimp);
} else {
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), NULL);
AstAssign* bitassignp
dimreadps.push_front(dimp);
} else {
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
bitvarname, dimp->width(), NULL);
AstAssign* bitassignp
= new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), bitvscp, true),
dimp);
nodep->addNextHere(bitassignp);
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
}
}
//
//=== Bitselect: __Vdlyvlsb__
AstNode* bitreadp=NULL; // Code to read Vdlyvlsb
if (bitselp) {
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
nodep->addNextHere(bitassignp);
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
}
}
//
//=== Bitselect: __Vdlyvlsb__
AstNode* bitreadp = NULL; // Code to read Vdlyvlsb
if (bitselp) {
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
if (VN_IS(bitselp->fromp(), Const)) { // vlsb = constant, can just push constant into where we use it
bitreadp = lsbvaluep;
} else {
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), NULL);
bitreadp = lsbvaluep;
} else {
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
bitvarname, lsbvaluep->width(), NULL);
AstAssign* bitassignp = new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), bitvscp, true),
lsbvaluep);
nodep->addNextHere(bitassignp);
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
}
}
//
//=== Value: __Vdlyvval__
AstNode* valreadp; // Code to read Vdlyvval
nodep->addNextHere(bitassignp);
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
}
}
//
//=== Value: __Vdlyvval__
AstNode* valreadp; // Code to read Vdlyvval
if (VN_IS(nodep->rhsp(), Const)) { // vval = constant, can just push constant into where we use it
valreadp = nodep->rhsp()->unlinkFrBack();
} else {
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
}
//
//=== Setting/not setting boolean: __Vdlyvset__
AstVarScope* setvscp;
AstAssignPre* setinitp = NULL;
valreadp = nodep->rhsp()->unlinkFrBack();
} else {
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
}
//
//=== Setting/not setting boolean: __Vdlyvset__
AstVarScope* setvscp;
AstAssignPre* setinitp = NULL;
if (nodep->user3p()) {
// Simplistic optimization. If the previous statement in same scope was also a =>,
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
// This is good for code like:
// for (i=0; i<5; i++) vector[i] <= something;
if (nodep->user3p()) {
// Simplistic optimization. If the previous statement in same scope was also a =>,
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
// This is good for code like:
// for (i=0; i<5; i++) vector[i] <= something;
setvscp = VN_CAST(nodep->user3p(), VarScope);
++m_statSharedSet;
} else { // Create new one
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
++m_statSharedSet;
} else { // Create new one
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
setinitp = new AstAssignPre(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, true),
new AstConst(nodep->fileline(), 0));
AstAssign* setassignp
AstAssign* setassignp
= new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, true),
new AstConst(nodep->fileline(),
V3Number(nodep->fileline(),1,true)));
nodep->addNextHere(setassignp);
}
if (m_nextDlyp) { // Tell next assigndly it can share the variable
m_nextDlyp->user3p(setvscp);
}
//
// Create ALWAYSPOST for delayed variable
// We add all logic to the same block if it's for the same memory
// This ensures that multiple assignments to the same memory will result
// in correctly ordered code - the last assignment must be last.
// It also has the nice side effect of assisting cache locality.
AstNode* selectsp = varrefp;
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
}
if (bitselp) {
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
bitselp->widthp()->cloneTree(false));
}
// Build "IF (changeit) ...
UINFO(9," For "<<setvscp<<endl);
UINFO(9," & "<<varrefp<<endl);
new AstConst(nodep->fileline(), AstConst::LogicTrue()));
nodep->addNextHere(setassignp);
}
if (m_nextDlyp) { // Tell next assigndly it can share the variable
m_nextDlyp->user3p(setvscp);
}
//
// Create ALWAYSPOST for delayed variable
// We add all logic to the same block if it's for the same memory
// This ensures that multiple assignments to the same memory will result
// in correctly ordered code - the last assignment must be last.
// It also has the nice side effect of assisting cache locality.
AstNode* selectsp = varrefp;
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
}
if (bitselp) {
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
bitselp->widthp()->cloneTree(false));
}
// Build "IF (changeit) ...
UINFO(9," For "<<setvscp<<endl);
UINFO(9," & "<<varrefp<<endl);
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
if (finalp) {
if (finalp) {
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
checkActivePost(varrefp, oldactivep);
if (setinitp) oldactivep->addStmtsp(setinitp);
} else { // first time we've dealt with this memory
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
UINFO(9," Created "<<finalp<<endl);
AstActive* newactp = createActivePost(varrefp);
newactp->addStmtsp(finalp);
varrefp->varScopep()->user4p(finalp);
finalp->user2p(newactp);
if (setinitp) newactp->addStmtsp(setinitp);
}
AstIf* postLogicp;
if (finalp->user3p() == setvscp) {
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
// we can share the IF statement too
checkActivePost(varrefp, oldactivep);
if (setinitp) oldactivep->addStmtsp(setinitp);
} else { // first time we've dealt with this memory
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
UINFO(9," Created "<<finalp<<endl);
AstActive* newactp = createActivePost(varrefp);
newactp->addStmtsp(finalp);
varrefp->varScopep()->user4p(finalp);
finalp->user2p(newactp);
if (setinitp) newactp->addStmtsp(setinitp);
}
AstIf* postLogicp;
if (finalp->user3p() == setvscp) {
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
// we can share the IF statement too
postLogicp = VN_CAST(finalp->user4p(), If);
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
UASSERT_OBJ(postLogicp, nodep,
"Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
postLogicp = new AstIf(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, false),
NULL, NULL);
UINFO(9," Created "<<postLogicp<<endl);
finalp->addBodysp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
}
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
return newlhsp;
UINFO(9," Created "<<postLogicp<<endl);
finalp->addBodysp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
}
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
return newlhsp;
}
// VISITORS
virtual void visit(AstNetlist* nodep) {
//VV***** We reset all userp() on the netlist
m_modVarMap.clear();
//VV***** We reset all userp() on the netlist
m_modVarMap.clear();
iterateChildren(nodep);
}
virtual void visit(AstScope* nodep) {
UINFO(4," MOD "<<nodep<<endl);
AstNode::user3ClearTree();
UINFO(4," MOD "<<nodep<<endl);
AstNode::user3ClearTree();
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
m_cfuncp = nodep;
m_cfuncp = nodep;
iterateChildren(nodep);
m_cfuncp = NULL;
m_cfuncp = NULL;
}
virtual void visit(AstActive* nodep) {
m_activep = nodep;
bool oldinit = m_inInitial;
m_inInitial = nodep->hasInitial();
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
m_activep = nodep;
bool oldinit = m_inInitial;
m_inInitial = nodep->hasInitial();
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
iterateChildren(nodep);
m_inInitial = oldinit;
m_inInitial = oldinit;
}
virtual void visit(AstAssignDly* nodep) {
m_inDly = true;
m_inDly = true;
m_nextDlyp = VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL.
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
if (VN_IS(nodep->lhsp(), ArraySel)
|| (VN_IS(nodep->lhsp(), Sel)
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newlhsp = createDlyArray(nodep, lhsp);
if (m_inLoop) nodep->v3warn(BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
if (newlhsp) {
nodep->lhsp(newlhsp);
} else {
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
lhsp->deleteTree(); VL_DANGLING(lhsp);
}
else {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newlhsp = createDlyArray(nodep, lhsp);
if (m_inLoop) nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
if (newlhsp) {
nodep->lhsp(newlhsp);
} else {
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
lhsp->deleteTree(); VL_DANGLING(lhsp);
}
else {
iterateChildren(nodep);
}
m_inDly = false;
m_nextDlyp = NULL;
}
m_inDly = false;
m_nextDlyp = NULL;
}
virtual void visit(AstVarRef* nodep) {
if (!nodep->user2Inc()) { // Not done yet
if (m_inDly && nodep->lvalue()) {
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
markVarUsage(nodep->varScopep(), VU_DLY);
if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block");
if (!nodep->user2Inc()) { // Not done yet
if (m_inDly && nodep->lvalue()) {
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
markVarUsage(nodep->varScopep(), VU_DLY);
UASSERT_OBJ(m_activep, nodep, "<= not under sensitivity block");
if (!m_activep->hasClocked()) {
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active");
}
AstVarScope* oldvscp = nodep->varScopep();
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
AstVarScope* oldvscp = nodep->varScopep();
UASSERT_OBJ(oldvscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
if (dlyvscp) { // Multiple use of delayed variable
if (dlyvscp) { // Multiple use of delayed variable
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
checkActivePost(nodep, oldactivep);
}
if (!dlyvscp) { // First use of this delayed variable
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
AstNodeAssign* prep
checkActivePost(nodep, oldactivep);
}
if (!dlyvscp) { // First use of this delayed variable
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
AstNodeAssign* prep
= new AstAssignPre(nodep->fileline(),
new AstVarRef(nodep->fileline(), dlyvscp, true),
new AstVarRef(nodep->fileline(), oldvscp, false));
AstNodeAssign* postp
AstNodeAssign* postp
= new AstAssignPost(nodep->fileline(),
new AstVarRef(nodep->fileline(), oldvscp, true),
new AstVarRef(nodep->fileline(), dlyvscp, false));
postp->lhsp()->user2(true); // Don't detect this assignment
oldvscp->user1p(dlyvscp); // So we can find it later
// Make new ACTIVE with identical sensitivity tree
AstActive* newactp = createActivePost(nodep);
dlyvscp->user2p(newactp);
newactp->addStmtsp(prep); // Add to FRONT of statements
newactp->addStmtsp(postp);
}
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
newrefp->user2(true); // No reason to do it again
nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep);
}
else if (!m_inDly && nodep->lvalue()) {
//UINFO(9,"NBA "<<nodep<<endl);
if (!m_inInitial) {
UINFO(4,"AssignNDlyVar: "<<nodep<<endl);
markVarUsage(nodep->varScopep(), VU_NONDLY);
}
}
}
postp->lhsp()->user2(true); // Don't detect this assignment
oldvscp->user1p(dlyvscp); // So we can find it later
// Make new ACTIVE with identical sensitivity tree
AstActive* newactp = createActivePost(nodep);
dlyvscp->user2p(newactp);
newactp->addStmtsp(prep); // Add to FRONT of statements
newactp->addStmtsp(postp);
}
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
newrefp->user2(true); // No reason to do it again
nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep);
}
else if (!m_inDly && nodep->lvalue()) {
//UINFO(9,"NBA "<<nodep<<endl);
if (!m_inInitial) {
UINFO(4,"AssignNDlyVar: "<<nodep<<endl);
markVarUsage(nodep->varScopep(), VU_NONDLY);
}
}
}
}
virtual void visit(AstNodeFor* nodep) {
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin");
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin");
}
virtual void visit(AstWhile* nodep) {
bool oldloop = m_inLoop;
m_inLoop = true;
bool oldloop = m_inLoop;
m_inLoop = true;
iterateChildren(nodep);
m_inLoop = oldloop;
m_inLoop = oldloop;
}
//--------------------
@@ -452,17 +463,17 @@ private:
public:
// CONSTUCTORS
explicit DelayedVisitor(AstNetlist* nodep) {
m_inDly = false;
m_activep=NULL;
m_cfuncp=NULL;
m_nextDlyp=NULL;
m_inLoop = false;
m_inInitial = false;
m_inDly = false;
m_activep = NULL;
m_cfuncp = NULL;
m_nextDlyp = NULL;
m_inLoop = false;
m_inInitial = false;
iterate(nodep);
}
virtual ~DelayedVisitor() {
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
}
};
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void delayedAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+61 -59
View File
@@ -20,10 +20,10 @@
// V3Depth's Transformations:
//
// Each module:
// For each wide OP, assign a temporary variable.
// For each deep expression, assign expression to temporary.
// For each wide OP, assign a temporary variable.
// For each deep expression, assign expression to temporary.
// Each CFunc:
// Any statements that need "this" are marked non-static
// Any statements that need "this" are marked non-static
//
//*************************************************************************
@@ -44,62 +44,64 @@ private:
// NODE STATE
// STATE
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current block
AstNode* m_stmtp; // Current statement
int m_depth; // How deep in an expression
int m_maxdepth; // Maximum depth in an expression
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current block
AstNode* m_stmtp; // Current statement
int m_depth; // How deep in an expression
int m_maxdepth; // Maximum depth in an expression
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void createDeepTemp(AstNode* nodep) {
UINFO(6," Deep "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout,"deep:");
UINFO(6," Deep "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout, "deep:");
string newvarname = (string("__Vdeeptemp")+cvtToStr(m_modp->varNumGetInc()));
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname,
// Width, not widthMin, as we may be in middle of BITSEL expression which
// though it's one bit wide, needs the mask in the upper bits.
// (Someday we'll have a valid bitmask instead of widths....)
// Width, not widthMin, as we may be in
// middle of BITSEL expression which though
// it's one bit wide, needs the mask in the
// upper bits. (Someday we'll have a valid
// bitmask instead of widths....)
// See t_func_crc for an example test that requires this
VFlagLogicPacked(), nodep->width());
if (!m_funcp) nodep->v3fatalSrc("Deep expression not under a function");
m_funcp->addInitsp(varp);
// Replace node tree with reference to var
UASSERT_OBJ(m_funcp, nodep, "Deep expression not under a function");
m_funcp->addInitsp(varp);
// Replace node tree with reference to var
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, false);
nodep->replaceWith(newp);
// Put assignment before the referencing statement
nodep->replaceWith(newp);
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), varp, true),
nodep);
AstNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
linker2.relink(assp);
AstNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
linker2.relink(assp);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) {
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_funcp = NULL;
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_funcp = NULL;
iterateChildren(nodep);
m_modp = NULL;
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
m_funcp = nodep;
m_depth = 0;
m_maxdepth = 0;
m_funcp = nodep;
m_depth = 0;
m_maxdepth = 0;
iterateChildren(nodep);
m_funcp = NULL;
m_funcp = NULL;
}
void visitStmt(AstNodeStmt* nodep) {
m_depth = 0;
m_maxdepth = 0;
m_stmtp = nodep;
m_depth = 0;
m_maxdepth = 0;
m_stmtp = nodep;
iterateChildren(nodep);
m_stmtp = NULL;
m_stmtp = NULL;
}
virtual void visit(AstNodeStmt* nodep) {
if (!nodep->isStatement()) {
@@ -112,44 +114,44 @@ private:
virtual void visit(AstNodeTermop* nodep) {
}
virtual void visit(AstNodeMath* nodep) {
// We have some operator defines that use 2 parens, so += 2.
m_depth += 2;
if (m_depth>m_maxdepth) m_maxdepth=m_depth;
// We have some operator defines that use 2 parens, so += 2.
m_depth += 2;
if (m_depth>m_maxdepth) m_maxdepth = m_depth;
iterateChildren(nodep);
m_depth -= 2;
m_depth -= 2;
if (m_stmtp
&& (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
&& (m_maxdepth-m_depth) > v3Global.opt.compLimitParens()
if (m_stmtp
&& (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
&& (m_maxdepth-m_depth) > v3Global.opt.compLimitParens()
&& !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it
) {
m_maxdepth = m_depth;
createDeepTemp(nodep);
}
) {
m_maxdepth = m_depth;
createDeepTemp(nodep);
}
}
//--------------------
// Marking of non-static functions (because they might need "this")
// (Here instead of new vistor after V3Descope just to avoid another visitor)
void needNonStaticFunc(AstNode* nodep) {
if (!m_funcp) nodep->v3fatalSrc("Non-static accessor not under a function");
UASSERT_OBJ(m_funcp, nodep, "Non-static accessor not under a function");
if (m_funcp->isStatic().trueU()) {
UINFO(5,"Mark non-public due to "<<nodep<<endl);
m_funcp->isStatic(false);
}
UINFO(5,"Mark non-public due to "<<nodep<<endl);
m_funcp->isStatic(false);
}
}
virtual void visit(AstUCFunc* nodep) {
needNonStaticFunc(nodep);
needNonStaticFunc(nodep);
iterateChildren(nodep);
}
virtual void visit(AstUCStmt* nodep) {
needNonStaticFunc(nodep);
visitStmt(nodep);
needNonStaticFunc(nodep);
visitStmt(nodep);
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
}
@@ -157,12 +159,12 @@ private:
public:
// CONSTUCTORS
explicit DepthVisitor(AstNetlist* nodep) {
m_modp=NULL;
m_funcp=NULL;
m_stmtp=NULL;
m_depth=0;
m_maxdepth=0;
//
m_modp = NULL;
m_funcp = NULL;
m_stmtp = NULL;
m_depth = 0;
m_maxdepth = 0;
//
iterate(nodep);
}
virtual ~DepthVisitor() {}
+1 -1
View File
@@ -34,4 +34,4 @@ public:
static void depthAll(AstNetlist* nodep);
};
#endif // Guard
#endif // Guard
+48 -48
View File
@@ -20,7 +20,7 @@
// V3DepthBlock's Transformations:
//
// Each module:
// For each deep block, create cfunc including that block.
// For each deep block, create cfunc including that block.
//
//*************************************************************************
@@ -42,69 +42,69 @@ private:
// NODE STATE
// STATE
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current function
int m_depth; // How deep in an expression
int m_deepNum; // How many functions made
AstNodeModule* m_modp; // Current module
AstCFunc* m_funcp; // Current function
int m_depth; // How deep in an expression
int m_deepNum; // How many functions made
// METHODS
VL_DEBUG_FUNC; // Declare debug()
AstCFunc* createDeepFunc(AstNode* nodep) {
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
// Create function
string name = m_funcp->name()+"__deep"+cvtToStr(++m_deepNum);
AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, NULL);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->symProlog(true);
funcp->slow(m_funcp->slow());
funcp->addStmtsp(nodep);
m_modp->addStmtp(funcp);
// Call it at the point where the body was removed from
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
callp->argTypes("vlSymsp");
UINFO(6," New "<<callp<<endl);
//
relinkHandle.relink(callp);
return funcp;
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
// Create function
string name = m_funcp->name()+"__deep"+cvtToStr(++m_deepNum);
AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, NULL);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->symProlog(true);
funcp->slow(m_funcp->slow());
funcp->addStmtsp(nodep);
m_modp->addStmtp(funcp);
// Call it at the point where the body was removed from
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
callp->argTypes("vlSymsp");
UINFO(6," New "<<callp<<endl);
//
relinkHandle.relink(callp);
return funcp;
}
// VISITORS
virtual void visit(AstNodeModule* nodep) {
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_deepNum = 0;
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_deepNum = 0;
iterateChildren(nodep);
m_modp = NULL;
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
// We recurse into this.
int lastDepth = m_depth;
AstCFunc* lastFuncp = m_funcp;
{
m_depth = 0;
m_funcp = nodep;
// We recurse into this.
int lastDepth = m_depth;
AstCFunc* lastFuncp = m_funcp;
{
m_depth = 0;
m_funcp = nodep;
iterateChildren(nodep);
}
m_depth = lastDepth;
m_funcp = lastFuncp;
}
m_depth = lastDepth;
m_funcp = lastFuncp;
}
void visitStmt(AstNodeStmt* nodep) {
m_depth++;
if (m_depth > v3Global.opt.compLimitBlocks()
m_depth++;
if (m_depth > v3Global.opt.compLimitBlocks()
&& !VN_IS(nodep, CCall)) { // Already done
UINFO(4, "DeepBlocks "<<m_depth<<" "<<nodep<<endl);
AstNode* backp = nodep->backp(); // Only for debug
if (debug()>=9) backp->dumpTree(cout,"- pre : ");
AstCFunc* funcp = createDeepFunc(nodep);
UINFO(4, "DeepBlocks "<<m_depth<<" "<<nodep<<endl);
AstNode* backp = nodep->backp(); // Only for debug
if (debug()>=9) backp->dumpTree(cout, "- pre : ");
AstCFunc* funcp = createDeepFunc(nodep);
iterate(funcp);
if (debug()>=9) backp->dumpTree(cout,"- post: ");
if (debug()>=9) funcp->dumpTree(cout,"- func: ");
} else {
if (debug()>=9) backp->dumpTree(cout, "- post: ");
if (debug()>=9) funcp->dumpTree(cout, "- func: ");
} else {
iterateChildren(nodep);
}
m_depth--;
}
m_depth--;
}
virtual void visit(AstNodeStmt* nodep) {
if (!nodep->isStatement()) {
@@ -117,7 +117,7 @@ private:
virtual void visit(AstNodeMath* nodep) {} // Accelerate
//--------------------
// Default: Just iterate
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
}
@@ -129,7 +129,7 @@ public:
m_funcp = NULL;
m_depth = 0;
m_deepNum = 0;
//
//
iterate(nodep);
}
virtual ~DepthBlockVisitor() {}

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