Compare commits

...
286 Commits
Author SHA1 Message Date
Ryszard Rozak ba508c00d2 Support until_with property (#7290 partial) (#7436) 2026-04-17 06:33:36 -04:00
Yilou Wang 72952fd3fb Fix inline constraint on array-indexed randomize target (#7431) (#7434)
Fixes #7431.
2026-04-16 11:02:22 -04:00
Kamil Danecki 3587ac48a4 Fix modification of members of object with const handle (#7433)
Signed-off-by: Kamil Danecki <[email protected]>
2026-04-16 09:43:12 -04:00
Wilson Snyder 4fe121e5aa Commentary: Changes update 2026-04-15 17:46:08 -04:00
Wilson Snyder 1011ea86fa Commentary (#7428) (#7432) 2026-04-15 17:45:48 -04:00
github action 885fbaf075 Apply 'make format' 2026-04-15 12:11:06 +00:00
Rowan Goemans c143c2fdd2 Fix side-effect loss when slicing array expressions (#7427) (#7429)
Fixes #7427.
2026-04-15 08:10:11 -04:00
Todd Strader 7f571971ca Optimize trace code for faster compiles on repeated types (#6707) (#6832) 2026-04-14 19:16:21 -04:00
Yilou Wang 38b7734530 Fix subclass with rand_mode(0) getting randomized (#7376) (#7383)
Fixes #7376.
2026-04-14 16:47:24 -04:00
Geza Lore ab0264deca Optimize more Dfg peephole patterns (#7423) 2026-04-14 16:22:54 +01:00
Wilson Snyder 7e3400b37d Fix virtual interface implied comparison with null (#7421).
Fixes #7421.
2026-04-14 07:25:02 -04:00
Yilou Wang 9eb2ba4c54 Support 2**n expressions in constraint randomization (#7422) 2026-04-14 07:17:53 -04:00
Wilson Snyder 2770e649ba Commentary: Changes update 2026-04-13 21:09:24 -04:00
Wilson Snyder ec4f66120c Test: Remove old Makefile rules 2026-04-13 21:09:09 -04:00
Wilson Snyder 232e6b55fa CI: Fix log_changes including duplicate bug numbers 2026-04-13 21:07:53 -04:00
Wilson Snyder 023b2edd48 Add newline on alarm timeouts to help error parsers 2026-04-13 21:07:03 -04:00
Yilou Wang 6ba45d3383 Support per-process RNG for process::srandom() and object seeding (#7408) (#7415)
Fixes #7408.
2026-04-13 13:58:53 -04:00
em2machine fd7a3f4a16 Fix zero-size parameter (#7387 repair) (#7411) (#7418)
Fixes #7411.
2026-04-13 13:19:11 -04:00
dependabot[bot] 369a315c27 CI: Bump actions/create-github-app-token in the everything group (#7419) 2026-04-13 13:00:35 -04:00
Yilou Wang 83b2061a35 Fix std::randomize treated as this.randomize in parameterized-derived class (#7409) (#7416)
Fixes #7409.
2026-04-13 11:34:17 -04:00
Leela Pakanati 14e2f834e9 Fix parameterized class typedef as interface type parameter (#7000) (#7006)
Fixes #7000.
2026-04-12 20:38:27 -04:00
Wilson Snyder fac07970e6 Improve too-small cast warning to show size 2026-04-12 18:12:41 -04:00
Wilson Snyder be7d26c5be Fix delete inside foreach skipping elements (#7404) (#7410)
Fixes #7404
2026-04-11 22:42:50 -04:00
Wilson Snyder fed8275e42 Internals: Refactor createForeachAssoc. No functional change intended. 2026-04-11 18:10:05 -04:00
Wilson Snyder 0891d51c75 Commentary: Changes update 2026-04-11 17:47:39 -04:00
Wilson Snyder d2c3d134c9 Tests: Speed up t_vpi_force build. No coverage change. 2026-04-11 07:51:39 -04:00
em2machine dc33e8bb18 Fix for Returning an object of the wrong type from a static function of a parameterized class (#5479) (#7387) 2026-04-11 07:49:45 -04:00
github action 529a6b7f93 Apply 'make format' 2026-04-10 23:21:56 +00:00
Shogo Yamazaki b886367128 Fix CMake compiler coroutine flags (#7404) 2026-04-10 19:20:54 -04:00
github action 89c93980f3 Apply 'make format' 2026-04-10 16:18:06 +00:00
Nikolay Puzanov a8f62703ab Fix virtual class inheritance (#7403) (#7405)
Fixes #7403.
2026-04-10 12:17:06 -04:00
em2machine 081ecbd095 Fix for elaboration hang (#7385 repair) #7398 (#7406)
Fixes #7398.
2026-04-10 11:03:14 -04:00
Geza Lore 6595e5cf55 Remove Dfg extract pass (#7394)
This has no measurable benefit and algorithmically will be superseded by
some upcoming work.
2026-04-10 13:46:59 +01:00
Ryszard Rozak 4da31b0418 Support 'until' property (partial #7290) (#7399) 2026-04-10 08:17:54 -04:00
Christian Hecken cf9a52cb92 Fix vpi_put_value not updating forced read value (#7092) (#7395)
Fixes #7092.
2026-04-09 19:08:36 -04:00
Yilou Wang 854e80c3c2 Support nonconsecutive repetition [=N] in sequence expressions (#7397) 2026-04-09 18:28:28 -04:00
Wilson Snyder ecf6d9b674 Commentary: Changes update 2026-04-09 17:50:40 -04:00
Artur Bieniek f32a692ce3 Fix $finish to immediately stop executing code from non-final blocks (#7213 partial) (#7390). 2026-04-09 17:49:57 -04:00
github action bb84bb643b Apply 'make format' 2026-04-09 16:33:37 +00:00
Geza Lore 1b4f574b31 Internals: Improve Dfg pattern dumps 2026-04-09 17:32:26 +01:00
Yilou Wang d20d765dd0 Support first_match sequence operator (#7392) 2026-04-09 11:42:43 -04:00
Wilson Snyder 426cc29d7a Revert "Fix $finish to immediately stop executing code from non-final block…" (#7393)
This reverts commit afd75ed1b8.
2026-04-09 11:38:07 -04:00
Artur Bieniek afd75ed1b8 Fix $finish to immediately stop executing code from non-final blocks (#7213 partial) (#7390)
Signed-off-by: Artur Bieniek <[email protected]>
2026-04-09 11:34:27 -04:00
Geza Lore fb66174d80 Internals: Run Dfg passes on all components at once
This is just a reordering of pass applications to make --stats more
useful. No functional change.
2026-04-09 15:22:10 +01:00
Ryszard Rozak cf6a2aec19 Internals: Clean up sampling. No functional change intended (#7389) 2026-04-09 08:49:07 -04:00
Geza Lore 9f9532ff78 Optimize Dfg only once, after V3Scope (#7362) 2026-04-09 08:31:12 -04:00
Yilou Wang e63c4f563e Support consecutive repetition [*N:M], [+], and [*] in sequence expressions (#7379) 2026-04-08 10:26:03 -04:00
Ryszard Rozak 7dcf586807 Fix sampling of hierarchical references (#7386) 2026-04-08 07:09:25 -04:00
Yilou Wang 141fe8bdad Support sequence intersect operator (#7374) 2026-04-08 09:31:54 +02:00
em2machine 2736262b98 Fix resolving default/nondefault type-of-type parameters (#7380) (#7385) 2026-04-07 17:58:36 -04:00
Artur Bieniek 8c11d0d0bd Support rise/fall delays (#7368)
Signed-off-by: Artur Bieniek <[email protected]>
2026-04-07 06:44:52 -04:00
Yilou Wang dfb7b034a5 Support sequence 'throughout' operator (#7378) 2026-04-06 17:12:22 -04:00
Yilou Wang 72b2ca5585 Support ##[*], ##[+], ##[M:$] unbounded cycle delays in SVA properties (#7377) 2026-04-06 16:31:43 -04:00
Christian Hecken 0df0064d64 Fix VPI access to Verilog force-ed signals (#7381) 2026-04-06 10:51:01 -04:00
Wilson Snyder 9f4546fcb9 Fix constraint 'with' in parameter classes (#7375) 2026-04-04 21:03:44 -04:00
Wilson Snyder 5b9cd12530 Internals: Remove mutex, part of last commit 2026-04-04 18:10:47 -04:00
Wilson Snyder 33493cf5b4 Add +verilator+solver+file (#7242).
Fixes #7242.
2026-04-04 17:26:43 -04:00
Wilson Snyder 2796294396 Fix string inside queue (#7373).
Fixes #7373.
2026-04-04 14:43:06 -04:00
Wilson Snyder 94f3e16a6c Commentary: Changes update 2026-04-04 14:42:11 -04:00
github action 4b6bc1ff83 Apply 'make format' 2026-04-04 18:32:27 +00:00
Yilou Wang 3b454a6f60 Internals: Add isNonPackedArray(). No functional change. (#7334 prep) 2026-04-04 14:31:09 -04:00
Yilou Wang adb48046c2 Fix parameter default comparison when value contains type cast (#6281) (#7369)
Fixes #6281.
2026-04-04 11:02:20 -04:00
Wilson Snyder de8d3c9356 Commentary: Changes update 2026-04-03 20:16:23 -04:00
Wilson Snyder 00bf59ac92 Internals: Cleanup some V3Simulate branches. No functional change intended. 2026-04-03 20:16:10 -04:00
Wilson Snyder ed0506ea8d Internals: Use Var flag instead of magic names. No functional change intended. 2026-04-03 19:05:56 -04:00
Yilou Wang efd60df2be Fix virtual interface select from sub-interface instance (#7203) (#7370)
Fixes #7203.
2026-04-03 19:04:10 -04:00
em2machine e7a644a3fc Fix functions in generate block resulting in "Broken link in node" (#7236) (#7367)
Fixes #7236
2026-04-03 11:19:17 -04:00
Yilou Wangandgithub action 56ed47ee7c Fix false ASSIGNIN on interface input port connections (#7365)
* add oneline fix

* Apply 'make format'

* merge test and update 2 space indents

---------

Co-authored-by: github action <[email protected]>
2026-04-02 20:44:48 +02:00
Yilou Wang 1e5c93cc51 Fix virtual interface function calls binding to wrong instance (#7363) 2026-04-02 10:53:01 -04:00
em2machine 32672deb6f Fix resolving default/nondefault type parameters (#7171) (#7346)
Fixes #7171
2026-04-02 10:51:11 -04:00
Geza Lore d7c484ae85 Fix missing temporary for DfgSplicePacked (#7361) 2026-04-01 22:44:58 +01:00
Geza Lore 2e151c3b74 Do not unroll simple array assignments in V3Slice (#7359)
See also #5644
2026-04-01 22:35:29 +01:00
Yilou Wang 894f6c4c58 Fix virtual interface member trigger convergence (#5116) (#7323) 2026-04-01 21:42:42 +01:00
Geza Lore 6aa6c45c73 Internals: Add DfgGraph::neighborhood for debugging 2026-04-01 10:53:30 +01:00
Geza Lore b4a0ca8ba6 Optimize Ast read references in Dfg directly (#7354)
Introduce a new DfgAstRd vertex, which holds an AstNodeExpr that is a
reference to a variable. This enables tracking all read references in
Dfg, which both enables more optimization, and allows inlining of
expressions in place of the reference more intelligently (e.g, when the
expression is only used once, and is not in a loop). This can get rid of
20-30% of temporary variables introduced in Dfg in some designs. Note
V3Gate later got rid of a lot of those, this is a step towards making
V3Gate redundant. The more intelligent expression inlining is worth ~10%
runtime speed on some designs.
2026-04-01 10:52:56 +01:00
Wilson Snyder dbd4823323 Fix error on illegal nand/nor binary operators (#7353).
Fixes #7353.
2026-03-31 18:35:27 -04:00
github action 1096740113 Apply 'make format' 2026-03-30 23:12:27 +00:00
Wilson Snyder 62ffe43a82 Fix port assignment to large arrays (#6904).
Fixes #6904.
2026-03-30 19:09:23 -04:00
Wilson Snyder 6aa1690745 Tests: Add t_inst_port_reverse (#5877) 2026-03-30 19:09:22 -04:00
Wilson Snyder dc67dc6dc8 Internals: Remove very old VL_SIG* when under VL_NO_LEGACY 2026-03-30 19:09:22 -04:00
Wilson Snyder c8a596e43e CI: Fix ubuntu-24.04-riscv needing install --yes, no systemc 2026-03-30 19:09:22 -04:00
Wilson Snyder 2458819794 Commentary: Changes update 2026-03-30 19:09:13 -04:00
dependabot[bot] ff37860e0c CI: Bump the everything group with 2 updates (#7347) 2026-03-30 14:06:41 -04:00
Yilou Wang 04f410622b Fix wait() hang when interface with combinational logic using process calls and VIF function (#7342) 2026-03-30 15:34:32 +01:00
Yilou Wang bf453361f6 Support consecutive repetition [*N] in SVA properties (#7311) 2026-03-30 07:01:33 -04:00
Artur Bieniek 55958efbe1 Fix false sensitivity of signals to unrelated interface members 2026-03-30 09:42:51 +01:00
Wilson Snyder be6780e44b Tests: Add t_interface_twoports (#5676) 2026-03-29 21:02:12 -04:00
Wilson Snyder d55790c91b Tests: Add t_interface_update (#2765) 2026-03-29 20:52:29 -04:00
Wilson Snyder 86eb6f060f Tests: Update t_disable_inside (#6591) 2026-03-29 20:34:19 -04:00
Wilson Snyder 600eb7ec86 Internals: Favor std::swap. No functional change. 2026-03-29 20:33:06 -04:00
Wilson Snyder 5036af3d37 Commentary: Refer to latest standard where can 2026-03-29 18:06:12 -04:00
Wei-Lun Chiu 092d3d4de0 Support map() method for associative and wildcard arrays (#7344) 2026-03-29 04:00:52 -04:00
Wilson Snyder e0b4d5ad44 Fix lost $stop on implied assertion $error failures. 2026-03-28 10:57:59 -04:00
Wilson Snyder 31757df229 Internals: clangtidy cleanups. No functional change intended (#7343) 2026-03-27 23:14:18 -04:00
Wilson Snyder 82c817d425 Internals: Refactor VPI case statement. No functional change intended 2026-03-27 21:51:34 -04:00
Wilson Snyder 8d109e85d0 Fix missing quotes on VPI error signal names 2026-03-27 21:44:11 -04:00
Wilson Snyder e826a47ad6 Fix broken out-of-bounds scan check 2026-03-27 21:43:11 -04:00
Wilson Snyder 91e6e3aca8 CI: Add note about failure 2026-03-27 21:42:14 -04:00
Wilson Snyder 087dae2a5d Commentary: Changes update 2026-03-27 21:41:52 -04:00
Nick Brereton f66df9e70e Fix disable iff imply-delay statement linking (#7337) 2026-03-27 14:31:03 -04:00
Yilou Wang ba9a7aaa66 Fix static initialization order for packages with class hierarchies (#7324) 2026-03-27 12:53:45 -04:00
Yilou Wang 55e5f01758 Support SVA goto repetition [->N] in concurrent assertions (#7310) 2026-03-27 10:31:15 -04:00
Geza Lore 9ed7a9f98b Internals: Do not optimize variables with RW refs in Dfg 2026-03-27 07:50:05 +00:00
Geza Lore f12cdac8da Optimize muxes in DfgPeephole 2026-03-27 07:50:05 +00:00
Geza Lore 2759f2ab16 Optimize comparisons with identical operands in Dfg 2026-03-27 07:50:05 +00:00
Geza Lore e0fc69a2f0 Optimize CountOnes in Dfg 2026-03-27 07:50:05 +00:00
Geza Lore 382f5003ca Optimize more patterns in DfgPeephole (#7332) 2026-03-27 07:47:50 +00:00
github action 057039e81c Apply 'make format' 2026-03-27 05:33:21 +00:00
Eunseo Song c28ed348fd Tests: Add regression test for PCH with output-split (#7251) (#7328) 2026-03-27 01:32:19 -04:00
Wilson Snyder 7fc76dd9d3 Fix Ubuntu 26.04.beta issues 2026-03-26 22:33:20 -04:00
Wilson Snyder c5364254bb Commentary: Changes update 2026-03-26 21:43:16 -04:00
Jakub Michalski 5d2d05236e Support very wide $display arguments (#7280) 2026-03-26 13:55:14 -04:00
Geza Lore e5adb60461 Internals: Only add VAR_PORT_DTYPE prior to V3Width (#7331) 2026-03-26 13:44:11 -04:00
github action 37a316f0f8 Apply 'make format' 2026-03-26 17:28:43 +00:00
Eunseo Song f369a629c7 Improve E_UNSUPPORTED warning messages (#7329) 2026-03-26 13:25:30 -04:00
Yilou Wang e0963bd587 Fix false ASSIGNIN on interface input ports driven from outside (#7322) 2026-03-26 12:30:16 -04:00
Eunseo Song fbc3b3618d Fix Apple clang PCH compile error with -o flag (#7251) (#7327)
Apple clang rejects `-o` when a precompiled header (.gch) is involved,
reporting "cannot specify -o when generating multiple output files".
Remove the unnecessary `-o $@` from the two PCH build rules; the
`%.o: %.cpp` pattern already implies the correct output name.
2026-03-26 11:52:21 -04:00
Yilou Wang 3ddf7ad6ec Support ##[M:N] range cycle delay in SVA sequences (#7312) 2026-03-26 10:08:22 -04:00
Geza Lore afa071a822 Optimize DfgPeephole until fixed point (#7309) 2026-03-26 06:56:36 +00:00
Yilou Wang 728ddf3331 Fix modport selection of virtual interface handle (#7321) 2026-03-25 07:16:52 -04:00
Artur Bieniek 860ac67c04 Fix linking shared library with its dependencies (#7320)
Signed-off-by: Artur Bieniek <[email protected]>
2026-03-25 07:15:24 -04:00
Geza Lore d33a81d32a Optimize commutative vertex operands in Dfg for better combining 2026-03-25 08:09:37 +00:00
Geza Lore d00f23a2b2 Internals: Remove unused vertices immediately in DfgPeephole 2026-03-25 08:09:37 +00:00
Geza Lore e508d2a6d7 Internals: Use a custom worklist in DfgPeephole 2026-03-25 08:09:37 +00:00
Geza Lore 855884f748 Internals: Simplify DfgPeephole replacement scheme
Always only replace the currently iterated node. This ensures it is not
on the work list, which simplifies upcoming work.

No functional of performance change intended.
2026-03-25 08:09:37 +00:00
Nick Brereton 24918b83be Fix typedef scope resolution for parameterized class aliases (#5977) (#7319) 2026-03-24 17:25:40 -07:00
github action fd2dfd6982 Apply 'make format' 2026-03-24 18:33:07 +00:00
Tracy Narine c58df87c27 Fix MacOs lexer compile error (#7314) (#7315) 2026-03-24 11:30:15 -07:00
Geza Lore 5060f884b5 Internals: Do not format configure generated files
These are in .gitignore, but formatting them causes a change that breaks
some versions of ccache with clang/PCH
2026-03-24 16:56:09 +00:00
Geza Lore 464ef88193 CI: Run RTLMeter when pushing to a 'ci-rtlmeter/**' branch
This enables running the RTLMeter workflow on forks by just pushing to a
branch of this form, without having to go and manually trigger in the
web UI.
2026-03-24 16:56:09 +00:00
Artur Bieniek aff85cef19 Support more than one dot in defparam (#7262)
Signed-off-by: Artur Bieniek <[email protected]>
2026-03-24 09:20:46 -04:00
Yilou Wang 0b2bf991a6 Support boolean and/or in sequence expressions (#7285) 2026-03-24 08:56:14 -04:00
Wei-Lun Chiu 6873dc2f63 Support array map() method (#7307) (#7316) 2026-03-24 02:38:50 -07:00
Ryszard Rozak 9a01a4054a Improve error message when variable used as data type (#7318)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-03-24 01:45:09 -07:00
Nick Brereton 3b328d2bb6 Support disable task by name (#6853) (#7136) 2026-03-23 19:56:31 -07:00
Wilson Snyder 716b404256 Optimize away empty ctor_var_reset.
Fixes #7154.
2026-03-23 18:10:34 -04:00
Geza Lore 06263ec724 Tests: Set MallocNanoZone=0 on macOS when using asan 2026-03-22 17:09:02 +00:00
Marco Bartoli ee7ec08cf5 Add macOS support for AddressSanitizer memory limit (#7308) 2026-03-22 10:05:23 -07:00
Geza Lore b9c8560d12 Internals: Simplify V3DfgCache 2026-03-22 16:01:22 +00:00
Yilou Wang 921607fd35 Support property-local variables and sequence match items (#7286) 2026-03-22 06:21:57 -07:00
Geza Lore 157fa9e4c5 Optimize duplicate vertices in DfgPeephole (#7305) 2026-03-21 22:45:02 +00:00
Geza Lore 13ceeb6add Tests: Add t_trace_lib* tests
Prep #7299
2026-03-21 22:21:00 +00:00
github action a3222bee94 Apply 'make format' 2026-03-21 15:00:48 +00:00
Wilson Snyder 947cbaf330 Deprecate --structs-packed (#7222). 2026-03-21 10:59:27 -04:00
Wilson Snyder 7e71bd1469 Commentary: Changes update 2026-03-21 10:56:13 -04:00
em2machine 05f640459e Fix interface data type consistency (#6965) (#7302) 2026-03-21 10:34:36 -04:00
Geza Lore 4dae9ed4e9 Optimize reuse of existing associative terms in DfgPeephole
Enable V3DfgCache to look up vertices without creating one. Reuse
existing terms in associative expression trees if they already exist
somewhere in the graph.
2026-03-21 11:09:32 +00:00
Geza Lore 92172e32c4 Internals: Add separate statistics for scoped Dfg stages 2026-03-21 10:49:39 +00:00
Geza Lore 13cf9ba075 Internals: Type check vertices made in DfgPeephole 2026-03-21 10:15:18 +00:00
Geza Lore fa30e0c9b5 Internals: Make Dfg type checking available on single vertex
No functional change
2026-03-21 10:13:49 +00:00
Geza Lore 416b30d884 Internals: Add utility to perform bisection search for debugging (#7294) 2026-03-21 10:13:27 +00:00
Christian Hecken 086bf351f2 Fix VPI force of bit-selected signals (#7211) (#7301) 2026-03-20 17:24:45 -07:00
Yilou Wang 9ea7abd1c7 Support ##0 cycle delays (#4263) (#7298) 2026-03-20 18:29:20 -04:00
Igor Zaworski c3fc0d9f0f Fix coroutine trace setters (#7078 repair) (#7296) 2026-03-20 09:23:32 -07:00
Igor Zaworski 331cac2054 Internals: Four-state pre-pull cleanup (#7281) 2026-03-20 12:11:22 -04:00
Yilou Wang b71abb0032 Fix local:: false error in randomize() with on parameterized class (#6680) (#7293)` 2026-03-20 10:25:46 -04:00
Yilou Wang a8bccab8e6 Support named sequence declarations and instances in assertions (#7283) 2026-03-20 10:24:46 -04:00
Yilou Wang 25c3bc814e Support ## delay on implication RHS in SVA properties (#7284) 2026-03-20 09:53:49 -04:00
Yilou Wang 998ec5b1d7 Fix streaming with descending unpacked arrays and unpacked-to-queue (#7287) 2026-03-20 09:51:35 -04:00
Wilson Snyder 9180eebdba Internals: MSVC cleanups 2026-03-19 22:48:12 -04:00
Wilson SnyderandMaksim Fonarev 94f8181cff Fix null assignment to virtual interfaces (#5974) (#5990). [Maxim Fonarev]
Fixes #5974.

Co-authored-by: Maksim Fonarev <[email protected]>
2026-03-19 20:29:02 -04:00
Christian Hecken 49716995fa Fix memory leaks in vpi_handle_by_name and vpi_handle_by_multi_index (#7187 repair) (#7289) 2026-03-19 20:19:36 -04:00
Wilson Snyder ef1f6d59b2 warning disable 2026-03-19 20:16:26 -04:00
Wilson Snyder adcbc86016 Tests: Update t_dpi_open (#7261 test) 2026-03-18 21:01:07 -04:00
Wilson Snyder 7b2277f584 Commentary: Changes update 2026-03-18 20:35:08 -04:00
Wilson Snyder 087cabcf35 Tests: Remove some vltmt-scenario runs. 2026-03-18 19:20:52 -04:00
Yilou Wang a0a684109f Support modport export/import task prototypes and out-of-block definitions (#7277) 2026-03-18 19:20:34 -04:00
Yilou Wang b8ca9292a4 Fix StreamR LHS assertion when source is narrower than destination (#7276) (#7282) 2026-03-18 15:15:27 -04:00
Igor Zaworski 907e775aa6 Internals: Add --fourstate flag and FUTURE warning (#7279) 2026-03-18 13:45:36 -04:00
em2machine a2154e9119 Fix interface derived types passed as parameters to generate loop module instantiation (#7273) 2026-03-18 09:26:55 -04:00
Yilou Wang 4b34bfffcb Fix soft constraint relaxation dropping compatible constraints (#7271)
* Fix soft constraint relaxation dropping compatible constraints

* patch changes the soft handle ordering so update the out files
2026-03-18 10:15:50 +01:00
Christian Hecken 612d1611b6 Fix VPI access to multidimensional packed arrays with ascending indices (#7275) 2026-03-17 23:30:05 -04:00
Yilou Wang 3bb0ea63ad Fix queue slice LHS assignment being silently discarded (#7270) 2026-03-17 15:10:49 -04:00
Yilou Wang 8925762077 Fix rand_mode(0) on sub-object members not preventing solver write-back (#7272) 2026-03-17 15:09:14 -04:00
Yilou Wangandgithub action 316fb02c60 Support assignment patterns as comparison operands (#7269)
* Support assignment patterns as EQ/NEQ comparison operands

* Apply 'make format'

* update the .out file

* add both-sides case

---------

Co-authored-by: github action <[email protected]>
2026-03-17 19:36:54 +01:00
Yilou Wang ef5281ab73 Fix false illegally-wrapped-around error for signed enums whose auto-incremented values cross zero. (#7268) 2026-03-17 19:36:24 +01:00
Wilson Snyder f4b09cffa7 Tests: Fix race in t_select_sideeffect. 2026-03-16 22:23:07 -04:00
Wilson Snyder de2c891ca5 Commentary: Changes update 2026-03-16 22:21:51 -04:00
Wilson Snyder e4bc792732 Tests: Limit test.build_jobs based on number of tests running 2026-03-16 18:50:41 -04:00
Yilou Wang d57324e5fb Support dynamic array .size in inline randomize() with constraints (#7258) (#7266) 2026-03-16 18:48:36 -04:00
Yilou Wang be0f4a507e Fix shift width mismatch in constraint solver SMT emission (#5420) (#7265) 2026-03-16 18:48:09 -04:00
Yilou Wang bf792f1809 Fix array reduction in constraints crashing with class inheritance (#7226) (#7263) 2026-03-16 16:36:48 -04:00
dependabot[bot] e5d0b89bfc CI: Bump actions/create-github-app-token (#7260) 2026-03-16 12:43:15 -04:00
Alex Zhou 651f223387 Fix false recursive definition error (#6769) (#7118) 2026-03-16 07:31:35 -04:00
Geza Lore 1e50fefb89 CI: Remove intermediate size 2x2 RTLMeter runs 2026-03-15 08:33:31 +00:00
Wilson Snyder 602ee384de Support $sformat with runtime format string (#7212). (#7257)
Fixes #7212.
2026-03-14 22:43:56 -04:00
Christian Hecken 42cf5d3be2 Fix vpi_put_value with vpiIntVal on VlWide data (#7256) 2026-03-14 20:08:59 -04:00
Geza Lore 9d38a63563 Support array and struct info metadata in FST traces (#7255)
- Emit arrays under the new FST scope type FST_ST_SV_ARRAY
- Emit packed/unpacked 'array' attributes before FST_ST_SV_ARRAY,
  including left and right indices of the array
- Emit packed/unpacked 'pack' attributes before FST_ST_VCD_STRUCT and
  FST_ST_VCD_UNION, including the number of members in the types

All attributes apply to the immediately following scope definition (just
like enum dtype attributes).
2026-03-14 12:31:33 +00:00
Yangyu Chen bb5a9dc247 Support jemalloc as the default allocator on Linux (#7250)
Add jemalloc as an alternative malloc implementation for the Verilator
binary. When both tcmalloc and jemalloc are available, jemalloc is
preferred due to its better performance on RTLMeter.

The new --enable-jemalloc flag (default=check) mirrors the existing
--enable-tcmalloc behavior: auto-detected at configure time, supports
both static and dynamic linking, and is disabled when --enable-dev-asan
is active.
2026-03-13 17:08:15 -04:00
Yilou Wang 7cd49a8028 Support dist and solve...before inside foreach constraints (#7245) (#7253) 2026-03-13 11:05:18 -04:00
Yilou Wang e0f1f316aa Fix randc solver hang with wide variables (#7068) (#7248) 2026-03-13 07:53:51 -04:00
Artur Bieniek c0d0180918 Fix final asserts and $stop (#7249) 2026-03-12 13:09:54 -04:00
Geza Lore 4b53f5f978 Tests: Consolidate format specific t_trace_* tests (#7216)
Factor out test execution into t/trace_*_common.py, which defines
a 'run' functions. All related t_trace_*py tests call this function.

Behaviour is partially encoded in the file name, which must be of the
form: t_trace_complex_<variant>_<mode>_<format>*.py, where '<variant>'
determines tracing options (default/params/structs), '<mode>' is the
compilation mode (--cc/--sc), and '<format>' determines the trace format
(vcd/fst/saif).

The part of the test name after '<format>' does not influence the test
directly and can be free form. If used, explicit 'verilator_flags2' is
passed to the 'run' function.
2026-03-12 15:21:02 +00:00
Yilou Wang c74eee5123 Fix rand variable used as array index in constraint evaluated as constant (#7238) (#7247) 2026-03-12 10:12:53 -04:00
Wilson Snyder 08c6d5bde5 Improve some display error handling (#7212 prep) 2026-03-12 07:49:21 -04:00
Wilson Snyder a787d631ce Internals: Add some missing const/UNLIKELY markers. No functional change 2026-03-12 07:46:21 -04:00
Wilson Snyder f2fdc57366 Fix --hierarchical dropping arguments in -f/-F files (#7240). 2026-03-12 07:25:32 -04:00
Wilson Snyder 1198ee919d Commentary: Changes update 2026-03-12 07:22:33 -04:00
Christian Hecken b3a1a15e2b Fix vpi_put_value release on non-continuous signal (#7231) (#7241) 2026-03-11 22:57:13 -04:00
Wilson Snyder cbf8d97a76 Tests: Misc string format test improvements 2026-03-11 19:58:52 -04:00
Wilson Snyder aca2c05ad9 Tests: Fix t_dump_json to not need sensitive golden file 2026-03-11 19:58:07 -04:00
Wilson Snyder d87035d187 Commentary: Fix non-grammar 2026-03-11 19:53:23 -04:00
Geza Lore be429ce956 Fix tracing of typedefed 1D packed arrays with --trace-structs (#7237)
Used to incorrectly unroll 1D packed arrays of 'bit' or 'logic' into
elements when using --trace-structs if the array element type was given
via a typedef. Keep them as a single signal instead.
2026-03-11 12:46:13 +00:00
Yilou Wang f351882cf0 Fix enum range constraints missing for rand variables in sub-objects (#7230) (#7235) 2026-03-11 07:21:40 -04:00
Wilson Snyder 985f45759c Update libfst from upstream 2026-03-11 07:17:02 -04:00
Wilson Snyder db9d782061 Commentary: Changes update 2026-03-11 07:10:41 -04:00
Rahul Behl cd8818ca23 Fix randomize size+element queue constraints (#5582) (#7225) 2026-03-11 06:51:32 -04:00
Pawel Kojma 4e6eafd994 Allow constant expressions without parentheses in PATHPULSE$ declaration (#7199) 2026-03-11 06:01:33 -04:00
Yilou Wang 5bf1d060c9 Fix internal error when derived class calls this.randomize() with inherited rand members (#7229) (#7234) 2026-03-10 19:03:18 -04:00
Yilou Wang 3ba9077726 Fix soft cross-object constraint priority inversion (#7228) (#7233)
Emit nested constraint setup tasks in depth-first order (deepest first)
so outer-scope soft constraints get higher priority per IEEE 18.5.13.
2026-03-10 19:02:15 -04:00
Christian Hecken 15c60c83aa Internals: Remove m_mask from VerilatedVpioVar (#7232)
Unused since #5573
2026-03-10 17:57:10 -04:00
Wilson Snyder a7a5c9f548 Improve not-found message to show cwd 2026-03-10 08:55:51 -04:00
Rahul Behl 2046879beb V3Randomize: Fix dist operator inside ConstraintIf blocks (#7221) (#7224)
The lowerDistConstraints() function was not recursing into ConstraintIf
nodes, causing dist operators inside if-else blocks to remain unlowered
and trigger an internal error when ConstraintExprVisitor encountered them.

Fix by adding recursive handling of ConstraintIf nodes in lowerDistConstraints:
- Check for AstConstraintIf nodes before AstConstraintExpr
- Recursively process thensp() and elsesp() branches
- This ensures all dist operators are lowered regardless of nesting

Test case: t_randomize_dist_conditional.v demonstrates conditional dist:
    constraint c {
      if (randd) {
        x dist { 8'd0 := 1, 8'd255 := 3 };  // 25% / 75%
      } else {
        x dist { 8'd0 := 3, 8'd255 := 1 };  // 75% / 25%
      }
    }

Fixes #7221
2026-03-10 07:06:00 +00:00
em2machine 1b2b8afdc1 Fix wrong $bits() for parameterized interface struct typedefs (#7218) (#7219) 2026-03-09 22:32:13 -04:00
Veripool API Bot 1f67080a1f Tests: Verilog format 2026-03-09 21:39:16 -04:00
dependabot[bot] b33537d7a7 CI: Bump docker actions with 5 updates (#7220) 2026-03-09 14:49:19 -04:00
Veripool API Bot 07ed6aef53 Tests: Verilog format 2026-03-08 18:26:40 -04:00
Christian Hecken 9c5f4e2483 Internals: Extend VerilatedVar to hold force control signal pointers (#7217) 2026-03-08 15:53:32 -04:00
Christian HeckenandChristian Hecken 0019c12242 Fix vpi_handle_by_name generated scope retrieval (#7187 repair) (#7214)
Co-authored-by: Christian Hecken <[email protected]>
2026-03-07 20:36:29 -05:00
Wilson Snyder 3097df46fa Change --converge-limit default to 10000 (#7209).
Fixes #7209.
2026-03-07 09:05:37 -05:00
Wilson Snyder 68d6dd7eee Internals: Add UASSERT to avoid error fault on sv-tests regress-sv_sv_import_hier_fail1_iv 2026-03-07 09:03:42 -05:00
Wilson Snyder 67ced4715d Tests: Fix t_json_only_debugcheck to not need sensitive golden file 2026-03-07 08:35:54 -05:00
Wilson Snyder 4d369e4262 Fix --debug-inputs for .vlt files 2026-03-07 08:01:27 -05:00
Veripool API Bot cf965464a2 Tests: Verilog format 2026-03-07 08:00:45 -05:00
Wilson Snyder b38c4bd7d6 Commentary: Changes update 2026-03-07 08:00:10 -05:00
em2machine bef5c1f475 Fix static function not found from parameterized extends class (#7204) (#7208) 2026-03-06 16:23:02 -05:00
Rahul Behl efa53189ea Support constraint with item.index array reduction (#7198) 2026-03-06 05:14:55 -05:00
Geza Lore cedec65c39 Change array tracing to always dump left index to right index (#7205)
Also add array bounds and struct/union member counts to trace pushPrefix
(not used by vcd/fst/saif). Together these improve consistency in some
waveform formats.
2026-03-06 09:32:08 +00:00
Wilson Snyder b89d29cab9 * Support procedural concurrent assertion simple cases (#6944).
Fixes #6944.
2026-03-05 20:03:48 -05:00
Wilson Snyder c2d65c2e13 * Fix class extend references between queues (#7195).
Fixes #7195.
2026-03-05 18:04:22 -05:00
Wilson Snyder caf624944f Commentary: Changes update 2026-03-05 18:04:22 -05:00
github action fb00eb3bc6 Apply 'make format' 2026-03-05 21:06:48 +00:00
Julian Carrier 45a5e72509 Fix recursive default assignment for subarrays (#4589) (#7202) 2026-03-05 16:05:54 -05:00
Geza Lore 4f4d48e9d7 Fix library/hier_block tracing when top name is empty (#7200) 2026-03-05 08:44:41 -05:00
Ryszard Rozak 258629634c Support force assignments to array elements of real type (#7048) 2026-03-05 08:37:20 -05:00
Nick Brereton e5ad9b3a4b Support Z non-blocking assignment (#7192) (#496) (#7197) 2026-03-04 22:39:05 -05:00
Wilson Snyder 572f50126b Improve function call argument error to show target function (rewording) 2026-03-04 20:22:25 -05:00
Wilson Snyder c93f5256d4 Fix string cast on array call 2026-03-04 20:12:10 -05:00
Veripool API Bot 4ff518de61 Verilog format 2026-03-04 20:12:10 -05:00
Wilson Snyder 9150e9b522 Commentary: Changes update 2026-03-04 20:07:05 -05:00
Christian Hecken 84f3cc4f48 Add VPI array indexing support in signal names (#7097) (#7187) 2026-03-04 19:37:06 -05:00
Wilson Snyder 1bf2ea7643 Improve function call argument error to show target function 2026-03-04 19:01:01 -05:00
Igor Zaworski 7acd73fede Fix super constructor calls with local variables, by using init functions (#6214) (#6933) 2026-03-04 17:55:55 -05:00
em2machine c34cd6ddf1 Fix missing scope when calling package function (#7128 repair) (#7188) (#7190) 2026-03-04 15:37:55 -05:00
Rahul Behl 9a5c1d27c8 Support array reduction methods with 'with' clause in constraints (#6455) (#6999) 2026-03-04 12:01:35 -05:00
Nick Brereton 44701201ac Support inout inside SV interface (#3466) (#7134) 2026-03-04 09:08:07 -05:00
jalcim 7cf539cf05 Add --func-recursion-depth CLI option (#7175) (#7179) 2026-03-04 06:46:07 -05:00
Yilou Wang 3bc73cc768 Support constraint imperfect distributions (#6811) (#7168) 2026-03-03 11:23:14 -05:00
em2machine 5f3d475736 Internals: Minor coverage improvement as followup to (#7128 followup) (#7184) 2026-03-03 11:13:32 -05:00
Wilson Snyder f232449252 Commentary: Changes update 2026-03-03 07:21:39 -05:00
Veripool API Bot ce4d35aa85 Verilog format 2026-03-03 07:21:24 -05:00
em2machine 5821d0697c Fix interface localparam dependencies and arbitrary nesting (#6936) (#7128) 2026-03-03 06:55:59 -05:00
Yilou Wang ed84f3adb2 Support constraints on fixed-size array of class object members (#7170) (#7183) 2026-03-03 06:27:31 -05:00
Wilson Snyder ee68d1c70c CI: Disallow pulls to stable 2026-03-02 17:48:32 -05:00
Wilson Snyder 1f2f4d9bba Commentary: Changes update 2026-03-02 17:47:54 -05:00
jalcim d406efdcf9 Fix recursive constant function in $unit scope (#7174) (#7178) 2026-03-02 15:15:34 -05:00
Wilson Snyder 251d1835cf Internals: Comment to explain stray emit semicolon 2026-03-02 11:26:48 -05:00
dependabot[bot] a3870c5e5e CI: depandabot: Bump actions with 2 updates (#7172) 2026-03-02 10:52:53 -05:00
Yilou Wang 8a413b3ec7 Fix std::randomize() in static function with static class members (#7167) (#7169) 2026-03-02 05:59:00 -05:00
Yilou Wang 108d209bd7 Support soft constraint solving with last-wins priority (#7124) (#7166) 2026-03-01 15:16:55 -05:00
Wilson Snyder 192445097a Internals: Fix non-determinism in V3Delayed, V3SplitVar, V3Task (#7120 partial) (#7165) 2026-03-01 11:59:29 -05:00
Geza Lore 6c48b3282e Enable V3LiftExpr with code coverage (#7164) 2026-03-01 15:04:49 +00:00
Geza Lore 3249fd8cc0 Internals: Mark Premit temporaries funcLocal, noReset (#7160) 2026-03-01 14:56:21 +00:00
Geza Lore 9d8baa1e44 Testing: Relax expected file count in t_flag_csplit_groups (#7163) 2026-03-01 13:27:46 +00:00
Geza Lore 97838325cd Fix scheduling non-determinism (#7120 partial) (#7162) 2026-03-01 07:44:59 -05:00
Geza Lore 77ce9cec1e Optimize conditional merging across some impure statements (#7159)
- Allow reordering pure statements with DPI import calls iff no public
  variables (including those read via a DPI export) are involved. This
  ensures the DPI import can't observe the reordering
- Allow reordering of pure statements with AstDisplay and AstStop. This
  requires an assumption that AstDisplay and AstStop will not read or
  write model state other than via a VarRef explicitly present int the
  Ast.
Overall this allows eliminating a lot of conditionals around assertions,
which were previously not possible.
2026-03-01 05:47:05 -05:00
Geza Lore 26eac21432 Internals: Use existing astgen type info for ASTGEN_MAX_NODE_SIZE (#7161) 2026-03-01 05:33:30 -05:00
Wilson Snyder e9dd6eaaf0 Internals: Fix VL_ALLOC_RANDOM_CHECKS with multiple threads 2026-02-28 20:24:09 -05:00
Wilson Snyder e23c7a510e Internals: Add VL_ALLOC_RANDOM_CHECKS 2026-02-28 18:21:51 -05:00
Wilson Snyder 230ce772c2 Tests: Verilog format; rename test 2026-02-28 18:19:34 -05:00
Wilson Snyder 2e351651cb Fix undefined weak 'vlog_startup_routines' on macOS 2026-02-28 18:17:00 -05:00
Wilson Snyder 17ad3970ee Commentary: Changes update 2026-02-28 18:15:07 -05:00
Geza Lore 098fe96643 Add V3LiftExpr pass to lower impure expressions and calls (#7141)
Introduce new pass that converts impure expressions, or those with
function and method calls into simple assignment statements. Please see
the blurb at the top of the file why this is useful and how it works.
In particular currently it enables more Dfg optimization as functions
will be inlined without AstExprStmt.

Ideally we should enforce this lowering is applied to every procedural
statement (there are still a handful of exceptions). With that, long
term with this pass + #6820, there should be no need to ever use an
AstExprStmt past this new lowering pass, which should enable more easier
optimization down the line.

Also ideally this should be run earlier. Currently it's after V3Tristate
as that calls pinReconnectSimple so we don't have to touch Cell ports.

Currently disabled when code coverage is enabled due to #7119.
2026-02-28 22:20:09 +00:00
Geza Lore 99238f67b8 Fix undefined weak 'vlog_startup_routines' on macOS 2026-02-28 16:10:59 +00:00
Geza Lore 2ceea267e5 Fix eliminating assignments to DPI-read vaiables (#7158) 2026-02-28 10:09:01 -05:00
Pawel Kojma face700f29 Improve assignment-compatibility type check (#2843) (#5666) (#7052) 2026-02-28 09:55:06 -05:00
Geza Lore 139bdc1ae3 Optimize function call return value temporaries (#7152) 2026-02-28 09:54:39 -05:00
Szymon Gizler 1af7fa92c2 Optimize size of trace declaration object code (#7150) 2026-02-28 09:54:08 -05:00
Ryszard Rozak 6f892d58ac Fix forcing unpacked variables (#7149) 2026-02-28 09:53:41 -05:00
Kamil Danecki df6b808c49 Fix parameters inside std::randomize with clause (#7140) 2026-02-28 09:53:05 -05:00
Todd Strader 8705bc56c8 Fix errant integer promotion (#7012) 2026-02-28 09:52:35 -05:00
Nick Brereton 02c1dbc5dc Fix lambda coroutines (#6106) (#7135) 2026-02-28 09:52:02 -05:00
Geza Lore 1716423d07 Internals: Fix invalid use of user1 in V3Const (#7157) 2026-02-28 09:51:07 -05:00
Wilson Snyder 1e6c1ab106 Add VPI callback support to --main (#7145).
Fixes #7145.
2026-02-28 09:42:28 -05:00
Wilson Snyder d40036239b Fix wide conditional short circuiting (#7155).
Fixes #7155.
2026-02-28 09:40:10 -05:00
Wilson Snyder 5ae285d268 devel release 2026-02-28 09:37:43 -05:00
3866 changed files with 272201 additions and 157554 deletions
+1 -1
View File
@@ -155,7 +155,7 @@ jobs:
- name: Zip up repository
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
- name: Upload zip archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/repo/verilator.zip
name: verilator-win.zip
+7 -7
View File
@@ -77,7 +77,7 @@ jobs:
uses: actions/checkout@v6
- name: Download code coverage data
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: code-coverage-*
path: obj_coverage
@@ -90,7 +90,7 @@ jobs:
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
- name: Upload to codecov.io
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v6
with:
disable_file_fixes: true
disable_search: true
@@ -114,7 +114,7 @@ jobs:
sudo apt install lcov
- name: Download repository archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: ${{ needs.build.outputs.archive }}
path: ${{ github.workspace }}
@@ -125,7 +125,7 @@ jobs:
ls -lsha
- name: Download code coverage data
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: code-coverage-*
path: repo/obj_coverage
@@ -170,14 +170,14 @@ jobs:
fi
- name: Upload report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: repo/obj_coverage
name: coverage-report
- name: Upload notification
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: repo/notification
name: coverage-pr-notification
@@ -193,7 +193,7 @@ jobs:
# Creating issues requires elevated privilege
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2.2.1
uses: actions/create-github-app-token@v3.1.1
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
+5 -5
View File
@@ -54,7 +54,7 @@ jobs:
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: |
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
@@ -64,21 +64,21 @@ jobs:
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
with:
buildkitd-flags: --debug
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
with:
context: ${{ env.build_context }}
+15
View File
@@ -0,0 +1,15 @@
---
# Block PRs against stable branch
name: "Check PR not against 'stable'"
on:
pull_request:
branches: [stable]
jobs:
block-pr:
runs-on: ubuntu-latest
steps:
- name: Fail and block PR
run: |-
echo "Error: Pull requests directly to 'stable' are not allowed. Please PR against 'master'."
exit 1
+2 -2
View File
@@ -61,7 +61,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
notify:
name: Notify
@@ -74,7 +74,7 @@ jobs:
# Use the Verilator CI app to post the comment
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2.2.1
uses: actions/create-github-app-token@v3.1.1
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
+1 -1
View File
@@ -93,7 +93,7 @@ jobs:
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
- name: Upload repository archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
@@ -36,8 +36,8 @@ jobs:
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
sudo apt update || \
sudo apt update
sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev || \
sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
- name: Use saved ccache
uses: actions/cache@v5
@@ -70,7 +70,7 @@ jobs:
run: tar --posix -c -z -f verilator-rtlmeter.tar.gz install
- name: Upload Verilator installation archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: verilator-rtlmeter.tar.gz
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
+4 -4
View File
@@ -60,11 +60,11 @@ jobs:
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
sudo apt update || \
sudo apt update
sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev || \
sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
- name: Download Verilator installation archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
@@ -117,7 +117,7 @@ jobs:
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
- name: Upload results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: results-${{ steps.results.outputs.hash }}.json
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}
+5 -3
View File
@@ -55,7 +55,7 @@ jobs:
steps:
- name: Download repository archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: ${{ inputs.archive }}
path: ${{ github.workspace }}
@@ -99,11 +99,13 @@ jobs:
- name: Upload code coverage data
if: ${{ inputs.dev-gcov }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
name: code-coverage-${{ inputs.suite }}
- name: Fail job if a test failed
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
run: exit 1
run: |-
echo "Click on '> Tests' arrow above (or on other steps), to expand it, and see the failure reasons"
exit 1
+3 -3
View File
@@ -20,13 +20,13 @@ jobs:
pull-requests: write
steps:
- name: Download report
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: rtlmeter-pr-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Download PR number
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: pr-number
run-id: ${{ github.event.workflow_run.id }}
@@ -34,7 +34,7 @@ jobs:
# Use the Verilator CI app to post the comment
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2.2.1
uses: actions/create-github-app-token@v3.1.1
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
+14 -17
View File
@@ -11,6 +11,8 @@ on:
- cron: '0 2 * * *' # Daily, starting at 02:00 UTC
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches: ['ci-rtlmeter/**']
permissions:
contents: read
@@ -35,7 +37,8 @@ jobs:
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request'
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) ||
(github.event_name == 'workflow_dispatch')
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push')
runs-on: ubuntu-24.04
steps:
- name: Startup
@@ -75,12 +78,10 @@ jobs:
matrix:
cases:
- "BlackParrot:1x1:*"
- "BlackParrot:2x2:*"
- "BlackParrot:4x4:*"
- "Caliptra:default:*"
- "NVDLA:*"
- "OpenPiton:1x1:*"
- "OpenPiton:2x2:*"
- "OpenPiton:4x4:*"
- "OpenTitan:*"
- "VeeR-EH1:asic*"
@@ -121,12 +122,10 @@ jobs:
matrix:
cases:
- "BlackParrot:1x1:*"
- "BlackParrot:2x2:*"
- "BlackParrot:4x4:*"
- "Caliptra:default:*"
- "NVDLA:*"
- "OpenPiton:1x1:*"
- "OpenPiton:2x2:*"
- "OpenPiton:4x4:*"
- "OpenTitan:*"
- "VeeR-EH1:asic*"
@@ -167,11 +166,9 @@ jobs:
matrix:
cases:
- "BlackParrot:1x1:* !-hier"
- "BlackParrot:2x2:* !-hier"
- "BlackParrot:4x4:* !-hier"
- "NVDLA:* !-hier"
- "OpenPiton:1x1:* !-hier"
- "OpenPiton:2x2:* !-hier"
- "OpenPiton:4x4:* !-hier"
- "OpenPiton:8x8:* !-hier"
- "OpenPiton:16x16:dhry !-hier"
@@ -198,7 +195,7 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Download all results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: rtlmeter-${{ matrix.tag }}-results-*
path: all-results-${{ matrix.tag }}
@@ -208,7 +205,7 @@ jobs:
run: |
./rtlmeter collate ../all-results-${{ matrix.tag }}/*.json > ../all-results-${{ matrix.tag }}.json
- name: Upload combined results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: all-results-${{ matrix.tag }}.json
name: all-results-${{ matrix.tag }}
@@ -227,20 +224,20 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download combined results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: all-results-*
path: results
merge-multiple: true
- name: Upload published results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: results/*.json
name: published-results
# Pushing to verilator/verilator-rtlmeter-results requires elevated permissions
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2.2.1
uses: actions/create-github-app-token@v3.1.1
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
@@ -289,7 +286,7 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Download combined results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: all-results-*
path: all-results
@@ -308,7 +305,7 @@ jobs:
DATE=$(gh run --repo ${{ github.repository }} view $ID --json createdAt --jq ".createdAt")
echo "date=$DATE" >> $GITHUB_OUTPUT
- name: Download scheduled run results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: published-results
path: nightly-results
@@ -374,14 +371,14 @@ jobs:
done
cat report.txt
- name: Upload report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: report.txt
name: rtlmeter-pr-results
- name: Save PR number
run: echo ${{ github.event.number }} > pr-number.txt
- name: Upload PR number
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: pr-number.txt
name: pr-number
@@ -397,7 +394,7 @@ jobs:
# Creating issues requires elevated privilege
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2.2.1
uses: actions/create-github-app-token@v3.1.1
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
+1 -1
View File
@@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(
Verilator
VERSION 5.046
VERSION 5.047
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
+146 -2
View File
@@ -8,6 +8,150 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks!
Verilator 5.047 devel
==========================
**Important:**
* Support jemalloc as the default allocator on Linux (#7250). [Yangyu Chen]
For better performance, `apt install libjemalloc-dev` or equivalent before running ./configure.
**Other:**
* Support inout inside SV interface (#3466) (#7134). [Nick Brereton]
* Support `##0` cycle delays (#4263) (#7298). [Yilou Wang]
* Support array reduction methods with 'with' clause in constraints (#6455) (#6999). [Rahul Behl]
* Support constraint imperfect distributions (#6811) (#7168). [Yilou Wang]
* Support disable task by name (#6853) (#7136). [Nick Brereton]
* Support procedural concurrent assertion simple cases (#6944).
* Support force assignments to array elements of real type (#7048). [Ryszard Rozak, Antmicro Ltd.]
* Support VPI array indexing in signal names (#7097) (#7187) (#7214) (#7289). [Christian Hecken, Heidelberg University]
* Support soft constraint solving (#7124) (#7166). [Yilou Wang]
* Support constraints on fixed-size array of class object members (#7170) (#7183). [Yilou Wang]
* Support Z non-blocking assignment (#7192) (#496) (#7197). [Nick Brereton]
* Support constraint `with` item.index array reduction (#7198). [Rahul Behl]
* Support constant expressions without parentheses in `PATHPULSE$` (#7199). [Pawel Kojma, Antmicro Ltd.]
* Support $sformat with runtime format string (#7212).
* Support dist and solve...before inside foreach constraints (#7245) (#7253). [Yilou Wang]
* Support array and struct info metadata in FST traces (#7255). [Geza Lore, Testorrent USA, Inc.]
* Support dynamic array .size in inline randomize() with constraints (#7258) (#7266). [Yilou Wang]
* Support defparam with more than one dot (#7262). [Artur Bieniek, Antmicro Ltd.]
* Support modport export/import task prototypes and out-of-block definitions (#7277). [Yilou Wang]
* Support very wide $display arguments (#7280). [Jakub Michalski]
* Support named sequence declarations and instances in assertions (#7283). [Yilou Wang]
* Support `##` delay on implication RHS in SVA properties (#7284). [Yilou Wang]
* Support boolean and/or in sequence expressions (#7285). [Yilou Wang]
* Support property-local variables and sequence match items (#7286). [Yilou Wang]
* Support 'until' property (partial #7290) (#7399). [Ryszard Rozak, Antmicro Ltd.]
* Support array map() method (#7307) (#7316) (#7344). [Wei-Lun Chiu]
* Support SVA goto repetition `[->N]` in concurrent assertions (#7310). [Yilou Wang]
* Support consecutive repetition `[\*N]` in SVA properties (#7311). [Yilou Wang]
* Support `##[*]`, `##[+]`, `##[M:N]`, `##[M:$]` cycle delays in assertions (#7312) (#7377). [Yilou Wang]
* Support rise/fall delays (#7368). [Artur Bieniek, Antmicro Ltd.]
* Support sequence intersect operator (#7374). [Yilou Wang]
* Support sequence 'throughout' operator (#7378). [Yilou Wang]
* Support sequence consecutive repetition `[*N:M]`, `[+]`, and `[*]` (#7379). [Yilou Wang]
* Support sequence `first_match` operator (#7392). [Yilou Wang]
* Support nonconsecutive repetition [=N] in sequence expressions (#7397). [Yilou Wang]
* Support per-process RNG for process::srandom() and object seeding (#7408) (#7415) (#7408). [Yilou Wang]
* Support 2**n expressions in constraint randomization (#7422). [Yilou Wang]
* Add VPI callback support to --main (#7145).
* Add V3LiftExpr pass to lower impure expressions and calls (#7141) (#7164). [Geza Lore, Testorrent USA, Inc.]
* Add --func-recursion-depth CLI option (#7175) (#7179).
* Add `+verilator+solver+file` (#7242).
* Add MacOS support for address sanitizer memory limit (#7308). [Marco Bartoli]
* Deprecate `--structs-packed` (#7222).
* Improve assignment-compatibility type check (#2843) (#5666) (#7052). [Pawel Kojma, Antmicro Ltd.]
* Improve error message when variable used as data type (#7318). [Ryszard Rozak, Antmicro Ltd.]
* Improve E_UNSUPPORTED warning messages (#7329). [Eunseo Song]
* Change array tracing to dump left index to right index (#7205). [Geza Lore, Testorrent USA, Inc.]
* Change `--converge-limit` default to 10000 (#7209).
* Remove DFG extract optimization pass (#7394). [Geza Lore, Testorrent USA, Inc.]
* Optimize trace code for faster compiles on repeated types (#6707) (#6832). [Todd Strader]
* Optimize size of trace declaration object code (#7150). [Szymon Gizler, Antmicro Ltd.]
* Optimize function call return value temporaries (#7152). [Geza Lore, Testorrent USA, Inc.]
* Optimize conditional merging across some impure statements (#7159). [Geza Lore, Testorrent USA, Inc.]
* Optimize reuse of existing associative terms in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
* Optimize duplicate vertices and multiplexers in DfgPeephole (#7305). [Geza Lore, Testorrent USA, Inc.]
* Optimize commutative vertex operands in DFG for better combining. [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG peephole until a fixed point (#7309). [Geza Lore, Testorrent USA, Inc.]
* Optimize comparisons with identical operands and $countones in DFG. [Geza Lore, Testorrent USA, Inc.]
* Optimize more patterns in DfgPeephole (#7332). [Geza Lore, Testorrent USA, Inc.]
* Optimize read references in DFG (#7354). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG only once, after scoping (#7362). [Geza Lore, Testorrent USA, Inc.]
* Optimize more DFG peephole patterns (#7423). [Geza Lore, Testorrent USA, Inc.]
* Fix recursive default assignment for sub-arrays (#4589) (#7202). [Julian Carrier]
* Fix virtual interface member trigger convergence (#5116) (#7323). [Yilou Wang]
* Fix shift width mismatch in constraint solver SMT emission (#5420) (#7265). [Yilou Wang]
* Fix returning wrong type from static function in parameterized class (#5479) (#7387) (#7411) (#7418). [em2machine]
* Fix randomize size+element queue constraints (#5582) (#7225). [Rahul Behl, Testorrent USA, Inc.]
* Fix null assignment to virtual interfaces (#5974) (#5990). [Maxim Fonarev]
* Fix typedef scope resolution for parameterized class aliases (#5977) (#7319). [Nick Brereton]
* Fix lambda coroutines (#6106) (#7135). [Nick Brereton]
* Fix super constructor calls with local variables (#6214) (#6933). [Igor Zaworski, Antmicro Ltd.]
* Fix parameter default comparison when value contains type cast (#6281) (#7369) (#6281). [Yilou Wang]
* Fix `local::` false error in randomize() with on parameterized class (#6680) (#7293). [Yilou Wang]
* Fix false recursive definition error (#6769) (#7118). [Alex Zhou]
* Fix port assignment to large arrays (#6904).
* Fix interface localparam dependencies and arbitrary nesting (#6936) (#7128) (#7188) (#7190). [em2machine]
* Fix parameterized class typedef as interface type parameter (#7000) (#7006). [Leela Pakanati]
* Fix errant integer promotion (#7012). [Todd Strader]
* Fix randc solver hang with wide variables (#7068) (#7248). [Yilou Wang]
* Fix coroutine trace setters (#7078 repair) (#7296). [Igor Zaworski, Antmicro Ltd.]
* Fix vpi_put_value not updating forced read value (#7092) (#7395) (#7092). [Christian Hecken, Heidelberg University]
* Fix scheduling non-determinism (#7120) (#7162) (#7165). [Geza Lore, Testorrent USA, Inc.]
* Fix parameters inside std::randomize `with` clause (#7140). [Kamil Danecki, Antmicro Ltd.]
* Fix forcing unpacked variables (#7149). [Ryszard Rozak, Antmicro Ltd.]
* Fix wide conditional short circuiting (#7155).
* Fix eliminating assignments to DPI-read variables (#7158). [Geza Lore, Testorrent USA, Inc.]
* Fix std::randomize() in static function with static class members (#7167) (#7169). [Yilou Wang]
* Fix resolving default/non-default type parameters (#7171) (#7346) (#7380) (#7385) (#7398) (#7406) (#7398). [em2machine]
* Fix recursive constant function in $unit scope (#7173) (#7174).
* Fix class extend references between queues (#7195).
* Fix library/hier_block tracing when top name is empty (#7200). [Geza Lore, Testorrent USA, Inc.]
* Fix virtual interface select from sub-interface instance (#7203) (#7370) (#7203). [Yilou Wang]
* Fix VPI force of bit-selected signals (#7211) (#7301). [Christian Hecken]
* Fix `$finish` to immediately stop executing code from non-final blocks (#7213 partial) (#7390).
* Fix wrong $bits() for parameterized interface struct typedefs (#7218) (#7219). [em2machine]
* Fix `dist` operator inside constraint if blocks (#7221) (#7224). [Rahul Behl, Testorrent USA, Inc.]
* Fix array reduction in constraints crashing with class inheritance (#7226) (#7263). [Yilou Wang]
* Fix soft cross-object constraint priority inversion (#7228) (#7233). [Yilou Wang]
* Fix internal error when derived class calls this.randomize() with inherited rand members (#7229) (#7234). [Yilou Wang]
* Fix enum range constraints missing for rand variables in sub-objects (#7230) (#7235). [Yilou Wang]
* Fix vpi_put_value release on non-continuous signal (#7231) (#7241). [Christian Hecken]
* Fix functions in generate block resulting in 'Broken link in node' (#7236) (#7367). [em2machine]
* Fix tracing of typedefed 1D packed arrays with --trace-structs (#7237). [Geza Lore, Testorrent USA, Inc.]
* Fix rand variable used as array index in constraint evaluated as constant (#7238) (#7247). [Yilou Wang]
* Fix --hierarchical dropping arguments in -f/-F files (#7240). [Clara Sparks]
* Fix `final` asserts and $stop (#7249). [Artur Bieniek, Antmicro Ltd.]
* Fix MacOS clang PCH compile error with -o flag (#7251) (#7327). [Eunseo Song]
* Fix vpi_put_value with vpiIntVal on VlWide data (#7256). [Christian Hecken]
* Fix streaming with descending unpacked arrays and unpacked-to-queue (#7287). [Yilou Wang]
* Fix MacOs lexer compile error (#7314) (#7315). [Tracy Narine]
* Fix linking shared library with its dependencies (#7320). [Artur Bieniek, Antmicro Ltd.]
* Fix modport selection of virtual interface handle (#7321). [Yilou Wang]
* Fix false ASSIGNIN on interface input ports driven from outside (#7322). [Yilou Wang]
* Fix static initialization order for packages with class hierarchies (#7324). [Yilou Wang]
* Fix sensitivity of signals to unrelated interface members (#7336). [Artur Bieniek, Antmicro Ltd.]
* Fix `disable iff` imply-delay statement linking (#7337). [Nick Brereton]
* Fix lost `$stop` on implied assertion `$error` failures.
* Fix wait() hang when interface uses process calls and VIF function (#7342). [Yilou Wang]
* Fix error on illegal nand/nor binary operators (#7353).
* Fix simple array assignment unrolling in slice optimization (#7359). [Geza Lore, Testorrent USA, Inc.]
* Fix missing temporary for DfgSplicePacked (#7361). [Geza Lore, Testorrent USA, Inc.]
* Fix virtual interface function calls binding to wrong instance (#7363). [Yilou Wang]
* Fix false ASSIGNIN on interface input port connections (#7365). [Yilou Wang]
* Fix string `inside` queue (#7373).
* Fix subclass with rand_mode(0) getting randomized (#7376) (#7383). [Yilou Wang]
* Fix VPI access to Verilog `force`-ed signals (#7381). [Christian Hecken, Heidelberg University]
* Fix sampling of hierarchical references (#7386). [Ryszard Rozak, Antmicro Ltd.]
* Fix virtual class inheritance false error (#7403) (#7405). [Nikolay Puzanov]
* Fix CMake compiler coroutine flags (#7404). [Shogo Yamazaki]
* Fix delete inside foreach skipping elements (#7407) (#7410)
* Fix std::randomize in parameterized-derived class (#7409) (#7416). [Yilou Wang]
* Fix virtual interface implied comparison with null (#7421). [Alex Solomatnikov]
Verilator 5.046 2026-02-28
==========================
@@ -31,7 +175,7 @@ Verilator 5.046 2026-02-28
* Support solve..before constraints (#5647) (#7123). [Yilou Wang]
* Support structure initial values (#6130).
* Support proper automatic/static initialization, and remove STATICVAR warning (#6405) (#7086).
* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken]
* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken, Heidelberg University]
* Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang]
* Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan]
* Support complex expressions as std::randomize arguments (#6860). [Jakub Wasilewski, Antmicro Ltd.]
@@ -94,7 +238,7 @@ Verilator 5.046 2026-02-28
* Fix `foreach` with mid-index empty commas (#6910).
* Fix internal error when fork under always expression (#6911).
* Fix error when calling non-static method (#6916). [Artur Bieniek, Antmicro Ltd.]
* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken]
* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken, Heidelberg University]
* Fix interface internal type reference (#6920) (#6966). [Todd Strader]
* Fix segfault after assignment pattern XOR error (#6928) (#6931). [emmettifelts]
* Fix delayed initial assignment (#6929). [Todd Strader]
+10 -4
View File
@@ -398,9 +398,12 @@ CPPCHECK_RUNTIME_CPP = $(wildcard $(srcdir)/include/*.cpp)
CPPCHECK_VLC_CPP = $(wildcard $(srcdir)/src/Vlc*.cpp)
CPPCHECK_EXAMPLES_CPP = $(wildcard $(srcdir)/examples/*/*.cpp)
CHECK_CPP = $(CPPCHECK_VERILATOR_CPP) $(CPPCHECK_RUNTIME_CPP) $(CPPCHECK_VLC_CPP) $(CPPCHECK_EXAMPLES_CPP)
CHECK_H = $(wildcard \
CHECK_H = $(filter-out \
$(srcdir)/src/config_package.h \
$(srcdir)/src/config_rev.h, \
$(wildcard \
$(srcdir)/include/*.h \
$(srcdir)/src/*.h )
$(srcdir)/src/*.h ))
CHECK_YL = $(wildcard \
$(srcdir)/src/*.y \
$(srcdir)/src/*.l )
@@ -441,7 +444,9 @@ cppcheck:
CLANGTIDY = clang-tidy
CLANGTIDY_FLAGS = -config='' \
-header-filter='.*' \
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
-checks='*,-abseil-*,-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
# Good checks: -checks='-*,bugprone-assignment-in-if-condition,google-build-using-namespace,google-readability-casting,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nullptr,performance-faster-string-find,readability-isolate-declaration,readability-redundant-control-flow,readability-redundant-member-init,readability-redundant-string-cstr,readability-uppercase-literal-suffix
CLANGTIDY_DEP = $(subst .cpp,.cpp.tidy,$(CHECK_CPP))
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_CPPCHECK=1
@@ -517,6 +522,7 @@ PY_PROGRAMS = \
nodist/lint_py_test_filter \
nodist/log_changes \
nodist/uvm_pkg_packer \
nodist/verilator_bisect \
nodist/verilator_saif_diff \
src/.gdbinit.py \
src/astgen \
@@ -577,7 +583,7 @@ MBAKE = mbake
MBAKE_FLAGS = format --config ./.bake.toml
format-exec:
-chmod a+x test_regress/t/*.py
-chmod a+x test_regress/t/t_*.py
-chmod a-x test_regress/t/*.v
format-make mbake:
+18 -8
View File
@@ -149,7 +149,7 @@ if ($opt_gdb) {
. verilator_bin()
. " " . join(' ', @quoted_sw));
} else {
# Normal, non gdb
# Normal, non-gdb
run(ulimit_stack_unlimited() . aslr(1) . verilator_bin() . " " . join(' ', @quoted_sw));
}
@@ -213,14 +213,22 @@ sub ulimit_stack_unlimited {
# AddressSanitizer doesn't work with 'ulimit -s unlimted'
if (`${\(verilator_bin())} --get-supported DEV_ASAN` eq "1\n") {
# Use host 'physical memory / #cores / 8' instead
open(my $fh, "<", "/proc/meminfo") || die "Can't read host memory for asan";
while (<$fh>) {
if (m/MemTotal:\s+(\d+)\s+kB/) {
$limit = int(int($1)/`nproc`/8);
last;
if ($^O eq "darwin") {
my $membytes = `sysctl -n hw.memsize`;
chomp $membytes;
my $ncpu = `sysctl -n hw.ncpu`;
chomp $ncpu;
$limit = int(int($membytes / 1024) / $ncpu / 8);
} else {
open(my $fh, "<", "/proc/meminfo") || die "Can't read host memory for asan";
while (<$fh>) {
if (m/MemTotal:\s+(\d+)\s+kB/) {
$limit = int(int($1)/`nproc`/8);
last;
}
}
close($fh);
}
close($fh);
}
system("ulimit -s $limit 2>/dev/null");
my $status = $?;
@@ -345,6 +353,7 @@ detailed descriptions of these arguments.
-CFLAGS <flags> C++ compiler arguments for makefile
--compiler <compiler-name> Tune for specified C++ compiler
--compiler-include Include additional header in the precompiled one
--constraint-array-limit <size> Maximum array size for constraint array reduction
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-expr Enable expression coverage
@@ -390,6 +399,7 @@ detailed descriptions of these arguments.
-f <file> Parse arguments from a file
-FI <file> Force include of a file
--flatten Force inlining of all modules, tasks and functions
--func-recursion-depth <value> Maximum recursive constant function depth
--future0 <option> Ignore an option for compatibility
--future1 <option> Ignore an option with argument for compatibility
-fno-<optimization> Disable internal optimization stage
@@ -496,7 +506,6 @@ detailed descriptions of these arguments.
--no-std-package Prevent parsing standard package
--no-std-waiver Prevent parsing standard lint waivers
--no-stop-fail Do not call $stop when assertion fails
--structs-packed Convert all unpacked structures to packed structures
-sv Enable SystemVerilog parsing
+systemverilogext+<ext> Synonym for +1800-2023ext+<ext>
--threads <threads> Enable multithreading
@@ -576,6 +585,7 @@ description of these arguments.
+verilator+quiet Minimize additional printing
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+solver+file+<filename> Set random solver log filename
+verilator+V Show verbose version and config
+verilator+version Show version and exit
+verilator+wno+unsatconstr+<value> Disable constraint warnings
+1 -1
View File
@@ -52,7 +52,7 @@ if (! GetOptions (
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Normal, non gdb
# Normal, non-gdb
run(verilator_coverage_bin()
. " " . join(' ', @Opt_Verilator_Sw));
+26 -22
View File
@@ -30,7 +30,7 @@ elif [ "$CI_OS_NAME" = "osx" ]; then
elif [ "$CI_OS_NAME" = "freebsd" ]; then
MAKE=gmake
else
fatal "Unknown os: '$CI_OS_NAME'"
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
if [ "$CI_OS_NAME" = "linux" ]; then
@@ -56,20 +56,22 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update ||
sudo apt-get update
sudo apt-get install ccache help2man libfl-dev ||
sudo apt-get install ccache help2man libfl-dev
sudo apt-get install --yes ccache help2man libfl-dev ||
sudo apt-get install --yes ccache help2man libfl-dev
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install libgoogle-perftools-dev ||
sudo apt-get install libgoogle-perftools-dev
sudo apt-get install --yes libjemalloc-dev ||
sudo apt-get install --yes libjemalloc-dev
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-20.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
sudo apt-get install libsystemc libsystemc-dev ||
sudo apt-get install libsystemc libsystemc-dev
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
fi
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
sudo apt-get install bear mold ||
sudo apt-get install bear mold
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes bear mold ||
sudo apt-get install --yes bear mold
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update ||
@@ -79,7 +81,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
elif [ "$CI_OS_NAME" = "freebsd" ]; then
sudo pkg install -y autoconf bison ccache gmake perl5
else
fatal "Unknown os: '$CI_OS_NAME'"
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
if [ -n "$CCACHE_DIR" ]; then
@@ -94,16 +96,18 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.py
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
sudo apt-get install python3-clang mold ||
sudo apt-get install python3-clang mold
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes python3-clang mold ||
sudo apt-get install --yes python3-clang mold
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-20.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
sudo apt-get install libsystemc-dev ||
sudo apt-get install libsystemc-dev
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
fi
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update
@@ -113,7 +117,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3 jq z3
else
fatal "Unknown os: '$CI_OS_NAME'"
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
# Common installs
install-vcddiff
@@ -122,5 +126,5 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
else
##############################################################################
# Unknown build stage
fatal "Unknown build stage: '$CI_BUILD_STAGE_NAME'"
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fi
+3 -3
View File
@@ -29,7 +29,7 @@ elif [ "$CI_OS_NAME" = "freebsd" ]; then
export MAKE=gmake
NPROC=$(sysctl -n hw.ncpu)
else
fatal "Unknown os: '$CI_OS_NAME'"
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
NPROC=$(expr $NPROC '+' 1)
@@ -192,7 +192,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
;;
*)
fatal "Unknown test: $TESTS"
fatal "Unknown TESTS: $TESTS"
;;
esac
@@ -204,5 +204,5 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
else
##############################################################################
# Unknown build stage
fatal "Unknown build stage: '$CI_BUILD_STAGE_NAME'"
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fi
+1 -1
View File
@@ -37,7 +37,7 @@ RUN apt-get update \
libfl2 \
libfl-dev \
libclang-rt-18-dev \
libgoogle-perftools-dev \
libjemalloc-dev \
libsystemc \
libsystemc-dev \
numactl \
+1 -1
View File
@@ -22,7 +22,7 @@ RUN apt-get update \
help2man \
libfl2 \
libfl-dev \
libgoogle-perftools-dev \
libjemalloc-dev \
numactl \
perl \
perl-doc \
+56 -3
View File
@@ -12,7 +12,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.046 2026-02-28],
AC_INIT([Verilator],[5.047 devel],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -64,8 +64,8 @@ AC_ARG_ENABLE([dev-asan],
[AS_HELP_STRING([--enable-dev-asan],
[Enable compiling Verilator with ASAN
AddressSanitizer for memory error detection.
This disables tcmalloc. Does not affect
Verilated models using ASAN.])],
This disables tcmalloc and jemalloc. Does not
affect Verilated models using ASAN.])],
[case "${enableval}" in
yes) CFG_WITH_DEV_ASAN=yes ;;
no) CFG_WITH_DEV_ASAN=no ;;
@@ -93,6 +93,27 @@ else
AC_MSG_RESULT($CFG_WITH_TCMALLOC)
fi
# Flag to enable linking Verilator with jemalloc if available
AC_MSG_CHECKING(whether to use jemalloc)
AC_ARG_ENABLE([jemalloc],
[AS_HELP_STRING([--enable-jemalloc],
[Use libjemalloc for faster dynamic memory
management in Verilator binary. Preferred over
tcmalloc when both are available
@<:@default=check@:>@])],
[case "${enableval}" in
yes) CFG_WITH_JEMALLOC=yes ;;
no) CFG_WITH_JEMALLOC=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-jemalloc]) ;;
esac],
[CFG_WITH_JEMALLOC=check;])
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
CFG_WITH_JEMALLOC=no
AC_MSG_RESULT("disabled by --enable-dev-asan")
else
AC_MSG_RESULT($CFG_WITH_JEMALLOC)
fi
# Flag to enable code coverage build with gcov
AC_MSG_CHECKING(whether to build for gcov code coverage collection)
AC_ARG_ENABLE([dev-gcov],
@@ -473,6 +494,7 @@ fi
AC_SUBST(HAVE_DEV_ASAN)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-shadow)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unnecessary-virtual-specifier)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unused-parameter)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Xclang -fno-pch-timestamp)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
@@ -532,6 +554,7 @@ m4_foreach([cflag],[
[-Wno-tautological-bitwise-compare],
[-Wno-tautological-compare],
[-Wno-uninitialized],
[-Wno-unnecessary-virtual-specifier],
[-Wno-unused-but-set-parameter],
[-Wno-unused-but-set-variable],
[-Wno-unused-parameter],
@@ -567,8 +590,10 @@ if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libstdc++)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -Xlinker -gc-sections)
LTCMALLOC="-Wl,--whole-archive -l:libtcmalloc_minimal.a -Wl,--no-whole-archive"
LJEMALLOC="-Wl,--whole-archive -l:libjemalloc.a -Wl,--no-whole-archive"
else
LTCMALLOC=-ltcmalloc_minimal
LJEMALLOC=-ljemalloc
fi
AC_SUBST(CFG_LDFLAGS_SRC)
AC_SUBST(CFG_LDFLAGS_VERILATED)
@@ -584,11 +609,38 @@ _MY_LDLIBS_CHECK_OPT(CFG_LIBS, -latomic)
_MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lbcrypt)
_MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lpsapi)
# Check if jemalloc is available based on --enable-jemalloc
# jemalloc is preferred over tcmalloc when both are available
CFG_HAVE_JEMALLOC=no
_MY_LDLIBS_CHECK_IFELSE(
$LJEMALLOC,
[if test "$CFG_WITH_JEMALLOC" != "no"; then
CFG_LIBS="$LJEMALLOC $CFG_LIBS";
CFG_HAVE_JEMALLOC=yes;
# If using jemalloc, add some extra options to make the compiler not assume
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free)
AC_DEFINE([HAVE_JEMALLOC],[1],[Defined if have jemalloc])
fi],
[if test "$CFG_WITH_JEMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-jemalloc was given but test for ${LJEMALLOC} failed])
fi])
AC_SUBST(HAVE_JEMALLOC)
# Check if tcmalloc is available based on --enable-tcmalloc
# Only use tcmalloc if jemalloc was not found/enabled
if test "$CFG_HAVE_JEMALLOC" = "yes"; then
AC_MSG_NOTICE([jemalloc found, skipping tcmalloc check])
else
CFG_HAVE_TCMALLOC=no
_MY_LDLIBS_CHECK_IFELSE(
$LTCMALLOC,
[if test "$CFG_WITH_TCMALLOC" != "no"; then
CFG_LIBS="$LTCMALLOC $CFG_LIBS";
CFG_HAVE_TCMALLOC=yes;
# If using tcmalloc, add some extra options to make the compiler not assume
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
@@ -600,6 +652,7 @@ _MY_LDLIBS_CHECK_IFELSE(
[if test "$CFG_WITH_TCMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-tcmalloc was given but test for ${LTCMALLOC} failed])
fi])
fi
AC_SUBST(HAVE_TCMALLOC)
AC_SUBST(CFG_LIBS)
+11
View File
@@ -60,6 +60,7 @@ Driss Hafdi
Edgar E. Iglesias
Eric Müller
Eric Rippey
Eunseo Song
Ethan Sifferman
Eyck Jentzsch
Fabian Keßler-Schulz
@@ -98,7 +99,9 @@ Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
Jakub Michalski
Jakub Wasilewski
jalcim
James Bailey
James Hanlon
James Hutchinson
@@ -133,6 +136,7 @@ Jose Loyola
Josep Sans
Joseph Nwabueze
Josh Redford
Julian Carrier
Julian Daube
Julie Schwartz
Julien Margetts
@@ -163,6 +167,7 @@ Lukasz Dalek
Maarten De Braekeleer
Maciej Sobkowski
Marcel Chang
Marco Bartoli
Marco Widmer
Mariusz Glebocki
Markus Krause
@@ -174,6 +179,7 @@ Mateusz Gancarz
Matt Stroud
Matthew Ballance
Max Wipfli
Maxim Fonarev
Michael Bedford Taylor
Michael Bikovitsky
Michael Killough
@@ -191,6 +197,7 @@ Nathan Graybeal
Nathan Kohagen
Nathan Myers
Nick Brereton
Nikolay Puzanov
Nolan Poe
Oleh Maksymenko
Patrick Stewart
@@ -214,6 +221,7 @@ Piotr Binkowski
Qingyao Sun
Quentin Corradi
Rafal Kapuscik
Rahul Behl
Rasfunk
Raynard Qiao
Ricardo Barbedo
@@ -222,6 +230,7 @@ Risto Pejašinović
Robert Balas
Robin Heinemann
Rodrigo Batista de Moraes
Rowan Goemans
Rupert Swarbrick
Ryan Ziegler
Ryszard Rozak
@@ -231,6 +240,7 @@ Sebastien Van Cauwenberghe
Sergey Fedorov
Sergi Granell
Seth Pellegrino
Shogo Yamazaki
Shou-Li Hsu
Srinivasan Venkataramanan
Stefan Wallentowitz
@@ -251,6 +261,7 @@ Tom Manner
Tomasz Gorochowik
Topa Topino
Toru Niina
Tracy Narine
Trung Nguyen
Tudor Timi
Tymoteusz Blazejczyk
+7 -4
View File
@@ -1,8 +1,11 @@
.. comment: generated by t_lint_assigneqexpr_bad
.. code-block:: sv
:linenos:
:emphasize-lines: 3,5
output logic c_o,
output logic d_o
);
assign c_o = (a_i != 0) ? 1 : 0;
assign d_o = // Note = not == below
(
e_o = 1 // <--- Warning: ASSIGNEQEXPR
) ? 1 : (
e_o = 0 // <--- Warning: ASSIGNEQEXPR
) ? b_i : 0;
+1 -1
View File
@@ -2,4 +2,4 @@
.. code-block::
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 100 tries
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
+1 -1
View File
@@ -1,4 +1,4 @@
.. comment: generated by t_lint_didnotconverge_nodbg_bad
.. code-block::
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 100 tries
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
+4 -4
View File
@@ -4,8 +4,8 @@
:emphasize-lines: 2
module t;
sub sub(); // <--- Warning
endmodule
module sub
(output port);
sub sub (); // <--- Warning
endmodule
module sub (
output port
);
+1 -1
View File
@@ -1,4 +1,4 @@
.. comment: generated by t_lint_pinmissing_bad
.. code-block::
%Warning-PINMISSING: example.v:1:8 Instance has missing pin: 'port'
%Warning-PINMISSING: example.v:1:7 Instance has missing pin: 'port'
+13
View File
@@ -16,3 +16,16 @@ C++14 compiler support
(Although this date has expired, this change is currently on hold until
the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
May 2028.)
`--structs-packed` option
The :vlopt:`--structs-packed` option was introduced when Verilator was
first implementing unpacked structs. That feature has been stable now
for multiple years, so :vlopt:`--structs-packed` should no longer be
used. Thus :vlopt:`--structs-packed` will change to a no-operation flag
and the related :option:`UNPACKED` warning will never be issued no
sooner than September 2026.
tcmalloc support
Verilator currently supports the default malloc, tcmalloc, or jemalloc.
As jemalloc has better performance, support for tcmalloc may be removed
no sooner than January 2027.
+6
View File
@@ -116,6 +116,12 @@ Options:
simulation runtime random seed value. If zero or not specified picks a
value from the system random number generator.
.. option:: +verilator+solver+file+<filename>
If specified, when the randomization solver is used, open the given
filename for writing, and log all random solver commands and responses
to it.
.. option:: +verilator+V
Shows the verbose version, including configuration information.
+40 -7
View File
@@ -262,10 +262,21 @@ Summary:
limitation that allow only one precompiled header per compilation.
Use this instead of ::vlopt:`-CFLAGS` with `-include <header-path>`.
.. option:: --constraint-array-limit <size>
Specifies the maximum array size for which array reduction methods
(sum, product, and, or, xor) in constraint expressions will be
expanded. Arrays larger than this limit will have their reduction
constraints ignored with a `CONSTRAINTIGN` warning. This prevents
excessive code generation for very large arrays.
Defaults to 64. Setting to 0 disables all array reduction constraint
expansion.
.. option:: --converge-limit <loops>
Rarely needed. Specifies the maximum number of runtime iterations before
creating a model failed to converge error. Defaults to 100.
creating a model failed to converge error. Defaults to 10000.
.. option:: --coverage
@@ -638,9 +649,12 @@ Summary:
.. option:: -fno-dfg
Rarely needed. Disable all use of the DFG-based combinational logic
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`,
:vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`.
Rarely needed. Disable the DFG-based combinational logic optimizer.
In versions before 5.048:
Alias for :vlopt:`-fno-dfg-pre-inline`, :vlopt:`-fno-dfg-post-inline` and
:vlopt:`-fno-dfg-scoped`.
.. option:: -fno-dfg-break-cycles
@@ -656,11 +670,15 @@ Summary:
.. option:: -fno-dfg-post-inline
Rarely needed. Do not apply the DFG optimizer after inlining.
Deprecated and has no effect (ignored).
In versions before 5.048: Do not apply the DFG optimizer after inlining.
.. option:: -fno-dfg-pre-inline
Rarely needed. Do not apply the DFG optimizer before inlining.
Deprecated and has no effect (ignored).
In versions before 5.048: Do not apply the DFG optimizer before inlining.
.. option:: -fno-dfg-push-down-sels
@@ -668,7 +686,10 @@ Summary:
.. option:: -fno-dfg-scoped
Rarely needed. Do not apply the DFG optimizer across module scopes.
Deprecated; use :vlopt:`-fno-dfg` instead.
In versions before 5.048: Do not apply the DFG optimizer across module
scopes.
.. option:: -fno-expand
@@ -694,6 +715,8 @@ Summary:
.. option:: -fno-life-post
.. option:: -fno-lift-expr
.. option:: -fno-localize
.. option:: -fno-merge-cond
@@ -731,6 +754,14 @@ Summary:
Rarely needed. Set the maximum array size (number of elements) for slice
optimization to avoid excessive memory usage.
.. option:: --func-recursion-depth <value>
Specifies the maximum depth of recursive constant function evaluation.
When a recursive function exceeds this depth during constant folding,
Verilator gives up and treats the expression as non-constant.
Defaults to 1000.
.. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
@@ -1661,6 +1692,8 @@ Summary:
.. option:: --structs-packed
Deprecated; discontinue use of this option.
Converts all unpacked structures to packed structures, and issues an
:option:`UNPACKED` warning. Specifying this option allows for backward
compatibility with versions before Verilator 5.006, when Verilator would
+5 -3
View File
@@ -40,7 +40,8 @@ To build using MSVC:
.. code-block:: bash
cd verilator # directory containing source files of verilator
mkdir build
mkdir -p build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install
cmake --build . --config Release
cmake --install . --prefix $PWD/../install
@@ -50,7 +51,8 @@ To build using ninja:
.. code-block:: bash
cd verilator
mkdir build
mkdir -p build
cd build
cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install -DCMAKE_MAKE_PROGRAM=<path to ninja binary> -DBISON_EXECUTABLE=<path to bison> -DFLEX_EXECUTABLE=<path to flex>
<path to ninja binary> #execute ninja
cmake --install . --prefix $PWD/../install
@@ -71,7 +73,7 @@ Example
cd verilator/examples
cd cmake_hello_c
mkdir build
mkdir -p build
cd build
cmake .. # cmake -G Ninja ..
cmake --build . --config Release # ninja
+2 -2
View File
@@ -61,7 +61,7 @@ In brief, to install from git:
# Prerequisites:
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
#sudo apt-get install libgoogle-perftools-dev libjemalloc-dev numactl perl-doc
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
@@ -144,7 +144,7 @@ installed for good performance:
sudo apt-get install ccache # If present at build, needed for run
sudo apt-get install mold # If present at build, needed for run
sudo apt-get install libgoogle-perftools-dev numactl
sudo apt-get install libjemalloc-dev numactl
To build Verilator you will need to install these packages; these do not
need to be present to run Verilator:
+16 -7
View File
@@ -856,6 +856,11 @@ List Of Warnings
pass.
.. option:: FUTURE
Warns that a feature is under development and not yet supported.
.. option:: GENCLK
Historical, never issued since version 5.000.
@@ -1850,8 +1855,12 @@ List Of Warnings
and #(1,2,3) AND (out, a, b);
Warns that rising, falling, and turn-off delays are currently unsupported.
The first (rising) delay is used for all cases.
Warns that the third (turn-off) delay is currently unsupported and is
ignored. Rising and falling delays are supported.
In versions before 5.048, warned that rising, falling, and turn-off
delays were unsupported. The first (rising) delay was used for all
cases.
.. option:: SELRANGE
@@ -2592,11 +2601,11 @@ List Of Warnings
Since version 5.046:
Issued if neither :vlopt:`--sched-zero-delay`, nor
:vlopt:`--sched-zero-delay` is used on the command line, and the input does
not contain a compile time known ``#0`` delay, but does contain a
``#(expressin)`` where the delay value cannot be determined at compile time.
Passing :vlopt:`--no-sched-zero-delay` can improve runtime performance if
variable delays are all known to be non-zero at runtime.
:vlopt:`--sched-zero-delay` is used on the command line, and the input
does not contain a compile time known ``#0`` delay, but does contain a
``#(expression)`` where the delay value cannot be determined at compile
time. Passing :vlopt:`--no-sched-zero-delay` can improve runtime
performance if variable delays are all known to be non-zero at runtime.
Also issued if :vlopt:`--no-sched-zero-delay` is used on the command line,
but the input contains a compile time known ``#0`` delay. This is safe to
+69 -36
View File
@@ -594,16 +594,15 @@ object.
This class manages processes that await events (triggers). There is one
such object per each trigger awaited by coroutines. Coroutines ``co_await``
this object's ``trigger`` function. They are stored in three stages -
`awaiting`, `fired` and `toResume`. First, they land in the `awaiting` stage, and
cannot be resumed. The ``ready`` function moves all coroutines from the
`awaiting` stage into the `fired` stage. The ``moveToResumeQueue`` function moves
`fired` coroutines into `toResume`. Finally, function `resume` resumes
all coroutines from the `toResume` stage.
`awaiting`, `fired` and `toResume`. First, they land in the `awaiting`
stage, and cannot be resumed. The ``ready`` function moves all coroutines
from the `awaiting` stage into the `fired` stage. The ``moveToResumeQueue``
function moves `fired` coroutines into `toResume`. Finally, function
`resume` resumes all coroutines from the `toResume` stage.
This split is done to avoid self-triggering, triggering coroutines
multiple times and triggering coroutines in the same iteration
they were suspended. See the `Scheduling with timing` section
for details on how this is used.
This split is done to avoid self-triggering, triggering coroutines multiple
times and triggering coroutines in the same iteration they were suspended.
See the `Scheduling with timing` section for details on how this is used.
``VlDynamicTriggerScheduler``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -739,6 +738,7 @@ There are two functions for managing timing logic called by ``_eval()``:
schedulers whose triggers were set in the current iteration.
Thanks to this separation a coroutine:
* awaiting a trigger cannot be suspended and resumed in the same iteration
(``test_regress/t/t_timing_eval_act.v``) - which is necessary to make
Verilator more predictable; this is the reason for introduction of 3rd stage
@@ -1843,33 +1843,25 @@ algorithmic stage. An example:
The following summarizes the above example dump, with more detail on each
field in the section below.
+---------------+--------------------------------------------------------+
| ``1:2:`` | The hierarchy of the ``VAR`` is the ``op2p`` |
| | pointer under the ``MODULE``, which in turn is the |
| | ``op1p`` pointer under the ``NETLIST``. |
+---------------+--------------------------------------------------------+
| ``VAR`` | The AstNodeType (e.g. ``AstVar``). |
+---------------+--------------------------------------------------------+
| ``0x91a780`` | Address of this node. |
+---------------+--------------------------------------------------------+
| ``<e74>`` | The 74th edit to the netlist was the last |
| | modification to this node. |
+---------------+--------------------------------------------------------+
| ``{a22ah}`` | This node is related to the source filename |
| | "a", where "a" is the first file read, "z" the 26th, |
| | and "aa" the 27th. Then line 22 in that file, then |
| | column 8 (aa=0, az=25, ba=26, ...). |
+---------------+--------------------------------------------------------+
| ``@dt=0x...`` | The address of the data type this node references. |
+---------------+--------------------------------------------------------+
| ``w32`` | The data-type width() is 32 bits. |
+---------------+--------------------------------------------------------+
| ``out_wide`` | The name() of the node, in this case, the name of the |
| | variable. |
+---------------+--------------------------------------------------------+
| ``[O]`` | Flags which vary with the type of node, in this |
| | case of a VAR, it means the variable is an output. |
+---------------+--------------------------------------------------------+
============= =============================================================
``1:2:`` The hierarchy of the ``VAR`` is the ``op2p`` pointer under
the ``MODULE``, which in turn is the ``op1p`` pointer under
the ``NETLIST``.
``VAR`` The AstNodeType (e.g. ``AstVar``).
``0x91a780`` Address of this node.
``<e74>`` The 74th edit to the netlist was the last modification to
this node.
``{a22ah}`` This node is related to the source filename "a", where "a" is
the first file read, "z" the 26th, and "aa" the 27th. Then
line 22 in that file, then column 8 (aa=0, az=25, ba=26,
...).
``@dt=0x...`` The address of the data type this node references.
``w32`` The data-type width() is 32 bits.
``out_wide`` The name() of the node, in this case, the name of the
variable.
``[O]`` Flags which vary with the type of node, in this case of a
VAR, it means the variable is an output.
============= =============================================================
In more detail, the following fields are dumped common to all nodes. They
are produced by the ``AstNode::dump()`` method:
@@ -2141,6 +2133,47 @@ backtrace. You will typically see a frame sequence something like:
visit()
...
Bisecting bad transformations
-----------------------------
If a bad transformation in the internals of Verilator causes a failure only at
runtime, it can be found fairly automatically by only applying the transform a
limited number of times, then performing a bisection search over the limit to
pinpoint the exact transformation that introduces the failure.
To facilitate this an instance of the ``V3DebugBisect`` class can be used in
conjunction with the ``verilator_bisect`` script.
In the offending algorithm, create a static instance of ``V3DebugBisect``:
::
static V3DebugBisect s_debugBisect{"TransformName"};
Call the ``stop`` method before applying a transformation, and do not proceed
if it returns ``false``. Then use ``verilator_bisect`` to search an interval of
values. You need to provide an arbitrary discriminator command, this should run
Verilator, then any necessary checks (e.g.: simulation) to detect that the
failure is still present. It should exit with a non-zero status if the failure
is still present. The discriminator command can otherwise be arbitrarily
complex, the actual search limit is passed via environment variables. E.g.:
::
bin/verilator_bisect DfgPeephole 0 1000 test_regress/t/t_myothertest.py
An additional command can be run before the discriminator command. E.g. this
will run RTLMeter, but first removes its working directory so the models are
recompiled on every step:
::
bin/verilator_bisect --pre "rm -rf work-bisect" DfgPeephole 0 10000000 \
rtlmeter run --cases "..." --workRoot=work-bisect
When the bisection ends, the first value that makes the discriminator command
fail is printed, which identifies the exact offending application of the
transform.
Adding a New Feature
====================
+6
View File
@@ -834,6 +834,7 @@ glibc
gmake
gmon
gotFinish
goto
gprof
gtkwave
hdr
@@ -878,6 +879,7 @@ iostream
ish
isunbounded
isunknown
jemalloc
jobserver
json
jwoutersymatra
@@ -891,6 +893,7 @@ len
libc
libext
libgoogle
libjemalloc
liblist
libsystemc
libtcmalloc
@@ -917,6 +920,7 @@ lxt
macromodule
makefile
makefiles
malloc
manpages
metacomment
metacomments
@@ -1146,6 +1150,7 @@ sys
systemc
taskify
tcmalloc
tcmalloc
tenghtt
testbench
threadsafe
@@ -1239,6 +1244,7 @@ vpiConstType
vpiDefName
vpiInertialDelay
vpiInstance
vpiIntVal
vpiLeftRange
vpiModule
vpiSigned
+3 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2023 Tony Bybell.
* Copyright (c) 2009-2026 Tony Bybell.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -3203,7 +3203,8 @@ static const char *modtypes[] = {"module",
"vhdl_for_generate",
"vhdl_if_generate",
"vhdl_generate",
"vhdl_package"};
"vhdl_package",
"sv_array"};
static const char *attrtypes[] = {"misc", "array", "enum", "class"};
+3 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2018 Tony Bybell.
* Copyright (c) 2009-2026 Tony Bybell.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -108,7 +108,8 @@ enum fstScopeType
FST_ST_VHDL_GENERATE = 20,
FST_ST_VHDL_PACKAGE = 21,
FST_ST_MAX = 21,
FST_ST_SV_ARRAY = 22,
FST_ST_MAX = 22,
FST_ST_GEN_ATTRBEGIN = 252,
FST_ST_GEN_ATTREND = 253,
+672 -440
View File
File diff suppressed because it is too large Load Diff
+20 -4
View File
@@ -392,7 +392,7 @@ protected:
int m_errorLimit = 1; // Stop on error number
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
int m_randSeed = 0; // Random seed: 0=random
enum { UNITS_NONE = 99 }; // Default based on precision
static constexpr int UNITS_NONE = 99; // Default based on precision
int m_timeFormatUnits = UNITS_NONE; // $timeformat units
int m_timeFormatPrecision = 0; // $timeformat number of decimal places
int m_timeFormatWidth = 20; // $timeformat character width
@@ -408,12 +408,14 @@ protected:
struct NonSerialized final { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
bool m_executingFinal = false; // Running generated final() code
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
// Slow path
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
std::string m_solverLogFilename; // SMT solver log filename
std::string m_solverProgram; // SMT solver program
bool m_warnUnsatConstr = true; // Warn on unsatisfied constraints
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
@@ -530,12 +532,16 @@ public:
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
/// Set if got a $finish or $stop/error
void gotFinish(bool flag) VL_MT_SAFE;
/// Check if generated final() code is executing
bool executingFinal() const VL_MT_SAFE;
/// Set if generated final() code is executing
void executingFinal(bool flag) VL_MT_SAFE;
/// Return if quiet enabled
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
/// Enable quiet (also prevents need for OS calls to get CPU time)
void quiet(bool flag) VL_MT_SAFE;
/// Return randReset value
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
int randReset() const VL_MT_SAFE { return m_s.m_randReset; }
/// Select initial value of otherwise uninitialized signals.
/// 0 = Set to zeros
/// 1 = Set all bits to one
@@ -581,6 +587,8 @@ public:
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
/// Return time as unit string
std::string timeWithUnitString() const VL_MT_SAFE;
/// Return time units as power-of-ten
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
/// Set time units as power-of-ten
@@ -661,6 +669,9 @@ public:
std::string profVltFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
// Internal: Solver log filename
std::string solverLogFilename() const VL_MT_SAFE;
void solverLogFilename(const std::string& flag) VL_MT_SAFE;
// Internal: SMT solver program
std::string solverProgram() const VL_MT_SAFE;
void solverProgram(const std::string& flag) VL_MT_SAFE;
@@ -731,8 +742,13 @@ public: // But internals only - called from verilated modules, VerilatedSyms
~VerilatedScope();
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
void varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags,
void* forceReadSignalData, const char* forceReadSignalName,
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
int udims, int pdims...) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
+3 -3
View File
@@ -114,7 +114,7 @@ CPPFLAGS += $(OPT)
# On macOS, specify all weak symbols as dynamic_lookup.
# Otherwise, you get undefined symbol errors.
ifeq ($(UNAME_S),Darwin)
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv,-U,_vlog_startup_routines
endif
# Allow upper level user makefiles to specify flags they want.
@@ -280,10 +280,10 @@ ifneq ($(VM_DEFAULT_RULES),0)
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
$(VK_OBJS_FAST): %.o: %.cpp $(VK_PCH_H).fast.gch
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_FAST) -c -o $@ $<
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_FAST) -c $<
$(VK_OBJS_SLOW): %.o: %.cpp $(VK_PCH_H).slow.gch
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_SLOW) -c -o $@ $<
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_SLOW) -c $<
$(VK_GLOBAL_OBJS): %.o: %.cpp
$(OBJCACHE) $(CXX) $(OPT_GLOBAL) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+3 -3
View File
@@ -147,7 +147,7 @@ private:
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
constexpr size_t LEN_MAX_HEX = 20;
char hex[LEN_MAX_HEX];
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
(void)VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
rtn += hex;
} else {
rtn += *pos;
@@ -344,9 +344,9 @@ public:
// Insert the values
int addKeynum = 0;
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
const std::string key = keys[i];
const std::string& key = keys[i];
if (!keys[i].empty()) {
const std::string val = valps[i];
const std::string& val = valps[i];
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << "\n";
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
+2 -2
View File
@@ -775,7 +775,7 @@ int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
//======================================================================
// Time
int svGetTime(const svScope scope, svTimeVal* time) {
int svGetTime(const svScope /*scope*/, svTimeVal* time) {
if (VL_UNLIKELY(!time)) return -1;
const QData qtime = VL_TIME_Q();
VlWide<2> itime;
@@ -796,7 +796,7 @@ int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
return 0;
}
int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
int svGetTimePrecision(const svScope /*scope*/, int32_t* time_precision) {
if (VL_UNLIKELY(!time_precision)) return -1;
*time_precision = Verilated::threadContextp()->timeprecision();
return 0;
+102 -46
View File
@@ -82,6 +82,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
fstWriterSetVersion(m_fst, "Generated by VerilatedFst");
constDump(true); // First dump must contain the const signals
fullDump(true); // First dump must be full for fst
@@ -138,19 +139,8 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elemen
assert(newEntry);
}
// TODO: should return std::optional<fstScopeType>, but I can't have C++17
static std::pair<bool, fstScopeType> toFstScopeType(VerilatedTracePrefixType type) {
switch (type) {
case VerilatedTracePrefixType::SCOPE_MODULE: return {true, FST_ST_VCD_MODULE};
case VerilatedTracePrefixType::SCOPE_INTERFACE: return {true, FST_ST_VCD_INTERFACE};
case VerilatedTracePrefixType::STRUCT_PACKED:
case VerilatedTracePrefixType::STRUCT_UNPACKED: return {true, FST_ST_VCD_STRUCT};
case VerilatedTracePrefixType::UNION_PACKED: return {true, FST_ST_VCD_UNION};
default: return {false, /* unused so whatever, just need a value */ FST_ST_VCD_SCOPE};
}
}
void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type, int left,
int right) {
assert(!m_prefixStack.empty()); // Constructor makes an empty entry
const std::string name{namep};
// An empty name means this is the root of a model created with
@@ -162,28 +152,64 @@ void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type)
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
} else if (name.empty()) {
}
if (name.empty()) {
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
}
bool isProperScope = true;
switch (type) {
case VerilatedTracePrefixType::ARRAY_PACKED:
case VerilatedTracePrefixType::ARRAY_UNPACKED: isProperScope = false; break;
default: break;
}
// This code assumes a signal at a given prefix level is declared before
// any pushPrefix are done at that same level.
const std::string newPrefix = prevPrefix + name;
const auto pair = toFstScopeType(type);
const bool properScope = pair.first;
const fstScopeType scopeType = pair.second;
m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
if (properScope) {
const std::string scopeName = lastWord(newPrefix);
fstWriterSetScope(m_fst, scopeType, scopeName.c_str(), nullptr);
m_prefixStack.emplace_back(newPrefix + (isProperScope ? " " : ""), type);
const uint32_t l = static_cast<uint32_t>(left);
const uint32_t r = static_cast<uint32_t>(right);
const uint64_t lr = static_cast<uint64_t>(l) << 32 | static_cast<uint64_t>(r);
switch (type) {
case VerilatedTracePrefixType::SCOPE_MODULE:
fstWriterSetScope(m_fst, FST_ST_VCD_MODULE, namep, nullptr);
break;
case VerilatedTracePrefixType::SCOPE_INTERFACE:
fstWriterSetScope(m_fst, FST_ST_VCD_INTERFACE, namep, nullptr);
break;
case VerilatedTracePrefixType::STRUCT_PACKED:
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_PACKED, "members", l);
fstWriterSetScope(m_fst, FST_ST_VCD_STRUCT, namep, nullptr);
break;
case VerilatedTracePrefixType::STRUCT_UNPACKED:
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_UNPACKED, "members", l);
fstWriterSetScope(m_fst, FST_ST_VCD_STRUCT, namep, nullptr);
break;
case VerilatedTracePrefixType::UNION_PACKED:
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_PACKED, "members", l);
fstWriterSetScope(m_fst, FST_ST_VCD_UNION, namep, nullptr);
break;
case VerilatedTracePrefixType::ARRAY_PACKED:
fstWriterSetAttrBegin(m_fst, FST_AT_ARRAY, FST_AR_PACKED, "bounds", lr);
fstWriterSetScope(m_fst, FST_ST_SV_ARRAY, namep, nullptr);
break;
case VerilatedTracePrefixType::ARRAY_UNPACKED:
fstWriterSetAttrBegin(m_fst, FST_AT_ARRAY, FST_AR_UNPACKED, "bounds", lr);
fstWriterSetScope(m_fst, FST_ST_SV_ARRAY, namep, nullptr);
break;
default: break;
}
}
void VerilatedFst::popPrefix() {
assert(!m_prefixStack.empty());
const bool properScope = toFstScopeType(m_prefixStack.back().second).first;
if (properScope) fstWriterSetUpscope(m_fst);
if (m_prefixStack.back().second != VerilatedTracePrefixType::ROOTIO_WRAPPER) {
fstWriterSetUpscope(m_fst);
}
m_prefixStack.pop_back();
assert(!m_prefixStack.empty()); // Always one left, the constructor's initial one
}
@@ -260,44 +286,74 @@ void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum,
}
}
void VerilatedFst::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
// versions to call when the sig is not array member
void VerilatedFst::declEvent(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
VerilatedTraceSigType type) {
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 0, 0);
}
void VerilatedFst::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
VerilatedTraceSigType type) {
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 0, 0);
}
void VerilatedFst::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
VerilatedTraceSigType type, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
}
void VerilatedFst::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
void VerilatedFst::declQuad(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
VerilatedTraceSigType type, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
}
void VerilatedFst::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
void VerilatedFst::declWide(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
}
void VerilatedFst::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, bool array, int arraynum) {
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 63, 0);
VerilatedTraceSigType type) {
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 63, 0);
}
// versions to call when the sig is array member
void VerilatedFst::declEventArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int arraynum) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 0, 0);
}
void VerilatedFst::declBitArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int arraynum) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 0, 0);
}
void VerilatedFst::declBusArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
}
void VerilatedFst::declQuadArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
}
void VerilatedFst::declWideArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
}
void VerilatedFst::declDoubleArray(uint32_t code, const char* name, int dtypenum,
VerilatedTraceSigDirection direction,
VerilatedTraceSigKind kind, VerilatedTraceSigType type,
int arraynum) {
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 63, 0);
}
//=============================================================================
// Get/commit trace buffer
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) {
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t /*fidx*/) {
if (offload()) return new OffloadBuffer{*this};
return new Buffer{*this};
}
+65 -19
View File
@@ -110,32 +110,78 @@ public:
//=========================================================================
// Internal interface to Verilator generated code
void pushPrefix(const char*, VerilatedTracePrefixType);
void pushPrefix(const char*, VerilatedTracePrefixType, int left = 0, int right = 0);
void popPrefix();
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
// versions to call when the sig is not array member
void declEvent(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType);
void declBit(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType);
void declBus(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
void declQuad(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
void declWide(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
void declDouble(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType);
// versions to call when the sig is array member
void declEventArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
void declBitArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
void declBusArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
int lsb);
void declQuadArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
int lsb);
void declWideArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
int lsb);
void declDoubleArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
const char** itemNamesp, const char** itemValuesp);
};
// We use macros to drop unused arguments at compile time. This saves code size.
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) \
tracep->pushPrefix(name, type, left, right);
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declEvent(code, name, dtypenum, dir, kind, type)
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declBit(code, name, dtypenum, dir, kind, type)
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declBus(code, name, dtypenum, dir, kind, type, msb, lsb)
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declQuad(code, name, dtypenum, dir, kind, type, msb, lsb)
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declWide(code, name, dtypenum, dir, kind, type, msb, lsb)
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declDouble(code, name, dtypenum, dir, kind, type)
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declEventArray(code, name, dtypenum, dir, kind, type, arraynum)
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declBitArray(code, name, dtypenum, dir, kind, type, arraynum)
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declBusArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declQuadArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declWideArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declDoubleArray(code, name, dtypenum, dir, kind, type, arraynum)
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <>
+125 -102
View File
@@ -98,10 +98,9 @@ inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
// Modulus isn't very fast but it's common that hi-low is power-of-two
return (rnd % (hi - lo + 1)) + lo;
} else {
if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
return (rnd % (lo - hi + 1)) + hi;
}
if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
return (rnd % (lo - hi + 1)) + hi;
}
/// Random reset a signal of given width (init time only, var-specific PRNG)
@@ -306,7 +305,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
// clang-format off
#if defined(SYSTEMC_VERSION)
#ifdef SYSTEMC_VERSION
/// Return current simulation time
// Already defined: extern sc_time sc_time_stamp();
inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); }
@@ -500,15 +499,15 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
sc_dt::sc_biguint<(obits)> _butemp = (svar).read(); \
uint32_t* chunkp = _butemp.get_raw(); \
int32_t lsb = 0; \
while (lsb < obits - BITS_PER_DIGIT) { \
while (lsb < (obits) - BITS_PER_DIGIT) { \
const uint32_t data = *chunkp; \
++chunkp; \
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
_vl_insert_WI((owp).data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
lsb += BITS_PER_DIGIT; \
} \
if (lsb < obits) { \
if (lsb < (obits)) { \
const uint32_t msb_data = *chunkp; \
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
_vl_insert_WI((owp).data(), msb_data, (obits) - 1, lsb); \
} \
(owp)[words - 1] &= VL_MASK_E(obits); \
}
@@ -762,14 +761,12 @@ static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_PURE {
static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1,
IData ctrl2) VL_PURE {
const int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1);
if (ctrlSum == 3) {
return VL_COUNTONES_I(lhs);
} else if (ctrlSum == 0) {
if (ctrlSum == 3) return VL_COUNTONES_I(lhs);
if (ctrlSum == 0) {
const IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1);
return VL_COUNTONES_I(~lhs & mask);
} else {
return (lbits == 32) ? 32 : lbits;
}
return (lbits == 32) ? 32 : lbits;
}
static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1,
IData ctrl2) VL_PURE {
@@ -1018,17 +1015,17 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
static inline IData VL_DIV_III(int /*lbits*/, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
static inline QData VL_DIV_QQQ(int /*lbits*/, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
static inline IData VL_MODDIV_III(int /*lbits*/, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
static inline QData VL_MODDIV_QQQ(int /*lbits*/, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
@@ -1176,9 +1173,8 @@ static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp
VL_DIV_WWW(lbits, qNoSign, ltup, rtup);
_vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign));
return owp;
} else {
return VL_DIV_WWW(lbits, owp, ltup, rtup);
}
return VL_DIV_WWW(lbits, owp, ltup, rtup);
}
static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp,
WDataInP const rwp) VL_MT_SAFE {
@@ -1199,9 +1195,8 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const
VL_MODDIV_WWW(lbits, qNoSign, ltup, rtup);
_vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign));
return owp;
} else {
return VL_MODDIV_WWW(lbits, owp, ltup, rtup);
}
return VL_MODDIV_WWW(lbits, owp, ltup, rtup);
}
#define VL_POW_IIQ(obits, lbits, rbits, lhs, rhs) VL_POW_QQQ(obits, lbits, rbits, lhs, rhs)
@@ -1255,14 +1250,11 @@ static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs
if (rsign && VL_SIGN_I(rbits, rhs)) {
if (lhs == 0) {
return 0; // "X"
} else if (lhs == 1) {
return 1;
} else if (lsign && lhs == VL_MASK_I(obits)) { // -1
if (rhs & 1) {
return VL_MASK_I(obits); // -1^odd=-1
} else {
return 1; // -1^even=1
}
}
if (lhs == 1) { return 1; }
if (lsign && lhs == VL_MASK_I(obits)) { // -1
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
return 1; // -1^even=1
}
return 0;
}
@@ -1272,16 +1264,12 @@ static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs
bool rsign) VL_MT_SAFE {
if (VL_UNLIKELY(rhs == 0)) return 1;
if (rsign && VL_SIGN_Q(rbits, rhs)) {
if (lhs == 0) {
return 0; // "X"
} else if (lhs == 1) {
return 1;
} else if (lsign && lhs == VL_MASK_Q(obits)) { // -1
if (rhs & 1) {
return VL_MASK_Q(obits); // -1^odd=-1
} else {
return 1; // -1^even=1
}
if (lhs == 0) return 0; // "X"
if (lhs == 1) return 1;
if (lsign && lhs == VL_MASK_Q(obits)) { // -1
if (rhs & 1) return VL_MASK_Q(obits); // -1^odd=-1
return 1; // -1^even=1
}
return 0;
}
@@ -1614,28 +1602,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
return owp;
}
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<CData>& q) {
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<CData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
ret |= static_cast<IData>(q.at(q.size() - 1 - i)) << (i * lbits);
return ret;
}
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<SData>& q) {
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<SData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
ret |= static_cast<IData>(q.at(q.size() - 1 - i)) << (i * lbits);
return ret;
}
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<IData>& q) {
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<IData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
return ret;
}
template <std::size_t N_Depth>
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<CData, N_Depth>& q) {
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<CData, N_Depth>& q) {
IData ret = 0;
for (size_t i = 0; i < N_Depth; ++i)
ret |= static_cast<IData>(q[N_Depth - 1 - i]) << (i * lbits);
@@ -1643,7 +1631,7 @@ static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<CData, N
}
template <std::size_t N_Depth>
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<SData, N_Depth>& q) {
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<SData, N_Depth>& q) {
IData ret = 0;
for (size_t i = 0; i < N_Depth; ++i)
ret |= static_cast<IData>(q[N_Depth - 1 - i]) << (i * lbits);
@@ -1651,27 +1639,27 @@ static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<SData, N
}
template <std::size_t N_Depth>
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<IData, N_Depth>& q) {
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<IData, N_Depth>& q) {
IData ret = 0;
for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
return ret;
}
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<CData>& q) {
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<CData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
return ret;
}
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<SData>& q) {
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<SData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
return ret;
}
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<IData>& q) {
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<IData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
@@ -1679,7 +1667,7 @@ static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<IData>& q)
}
template <std::size_t N_Depth>
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<CData, N_Depth>& q) {
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<CData, N_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < N_Depth; ++i)
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
@@ -1687,7 +1675,7 @@ static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<CData, N
}
template <std::size_t N_Depth>
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<SData, N_Depth>& q) {
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<SData, N_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < N_Depth; ++i)
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
@@ -1695,21 +1683,21 @@ static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<SData, N
}
template <std::size_t N_Depth>
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<IData, N_Depth>& q) {
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<IData, N_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < N_Depth; ++i)
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
return ret;
}
static inline QData VL_PACK_Q_RQ(int obits, int lbits, const VlQueue<QData>& q) {
static inline QData VL_PACK_Q_RQ(int /*obits*/, int lbits, const VlQueue<QData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
return ret;
}
template <std::size_t N_Depth>
static inline QData VL_PACK_Q_UQ(int obits, int lbits, const VlUnpacked<QData, N_Depth>& q) {
static inline QData VL_PACK_Q_UQ(int /*obits*/, int lbits, const VlUnpacked<QData, N_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
return ret;
@@ -1922,7 +1910,7 @@ static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp,
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
// If RHS (rd/rwp) is larger than the output, zeros (or all ones for >>>) must be returned
// (This corresponds to AstShift*Ovr Ast nodes)
static inline IData VL_SHIFTL_III(int obits, int, int, IData lhs, IData rhs) VL_MT_SAFE {
static inline IData VL_SHIFTL_III(int /*obits*/, int, int, IData lhs, IData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return lhs << rhs; // Small is common so not clean return
}
@@ -1930,7 +1918,7 @@ static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return VL_CLEAN_II(obits, obits, lhs << rhs);
}
static inline QData VL_SHIFTL_QQI(int obits, int, int, QData lhs, IData rhs) VL_MT_SAFE {
static inline QData VL_SHIFTL_QQI(int /*obits*/, int, int, QData lhs, IData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
return lhs << rhs; // Small is common so not clean return
}
@@ -1991,19 +1979,19 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs,
// EMIT_RULE: VL_SHIFTR: 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 IData VL_SHIFTR_III(int obits, int, int, IData lhs, IData rhs) VL_PURE {
static inline IData VL_SHIFTR_III(int /*obits*/, int, int, IData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return lhs >> rhs;
}
static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_PURE {
static inline IData VL_SHIFTR_IIQ(int /*obits*/, int, int, IData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return lhs >> rhs;
}
static inline QData VL_SHIFTR_QQI(int obits, int, int, QData lhs, IData rhs) VL_PURE {
static inline QData VL_SHIFTR_QQI(int /*obits*/, int, int, QData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
return lhs >> rhs;
}
static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_PURE {
static inline QData VL_SHIFTR_QQQ(int /*obits*/, int, int, QData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
return lhs >> rhs;
}
@@ -2181,9 +2169,8 @@ static inline IData VL_BITSEL_IWII(int lbits, WDataInP const lwp, IData rd) VL_M
if (VL_UNLIKELY(rd > static_cast<IData>(lbits))) {
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
// We return all 1's as that's more likely to find bugs (?) than 0's.
} else {
return (lwp[word] >> VL_BITBIT_E(rd));
}
return (lwp[word] >> VL_BITBIT_E(rd));
}
// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty
@@ -2196,34 +2183,35 @@ static inline IData VL_SEL_IWII(int lbits, WDataInP const lwp, IData lsb, IData
const int msb = lsb + width - 1;
if (VL_UNLIKELY(msb >= lbits)) {
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
return VL_BITRSHIFT_W(lwp, lsb);
} else {
// 32 bit extraction may span two words
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word
return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb));
}
if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
return VL_BITRSHIFT_W(lwp, lsb);
}
// 32 bit extraction may span two words
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word
return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb));
}
static inline QData VL_SEL_QWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE {
const int msb = lsb + width - 1;
if (VL_UNLIKELY(msb > lbits)) {
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
}
if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
return VL_BITRSHIFT_W(lwp, lsb);
} else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast<int>(lsb))) {
}
if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast<int>(lsb))) {
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
const QData hi = (lwp[VL_BITWORD_E(msb)]);
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
return (hi << nbitsfromlow) | lo;
} else {
// 64 bit extraction may span three words
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
const QData hi = (lwp[VL_BITWORD_E(msb)]);
const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]);
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo;
}
// 64 bit extraction may span three words
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
const QData hi = (lwp[VL_BITWORD_E(msb)]);
const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]);
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo;
}
static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataInP const lwp,
@@ -2277,13 +2265,11 @@ static inline bool VL_GET_QUEUE_BIT(const VlQueue<T>& queue, int srcElementBits,
if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
const T element = queue.at(elemIdx);
if (srcElementBits == 1) {
return element & 1;
} else {
const size_t bitInElem = bitIndex % srcElementBits;
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
return (element >> actualBitPos) & 1;
}
if (srcElementBits == 1) return element & 1;
const size_t bitInElem = bitIndex % srcElementBits;
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
return (element >> actualBitPos) & 1;
}
// Helper function to set a bit in the destination queue
@@ -2351,8 +2337,8 @@ static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
// This specialization works for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>.
template <typename T>
static inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
int dstElementBits) {
static inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/,
int srcElementBits, int dstElementBits) {
if (srcElementBits == dstElementBits) {
// Simple case: same element bit width, direct copy of each element
if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op
@@ -2417,6 +2403,46 @@ static inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits
}
}
// Reverse element order of an unpacked array in-place.
// Used by emitter for descending-range arrays after VL_UNPACK_*.
template <typename T_Value, std::size_t N_Depth>
static inline void VL_UNPACK_REVERSED(VlUnpacked<T_Value, N_Depth>& q) {
for (size_t i = 0; i < N_Depth / 2; ++i) {
const T_Value tmp = q[i];
q[i] = q[N_Depth - 1 - i];
q[N_Depth - 1 - i] = tmp;
}
}
// Return a reversed copy of an unpacked array.
// Used by emitter for descending-range arrays before VL_PACK_*.
template <typename T_Value, std::size_t N_Depth>
static inline VlUnpacked<T_Value, N_Depth>
VL_PACK_REVERSED(const VlUnpacked<T_Value, N_Depth>& q) {
VlUnpacked<T_Value, N_Depth> ret;
for (size_t i = 0; i < N_Depth; ++i) ret[i] = q[N_Depth - 1 - i];
return ret;
}
// Overloads for VlUnpacked source -> VlQueue destination
template <typename T, std::size_t N_Depth>
static inline void VL_COPY_Q(VlQueue<T>& q, const VlUnpacked<T, N_Depth>& from, int lbits,
int srcElementBits, int dstElementBits) {
VlQueue<T> srcQ;
srcQ.renew(N_Depth);
for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[i];
VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits);
}
template <typename T, std::size_t N_Depth>
static inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlUnpacked<T, N_Depth>& from, int lbits,
int srcElementBits, int dstElementBits) {
VlQueue<T> srcQ;
srcQ.renew(N_Depth);
for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[N_Depth - 1 - i];
VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits);
}
//======================================================================
// Expressions needing insert/select
@@ -2536,49 +2562,49 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue<VlWide<N_Words>>
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<CData, N_Depth>& q,
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<CData, N_Depth>& q,
IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<SData, N_Depth>& q,
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<SData, N_Depth>& q,
IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<IData, N_Depth>& q,
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<IData, N_Depth>& q,
IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<CData, N_Depth>& q,
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<CData, N_Depth>& q,
QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<SData, N_Depth>& q,
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<SData, N_Depth>& q,
QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<IData, N_Depth>& q,
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<IData, N_Depth>& q,
QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t N_Depth>
static inline void VL_UNPACK_UQ_Q(int lbits, int rbits, VlUnpacked<QData, N_Depth>& q,
static inline void VL_UNPACK_UQ_Q(int lbits, int /*rbits*/, VlUnpacked<QData, N_Depth>& q,
QData from) {
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
@@ -2792,10 +2818,9 @@ static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataIn
//======================================================================
// Triops
static inline WDataOutP VL_COND_WIWW(int obits, WDataOutP owp, int cond, WDataInP const w1p,
WDataInP const w2p) VL_MT_SAFE {
return VL_MEMCPY_W(owp, cond ? w1p : w2p, VL_WORDS_I(obits));
}
// This must be a macro in order for short-circuiting of the values to work.
#define VL_COND_WIWW(obits, owp, cond, w1p, w2p) \
VL_MEMCPY_W(owp, (cond) ? (w1p) : (w2p), VL_WORDS_I(obits))
//======================================================================
// Constification
@@ -2936,17 +2961,15 @@ extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
if (ignoreCase) {
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
return std::strcmp(lhs.c_str(), rhs.c_str());
}
if (ignoreCase) { return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); }
return std::strcmp(lhs.c_str(), rhs.c_str());
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE;
extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE;
extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE;
extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str,
int truncFront = 0) VL_PURE;
extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE;
+2 -3
View File
@@ -378,7 +378,7 @@ private:
// MCD Case
if (fdi & 1) fp.push_back(stdout);
fdi >>= 1;
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
for (size_t i = 1; (fdi != 0) && (i < VerilatedFpList::capacity()); ++i, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
}
}
@@ -553,9 +553,8 @@ private:
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
return s().m_exportNext++;
} else {
return it->second;
}
return it->second;
}
public:
+13 -11
View File
@@ -99,10 +99,11 @@ IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE {
double r = _vl_dbase_chi_square(seedr, df);
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r; // LCOV_EXCL_LINE
i = static_cast<int32_t>(r + 0.5); // LCOV_EXCL_LINE
i = static_cast<int32_t>(
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
i = -i; // LCOV_EXCL_LINE
}
return static_cast<IData>(i);
@@ -122,10 +123,10 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
double r = -a * log(x) / b;
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
@@ -140,10 +141,11 @@ IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
int32_t i;
double r = _vl_dbase_exponential(seedr, mean);
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r; // LCOV_EXCL_LINE
i = static_cast<int32_t>(r + 0.5); // LCOV_EXCL_LINE
i = static_cast<int32_t>(
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
i = -i; // LCOV_EXCL_LINE
}
return static_cast<IData>(i);
@@ -155,10 +157,10 @@ IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE {
double r = _vl_dbase_normal(seedr, mean, sd);
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
@@ -172,7 +174,7 @@ IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE {
}
int32_t i = 0;
double q = -static_cast<double>(mean);
double p = exp(q);
const double p = exp(q);
q = _vl_dbase_uniform(seedr, 0, 1);
while (p < q) {
++i;
@@ -193,10 +195,10 @@ IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE {
double r = _vl_dbase_normal(seedr, 0, 1) / root;
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5);
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
+1 -1
View File
@@ -40,7 +40,7 @@ constexpr const char* const VlExecutionRecord::s_ascii[];
template <size_t N>
static size_t roundUptoMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
const size_t mask = N - 1;
return (value + mask) & ~mask;
}
+213 -159
View File
@@ -23,7 +23,7 @@
#include "verilated_random.h"
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -62,17 +62,20 @@ class VlRProcess final : private std::streambuf, public std::iostream {
char m_readBuf[BUFFER_SIZE];
char m_writeBuf[BUFFER_SIZE];
std::unique_ptr<std::ofstream> m_logfp; // Log file stream
uint64_t m_logLastTime = ~0ULL; // Last timestamp for logfile
public:
typedef std::streambuf::traits_type traits_type;
protected:
int overflow(int c = traits_type::eof()) override {
char c2 = static_cast<char>(c);
const char c2 = static_cast<char>(c);
if (pbase() == pptr()) return 0;
size_t size = pptr() - pbase();
ssize_t n = ::write(m_writeFd, pbase(), size);
// VL_PRINTF_MT("solver-write '%s'\n", std::string(pbase(), size).c_str());
if (n == -1) perror("write");
const size_t size = pptr() - pbase();
log(" ", std::string(pbase(), size));
const ssize_t n = ::write(m_writeFd, pbase(), size);
if (VL_UNLIKELY(n == -1)) perror("write");
if (n <= 0) {
wait_report();
return traits_type::eof();
@@ -86,12 +89,13 @@ protected:
}
int underflow() override {
sync();
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
if (n == -1) perror("read");
const ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
if (VL_UNLIKELY(n == -1)) perror("read");
if (n <= 0) {
wait_report();
return traits_type::eof();
}
log("< ", std::string(m_readBuf, n));
setg(m_readBuf, m_readBuf, m_readBuf + n);
return traits_type::to_int_type(m_readBuf[0]);
}
@@ -105,6 +109,7 @@ public:
: std::streambuf{}
, std::iostream{this}
, m_cmd{cmd} {
logOpen();
open(cmd);
}
@@ -153,11 +158,11 @@ public:
constexpr int P_RD = 0;
constexpr int P_WR = 1;
if (pipe(fd_stdin) != 0) {
if (VL_UNLIKELY(pipe(fd_stdin) != 0)) {
perror("VlRProcess::open: pipe");
return false;
}
if (pipe(fd_stdout) != 0) {
if (VL_UNLIKELY(pipe(fd_stdout) != 0)) {
perror("VlRProcess::open: pipe");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
@@ -176,8 +181,9 @@ public:
return false;
}
log("", "# Open: "s + cmd[0]);
const pid_t pid = fork();
if (pid < 0) {
if (VL_UNLIKELY(pid < 0)) {
perror("VlRProcess::open: fork");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
@@ -213,6 +219,35 @@ public:
return false;
#endif
}
private:
void logOpen() {
const std::string filename = Verilated::threadContextp()->solverLogFilename();
if (filename.empty()) return;
m_logfp = std::make_unique<std::ofstream>(filename);
if (m_logfp.get() && m_logfp.get()->fail()) m_logfp = nullptr;
if (!m_logfp) {
const std::string msg = "%Error: Can't write '"s + filename + "'";
VL_FATAL_MT("", 0, "", msg.c_str());
return;
}
*m_logfp << "# Verilator solver log\n";
}
void log(const std::string& prefix, const std::string& text) {
if (VL_LIKELY(!m_logfp.get()) || text.empty()) return;
if (m_logLastTime != Verilated::threadContextp()->time()) {
m_logLastTime = Verilated::threadContextp()->time();
*m_logfp << "# [" << Verilated::threadContextp()->timeWithUnitString() << "]\n";
}
std::size_t startPos = 0;
while (1) {
const std::size_t endPos = text.find('\n', startPos);
if (endPos == std::string::npos) break;
*m_logfp << prefix << text.substr(startPos, endPos - startPos) << '\n';
startPos = endPos + 1;
}
if (startPos < text.length()) *m_logfp << prefix << text.substr(startPos) << '\n';
}
};
static VlRProcess& getSolver() {
@@ -275,7 +310,8 @@ static std::string readUntilBalanced(std::istream& stream) {
static std::string parseNestedSelect(const std::string& nested_select_expr,
std::vector<std::string>& indices) {
std::istringstream nestedStream(nested_select_expr);
std::string name, idx;
std::string name;
std::string idx;
nestedStream >> name;
if (name == "(select") {
const std::string further_nested_expr = readUntilBalanced(nestedStream);
@@ -376,76 +412,33 @@ size_t VlRandomizer::hashConstraints() const {
return h;
}
void VlRandomizer::enumerateRandcValues(const std::string& varName, VlRNG& rngr) {
std::vector<uint64_t> values;
const auto varIt = m_vars.find(varName);
if (varIt == m_vars.end()) return;
const int width = varIt->second->width();
std::iostream& os = getSolver();
if (!os) return;
// Set up a single incremental solver session for enumeration
os << "(set-option :produce-models true)\n";
os << "(set-logic QF_ABV)\n";
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
// Declare all variables (solver needs full context for cross-var constraints)
for (const auto& var : m_vars) {
if (var.second->dimension() > 0) {
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
var.second->setArrayInfo(arrVarsp);
void VlRandomizer::emitRandcExclusions(std::ostream& os) const {
for (const auto& name : m_randcVarNames) {
const auto usedIt = m_randcUsedValues.find(name);
if (usedIt != m_randcUsedValues.end()) {
const int w = m_vars.at(name)->width();
for (const uint64_t val : usedIt->second) {
os << "(assert (not (= " << name << " (_ bv" << val << " " << w << "))))\n";
}
}
os << "(declare-fun " << var.first << " () ";
var.second->emitType(os);
os << ")\n";
}
}
// Assert all user constraints
for (const std::string& constraint : m_constraints) {
os << "(assert (= #b1 " << constraint << "))\n";
static uint64_t readVarValueU64(const void* datap, int width) {
if (width <= VL_BYTESIZE) return *static_cast<const CData*>(datap);
if (width <= VL_SHORTSIZE) return *static_cast<const SData*>(datap);
if (width <= VL_IDATASIZE) return *static_cast<const IData*>(datap);
if (width <= VL_QUADSIZE) return *static_cast<const QData*>(datap);
return 0;
}
void VlRandomizer::recordRandcValues() {
for (const auto& name : m_randcVarNames) {
const auto varIt = m_vars.find(name);
if (varIt == m_vars.end()) continue;
const VlRandomVar& var = *varIt->second;
m_randcUsedValues[name].insert(readVarValueU64(var.datap(0), var.width()));
}
// Incrementally enumerate all valid values for this randc variable
while (true) {
os << "(check-sat)\n";
std::string sat;
do { std::getline(os, sat); } while (sat.empty());
if (sat != "sat") break;
// Read just this variable's value
os << "(get-value (" << varName << "))\n";
char c;
os >> c; // '('
os >> c; // '('
std::string name, value;
os >> name; // Consume variable name token from solver output
(void)name;
std::getline(os, value, ')');
os >> c; // ')'
// Parse the SMT value to uint64_t
VlWide<VL_WQ_WORDS_E> qowp;
VL_SET_WQ(qowp, 0ULL);
if (!parseSMTNum(width, qowp, value)) break;
const uint64_t numVal = (width <= 32) ? qowp[0] : VL_SET_QW(qowp);
values.push_back(numVal);
// Exclude this value for next iteration (incremental)
os << "(assert (not (= " << varName << " (_ bv" << numVal << " " << width << "))))\n";
}
os << "(reset)\n";
// Shuffle using Fisher-Yates
for (size_t i = values.size(); i > 1; --i) {
const size_t j = VL_RANDOM_RNG_I(rngr) % i;
std::swap(values[i - 1], values[j]);
}
m_randcValueQueues[varName] = std::deque<uint64_t>(values.begin(), values.end());
}
bool VlRandomizer::next(VlRNG& rngr) {
@@ -466,67 +459,30 @@ bool VlRandomizer::next(VlRNG& rngr) {
}
}
// Randc queue-based cycling: enumerate valid values once, then pop per call
// Randc exclusion-based cycling: exclude previously used values per randc var.
// When solver returns unsat (all values exhausted), clear history for new cycle.
if (!m_randcVarNames.empty()) {
const size_t currentHash = hashConstraints();
// Invalidate queues if constraints changed (e.g., constraint_mode toggled)
// Invalidate history if constraints changed (e.g., constraint_mode toggled)
if (currentHash != m_randcConstraintHash) {
m_randcValueQueues.clear();
m_randcUsedValues.clear();
m_randcConstraintHash = currentHash;
}
// Refill empty queues (start of new cycle)
for (const auto& name : m_randcVarNames) {
auto& queue = m_randcValueQueues[name];
if (queue.empty()) enumerateRandcValues(name, rngr);
}
}
// Pop randc values from queues (will be pinned in solver)
std::map<std::string, uint64_t> randcPinned;
for (const auto& name : m_randcVarNames) {
auto& queue = m_randcValueQueues[name];
if (queue.empty()) return false; // No valid values exist
randcPinned[name] = queue.front();
queue.pop_front();
}
// If solve-before constraints are present, use phased solving
if (!m_solveBefore.empty()) return nextPhased(rngr);
std::iostream& os = getSolver();
if (!os) return false;
// Randc retry: if unsat due to randc exhaustion, clear history and retry once
const bool hasRandc = !m_randcVarNames.empty();
for (int attempt = 0; attempt < (hasRandc ? 2 : 1); ++attempt) {
std::iostream& os = getSolver();
if (!os) return false;
os << "(set-option :produce-models true)\n";
os << "(set-logic QF_ABV)\n";
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
for (const auto& var : m_vars) {
if (var.second->dimension() > 0) {
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
var.second->setArrayInfo(arrVarsp);
}
os << "(declare-fun " << var.first << " () ";
var.second->emitType(os);
os << ")\n";
}
for (const std::string& constraint : m_constraints) {
os << "(assert (= #b1 " << constraint << "))\n";
}
// Pin randc values from pre-enumerated queues
for (const auto& pair : randcPinned) {
const int w = m_vars.at(pair.first)->width();
os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n";
}
os << "(check-sat)\n";
bool sat = parseSolution(os, true);
if (!sat) {
// If unsat, use named assertions to get unsat-core
os << "(reset)\n";
os << "(set-option :produce-unsat-cores true)\n";
// Soft constraint relaxation (IEEE 1800-2023 18.5.13, last-wins priority):
// Try hard + soft[0..N-1], then hard + soft[1..N-1], ..., then hard only.
// First SAT phase wins. If hard-only is UNSAT, report via unsat-core.
os << "(set-option :produce-models true)\n";
os << "(set-logic QF_ABV)\n";
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
@@ -539,28 +495,102 @@ bool VlRandomizer::next(VlRNG& rngr) {
var.second->emitType(os);
os << ")\n";
}
int j = 0;
for (const std::string& constraint : m_constraints) {
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j << "))\n";
j++;
}
os << "(check-sat)\n";
sat = parseSolution(os, true);
(void)sat;
os << "(reset)\n";
return false;
}
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
os << ")\n";
os << "\n(check-sat)\n";
sat = parseSolution(os, false);
(void)sat;
}
os << "(reset)\n";
return true;
for (const std::string& constraint : m_constraints) {
os << "(assert (= #b1 " << constraint << "))\n";
}
// Randc: exclude previously used values to enforce cyclic non-repetition
emitRandcExclusions(os);
const size_t nSoft = m_softConstraints.size();
bool sat = false;
if (nSoft > 0) {
// Fast path: try all soft constraints at once
os << "(push 1)\n";
for (const auto& s : m_softConstraints) os << "(assert (= #b1 " << s << "))\n";
os << "(check-sat)\n";
sat = parseSolution(os, false);
if (!sat) {
// Some soft constraints conflict. Incrementally add from back
// (highest priority first), keeping only compatible ones.
// This preserves the maximum set of compatible soft constraints.
os << "(pop 1)\n";
for (int i = static_cast<int>(nSoft) - 1; i >= 0; --i) {
os << "(push 1)\n";
os << "(assert (= #b1 " << m_softConstraints[i] << "))\n";
os << "(check-sat)\n";
if (checkSat(os)) {
// Compatible -- keep this push level
} else {
// Incompatible -- remove this soft constraint
os << "(pop 1)\n";
}
}
// Read solution with remaining compatible soft constraints
os << "(check-sat)\n";
sat = parseSolution(os, false);
}
} else {
// No soft constraints -- hard-only
os << "(check-sat)\n";
sat = parseSolution(os, false);
}
if (!sat) {
os << "(reset)\n";
// If randc vars have used values, this may be cycle exhaustion - retry
if (hasRandc && !m_randcUsedValues.empty() && attempt == 0) {
m_randcUsedValues.clear();
continue; // Retry without exclusions
}
// Genuine unsat: report via unsat-core
os << "(set-option :produce-unsat-cores true)\n";
os << "(set-logic QF_ABV)\n";
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
for (const auto& var : m_vars) {
if (var.second->dimension() > 0) {
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
var.second->setArrayInfo(arrVarsp);
}
os << "(declare-fun " << var.first << " () ";
var.second->emitType(os);
os << ")\n";
}
int j = 0;
for (const std::string& constraint : m_constraints) {
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j++ << "))\n";
}
os << "(check-sat)\n";
sat = parseSolution(os, true);
(void)sat;
os << "(reset)\n";
return false;
}
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
os << ")\n";
os << "\n(check-sat)\n";
sat = parseSolution(os, false);
(void)sat;
}
// Record solved randc values for future exclusion
recordRandcValues();
os << "(reset)\n";
return true;
}
return false; // Should not reach here
}
bool VlRandomizer::checkSat(std::iostream& os) {
std::string result;
do { std::getline(os, result); } while (result.empty());
return result == "sat";
}
bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
@@ -573,7 +603,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
std::getline(os, sat);
std::vector<int> numbers;
std::string currentNum;
for (char c : sat) {
for (const char c : sat) {
if (std::isdigit(c)) {
currentNum += c;
numbers.push_back(std::stoi(currentNum));
@@ -581,14 +611,14 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
}
}
if (Verilated::threadContextp()->warnUnsatConstr()) {
for (int n : numbers) {
for (const int n : numbers) {
if (n < m_constraints_line.size()) {
const std::string& constraint_info = m_constraints_line[n];
// Parse "filename:linenum source" format
size_t colon_pos = constraint_info.find(':');
const size_t colon_pos = constraint_info.find(':');
if (colon_pos != std::string::npos) {
std::string filename = constraint_info.substr(0, colon_pos);
size_t space_pos = constraint_info.find(" ", colon_pos);
const std::string filename = constraint_info.substr(0, colon_pos);
const size_t space_pos = constraint_info.find(" ", colon_pos);
std::string linenum_str;
std::string source;
if (space_pos != std::string::npos) {
@@ -602,7 +632,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
std::string msg = "UNSATCONSTR: Unsatisfied constraint";
if (!source.empty()) {
// Trim leading whitespace and add quotes
size_t start = source.find_first_not_of(" \t");
const size_t start = source.find_first_not_of(" \t");
if (start != std::string::npos) {
msg += ": '" + source.substr(start) + "'";
}
@@ -650,7 +680,9 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
"Internal: Unable to parse solver's response: invalid S-expression");
return false;
}
std::string name, idx, value;
std::string name;
std::string idx;
std::string value;
std::vector<std::string> indices;
os >> name;
indices.clear();
@@ -665,6 +697,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
if (m_randmodep && !varr.randModeIdxNone()) {
if (!m_randmodep->at(varr.randModeIdx())) continue;
}
if (m_disabledVars.count(name)) continue;
if (!indices.empty()) {
std::ostringstream oss;
oss << varr.name();
@@ -723,25 +756,40 @@ void VlRandomizer::hard(std::string&& constraint, const char* filename, uint32_t
}
}
void VlRandomizer::soft(std::string&& constraint, const char* /*filename*/, uint32_t /*linenum*/,
const char* /*source*/) {
m_softConstraints.emplace_back(std::move(constraint));
}
void VlRandomizer::disable_soft(const std::string& varName) {
// IEEE 1800-2023 18.5.13: Remove all soft constraints referencing the variable
m_softConstraints.erase(
std::remove_if(m_softConstraints.begin(), m_softConstraints.end(),
[&](const std::string& c) { return c.find(varName) != std::string::npos; }),
m_softConstraints.end());
}
void VlRandomizer::clearConstraints() {
m_constraints.clear();
m_constraints_line.clear();
m_solveBefore.clear();
m_softConstraints.clear();
// Keep m_vars for class member randomization
}
void VlRandomizer::clearAll() {
m_constraints.clear();
m_softConstraints.clear();
m_vars.clear();
m_randcVarNames.clear();
m_randcValueQueues.clear();
m_randcUsedValues.clear();
m_randcConstraintHash = 0;
}
void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
void VlRandomizer::solveBefore(const char* beforeName, const char* afterName) {
m_solveBefore.emplace_back(std::string(beforeName), std::string(afterName));
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
m_solveBefore.emplace_back(beforeName, afterName);
}
bool VlRandomizer::nextPhased(VlRNG& rngr) {
@@ -845,6 +893,9 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
os << "(assert (= #b1 " << constraint << "))\n";
}
// Randc: exclude previously used values
emitRandcExclusions(os);
// Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
os << "(check-sat)\n";
@@ -852,9 +903,12 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
// Final phase: use parseSolution to write ALL values to memory
bool sat = parseSolution(os, true);
if (!sat) {
if (!m_randcVarNames.empty()) m_randcUsedValues.clear();
os << "(reset)\n";
return false;
}
// Record solved randc values for future exclusion
recordRandcValues();
// Diversity loop (same as normal next())
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
os << "(assert ";
+78 -37
View File
@@ -28,10 +28,10 @@
#include "verilated.h"
#include <deque>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <set>
#include <sstream>
//=============================================================================
@@ -75,7 +75,7 @@ public:
std::string name() const { return m_name; }
int width() const { return m_width; }
int dimension() const { return m_dimension; }
virtual void* datap(int idx) const { return m_datap; }
virtual void* datap(int /*idx*/) const { return m_datap; }
std::uint32_t randModeIdx() const { return m_randModeIdx; }
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
bool set(const std::string& idx, const std::string& val) const;
@@ -88,6 +88,7 @@ public:
m_arrVarsRefp = arrVarsRefp;
}
mutable std::map<std::string, int> count_cache;
void clearCountCache() const { count_cache.clear(); }
int countMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
if (VL_LIKELY(count_cache.find(base_name) != count_cache.end()))
return count_cache[base_name];
@@ -109,12 +110,9 @@ public:
void* datap(int idx) const override {
const std::string indexed_name = name() + std::to_string(idx);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
return it->second->m_datap;
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
return nullptr; // LCOV_EXCL_BR_LINE
}
if (VL_LIKELY(it != m_arrVarsRefp->end())) return it->second->m_datap;
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
return nullptr; // LCOV_EXCL_BR_LINE
}
void emitHexs(std::ostream& s, const std::vector<IData>& indices, const size_t bit_width,
size_t idx) const {
@@ -172,7 +170,14 @@ public:
for (int i = 0; i < dimension(); ++i) s << ")";
}
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
if (dimension() > 0) {
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
s << "(_ BitVec " << width() << ")";
for (int i = 0; i < dimension(); ++i) s << ")";
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"indexed_name not found in m_arr_vars");
}
}
}
int totalWidth() const override {
@@ -200,27 +205,31 @@ public:
// Object holding constraints and variable references.
class VlRandomizer VL_NOT_FINAL {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
std::vector<std::string> m_constraints; // Solver-dependent hard constraints
std::vector<std::string>
m_constraints_line; // fileline content of the constraint for unsat constraints
std::vector<std::string> m_softConstraints; // Soft constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// variables
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
// variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
std::vector<std::string> m_unique_arrays;
std::map<std::string, uint32_t> m_unique_array_sizes;
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
int m_index = 0; // Internal counter for key generation
std::set<std::string> m_randcVarNames; // Names of randc variables for cyclic tracking
std::map<std::string, std::deque<uint64_t>>
m_randcValueQueues; // Remaining values per randc var (queue-based cycling)
size_t m_randcConstraintHash = 0; // Hash of constraints when queues were built
std::map<std::string, std::set<uint64_t>>
m_randcUsedValues; // Previously used values per randc var (exclusion-based cycling)
size_t m_randcConstraintHash = 0; // Hash of constraints when history was valid
std::vector<std::pair<std::string, std::string>>
m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar)
// PRIVATE METHODS
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
bool parseSolution(std::iostream& file, bool log = false);
void enumerateRandcValues(const std::string& varName, VlRNG& rngr);
bool parseSolution(std::iostream& os, bool log = false);
bool checkSat(std::iostream& os);
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
void recordRandcValues(); // Record solved randc values for future exclusion
size_t hashConstraints() const;
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
@@ -328,6 +337,12 @@ public:
"supported currently.");
}
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars
// (so constraints still reference it) but skips write-back after solving.
void set_var_disabled(const char* name) { m_disabledVars.insert(name); }
// Clear disabled state for a variable
void clear_var_disabled(const char* name) { m_disabledVars.erase(name); }
// --- write_var to register variables ---
// Register scalar variable (non-struct, basic type)
template <typename T>
@@ -354,12 +369,15 @@ public:
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
name, width, &var, dimension, randmodeIdx);
if (m_vars.find(name) == m_vars.end()) {
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
name, width, &var, dimension, randmodeIdx);
}
if (dimension > 0) {
m_index = 0;
clear_arr_table(name);
record_arr_table(var, name, dimension, {}, {});
m_vars[name]->clearCountCache();
}
}
@@ -376,21 +394,23 @@ public:
write_var(VlUnpacked<T, N_Depth>& var, uint32_t width, const std::string& name,
uint32_t dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
name, width, &var, dimension, randmodeIdx);
if (m_vars.find(name) == m_vars.end()) {
m_vars[name]
= std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
name, width, &var, dimension, randmodeIdx);
}
if (dimension > 0) {
m_index = 0;
clear_arr_table(name);
record_arr_table(var, name, dimension, {}, {});
m_vars[name]->clearCountCache();
}
}
// Register unpacked array of structs
template <typename T, std::size_t N_Depth>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
}
@@ -400,20 +420,23 @@ public:
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name]
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, width, &var, dimension, randmodeIdx);
if (m_vars.find(name) == m_vars.end()) {
m_vars[name]
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, width, &var, dimension, randmodeIdx);
}
if (dimension > 0) {
m_index = 0;
clear_arr_table(name);
record_arr_table(var, name, dimension, {}, {});
m_vars[name]->clearCountCache();
}
}
// Register associative array of structs
template <typename T_Key, typename T_Value>
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
}
@@ -423,8 +446,8 @@ public:
// Record a flat (non-class) element into the array variable table
template <typename T>
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
record_arr_table(T& var, const std::string& name, int dimension, std::vector<IData> indices,
std::vector<size_t> idxWidths) {
record_arr_table(T& var, const std::string& name, int /*dimension*/,
std::vector<IData> indices, std::vector<size_t> idxWidths) {
const std::string key = generateKey(name, m_index);
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, m_index, indices, idxWidths);
++m_index;
@@ -499,8 +522,8 @@ public:
// Register a single structArray element via write_var
template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
record_struct_arr(T& var, const std::string& name, int dimension, std::vector<IData> indices,
std::vector<size_t> idxWidths) {
record_struct_arr(T& var, const std::string& name, int /*dimension*/,
std::vector<IData> indices, std::vector<size_t> idxWidths) {
std::ostringstream oss;
for (size_t i = 0; i < indices.size(); ++i) {
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
@@ -580,7 +603,7 @@ public:
}
// Helper: Generate unique variable key from name and index
std::string generateKey(const std::string& name, int idx) {
static std::string generateKey(const std::string& name, int idx) {
if (!name.empty() && name[0] == '\\') {
const size_t space_pos = name.find(' ');
return (space_pos != std::string::npos ? name.substr(0, space_pos) : name)
@@ -591,13 +614,31 @@ public:
+ std::to_string(idx);
}
// Helper: Clear existing array element entries for a base name
void clear_arr_table(const std::string& name) {
for (int index = 0;; ++index) {
const std::string key = generateKey(name, index);
const auto it = m_arr_vars.find(key);
if (it == m_arr_vars.end()) break;
m_arr_vars.erase(it);
}
}
void hard(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
const char* source = "");
void soft(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
const char* source = "");
void pin_var(const char* name, int width, uint64_t value) {
std::string constraint = "(__Vbv (= "s + name + " (_ bv" + std::to_string(value) + " "
+ std::to_string(width) + ")))";
hard(std::move(constraint));
}
void disable_soft(const std::string& varName);
void clearConstraints();
void clearAll(); // Clear both constraints and variables
void markRandc(const char* name); // Mark variable as randc for cyclic tracking
void solveBefore(const char* beforeName,
const char* afterName); // Register solve-before ordering
void solveBefore(const std::string& beforeName,
const std::string& afterName); // Register solve-before ordering
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
#ifdef VL_DEBUG
void dump() const;
+68 -53
View File
@@ -88,14 +88,15 @@ public:
class VerilatedSaifActivityVar final {
// MEMBERS
uint64_t m_lastTime = 0; // Last time when variable value was updated
uint64_t m_lastTime; // Last time when variable value was updated
VerilatedSaifActivityBit* m_bits; // Pointer to variable bits objects
uint32_t m_width; // Width of variable (in bits)
public:
// CONSTRUCTORS
VerilatedSaifActivityVar(uint32_t width, VerilatedSaifActivityBit* bits)
: m_bits{bits}
VerilatedSaifActivityVar(uint64_t startTime, uint32_t width, VerilatedSaifActivityBit* bits)
: m_lastTime{startTime}
, m_bits{bits}
, m_width{width} {}
VerilatedSaifActivityVar(VerilatedSaifActivityVar&&) = default;
@@ -139,9 +140,9 @@ class VerilatedSaifActivityScope final {
// Name of the activity scope
std::string m_scopeName;
// Array indices of child scopes
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_childScopes{};
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_childScopes;
// Children signals codes mapped to their names in the current scope
std::vector<std::pair<uint32_t, std::string>> m_childActivities{};
std::vector<std::pair<uint32_t, std::string>> m_childActivities;
// Parent scope pointer
VerilatedSaifActivityScope* m_parentScope = nullptr;
@@ -203,7 +204,7 @@ class VerilatedSaifActivityAccumulator final {
public:
// METHODS
void declare(uint32_t code, const std::string& absoluteScopePath, std::string variableName,
int bits, bool array, int arraynum);
int bits, bool array, int arraynum, uint64_t startTime);
// CONSTRUCTORS
VerilatedSaifActivityAccumulator() = default;
@@ -252,7 +253,7 @@ VerilatedSaifActivityBit& VerilatedSaifActivityVar::bit(const std::size_t index)
void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string& absoluteScopePath,
std::string variableName, int bits, bool array,
int arraynum) {
int arraynum, uint64_t startTime) {
const size_t block_size = 1024;
if (m_activityArena.empty()
|| m_activityArena.back().size() + bits > m_activityArena.back().capacity()) {
@@ -268,7 +269,7 @@ void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string&
variableName += ']';
}
m_scopeToActivities[absoluteScopePath].emplace_back(code, variableName);
m_activity.emplace(code, VerilatedSaifActivityVar{static_cast<uint32_t>(bits),
m_activity.emplace(code, VerilatedSaifActivityVar{startTime, static_cast<uint32_t>(bits),
m_activityArena.back().data() + bitsIdx});
}
@@ -277,18 +278,18 @@ void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string&
//=============================================================================
// VerilatedSaif implementation
VerilatedSaif::VerilatedSaif(void* filep) {
m_activityAccumulators.emplace_back(std::make_unique<VerilatedSaifActivityAccumulator>());
}
VerilatedSaif::VerilatedSaif(void* /*filep*/) {}
void VerilatedSaif::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (isOpen()) return;
m_startTime = currentTime();
m_filename = filename; // "" is ok, as someone may overload open
m_filep = ::open(m_filename.c_str(),
O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
m_isOpen = true;
m_activityAccumulators.emplace_back(std::make_unique<VerilatedSaifActivityAccumulator>());
initializeSaifFileContents();
@@ -328,7 +329,7 @@ void VerilatedSaif::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedSaif::finalizeSaifFileContents() {
printStr("(DURATION ");
printStr(std::to_string(currentTime()));
printStr(std::to_string(currentTime() - m_startTime));
printStr(")\n");
incrementIndent();
@@ -377,8 +378,7 @@ bool VerilatedSaif::printScopeActivitiesFromAccumulatorIfPresent(
for (const auto& childSignal : accumulator.m_scopeToActivities.at(absoluteScopePath)) {
VerilatedSaifActivityVar& activityVariable = accumulator.m_activity.at(childSignal.first);
anyNetWritten
= printActivityStats(activityVariable, childSignal.second.c_str(), anyNetWritten);
anyNetWritten = printActivityStats(activityVariable, childSignal.second, anyNetWritten);
}
return anyNetWritten;
@@ -419,7 +419,7 @@ bool VerilatedSaif::printActivityStats(VerilatedSaifActivityVar& activity,
// We only have two-value logic so TZ, TX and TB will always be 0
printStr(" (T0 ");
printStr(std::to_string(currentTime() - bit.highTime()));
printStr(std::to_string(currentTime() - m_startTime - bit.highTime()));
printStr(") (T1 ");
printStr(std::to_string(bit.highTime()));
printStr(") (TZ 0) (TX 0) (TB 0) (TC ");
@@ -451,7 +451,8 @@ void VerilatedSaif::printStr(const std::string& str) {
void VerilatedSaif::writeBuffered(bool force) {
if (VL_UNLIKELY(m_buffer.size() >= WRITE_BUFFER_SIZE || force)) {
if (VL_UNLIKELY(!m_buffer.empty())) {
::write(m_filep, m_buffer.data(), m_buffer.size());
const ssize_t n = ::write(m_filep, m_buffer.data(), m_buffer.size());
assert(n == static_cast<ssize_t>(m_buffer.size()));
m_buffer = "";
m_buffer.reserve(WRITE_BUFFER_SIZE * 2);
}
@@ -486,7 +487,8 @@ void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type)
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
} else if (name.empty()) {
}
if (name.empty()) {
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
}
@@ -511,9 +513,9 @@ void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type)
}
const std::string newPrefix = prevPrefix + name;
bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED
&& type != VerilatedTracePrefixType::ARRAY_PACKED
&& type != VerilatedTracePrefixType::ROOTIO_WRAPPER);
const bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED
&& type != VerilatedTracePrefixType::ARRAY_PACKED
&& type != VerilatedTracePrefixType::ROOTIO_WRAPPER);
m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
}
@@ -529,8 +531,8 @@ void VerilatedSaif::popPrefix() {
}
void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name,
const char* wirep, const bool array, const int arraynum,
const bool bussed, const int msb, const int lsb) {
const char* /*wirep*/, const bool array, const int arraynum,
const bool /*bussed*/, const int msb, const int lsb) {
assert(m_activityAccumulators.size() > fidx);
VerilatedSaifActivityAccumulator& accumulator = *m_activityAccumulators.at(fidx);
@@ -544,51 +546,64 @@ void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name
m_currentScope->addActivityVar(code, variableName);
accumulator.declare(code, m_currentScope->path(), std::move(variableName), bits, array,
arraynum);
arraynum, m_startTime);
}
void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum) {
declare(code, fidx, name, "event", array, arraynum, false, 0, 0);
// versions to call when the sig is not array member
void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name) {
declare(code, fidx, name, "event", false, -1, false, 0, 0);
}
void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum) {
declare(code, fidx, name, "wire", array, arraynum, false, 0, 0);
void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name) {
declare(code, fidx, name, "wire", false, -1, false, 0, 0);
}
void VerilatedSaif::declBus(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
const int msb, const int lsb) {
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedSaif::declQuad(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
const int msb, const int lsb) {
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedSaif::declArray(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
void VerilatedSaif::declWide(const uint32_t code, const uint32_t fidx, const char* name,
const int msb, const int lsb) {
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name,
const int dtypenum, const VerilatedTraceSigDirection,
const VerilatedTraceSigKind, const VerilatedTraceSigType,
const bool array, const int arraynum) {
declare(code, fidx, name, "real", array, arraynum, false, 63, 0);
void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name) {
declare(code, fidx, name, "real", false, -1, false, 63, 0);
}
// versions to call when the sig is array member
void VerilatedSaif::declEventArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum) {
declare(code, fidx, name, "event", true, arraynum, false, 0, 0);
}
void VerilatedSaif::declBitArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum) {
declare(code, fidx, name, "wire", true, arraynum, false, 0, 0);
}
void VerilatedSaif::declBusArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedSaif::declQuadArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedSaif::declWideArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum, const int msb, const int lsb) {
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedSaif::declDoubleArray(const uint32_t code, const uint32_t fidx, const char* name,
const int arraynum) {
declare(code, fidx, name, "real", true, arraynum, false, 63, 0);
}
//=============================================================================
// Get/commit trace buffer
VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t fidx) { return new Buffer{*this}; }
VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t /*fidx*/) {
return new Buffer{*this};
}
void VerilatedSaif::commitTraceBuffer(VerilatedSaif::Buffer* bufp) { delete bufp; }
+54 -20
View File
@@ -51,6 +51,7 @@ private:
int m_filep = 0; // File we're writing to
bool m_isOpen = false; // True indicates open file
uint64_t m_startTime = 0; // Time file was opened
std::string m_filename; // Filename we're writing to (if open)
std::string m_buffer; // Write data buffer
@@ -60,9 +61,9 @@ private:
// Currently active scope
VerilatedSaifActivityScope* m_currentScope = nullptr;
// Array of declared scopes
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_scopes{};
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_scopes;
// Activity accumulators used to store variables statistics over simulation time
std::vector<std::unique_ptr<VerilatedSaifActivityAccumulator>> m_activityAccumulators{};
std::vector<std::unique_ptr<VerilatedSaifActivityAccumulator>> m_activityAccumulators;
// Total time of the currently traced simulation
uint64_t m_time = 0;
@@ -146,26 +147,59 @@ public:
void pushPrefix(const char*, VerilatedTracePrefixType);
void popPrefix();
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
// versions to call when the sig is not array member
void declEvent(uint32_t code, uint32_t fidx, const char* name);
void declBit(uint32_t code, uint32_t fidx, const char* name);
void declBus(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
void declQuad(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
void declWide(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
void declDouble(uint32_t code, uint32_t fidx, const char* name);
// versions to call when the sig is array member
void declEventArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
void declBitArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
void declBusArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
int lsb);
void declQuadArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
int lsb);
void declWideArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
int lsb);
void declDoubleArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
};
// We use macros to drop unused arguments at compile time. This saves code size.
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) tracep->pushPrefix(name, type);
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declEvent(code, fidx, name)
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declBit(code, fidx, name)
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declBus(code, fidx, name, msb, lsb)
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declQuad(code, fidx, name, msb, lsb)
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declWide(code, fidx, name, msb, lsb)
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declDouble(code, fidx, name)
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declEventArray(code, fidx, name, arraynum)
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declBitArray(code, fidx, name, arraynum)
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declBusArray(code, fidx, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declQuadArray(code, fidx, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declWideArray(code, fidx, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declDoubleArray(code, fidx, name, arraynum)
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedSaifC just below
template <>
+2 -2
View File
@@ -254,13 +254,13 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
// Serialization of types
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) {
os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size());
os.write(rhsp->serialized1Ptr(), VerilatedContext::serialized1Size());
os << rhsp->impp()->timeFormatSuffix();
os << rhsp->dumpfile();
return os;
}
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) {
os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size());
os.read(rhsp->serialized1Ptr(), VerilatedContext::serialized1Size());
std::string s;
os >> s;
rhsp->impp()->timeFormatSuffix(s);
+2 -13
View File
@@ -181,8 +181,8 @@ package std;
static task killQueue(ref process processQueue[$]);
`ifdef VERILATOR_TIMING
while (processQueue.size() > 0) begin
processQueue.pop_back().kill();
repeat (processQueue.size()) begin
processQueue.pop_front().kill();
end
`endif
endtask
@@ -215,17 +215,6 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
`endif
// verilog_format: on
// When really implemented, srandom must operate on the process, but for
// now rely on the srandom() that is automatically generated for all
// classes.
//
// function void srandom(int seed);
// endfunction
// The methods below access the common RNG, full support
// of get_randstate/set_randstate requires accessing the RNG state
// of the specified process (see IEEE 1800-2023, 18.14.), but as for
// now processes do not have their own RNGs.
function string get_randstate();
// Initialize with $c to ensure it won't be constified
string s = string'($c("0"));
+30 -6
View File
@@ -28,6 +28,8 @@
#include "verilatedos.h"
#include "verilated.h"
#include <vector>
//===========================================================================
@@ -168,12 +170,9 @@ public:
const std::vector<VerilatedRange>& packedRanges() const VL_MT_SAFE { return m_packed; }
const std::vector<VerilatedRange>& unpackedRanges() const VL_MT_SAFE { return m_unpacked; }
const VerilatedRange* range(int dim) const VL_MT_SAFE {
if (dim < udims())
return &m_unpacked[dim];
else if (dim < dims())
return &m_packed[dim - udims()];
else
return nullptr;
if (dim < udims()) return &m_unpacked[dim];
if (dim < dims()) return &m_packed[dim - udims()];
return nullptr;
}
// DPI accessors (with packed dimensions flattened!)
int left(int dim) const VL_MT_SAFE {
@@ -252,10 +251,14 @@ public:
// Verilator variable
// Thread safety: Assume is constructed only with model, then any number of readers
struct VerilatedForceControlSignals;
class VerilatedVar final : public VerilatedVarProps {
// MEMBERS
void* const m_datap; // Location of data
const char* const m_namep; // Name - slowpath
std::unique_ptr<const VerilatedForceControlSignals>
m_forceControlSignals; // Force control signals
protected:
const bool m_isParam;
friend class VerilatedScope;
@@ -266,13 +269,34 @@ protected:
, m_datap{datap}
, m_namep{namep}
, m_isParam{isParam} {}
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals)
: VerilatedVarProps{vltype, vlflags, udims, pdims}
, m_datap{datap}
, m_namep{namep}
, m_forceControlSignals{std::move(forceControlSignals)}
, m_isParam{isParam} {}
public:
~VerilatedVar() = default;
VerilatedVar(VerilatedVar&&) = default;
// ACCESSORS
void* datap() const { return m_datap; }
const char* name() const { return m_namep; }
bool isParam() const { return m_isParam; }
const VerilatedForceControlSignals* forceControlSignals() const {
return m_forceControlSignals.get();
}
};
//===========================================================================
// Force control signals of a VerilatedVar
struct VerilatedForceControlSignals final {
const VerilatedVar* forceEnableSignalp{nullptr}; // __VforceEn signal
const VerilatedVar* forceValueSignalp{nullptr}; // __VforceVal signal
const VerilatedVar forceReadSignal; // __VforceRd signal
};
#endif // Guard
+5 -6
View File
@@ -148,12 +148,11 @@ VlThreadPool::~VlThreadPool() {
std::string VlThreadPool::numaAssign(VerilatedContext* contextp) {
#if defined(__linux) || defined(CPU_ZERO) || defined(VL_CPPCHECK) // Linux-like pthreads
if (contextp && !contextp->useNumaAssign()) { return "NUMA assignment not requested"; }
std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default");
if (numa_strategy == "none") {
return "no NUMA assignment requested";
} else if (numa_strategy != "default" && numa_strategy != "") {
const std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default");
if (numa_strategy == "none") return "no NUMA assignment requested";
if (numa_strategy != "default" && numa_strategy != "")
return "%Warning: unknown VERILATOR_NUMA_STRATEGY value '" + numa_strategy + "'";
}
// Get number of processor available to the current process
const unsigned num_proc = VlOs::getProcessAvailableParallelism();
if (!num_proc) return "Can't determine number of available threads";
@@ -184,7 +183,7 @@ std::string VlThreadPool::numaAssign(VerilatedContext* contextp) {
while (!is.eof()) {
std::string line;
std::getline(is, line);
std::string::size_type pos = line.find(":");
const std::string::size_type pos = line.find(':');
int number = -1;
if (pos != std::string::npos) number = atoi(line.c_str() + pos + 1);
if (line.compare(0, std::strlen("processor"), "processor") == 0) {
+7 -8
View File
@@ -101,12 +101,11 @@ public:
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
const uint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
const uint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
bool areUpstreamDepsDone(bool evenCycle) const {
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
@@ -158,7 +157,7 @@ class VlWorkerThread final {
#ifdef VL_USE_PTHREADS
pthread_t m_pthread{};
#else
std::thread m_cthread{};
std::thread m_cthread;
#endif
// METHDOS
@@ -182,7 +181,7 @@ public:
VL_CPU_RELAX();
}
}
VerilatedLockGuard lock{m_mutex};
const VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(m_mutex);
@@ -239,7 +238,7 @@ public:
}
void freeWorkerIndexes(std::vector<size_t>& indexes) {
const VerilatedLockGuard lock{m_mutex};
for (size_t index : indexes) m_unassignedWorkers.push(index);
for (const size_t index : indexes) m_unassignedWorkers.push(index);
indexes.clear();
}
unsigned assignTaskIndex() { return m_assignedTasks++; }
+95 -3
View File
@@ -36,9 +36,12 @@ void VlCoroutineHandle::resume() {
m_coro.destroy();
} else {
m_process->state(VlProcess::RUNNING);
VlProcess::currentp(m_process.get());
m_coro();
VlProcess::currentp(nullptr);
}
} else {
VlProcess::currentp(nullptr);
m_coro();
}
m_coro = nullptr;
@@ -58,6 +61,12 @@ void VlDelayScheduler::resume() {
#ifdef VL_DEBUG
VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n"););
#endif
if (VL_UNLIKELY(m_context.gotFinish())) {
m_queue.clear();
m_zeroDelayed.clear();
m_zeroDelayesSwap.clear();
return;
}
bool resumed = false;
while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
@@ -80,6 +89,11 @@ void VlDelayScheduler::resume() {
}
void VlDelayScheduler::resumeZeroDelay() {
if (VL_UNLIKELY(m_context.gotFinish())) {
m_zeroDelayed.clear();
m_zeroDelayesSwap.clear();
return;
}
m_zeroDelayesSwap.swap(m_zeroDelayed);
for (VlCoroutineHandle& handle : m_zeroDelayesSwap) handle.resume();
m_zeroDelayesSwap.clear();
@@ -120,6 +134,12 @@ void VlTriggerScheduler::resume(const char* eventDescription) {
VL_DEBUG_IF(dump(eventDescription);
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
#endif
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_toResume.clear();
m_fired.clear();
m_awaiting.clear();
return;
}
for (VlCoroutineHandle& coro : m_toResume) coro.resume();
m_toResume.clear();
}
@@ -136,6 +156,11 @@ void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) {
});
}
#endif
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_toResume.clear();
m_fired.clear();
return;
}
std::swap(m_fired, m_toResume);
}
@@ -151,6 +176,11 @@ void VlTriggerScheduler::ready(const char* eventDescription) {
});
}
#endif
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_fired.clear();
m_awaiting.clear();
return;
}
const size_t expectedSize = m_fired.size() + m_awaiting.size();
if (m_fired.capacity() < expectedSize) m_fired.reserve(expectedSize * 2);
m_fired.insert(m_fired.end(), std::make_move_iterator(m_awaiting.begin()),
@@ -190,6 +220,14 @@ void VlTriggerScheduler::dump(const char* eventDescription) const {
// VlDynamicTriggerScheduler:: Methods
bool VlDynamicTriggerScheduler::evaluate() {
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_anyTriggered = false;
m_suspended.clear();
m_evaluated.clear();
m_triggered.clear();
m_post.clear();
return false;
}
m_anyTriggered = false;
VL_DEBUG_IF(dump(););
std::swap(m_suspended, m_evaluated);
@@ -206,6 +244,10 @@ void VlDynamicTriggerScheduler::doPostUpdates() {
VL_DBG_MSGF(" - ");
susp.dump();
});
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_post.clear();
return;
}
for (auto& coro : m_post) coro.resume();
m_post.clear();
}
@@ -217,6 +259,10 @@ void VlDynamicTriggerScheduler::resume() {
VL_DBG_MSGF(" - ");
susp.dump();
});
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
m_triggered.clear();
return;
}
for (auto& coro : m_triggered) coro.resume();
m_triggered.clear();
}
@@ -238,10 +284,56 @@ void VlDynamicTriggerScheduler::dump() const {
//======================================================================
// VlForkSync:: Methods
void VlForkSync::done(const char* filename, int lineno) {
void VlProcess::forkSyncOnKill(VlForkSyncState* forkSyncp) {
m_forkSyncOnKillp = forkSyncp;
m_forkSyncOnKillDone = false;
}
void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
if (m_forkSyncOnKillp != forkSyncp) return;
m_forkSyncOnKillp = nullptr;
m_forkSyncOnKillDone = false;
}
void VlProcess::state(int s) {
if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp
&& !m_forkSyncOnKillDone) {
m_forkSyncOnKillDone = true;
m_state = s;
m_forkSyncOnKillp->done();
return;
}
m_state = s;
}
VlForkSyncState::~VlForkSyncState() {
for (const VlProcessRef& processp : m_onKillProcessps) processp->forkSyncOnKillClear(this);
}
void VlForkSync::onKill(VlProcessRef process) {
if (!process) return;
m_state->m_onKillProcessps.emplace_back(process);
process->forkSyncOnKill(m_state.get());
}
void VlForkSyncState::done(const char* filename, int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno););
if (m_join->m_counter > 0) m_join->m_counter--;
if (m_join->m_counter == 0) m_join->m_susp.resume();
if (!m_inited) {
++m_pendingDones;
return;
}
if (m_counter > 0) m_counter--;
if (m_counter != 0) return;
if (m_inDone) {
m_resumePending = true;
return;
}
m_inDone = true;
do {
m_resumePending = false;
m_susp.resume();
} while (m_resumePending && m_inited && m_counter == 0);
m_inDone = false;
}
//======================================================================
+39 -19
View File
@@ -33,7 +33,7 @@
// clang-format off
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
#if defined _LIBCPP_VERSION // libc++
#ifdef _LIBCPP_VERSION // libc++
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
@@ -190,10 +190,11 @@ public:
bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
// Are there coroutines to resume at the current simulation time?
bool awaitingCurrentTime() const {
return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()));
return !m_context.gotFinish()
&& (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()));
}
// Are there coroutines to resume in the inactive region after a #0 delay?
bool awaitingZeroDelay() const { return !m_zeroDelayed.empty(); }
bool awaitingZeroDelay() const { return !m_context.gotFinish() && !m_zeroDelayed.empty(); }
#ifdef VL_DEBUG
void dump() const;
#endif
@@ -383,40 +384,59 @@ struct VlForever final {
//=============================================================================
// VlForkSync is used to manage fork..join and fork..join_any constructs.
class VlForkSync final {
// VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
// If the counter reaches 0, the suspended coroutine shall be resumed.
struct VlJoin final {
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
VlCoroutineHandle m_susp; // Coroutine to resume
};
// Shared fork..join state, because VlForkSync is copied into generated coroutine frames.
class VlForkSyncState final {
public:
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
VlCoroutineHandle m_susp; // Coroutine to resume
bool m_inited = false;
size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch)
bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills
bool m_resumePending = false; // Join reached zero again while inside done()
std::vector<VlProcessRef> m_onKillProcessps; // Branches registered for kill hooks
// The join info is shared among all forked processes
std::shared_ptr<VlJoin> m_join;
VlForkSyncState() // Construct with a null coroutine handle
: m_susp{VlProcessRef{}} {}
~VlForkSyncState();
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
};
class VlForkSync final {
std::shared_ptr<VlForkSyncState> m_state{std::make_shared<VlForkSyncState>()};
public:
// Create the join object and set the counter to the specified number
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
void init(size_t count, VlProcessRef process) {
const size_t pendingDones = m_state->m_pendingDones;
m_state->m_pendingDones = 0;
count = (pendingDones >= count) ? 0 : (count - pendingDones);
m_state->m_counter = count;
m_state->m_susp = {process};
m_state->m_inited = true;
}
// Register process kill callback so killed fork branches still decrement join counter
void onKill(VlProcessRef process);
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
// main process gets resumed
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
void done(const char* filename = VL_UNKNOWN, int lineno = 0) {
m_state->done(filename, lineno);
}
// Used by coroutines for co_awaiting a join
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
assert(m_join);
VL_DEBUG_IF(
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
struct Awaitable final {
VlProcessRef process; // Data of the suspended process, null if not needed
const std::shared_ptr<VlJoin> join; // Join to await on
const std::shared_ptr<VlForkSyncState> state; // Join to await on
VlFileLineDebug fileline;
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
bool await_ready() { return state->m_counter == 0; } // Suspend if join still exists
void await_suspend(std::coroutine_handle<> coro) {
join->m_susp = {coro, process, fileline};
state->m_susp = {coro, process, fileline};
}
void await_resume() const {}
};
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
return Awaitable{process, m_state, VlFileLineDebug{filename, lineno}};
}
};
+157 -54
View File
@@ -78,11 +78,14 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//=========================================================================
// Declare net data types
#ifndef VL_NO_LEGACY
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
#define VL_SIGW(name, msb, lsb, words) VlWide<words> name ///< Declare signal, 65+ bits
#endif
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
@@ -107,14 +110,47 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
}
//===================================================================
// Random
// Random Number Generator with internal state
class VlRNG final {
std::array<uint64_t, 2> m_state;
public:
// The default constructor simply sets state, to avoid vl_rand64()
// having to check for construction at each call
// Alternative: seed with zero and check on rand64() call
VlRNG() VL_MT_SAFE;
explicit VlRNG(uint64_t seed) VL_PURE;
void srandom(uint64_t n) VL_MT_UNSAFE;
std::string get_randstate() const VL_MT_UNSAFE;
void set_randstate(const std::string& state) VL_MT_UNSAFE;
uint64_t rand64() VL_MT_UNSAFE;
// Threadsafe, but requires use on vl_thread_rng or vl_current_rng
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
static uint64_t vl_current_rng_rand64() VL_MT_SAFE;
static VlRNG& vl_thread_rng() VL_MT_SAFE;
};
//===================================================================
// Metadata of processes
using VlProcessRef = std::shared_ptr<VlProcess>;
class VlForkSync;
class VlForkSyncState;
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
VlProcessRef m_parentp = nullptr; // Parent process, if exists
std::set<VlProcess*> m_children; // Active child processes
VlForkSyncState* m_forkSyncOnKillp
= nullptr; // Optional fork..join counter to decrement on kill
bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once
VlRNG m_rng; // Per-process RNG (IEEE 1800-2023 18.14)
// Thread-local current process pointer for hierarchical object seeding
static thread_local VlProcess* t_currentp;
public:
// TYPES
@@ -145,14 +181,18 @@ public:
void detach(VlProcess* childp) { m_children.erase(childp); }
int state() const { return m_state; }
void state(int s) { m_state = s; }
void state(int s);
void disable() {
state(KILLED);
disableFork();
}
void disableFork() {
for (VlProcess* childp : m_children) childp->disable();
// childp->disable() may resume coroutines and mutate m_children
const std::set<VlProcess*> children = m_children;
for (VlProcess* childp : children) childp->disable();
}
void forkSyncOnKill(VlForkSyncState* forkSyncp);
void forkSyncOnKillClear(VlForkSyncState* forkSyncp);
bool completed() const { return state() == FINISHED || state() == KILLED; }
bool completedFork() const {
for (const VlProcess* const childp : m_children)
@@ -160,11 +200,22 @@ public:
return true;
}
// Random state (IEEE 1800-2023 9.7, 18.14)
void srandom(uint64_t seed) VL_MT_UNSAFE { m_rng.srandom(seed); }
std::string randstate() const VL_MT_UNSAFE;
void randstate(const std::string& state) VL_MT_UNSAFE;
// Current process tracking for hierarchical object seeding
static VlProcess* currentp() VL_MT_UNSAFE { return t_currentp; }
static void currentp(VlProcess* processp) VL_MT_UNSAFE { t_currentp = processp; }
// Return process RNG if in a process, else thread RNG
static VlRNG& currentRng() VL_MT_SAFE;
};
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
inline std::string VL_TO_STRING(const VlProcessRef&) { return std::string("process"); }
// Use process RNG if in a process, else thread RNG (IEEE 1800-2023 18.14)
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_current_rng_rand64(); }
//===================================================================
// SystemVerilog event type
@@ -232,30 +283,6 @@ inline std::string VL_TO_STRING(const VlEventBase& e) {
return "triggered="s + (e.isTriggered() ? "true" : "false");
}
//===================================================================
// Random
// Random Number Generator with internal state
class VlRNG final {
std::array<uint64_t, 2> m_state;
public:
// The default constructor simply sets state, to avoid vl_rand64()
// having to check for construction at each call
// Alternative: seed with zero and check on rand64() call
VlRNG() VL_MT_SAFE;
explicit VlRNG(uint64_t seed) VL_PURE;
void srandom(uint64_t n) VL_MT_UNSAFE;
std::string get_randstate() const VL_MT_UNSAFE;
void set_randstate(const std::string& state) VL_MT_UNSAFE;
uint64_t rand64() VL_MT_UNSAFE;
// Threadsafe, but requires use on vl_thread_rng
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
static VlRNG& vl_thread_rng() VL_MT_SAFE;
};
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
// RNG for shuffle()
class VlURNG final {
public:
@@ -428,6 +455,9 @@ std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj.data());
}
template <typename T_Class>
class VlClassRef;
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
@@ -438,6 +468,9 @@ std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
template <typename T_Value, size_t N_MaxSize = 0>
class VlQueue final {
private:
template <typename U_Value, size_t M_MaxSize>
friend class VlQueue;
// TYPES
using Deque = std::deque<T_Value>;
@@ -460,6 +493,13 @@ public:
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Template constuctors that construct from containers holding sub-classes
template <typename T_Subclass>
inline VlQueue(const VlQueue<VlClassRef<T_Subclass>>&);
template <typename T_Subclass>
inline VlQueue(VlQueue<VlClassRef<T_Subclass>>&&);
bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
bool operator<(const VlQueue& rhs) const {
@@ -589,11 +629,8 @@ public:
// Accessing. Verilog: v = assoc[index]
const T_Value& at(int32_t index) const {
// Needs to work for dynamic arrays, so does not use N_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return atDefault();
return m_deque[index];
}
// Access with an index counted from end (e.g. q[$])
T_Value& atWriteAppendBack(int32_t index) { return atWriteAppend(m_deque.size() - 1 - index); }
@@ -625,6 +662,24 @@ public:
VlQueue sliceBackBack(int32_t lsb, int32_t msb) const {
return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb);
}
// Assign src elements to q[lsb:msb]
void sliceAssign(int32_t lsb, int32_t msb, const VlQueue& src) {
const int32_t sz = static_cast<int32_t>(m_deque.size());
const int32_t srcSz = static_cast<int32_t>(src.m_deque.size());
if (VL_UNLIKELY(sz <= 0 || srcSz <= 0)) return;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= sz)) lsb = sz - 1;
if (VL_UNLIKELY(msb >= sz)) msb = sz - 1;
const int32_t count = std::min(msb - lsb + 1, srcSz);
if (VL_UNLIKELY(count <= 0)) return;
std::copy_n(src.m_deque.begin(), count, m_deque.begin() + lsb);
}
void sliceAssignFrontBack(int32_t lsb, int32_t msb, const VlQueue& src) {
sliceAssign(lsb, m_deque.size() - 1 - msb, src);
}
void sliceAssignBackBack(int32_t lsb, int32_t msb, const VlQueue& src) {
sliceAssign(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb, src);
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
@@ -764,6 +819,17 @@ public:
}
return VlQueue<IData>{};
}
// Map method (IEEE 1800-2023 7.12.5)
template <typename T_Func>
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
VlQueue<WithFuncReturnType<T_Func>> out;
IData index = 0;
for (const auto& i : m_deque) {
out.push_back(with_func(index, i));
++index;
}
return out;
}
// Reduction operators
VlQueue min() const {
@@ -976,11 +1042,8 @@ public:
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
if (it == m_map.end()) return m_defaultValue;
return it->second;
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
@@ -1100,6 +1163,13 @@ public:
if (it == m_map.rend()) return VlQueue<T_Key>{};
return VlQueue<T_Key>::consV(it->first);
}
// Map method (IEEE 1800-2023 7.12.5)
template <typename T_Func>
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
VlQueue<WithFuncReturnType<T_Func>> out;
for (const auto& i : m_map) out.push_back(with_func(i.first, i.second));
return out;
}
// Reduction operators
VlQueue<T_Value> min() const {
@@ -1284,7 +1354,6 @@ public:
// Default copy assignment operators are used.
// METHODS
public:
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
@@ -1303,11 +1372,8 @@ public:
template <std::size_t N_CurrentDimension = 0, typename U = T_Value>
int find_length(int dimension, std::true_type) const {
if (dimension == N_CurrentDimension) {
return size();
} else {
return m_storage[0].template find_length<N_CurrentDimension + 1>(dimension);
}
if (dimension == N_CurrentDimension) return size();
return m_storage[0].template find_length<N_CurrentDimension + 1>(dimension);
}
template <std::size_t N_CurrentDimension = 0>
@@ -1492,6 +1558,13 @@ public:
}
return VlQueue<T_Key>{};
}
// Map method (IEEE 1800-2023 7.12.5)
template <typename T_Func>
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
VlQueue<WithFuncReturnType<T_Func>> out;
for (IData i = 0; i < N_Depth; ++i) out.push_back(with_func(i, m_storage[i]));
return out;
}
// Reduction operators
VlQueue<T_Value> min() const {
@@ -1883,7 +1956,7 @@ class VlClass VL_NOT_FINAL : public VlDeletable {
public:
// CONSTRUCTORS
VlClass() {}
VlClass(const VlClass& copied) {}
VlClass(const VlClass& /*copied*/) {}
~VlClass() override = default;
// Polymorphic shallow clone. Overridden in each generated concrete class.
virtual VlClass* clone() const { return nullptr; }
@@ -1900,7 +1973,10 @@ public:
struct VlNull final {
operator bool() const { return false; }
bool operator==(const void* ptr) const { return !ptr; }
template <class T>
operator T*() const {
return nullptr;
}
};
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
@@ -1937,11 +2013,26 @@ public:
VlClassRef(VlNull){};
template <typename... T_Args>
VlClassRef(VlDeleter& deleter, T_Args&&... args)
// () required here to avoid narrowing conversion warnings,
// when a new() has an e.g. CData type and passed a 1U.
: m_objp{new T_Class(std::forward<T_Args>(args)...)} {
// refCountInc was moved to the constructor of T_Class
// to fix self references in constructor.
: m_objp{new T_Class} {
// Instantly init the object to presevrve RAII
m_objp->init(std::forward<T_Args>(args)...);
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, T_Class&& args)
// Move constructor
: m_objp{new T_Class{std::forward<T_Class>(args)}} {
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, const T_Class& args)
// Copy constructor
: m_objp{new T_Class{args}} {
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, T_Class& args)
// Copy constructor - this is required since if `T_Class&`
// will be provided a compiler will match it to the constructor
// with variadic template instead of `T_Class&&`
: m_objp{new T_Class{args}} {
m_objp->m_deleterp = &deleter;
}
// Explicit to avoid implicit conversion from 0
@@ -2010,7 +2101,7 @@ public:
VlClassRef<T_OtherClass> dynamicCast() const {
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
}
// Polymorphic shallow clone (IEEE 1800-2017 8.7: new <handle> preserves runtime type)
// Polymorphic shallow clone (IEEE 1800-2023 8.7: new <handle> preserves runtime type)
VlClassRef clone(VlDeleter& deleter) const {
VlClass* clonedp = m_objp->clone();
if (VL_UNLIKELY(!clonedp)) return {};
@@ -2049,13 +2140,12 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
return false;
}
template <typename T_Lhs>
static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef<T_Lhs>& outr) {
static inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
outr = VlNull{};
return true;
}
@@ -2136,4 +2226,17 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
//======================================================================
template <typename T_Value, size_t N_MaxSize>
template <typename T_Subclass>
VlQueue<T_Value, N_MaxSize>::VlQueue(const VlQueue<VlClassRef<T_Subclass>>& that)
: m_deque{that.m_deque.begin(), that.m_deque.end()}
, m_defaultValue{that.m_defaultValue} {}
template <typename T_Value, size_t N_MaxSize>
template <typename T_Subclass>
VlQueue<T_Value, N_MaxSize>::VlQueue(VlQueue<VlClassRef<T_Subclass>>&& that)
: m_deque{std::make_move_iterator(that.m_deque.begin()),
std::make_move_iterator(that.m_deque.end())}
, m_defaultValue{std::move(that.m_defaultValue)} {}
#endif // Guard
+39 -29
View File
@@ -333,7 +333,8 @@ void VerilatedVcd::pushPrefix(const char* namep, VerilatedTracePrefixType type)
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
} else if (name.empty()) {
}
if (name.empty()) {
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
return;
}
@@ -404,7 +405,7 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
char* vcdCodeWritep = vcdCode;
uint32_t codeEnc = code;
do {
*vcdCodeWritep++ = static_cast<char>('!' + codeEnc % 94);
*vcdCodeWritep++ = static_cast<char>('!' + (codeEnc % 94));
codeEnc /= 94;
} while (codeEnc--);
*vcdCodeWritep = '\0';
@@ -451,41 +452,50 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
printStr(decl.c_str());
}
void VerilatedVcd::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum) {
declare(code, name, "event", array, arraynum, false, 0, 0);
// versions to call when the sig is not array member
void VerilatedVcd::declEvent(uint32_t code, const char* name) {
declare(code, name, "event", false, -1, false, 0, 0);
}
void VerilatedVcd::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum) {
declare(code, name, "wire", array, arraynum, false, 0, 0);
void VerilatedVcd::declBit(uint32_t code, const char* name) {
declare(code, name, "wire", false, -1, false, 0, 0);
}
void VerilatedVcd::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
declare(code, name, "wire", array, arraynum, true, msb, lsb);
void VerilatedVcd::declBus(uint32_t code, const char* name, int msb, int lsb) {
declare(code, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedVcd::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
declare(code, name, "wire", array, arraynum, true, msb, lsb);
void VerilatedVcd::declQuad(uint32_t code, const char* name, int msb, int lsb) {
declare(code, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedVcd::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
declare(code, name, "wire", array, arraynum, true, msb, lsb);
void VerilatedVcd::declWide(uint32_t code, const char* name, int msb, int lsb) {
declare(code, name, "wire", false, -1, true, msb, lsb);
}
void VerilatedVcd::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind,
VerilatedTraceSigType, bool array, int arraynum) {
declare(code, name, "real", array, arraynum, false, 63, 0);
void VerilatedVcd::declDouble(uint32_t code, const char* name) {
declare(code, name, "real", false, -1, false, 63, 0);
}
// versions to call when the sig is array member
void VerilatedVcd::declEventArray(uint32_t code, const char* name, int arraynum) {
declare(code, name, "event", true, arraynum, false, 0, 0);
}
void VerilatedVcd::declBitArray(uint32_t code, const char* name, int arraynum) {
declare(code, name, "wire", true, arraynum, false, 0, 0);
}
void VerilatedVcd::declBusArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
declare(code, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedVcd::declQuadArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
declare(code, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedVcd::declWideArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
declare(code, name, "wire", true, arraynum, true, msb, lsb);
}
void VerilatedVcd::declDoubleArray(uint32_t code, const char* name, int arraynum) {
declare(code, name, "real", true, arraynum, false, 63, 0);
}
//=============================================================================
// Get/commit trace buffer
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t /*fidx*/) {
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
if (parallel()) {
// Note: This is called from VerilatedVcd::dump, which already holds the lock
@@ -494,7 +504,7 @@ VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
// cppcheck-suppress unreadVariable // cppcheck bug, used below
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = vlstd::roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
const size_t startingSize = vlstd::roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
}
@@ -679,7 +689,7 @@ VL_ATTR_ALWINLINE
void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
char* wp = m_writep;
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
(void)VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
wp += std::strlen(wp);
finishLine(code, wp);
}
+48 -18
View File
@@ -137,26 +137,56 @@ public:
void pushPrefix(const char*, VerilatedTracePrefixType);
void popPrefix();
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum, int msb, int lsb);
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
bool array, int arraynum);
// versions to call when the sig is not array member
void declEvent(uint32_t code, const char* name);
void declBit(uint32_t code, const char* name);
void declBus(uint32_t code, const char* name, int msb, int lsb);
void declQuad(uint32_t code, const char* name, int msb, int lsb);
void declWide(uint32_t code, const char* name, int msb, int lsb);
void declDouble(uint32_t code, const char* name);
// versions to call when the sig is array member
void declEventArray(uint32_t code, const char* name, int arraynum);
void declBitArray(uint32_t code, const char* name, int arraynum);
void declBusArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
void declQuadArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
void declWideArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
void declDoubleArray(uint32_t code, const char* name, int arraynum);
};
// We use macros to drop unused arguments at compile time. This saves code size.
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) tracep->pushPrefix(name, type);
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declEvent(code, name)
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declBit(code, name)
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declBus(code, name, msb, lsb)
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declQuad(code, name, msb, lsb)
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
tracep->declWide(code, name, msb, lsb)
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
tracep->declDouble(code, name)
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declEventArray(code, name, arraynum)
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declBitArray(code, name, arraynum)
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declBusArray(code, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declQuadArray(code, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
msb, lsb) \
tracep->declWideArray(code, name, arraynum, msb, lsb)
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
tracep->declDoubleArray(code, name, arraynum)
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <>
+706 -366
View File
File diff suppressed because it is too large Load Diff
+18 -11
View File
@@ -37,7 +37,7 @@
//=========================================================================
// Compiler pragma abstraction
#if defined(__clang__)
#ifdef __clang__
# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
#else
# define VL_CLANG_ATTR(attr)
@@ -127,7 +127,7 @@
VL_CLANG_ATTR(no_thread_safety_analysis)
// Require mutex locks only in code units which work with enabled multi-threading.
#if !defined(VL_MT_DISABLED_CODE_UNIT)
#ifndef VL_MT_DISABLED_CODE_UNIT
// Function requires not having a capability inbound (-fthread-safety)
# define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES")) \
@@ -318,7 +318,6 @@
# define VL_CONSTEXPR_CXX17
#endif
//=========================================================================
// Optimization
@@ -351,6 +350,8 @@ extern "C" void __gcov_dump();
# pragma warning(disable:4189) // C4189: local variable is initialized but not referenced (L4)
# pragma warning(disable:4244) // C4244: conversion from 'uint64_t' to 'uint_32_t', possible loss of data
# pragma warning(disable:4245) // C4245: conversion from 'int' to 'unsigned', signed/unsigned mismatch
# pragma warning(disable:4267) // C4267: conversion from 'size_t' to 'int', possible loss of data
# pragma warning(disable:4316) // C4316: 'Vtop': object allocated on the heap may not be aligned 64
# pragma warning(disable:4996) // C4996: sscanf/fopen/etc may be unsafe
# endif
#endif
@@ -383,7 +384,7 @@ using vlsint32_t = int32_t; ///< 32-bit signed type (backward compatibility)
using vlsint64_t = int64_t; ///< 64-bit signed type (backward compatibility)
#endif
#if defined(__CYGWIN__)
#ifdef __CYGWIN__
# include <sys/types.h> // __WORDSIZE
# include <unistd.h> // ssize_t
@@ -434,6 +435,17 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
# define VL_VSNPRINTF vsnprintf
#endif
// Constants for VL_SFORMATF; see V3Number.h VFormatAttr
// Character codes are upper case so harder to confuse with format %codes.
// (...) indicates what is passed as arguments in emitted code
#define VL_VFORMATATTR_UNSIGNED '#' // (int widthMin, IData/WData/etc) Use standard format
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/WData/etc) Signed number; for %d showing sign
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
//=========================================================================
// File system functions
@@ -473,12 +485,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Declare a class as uncopyable; put after a private:
#define VL_UNCOPYABLE(Type) \
Type(const Type& other) = delete; \
Type(const Type& other) = delete; \
Type& operator=(const Type&) = delete
// Declare a class as unmovable; put after a private:
#define VL_UNMOVABLE(Type) \
Type(Type&& other) = delete; \
Type(Type&& other) = delete; \
Type& operator=(Type&&) = delete
//=========================================================================
@@ -486,11 +498,6 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_MULS_MAX_WORDS 128 ///< Max size in words of MULS operation
#ifndef VL_VALUE_STRING_MAX_WORDS
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#endif
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4
//=========================================================================
// Base macros
+4 -4
View File
@@ -35,13 +35,13 @@
# include <psapi.h> // GetProcessMemoryInfo
#endif
#if defined(__linux)
#ifdef __linux
# include <sched.h> // For sched_getcpu()
#endif
#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
# include <cpuid.h> // For __cpuid_count()
#endif
#if defined(__FreeBSD__)
#ifdef __FreeBSD__
# include <pthread_np.h> // For pthread_getaffinity_np()
#endif
@@ -93,7 +93,7 @@ double DeltaWallTime::gettime() VL_MT_SAFE {
// Vlos::getcpu implementation
uint16_t getcpu() VL_MT_SAFE {
#if defined(__linux)
#ifdef __linux
return sched_getcpu(); // TODO: this is a system call. Not exactly cheap.
#elif defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
uint32_t info[4];
@@ -194,7 +194,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
std::string ret;
#if defined(_MSC_VER)
#ifdef _MSC_VER
// Note: MinGW does not offer _dupenv_s
const char* envvalue = nullptr;
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
+6 -2
View File
@@ -58,6 +58,8 @@ def process() -> None:
author = am.group(1)
if re.search(r'antmicro', email):
author += ", Antmicro Ltd."
if re.search(r'stud\.uni-heidelberg', email):
author += ", Heidelberg University"
if re.search(r'tenstorrent', email):
author += ", Testorrent USA, Inc."
if re.search(r'github action', author):
@@ -87,8 +89,10 @@ def process() -> None:
line,
flags=re.IGNORECASE)
if m:
# print("K %s" % line)
msgs[key] += ' (#' + m.group(2) + ')'
mid = m.group(2)
if ("#" + mid) not in msgs[key]:
# print("K %s" % line)
msgs[key] += ' (#' + mid + ')'
if not msgs:
print("No Changes need to be inserted.")
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116,W0613
#
# This program is free software; you can redistribute it and/or modify the
# Verilator internals under the terms of either the GNU Lesser General
# Public License Version 3 or the Perl Artistic License Version 2.0.
#
# SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
######################################################################
import argparse
import os
import subprocess
import sys
try:
from termcolor import colored
except ModuleNotFoundError:
def colored(msg, **kwargs):
return msg
def cprint(msg="", *, color=None, attrs=None, **kwargs):
print(colored(msg, color=color, attrs=attrs), **kwargs)
parser = argparse.ArgumentParser(
description='Binary search utility for debugging Verilator with V3DebugBisect',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
example:
%(prog)s DfgPeephole 0 1000 test_regress/t/t_foo.py --no-skip-identical
''')
parser.add_argument("--pre", type=str, help='Command to run before each execution')
parser.add_argument('name', help='Name of V3DebugBisect instance')
parser.add_argument('low', type=int, help='Bisection range low value, use 0 by default')
parser.add_argument('high',
type=int,
help='Bisection range high value, use a sufficiently high number')
parser.add_argument('cmd',
nargs=argparse.REMAINDER,
help='Discriminator command that should exit non-zero on failure')
args = parser.parse_args()
var = f"VERILATOR_DEBUG_BISECT_{args.name}"
passing = args.low - 1
failing = args.high + 1
cprint()
cprint(f"Starting bisection serach for {var} in interval [{args.low}, {args.high}]",
attrs=["bold"])
while True:
cprint()
passStr = str(passing) if passing >= args.low else '?'
failStr = str(failing) if failing < args.high else '?'
cprint(f"Current step Pass: {passStr} Fail: {failStr}", attrs=["bold"])
# Stop if found, or exhausted interval without finding both a pass and a fail
if failing == args.low:
cprint(f"The low endpoint of the search interval ({args.low}) fails. Suggest rerun with:",
color="yellow")
cprint(f" {sys.argv[0]} {args.name} 0 {args.low} ...", color="yellow")
sys.exit(1)
if passing == args.high:
cprint(
f"The high endpoint of the search interval ({args.high}) passes. Suggest rerun with:",
color="yellow")
cprint(f" {sys.argv[0]} {args.name} {args.high} {10*args.high} ...", color="yellow")
sys.exit(1)
if failing == passing + 1:
cprint(f"First faling value: {var}={failing}", attrs=["bold"])
sys.exit(0)
# Compute middle of interval to evaluate
mid = (failing + passing) // 2
# Run pre command if given:
if args.pre:
cprint("Running --pre command", attrs=["bold"])
preResult = subprocess.run(args.pre, shell=True, check=False)
if preResult.returncode != 0:
cprint("Pre command failed", color="red")
sys.exit(2)
# Set up environment variable
env = os.environ.copy()
env[var] = str(mid)
# Run the discriminator command
cprint(f"Running with {var}={mid}", attrs=["bold"])
result = subprocess.run(args.cmd, env=env, check=False)
# Check status, update interval
if result.returncode != 0:
cprint(f"Run with {var}={mid}: Fail", color="red")
failing = mid
else:
cprint(f"Run with {var}={mid}: Pass", color="green")
passing = mid
+8 -3
View File
@@ -65,11 +65,13 @@ set(HEADERS
V3Clock.h
V3Combine.h
V3Common.h
V3Control.h
V3Const.h
V3Container.h
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Dead.h
V3DebugBisect.h
V3Delayed.h
V3Depth.h
V3DepthBlock.h
@@ -80,7 +82,6 @@ set(HEADERS
V3DfgDataType.h
V3DfgOptimizer.h
V3DfgPasses.h
V3DfgPatternStats.h
V3DfgPeepholePatterns.h
V3DfgVertices.h
V3DiagSarif.h
@@ -120,6 +121,7 @@ set(HEADERS
V3LanguageWords.h
V3Life.h
V3LifePost.h
V3LiftExpr.h
V3LinkCells.h
V3LinkDot.h
V3LinkDotIfaceCapture.h
@@ -129,6 +131,7 @@ set(HEADERS
V3LinkLevel.h
V3LinkParse.h
V3LinkResolve.h
V3LinkWith.h
V3List.h
V3Localize.h
V3MemberMap.h
@@ -241,12 +244,12 @@ set(COMMON_SOURCES
V3Dfg.cpp
V3DfgAstToDfg.cpp
V3DfgBreakCycles.cpp
V3DfgCache.cpp
V3DfgColorSCCs.cpp
V3DfgCse.cpp
V3DfgDataType.cpp
V3DfgDecomposition.cpp
V3DfgDfgToAst.cpp
V3DfgDumpPatterns.cpp
V3DfgOptimizer.cpp
V3DfgPasses.cpp
V3DfgPeephole.cpp
@@ -294,6 +297,7 @@ set(COMMON_SOURCES
V3LibMap.cpp
V3Life.cpp
V3LifePost.cpp
V3LiftExpr.cpp
V3LinkCells.cpp
V3LinkDot.cpp
V3LinkDotIfaceCapture.cpp
@@ -303,6 +307,7 @@ set(COMMON_SOURCES
V3LinkLevel.cpp
V3LinkParse.cpp
V3LinkResolve.cpp
V3LinkWith.cpp
V3Localize.cpp
V3MergeCond.cpp
V3Name.cpp
+5 -1
View File
@@ -102,6 +102,7 @@ LIBS = $(CFG_LIBS) -lm
CPPFLAGS += -DVERILATOR_INTERNAL_
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_ALLOC_RANDOM_CHECKS # To allow --debug-new-random
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += -MP # Only works on recent GCC versions
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
@@ -257,12 +258,12 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Dfg.o \
V3DfgAstToDfg.o \
V3DfgBreakCycles.o \
V3DfgCache.o \
V3DfgColorSCCs.o \
V3DfgCse.o \
V3DfgDataType.o \
V3DfgDecomposition.o \
V3DfgDfgToAst.o \
V3DfgDumpPatterns.o \
V3DfgOptimizer.o \
V3DfgPasses.o \
V3DfgPeephole.o \
@@ -290,6 +291,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3LibMap.o \
V3Life.o \
V3LifePost.o \
V3LiftExpr.o \
V3LinkCells.o \
V3LinkDot.o \
V3LinkDotIfaceCapture.o \
@@ -299,6 +301,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3LinkLevel.o \
V3LinkParse.o \
V3LinkResolve.o \
V3LinkWith.o \
V3Localize.o \
V3MergeCond.o \
V3Name.o \
@@ -360,6 +363,7 @@ NON_STANDALONE_HEADERS = \
V3AstNodeExpr.h \
V3AstNodeOther.h \
V3AstNodeStmt.h \
V3DebugBisect.h \
V3DfgVertices.h \
V3ThreadPool.h \
V3WidthRemove.h \
+64 -48
View File
@@ -127,6 +127,7 @@ class AssertVisitor final : public VNVisitor {
// Cleared on netlist
// AstNode::user1() -> bool. True if processed
// AstAlways::user2p() -> std::vector<AstVar*>. Delayed variables via 'm_delayed'
// AstNodeVarRef::user2() -> bool. True if shouldn't be sampled
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
AstUser2Allocator<AstAlways, std::vector<AstVar*>> m_delayed;
@@ -139,7 +140,7 @@ class AssertVisitor final : public VNVisitor {
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
unsigned m_modPastNum = 0; // Module past numbering
unsigned m_modStrobeNum = 0; // Module $strobe numbering
const AstNodeProcedure* m_procedurep = nullptr; // Current procedure
AstNodeProcedure* m_procedurep = nullptr; // Current procedure
VDouble0 m_statCover; // Statistic tracking
VDouble0 m_statAsNotImm; // Statistic tracking
VDouble0 m_statAsImm; // Statistic tracking
@@ -149,7 +150,6 @@ class AssertVisitor final : public VNVisitor {
bool m_inRestrict = false; // True inside restrict assertion
AstNode* m_passsp = nullptr; // Current pass statement
AstNode* m_failsp = nullptr; // Current fail statement
bool m_underAssert = false; // Visited from assert
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp;
@@ -198,11 +198,10 @@ class AssertVisitor final : public VNVisitor {
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
+ ((message != "") ? ": " : "") + message + "\n");
} else {
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
+ ((message != "") ? ": " : "") + message + "\n");
}
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
+ message + "\n");
}
static bool resolveAssertType(AstAssertCtl* nodep) {
if (!nodep->assertTypesp()) {
@@ -308,7 +307,7 @@ class AssertVisitor final : public VNVisitor {
} else {
bodyp = assertCond(nodep, VN_AS(propp, NodeExpr), passsp, failsp);
}
return newIfAssertOn(bodyp, nodep->directive(), nodep->type());
return newIfAssertOn(bodyp, nodep->directive(), nodep->userType());
}
AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
@@ -395,27 +394,43 @@ class AssertVisitor final : public VNVisitor {
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
{ AssertDeFutureVisitor{nodep->propp(), m_modp, m_modPastNum++}; }
iterateChildren(nodep);
iterateAndNextNull(nodep->sentreep());
if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
iterateAndNextNull(assertp->failsp());
} else if (AstAssertIntrinsic* const assertp = VN_CAST(nodep, AssertIntrinsic)) {
iterateAndNextNull(assertp->failsp());
} else if (AstCover* const coverp = VN_CAST(nodep, Cover)) {
iterateAndNextNull(coverp->coverincsp());
} else if (!VN_IS(nodep, Restrict)) {
nodep->v3fatalSrc("Unhandled assert type");
}
iterateAndNextNull(nodep->passsp());
AstSenTree* const sentreep = nodep->sentreep();
if (nodep->immediate()) {
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
} else {
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
if (m_procedurep) {
if (!nodep->senFromAlways()) {
// To support this need queue of asserts to activate
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Procedural concurrent assertion with"
" clocking event inside always (IEEE 1800-2023 16.14.6)");
}
// Change type to concurrent and relink after process
nodep->immediate(false);
static_cast<AstNode*>(m_procedurep)->addNext(nodep->unlinkFrBack());
return; // Later iterate will pick up
}
sentreep->unlinkFrBack();
}
//
const string& message = nodep->name();
AstNode* passsp = nodep->passsp();
if (passsp) passsp->unlinkFrBackWithNext();
if (failsp) failsp->unlinkFrBackWithNext();
if (nodep->immediate()) {
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
} else {
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
sentreep->unlinkFrBack();
if (m_procedurep) {
// To support this need queue of asserts to activate
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Procedural concurrent assertion with"
" clocking event inside always (IEEE 1800-2023 16.14.6)");
}
}
//
bool selfDestruct = false;
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
++m_statCover;
@@ -448,10 +463,8 @@ class AssertVisitor final : public VNVisitor {
VL_RESTORER(m_passsp);
VL_RESTORER(m_failsp);
VL_RESTORER(m_underAssert);
m_passsp = passsp;
m_failsp = failsp;
m_underAssert = true;
iterate(nodep->propp());
AstNode* propExprp;
@@ -704,9 +717,8 @@ class AssertVisitor final : public VNVisitor {
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstVarRef* nodep) override {
iterateChildren(nodep);
if (m_inSampled && !VString::startsWith(nodep->name(), "__VcycleDly")) {
void visit(AstNodeVarRef* nodep) override {
if (m_inSampled && !nodep->user2() && !nodep->varp()->isTemp()) {
if (!nodep->access().isReadOnly()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Write to variable in sampled expression");
@@ -727,27 +739,31 @@ class AssertVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstPExprClause* nodep) override {
if (m_underAssert) {
if (nodep->pass() && m_passsp) {
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
nodep->replaceWith(m_passsp->cloneTree(true));
} else if (!nodep->pass() && m_failsp) {
// Asserts with multiple statements are wrapped in implicit begin/end blocks so no
// need to clone next.
nodep->replaceWith(m_failsp->cloneTree(false));
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
AstNode* stmtsp = nullptr;
if (nodep->pass() && m_passsp) {
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
stmtsp = m_passsp->cloneTree(true);
} else if (!nodep->pass() && m_failsp) {
stmtsp = m_failsp->cloneTree(true);
}
if (stmtsp) {
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
// References inside action blocks shouldn't be implicitly sampled
// m_passsp/m_failsp have been already visited once and refs explicitly sampled
// are handled already
refp->user2(1);
});
nodep->replaceWith(stmtsp);
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstPExpr* nodep) override {
if (m_underAssert) {
VL_RESTORER(m_inSampled);
m_inSampled = false;
iterateChildren(nodep);
} else if (m_inRestrict) {
if (m_inRestrict) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} else {
iterateChildren(nodep);
}
}
@@ -766,10 +782,10 @@ class AssertVisitor final : public VNVisitor {
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
nodep->displayType(VDisplayType::DT_DISPLAY);
FileLine* const fl = nodep->fileline();
AstNode* monExprsp = nodep->fmtp()->exprsp();
AstSenItem* monSenItemsp = nullptr;
while (monExprsp) {
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
if (AstNode* const monExprsp = nodep->fmtp()->exprsp()) {
monExprsp->foreachAndNext([&](AstVarRef* varrefp) {
AstSenItem* const senItemp
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
// Clone so get VarRef or VarXRef as needed
@@ -779,9 +795,9 @@ class AssertVisitor final : public VNVisitor {
} else {
monSenItemsp->addNext(senItemp);
}
}
monExprsp = monExprsp->nextp();
});
}
AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
const auto monNum = ++m_monitorNum;
// Where $monitor was we do "__VmonitorNum = N;"
+634 -34
View File
@@ -15,7 +15,7 @@
//*************************************************************************
// Pre steps:
// Attach clocks to each assertion
// Substitute property references by property body (IEEE 1800-2012 16.12.1).
// Substitute property references by property body (IEEE 1800-2023 16.12.1).
// Transform clocking blocks into imperative logic
//*************************************************************************
@@ -27,6 +27,8 @@
#include "V3Task.h"
#include "V3UniqueNames.h"
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
@@ -39,6 +41,7 @@ class AssertPreVisitor final : public VNVisitor {
private:
// NODE STATE
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
// AstPExpr::user1() // bool. Created from AstUntil
const VNUser1InUse m_inuser1;
// STATE
// Current context:
@@ -47,6 +50,7 @@ private:
AstClocking* m_clockingp = nullptr; // Current clocking block
// Reset each module:
AstClocking* m_defaultClockingp = nullptr; // Default clocking for current module
AstVar* m_defaultClkEvtVarp = nullptr; // Event var for default clocking (for ##0)
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable for current module
// Reset each assertion:
AstSenItem* m_senip = nullptr; // Last sensitivity
@@ -55,40 +59,91 @@ private:
// Reset each assertion:
AstNodeExpr* m_disablep = nullptr; // Last disable
AstIf* m_disableSeqIfp = nullptr; // Used for handling disable iff in sequences
AstPExpr* m_pexprp = nullptr; // Last AstPExpr
// Other:
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
V3UniqueNames m_consRepNames{"__VconsRep"}; // Consecutive repetition counter name generator
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
bool m_inPExpr = false; // True if in AstPExpr
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
// METHODS
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr) {
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
AstNodeCoverOrAssert* cassertp = nullptr) {
// Create sentree based on clocked or default clock
// Return nullptr for always
if (useTreep) return useTreep;
AstSenTree* newp = nullptr;
AstSenItem* senip = m_senip;
bool fromAlways = false;
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
if (!senip) senip = m_seniAlwaysp;
if (!senip) {
senip = m_seniAlwaysp;
fromAlways = true;
}
if (!senip) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
newp = new AstSenTree{nodep->fileline(), nullptr};
} else {
if (cassertp && fromAlways) cassertp->senFromAlways(true);
newp = new AstSenTree{nodep->fileline(), senip->cloneTree(true)};
}
return newp;
}
AstNodeExpr* getSequenceBodyExprp(const AstSequence* const seqp) const {
// The statements in AstSequence are optional AstVar (ports) followed by body expr.
AstNode* bodyp = seqp->stmtsp();
while (bodyp && VN_IS(bodyp, Var)) bodyp = bodyp->nextp();
return VN_CAST(bodyp, NodeExpr);
}
AstPropSpec* getPropertyExprp(const AstProperty* const propp) {
// The only statements possible in AstProperty are AstPropSpec (body)
// and AstVar (arguments).
// Statements in AstProperty: AstVar (ports/local vars),
// AstInitialStaticStmt/AstInitialAutomaticStmt (var init), AstPropSpec (body).
AstNode* propExprp = propp->stmtsp();
while (VN_IS(propExprp, Var)) propExprp = propExprp->nextp();
while (propExprp
&& (VN_IS(propExprp, Var) || VN_IS(propExprp, InitialStaticStmt)
|| VN_IS(propExprp, InitialAutomaticStmt))) {
propExprp = propExprp->nextp();
}
return VN_CAST(propExprp, PropSpec);
}
void substituteSequenceCall(AstFuncRef* funcrefp, AstSequence* seqp) {
// IEEE 1800-2023 16.7 (sequence declarations), 16.8 (sequence instances)
// Inline the sequence body at the call site, replacing the FuncRef
AstNodeExpr* bodyExprp = getSequenceBodyExprp(seqp);
UASSERT_OBJ(bodyExprp, funcrefp, "Sequence has no body expression");
// Clone the body expression since the sequence may be referenced multiple times
AstNodeExpr* clonedp = bodyExprp->cloneTree(false);
// Build substitution map, then do a single traversal to replace all formals
// (textual substitution per IEEE 16.8.2).
const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, seqp->stmtsp());
std::unordered_map<const AstVar*, AstNodeExpr*> portMap;
for (const auto& tconnect : tconnects) {
portMap[tconnect.first] = tconnect.second->exprp();
}
clonedp->foreach([&](AstVarRef* refp) {
const auto it = portMap.find(refp->varp());
if (it != portMap.end()) {
refp->replaceWith(it->second->cloneTree(false));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
});
for (const auto& tconnect : tconnects) {
pushDeletep(tconnect.second->exprp()->unlinkFrBack());
}
// Replace the FuncRef with the inlined body
funcrefp->replaceWith(clonedp);
VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp);
// Clear referenced flag; sequences with isReferenced==false are deleted in assertPreAll
seqp->isReferenced(false);
}
AstPropSpec* substitutePropertyCall(AstPropSpec* nodep) {
if (AstFuncRef* const funcrefp = VN_CAST(nodep->propp(), FuncRef)) {
if (const AstProperty* const propp = VN_CAST(funcrefp->taskp(), Property)) {
@@ -99,20 +154,53 @@ private:
// Clone subtree after substitution. It is needed, because property might be called
// multiple times with different arguments.
propExprp = propExprp->cloneTree(false);
// Substitute formal arguments with actual arguments
// Build substitution maps for formal arguments and property-local
// variables, then perform a single foreach to apply all replacements.
// Map port vars to their actual argument expressions
const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, propp->stmtsp());
std::unordered_map<const AstVar*, AstNodeExpr*> portMap;
for (const auto& tconnect : tconnects) {
const AstVar* const portp = tconnect.first;
// cppcheck-suppress constVariablePointer // 'exprp' unlinked below
AstArg* const argp = tconnect.second;
propExprp->foreach([&](AstVarRef* refp) {
if (refp->varp() == portp) {
refp->replaceWith(argp->exprp()->cloneTree(false));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
});
pushDeletep(argp->exprp()->unlinkFrBack());
portMap[tconnect.first] = tconnect.second->exprp();
}
// Promote property-local variables (non-port vars, IEEE 16.10) to
// module-level __Vpropvar temps. Cross-cycle persistence is handled
// by the match item lowering in visit(AstImplication*).
std::unordered_map<const AstVar*, AstVar*> localVarMap;
for (AstNode* stmtp = propp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
if (!varp->isIO()) {
const string newName = m_propVarNames.get(varp);
AstVar* const newVarp = new AstVar{
varp->fileline(), VVarType::MODULETEMP, newName, varp->dtypep()};
newVarp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(newVarp);
localVarMap[varp] = newVarp;
}
}
}
// Single traversal: substitute ports and update local var references
propExprp->foreach([&](AstVarRef* refp) {
{
const auto portIt = portMap.find(refp->varp());
if (portIt != portMap.end()) {
refp->replaceWith(portIt->second->cloneTree(false));
VL_DO_DANGLING(pushDeletep(refp), refp);
return;
}
}
{
const auto localIt = localVarMap.find(refp->varp());
if (localIt != localVarMap.end()) { refp->varp(localIt->second); }
}
});
// Clean up argument expressions
for (const auto& tconnect : tconnects) {
pushDeletep(tconnect.second->exprp()->unlinkFrBack());
}
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
if (nodep->disablep() && propExprp->disablep()) {
nodep->v3error("disable iff expression before property call and in its "
@@ -337,14 +425,44 @@ private:
nodep->v3error(
"Delay value is not an elaboration-time constant (IEEE 1800-2023 16.7)");
} else if (constp->isZero()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(valuep->deleteTree(), valuep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
if (m_inSynchDrive) {
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (m_pexprp) {
// ##0 in sequence context = zero delay = same clock tick
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
// Procedural ##0: synchronize with default clocking event (IEEE 1800-2023 14.11)
// If the clocking event has not yet occurred this timestep, wait for it;
// otherwise continue without suspension.
if (!m_defaultClockingp) {
nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
AstVar* const evtVarp = m_defaultClkEvtVarp;
UASSERT_OBJ(evtVarp, nodep, "Default clocking event var not pre-created");
AstCMethodHard* const isTriggeredp = new AstCMethodHard{
flp, new AstVarRef{flp, evtVarp, VAccess::READ}, VCMethod::EVENT_IS_TRIGGERED};
isTriggeredp->dtypeSetBit();
AstEventControl* const waitp = new AstEventControl{
flp,
new AstSenTree{flp, new AstSenItem{flp, VEdgeType::ET_EVENT,
new AstVarRef{flp, evtVarp, VAccess::READ}}},
nullptr};
AstIf* const ifp = new AstIf{flp, new AstNot{flp, isTriggeredp}, waitp};
nodep->replaceWith(ifp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
AstSenItem* sensesp = nullptr;
if (!m_defaultClockingp) {
if (!m_inPExpr) {
if (!m_pexprp) {
nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
@@ -358,28 +476,75 @@ private:
AstEventControl* const controlp = new AstEventControl{
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
const std::string delayName = m_cycleDlyNames.get(nodep);
AstNodeExpr* throughoutp
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
// Throughout: create flag tracking whether condition held every tick
AstVar* throughoutOkp = nullptr;
if (throughoutp) {
throughoutOkp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__throughoutOk",
nodep->findBasicDType(VBasicDTypeKwd::LOGIC_IMPLICIT)};
throughoutOkp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
beginp->addStmtsp(throughoutOkp);
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}});
// Check condition at tick 0 (sequence start, before entering loop)
beginp->addStmtsp(
new AstIf{flp, new AstLogNot{flp, throughoutp->cloneTreePure(false)},
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}});
}
{
AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp(new AstLoopTest{
flp, loopp,
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}}});
// When throughout is present, exit loop early if condition fails
AstNodeExpr* loopCondp
= new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}};
if (throughoutOkp) {
loopCondp = new AstLogAnd{flp, loopCondp,
new AstVarRef{flp, throughoutOkp, VAccess::READ}};
}
loopp->addStmtsp(new AstLoopTest{flp, loopp, loopCondp});
loopp->addStmtsp(controlp);
loopp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, 1}}});
// Check throughout condition at each tick during delay (IEEE 1800-2023 16.9.9)
if (throughoutp) {
loopp->addStmtsp(
new AstIf{flp, new AstLogNot{flp, throughoutp},
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}});
}
beginp->addStmtsp(loopp);
}
if (m_disableSeqIfp) {
// Compose wrappers on remaining sequence: throughout gate (inner), disable iff (outer)
AstNode* remainp = nodep->nextp() ? nodep->nextp()->unlinkFrBackWithNext() : nullptr;
if (throughoutOkp) {
// If condition failed during delay, fail assertion
remainp = new AstIf{flp, new AstVarRef{flp, throughoutOkp, VAccess::READ}, remainp,
new AstPExprClause{flp, /*pass=*/false}};
}
if (m_disableSeqIfp && remainp) {
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
disableSeqIfp->addThensp(nodep->nextp()->unlinkFrBackWithNext());
nodep->addNextHere(disableSeqIfp);
// Keep continuation statements in a proper statement-list container.
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
remainp = disableSeqIfp;
}
if (remainp) {
if (throughoutOkp) {
// throughoutOkp is declared in beginp scope -- check must be inside it
beginp->addStmtsp(remainp);
} else {
nodep->addNextHere(remainp);
}
}
nodep->replaceWith(beginp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
@@ -494,7 +659,7 @@ private:
// Find Clocking's buried under nodep->exprsp
iterateChildren(nodep);
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep, nullptr, nodep));
}
void visit(AstFalling* nodep) override {
if (nodep->user1SetOnce()) return;
@@ -537,6 +702,54 @@ private:
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
void visit(AstSConsRep* nodep) override {
// IEEE 1800-2023 16.9.2 -- Lower standalone exact [*N] (N >= 2) via saturating counter.
// Range/unbounded forms and SExpr-contained forms are lowered by V3AssertProp.
iterateChildren(nodep);
if (nodep->unbounded() || nodep->maxCountp()) return; // Handled by V3AssertProp
const AstConst* const constp = VN_CAST(nodep->countp(), Const);
if (VL_UNLIKELY(!constp || constp->toSInt() < 1)) {
nodep->v3fatalSrc("Consecutive repetition count must be a positive constant"
" (should have been caught by V3Width)");
return;
}
const int n = constp->toSInt();
FileLine* const flp = nodep->fileline();
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
if (n == 1) {
nodep->replaceWith(exprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
// Saturating counter: if (expr) cnt <= min(cnt+1, N); else cnt <= 0;
AstVar* const cntVarp = new AstVar{flp, VVarType::MODULETEMP, m_consRepNames.get(""),
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(cntVarp);
AstNodeExpr* const exprClonep = exprp->cloneTreePure(false);
AstNodeExpr* const saturatingIncrp = new AstCond{
flp,
new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, static_cast<uint32_t>(n)}},
new AstAdd{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 1u}},
new AstConst{flp, static_cast<uint32_t>(n)}};
AstAssignDly* const incrAssignp
= new AstAssignDly{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, saturatingIncrp};
AstAssignDly* const resetAssignp = new AstAssignDly{
flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, new AstConst{flp, 0u}};
AstIf* const ifp = new AstIf{flp, exprClonep, incrAssignp, resetAssignp};
AstSenTree* const senTreep = newSenTree(nodep);
AstAlways* const alwaysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, senTreep, ifp};
cntVarp->addNextHere(alwaysp);
// Match: cnt >= N-1 (previous cycles via NBA) && expr (current cycle)
AstNodeExpr* const cntCheckp = new AstGte{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, static_cast<uint32_t>(n - 1)}};
cntCheckp->dtypeSetBit();
AstNodeExpr* const matchp = new AstAnd{flp, cntCheckp, exprp};
matchp->dtypeSetBit();
nodep->replaceWith(matchp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstRising* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
@@ -595,15 +808,323 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
// Validate repetition count: must be a non-negative elaboration-time constant.
// Shared by goto [->N] and nonconsecutive [=N] repetition.
// On error, replaces nodep with BitFalse placeholder and returns nullptr.
const AstConst* validateRepCount(AstNode* nodep, AstNodeExpr*& countp) {
countp = V3Const::constifyEdit(countp);
const AstConst* const constp = VN_CAST(countp, Const);
if (!constp) {
nodep->v3error("Repetition count is not an elaboration-time constant"
" (IEEE 1800-2023 16.9.2)");
VL_DO_DANGLING(pushDeletep(countp), countp);
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr;
}
if (constp->toSInt() < 0) {
nodep->v3error("Repetition count must be non-negative"
" (IEEE 1800-2023 16.9.2)");
VL_DO_DANGLING(pushDeletep(countp), countp);
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr;
}
if (constp->isZero()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: zero repetition count"
" (IEEE 1800-2023 16.9.2)");
VL_DO_DANGLING(pushDeletep(countp), countp);
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr;
}
return constp;
}
// Lower goto/nonconsecutive repetition to a counter-based PExpr loop.
// IEEE 1800-2023 16.9.2:
// Goto [->N]: count N occurrences, then check consequent
// Nonconsec [=N] = [->N] ##1 !b[*0:$]: count N, then scan trailing !b window
// Generated structure for goto [->N]:
// begin
// automatic uint32 cnt = 0;
// loop { test(cnt < N); if (sampled(expr)) cnt++; if (cnt < N) @(clk); }
// // consequent check or pass clause
// end
// Generated structure for nonconsec [=N] with implication:
// begin
// automatic uint32 cnt = 0;
// loop { test(cnt < N); if (sampled(expr)) cnt++; if (cnt < N) @(clk); }
// @(clk); // ##1
// if (!isOverlapped) @(clk); // |=> delay
// loop { if (sampled(expr)) fail; if (sampled(rhs)) pass; @(clk); }
// end
AstPExpr* createRepPExpr(FileLine* flp, AstNodeExpr* exprp, AstNodeExpr* countp,
AstNodeExpr* rhsp, bool isOverlapped, bool isNonConsec) {
AstSenItem* const sensesp = m_senip;
UASSERT_OBJ(sensesp, exprp, "Repetition requires a clock");
const std::string name
= isNonConsec ? m_nonConsRepNames.get(exprp) : m_gotoRepNames.get(exprp);
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, name + "__counter",
exprp->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const beginp = new AstBegin{flp, name + "__block", cntVarp, true};
beginp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, new AstConst{flp, 0}});
// Counting loop: find N occurrences of expr (shared by goto and nonconsec)
AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp(new AstLoopTest{flp, loopp,
new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
countp->cloneTreePure(false)}});
// if (expr) cnt++ -- sampled is applied to whole property expr by V3Assert
loopp->addStmtsp(
new AstIf{flp, exprp,
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
new AstAdd{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, 1}}}});
loopp->addStmtsp(new AstIf{
flp, new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, countp},
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}});
beginp->addStmtsp(loopp);
if (isNonConsec && rhsp) {
// IEEE 16.9.2: b[=N] = b[->N] ##1 !b[*0:$]
// Trailing window: ##1 advance, then scan !expr cycles checking rhs.
// Window closes when expr becomes true again (fail if rhs was never true).
beginp->addStmtsp(new AstEventControl{
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); // ##1
if (!isOverlapped) {
beginp->addStmtsp(new AstEventControl{
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); // |=>
}
// Window loop: check rhs at each !expr cycle (done variable for termination)
AstVar* const doneVarp
= new AstVar{flp, VVarType::BLOCKTEMP, name + "__done", exprp->findBitDType()};
doneVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
beginp->addStmtsp(doneVarp);
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, doneVarp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
auto setDone = [&]() {
return new AstAssign{flp, new AstVarRef{flp, doneVarp, VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}};
};
AstLoop* const windowp = new AstLoop{flp};
// LoopTest: continue while !done
windowp->addStmtsp(new AstLoopTest{
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
// if (expr) { fail; done = 1; } -- window closed, expr true again
AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true};
failBlockp->addStmtsp(new AstPExprClause{flp, false});
failBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
passBlockp->addStmtsp(new AstPExprClause{flp, true});
passBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
// @(clk) -- advance to next cycle in window
windowp->addStmtsp(
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
beginp->addStmtsp(windowp);
} else if (isNonConsec) {
// Standalone nonconsec: ##1 into window, then pass
beginp->addStmtsp(
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
beginp->addStmtsp(new AstPExprClause{flp, true});
} else if (rhsp) {
// Goto rep: check consequent once at match endpoint
if (!isOverlapped) {
beginp->addStmtsp(new AstEventControl{
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
}
beginp->addStmtsp(new AstIf{flp, rhsp, new AstPExprClause{flp, true},
new AstPExprClause{flp, false}});
} else {
// Standalone goto: pass after counting
beginp->addStmtsp(new AstPExprClause{flp, true});
}
return new AstPExpr{flp, beginp, exprp->findBitDType()};
}
void visit(AstSGotoRep* nodep) override {
// Standalone goto rep (not inside implication antecedent)
iterateChildren(nodep);
FileLine* const flp = nodep->fileline();
AstNodeExpr* countp = nodep->countp()->unlinkFrBack();
if (!validateRepCount(nodep, countp)) return;
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
AstPExpr* const pexprp = createRepPExpr(flp, exprp, countp, nullptr, true, false);
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstSNonConsRep* nodep) override {
// Standalone nonconsecutive rep (not inside implication antecedent)
iterateChildren(nodep);
FileLine* const flp = nodep->fileline();
AstNodeExpr* countp = nodep->countp()->unlinkFrBack();
if (!validateRepCount(nodep, countp)) return;
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
AstPExpr* const pexprp = createRepPExpr(flp, exprp, countp, nullptr, true, true);
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstFuncRef* nodep) override {
// IEEE 1800-2023 16.8: Inline sequence instances wherever they appear
// in the expression tree (inside implications, boolean ops, nested refs, etc.)
if (AstSequence* const seqp = VN_CAST(nodep->taskp(), Sequence)) {
substituteSequenceCall(nodep, seqp);
// The FuncRef has been replaced; do not access nodep after this point.
// The replacement node will be visited by the parent's iterateChildren.
return;
}
iterateChildren(nodep);
}
void visit(AstImplication* nodep) override {
if (nodep->sentreep()) return; // Already processed
// Handle goto repetition as antecedent before iterateChildren,
// so the standalone AstSGotoRep visitor doesn't process it
if (AstSGotoRep* const gotop = VN_CAST(nodep->lhsp(), SGotoRep)) {
iterateChildren(gotop);
iterateAndNextNull(nodep->rhsp());
FileLine* const flp = nodep->fileline();
AstNodeExpr* countp = gotop->countp()->unlinkFrBack();
if (!validateRepCount(nodep, countp)) return;
AstNodeExpr* const exprp = gotop->exprp()->unlinkFrBack();
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
AstPExpr* const pexprp
= createRepPExpr(flp, exprp, countp, rhsp, nodep->isOverlapped(), false);
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
// Handle nonconsecutive repetition as antecedent before iterateChildren,
// so the standalone AstSNonConsRep visitor doesn't process it
if (AstSNonConsRep* const ncrp = VN_CAST(nodep->lhsp(), SNonConsRep)) {
iterateChildren(ncrp);
iterateAndNextNull(nodep->rhsp());
FileLine* const flp = nodep->fileline();
AstNodeExpr* countp = ncrp->countp()->unlinkFrBack();
if (!validateRepCount(nodep, countp)) return;
AstNodeExpr* const exprp = ncrp->exprp()->unlinkFrBack();
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
AstPExpr* const pexprp
= createRepPExpr(flp, exprp, countp, rhsp, nodep->isOverlapped(), true);
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
iterateChildren(nodep);
FileLine* const flp = nodep->fileline();
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
if (nodep->isOverlapped()) {
// Lower sequence match items (IEEE 16.11): (expr, var = val, ...) |-> / |=>
if (AstExprStmt* const exprStmtp = VN_CAST(lhsp, ExprStmt)) {
AstNodeExpr* const antExprp = exprStmtp->resultp()->unlinkFrBack();
if (nodep->isOverlapped()) {
// |-> : assign to __Vpropvar via always_comb (continuous).
// The assign evaluates RHS once; V3Sampled snapshots the
// result so all consequent refs read the same value.
AstNode* matchAssignsp = nullptr;
for (AstNode* stmtp = exprStmtp->stmtsp(); stmtp;) {
AstNode* const nextp = stmtp->nextp();
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
assignp->unlinkFrBack();
if (!matchAssignsp) {
matchAssignsp = assignp;
} else {
matchAssignsp->addNext(assignp);
}
}
stmtp = nextp;
}
VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
lhsp = antExprp;
if (matchAssignsp) {
AstAlways* const alwaysp
= new AstAlways{flp, VAlwaysKwd::ALWAYS_COMB, nullptr, matchAssignsp};
m_modp->addStmtsp(alwaysp);
}
} else {
// |=> : assign to __Vpropvar via NBA in a clocked always block.
// The NBA commits before the next cycle's sampled snapshot,
// so the consequent (which already references __Vpropvar)
// sees the captured value.
AstNode* matchAssignsp = nullptr;
for (AstNode* stmtp = exprStmtp->stmtsp(); stmtp;) {
AstNode* const nextp = stmtp->nextp();
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
assignp->unlinkFrBack();
AstNodeExpr* const assignLhsp = assignp->lhsp()->unlinkFrBack();
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
VL_DO_DANGLING(pushDeletep(assignp), assignp);
if (!matchAssignsp) {
matchAssignsp = dlyp;
} else {
matchAssignsp->addNext(dlyp);
}
}
stmtp = nextp;
}
VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
lhsp = antExprp;
if (matchAssignsp) {
AstIf* const condp
= new AstIf{flp, antExprp->cloneTreePure(false), matchAssignsp};
AstAlways* const alwaysp
= new AstAlways{flp, VAlwaysKwd::ALWAYS, newSenTree(nodep), condp};
m_modp->addStmtsp(alwaysp);
}
}
}
if (AstPExpr* const pexprp = VN_CAST(rhsp, PExpr)) {
// Implication with sequence expression on RHS (IEEE 1800-2023 16.11, 16.12.7).
// The PExpr was already lowered from the property expression by V3AssertProp.
// Wrap the PExpr body with the antecedent check so the sequence only
// starts when the antecedent holds.
AstNodeExpr* condp;
if (nodep->isOverlapped()) {
// Overlapped implication (|->): check antecedent on same cycle.
// disable iff is applied at the assertion level, not at the
// antecedent gate, matching the existing non-PExpr overlapped path.
condp = lhsp;
} else {
// Non-overlapped implication (|=>): check antecedent from previous cycle
if (m_disablep) {
lhsp
= new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
}
AstPast* const pastp = new AstPast{flp, lhsp};
pastp->dtypeFrom(lhsp);
pastp->sentreep(newSenTree(nodep));
condp = pastp;
}
// Wrap existing PExpr body: if (antecedent) { <original body> } else { /* vacuous pass
// */ }
AstBegin* const bodyp = pexprp->bodyp();
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}};
bodyp->addStmtsp(guardp);
nodep->replaceWith(pexprp);
// Don't iterate pexprp here -- it was already iterated when created
// (in visit(AstSExpr*)), so delays and disable iff are already processed.
} else if (nodep->isOverlapped()) {
nodep->replaceWith(new AstLogOr{flp, new AstLogNot{flp, lhsp}, rhsp});
} else {
if (m_disablep) {
@@ -619,6 +1140,42 @@ private:
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstUntil* nodep) override {
FileLine* const flp = nodep->fileline();
if (m_pexprp) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'until' in complex property expression");
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (nodep->isStrong()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: s_until"
<< (nodep->isOverlapping() ? "_with" : "")
<< " (in property expresion)");
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
AstLoop* const loopp = new AstLoop{flp};
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstLogAnd* const loopCondp = new AstLogAnd{flp, lhsp, new AstLogNot{flp, rhsp}};
loopp->addStmtsp(new AstLoopTest{flp, loopp, loopCondp});
loopp->addStmtsp(new AstEventControl{flp, newSenTree(nodep), nullptr});
AstNodeExpr* const rhsCopyp = rhsp->cloneTreePure(false);
AstNodeExpr* const passCondp
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
: rhsCopyp;
AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
beginp->addStmtsp(
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
AstPExpr* const pexprp = new AstPExpr{flp, beginp, nodep->dtypep()};
pexprp->user1(1);
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstDefaultDisable* nodep) override {
// Done with these
@@ -647,7 +1204,7 @@ private:
// Unlink and just keep a pointer to it, convert to sentree as needed
m_senip = nodep->sensesp();
iterateNull(nodep->disablep());
if (VN_AS(nodep->backp(), NodeCoverOrAssert)->type() == VAssertType::CONCURRENT) {
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack()});
nodep->propp()->dtypeFrom(propDtp);
@@ -655,15 +1212,32 @@ private:
iterate(nodep->propp());
}
void visit(AstPExpr* nodep) override {
if (m_pexprp && m_pexprp->user1()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Complex property expression inside 'until''");
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (AstLogNot* const notp = VN_CAST(nodep->backp(), LogNot)) {
notp->replaceWith(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(notp), notp);
iterate(nodep);
return;
}
VL_RESTORER(m_inPExpr);
// Sequence expression as antecedent of implication is not yet supported
if (AstImplication* const implp = VN_CAST(nodep->backp(), Implication)) {
if (implp->lhsp() == nodep) {
implp->v3warn(E_UNSUPPORTED,
"Unsupported: Implication with sequence expression as antecedent");
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
VL_RESTORER(m_pexprp);
VL_RESTORER(m_disableSeqIfp);
m_inPExpr = true;
m_pexprp = nodep;
if (m_disablep) {
const AstSampled* sampledp;
@@ -700,11 +1274,14 @@ private:
AstVar* const initialCntp = new AstVar{flp, VVarType::BLOCKTEMP, "__VinitialCnt",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
initialCntp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
bodyp->stmtsp()->addHereThisAsNext(initialCntp);
AstAssign* const assignp
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
readCntRefp->cloneTree(false)};
// Prepend to the sequence body to keep statement list structure valid.
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
bodyp->addStmtsp(initialCntp);
initialCntp->addNextHere(assignp);
assignp->addNextHere(origStmtsp);
m_disableSeqIfp
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
@@ -716,9 +1293,11 @@ private:
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_defaultClockingp);
VL_RESTORER(m_defaultClkEvtVarp);
VL_RESTORER(m_defaultDisablep);
VL_RESTORER(m_modp);
m_defaultClockingp = nullptr;
m_defaultClkEvtVarp = nullptr;
nodep->foreach([&](AstClocking* const clockingp) {
if (clockingp->isDefault()) {
if (m_defaultClockingp) {
@@ -737,6 +1316,11 @@ private:
m_defaultDisablep = disablep;
});
m_modp = nodep;
// Pre-create and cache the clocking event var before iterating children.
// visit(AstClocking) will unlink the event from the clocking node and place it
// in the module tree, then delete the clocking. After that, ensureEventp() would
// create an orphaned var. Caching here avoids this.
m_defaultClkEvtVarp = m_defaultClockingp ? m_defaultClockingp->ensureEventp() : nullptr;
iterateChildren(nodep);
}
void visit(AstProperty* nodep) override {
@@ -744,6 +1328,11 @@ private:
// (AstFuncRef)
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstSequence* nodep) override {
// Sequence declarations are not visited directly; their bodies are inlined
// at call sites by visit(AstFuncRef*). Collect for post-traversal cleanup.
m_seqsToCleanup.push_back(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
@@ -754,6 +1343,17 @@ public:
iterate(nodep);
// Fix up varref names
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
// Clean up sequence declarations after inlining.
// Referenced sequences that were inlined have isReferenced cleared.
// Remaining referenced sequences are in unsupported contexts (e.g. @seq event).
for (AstSequence* seqp : m_seqsToCleanup) {
if (seqp->isReferenced()) {
seqp->v3warn(E_UNSUPPORTED,
"Unsupported: sequence referenced outside assertion property");
} else {
VL_DO_DANGLING(seqp->unlinkFrBack()->deleteTree(), seqp);
}
}
}
~AssertPreVisitor() override = default;
};
+1186 -4
View File
File diff suppressed because it is too large Load Diff
+96 -55
View File
@@ -63,7 +63,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
void VCMethod::selfTest() {
int i = 0;
for (const auto& it : s_itemData) {
VCMethod exp{i};
const VCMethod exp{i};
UASSERT_STATIC(it.m_e == exp,
"VCMethod::s_itemData table rows are out-of-order, starting at row "s
+ cvtToStr(i) + " '" + +it.m_name + '\'');
@@ -181,11 +181,8 @@ string AstNode::encodeName(const string& namein) {
}
string AstNode::encodeNumber(int64_t num) {
if (num < 0) {
return "__02D" + cvtToStr(-num); // 2D=-
} else {
return cvtToStr(num);
}
if (num < 0) return "__02D" + cvtToStr(-num); // 2D=-
return cvtToStr(num);
}
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
@@ -394,36 +391,36 @@ AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
if (!nodep) { // verilog.y and lots of other places assume this
if (!nodep) // verilog.y and lots of other places assume this
return newp;
} else {
// Find end of old list
AstNode* oldtailp = nodep;
if (oldtailp->m_nextp) {
if (oldtailp->m_headtailp) {
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
"Node had next, but headtail says it shouldn't"););
} else {
// Though inefficient, we are occasionally passed an
// addNext in the middle of a list.
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
}
// Find end of old list
AstNode* oldtailp = nodep;
if (oldtailp->m_nextp) {
if (oldtailp->m_headtailp) {
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
"Node had next, but headtail says it shouldn't"););
} else {
// Though inefficient, we are occasionally passed an
// addNext in the middle of a list.
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
}
// Link it in
oldtailp->m_nextp = newp;
newp->m_backp = oldtailp;
// New tail needs the head
AstNode* const newtailp = newp->m_headtailp;
AstNode* const headp = oldtailp->m_headtailp;
oldtailp->m_headtailp = nullptr; // May be written again as new head
newp->m_headtailp = nullptr; // May be written again as new tail
newtailp->m_headtailp = headp;
headp->m_headtailp = newtailp;
newp->editCountInc();
// No change of m_iterpp, as only changing m_nextp of current node;
// the current node is still the one at the iteration point
}
// Link it in
oldtailp->m_nextp = newp;
newp->m_backp = oldtailp;
// New tail needs the head
AstNode* const newtailp = newp->m_headtailp;
AstNode* const headp = oldtailp->m_headtailp;
oldtailp->m_headtailp = nullptr; // May be written again as new head
newp->m_headtailp = nullptr; // May be written again as new tail
newtailp->m_headtailp = headp;
headp->m_headtailp = newtailp;
newp->editCountInc();
// No change of m_iterpp, as only changing m_nextp of current node;
// the current node is still the one at the iteration point
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
return nodep;
}
@@ -992,6 +989,54 @@ void AstNode::operator delete(void* objp, size_t size) {
}
#endif
#ifdef VL_ALLOC_RANDOM_CHECKS
void* AstNode::operator new(size_t size) { // VL_MT_SAFE
// Compute the maximum node size once and cache it
static const size_t ASTGEN_MAX_NODE_SIZE = []() {
size_t maxSize = 0;
for (size_t t = 0; t < VNType::NUM_TYPES(); ++t) {
maxSize = std::max(maxSize, VNType::typeInfo(static_cast<VNType::en>(t)).m_sizeof);
}
return maxSize;
}();
// Make the following small to debug this routine, larger for performance and better random
constexpr size_t POOL_SIZE = 65536; // Ideally large enough to fit all nodes used in tests
// Randomly select from a large pool (POOL_SIZE) of max-node sized (MAX_NODE_SIZE) pointers
static uint64_t s_lfsr = 0; // LFSR, 0 = didn't initialize yet
if (int seed = v3Global.opt.debugAllocRandom()) {
static V3Mutex s_mutex;
const V3LockGuard lock{s_mutex};
static std::array<void*, POOL_SIZE> s_nodePool VL_GUARDED_BY(s_mutex);
constexpr uint64_t POLYNOMIAL = 0x80000000000019e2ULL;
UASSERT_STATIC(size <= ASTGEN_MAX_NODE_SIZE, "fix ASTGEN_MAX_NODE_SIZE");
if (!s_lfsr) {
s_lfsr = seed;
if (!s_lfsr) s_lfsr = ~s_lfsr;
for (size_t i = 0; i < POOL_SIZE; ++i) {
s_nodePool[i] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64);
}
// Sort, just to make it more obvious we are properly randomizing
std::sort(std::begin(s_nodePool), std::end(s_nodePool));
}
// Xoroshiro128+ algorithm
s_lfsr = (s_lfsr & 1ULL) ? ((s_lfsr >> 1ULL) ^ POLYNOMIAL) : (s_lfsr >> 1ULL);
const size_t index = s_lfsr % POOL_SIZE;
AstNode* const objp = static_cast<AstNode*>(s_nodePool[index]);
s_nodePool[index] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64); // For later new()
return objp;
} else {
AstNode* const objp = static_cast<AstNode*>(::operator new(size));
return objp;
}
}
void AstNode::operator delete(void* objp, size_t size) {
// Leak due to size difference between true node and MAX_NODE_SIZE
(void)objp;
(void)size;
}
#endif
//======================================================================
// Iterators
@@ -1201,10 +1246,10 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
break;
case VNTypeInfo::OP_USED:
UASSERT_OBJ(nodep, this,
typeInfo.m_namep << " must have non nullptr " << opName << "()");
typeInfo.m_namep << " must have non-nullptr " << opName << "()");
UASSERT_OBJ(!nodep->nextp(), this,
typeInfo.m_namep << "::" << opName
<< "() cannot have a non nullptr nextp()");
<< "() cannot have a non-nullptr nextp()");
nodep->checkTreeIter(this);
break;
case VNTypeInfo::OP_LIST:
@@ -1546,11 +1591,12 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, VSigning numeric) {
// Enums need to become direct sizes to avoid later ENUMVALUE errors
&& !VN_IS(dtypep()->skipRefToEnump(), EnumDType))
return; // Correct already
// FUTURE: We may be pointing at a two state data type, and this may
// convert it to logic. Since the AstVar remains correct, we
// work OK but this assumption may break in the future.
// Note we can't just clone and do a widthForce, as if it's a BasicDType
// the msb() indications etc will be incorrect.
if (AstBasicDType* const basicp = VN_CAST(dtypep(), BasicDType)) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) {
dtypeSetBitUnsized(width, widthMin, numeric);
return;
}
}
dtypeSetLogicUnsized(width, widthMin, numeric);
}
}
@@ -1602,7 +1648,8 @@ static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) {
if (const AstPackArrayDType* const packp = VN_CAST(nodep, PackArrayDType)) {
nodep = packp->subDTypep();
continue;
} else if (const AstNodeDType* const refp = nodep->skipRefToEnump()) {
}
if (const AstNodeDType* const refp = nodep->skipRefToEnump()) {
if (refp != nodep) {
nodep = refp;
continue;
@@ -1630,9 +1677,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
const bool toNumericable
= VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType);
if (toBaseDtp == fromBaseDtp) {
return VCastable::COMPATIBLE;
} else if (toNumericable) {
if (toBaseDtp == fromBaseDtp) return VCastable::COMPATIBLE;
if (toNumericable) {
if (fromNumericable) return VCastable::COMPATIBLE;
} else if (VN_IS(toBaseDtp, EnumDType)) {
if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp))
@@ -1648,13 +1695,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
const AstClass* const fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp);
const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp);
if (upcast) {
return VCastable::COMPATIBLE;
} else if (downcast) {
return VCastable::DYNAMIC_CLASS;
} else {
return VCastable::INCOMPATIBLE;
}
if (upcast) return VCastable::COMPATIBLE;
if (downcast) return VCastable::DYNAMIC_CLASS;
return VCastable::INCOMPATIBLE;
}
return castable;
}
@@ -1678,11 +1721,9 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
if (VN_IS(node1p, Const)) std::swap(node1p, node2p);
{
const VCastable castable = computeCastable(node1p->dtypep(), node2p->dtypep(), node2p);
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE)
return node1p->dtypep();
} else if (castable == VCastable::DYNAMIC_CLASS) {
return node2p->dtypep();
}
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
}
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
+12 -12
View File
@@ -95,7 +95,7 @@ class ExecMTask;
struct VNTypeInfo final {
const char* m_namep;
enum uint8_t {
enum OpEn : uint8_t {
OP_UNUSED,
OP_USED,
OP_LIST,
@@ -147,7 +147,7 @@ public:
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(int i) {
m_u.up = 0;
m_u.up = nullptr;
m_u.ui = i;
}
explicit VNUser(void* p) { m_u.up = p; }
@@ -560,7 +560,7 @@ public:
// Perform a function on every link in a node
virtual void foreachLink(std::function<void(AstNode** linkpp, const char* namep)> f) = 0;
#ifdef VL_LEAK_CHECKS
#if defined(VL_LEAK_CHECKS) || defined(VL_ALLOC_RANDOM_CHECKS)
static void* operator new(size_t size);
static void operator delete(void* obj, size_t size);
#endif
@@ -766,7 +766,7 @@ public:
void dtypeSetVoid() { dtypep(findVoidDType()); }
// Data type locators
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::BIT); }
AstNodeDType* findDoubleDType() const { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
AstNodeDType* findIntDType() const { return findBasicDType(VBasicDTypeKwd::INT); }
AstNodeDType* findStringDType() const { return findBasicDType(VBasicDTypeKwd::STRING); }
@@ -932,7 +932,7 @@ public:
virtual bool isSame(const AstNode* samep) const {
return type() == samep->type() && sameNode(samep);
}
// Iff has a data type; dtype() must be non null
// Iff has a data type; dtype() must be non-null
virtual bool hasDType() const VL_MT_SAFE { return false; }
// Iff has a non-null childDTypep(), as generic node function
virtual AstNodeDType* getChildDTypep() const { return nullptr; }
@@ -1005,7 +1005,7 @@ public:
// For use via the VN_IS macro only, or in templated code
template <typename T_TargetType, typename T_Node>
static bool is(const T_Node* nodep) VL_MT_SAFE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_IS called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_IS called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_IS, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1016,7 +1016,7 @@ public:
// For use via the VN_CAST macro only, or in templated code
template <typename T_TargetType, typename T_Node>
static T_TargetType* cast(T_Node* nodep) VL_MT_SAFE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1027,7 +1027,7 @@ public:
}
template <typename T_TargetType, typename T_Node>
static const T_TargetType* cast(const T_Node* nodep) VL_MT_SAFE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1040,7 +1040,7 @@ public:
// For use via the VN_AS macro only, or in templated code
template <typename T_TargetType, typename T_Node>
static T_TargetType* as(T_Node* nodep) VL_PURE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1052,7 +1052,7 @@ public:
}
template <typename T_TargetType, typename T_Node>
static const T_TargetType* as(const T_Node* nodep) VL_PURE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1066,7 +1066,7 @@ public:
// For use via privateAs or the VN_DBG_AS macro only
template <typename T_TargetType, typename T_Node>
static T_TargetType* unsafePrivateAs(T_Node* nodep) VL_PURE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_DBG_AS, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
@@ -1075,7 +1075,7 @@ public:
}
template <typename T_TargetType, typename T_Node>
static const T_TargetType* unsafePrivateAs(const T_Node* nodep) VL_PURE {
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non AstNode");
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non-AstNode");
static_assert(!uselessCast<T_TargetType, T_Node>(),
"Unnecessary VN_DBG_AS, node known to have target type.");
static_assert(!impossibleCast<T_TargetType, T_Node>(),
+85 -16
View File
@@ -574,6 +574,7 @@ public:
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64
|| m_e == TIME);
}
bool isBit() const { return m_e == BIT; }
bool isBitLogic() const { // Bit/logic vector types; can form a packed array
return (m_e == LOGIC || m_e == BIT);
}
@@ -715,13 +716,9 @@ public:
bool likely() const { return m_e == BP_LIKELY; }
bool unlikely() const { return m_e == BP_UNLIKELY; }
VBranchPred invert() const {
if (m_e == BP_UNLIKELY) {
return BP_LIKELY;
} else if (m_e == BP_LIKELY) {
return BP_UNLIKELY;
} else {
return m_e;
}
if (m_e == BP_UNLIKELY) return BP_LIKELY;
if (m_e == BP_LIKELY) return BP_UNLIKELY;
return m_e;
}
const char* ascii() const {
static const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
@@ -757,6 +754,7 @@ public:
ARRAY_FIRST,
ARRAY_INSIDE,
ARRAY_LAST,
ARRAY_MAP,
ARRAY_MAX,
ARRAY_MIN,
ARRAY_NEXT,
@@ -801,6 +799,9 @@ public:
DYN_RESIZE,
DYN_SIZE,
DYN_SLICE,
DYN_SLICE_ASSIGN,
DYN_SLICE_ASSIGN_BACK_BACK,
DYN_SLICE_ASSIGN_FRONT_BACK,
DYN_SLICE_BACK_BACK,
DYN_SLICE_FRONT_BACK,
EVENT_CLEAR_FIRED,
@@ -811,14 +812,20 @@ public:
FORK_DONE,
FORK_INIT,
FORK_JOIN,
FORK_ON_KILL,
RANDOMIZER_BASIC_STD_RANDOMIZATION,
RANDOMIZER_CLEARCONSTRAINTS,
RANDOMIZER_CLEARALL,
RANDOMIZER_DISABLE_SOFT,
RANDOMIZER_HARD,
RANDOMIZER_SOFT,
RANDOMIZER_UNIQUE,
RANDOMIZER_MARK_RANDC,
RANDOMIZER_SOLVE_BEFORE,
RANDOMIZER_PIN_VAR,
RANDOMIZER_WRITE_VAR,
RANDOMIZER_SET_VAR_DISABLED,
RANDOMIZER_CLEAR_VAR_DISABLED,
RNG_GET_RANDSTATE,
RNG_SET_RANDSTATE,
SCHED_ANY_TRIGGERED,
@@ -893,6 +900,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{ARRAY_FIRST, "first", false}, \
{ARRAY_INSIDE, "inside", true}, \
{ARRAY_LAST, "last", false}, \
{ARRAY_MAP, "map", true}, \
{ARRAY_MAX, "max", true}, \
{ARRAY_MIN, "min", true}, \
{ARRAY_NEXT, "next", false}, \
@@ -937,6 +945,9 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{DYN_RESIZE, "resize", false}, \
{DYN_SIZE, "size", true}, \
{DYN_SLICE, "slice", true}, \
{DYN_SLICE_ASSIGN, "sliceAssign", false}, \
{DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false}, \
{DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false}, \
{DYN_SLICE_BACK_BACK, "sliceBackBack", true}, \
{DYN_SLICE_FRONT_BACK, "sliceFrontBack", true}, \
{EVENT_CLEAR_FIRED, "clearFired", false}, \
@@ -947,14 +958,20 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{FORK_DONE, "done", false}, \
{FORK_INIT, "init", false}, \
{FORK_JOIN, "join", false}, \
{FORK_ON_KILL, "onKill", false}, \
{RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false}, \
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
{RANDOMIZER_CLEARALL, "clearAll", false}, \
{RANDOMIZER_DISABLE_SOFT, "disable_soft", false}, \
{RANDOMIZER_HARD, "hard", false}, \
{RANDOMIZER_SOFT, "soft", false}, \
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
{RANDOMIZER_MARK_RANDC, "markRandc", false}, \
{RANDOMIZER_SOLVE_BEFORE, "solveBefore", false}, \
{RANDOMIZER_PIN_VAR, "pin_var", false}, \
{RANDOMIZER_WRITE_VAR, "write_var", false}, \
{RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false}, \
{RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false}, \
{RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true}, \
{RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false}, \
{SCHED_ANY_TRIGGERED, "anyTriggered", false}, \
@@ -980,6 +997,48 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
// ######################################################################
class VCStmtType final {
public:
enum en : uint8_t {
NONE, // Unknown or not applicable
CTOR_VAR_RESET_CALL,
_ENUM_MAX // Leave last
};
private:
struct Item final {
enum en m_e; // Statement's enum mnemonic, for checking
const char* m_name; // Statements name, for debugging
};
static Item s_itemData[];
public:
enum en m_e;
VCStmtType()
: m_e{NONE} {}
// cppcheck-suppress noExplicitConstructor
constexpr VCStmtType(en _e)
: m_e{_e} {}
explicit VCStmtType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const VL_PURE {
static const char* const names[] = {"none", "ctor_var_reset_call"};
return names[m_e];
}
bool isNone() const { return m_e == NONE; }
};
constexpr bool operator==(const VCStmtType& lhs, const VCStmtType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VCStmtType& lhs, VCStmtType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VCStmtType::en lhs, const VCStmtType& rhs) { return lhs == rhs.m_e; }
inline std::ostream& operator<<(std::ostream& os, const VCStmtType& rhs) {
return os << rhs.ascii();
}
// ######################################################################
class VCaseType final {
public:
enum en : uint8_t {
@@ -1080,6 +1139,16 @@ public:
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
bool isRef() const VL_MT_SAFE { return m_e == REF; }
bool isConstRef() const VL_MT_SAFE { return m_e == CONSTREF; }
string traceSigDirection() const {
if (isInout()) {
return "VerilatedTraceSigDirection::INOUT";
} else if (isWritable()) {
return "VerilatedTraceSigDirection::OUTPUT";
} else if (isNonOutput()) {
return "VerilatedTraceSigDirection::INPUT";
}
return "VerilatedTraceSigDirection::NONE";
}
};
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
return lhs.m_e == rhs.m_e;
@@ -1375,7 +1444,7 @@ public:
bool isAutomatic() const { return m_e == AUTOMATIC_EXPLICIT || m_e == AUTOMATIC_IMPLICIT; }
bool isStatic() const { return m_e == STATIC_EXPLICIT || m_e == STATIC_IMPLICIT; }
bool isStaticExplicit() const { return m_e == STATIC_EXPLICIT; }
VLifetime makeImplicit() {
VLifetime makeImplicit() const {
switch (m_e) {
case AUTOMATIC_EXPLICIT: return AUTOMATIC_IMPLICIT;
case STATIC_EXPLICIT: return STATIC_IMPLICIT;
@@ -1423,11 +1492,7 @@ public:
~VNumRange() = default;
// MEMBERS
void init(int hi, int lo, bool ascending) {
if (lo > hi) {
const int t = hi;
hi = lo;
lo = t;
}
if (lo > hi) std::swap(hi, lo);
m_left = ascending ? lo : hi;
m_right = ascending ? hi : lo;
m_ranged = true;
@@ -1743,6 +1808,10 @@ public:
static const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
return names[m_e];
}
const char* func_prefix() const {
static const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
return names[m_e];
}
};
constexpr bool operator==(const VTraceType& lhs, const VTraceType& rhs) {
return lhs.m_e == rhs.m_e;
@@ -1771,7 +1840,7 @@ public:
explicit VUseType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
bool containsAny(VUseType other) { return m_e & other.m_e; }
bool containsAny(VUseType other) const { return m_e & other.m_e; }
const char* ascii() const {
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
return names[m_e - 1];
@@ -1781,10 +1850,10 @@ constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
return VUseType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
return VUseType::en(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
return os << rhs.ascii();
+19 -16
View File
@@ -153,7 +153,7 @@ AstAlways::AstAlways(AstAssignW* assignp)
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* exprsp)
: ASTGEN_SUPER_ElabDisplay(fl) {
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp});
addFmtp(new AstSFormatF{fl, "", true, exprsp});
m_displayType = dispType;
}
@@ -183,23 +183,17 @@ AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
string AstVarRef::name() const { return varp() ? varp()->name() : nameThis(); }
bool AstVarRef::sameNode(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
} else {
return (selfPointer() == samep->selfPointer()
&& classOrPackagep() == samep->classOrPackagep() && access() == samep->access()
&& (varp() && samep->varp() && varp()->sameNode(samep->varp())));
}
if (varScopep()) return (varScopep() == samep->varScopep() && access() == samep->access());
return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep()
&& access() == samep->access()
&& (varp() && samep->varp() && varp()->sameNode(samep->varp())));
}
bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& classOrPackagep() == samep->classOrPackagep()
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
&& varp()->sameNode(samep->varp()));
}
if (varScopep()) return (varScopep() == samep->varScopep());
return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep()
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
&& varp()->sameNode(samep->varp()));
}
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
@@ -211,4 +205,13 @@ AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const V
AstStmtExpr* AstNodeExpr::makeStmt() { return new AstStmtExpr{fileline(), this}; }
// Walk up the AST via backp() to find the containing AstNodeModule.
// Returns nullptr if not found.
inline AstNodeModule* findParentModule(AstNode* nodep) {
for (AstNode* curp = nodep; curp; curp = curp->backp()) {
if (AstNodeModule* const modp = VN_CAST(curp, NodeModule)) return modp;
}
return nullptr;
}
#endif // Guard
+17 -26
View File
@@ -125,10 +125,13 @@ public:
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return nullptr; }
virtual AstNodeDType* subDType2p() const VL_MT_STABLE { return nullptr; }
virtual bool isAggregateType() const { return false; }
// True for unpacked, dynamic, queue, and associative arrays (not packed arrays)
bool isNonPackedArray() const;
virtual bool isFourstate() const;
// Ideally an IEEE $typename
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
string stateDTypeName() const { return this->isFourstate() ? "(4-state)" : "(2-state)"; }
//
// Changing the width may confuse the data type resolution, so must clear
// TypeTable cache after use.
@@ -250,8 +253,7 @@ protected:
, m_name{other.m_name}
, m_uniqueNum{uniqueNumInc()}
, m_packed{other.m_packed}
, m_isFourstate{other.m_isFourstate}
, m_constrainedRand{false} {}
, m_isFourstate{other.m_isFourstate} {}
public:
ASTGEN_MEMBERS_AstNodeUOrStructDType;
@@ -458,6 +460,7 @@ public:
return m.m_keyword;
}
bool isBitLogic() const { return keyword().isBitLogic(); }
bool isBit() const { return keyword().isBit(); }
bool isCHandle() const VL_MT_STABLE { return keyword().isCHandle(); }
bool isDouble() const VL_MT_STABLE { return keyword().isDouble(); }
bool isEvent() const VL_MT_STABLE { return keyword() == VBasicDTypeKwd::EVENT; }
@@ -540,7 +543,6 @@ public:
dtypep(this);
}
public:
ASTGEN_MEMBERS_AstCDType;
bool sameNode(const AstNode* samep) const override {
const AstCDType* const asamep = VN_DBG_AS(samep, CDType);
@@ -555,16 +557,12 @@ public:
int widthTotalBytes() const override { return 8; } // Assume
bool isCompound() const override { return true; }
static string typeToHold(int width) {
if (width <= 8)
return "CData";
else if (width <= 16)
return "SData";
else if (width <= VL_IDATASIZE)
return "IData";
else if (width <= VL_QUADSIZE)
return "QData";
else
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
if (width <= 8) return "CData";
if (width <= 16) return "SData";
if (width <= VL_IDATASIZE) return "IData";
if (width <= VL_QUADSIZE) return "QData";
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
}
};
class AstClassRefDType final : public AstNodeDType {
@@ -586,11 +584,7 @@ public:
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
}
bool similarDTypeNode(const AstNodeDType* samep) const override {
// Doesn't need to compare m_classOrPackagep
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
return m_classp == asamep->m_classp;
}
bool similarDTypeNode(const AstNodeDType* samep) const override;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
@@ -966,8 +960,7 @@ public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
AstNode* valuep)
: ASTGEN_SUPER_MemberDType(fl)
, m_name{name}
, m_constrainedRand{false} {
, m_name{name} {
childDTypep(dtp); // Only for parser
this->valuep(valuep);
dtypep(nullptr); // V3Width will resolve
@@ -975,8 +968,7 @@ public:
}
AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp)
: ASTGEN_SUPER_MemberDType(fl)
, m_name{name}
, m_constrainedRand{false} {
, m_name{name} {
UASSERT(dtp, "AstMember created with no dtype");
refDTypep(dtp);
dtypep(this);
@@ -1251,10 +1243,9 @@ public:
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
// Used for recursive definition checking
if (AstNodeDType* const dtp = VN_CAST(lhsp(), NodeDType))
return dtp;
else
return nullptr;
if (AstNodeDType* const dtp = VN_CAST(lhsp(), NodeDType)) return dtp;
return nullptr;
}
int widthAlignBytes() const override { V3ERROR_NA_RETURN(1); }
int widthTotalBytes() const override { V3ERROR_NA_RETURN(1); }
+284 -37
View File
@@ -64,6 +64,7 @@ public:
// Someday we will generically support data types on every expr node
// Until then isOpaque indicates we shouldn't constant optimize this node type
bool isOpaque() const { return VN_IS(this, CvtPackString); }
bool isLValue() const;
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
inline AstStmtExpr* makeStmt();
@@ -232,6 +233,7 @@ private:
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
bool m_pli = false; // Pli system call ($name)
bool m_containsGenBlock = false; // Contains gen block reference
VIsCached m_purity; // Pure state
protected:
@@ -258,6 +260,8 @@ public:
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
bool pli() const { return m_pli; }
void pli(bool flag) { m_pli = flag; }
bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
bool isPure() override;
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
@@ -561,7 +565,6 @@ public:
dtypep(findCHandleDType());
}
public:
ASTGEN_MEMBERS_AstAddrOfCFunc;
void dump(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
@@ -601,7 +604,7 @@ class AstCExpr final : public AstNodeExpr {
void init(const string& text, int setwidth) {
if (!text.empty()) add(text);
if (setwidth) {
dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
dtypeSetBitSized(setwidth, VSigning::UNSIGNED);
} else {
dtypeSetVoid(); // Caller to override if necessary
}
@@ -620,6 +623,8 @@ public:
init(text, setwidth);
}
ASTGEN_MEMBERS_AstCExpr;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
// METHODS
bool cleanOut() const override { return true; }
std::string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -936,10 +941,7 @@ public:
addMembersp(membersp);
}
ASTGEN_MEMBERS_AstConsPackUOrStruct;
const char* broken() const override {
BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType));
return nullptr;
}
const char* broken() const override { return nullptr; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
@@ -1005,9 +1007,12 @@ class AstConst final : public AstNodeExpr {
dtypeSetDouble();
} else if (m_num.isString()) {
dtypeSetString();
} else {
} else if (m_num.isAnyXZ()) {
dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()),
VSigning::fromBool(m_num.isSigned()));
} else {
dtypeSetBitUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()),
VSigning::fromBool(m_num.isSigned()));
}
m_num.nodep(this);
}
@@ -1046,35 +1051,35 @@ public:
AstConst(FileLine* fl, uint32_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, 32, num) { // Need () constructor
dtypeSetLogicUnsized(m_num.width(), 0, VSigning::UNSIGNED);
dtypeSetBitUnsized(m_num.width(), 0, VSigning::UNSIGNED);
}
class Unsized32 {}; // for creator type-overload selection
AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value
: ASTGEN_SUPER_Const(fl)
, m_num(this, 32, num) { // Need () constructor
m_num.width(32, false);
dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED);
dtypeSetBitUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED);
}
class Signed32 {}; // for creator type-overload selection
AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value
: ASTGEN_SUPER_Const(fl)
, m_num(this, 32, num) { // Need () constructor
m_num.width(32, true);
dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::SIGNED);
dtypeSetBitUnsized(32, m_num.widthToFit(), VSigning::SIGNED);
}
class Unsized64 {}; // for creator type-overload selection
AstConst(FileLine* fl, Unsized64, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, 64, 0) { // Need () constructor
m_num.setQuad(num);
dtypeSetLogicSized(64, VSigning::UNSIGNED);
dtypeSetBitSized(64, VSigning::UNSIGNED);
}
class SizedEData {}; // for creator type-overload selection
AstConst(FileLine* fl, SizedEData, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, VL_EDATASIZE, 0) { // Need () constructor
m_num.setQuad(num);
dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED);
dtypeSetBitSized(VL_EDATASIZE, VSigning::UNSIGNED);
}
class RealDouble {}; // for creator type-overload selection
AstConst(FileLine* fl, RealDouble, double num)
@@ -1090,12 +1095,12 @@ public:
dtypeSetString();
}
class BitFalse {};
AstConst(FileLine* fl, BitFalse) // Shorthand const 0, dtype should be a logic of size 1
AstConst(FileLine* fl, BitFalse) // Shorthand const 0, dtype should be a bit of size 1
: ASTGEN_SUPER_Const(fl)
, m_num(this, 1, 0) { // Need () constructor
dtypeSetBit();
}
// Shorthand const 1 (or with argument 0/1), dtype should be a logic of size 1
// Shorthand const 1 (or with argument 0/1), dtype should be a bit of size 1
class BitTrue {};
AstConst(FileLine* fl, BitTrue, bool on = true)
: ASTGEN_SUPER_Const(fl)
@@ -1127,7 +1132,7 @@ public:
AstConst(FileLine* fl, OneStep)
: ASTGEN_SUPER_Const(fl)
, m_num{this, V3Number::OneStep{}} {
dtypeSetLogicSized(64, VSigning::UNSIGNED);
dtypeSetBitSized(64, VSigning::UNSIGNED);
initWithNumber();
}
ASTGEN_MEMBERS_AstConst;
@@ -1268,7 +1273,7 @@ class AstDist final : public AstNodeExpr {
// @astgen op2 := itemsp : List[AstDistItem]
public:
AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
: ASTGEN_SUPER_Inside(fl) {
: ASTGEN_SUPER_Dist(fl) {
this->exprp(exprp);
addItemsp(itemsp);
dtypeSetBit();
@@ -2045,17 +2050,11 @@ public:
}
string emitC() override {
if (seedp()) {
if (urandom()) {
return "VL_URANDOM_SEEDED_%nq%lq(%li)";
} else {
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
}
}
if (isWide()) {
return "VL_RANDOM_%nq(%nw, %P)";
} else {
return "VL_RANDOM_%nq()";
if (urandom()) return "VL_URANDOM_SEEDED_%nq%lq(%li)";
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
}
if (isWide()) return "VL_RANDOM_%nq(%nw, %P)";
return "VL_RANDOM_%nq()";
}
bool cleanOut() const override { return false; }
bool isGateOptimizable() const override { return false; }
@@ -2126,6 +2125,54 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstSConsRep final : public AstNodeExpr {
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
// op1 := exprp -- the repeated expression
// op2 := countp -- min repetition count (N); always a positive constant after V3Width
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
//
// Encoding:
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
// [*N:M]: countp=N, maxCountp=M, unbounded=false
// [+]: countp=1, maxCountp=nullptr, unbounded=true (= [*1:$])
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
//
// Lowering:
// Exact [*N] standalone: V3AssertPre saturating counter
// All other forms and all SExpr-contained forms: V3AssertProp PExpr loop
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := countp : AstNodeExpr
// @astgen op3 := maxCountp : Optional[AstNodeExpr]
const bool m_unbounded = false; // True for [+] and [*] (upper bound is $)
public:
// Exact [*N]
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
: ASTGEN_SUPER_SConsRep(fl) {
this->exprp(exprp);
this->countp(countp);
}
// Range [*N:M] or unbounded [+]/[*]
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
bool unbounded)
: ASTGEN_SUPER_SConsRep(fl)
, m_unbounded{unbounded} {
this->exprp(exprp);
this->countp(countp);
this->maxCountp(maxCountp);
}
ASTGEN_MEMBERS_AstSConsRep;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { V3ERROR_NA_RETURN(0); }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_LINE
return m_unbounded == VN_DBG_AS(samep, SConsRep)->m_unbounded; // LCOV_EXCL_LINE
}
bool unbounded() const { return m_unbounded; }
};
class AstSExpr final : public AstNodeExpr {
// Sequence expression
// @astgen op1 := preExprp: Optional[AstNodeExpr]
@@ -2150,39 +2197,77 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
};
class AstSFormatArg final : public AstNodeExpr {
// Information for formatting each argument to AstSFormat,
// used to pass to (potentially) runtime decoding of format arguments
// PARENT: SFormatF (or next list of expressions)
// @astgen op1 := exprp : AstNodeExpr
VFormatAttr m_formatAttr; // How to format expression
public:
AstSFormatArg(FileLine* fl, VFormatAttr formatAttr, AstNodeExpr* exprp)
: ASTGEN_SUPER_SFormatArg(fl)
, m_formatAttr{formatAttr} {
dtypeFrom(exprp);
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstSFormatArg;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
int instrCount() const override { return 0; }
bool sameNode(const AstNode* samep) const override {
return formatAttr() == VN_DBG_AS(samep, SFormatArg)->formatAttr();
}
string verilogKwd() const override { return "$sformatarg"; }
string emitVerilog() override { return "%l"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
return nullptr;
}
VFormatAttr formatAttr() const { return m_formatAttr; }
void formatAttr(const VFormatAttr& formatAttr) { m_formatAttr = formatAttr; }
static VFormatAttr formatAttrDefauled(const AstSFormatArg* nodep, const AstNodeDType* dtypep);
};
class AstSFormatF final : public AstNodeExpr {
// Convert format to string, generally under an AstDisplay or AstSFormat
// Also used as "real" function for /*verilator sformat*/ functions
// exprsp() once past parsing may be AstSFormatArgs
// @astgen op1 := exprsp : List[AstNodeExpr]
// @astgen op2 := scopeNamep : Optional[AstScopeName]
string m_text;
const bool m_hidden; // Under display, etc
bool m_hasFormat; // Has format code
bool m_exprFormat
= false; // Runtime Node* format, false = text() format code, false = possibly r
bool m_optionalFormat
= false; // With exprFormat, first argument is either format or format-implied
const char m_missingArgChar; // Format code when argument without format, 'h'/'o'/'b'
VTimescale m_timeunit; // Parent module time unit
public:
class NoFormat {};
class ExprFormat {};
AstSFormatF(FileLine* fl, const string& text, bool hidden, AstNodeExpr* exprsp,
char missingArgChar = 'd')
: ASTGEN_SUPER_SFormatF(fl)
, m_text{text}
, m_hidden{hidden}
, m_hasFormat{true}
, m_missingArgChar{missingArgChar} {
dtypeSetString();
addExprsp(exprsp);
}
AstSFormatF(FileLine* fl, NoFormat, AstNodeExpr* exprsp, char missingArgChar = 'd',
AstSFormatF(FileLine* fl, ExprFormat, AstNodeExpr* exprsp, char missingArgChar = 'd',
bool hidden = true)
: ASTGEN_SUPER_SFormatF(fl)
, m_text{""}
, m_hidden{hidden}
, m_hasFormat{false}
, m_exprFormat{true}
, m_missingArgChar{missingArgChar} {
dtypeSetString();
addExprsp(exprsp);
}
ASTGEN_MEMBERS_AstSFormatF;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_text; }
void name(const string& name) override { m_text = name; }
int instrCount() const override { return INSTR_COUNT_PLI; }
@@ -2192,12 +2277,19 @@ public:
string verilogKwd() const override { return "$sformatf"; }
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text = text; }
const char* broken() const override {
BROKEN_RTN(text() != "" && exprFormat()); // Expr format means no literal format
return nullptr;
}
bool formatScopeTracking() const { // Track scopeNamep(); Ok if false positive
return (name().find("%m") != string::npos || name().find("%M") != string::npos);
return exprFormat() || name().find("%m") != string::npos
|| name().find("%M") != string::npos;
}
bool hidden() const { return m_hidden; }
bool hasFormat() const { return m_hasFormat; }
void hasFormat(bool flag) { m_hasFormat = flag; }
bool exprFormat() const { return m_exprFormat; }
void exprFormat(bool flag) { m_exprFormat = flag; }
bool optionalFormat() const { return m_optionalFormat; }
void optionalFormat(bool flag) { m_optionalFormat = flag; }
char missingArgChar() const { return m_missingArgChar; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@@ -2207,6 +2299,38 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
bool isSystemFunc() const override { return true; }
};
class AstSGotoRep final : public AstNodeExpr {
// Goto repetition: expr [-> count]
// IEEE 1800-2023 16.9.2
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := countp : AstNodeExpr
public:
explicit AstSGotoRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
: ASTGEN_SUPER_SGotoRep(fl) {
this->exprp(exprp);
this->countp(countp);
}
ASTGEN_MEMBERS_AstSGotoRep;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
};
class AstSNonConsRep final : public AstNodeExpr {
// Nonconsecutive repetition: expr [= count]
// IEEE 1800-2023 16.9.2
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := countp : AstNodeExpr
public:
explicit AstSNonConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
: ASTGEN_SUPER_SNonConsRep(fl) {
this->exprp(exprp);
this->countp(countp);
}
ASTGEN_MEMBERS_AstSNonConsRep;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
};
class AstSScanF final : public AstNodeExpr {
// @astgen op1 := exprsp : List[AstNodeExpr] // VarRefs for results
// @astgen op2 := fromp : AstNodeExpr
@@ -2609,6 +2733,33 @@ public:
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstUntil final : public AstNodeExpr {
// The until property expression
// @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := rhsp : AstNodeExpr
const bool m_strong; // 's_' preffix
const bool m_overlapping; // '_with` suffix
public:
AstUntil(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool strong, bool overlapping)
: ASTGEN_SUPER_Until(fl)
, m_strong{strong}
, m_overlapping{overlapping} {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstUntil;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isStrong() const { return m_strong; }
bool isOverlapping() const { return m_overlapping; }
};
class AstValuePlusArgs final : public AstNodeExpr {
// Search expression. If nullptr then this is a $test$plusargs instead of $value$plusargs.
// @astgen op1 := searchp : Optional[AstNodeExpr]
@@ -2736,8 +2887,13 @@ public:
AstConcat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_Concat(fl, lhsp, rhsp) {
if (lhsp->dtypep() && rhsp->dtypep()) {
dtypeSetLogicSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
VSigning::UNSIGNED);
if (lhsp->dtypep()->isFourstate() || rhsp->dtypep()->isFourstate()) {
dtypeSetLogicSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
VSigning::UNSIGNED);
} else {
dtypeSetBitSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
VSigning::UNSIGNED);
}
}
}
ASTGEN_MEMBERS_AstConcat;
@@ -3564,6 +3720,96 @@ public:
int instrCount() const override { return widthInstrs() * 2; }
bool stringFlavor() const override { return true; }
};
class AstSAnd final : public AstNodeBiop {
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
public:
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSAnd;
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogAnd(lhs, rhs);
}
string emitVerilog() override { return "%k(%l %fand %r)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstSIntersect final : public AstNodeBiop {
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
// AND with a length restriction. For boolean operands, lowered to AstLogAnd.
public:
AstSIntersect(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SIntersect(fl, lhsp, rhsp) {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSIntersect;
// LCOV_EXCL_START // Lowered before these are ever called
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogAnd(lhs, rhs);
}
string emitVerilog() override { return "%k(%l %fintersect %r)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
// LCOV_EXCL_STOP
};
class AstSOr final : public AstNodeBiop {
// Sequence 'or' (IEEE 1800-2023 16.9.7): at least one operand sequence must match.
// Operates on match sets, not values. For boolean operands, lowered to AstLogOr.
public:
AstSOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SOr(fl, lhsp, rhsp) {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSOr;
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogOr(lhs, rhs);
}
string emitVerilog() override { return "%k(%l %for %r)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstSThroughout final : public AstNodeBiop {
// expr throughout seq (IEEE 1800-2023 16.9.9)
public:
AstSThroughout(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SThroughout(fl, lhsp, rhsp) {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSThroughout;
// LCOV_EXCL_START // Lowered in V3AssertProp before these are called
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogAnd(lhs, rhs);
}
string emitVerilog() override { return "%k(%l %fthroughout %r)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
// LCOV_EXCL_STOP
};
class AstSel final : public AstNodeBiop {
// *Resolved* (tyep checked) multiple bit range extraction. Always const width
// @astgen alias op1 := fromp
@@ -4442,6 +4688,7 @@ public:
string selfPointerProtect(bool useSelfForThis) const {
return selfPointer().protect(useSelfForThis, protect());
}
bool maybePointedTo() const override VL_MT_SAFE { return true; }
};
class AstCMethodCall final : public AstNodeCCall {
// C++ method call
@@ -4960,7 +5207,7 @@ public:
m_size = setwidth;
if (setwidth) {
if (minwidth == -1) minwidth = setwidth;
dtypeSetLogicUnsized(setwidth, minwidth, VSigning::UNSIGNED);
dtypeSetBitUnsized(setwidth, minwidth, VSigning::UNSIGNED);
}
}
// cppcheck-suppress constParameterPointer
+60 -12
View File
@@ -87,6 +87,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
// @astgen op4 := scopeNamep : Optional[AstScopeName]
string m_name; // Name of task
string m_cname; // Name of task if DPI import
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
bool m_taskPublic : 1; // Public task
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
@@ -179,6 +180,8 @@ public:
void isExternProto(bool flag) { m_isExternProto = flag; }
bool isExternDef() const { return m_isExternDef; }
void isExternDef(bool flag) { m_isExternDef = flag; }
const string& ifacePortName() const { return m_ifacePortName; }
void ifacePortName(const string& name) { m_ifacePortName = name; }
bool prototype() const { return m_prototype; }
void prototype(bool flag) { m_prototype = flag; }
bool dpiExport() const { return m_dpiExport; }
@@ -277,6 +280,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_modPublic : 1; // Module has public references
bool m_modTrace : 1; // Tracing this module
bool m_inLibrary : 1; // From a library, no error if not used, never top level
bool m_ctorVarReset : 1; // Ctor needs to call ctor_var_reset
bool m_dead : 1; // LinkDot believes is dead; will remove in Dead visitors
bool m_hasGParam : 1; // Has global parameter (for link)
bool m_hasParameterList : 1; // Has #() for parameter declaration
@@ -285,6 +289,9 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_internal : 1; // Internally created
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
// set by V3Param::deepCloneModule. Suppresses
// width/type errors on the unresolved template.
bool m_verilatorLib : 1; // Module is a stub for a Verilator produced --lib-create
protected:
AstNodeModule(VNType t, FileLine* fl, const string& name, const string& libname)
@@ -295,6 +302,7 @@ protected:
, m_modPublic{false}
, m_modTrace{false}
, m_inLibrary{false}
, m_ctorVarReset{false}
, m_dead{false}
, m_hasGParam{false}
, m_hasParameterList{false}
@@ -303,6 +311,7 @@ protected:
, m_internal{false}
, m_recursive{false}
, m_recursiveClone{false}
, m_parameterizedTemplate{false}
, m_verilatorLib{false} {}
public:
@@ -329,6 +338,8 @@ public:
void modPublic(bool flag) { m_modPublic = flag; }
bool modTrace() const { return m_modTrace; }
void modTrace(bool flag) { m_modTrace = flag; }
bool ctorVarReset() const { return m_ctorVarReset; }
void ctorVarReset(bool flag) { m_ctorVarReset = flag; }
bool dead() const { return m_dead; }
void dead(bool flag) { m_dead = flag; }
bool hasGParam() const { return m_hasGParam; }
@@ -345,6 +356,8 @@ public:
void recursive(bool flag) { m_recursive = flag; }
void recursiveClone(bool flag) { m_recursiveClone = flag; }
bool recursiveClone() const { return m_recursiveClone; }
bool parameterizedTemplate() const { return m_parameterizedTemplate; }
void parameterizedTemplate(bool flag) { m_parameterizedTemplate = flag; }
void verilatorLib(bool flag) { m_verilatorLib = flag; }
bool verilatorLib() const { return m_verilatorLib; }
VLifetime lifetime() const { return m_lifetime; }
@@ -1011,19 +1024,18 @@ class AstDefParam final : public AstNode {
// A defparam assignment
// Parents: MODULE
// @astgen op1 := rhsp : AstNodeExpr
// @astgen op2 := pathp : AstNodeExpr
string m_name; // Name of variable getting set
string m_path; // Dotted cellname to set parameter of
public:
AstDefParam(FileLine* fl, const string& path, const string& name, AstNodeExpr* rhsp)
AstDefParam(FileLine* fl, AstNodeExpr* pathp, const string& name, AstNodeExpr* rhsp)
: ASTGEN_SUPER_DefParam(fl)
, m_name{name}
, m_path{path} {
, m_name{name} {
this->rhsp(rhsp);
this->pathp(pathp);
}
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
ASTGEN_MEMBERS_AstDefParam;
bool sameNode(const AstNode*) const override { return true; }
string path() const { return m_path; }
};
class AstDefaultDisable final : public AstNode {
// @astgen op1 := condp : AstNodeExpr
@@ -1377,6 +1389,7 @@ class AstPin final : public AstNode {
// @astgen ptr := m_modPTypep : Optional[AstParamTypeDType] // Param type connects to on sub
int m_pinNum; // Pin number
string m_name; // Pin name, or "" for number based interconnect
string m_paramPath; // Original defparam cell path, if this pin came from a defparam
bool m_param = false; // Pin connects to parameter
bool m_svDotName = false; // Pin is SystemVerilog .name'ed
bool m_svImplicit = false; // Pin is SystemVerilog .name'ed, allow implicit
@@ -1403,6 +1416,8 @@ public:
void modPTypep(AstParamTypeDType* nodep) { m_modPTypep = nodep; }
bool param() const { return m_param; }
void param(bool flag) { m_param = flag; }
const string& paramPath() const { return m_paramPath; }
void paramPath(const string& path) { m_paramPath = path; }
bool svDotName() const { return m_svDotName; }
void svDotName(bool flag) { m_svDotName = flag; }
bool svImplicit() const { return m_svImplicit; }
@@ -1481,7 +1496,7 @@ public:
bool sameNode(const AstNode* samep) const override {
return direction() == VN_DBG_AS(samep, Pull)->direction();
}
uint32_t direction() const { return (uint32_t)m_direction; }
uint32_t direction() const { return static_cast<uint32_t>(m_direction); }
};
class AstScope final : public AstNode {
// A particular usage of a cell
@@ -1772,6 +1787,15 @@ public:
addAttrsp(attrsp);
dtypep(nullptr); // V3Width will resolve
}
AstTypedef(FileLine* fl, const string& name, AstNodeDType* dtp, bool underClass)
: ASTGEN_SUPER_Typedef(fl)
, m_name{name}
, m_declTokenNum{fl->tokenNum()}
, m_isHideLocal{false}
, m_isHideProtected{false}
, m_isUnderClass{underClass} {
dtypep(dtp);
}
ASTGEN_MEMBERS_AstTypedef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
@@ -1927,20 +1951,24 @@ class AstVar final : public AstNode {
bool m_noCReset : 1; // Do not do automated CReset creation
bool m_noReset : 1; // Do not do automated reset/randomization
bool m_noSubst : 1; // Do not substitute out references
bool m_sampled : 1; // Sampled timing region
bool m_substConstOnly : 1; // Only substitute if constant
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
bool m_isForceable : 1; // May be forced/released externally from user C code
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
bool m_isReadByDpi : 1; // This variable can be read by a DPI Export
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
bool m_ignorePostRead : 1; // Ignore reads in 'Post' blocks during ordering
bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
bool m_dfgTriLowered : 1; // Signal/temporary introduced by tristate lowering
bool m_dfgAllowMultidriveTri : 1; // Allow DFG MULTIDRIVEN warning for intentional tri nets
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
bool m_processQueue : 1; // Process queue variable
void init() {
m_ansi = false;
m_declTyped = false;
@@ -1981,20 +2009,24 @@ class AstVar final : public AstNode {
m_noCReset = false;
m_noReset = false;
m_noSubst = false;
m_sampled = false;
m_substConstOnly = false;
m_overridenParam = false;
m_trace = false;
m_isLatched = false;
m_isForceable = false;
m_isForcedByCode = false;
m_isReadByDpi = false;
m_isWrittenByDpi = false;
m_isWrittenBySuspendable = false;
m_ignorePostRead = false;
m_ignorePostWrite = false;
m_ignoreSchedWrite = false;
m_dfgMultidriven = false;
m_dfgTriLowered = false;
m_dfgAllowMultidriveTri = false;
m_globalConstrained = false;
m_isStdRandomizeArg = false;
m_processQueue = false;
}
public:
@@ -2057,6 +2089,12 @@ public:
}
VDirection direction() const VL_MT_SAFE { return m_direction; }
bool isIO() const VL_MT_SAFE { return m_direction != VDirection::NONE; }
bool isVLIO() const {
const AstBasicDType* const bdtypep = basicp();
return isPrimaryIO() && bdtypep && !bdtypep->isOpaque()
&& !dtypep()->skipRefp()->isCompound()
&& !VN_IS(dtypep()->skipRefp(), UnpackArrayDType);
}
void declDirection(const VDirection& flag) { m_declDirection = flag; }
VDirection declDirection() const { return m_declDirection; }
void varType(VVarType type) { m_varType = type; }
@@ -2075,7 +2113,7 @@ public:
string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool asRef = false) const;
bool asRef = false, bool constRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
@@ -2142,6 +2180,10 @@ public:
void noReset(bool flag) { m_noReset = flag; }
bool noSubst() const { return m_noSubst; }
void noSubst(bool flag) { m_noSubst = flag; }
bool processQueue() const { return m_processQueue; }
void processQueue(bool flag) { m_processQueue = flag; }
bool sampled() const { return m_sampled; }
void sampled(bool flag) { m_sampled = flag; }
bool substConstOnly() const { return m_substConstOnly; }
void substConstOnly(bool flag) { m_substConstOnly = flag; }
bool overriddenParam() const { return m_overridenParam; }
@@ -2152,6 +2194,8 @@ public:
void setForceable() { m_isForceable = true; }
void setForcedByCode() { m_isForcedByCode = true; }
bool isForced() const { return m_isForceable || m_isForcedByCode; }
bool isReadByDpi() const { return m_isReadByDpi; }
void setReadByDpi() { m_isReadByDpi = true; }
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
void setWrittenByDpi() { m_isWrittenByDpi = true; }
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
@@ -2162,8 +2206,10 @@ public:
void setIgnorePostWrite() { m_ignorePostWrite = true; }
bool ignoreSchedWrite() const { return m_ignoreSchedWrite; }
void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; }
bool dfgMultidriven() const { return m_dfgMultidriven; }
void setDfgMultidriven() { m_dfgMultidriven = true; }
bool dfgTriLowered() const { return m_dfgTriLowered; }
void setDfgTriLowered() { m_dfgTriLowered = true; }
bool dfgAllowMultidriveTri() const { return m_dfgAllowMultidriveTri; }
void setDfgAllowMultidriveTri() { m_dfgAllowMultidriveTri = true; }
void globalConstrained(bool flag) { m_globalConstrained = flag; }
bool globalConstrained() const { return m_globalConstrained; }
bool isStdRandomizeArg() const { return m_isStdRandomizeArg; }
@@ -2424,7 +2470,9 @@ public:
class AstSequence final : public AstNodeFTask {
// A sequence inside a module
// TODO when supported might not want to be a NodeFTask
bool m_referenced = false; // Ever referenced (for unsupported check)
bool m_referenced = false; // Set by V3LinkResolve when referenced; cleared by
// V3AssertPre after inlining; if still set after
// V3AssertPre, the reference is in an unsupported context
public:
AstSequence(FileLine* fl, const string& name, AstNode* stmtp)
: ASTGEN_SUPER_Sequence(fl, name, stmtp) {}
+71 -28
View File
@@ -97,18 +97,25 @@ class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
// op3 used by some sub-types only
// @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly
string m_name; // Name to report
const VAssertType m_type; // Assertion/cover type
const VAssertType m_userType; // Assertion/cover type for user enable/disable
const VAssertDirectiveType m_directive; // Assertion directive type
bool m_senFromAlways = false; // Sensitivity list copied from upper always
bool m_immediate = false; // Immediate assert (may differ from userType being immediate)
public:
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type,
VAssertDirectiveType directive, const string& name = "")
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp,
VAssertType userType, VAssertDirectiveType directive,
const string& name = "")
: AstNodeStmt{t, fl}
, m_name{name}
, m_type{type}
, m_userType{userType}
, m_directive{directive} {
this->propp(propp);
addPasssp(passsp);
m_immediate = m_userType.containsAny(VAssertType::SIMPLE_IMMEDIATE
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|| m_userType == VAssertType::INTERNAL;
}
ASTGEN_MEMBERS_AstNodeCoverOrAssert;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
@@ -116,14 +123,13 @@ public:
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
VAssertType type() const VL_MT_SAFE { return m_type; }
string type() { return ""; }
VAssertType userType() const VL_MT_SAFE { return m_userType; }
VAssertDirectiveType directive() const { return m_directive; }
bool immediate() const {
return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|| this->type() == VAssertType::INTERNAL;
}
bool immediate() const { return m_immediate; }
void immediate(bool flag) { m_immediate = flag; }
bool senFromAlways() const VL_MT_STABLE { return m_senFromAlways; }
void senFromAlways(bool flag) { m_senFromAlways = flag; }
};
class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := headerp : AstForeachHeader
@@ -305,6 +311,7 @@ public:
class AstCStmt final : public AstNodeStmt {
// C statement emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode<AstNodeStmt|AstNodeExpr|AstText>]
const VCStmtType m_stmtType; // Special statement (instead of comparing name())
static AstCStmt* profExecSection(FileLine* flp, const std::string& section, bool push) {
// Compute the label
@@ -331,8 +338,10 @@ class AstCStmt final : public AstNodeStmt {
}
public:
explicit AstCStmt(FileLine* fl, const std::string& text = "")
: ASTGEN_SUPER_CStmt(fl) {
explicit AstCStmt(FileLine* fl, const std::string& text = "",
const VCStmtType& stmtType = VCStmtType::NONE)
: ASTGEN_SUPER_CStmt(fl)
, m_stmtType{stmtType} {
if (!text.empty()) add(text);
}
ASTGEN_MEMBERS_AstCStmt;
@@ -341,6 +350,9 @@ public:
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool sameNode(const AstNode*) const override { return true; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VCStmtType stmtType() const { return m_stmtType; }
// Add some text, or a node to this statement
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
@@ -529,11 +541,12 @@ public:
};
class AstDelay final : public AstNodeStmt {
// Delay statement
// @astgen op1 := lhsp : AstNodeExpr // Delay value
// @astgen op1 := lhsp : AstNodeExpr // Delay value (or min for range)
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max bound for cycle range or fall delay
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
VTimescale m_timeunit; // Delay's time unit
const bool m_isCycle; // True if it is a cycle delay
public:
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
: ASTGEN_SUPER_Delay(fl)
@@ -548,6 +561,10 @@ public:
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
bool isCycleDelay() const { return m_isCycle; }
bool isRangeDelay() const { return m_isCycle && rhsp() != nullptr; }
bool isUnbounded() const { return isRangeDelay() && VN_IS(rhsp(), Unbounded); }
void fallDelay(AstNodeExpr* const fallDelayp) { rhsp(fallDelayp); }
AstNodeExpr* fallDelay() const { return m_isCycle ? nullptr : rhsp(); }
};
class AstDisable final : public AstNodeStmt {
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
@@ -589,7 +606,10 @@ public:
char missingArgChar = 'd')
: ASTGEN_SUPER_Display(fl)
, m_displayType{dispType} {
fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar});
AstSFormatF* const newp
= new AstSFormatF{fl, AstSFormatF::ExprFormat{}, exprsp, missingArgChar};
newp->optionalFormat(true);
fmtp(newp);
this->filep(filep);
}
ASTGEN_MEMBERS_AstDisplay;
@@ -1098,15 +1118,13 @@ class AstSFormat final : public AstNodeStmt {
// @astgen op1 := fmtp : AstSFormatF
// @astgen op2 := lhsp : AstNodeExpr
public:
AstSFormat(FileLine* fl, AstNodeExpr* lhsp, const string& text, AstNodeExpr* exprsp,
AstSFormat(FileLine* fl, bool optionalFormat, AstNodeExpr* lhsp, AstNodeExpr* exprsp,
char missingArgChar = 'd')
: ASTGEN_SUPER_SFormat(fl) {
fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar});
this->lhsp(lhsp);
}
AstSFormat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* exprsp, char missingArgChar = 'd')
: ASTGEN_SUPER_SFormat(fl) {
fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar});
AstSFormatF* const newp
= new AstSFormatF{fl, AstSFormatF::ExprFormat{}, exprsp, missingArgChar};
newp->optionalFormat(optionalFormat);
fmtp(newp);
this->lhsp(lhsp);
}
ASTGEN_MEMBERS_AstSFormat;
@@ -1249,6 +1267,9 @@ class AstTraceDecl final : public AstNodeStmt {
// Parents: {statement list}
// Expression being traced - Moved to AstTraceInc by V3Trace
// @astgen op1 := valuep : Optional[AstNodeExpr]
//
// @astgen ptr := m_dtypeCallp: Optional[AstCCall] // Type init function call
// @astgen ptr := m_dtypeDeclp: Optional[AstTraceDecl] // CCall TraceDecl which replaces this
uint32_t m_code{std::numeric_limits<uint32_t>::max()}; // Trace identifier code
uint32_t m_fidx{0}; // Trace function index
const string m_showname; // Name of variable
@@ -1256,18 +1277,23 @@ class AstTraceDecl final : public AstNodeStmt {
const VNumRange m_arrayRange; // Property of var the trace details
const VVarType m_varType; // Type of variable (for localparam vs. param)
const VDirection m_declDirection; // Declared direction input/output etc
const bool m_inDtypeFunc; // Trace decl inside type init function
int m_codeInc{0}; // Code increment for type
public:
AstTraceDecl(FileLine* fl, const string& showname,
AstVar* varp, // For input/output state etc
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange)
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange,
AstCCall* const dtypeCallp, const bool inDtypeFunc)
: ASTGEN_SUPER_TraceDecl(fl)
, m_showname{showname}
, m_bitRange{bitRange}
, m_arrayRange{arrayRange}
, m_varType{varp->varType()}
, m_declDirection{varp->declDirection()} {
, m_declDirection{varp->declDirection()}
, m_inDtypeFunc{inDtypeFunc} {
dtypeFrom(valuep);
this->valuep(valuep);
this->dtypeCallp(dtypeCallp);
}
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
@@ -1276,7 +1302,7 @@ public:
string name() const override VL_MT_STABLE { return m_showname; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
bool hasDType() const override VL_MT_SAFE { return true; }
bool sameNode(const AstNode* samep) const override { return false; }
bool sameNode(const AstNode* samep) const override { return true; }
string showname() const { return m_showname; } // * = Var name
// Details on what we're tracing
uint32_t code() const { return m_code; }
@@ -1284,7 +1310,9 @@ public:
bool codeAssigned() const { return m_code != std::numeric_limits<uint32_t>::max(); }
uint32_t fidx() const { return m_fidx; }
void fidx(uint32_t fidx) { m_fidx = fidx; }
void codeInc(uint32_t codeInc) { m_codeInc = codeInc; }
uint32_t codeInc() const {
if (m_codeInc) { return m_codeInc; }
return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1)
* valuep()->dtypep()->widthWords()
* (VL_EDATASIZE / 32); // A code is always 32-bits
@@ -1293,6 +1321,11 @@ public:
const VNumRange& arrayRange() const { return m_arrayRange; }
VVarType varType() const { return m_varType; }
VDirection declDirection() const { return m_declDirection; }
AstCCall* dtypeCallp() const { return m_dtypeCallp; }
void dtypeCallp(AstCCall* const callp) { m_dtypeCallp = callp; }
AstTraceDecl* dtypeDeclp() const { return m_dtypeDeclp; }
void dtypeDeclp(AstTraceDecl* const declp) { m_dtypeDeclp = declp; }
bool inDtypeFunc() const { return m_inDtypeFunc; }
};
class AstTraceInc final : public AstNodeStmt {
// Trace point dump
@@ -1339,15 +1372,25 @@ public:
class AstTracePushPrefix final : public AstNodeStmt {
const string m_prefix; // Prefix to add to signal names
const VTracePrefixType m_prefixType; // Type of prefix being pushed
const int m_left; // Array left index, or struct/union member count
const int m_right; // Array right index
const bool m_quotedPrefix; // Quote prefix name
public:
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType)
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType,
int left = 0, int right = 0, bool quotedPrefix = true)
: ASTGEN_SUPER_TracePushPrefix(fl)
, m_prefix{prefix}
, m_prefixType{prefixType} {}
, m_prefixType{prefixType}
, m_left{left}
, m_right{right}
, m_quotedPrefix{quotedPrefix} {}
ASTGEN_MEMBERS_AstTracePushPrefix;
bool sameNode(const AstNode* samep) const override { return false; }
string prefix() const { return m_prefix; }
VTracePrefixType prefixType() const { return m_prefixType; }
int left() const { return m_left; }
int right() const { return m_right; }
bool quotedPrefix() const { return m_quotedPrefix; }
};
class AstWait final : public AstNodeStmt {
// @astgen op1 := condp : AstNodeExpr
+153 -27
View File
@@ -192,7 +192,7 @@ void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int
AstRange* rangep) {
// wantwidth=0 means figure it out, but if a widthmin is >=0
// we allow width 0 so that {{0{x}},y} works properly
// wantwidthmin=-1: default, use wantwidth if it is non zero
// wantwidthmin=-1: default, use wantwidth if it is non-zero
m.m_keyword = kwd;
// Implicitness: // "parameter X" is implicit and sized from initial
// value, "parameter reg x" not
@@ -448,6 +448,14 @@ void AstConsDynArray::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
}
void AstSConsRep::dump(std::ostream& str) const { // LCOV_EXCL_START
this->AstNodeExpr::dump(str);
if (unbounded()) str << " [unbounded]";
}
void AstSConsRep::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, unbounded);
dumpJsonGen(str);
} // LCOV_EXCL_STOP
void AstConsQueue::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (lhsIsValue()) str << " [LVAL]";
@@ -614,7 +622,7 @@ string AstVar::verilogKwd() const {
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
bool asRef) const {
bool asRef, bool constRef) const {
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string ostatic;
@@ -622,7 +630,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
if (forFunc && isReadOnly() && asRef) ostatic = ostatic + "const ";
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
string oname;
if (named) {
@@ -682,6 +690,12 @@ string AstVar::vlEnumDir() const {
if (const AstBasicDType* const bdtypep = basicp()) {
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
}
//
if (dtypep()->skipRefp()->isSigned()) out += "|VLVF_SIGNED";
//
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
}
return out;
}
@@ -1007,6 +1021,11 @@ bool AstNodeDType::similarDType(const AstNodeDType* samep) const {
bool AstNodeDType::isFourstate() const { return basicp() && basicp()->isFourstate(); }
bool AstNodeDType::isNonPackedArray() const {
return VN_IS(this, UnpackArrayDType) || VN_IS(this, DynArrayDType) || VN_IS(this, QueueDType)
|| VN_IS(this, AssocArrayDType);
}
class AstNodeDType::CTypeRecursed final {
public:
string m_type; // The base type, e.g.: "Foo_t"s
@@ -1791,6 +1810,26 @@ string AstBasicDType::prettyDTypeName(bool) const {
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
bool AstNodeExpr::isLValue() const {
if (const AstNodeVarRef* const varrefp = VN_CAST(this, NodeVarRef)) {
return varrefp->access().isWriteOrRW();
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
return memberselp->access().isWriteOrRW();
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
return selp->fromp()->isLValue();
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
return nodeSelp->fromp()->isLValue();
} else if (const AstConcat* const concatp = VN_CAST(this, Concat)) {
// Enough to check only one side, as both must be same otherwise malformed
return concatp->lhsp()->isLValue();
} else if (const AstCMethodHard* const cMethodHardp = VN_CAST(this, CMethodHard)) {
// Used for things like Queue/AssocArray/DynArray
return cMethodHardp->fromp()->isLValue();
}
return false;
}
void AstNodeUniop::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); }
void AstNodeUniop::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -1849,6 +1888,14 @@ void AstCellInlineScope::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origModName);
dumpJsonGen(str);
}
void AstCExpr::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (m_pure) str << " [PURE]";
}
void AstCExpr::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "pure", m_pure);
dumpJsonGen(str);
}
bool AstClass::isCacheableChild(const AstNode* nodep) {
return VN_IS(nodep, Var) || VN_IS(nodep, Typedef)
|| (VN_IS(nodep, Constraint) && !VN_AS(nodep, Constraint)->isExternProto())
@@ -1901,9 +1948,8 @@ AstClass* AstClassExtends::classOrNullp() const {
if (refp && !refp->paramsp()) {
// Class already resolved
return refp->classp();
} else {
return nullptr;
}
return nullptr;
}
AstClass* AstClassExtends::classp() const {
AstClass* const clsp = classOrNullp();
@@ -1927,13 +1973,39 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
}
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
bool AstClassRefDType::similarDTypeNode(const AstNodeDType* samep) const {
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
if (m_classp != asamep->m_classp) return false;
// Compare type parameters so C#(int) != C#(string)
const AstPin* lp = paramsp();
const AstPin* rp = asamep->paramsp();
while (lp && rp) {
if (!lp->exprp() != !rp->exprp()) return false;
if (lp->exprp()) {
const AstNodeDType* const lDtp = VN_CAST(lp->exprp(), NodeDType);
const AstNodeDType* const rDtp = VN_CAST(rp->exprp(), NodeDType);
if (lDtp && rDtp) {
if (!lDtp->similarDType(rDtp)) return false;
} else {
if (!lp->exprp()->sameTree(rp->exprp())) return false;
}
}
lp = VN_CAST(lp->nextp(), Pin);
rp = VN_CAST(rp->nextp(), Pin);
}
return !lp && !rp;
}
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " ["s + this->type().ascii() + "]";
str << " ["s + this->userType().ascii() + "]";
if (immediate()) str << " [IMMEDIATE]";
if (senFromAlways()) str << " [SENALW]";
}
void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "type", "["s + this->type().ascii() + "]");
dumpJsonStr(str, "type", "["s + this->userType().ascii() + "]");
dumpJsonGen(str);
dumpJsonBoolFuncIf(str, immediate);
dumpJsonBoolFuncIf(str, senFromAlways);
}
void AstClocking::dump(std::ostream& str) const {
this->AstNode::dump(str);
@@ -1981,7 +2053,7 @@ void AstEnumDType::dump(std::ostream& str) const {
str << " enum";
}
void AstEnumDType::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "enum", 1);
dumpJsonBoolIf(str, "enum", true);
dumpJsonGen(str);
}
void AstEnumDType::dumpSmall(std::ostream& str) const {
@@ -2114,11 +2186,8 @@ void AstInitArray::addIndexValuep(uint64_t index, AstNodeExpr* newp) {
}
AstNodeExpr* AstInitArray::getIndexValuep(uint64_t index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return nullptr;
} else {
return it->second->valuep();
}
if (it == m_map.end()) { return nullptr; }
return it->second->valuep();
}
AstNodeExpr* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
AstNodeExpr* valuep = getIndexValuep(index);
@@ -2274,12 +2343,14 @@ void AstPin::dump(std::ostream& str) const {
} else {
str << " ->UNLINKED";
}
if (!paramPath().empty()) str << " paramPath=" << paramPath();
if (svDotName()) str << " [.n]";
if (svImplicit()) str << " [.SV]";
}
void AstPin::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, svDotName);
dumpJsonBoolFuncIf(str, svImplicit);
if (!paramPath().empty()) dumpJsonStr(str, "paramPath", paramPath());
dumpJsonGen(str);
}
string AstPin::prettyOperatorName() const {
@@ -2453,6 +2524,16 @@ bool AstUnionDType::sameNode(const AstNode* samep) const {
const AstUnionDType* const asamep = VN_DBG_AS(samep, UnionDType);
return m_isSoft == asamep->m_isSoft && m_isTagged == asamep->m_isTagged;
}
void AstUntil::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (isStrong()) str << " [strong]";
if (isOverlapping()) str << " [overlapping]";
}
void AstUntil::dumpJson(std::ostream& str) const {
this->AstNodeExpr::dumpJson(str);
dumpJsonBoolFuncIf(str, isStrong);
dumpJsonBoolFuncIf(str, isOverlapping);
}
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
string result = verilogKwd() + "{";
if (full) { // else shorten for errors
@@ -2475,7 +2556,7 @@ void AstNodeDType::dump(std::ostream& str) const {
}
void AstNodeDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, generic);
if (isSigned() && !isDouble()) dumpJsonBoolIf(str, "signed", 1);
if (isSigned() && !isDouble()) dumpJsonBoolIf(str, "signed", true);
dumpJsonGen(str);
}
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
@@ -2595,12 +2676,14 @@ void AstNodeModule::dump(std::ostream& str) const {
str << " D" << depth();
if (modPublic()) str << " [P]";
if (inLibrary()) str << " [LIB]";
if (ctorVarReset()) str << " [CVRESET]";
if (dead()) str << " [DEAD]";
if (recursiveClone()) {
str << " [RECURSIVE-CLONE]";
} else if (recursive()) {
str << " [RECURSIVE]";
}
if (parameterizedTemplate()) str << " [PAR-TEMPL]";
if (verilatorLib()) str << " [VERILATOR-LIB]";
str << " [" << timeunit() << "]";
if (libname() != "work") str << " libname=" << libname();
@@ -2611,6 +2694,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
dumpJsonNumFunc(str, level);
dumpJsonBoolFuncIf(str, modPublic);
dumpJsonBoolFuncIf(str, inLibrary);
dumpJsonBoolFuncIf(str, ctorVarReset);
dumpJsonBoolFuncIf(str, dead);
dumpJsonBoolFuncIf(str, recursiveClone);
dumpJsonBoolFuncIf(str, recursive);
@@ -2652,6 +2736,35 @@ void AstPatMember::dumpJson(std::ostream& str) const {
}
void AstNodeTriop::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); }
void AstNodeTriop::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstSFormatArg::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " [" << formatAttr().ascii() << "]";
}
void AstSFormatArg::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
dumpJsonStr(str, "formatAttr", std::string{formatAttr().ascii()});
}
VFormatAttr AstSFormatArg::formatAttrDefauled(const AstSFormatArg* nodep,
const AstNodeDType* dtypep) {
if (nodep) return nodep->formatAttr();
// Used to initially assign the formatArg
// Later, V3Randomize creates raw %s's without SFormatArg's that have string arguments
if (!dtypep) return VFormatAttr{};
const AstNodeDType* skipDtypep = dtypep->skipRefp();
if (skipDtypep->isDouble()) return VFormatAttr{VFormatAttr::DOUBLE};
if (skipDtypep->isString()) return VFormatAttr{VFormatAttr::STRING};
return VFormatAttr{};
}
void AstSFormatF::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (exprFormat()) str << " [EXPRFMT]";
if (optionalFormat()) str << " [OPTFMT]";
}
void AstSFormatF::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
dumpJsonBoolFuncIf(str, exprFormat);
dumpJsonBoolFuncIf(str, optionalFormat);
}
void AstSel::dump(std::ostream& str) const {
this->AstNodeBiop::dump(str);
str << " widthConst=" << this->widthConst();
@@ -2880,12 +2993,16 @@ void AstVar::dump(std::ostream& str) const {
if (isSigPublic()) str << " [P]";
if (isSigUserRdPublic()) str << " [PRD]";
if (isSigUserRWPublic()) str << " [PWR]";
if (isReadByDpi()) str << " [DPIRD]";
if (isWrittenByDpi()) str << " [DPIWR]";
if (isInternal()) str << " [INTERNAL]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOPIDX]";
if (rand().isRandomizable()) str << " [" << rand() << "]";
if (noCReset()) str << " [!CRST]";
if (noReset()) str << " [!RST]";
if (processQueue()) str << " [PROCQ]";
if (sampled()) str << " [SAMPLED]";
if (attrIsolateAssign()) str << " [aISO]";
if (attrFileDescr()) str << " [aFD]";
if (isFuncReturn()) {
@@ -2916,6 +3033,8 @@ void AstVar::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isUsedLoopIdx);
dumpJsonBoolFuncIf(str, noCReset);
dumpJsonBoolFuncIf(str, noReset);
dumpJsonBoolFuncIf(str, processQueue);
dumpJsonBoolFuncIf(str, sampled);
dumpJsonBoolFuncIf(str, attrIsolateAssign);
dumpJsonBoolFuncIf(str, attrFileDescr);
dumpJsonBoolFuncIf(str, isDpiOpenArray);
@@ -2927,6 +3046,8 @@ void AstVar::dumpJson(std::ostream& str) const {
if (dtypep()) dumpJsonStr(str, "dtypeName", dtypep()->name());
dumpJsonBoolFuncIf(str, isSigUserRdPublic);
dumpJsonBoolFuncIf(str, isSigUserRWPublic);
dumpJsonBoolFuncIf(str, isReadByDpi);
dumpJsonBoolFuncIf(str, isWrittenByDpi);
dumpJsonBoolFuncIf(str, isGParam);
dumpJsonBoolFuncIf(str, isParam);
dumpJsonBoolFuncIf(str, attrScBv);
@@ -3039,6 +3160,7 @@ void AstActive::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstNodeFTaskRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
if (containsGenBlock()) str << " [GENBLK]";
str << " -> ";
if (dotted() != "") str << ".=" << dotted() << " ";
if (taskp()) {
@@ -3049,6 +3171,7 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
}
void AstNodeFTaskRef::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, dotted);
dumpJsonBoolFuncIf(str, containsGenBlock);
dumpJsonGen(str);
}
void AstNodeFTask::dump(std::ostream& str) const {
@@ -3204,7 +3327,8 @@ void AstStop::dumpJson(std::ostream& str) const {
}
void AstTraceDecl::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (code()) str << " [code=" << code() << "]";
if (codeAssigned()) str << " [code=" << code() << "]";
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
}
void AstTraceDecl::dumpJson(std::ostream& str) const {
dumpJsonNumFunc(str, code);
@@ -3326,6 +3450,14 @@ void AstCMethodHard::setPurity() {
}
}
void AstCStmt::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (!stmtType().isNone()) str << " [" << stmtType().ascii() << "]";
}
void AstCStmt::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
if (!stmtType().isNone()) dumpJsonStr(str, "stmtType", stmtType().ascii());
}
void AstCUse::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << useType() << "]";
@@ -3346,18 +3478,12 @@ static AstDelay* getLhsNetDelayRecurse(const AstNodeExpr* const nodep) {
AstDelay* AstAssignW::getLhsNetDelay() const { return getLhsNetDelayRecurse(lhsp()); }
string AstCase::pragmaString() const {
if (fullPragma() && parallelPragma())
return "synthesis full_case parallel_case";
else if (fullPragma())
return "synthesis full_case";
else if (parallelPragma())
return "synthesis parallel_case";
else if (uniquePragma())
return "unique case";
else if (unique0Pragma())
return "unique0 case";
else if (priorityPragma())
return "priority case";
if (fullPragma() && parallelPragma()) return "synthesis full_case parallel_case";
if (fullPragma()) return "synthesis full_case";
if (parallelPragma()) return "synthesis parallel_case";
if (uniquePragma()) return "unique case";
if (unique0Pragma()) return "unique0 case";
if (priorityPragma()) return "priority case";
return "";
}
+114 -42
View File
@@ -499,8 +499,9 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
}
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
static AstNode* createForeachLoop(AstNodeForeach* /*nodep*/, AstNode* bodysp, bool arrayMayResize,
AstNodeExpr* subfromp, AstVar* varp, AstNodeExpr* leftp,
AstNodeExpr* rightp, VNType nodeType) {
FileLine* const fl = varp->fileline();
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
AstNodeExpr* condp;
@@ -522,32 +523,128 @@ static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar
AstLoop* const loopp = new AstLoop{fl};
loopp->addStmtsp(new AstLoopTest{fl, loopp, condp});
AstVar* sizeVarp = nullptr;
AstNodeExpr* sizeGetp = nullptr;
if (arrayMayResize) {
sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vloopsize",
varp->findUInt32DType()};
sizeVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
sizeVarp->usedLoopIdx(true); // Not technically an index, but used only inside loop
varp->addNext(sizeVarp);
sizeGetp
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::DYN_SIZE, nullptr};
sizeGetp->dtypeSetUInt32();
loopp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, sizeVarp, VAccess::WRITE}, sizeGetp});
}
loopp->addStmtsp(bodysp);
loopp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp});
AstNodeStmt* incStmtp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp};
if (arrayMayResize) {
incStmtp = new AstIf{fl,
new AstLte{fl, new AstVarRef{fl, sizeVarp, VAccess::READ},
sizeGetp->cloneTreePure(false)},
incStmtp};
}
loopp->addStmtsp(incStmtp);
AstNode* const stmtsp = varp; // New statements for outer loop
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
stmtsp->addNext(loopp);
return stmtsp;
}
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp,
AstNodeExpr* subfromp, AstVar* varp,
const VNumRange& declRange) {
FileLine* const fl = varp->fileline();
V3Number left{nodep, 32}, right{nodep, 32};
V3Number left{nodep, 32};
V3Number right{nodep, 32};
left.isSigned(true);
right.isSigned(true);
left.setLongS(declRange.left());
right.setLongS(declRange.right());
AstNodeExpr* const leftp = new AstConst{fl, left};
AstNodeExpr* const rightp = new AstConst{fl, right};
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
return createForeachLoop(nodep, bodysp, false /*fixed array size*/, subfromp, varp, leftp,
rightp,
declRange.left() <= declRange.right() ? VNType::LteS : VNType::GteS);
}
static AstNode* createForeachAssoc(FileLine* fl, AstVar* varp, AstNodeExpr* subfromp,
AstNodeDType* fromDtp, AstNode* bodyPointp) {
AstNode* loopp = varp;
AstVar* const next_varp // Iterator containing next element (to handle mid-array delete)
= new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vnext", varp};
next_varp->usedLoopIdx(true);
next_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
loopp->addNext(next_varp);
AstVar* const more_varp // bool var. 0 = loop empty/done, 1 = continue with loop
= new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vmore", VFlagBitPacked{}, 1};
more_varp->usedLoopIdx(true); // Not technically an index, but used only inside loop
more_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
loopp->addNext(more_varp);
AstNodeExpr* const firstp
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_FIRST,
new AstVarRef{fl, next_varp, VAccess::READWRITE}};
firstp->dtypeSetSigned32();
AstNodeExpr* const nextp
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_NEXT,
new AstVarRef{fl, next_varp, VAccess::READWRITE}};
nextp->dtypeSetSigned32();
// _Vmore = array.first(__Vnext)
loopp->addNext(new AstAssign{fl, new AstVarRef{fl, more_varp, VAccess::WRITE},
new AstNeq{fl, new AstConst{fl, 0}, firstp}});
// LOOP(if (!_Vmore) break; ...)
AstLoop* const lp = new AstLoop{fl};
loopp->addNext(lp);
lp->addStmtsp(new AstLoopTest{fl, lp, new AstVarRef{fl, more_varp, VAccess::READ}});
// index = __Vnext
lp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstVarRef{fl, next_varp, VAccess::READ}});
// _Vmore = array.next(__Vnext)
lp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, more_varp, VAccess::WRITE},
new AstNeq{fl, new AstConst{fl, 0}, nextp}});
lp->addStmtsp(bodyPointp);
return loopp;
}
static bool arrayMayResizeCheck(AstNode* nodesp, AstVar* fromVarp) {
// IEEE 1800-2023 12.7.3 "If the dimensions of a dynamically sized
// array are changed while iterating over a foreach-loop construct, th
// results are undefined". However UVM calls delete within a foreach
// in the uvm_reg code and in the tests, and expects the foreach to see
// all elements post-delete.
//
// Cannot just check body for delete() because the body may call a
// function that does a delete. Cannot easily have the verilated_types
// code set a flag on delete, because might be multiple foreach in
// flight. Could use generation counter and each loop checks, but adds
// time to every array modification. So, we assume if the array
// shrinks, we should repeat the loop.
if (!nodesp) return false;
return nodesp->existsAndNext([&](const AstNode* nodep) -> bool {
if (const AstNodeVarRef* varrefp = VN_CAST(nodep, NodeVarRef)) {
// Any variable written, as might have indirect ref to array
if (varrefp->access().isWriteOrRW()) {
if (!fromVarp) return true;
if (varrefp->varp() == fromVarp) return true;
}
}
// No idea what a function might be doing
if (VN_IS(nodep, MethodCall) || VN_IS(nodep, NodeFTaskRef)) return true;
return false;
});
}
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
// UINFOTREE(1, nodep, "", "foreach-old");
const AstForeachHeader* const headerp = nodep->headerp();
AstNodeExpr* const fromp = headerp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstVar* const fromVarp = VN_IS(fromp, VarRef) ? VN_CAST(fromp, VarRef)->varp() : nullptr;
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
const bool arrayMayResize = arrayMayResizeCheck(nodep->bodyp(), fromVarp);
// Split into for loop
// We record where the body needs to eventually go with bodyPointp
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false};
@@ -573,15 +670,18 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
VNRelinker handle;
lastp->unlinkFrBack(&handle);
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
loopp = createForeachLoopRanged(nodep, bodyPointp, subfromp, varp,
adtypep->declRange());
} else if (const AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
if (adtypep->isString()) {
AstConst* const leftp = new AstConst{fl, 0};
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::Lt);
loopp = createForeachLoop(nodep, bodyPointp, arrayMayResize, subfromp, varp,
leftp, rightp, VNType::Lt);
} else {
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
loopp = createForeachLoopRanged(nodep, bodyPointp, subfromp, varp,
adtypep->declRange());
}
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
AstConst* const leftp = new AstConst{fl, 0};
@@ -593,50 +693,22 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
: subfromp->cloneTreePure(false),
VCMethod::DYN_SIZE};
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
subfromp->dtypep(fromDtp);
rightp->dtypeSetSigned32();
rightp->protect(false);
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::Lt);
loopp = createForeachLoop(nodep, bodyPointp, arrayMayResize, subfromp, varp, leftp,
rightp, VNType::Lt);
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
subfromp->dtypep(fromDtp);
} else if (VN_IS(fromDtp, AssocArrayDType)) {
// Make this: var KEY_TYPE index;
// bit index__Vfirst;
// index__Vfirst = 0;
// if (0 != array.first(index))
// do body while (index__Vfirst || 0 != array.next(index))
AstVar* const first_varp = new AstVar{
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
first_varp->usedLoopIdx(true);
first_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstNodeExpr* const firstp
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_FIRST,
new AstVarRef{fl, varp, VAccess::READWRITE}};
firstp->dtypeSetSigned32();
AstNodeExpr* const nextp
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_NEXT,
new AstVarRef{fl, varp, VAccess::READWRITE}};
nextp->dtypeSetSigned32();
loopp = createForeachAssoc(fl, varp, subfromp, fromDtp, bodyPointp);
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
subfromp->dtypep(fromDtp);
AstNode* const first_clearp
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}};
AstLogOr* const orp
= new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
AstLoop* const lp = new AstLoop{fl};
lp->addStmtsp(new AstLoopTest{fl, lp, orp});
lp->addStmtsp(first_clearp);
first_clearp->addNext(bodyPointp);
AstNode* const ifbodyp
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitTrue{}}};
ifbodyp->addNext(lp);
loopp = varp;
loopp->addNext(first_varp);
loopp->addNext(
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
}
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
// New loop goes UNDER previous loop
+3 -5
View File
@@ -71,7 +71,7 @@ public:
void addNewed(const AstNode* nodep) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Called by operator new on any node - only if VL_LEAK_CHECKS
// LCOV_EXCL_START
V3LockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
}
@@ -80,7 +80,7 @@ public:
void deleted(const AstNode* nodep) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
// LCOV_EXCL_START
V3LockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
}
@@ -190,8 +190,6 @@ private:
}
if (v3Global.assertDTypesResolved()) {
if (nodep->hasDType()) {
UASSERT_OBJ(nodep->dtypep(), nodep,
"No dtype on node with hasDType(): " << nodep->prettyTypeName());
} else {
UASSERT_OBJ(!VN_IS(nodep, NodeExpr), nodep,
"All AstNodeExpr must have a dtype post V3WidthCommit");
@@ -379,7 +377,7 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
} else {
s_inBroken = true;
V3LockGuard lock{s_allocTable.m_mutex};
const V3LockGuard lock{s_allocTable.m_mutex};
// Mark every node in the tree
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
+28 -5
View File
@@ -65,6 +65,7 @@ class V3CCtorsBuilder final {
funcp->keepIfEmpty(true); // TODO relax
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
if (!m_type.isCoverage()) m_modp->ctorVarReset(true);
string preventUnusedStmt;
if (m_type.isClass()) {
funcp->argTypes(EmitCUtil::symClassVar());
@@ -83,6 +84,7 @@ class V3CCtorsBuilder final {
public:
void add(AstNode* nodep) {
if (m_newFunctions.empty()) m_newFunctions.push_back(makeNewFunc());
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
m_newFunctions.push_back(makeNewFunc());
}
@@ -94,13 +96,13 @@ public:
: m_modp{nodep}
, m_basename{basename}
, m_type{type} {
// Note: The constructor is always called, even if empty, so we must always create at least
// one.
m_newFunctions.push_back(makeNewFunc());
// Expect coverage function to always exist, so must always create at least one.
if (m_type.isCoverage()) m_newFunctions.push_back(makeNewFunc());
}
~V3CCtorsBuilder() {
if (m_newFunctions.size() == 1) {
if (m_newFunctions.size() == 0) {
} else if (m_newFunctions.size() == 1) {
// No split was necessary, rename the one function to the basename
m_newFunctions.front()->name(m_basename);
} else {
@@ -136,6 +138,7 @@ class CCtorsVisitor final : public VNVisitor {
AstNodeModule* m_modp = nullptr; // Current module
AstCFunc* m_cfuncp = nullptr; // Current function
V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset
std::map<AstCStmt*, const AstNodeModule*> m_ctorCalls; // Calls to _ctor_var_reset
// METHODS
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
@@ -149,6 +152,10 @@ class CCtorsVisitor final : public VNVisitor {
// VISITORS
void visit(AstNodeModule* nodep) override {
if (const AstClass* const classp = VN_CAST(nodep, Class)) {
// Interface class may only have pure virtuals and params which do not need cctor reset
if (classp->isInterfaceClass()) return;
}
VL_RESTORER(m_modp);
VL_RESTORER(m_varResetp);
m_modp = nodep;
@@ -190,6 +197,13 @@ class CCtorsVisitor final : public VNVisitor {
iterateChildren(nodep);
if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR);
}
void visit(AstCStmt* nodep) override {
if (nodep->stmtType() == VCStmtType::CTOR_VAR_RESET_CALL) {
UASSERT_OBJ(m_modp, nodep, "ctor_var_reset call not under module");
m_ctorCalls.emplace(nodep, m_modp);
}
iterateChildren(nodep);
}
void visit(AstVar* nodep) override {
if (nodep->needsCReset()) {
AstNode* const crstp = new AstAssign{
@@ -199,6 +213,8 @@ class CCtorsVisitor final : public VNVisitor {
m_varResetp->add(crstp);
} else if (m_cfuncp) {
nodep->addNextHere(crstp);
} else {
nodep->v3fatalSrc("Var needs CReset but nowhere to place it");
}
}
}
@@ -209,7 +225,14 @@ class CCtorsVisitor final : public VNVisitor {
public:
// CONSTRUCTORS
explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
~CCtorsVisitor() override = default;
~CCtorsVisitor() override {
// Remove CStmts to ctor_var_resets that are no longer needed
for (auto& itr : m_ctorCalls) {
AstCStmt* const nodep = itr.first;
const AstNodeModule* const modp = itr.second;
if (!modp->ctorVarReset()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
}
};
//######################################################################
+1 -1
View File
@@ -59,7 +59,7 @@ class CUseVisitor final : public VNVisitorConst {
iterateAndNextConstNull(nodep->argsp());
iterateAndNextConstNull(nodep->stmtsp());
}
void visit(AstCCall* nodep) override { return; }
void visit(AstCCall* /*nodep*/) override {}
void visit(AstCReturn* nodep) override {
UASSERT_OBJ(!nodep->user1SetOnce(), nodep, "Visited same return twice");
iterateConst(nodep->lhsp()->dtypep());
+2 -1
View File
@@ -82,7 +82,8 @@ class CastVisitor final : public VNVisitor {
}
void ensureCast(AstNodeExpr* nodep) {
if (castSize(nodep->backp()) != castSize(nodep) || !nodep->user1()) {
if (!nodep->isNull()) insertCast(nodep, castSize(nodep->backp()));
if (!nodep->isNull() && !nodep->isString() && !nodep->isDouble())
insertCast(nodep, castSize(nodep->backp()));
}
}
// cppcheck-suppress constParameterPointer // lhsp might be changed
+2 -1
View File
@@ -18,7 +18,7 @@
// Each module:
// For each expression, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node.
// Resize operands to C++ 32/64/wide types.
// Resize operands (but not variables or variable selects) to C++ 32/64/wide types.
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
//
//*************************************************************************
@@ -83,6 +83,7 @@ class CleanVisitor final : public VNVisitor {
if (VN_IS(nodep, Var) //
|| VN_IS(nodep, ConsPackMember) //
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep, NodeSel) // Array selects should reflect variable widths
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
+1 -1
View File
@@ -100,7 +100,7 @@ class ClockVisitor final : public VNVisitor {
void visit(AstVarScope* nodep) override {
AstVar* const varp = nodep->varp();
if (!varp->valuep()) return;
if (!VString::startsWith(varp->name(), "__Vsampled")) return;
if (!varp->sampled()) return;
// Create the containing function on first encounter
if (!m_sampleCFuncp) {
+235 -83
View File
@@ -106,6 +106,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
// CONSTRUCTORS
LeafInfo() = default;
LeafInfo(const LeafInfo& other) = default;
LeafInfo& operator=(const LeafInfo& other) = default;
LeafInfo(LeafInfo&& other) = default;
LeafInfo& operator=(LeafInfo&& other) = default;
explicit LeafInfo(int lsb)
: m_lsb{lsb} {}
@@ -194,7 +197,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
const size_t m_frozenSize;
const unsigned m_ops;
const bool m_polarity;
bool m_restore;
bool m_restore = true;
public:
explicit Restorer(ConstBitOpTreeVisitor& visitor)
@@ -202,8 +205,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
, m_polaritiesSize{visitor.m_bitPolarities.size()}
, m_frozenSize{visitor.m_frozenNodes.size()}
, m_ops{visitor.m_ops}
, m_polarity{visitor.m_polarity}
, m_restore{true} {}
, m_polarity{visitor.m_polarity} {}
~Restorer() {
UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize,
"m_bitPolarities must grow monotonically");
@@ -1348,7 +1350,7 @@ class ConstVisitor final : public VNVisitor {
if (newp) {
newp->dumpTree(debugPrefix + "RESULT: ");
} else {
cout << debugPrefix << "not replaced" << endl;
cout << debugPrefix << "not replaced\n";
}
} // LCOV_EXCL_STOP
@@ -1525,7 +1527,7 @@ class ConstVisitor final : public VNVisitor {
nodep->rhsp(smallerp);
constp->unlinkFrBack();
V3Number num{constp, subsize, constp->num()};
const V3Number num{constp, subsize, constp->num()};
nodep->lhsp(new AstConst{constp->fileline(), num});
VL_DO_DANGLING(pushDeletep(constp), constp);
UINFOTREE(9, nodep, "", "BI(EXTEND)-ou");
@@ -1596,7 +1598,13 @@ class ConstVisitor final : public VNVisitor {
static bool operandsSame(const AstNode* node1p, const AstNode* node2p) {
// For now we just detect constants & simple vars, though it could be more generic
if (VN_IS(node1p, Const) && VN_IS(node2p, Const)) return node1p->sameGateTree(node2p);
if (const AstConst* const const1p = VN_CAST(node1p, Const)) {
if (const AstConst* const const2p = VN_CAST(node2p, Const)) {
return V3Number{node1p->fileline(), 1, 0}
.opEq(const1p->num(), const2p->num())
.isEqOne();
}
}
if (VN_IS(node1p, VarRef) && VN_IS(node2p, VarRef)) {
// Avoid comparing widthMin's, which results in lost optimization attempts
// If cleanup sameGateTree to be smarter, this can be restored.
@@ -1622,8 +1630,15 @@ class ConstVisitor final : public VNVisitor {
if (!thensp->lhsp()->sameGateTree(elsesp->lhsp())) return false;
if (!thensp->rhsp()->gateTree()) return false;
if (!elsesp->rhsp()->gateTree()) return false;
// Must not create an expression with unpacked array type
if (VN_IS(thensp->rhsp()->dtypep()->skipRefp(), UnpackArrayDType)) return false;
if (VN_IS(elsesp->rhsp()->dtypep()->skipRefp(), UnpackArrayDType)) return false;
if (m_underRecFunc) return false; // This optimization may lead to infinite recursion
return true;
// Only do it if not calls and both pure, otherwise undoes V3LiftExpr
return !VN_IS(thensp->rhsp(), NodeFTaskRef) //
&& !VN_IS(elsesp->rhsp(), NodeFTaskRef) //
&& thensp->rhsp()->isPure() //
&& elsesp->rhsp()->isPure();
}
bool operandIfIf(const AstNodeIf* nodep) {
if (nodep->elsesp()) return false;
@@ -1728,7 +1743,7 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(oldp), oldp);
}
void replaceNum(AstNode* nodep, uint32_t val) {
V3Number num{nodep, nodep->width(), val};
const V3Number num{nodep, nodep->width(), val};
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceNumSigned(AstNodeBiop* nodep, uint32_t val) {
@@ -2222,7 +2237,7 @@ class ConstVisitor final : public VNVisitor {
}
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
bool need_temp = false;
bool need_temp_pure = !nodep->rhsp()->isPure();
const bool need_temp_pure = !nodep->rhsp()->isPure();
if (m_warn && !VN_IS(nodep, AssignDly)
&& !need_temp_pure) { // Is same var on LHS and RHS?
// Note only do this (need user4) when m_warn, which is
@@ -2382,6 +2397,32 @@ class ConstVisitor final : public VNVisitor {
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
// CvtArrayToArray (StreamL reverses, so reverse=true)
if ((VN_IS(srcDTypep, UnpackArrayDType) || VN_IS(srcDTypep, QueueDType)
|| VN_IS(srcDTypep, DynArrayDType))
&& (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType))) {
int blockSize = 1;
if (const AstConst* const constp
= VN_CAST(VN_AS(streamp, StreamL)->rhsp(), Const)) {
blockSize = constp->toSInt();
if (VL_UNLIKELY(blockSize <= 0)) blockSize = 1;
}
int srcElementBits = 0;
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
srcElementBits = elemDtp->width();
}
int dstElementBits = 0;
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
dstElementBits = elemDtp->width();
}
nodep->lhsp(dstp);
nodep->rhsp(new AstCvtArrayToArray{srcp->fileline(), srcp, dstDTypep, true,
blockSize, dstElementBits, srcElementBits});
nodep->dtypep(dstDTypep);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
return true;
}
const int sWidth = srcp->width();
const int dWidth = dstp->width();
// Connect the rhs to the stream operator and update its width
@@ -2413,6 +2454,44 @@ class ConstVisitor final : public VNVisitor {
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
// CvtArrayToArray (StreamR does not reverse, so reverse=false).
// V3Width may have wrapped the source in CvtArrayToPacked; unwrap it.
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
AstNodeExpr* origSrcp = srcp;
if (AstCvtArrayToPacked* const cvtp = VN_CAST(srcp, CvtArrayToPacked)) {
origSrcp = cvtp->fromp();
}
const AstNodeDType* const origSrcDTypep = origSrcp->dtypep()->skipRefp();
if (VN_IS(origSrcDTypep, UnpackArrayDType) || VN_IS(origSrcDTypep, QueueDType)
|| VN_IS(origSrcDTypep, DynArrayDType)) {
int srcElementBits = 0;
if (const AstNodeDType* const elemDtp = origSrcDTypep->subDTypep()) {
srcElementBits = elemDtp->width();
}
int dstElementBits = 0;
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
dstElementBits = elemDtp->width();
}
if (VN_IS(srcp, CvtArrayToPacked)) {
origSrcp = VN_AS(srcp, CvtArrayToPacked)->fromp()->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(srcp), srcp);
srcp = origSrcp;
}
// Descending unpacked arrays need element reversal
bool reverse = false;
if (const AstUnpackArrayDType* const unpackDtp
= VN_CAST(origSrcDTypep, UnpackArrayDType)) {
reverse = !unpackDtp->declRange().ascending();
}
nodep->lhsp(dstp);
nodep->rhsp(new AstCvtArrayToArray{srcp->fileline(), srcp, dstDTypep, reverse,
1, dstElementBits, srcElementBits});
nodep->dtypep(dstDTypep);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
return true;
}
}
const int sWidth = srcp->width();
const int dWidth = dstp->width();
if (VN_IS(dstDTypep, UnpackArrayDType)) {
@@ -2427,12 +2506,20 @@ class ConstVisitor final : public VNVisitor {
}
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstDTypep};
} else {
UASSERT_OBJ(sWidth >= dWidth, nodep,
"sWidth >= dWidth should have caused an error earlier");
if (dWidth == 0) {
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstDTypep};
} else if (sWidth >= dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
} else {
// Source narrower than destination: left-justify by shifting left.
// The right stream operator packs left-to-right, so remaining
// LSBs are zero-filled (IEEE 1800-2023 11.4.14.2).
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
srcp = new AstShiftL{
srcp->fileline(), extendp,
new AstConst{srcp->fileline(), static_cast<uint32_t>(dWidth - sWidth)},
dWidth};
}
}
nodep->lhsp(dstp);
@@ -2660,12 +2747,28 @@ class ConstVisitor final : public VNVisitor {
iterateChildren(nodep);
if (const AstInitArray* const initp = VN_CAST(nodep->lhsp(), InitArray)) {
if (!(m_doExpensive || m_params)) return false;
// At present only support 1D unpacked arrays
const auto initOfConst = [](const AstNode* const nodep) -> bool { //
return VN_IS(nodep, Const) || VN_IS(nodep, InitItem);
const auto isConstInit
= [](const AstNode* const exprp, const auto& isConstInitRecurse) -> bool {
if (VN_IS(exprp, Const)) return true;
if (const AstInitItem* const itemp = VN_CAST(exprp, InitItem)) {
return isConstInitRecurse(itemp->valuep(), isConstInitRecurse);
}
if (const AstInitArray* const arrayp = VN_CAST(exprp, InitArray)) {
const auto itemIsConstInit
= [&isConstInitRecurse](const AstNode* const itemp) -> bool {
return isConstInitRecurse(itemp, isConstInitRecurse);
};
if (arrayp->initsp() && !arrayp->initsp()->forall(itemIsConstInit))
return false;
if (arrayp->defaultp()
&& !isConstInitRecurse(arrayp->defaultp(), isConstInitRecurse)) {
return false;
}
return true;
}
return false;
};
if (initp->initsp() && !initp->initsp()->forall(initOfConst)) return false;
if (initp->defaultp() && !initp->defaultp()->forall(initOfConst)) return false;
if (!isConstInit(initp, isConstInit)) return false;
} else if (!VN_IS(nodep->lhsp(), Const)) {
return false;
}
@@ -2890,7 +2993,7 @@ class ConstVisitor final : public VNVisitor {
void replaceSelIntoBiop(AstSel* nodep) {
// SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit))
AstNodeBiop* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeBiop);
UASSERT_OBJ(fromp, nodep, "Called on non biop");
UASSERT_OBJ(fromp, nodep, "Called on non-biop");
AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack();
//
AstNodeExpr* const bilhsp = fromp->lhsp()->unlinkFrBack();
@@ -2905,7 +3008,7 @@ class ConstVisitor final : public VNVisitor {
void replaceSelIntoUniop(AstSel* nodep) {
// SEL(NOT(a),1,bit) => NOT(SEL(a,bit))
AstNodeUniop* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeUniop);
UASSERT_OBJ(fromp, nodep, "Called on non biop");
UASSERT_OBJ(fromp, nodep, "Called on non-biop");
AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack();
//
AstNodeExpr* const bilhsp = fromp->lhsp()->unlinkFrBack();
@@ -2940,6 +3043,16 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
// Handle ARRAYSEL directly on InitArray (not through VarRef)
else if (VN_IS(nodep->bitp(), Const) && VN_IS(nodep->fromp(), InitArray)) {
const AstInitArray* const initarp = VN_AS(nodep->fromp(), InitArray);
const uint32_t bit = VN_AS(nodep->bitp(), Const)->toUInt();
const AstNode* const itemp = initarp->getIndexDefaultedValuep(bit);
if (VN_IS(itemp, Const)) {
const V3Number& num = VN_AS(itemp, Const)->num();
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
}
m_selp = nullptr;
}
@@ -3343,58 +3456,54 @@ class ConstVisitor final : public VNVisitor {
void visit(AstCvtArrayToArray* nodep) override {
iterateChildren(nodep);
// Handle the case where we have a stream operation inside a cast conversion
// To avoid infinite recursion, mark the node as processed by setting user1.
if (!nodep->user1()) {
nodep->user1(true);
// Check for both StreamL and StreamR operations
AstNodeStream* streamp = nullptr;
bool isReverse = false;
if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) {
streamp = streamLp;
isReverse = true; // StreamL reverses the operation
} else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) {
streamp = streamRp;
isReverse = false; // StreamR doesn't reverse the operation
}
if (streamp) {
AstNodeExpr* srcp = streamp->lhsp();
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp();
if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) {
int blockSize = 1;
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
blockSize = constp->toSInt();
if (VL_UNLIKELY(blockSize <= 0)) {
// Not reachable due to higher level checks when parsing stream
// operators commented out to not fail v3error-coverage-checks.
// nodep->v3error("Stream block size must be positive, got " <<
// blockSize);
blockSize = 1;
}
// Check for both StreamL and StreamR operations
AstNodeStream* streamp = nullptr;
bool isReverse = false;
if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) {
streamp = streamLp;
isReverse = true; // StreamL reverses the operation
} else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) {
streamp = streamRp;
isReverse = false; // StreamR doesn't reverse the operation
}
if (streamp) {
AstNodeExpr* srcp = streamp->lhsp();
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp();
if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) {
int blockSize = 1;
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
blockSize = constp->toSInt();
if (VL_UNLIKELY(blockSize <= 0)) {
// Not reachable due to higher level checks when parsing stream
// operators commented out to not fail v3error-coverage-checks.
// nodep->v3error("Stream block size must be positive, got " <<
// blockSize);
blockSize = 1;
}
// Not reachable due to higher level checks when parsing stream operators
// commented out to not fail v3error-coverage-checks.
// else {
// nodep->v3error("Stream block size must be constant (got " <<
// streamp->rhsp()->prettyTypeName() << ")");
// }
int srcElementBits = 0;
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
srcElementBits = elemDtp->width();
}
int dstElementBits = 0;
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
dstElementBits = elemDtp->width();
}
streamp->unlinkFrBack();
AstNodeExpr* newp = new AstCvtArrayToArray{
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse,
blockSize, dstElementBits, srcElementBits};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
// Not reachable due to higher level checks when parsing stream operators
// commented out to not fail v3error-coverage-checks.
// else {
// nodep->v3error("Stream block size must be constant (got " <<
// streamp->rhsp()->prettyTypeName() << ")");
// }
int srcElementBits = 0;
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
srcElementBits = elemDtp->width();
}
int dstElementBits = 0;
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
dstElementBits = elemDtp->width();
}
streamp->unlinkFrBack();
AstNodeExpr* newp = new AstCvtArrayToArray{
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse,
blockSize, dstElementBits, srcElementBits};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
}
@@ -3577,26 +3686,43 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return true;
}
void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); }
void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.
iterateChildren(nodep);
if (nodep->exprFormat()) {
// Upconvert to text-based format?
// Similar code in V3LinkResolve::visit(AstSFormatF)
UASSERT_OBJ(nodep->text() == "", nodep,
"Non-format $sformatf should have text format == \"\"");
if (VN_IS(nodep->exprsp(), Const)
&& VN_AS(nodep->exprsp(), Const)->num().isFromString()) {
AstConst* const fmtp = VN_AS(nodep->exprsp()->unlinkFrBack(), Const);
nodep->text(fmtp->num().toString());
nodep->exprFormat(false);
VL_DO_DANGLING(pushDeletep(fmtp), fmtp);
}
}
if (nodep->exprFormat()) return; // Runtime, unfortunately (unless constify format later)
bool anyconst = false;
for (AstNode* argp = nodep->exprsp(); argp; argp = argp->nextp()) {
if (VN_IS(argp, Const)) {
AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg);
AstNode* const subargp = fargp ? fargp->exprp() : argp;
if (VN_IS(subargp, Const)) {
anyconst = true;
break;
}
}
if (m_doNConst && anyconst) {
// UINFO(9, " Display in " << nodep->text());
UINFO(9, " Display in " << nodep->text());
string newFormat;
string fmt;
bool inPct = false;
AstNode* argp = nodep->exprsp();
const string text = nodep->text();
for (const char ch : text) {
for (char ch : text) {
if (!inPct && ch == '%') {
inPct = true;
fmt = ch;
@@ -3605,22 +3731,30 @@ class ConstVisitor final : public VNVisitor {
} else if (inPct) {
inPct = false;
fmt += ch;
switch (std::tolower(ch)) {
ch = std::tolower(ch);
switch (ch) {
case '%': break; // %% - still %%
case 'm': break; // %m - still %m - auto insert "name"
case 'l': break; // %l - still %l - auto insert "library"
case 't': // FALLTHRU
case '^': // %t/%^ - don't know $timeformat so can't constify
case 't': // %t - don't know $timeformat so can't constify
if (argp) argp = argp->nextp();
break;
default: // Most operators, just move to next argument
if (argp) {
AstNode* const nextp = argp->nextp();
if (VN_IS(argp, Const)) { // Convert it
const string out = constNumV(argp).displayed(nodep, fmt);
AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg);
AstNode* const subargp = fargp ? fargp->exprp() : argp;
const VFormatAttr formatAttr
= argp
? AstSFormatArg::formatAttrDefauled(fargp, subargp->dtypep())
: VFormatAttr{};
if (VN_IS(subargp, Const)) { // Convert it
const string out
= constNumV(subargp).displayed(nodep, fmt, formatAttr);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< argp);
// fmt = out w/ replace % with %% as it must be literal.
<< subargp);
// fmt = out w/ replace % with %% as it must later when
// used as a format output as literal.
fmt = VString::quotePercent(out);
VL_DO_DANGLING(pushDeletep(argp->unlinkFrBack()), argp);
}
@@ -3638,9 +3772,20 @@ class ConstVisitor final : public VNVisitor {
UINFO(9, " Display out " << nodep);
}
}
if (!nodep->exprsp() && nodep->name().find('%') == string::npos && !nodep->hidden()) {
if (!nodep->exprsp() && nodep->text().find('%') == string::npos && !nodep->hidden()) {
// Just a simple constant string - the formatting is pointless
VL_DO_DANGLING(replaceConstString(nodep, nodep->name()), nodep);
UINFO(9, "replaceConstStr");
VL_DO_DANGLING(replaceConstString(nodep, nodep->text()), nodep);
return;
}
if (VN_IS(nodep->backp(), NodeAssign) // Can't remove under Display etc
&& nodep->text() == "%s" && VN_IS(nodep->exprsp(), SFormatArg)
&& VN_AS(nodep->exprsp(), SFormatArg)->formatAttr().isString()
&& !nodep->exprsp()->nextp()) {
UINFO(9, " Display(%p, expr) -> constant expr " << nodep);
nodep->replaceWith(VN_AS(nodep->exprsp(), SFormatArg)->exprp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
void visit(AstNodeFTask* nodep) override {
@@ -3674,7 +3819,7 @@ class ConstVisitor final : public VNVisitor {
m_hasJumpDelay = false;
m_hasLoopTest = false;
iterateChildren(nodep);
bool thisLoopHasJumpDelay = m_hasJumpDelay;
const bool thisLoopHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = thisLoopHasJumpDelay || oldHasJumpDelay;
// If the first statement always break, the loop is useless
if (const AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
@@ -3848,7 +3993,7 @@ class ConstVisitor final : public VNVisitor {
// In the future maybe support more complicated match & replace:
// ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete");
// Lhs/rhs would be implied; for non math operations you'd need $lhsp etc.
// Lhs/rhs would be implied; for non-math operations you'd need $lhsp etc.
// v--- * * This op done on Verilog or C+++ mode, in all non-m_doConst stages
// v--- *1* These ops are always first, as we warn before replacing
@@ -4172,7 +4317,14 @@ class ConstVisitor final : public VNVisitor {
if (m_required) {
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)
|| VN_IS(nodep, Dot)) {
// Ignore dtypes for parameter type pins
// ignore
} else if (AstCellRef* const crp = VN_CAST(nodep, CellRef)) {
iterate(crp->exprp());
if (AstNode* const newp = crp->exprp()) {
crp->replaceWithKeepDType(newp->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(crp), crp);
}
return;
} else {
nodep->v3error("Expecting expression to be constant, but can't convert a "
<< nodep->prettyTypeName() << " to constant.");
+65
View File
@@ -0,0 +1,65 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Generic container types
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3CONTAINER_H_
#define VERILATOR_V3CONTAINER_H_
#include "config_build.h"
#include "verilatedos.h"
#include <unordered_map>
#include <unordered_set>
#include <vector>
//============================================================================
// Similar to std::set, but ordered based on call order to emplace. Used
// when insertion order is desired (e.g. std::vector), but duplicates need removal.
// Keys may not be modified. (If needed in future, m_set could contain vector positions.)
template <typename T_Key>
class VInsertionSet final {
std::vector<T_Key> m_keys; // Elements by insertion order
std::unordered_set<T_Key> m_keySet; // Elements by key
public:
// METHODS
bool insert(const T_Key& key) {
// Returns if did insertion (second pair argument of traditional emplace)
const auto itFoundPair = m_keySet.insert(key);
if (itFoundPair.second) m_keys.push_back(key);
return itFoundPair.second;
}
void clear() {
m_keys.clear();
m_keySet.clear();
}
// ACCESSORS
bool empty() const { return m_keys.empty(); }
bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); }
// ITERATORS
using const_iterator = typename std::vector<T_Key>::const_iterator;
const_iterator begin() const { return m_keys.begin(); }
const_iterator end() const { return m_keys.end(); }
};
//============================================================================
// VInsertionMap: Not currently needed; prototype code exists, just ask.
// Similar to std::map, but ordered based on call order to emplace. Used
// when insertion order is desired (e.g. std::vector), but duplicates need removal.
// Values may be modified.
#endif // Guard
+41 -45
View File
@@ -49,8 +49,8 @@ public:
// Update this resolved file's item map by inserting other's (wildcarded filename's) items
void update(const V3ControlWildcardResolver& other) VL_MT_SAFE_EXCLUDES(m_mutex)
VL_EXCLUDES(other.m_mutex) {
V3LockGuard lock{m_mutex};
V3LockGuard otherLock{other.m_mutex};
const V3LockGuard lock{m_mutex};
const V3LockGuard otherLock{other.m_mutex};
// Clear the resolved cache, as 'other' might add new patterns that need to be applied as
// well.
m_mapResolved.clear();
@@ -59,14 +59,14 @@ public:
// Access and create a pattern entry
T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
V3LockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
// We might be adding a new entry under this, so clear the cache.
m_mapResolved.clear();
return m_mapPatterns[name];
}
// Access an entity and resolve patterns that match it
T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
V3LockGuard lock{m_mutex};
const V3LockGuard lock{m_mutex};
// Lookup if it was resolved before, typically not
const auto pair = m_mapResolved.emplace(name, nullptr);
std::unique_ptr<T>& entryr = pair.first->second;
@@ -369,10 +369,11 @@ private:
maxval = std::max(maxval, cign.m_lineMax);
}
int center = minval + (maxval - minval) / 2;
const int center = minval + (maxval - minval) / 2;
auto entp = std::unique_ptr<Entry>(new Entry{center});
IgnIndices leftPoints, rightPoints;
IgnIndices leftPoints;
IgnIndices rightPoints;
for (const auto& it : points) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
if (cign.everyLine()) continue;
@@ -438,7 +439,7 @@ private:
public:
// CONSTRUCTORS
VIntervalTree() {}
VIntervalTree() = default;
~VIntervalTree() = default;
// METHODS
void clear() { m_rootp = nullptr; }
@@ -477,41 +478,41 @@ public:
IgnIndices results;
int nextChange = 0;
tree.find(0, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 0);
UASSERT_SELFTEST(int, nextChange, 10);
UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, 10);
tree.find(10, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 0);
UASSERT_SELFTEST(int, results[1], 3);
UASSERT_SELFTEST(int, nextChange, 11);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 0);
UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(const int, nextChange, 11);
tree.find(11, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 1);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, nextChange, 15); // Center, or would be 20
UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, nextChange, 15); // Center, or would be 20
tree.find(20, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 3);
UASSERT_SELFTEST(int, results[0], 1);
UASSERT_SELFTEST(int, results[1], 3);
UASSERT_SELFTEST(int, results[2], 4);
UASSERT_SELFTEST(int, nextChange, 21);
UASSERT_SELFTEST(const size_t, results.size(), 3);
UASSERT_SELFTEST(const int, results[0], 1);
UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(const int, results[2], 4);
UASSERT_SELFTEST(const int, nextChange, 21);
tree.find(21, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 25); // Center, or would be 30
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 25); // Center, or would be 30
tree.find(30, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 31);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 31);
tree.find(40, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 2);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 41);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 2);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 41);
tree.find(41, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 0);
UASSERT_SELFTEST(int, nextChange, std::numeric_limits<int>::max());
UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, std::numeric_limits<int>::max());
//
points = {{0, 0}};
for (const auto& it : points) {
@@ -522,8 +523,8 @@ public:
tree.build(data);
//
tree.find(50, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 1);
UASSERT_SELFTEST(int, results[0], 5);
UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 5);
}
};
@@ -541,12 +542,7 @@ public:
, m_contents{contents}
, m_match{match} {}
~WaiverSetting() = default;
WaiverSetting& operator=(const WaiverSetting& rhs) {
m_code = rhs.m_code;
m_contents = rhs.m_contents;
m_match = rhs.m_match;
return *this;
}
WaiverSetting& operator=(const WaiverSetting& rhs) = default;
};
// File entity
@@ -573,7 +569,7 @@ class V3ControlFile final {
}
public:
V3ControlFile() {}
V3ControlFile() = default;
void update(const V3ControlFile& file) {
// Copy in all attributes and waivers
for (const auto& itr : file.m_lineAttrs) m_lineAttrs[itr.first] |= itr.second;
@@ -1083,7 +1079,7 @@ bool V3Control::getScopeTraceOn(const string& scope) {
return V3ControlResolver::s().scopeTraces().getScopeTraceOn(scope);
}
void V3Control::contentsPushText(const string& text) { return WildcardContents::pushText(text); }
void V3Control::contentsPushText(const string& text) { WildcardContents::pushText(text); }
bool V3Control::containsMTaskProfileData() {
return V3ControlResolver::s().containsMTaskProfileData();
+8 -8
View File
@@ -78,7 +78,7 @@ public:
explicit ExprCoverageEligibleVisitor(AstNode* nodep) { iterateConst(nodep); }
~ExprCoverageEligibleVisitor() override = default;
bool eligible() { return m_eligible; }
bool eligible() const { return m_eligible; }
};
//######################################################################
@@ -252,7 +252,7 @@ class CoverageVisitor final : public VNVisitor {
const LinenoSet& lines = m_handleLines[state.m_handle];
int first = 0;
int last = 0;
for (int linen : lines) {
for (const int linen : lines) {
if (!first) {
first = last = linen;
} else if (linen == last + 1) {
@@ -898,8 +898,8 @@ class CoverageVisitor final : public VNVisitor {
CoverExprs rhsExprs;
m_exprs.swap(rhsExprs);
for (CoverExpr& l : lhsExprs) {
for (CoverExpr& r : rhsExprs) {
for (const CoverExpr& l : lhsExprs) {
for (const CoverExpr& r : rhsExprs) {
// array size 2 -> (false, true)
std::array<std::set<AstVar*>, 2> varps;
std::array<std::set<std::string>, 2> strs;
@@ -913,7 +913,7 @@ class CoverageVisitor final : public VNVisitor {
// Equivalent terms which don't match on either of these criteria will
// not be flagged as redundant or impossible, however the results will
// still be valid, albeit messier
for (CoverTerm& term : l) {
for (const CoverTerm& term : l) {
if (const AstVarRef* const refp = VN_CAST(term.m_exprp, VarRef)) {
varps[term.m_objective].insert(refp->varp());
} else {
@@ -922,7 +922,7 @@ class CoverageVisitor final : public VNVisitor {
expr.push_back(term);
}
bool impossible = false;
for (CoverTerm& term : r) {
for (const CoverTerm& term : r) {
bool redundant = false;
if (const AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) {
if (varps[term.m_objective].find(refp->varp())
@@ -1087,8 +1087,8 @@ class CoverageVisitor final : public VNVisitor {
void exprUnsupported(AstNode* nodep, const string& why) {
UINFO(9, "unsupported: " << why << " " << nodep);
bool wasSeeking = m_seeking == SEEKING;
Objective oldSeeking = m_seeking;
const bool wasSeeking = m_seeking == SEEKING;
const Objective oldSeeking = m_seeking;
if (wasSeeking) abortExprCoverage();
m_seeking = ABORTED;
iterateChildren(nodep);
+2 -1
View File
@@ -173,6 +173,7 @@ class DeadVisitor final : public VNVisitor {
}
void visit(AstNodeFTaskRef* nodep) override {
iterateChildren(nodep);
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
checkAll(nodep);
if (nodep->taskp()) nodep->taskp()->user1Inc();
if (nodep->classOrPackagep()) {
@@ -361,7 +362,7 @@ class DeadVisitor final : public VNVisitor {
checkAll(typedefp);
}
}
bool shouldDeleteTypedef(const AstTypedef* typedefp) {
bool shouldDeleteTypedef(const AstTypedef* typedefp) const {
if (const auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) {
if (structp->user1() && !structp->packed()) return false;
}
+50
View File
@@ -0,0 +1,50 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Bisection serach debugging utility
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VL_MT_DISABLED_CODE_UNIT
#error "V3DebugBisect.h uses global state and is not thread-safe"
#endif
#include "V3Os.h"
#include <string>
// Please see the inernals documentation for using this utility class with 'verilator_bisect'
class V3DebugBisect final {
// Name of instance
const char* const m_namep;
// Limit for stopping - 0 means no limit
const size_t m_limit = std::stoull(V3Os::getenvStr("VERILATOR_DEBUG_BISECT_"s + m_namep, "0"));
// Calls so far
size_t m_count = 0;
public:
explicit V3DebugBisect(const char* namep)
: m_namep{namep} {}
// Returns 'false' up to m_limit invocations, then returns 'true'.
// Calls 'f' on the last invocation that returns 'false', which can be used for reporting.
template <typename Callable>
bool stop(Callable&& f) {
if (VL_LIKELY(!m_limit)) return false;
++m_count;
if (VL_UNLIKELY(m_count == m_limit)) f();
return m_count > m_limit;
}
// Returns true if the limit has been reached
bool isStopped() const { return m_count > m_limit; }
};
+4 -4
View File
@@ -255,15 +255,15 @@ class DelayedVisitor final : public VNVisitor {
// AstVarScope::user1p() -> VarScopeInfo via m_vscpInfo
// AstVarScope::user2p() -> AstVarRef*: First write reference to the Variable
// AstVarScope::user3p() -> std::vector<WriteReference> via m_writeRefs;
const VNUser1InUse m_user1InUse{};
const VNUser2InUse m_user2InUse{};
const VNUser3InUse m_user3InUse{};
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
const VNUser3InUse m_user3InUse;
AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap;
AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo;
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
// STATE - across all visitors
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains
// STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate
+1
View File
@@ -85,6 +85,7 @@ class DepthVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstNodeStmt* nodep) override { visitStmt(nodep); }
void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); } // Don't split
// Operators
void visit(AstNodeTermop* nodep) override {}
void visit(AstNodeExpr* nodep) override {
+2 -2
View File
@@ -72,8 +72,8 @@ class DescopeVisitor final : public VNVisitor {
auto& ret = m_scopeToSelf[scopep];
if (ret.thisPtr.isEmpty()) {
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
string::size_type pos = name.rfind('.');
if (pos != string::npos) name.erase(0, pos + 1);
ret.thisPtr = VSelfPointerText{VSelfPointerText::This{}, name};
}
return ret.thisPtr;
+354 -72
View File
@@ -18,6 +18,7 @@
#include "V3Dfg.h"
#include "V3Ast.h"
#include "V3EmitV.h"
#include "V3File.h"
@@ -26,18 +27,16 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//------------------------------------------------------------------------------
// DfgGraph
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
: m_modulep{modulep}
, m_name{name} {}
DfgGraph::DfgGraph(const string& name)
: m_name{name} {}
DfgGraph::~DfgGraph() {
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
}
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
const bool scoped = !modulep();
DfgGraph* const clonep = new DfgGraph{modulep(), name()};
// Create the new graph
DfgGraph* const clonep = new DfgGraph{name()};
// Map from original vertex to clone
std::unordered_map<const DfgVertex*, DfgVertex*> vtxp2clonep(size() * 2);
@@ -54,20 +53,12 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
switch (vtx.type()) {
case VDfgType::VarArray: {
if (scoped) {
cp = new DfgVarArray{*clonep, vp->varScopep()};
} else {
cp = new DfgVarArray{*clonep, vp->varp()};
}
cp = new DfgVarArray{*clonep, vp->vscp()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::VarPacked: {
if (scoped) {
cp = new DfgVarPacked{*clonep, vp->varScopep()};
} else {
cp = new DfgVarPacked{*clonep, vp->varp()};
}
cp = new DfgVarPacked{*clonep, vp->vscp()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
@@ -78,8 +69,24 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
}
}
if (AstNode* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
if (AstVarScope* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
}
// Clone ast reference vertices
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
switch (vtx.type()) {
case VDfgType::AstRd: {
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
DfgAstRd* const cp = new DfgAstRd{*clonep, vp->exprp(), vp->inSenItem(), vp->inLoop()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
default: {
vtx.v3fatalSrc("Unhandled ast reference vertex type: " + vtx.typeName());
VL_UNREACHABLE;
break;
}
}
} // LCOV_EXCL_STOP
// Clone operation vertices
for (const DfgVertex& vtx : m_opVertices) {
switch (vtx.type()) {
@@ -136,6 +143,22 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
}
// Hook up inputs of cloned ast references
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
switch (vtx.type()) {
case VDfgType::AstRd: {
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
DfgAstRd* const cp = vtxp2clonep.at(&vtx)->as<DfgAstRd>();
if (const DfgVertex* const srcp = vp->srcp()) cp->srcp(vtxp2clonep.at(srcp));
break;
}
default: {
vtx.v3fatalSrc("Unhandled DfgVertexAst sub type: " + vtx.typeName());
VL_UNREACHABLE;
break;
}
}
} // LCOV_EXCL_STOP
// Hook up inputs of cloned operation vertices
for (const DfgVertex& vtx : m_opVertices) {
if (vtx.is<DfgVertexVariadic>()) {
@@ -171,18 +194,18 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
if (otherps.empty()) return;
// NODE STATE
// AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph
// AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph
const VNUser2InUse user2InUse;
// Set up Ast Variable -> DfgVertexVar map for 'this' graph
for (DfgVertexVar& vtx : m_varVertices) vtx.nodep()->user2p(&vtx);
for (DfgVertexVar& vtx : m_varVertices) vtx.vscp()->user2p(&vtx);
// Merge in each of the other graphs
for (const std::unique_ptr<DfgGraph>& otherp : otherps) {
// Process variables
for (DfgVertexVar* const vtxp : otherp->m_varVertices.unlinkable()) {
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'.
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
if (DfgVertexVar* const altp = vtxp->vscp()->user2u().to<DfgVertexVar*>()) {
DfgVertex* const srcp = vtxp->srcp();
DfgVertex* const defaultp = vtxp->defaultp();
UASSERT_OBJ(!(srcp || defaultp) || (!altp->srcp() && !altp->defaultp()), vtxp,
@@ -194,13 +217,21 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
continue;
}
// Otherwise they will be moved
vtxp->nodep()->user2p(vtxp);
vtxp->vscp()->user2p(vtxp);
vtxp->m_userGeneration = 0;
#ifdef VL_DEBUG
vtxp->m_dfgp = this;
#endif
}
m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices);
// Process Ast references
for (DfgVertexAst* const vtxp : otherp->m_astVertices.unlinkable()) {
vtxp->m_userGeneration = 0;
#ifdef VL_DEBUG
vtxp->m_dfgp = this;
#endif
}
m_astVertices.splice(m_astVertices.end(), otherp->m_astVertices);
// Process constants
for (DfgConst& vtx : otherp->m_constVertices) {
vtx.m_userGeneration = 0;
@@ -239,29 +270,17 @@ std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name,
const DfgDataType& dtype, AstScope* scopep) {
UASSERT_OBJ(!!scopep != !!modulep(), flp,
"makeNewVar scopep should only be provided for a scoped DfgGraph");
// Create AstVar
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtype.astDtypep()};
if (scopep) {
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
scopep->addVarsp(vscp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, vscp};
return new DfgVarPacked{*this, vscp};
} else {
// Add AstVar to containing module
modulep()->addStmtsp(varp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, varp};
return new DfgVarPacked{*this, varp};
}
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
scopep->addVarsp(vscp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, vscp};
return new DfgVarPacked{*this, vscp};
}
static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
@@ -269,20 +288,19 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
// Dump one DfgVertex in Graphviz format
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
const AstNode* const nodep = varVtxp->nodep();
const AstVar* const varp = varVtxp->varp();
const AstVarScope* const vscp = varVtxp->vscp();
os << toDotId(vtx);
// Begin attributes
os << " [";
// Begin 'label'
os << "label=\"";
// Name
os << nodep->prettyName();
os << vscp->prettyName();
// Address
os << '\n' << cvtToHex(varVtxp);
// Original variable, if any
if (const AstNode* const tmpForp = varVtxp->tmpForp()) {
if (tmpForp != nodep) os << "\ntemporary for: " << tmpForp->prettyName();
if (const AstVarScope* const tmpForp = varVtxp->tmpForp()) {
if (tmpForp != vscp) os << "\ntemporary for: " << tmpForp->prettyName();
}
// Type and fanout
os << '\n';
@@ -292,7 +310,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << " / ";
static const char* const rwmn[2][2] = {{"_", "W"}, {"R", "M"}};
os << rwmn[varVtxp->hasExtRdRefs()][varVtxp->hasExtWrRefs()];
os << rwmn[varVtxp->hasModRdRefs()][varVtxp->hasModWrRefs()];
os << rwmn[false][varVtxp->hasModWrRefs()];
os << (varVtxp->hasDfgRefs() ? "D" : "_");
// End 'label'
os << '"';
@@ -305,21 +323,13 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
varVtxp->v3fatalSrc("Unhandled DfgVertexVar sub-type");
}
// Color
if (varp->direction() == VDirection::INPUT) {
os << ", style=filled, fillcolor=chartreuse2"; // Green
} else if (varp->direction() == VDirection::OUTPUT) {
os << ", style=filled, fillcolor=cyan2"; // Cyan
} else if (varp->direction() == VDirection::INOUT) {
os << ", style=filled, fillcolor=darkorchid2"; // Purple
} else if (varVtxp->hasExtRefs()) {
os << ", style=filled, fillcolor=firebrick2"; // Red
} else if (varVtxp->hasModRefs()) {
os << ", style=filled, fillcolor=darkorange1"; // Orange
} else if (varVtxp->hasDfgRefs()) {
os << ", style=filled, fillcolor=gold2"; // Yellow
} else if (varVtxp->tmpForp()) {
os << ", style=filled, fillcolor=gray95";
}
const char* const colorp = varVtxp->hasExtRefs() ? "firebrick2" // Red
: varVtxp->hasModWrRefs() ? "darkorange1" // Orange
: varVtxp->hasDfgRefs() ? "gold2" // Yellow
: varVtxp->tmpForp() ? "gray95" // Gray
: "white";
os << ", style=filled";
os << ", fillcolor=\"" << colorp << "\"";
// End attributes
os << "]\n";
return;
@@ -379,7 +389,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << toDotId(vtx);
std::stringstream ss;
V3EmitV::debugVerilogForTree(logicp->nodep(), ss);
std::string str = ss.str();
std::string str = "AstNode: " + cvtToHex(logicp->nodep()) + "\n" + ss.str();
str = VString::quoteBackslash(str);
str = VString::quoteAny(str, '"', '\\');
str = VString::replaceSubstr(str, "\n", "\\l");
@@ -390,7 +400,32 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << " [label=\"";
os << str;
os << "\\n" << cvtToHex(&vtx);
os << '\n' << vtx.typeName() << '\n' << cvtToHex(&vtx);
os << "\"\n";
os << ", shape=box, style=\"rounded,filled\", nojustify=true";
os << ", fillcolor=\"" << colorp << "\"";
os << "]\n";
return;
}
if (const DfgVertexAst* const astVtxp = vtx.cast<DfgVertexAst>()) {
os << toDotId(vtx);
std::stringstream ss;
V3EmitV::debugVerilogForTree(astVtxp->exprp(), ss);
std::string str = "AstNode: " + cvtToHex(astVtxp->exprp()) + "\n" + ss.str() + "\n";
str = VString::quoteBackslash(str);
str = VString::quoteAny(str, '"', '\\');
str = VString::replaceSubstr(str, "\n", "\\l");
const DfgAstRd* const astRdVtxp = astVtxp->cast<DfgAstRd>();
const char* const colorp = astRdVtxp ? "#80ff80" // Green
: "#ffff80"; // Yellow
os << " [label=\"";
os << str;
os << '\n' << vtx.typeName() << '\n' << cvtToHex(&vtx) << '\n';
vtx.dtype().astDtypep()->dumpSmall(os);
os << " / ";
os << "_S"[astRdVtxp->inSenItem()];
os << "_L"[astRdVtxp->inLoop()];
os << "\"\n";
os << ", shape=box, style=\"rounded,filled\", nojustify=true";
os << ", fillcolor=\"" << colorp << "\"";
@@ -529,6 +564,34 @@ DfgGraph::sinkCone(const std::vector<const DfgVertex*>& vtxps) const {
return dfgGraphCollectCone<true>(vtxps);
}
std::unique_ptr<std::unordered_set<const DfgVertex*>>
DfgGraph::neighborhood(const std::vector<const DfgVertex*>& vtxps, size_t n) const {
// Neighborhood
std::vector<const DfgVertex*> vec = vtxps;
// Set of already visited vertices
std::unordered_set<const DfgVertex*> res{vec.begin(), vec.end()};
// Expand neihborhood by 'n' hops
size_t begin = 0;
size_t end = vec.size();
for (size_t hops = 1; hops <= n; ++hops) {
for (size_t i = begin; i < end; ++i) {
const DfgVertex* const vtxp = vec[i];
vtxp->foreachSink([&](const DfgVertex& dst) {
if (res.insert(&dst).second) vec.push_back(&dst);
return false;
});
vtxp->foreachSource([&](const DfgVertex& src) {
if (res.insert(&src).second) vec.push_back(&src);
return false;
});
}
begin = end;
end = vec.size();
}
// Move out the results
return std::make_unique<std::unordered_set<const DfgVertex*>>(std::move(res));
}
//------------------------------------------------------------------------------
// DfgVertex
@@ -539,6 +602,222 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataT
dfg.addVertex(*this);
}
void DfgVertex::typeCheck(const DfgGraph& dfg) const {
#define CHECK(cond, msg) \
UASSERT_OBJ(cond, this, \
"Dfg type error for vertex " << typeName() << " in " << dfg.name() << ": " \
<< msg);
switch (type()) {
case VDfgType::Const: {
CHECK(isPacked(), "Should be Packed type");
return;
}
case VDfgType::AstRd: {
const DfgAstRd& v = *as<DfgAstRd>();
CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type");
return;
}
case VDfgType::VarArray:
case VDfgType::VarPacked: {
const DfgVertexVar& v = *as<DfgVertexVar>();
CHECK(!v.defaultp() || v.defaultp()->dtype() == v.dtype(), "'defaultp' should match");
CHECK(!v.srcp() || v.srcp()->dtype() == v.dtype(), "'srcp' should match");
return;
}
case VDfgType::SpliceArray:
case VDfgType::SplicePacked: {
const DfgVertexSplice& v = *as<DfgVertexSplice>();
v.foreachDriver([&](const DfgVertex& src, uint32_t lo) {
CHECK(src.dtype() == DfgDataType::select(v.dtype(), lo, src.size()), "driver");
return false;
});
return;
}
case VDfgType::Logic: {
CHECK(dtype().isNull(), "Should be Null type");
return;
}
case VDfgType::Unresolved: {
CHECK(!dtype().isNull(), "Should not be Null type");
return;
}
case VDfgType::UnitArray: {
const DfgUnitArray& v = *as<DfgUnitArray>();
CHECK(v.isArray(), "Should be Array type");
CHECK(v.size() == 1, "Should be one element");
CHECK(v.srcp()->dtype() == v.dtype().elemDtype(), "Input should be the element type");
return;
}
case VDfgType::Sel: {
const DfgSel& v = *as<DfgSel>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.dtype() == DfgDataType::select(v.srcp()->dtype(), v.lsb(), v.size()), "sel");
return;
}
case VDfgType::Mux: {
const DfgMux& v = *as<DfgMux>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.fromp()->isPacked(), "Source operand should be Packed type");
CHECK(v.fromp()->size() >= v.size(), "Source operand should not be narrower");
CHECK(v.lsbp()->isPacked(), "Index should be Packed type");
return;
}
case VDfgType::ArraySel: {
const DfgArraySel& v = *as<DfgArraySel>();
CHECK(v.dtype() == v.fromp()->dtype().elemDtype(), "Element type should match");
CHECK(v.bitp()->isPacked(), "Index should be Packed type");
return;
}
case VDfgType::Add:
case VDfgType::And:
case VDfgType::BufIf1:
case VDfgType::Div:
case VDfgType::DivS:
case VDfgType::ModDiv:
case VDfgType::ModDivS:
case VDfgType::Mul:
case VDfgType::MulS:
case VDfgType::Or:
case VDfgType::Sub:
case VDfgType::Xor: {
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->dtype() == dtype(), "LHS should be same type");
CHECK(inputp(1)->dtype() == dtype(), "RHS should be same type");
return;
}
case VDfgType::Negate:
case VDfgType::Not: {
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->dtype() == dtype(), "Input should be same type");
return;
}
case VDfgType::ShiftL:
case VDfgType::ShiftR:
case VDfgType::ShiftRS: {
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->dtype() == dtype(), "LHS should be same type");
CHECK(inputp(1)->isPacked(), "RHS should be Packed type");
return;
}
case VDfgType::Concat: {
const DfgConcat& v = *as<DfgConcat>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.lhsp()->isPacked(), "LHS should be Packed type");
CHECK(v.rhsp()->isPacked(), "RHS should be Packed type");
CHECK(v.size() == v.rhsp()->size() + v.lhsp()->size(), "Concat result mismatch");
return;
}
case VDfgType::Replicate: {
// TODO: model DfgReplicate without an explicit 'countp' which is always constant
const DfgReplicate& v = *as<DfgReplicate>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.srcp()->isPacked(), "'srcp' should be same type");
CHECK(v.countp()->isPacked(), "'countp' should be Packed type");
CHECK(v.size() % v.srcp()->size() == 0, "Not a replicate");
return;
}
case VDfgType::StreamL:
case VDfgType::StreamR: {
// TODO: model these without an explicit slice size which is always constant (?)
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->dtype() == dtype(), "LHS should be same type");
CHECK(inputp(1)->isPacked(), "Slice size should be Packed type");
return;
}
case VDfgType::Eq:
case VDfgType::EqCase:
case VDfgType::EqWild:
case VDfgType::Neq:
case VDfgType::NeqCase:
case VDfgType::NeqWild:
case VDfgType::Gt:
case VDfgType::GtS:
case VDfgType::Gte:
case VDfgType::GteS:
case VDfgType::Lt:
case VDfgType::LtS:
case VDfgType::Lte:
case VDfgType::LteS: {
CHECK(dtype() == DfgDataType::packed(1), "Should be 1-bit");
CHECK(inputp(0)->dtype() == inputp(1)->dtype(), "Sides should match");
return;
}
case VDfgType::Extend:
case VDfgType::ExtendS: {
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->isPacked(), "Operand should be same type");
CHECK(inputp(0)->size() < size(), "Operand should be narrower");
return;
}
case VDfgType::SAnd:
case VDfgType::SIntersect:
case VDfgType::SOr:
case VDfgType::SThroughout: {
// LCOV_EXCL_START // Lowered before DFG
UASSERT_OBJ(false, this, "SAnd/SIntersect/SOr/SThroughout should be removed before DFG");
return;
// LCOV_EXCL_STOP
}
case VDfgType::LogAnd:
case VDfgType::LogEq:
case VDfgType::LogIf:
case VDfgType::LogOr: {
CHECK(dtype() == DfgDataType::packed(1), "Should be 1-bit");
CHECK(inputp(0)->isPacked(), "LHS should be Packed type");
CHECK(inputp(1)->isPacked(), "RHS should be Packed type");
return;
}
case VDfgType::LogNot:
case VDfgType::RedAnd:
case VDfgType::RedOr:
case VDfgType::RedXor: {
CHECK(dtype() == DfgDataType::packed(1), "Should be 1-bit");
CHECK(inputp(0)->isPacked(), "Operand should be Packed type");
return;
}
case VDfgType::Cond: {
const DfgCond& v = *as<DfgCond>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.condp()->isPacked(), "Condition should be Packed type");
CHECK(v.thenp()->dtype() == v.dtype(), "Then should be same type");
CHECK(v.elsep()->dtype() == v.dtype(), "Else should be same type");
return;
}
case VDfgType::CountOnes: {
const DfgCountOnes& v = *as<DfgCountOnes>();
CHECK(v.isPacked(), "Should be Packed type");
CHECK(v.srcp()->isPacked(), "Source should be Packed type");
CHECK(v.size() == 32U, "Should yield a 32-bit result");
return;
}
case VDfgType::Pow:
case VDfgType::PowSS:
case VDfgType::PowSU:
case VDfgType::PowUS: {
CHECK(isPacked(), "Should be Packed type");
CHECK(inputp(0)->dtype() == dtype(), "LHS should be same type");
CHECK(inputp(1)->isPacked(), "RHS should be Packed type");
return;
}
}
#undef CHECK
}
uint32_t DfgVertex::fanout() const {
uint32_t result = 0;
foreachSink([&](const DfgVertex&) {
@@ -577,16 +856,19 @@ DfgVertexVar* DfgVertex::getResultVar() {
if (!resp->hasDfgRefs()) resp = varp;
return false;
}
// Prefer those that already have module references
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
if (!resp->hasModRdRefs()) resp = varp;
// Prefer real variables over temporaries
const bool resIsTemp = resp->vscp()->varp()->isTemp();
const bool varIsTemp = varp->vscp()->varp()->isTemp();
if (resIsTemp != varIsTemp) {
if (resIsTemp) resp = varp;
return false;
}
// Prefer real variabels over temporaries
if (!resp->tmpForp() != !varp->tmpForp()) {
if (resp->tmpForp()) resp = varp;
return false;
}
// Prefer the earlier one in source order
const FileLine& oldFlp = *(resp->fileline());
const FileLine& newFlp = *(varp->fileline());
@@ -595,7 +877,7 @@ DfgVertexVar* DfgVertex::getResultVar() {
return false;
}
// Prefer the one with the lexically smaller name
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
if (const int cmp = resp->vscp()->name().compare(varp->vscp()->name())) {
if (cmp > 0) resp = varp;
return false;
}
@@ -608,13 +890,13 @@ DfgVertexVar* DfgVertex::getResultVar() {
AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED {
// If this is a variable, we are done
if (const DfgVertexVar* const varp = this->cast<DfgVertexVar>()) {
return varp->varScopep()->scopep();
return varp->vscp()->scopep();
}
// Try the result var first if instructed (usully only in the recursive case)
if (tryResultVar) {
if (const DfgVertexVar* const varp = this->getResultVar()) {
return varp->varScopep()->scopep();
return varp->vscp()->scopep();
}
}
+67 -50
View File
@@ -68,37 +68,6 @@ class DfgVisitor;
template <typename T_User, bool = fitsSpaceAllocatedFor<T_User, void*>()>
class DfgUserMap;
namespace V3Dfg {
//-----------------------------------------------------------------------
// Functions for compatibility tests
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVar* varp) {
if (varp->isIfaceRef()) return false; // Cannot handle interface references
if (varp->delayp()) return false; // Cannot handle delayed variables
if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore
if (varp->dfgMultidriven()) return false; // Discovered as multidriven on earlier DFG run
return DfgDataType::fromAst(varp->dtypep());
}
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVarScope* vscp) {
const AstNodeModule* const modp = vscp->scopep()->modp();
if (VN_IS(modp, Module)) {
// Regular module supported
} else if (const AstIface* const ifacep = VN_CAST(modp, Iface)) {
// Interfaces supported if there are no virtual interfaces for
// them, otherwise they cannot be resovled statically.
if (ifacep->hasVirtualRef()) return false;
} else {
return false; // Anything else (package, class, etc) not supported
}
// Check the AstVar
return isSupported(vscp->varp());
}
} //namespace V3Dfg
//------------------------------------------------------------------------------
// Dataflow graph vertex type enum
@@ -128,7 +97,6 @@ class DfgEdge final {
DfgVertex* const m_dstp; // The vertex driven by this edge, which owns this edge, so immutable
V3ListLinks<DfgEdge> m_links; // V3List links in the list of sinks of m_srcp
DfgEdge() = delete;
VL_UNCOPYABLE(DfgEdge);
VL_UNMOVABLE(DfgEdge);
@@ -139,6 +107,7 @@ public:
explicit DfgEdge(DfgVertex* dstp)
: m_dstp{dstp} {}
~DfgEdge() { unlinkSrcp(); }
DfgEdge() = delete;
// The source (driver) of this edge
DfgVertex* srcp() const { return m_srcp; }
@@ -200,8 +169,6 @@ protected:
m_inputps.emplace_back(new DfgEdge{this});
return m_inputps.back().get();
}
// Unlink all inputs and reset to no inputs
void resetInputs() { m_inputps.clear(); }
public:
// Get input 'i'
@@ -210,6 +177,8 @@ public:
void inputp(size_t i, DfgVertex* vtxp) { m_inputps[i]->relinkSrcp(vtxp); }
// The number of inputs this vertex has. Some might be unconnected.
size_t nInputs() const { return m_inputps.size(); }
// Unlink all inputs and reset to no inputs - use very carefully
void resetInputs() { m_inputps.clear(); }
// The type of this vertex
VDfgType type() const { return m_type; }
@@ -227,6 +196,9 @@ public:
return m_dtype.size();
}
// Type check vertex (for debugging)
void typeCheck(const DfgGraph& dfg) const;
// Predicate: has 1 or more sinks
bool hasSinks() const { return !m_sinks.empty(); }
@@ -410,12 +382,10 @@ class DfgGraph final {
// enables significant Verilation performance gains, so we keep these in separate lists for
// direct access.
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
DfgVertex::List<DfgVertexAst> m_astVertices; // The ast reference vertices in the graph
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
size_t m_size = 0; // Number of vertices in the graph
// Parent of the graph (i.e.: the module containing the logic represented by this graph),
// or nullptr when run after V3Scope
AstModule* const m_modulep;
const std::string m_name; // Name of graph - need not be unique
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
@@ -426,16 +396,13 @@ class DfgGraph final {
public:
// CONSTRUCTOR
explicit DfgGraph(AstModule* modulep, const string& name = "") VL_MT_DISABLED;
explicit DfgGraph(const string& name = "") VL_MT_DISABLED;
~DfgGraph() VL_MT_DISABLED;
VL_UNCOPYABLE(DfgGraph);
// METHODS
public:
// Number of vertices in this graph
size_t size() const { return m_size; }
// Parent module - or nullptr when run after V3Scope
AstModule* modulep() const { return m_modulep; }
// Name of this graph
const string& name() const { return m_name; }
@@ -446,6 +413,8 @@ public:
// Access to vertex lists
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
DfgVertex::List<DfgVertexAst>& astVertices() { return m_astVertices; }
const DfgVertex::List<DfgVertexAst>& astVertices() const { return m_astVertices; }
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
@@ -458,10 +427,12 @@ public:
#endif
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
++m_size;
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.linkBack(cVtxp);
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
m_varVertices.linkBack(vVtxp);
} else if (DfgVertexAst* const aVtxp = vtx.cast<DfgVertexAst>()) {
m_astVertices.linkBack(aVtxp);
} else if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.linkBack(cVtxp);
} else {
m_opVertices.linkBack(&vtx);
}
@@ -478,10 +449,12 @@ public:
#endif
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
--m_size;
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.unlink(cVtxp);
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
m_varVertices.unlink(vVtxp);
} else if (DfgVertexAst* const aVtxp = vtx.cast<DfgVertexAst>()) {
m_astVertices.unlink(aVtxp);
} else if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.unlink(cVtxp);
} else {
m_opVertices.unlink(&vtx);
}
@@ -496,6 +469,7 @@ public:
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
void forEachVertex(std::function<void(DfgVertex&)> f) {
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
for (DfgVertexAst* const vtxp : m_astVertices.unlinkable()) f(*vtxp);
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
}
@@ -503,6 +477,7 @@ public:
// 'const' variant of 'forEachVertex'. No mutation allowed.
void forEachVertex(std::function<void(const DfgVertex&)> f) const {
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
for (const DfgVertexAst& vtx : m_astVertices) f(vtx);
for (const DfgConst& vtx : m_constVertices) f(vtx);
for (const DfgVertex& vtx : m_opVertices) f(vtx);
}
@@ -569,8 +544,38 @@ public:
// Returns the set of vertices in the downstream cones of the given vertices
std::unique_ptr<std::unordered_set<const DfgVertex*>>
sinkCone(const std::vector<const DfgVertex*>&) const VL_MT_DISABLED;
// Returns the set of vertices within an 'n' hop neighborhood of the given vertices
std::unique_ptr<std::unordered_set<const DfgVertex*>>
neighborhood(const std::vector<const DfgVertex*>&, size_t n) const VL_MT_DISABLED;
};
namespace V3Dfg {
//-----------------------------------------------------------------------
// Functions for compatibility tests
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVarScope* vscp) {
const AstNodeModule* const modp = vscp->scopep()->modp();
if (VN_IS(modp, Module)) {
// Regular module supported
} else if (const AstIface* const ifacep = VN_CAST(modp, Iface)) {
// Interfaces supported if there are no virtual interfaces for
// them, otherwise they cannot be resovled statically.
if (ifacep->hasVirtualRef()) return false;
} else {
return false; // Anything else (package, class, etc) not supported
}
if (DfgVertexVar::hasRWRefs(vscp)) return false; // Referenced via READWRITE references
// Check the AstVar
AstVar* const varp = vscp->varp();
if (varp->isIfaceRef()) return false; // Cannot handle interface references
if (varp->delayp()) return false; // Cannot handle delayed variables
if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore
return DfgDataType::fromAst(varp->dtypep());
}
} //namespace V3Dfg
//------------------------------------------------------------------------------
// Map from DfgVertices to T_Value implemeneted via DfgVertex::m_userStorage
@@ -599,12 +604,12 @@ protected:
, m_currentGeneration{that.m_currentGeneration} {
that.m_dfgp = nullptr;
}
DfgUserMapBase& operator=(DfgUserMapBase&&) = delete;
public:
~DfgUserMapBase() {
if (m_dfgp) m_dfgp->m_vertexUserInUse = false;
}
DfgUserMapBase& operator=(DfgUserMapBase&&) = delete;
};
// Specialization where T_Value fits in DfgVertex::m_userStorage directly
@@ -618,11 +623,11 @@ class DfgUserMap<T_Value, true> final : public DfgUserMapBase {
explicit DfgUserMap(const DfgGraph* dfgp)
: DfgUserMapBase{dfgp} {}
VL_UNCOPYABLE(DfgUserMap);
DfgUserMap& operator=(DfgUserMap&&) = delete;
public:
DfgUserMap(DfgUserMap&&) = default;
~DfgUserMap() = default;
DfgUserMap& operator=(DfgUserMap&&) = delete;
// METHODS
// Retrieve mapped value for 'vtx', value initializing it on first access
@@ -667,11 +672,11 @@ class DfgUserMap<T_Value, false> final : public DfgUserMapBase {
explicit DfgUserMap(const DfgGraph* dfgp)
: DfgUserMapBase{dfgp} {}
VL_UNCOPYABLE(DfgUserMap);
DfgUserMap& operator=(DfgUserMap&&) = delete;
public:
DfgUserMap(DfgUserMap&&) = default;
~DfgUserMap() = default;
DfgUserMap& operator=(DfgUserMap&&) = delete;
// METHODS
// Retrieve mapped value for 'vtx', value initializing it on first access
@@ -805,6 +810,8 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
// DfgVertex {{{
bool DfgVertex::isCheaperThanLoad() const {
// Constants
if (is<DfgConst>()) return true;
// Array sels are just address computation
if (is<DfgArraySel>()) return true;
// Small constant select from variable
@@ -823,6 +830,16 @@ bool DfgVertex::isCheaperThanLoad() const {
if (!lCatp->isZero()) return false;
return catp->rhsp()->isCheaperThanLoad();
}
// Reduction of a cheap vertex
if (const DfgRedOr* const redOrp = cast<DfgRedOr>()) {
return redOrp->srcp()->isCheaperThanLoad();
}
if (const DfgRedAnd* const redAndp = cast<DfgRedAnd>()) {
return redAndp->srcp()->isCheaperThanLoad();
}
if (const DfgRedXor* const redXorp = cast<DfgRedXor>()) {
return redXorp->srcp()->isCheaperThanLoad();
}
// Otherwise probably not
return false;
}
+91 -67
View File
@@ -28,61 +28,92 @@
#include "V3Dfg.h"
#include "V3DfgPasses.h"
#include <iterator>
VL_DEFINE_DEBUG_FUNCTIONS;
template <bool T_Scoped>
class AstToDfgAddAstRefs final : public VNVisitorConst {
// STATE
DfgGraph& m_dfg; // The graph being processed
// Function to get the DfgVertexVar for a AstVarScope
const std::function<DfgVertexVar*(AstVarScope*)> m_getVarVertex;
bool m_inSenItem = false; // Inside an AstSenItem
bool m_inLoop = false; // Inside an AstLoop
// VISITORS
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
void visit(AstSenItem* nodep) override {
VL_RESTORER(m_inSenItem);
m_inSenItem = true;
iterateChildrenConst(nodep);
}
void visit(AstLoop* nodep) override {
VL_RESTORER(m_inLoop);
m_inLoop = true;
iterateChildrenConst(nodep);
}
void visit(AstVarRef* nodep) override {
// Disguised hierarchical reference handled as external reference, ignore
if (nodep->classOrPackagep()) return;
// If target is not supported, ignore
AstVarScope* const vscp = nodep->varScopep();
if (!V3Dfg::isSupported(vscp)) return;
// V3Dfg::isSupported should reject vars with READWRITE references
UASSERT_OBJ(!nodep->access().isRW(), nodep, "AstVarScope with READWRITE ref not rejected");
// Get target variable vergtex, ignore if not given one
DfgVertexVar* const varp = m_getVarVertex(vscp);
if (!varp) return;
// Create Ast reference vertices
if (nodep->access().isReadOnly()) {
DfgAstRd* const astp = new DfgAstRd{m_dfg, nodep, m_inSenItem, m_inLoop};
astp->srcp(varp);
return;
}
// Mark as written from non-DFG logic
DfgVertexVar::setHasModWrRefs(vscp);
}
AstToDfgAddAstRefs(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex)
: m_dfg{dfg}
, m_getVarVertex{getVarVertex} {
iterateConst(nodep);
}
public:
static void apply(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) {
AstToDfgAddAstRefs{dfg, nodep, getVarVertex};
}
};
void V3DfgPasses::addAstRefs(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) {
AstToDfgAddAstRefs::apply(dfg, nodep, getVarVertex);
}
class AstToDfgVisitor final : public VNVisitor {
// NODE STATE
// AstVar/AstVarScope::user2() -> DfgVertexVar* : the corresponding variable vertex
// AstVar/AstVarScope::user3() -> bool : Already gathered - used fine grained below
// AstVarScope::user2() -> DfgVertexVar* : the corresponding variable vertex
// AstVarScope::user3() -> bool : Already gathered - used fine grained below
const VNUser2InUse m_user2InUse;
// TYPES
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// STATE
DfgGraph& m_dfg; // The graph being built
V3DfgAstToDfgContext& m_ctx; // The context for stats
AstScope* m_scopep = nullptr; // The current scope, iff T_Scoped
// METHODS
static Variable* getTarget(const AstVarRef* refp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(refp->varp());
}
}
std::unique_ptr<std::vector<Variable*>> getLiveVariables(const CfgGraph& cfg) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
std::unique_ptr<std::vector<AstVarScope*>> result = V3Cfg::liveVarScopes(cfg);
const auto resultp = reinterpret_cast<std::vector<Variable*>*>(result.release());
return std::unique_ptr<std::vector<Variable*>>{resultp};
} else {
std::unique_ptr<std::vector<AstVar*>> result = V3Cfg::liveVars(cfg);
const auto resultp = reinterpret_cast<std::vector<Variable*>*>(result.release());
return std::unique_ptr<std::vector<Variable*>>{resultp};
}
}
// Mark variables referenced under node
static void markReferenced(const AstNode* nodep) {
nodep->foreach([](const AstVarRef* refp) {
Variable* const tgtp = getTarget(refp);
// Mark as read from non-DFG logic
if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp);
// Mark as written from non-DFG logic
if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp);
void markReferenced(AstNode* nodep) {
V3DfgPasses::addAstRefs(m_dfg, nodep, [this](AstVarScope* vscp) { //
return getVarVertex(vscp);
});
}
DfgVertexVar* getVarVertex(Variable* varp) {
DfgVertexVar* getVarVertex(AstVarScope* varp) {
if (!varp->user2p()) {
// TODO: fix this up when removing the different flavours of DfgVar
const AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
@@ -100,14 +131,11 @@ class AstToDfgVisitor final : public VNVisitor {
std::unique_ptr<std::vector<DfgVertexVar*>> gatherWritten(const AstNode* nodep) {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
// We can ignore AstVarXRef here. The only thing we can do with DfgLogic is
// synthesize it into regular vertices, which will fail on a VarXRef at that point.
const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool {
if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isReadOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
const bool abort = nodep->exists([&](const AstVarRef* refp) -> bool {
if (refp->access().isReadOnly()) return false;
AstVarScope* const vscp = refp->varScopep();
if (!V3Dfg::isSupported(vscp)) return true;
if (!vscp->user3SetOnce()) resp->emplace_back(getVarVertex(vscp));
return false;
});
if (abort) {
@@ -122,14 +150,11 @@ class AstToDfgVisitor final : public VNVisitor {
std::unique_ptr<std::vector<DfgVertexVar*>> gatherRead(const AstNode* nodep) {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
// We can ignore AstVarXRef here. The only thing we can do with DfgLogic is
// synthesize it into regular vertices, which will fail on a VarXRef at that point.
const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool {
if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isWriteOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
const bool abort = nodep->exists([&](const AstVarRef* refp) -> bool {
if (refp->access().isWriteOnly()) return false;
AstVarScope* const vscp = refp->varScopep();
if (!V3Dfg::isSupported(vscp)) return true;
if (!vscp->user3SetOnce()) resp->emplace_back(getVarVertex(vscp));
return false;
});
if (abort) {
@@ -143,7 +168,7 @@ class AstToDfgVisitor final : public VNVisitor {
// Return nullptr if any are not supported.
std::unique_ptr<std::vector<DfgVertexVar*>> gatherLive(const CfgGraph& cfg) {
// Run analysis
std::unique_ptr<std::vector<Variable*>> varps = getLiveVariables(cfg);
std::unique_ptr<std::vector<AstVarScope*>> varps = V3Cfg::liveVarScopes(cfg);
if (!varps) {
++m_ctx.m_nonRepLive;
return nullptr;
@@ -153,7 +178,7 @@ class AstToDfgVisitor final : public VNVisitor {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
resp->reserve(varps->size());
for (Variable* const varp : *varps) {
for (AstVarScope* const varp : *varps) {
if (!V3Dfg::isSupported(varp)) {
++m_ctx.m_nonRepVar;
return nullptr;
@@ -272,7 +297,7 @@ class AstToDfgVisitor final : public VNVisitor {
}
// CONSTRUCTOR
AstToDfgVisitor(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx)
AstToDfgVisitor(DfgGraph& dfg, AstNetlist& root, V3DfgAstToDfgContext& ctx)
: m_dfg{dfg}
, m_ctx{ctx} {
iterate(&root);
@@ -281,24 +306,23 @@ class AstToDfgVisitor final : public VNVisitor {
VL_UNMOVABLE(AstToDfgVisitor);
public:
static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) {
static void apply(DfgGraph& dfg, AstNetlist& root, V3DfgAstToDfgContext& ctx) {
// Convert all logic under 'root'
AstToDfgVisitor{dfg, root, ctx};
// Remove unread and undriven variables (created when something failed to convert)
for (DfgVertexVar* const varp : dfg.varVertices().unlinkable()) {
if (!varp->srcp() && !varp->hasSinks()) VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
if (varp->srcp()) continue;
const bool keep = varp->foreachSink([&](DfgVertex& d) { return !d.is<DfgAstRd>(); });
if (!keep) {
while (varp->hasSinks()) varp->firtsSinkp()->unlinkDelete(dfg);
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
}
}
}
};
std::unique_ptr<DfgGraph> V3DfgPasses::astToDfg(AstModule& module, V3DfgContext& ctx) {
DfgGraph* const dfgp = new DfgGraph{&module, module.name()};
AstToDfgVisitor</* T_Scoped: */ false>::apply(*dfgp, module, ctx.m_ast2DfgContext);
return std::unique_ptr<DfgGraph>{dfgp};
}
std::unique_ptr<DfgGraph> V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgContext& ctx) {
DfgGraph* const dfgp = new DfgGraph{nullptr, "netlist"};
AstToDfgVisitor</* T_Scoped: */ true>::apply(*dfgp, netlist, ctx.m_ast2DfgContext);
DfgGraph* const dfgp = new DfgGraph{"netlist"};
AstToDfgVisitor::apply(*dfgp, netlist, ctx.m_ast2DfgContext);
return std::unique_ptr<DfgGraph>{dfgp};
}

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