Compare commits

..

156 commits

Author SHA1 Message Date
Yehowshua Immanuel f2676673cd small update 2024-03-16 23:29:10 -04:00
Yehowshua Immanuel 133b1a2693
Sunsetting 2024-03-12 18:50:29 -04:00
Frans Skarman de897a5010
Merge pull request #36 from ThePerfectComputer/double_sided_query
Don't throw away the right hand time of changes
2023-12-13 08:56:15 +00:00
TheZoq2 e2e3541e3f Refactor the double-sided-query to make it less error-prone 2023-12-05 16:44:19 +01:00
TheZoq2 7d414f36dd Correctly compute next index 2023-12-05 14:59:04 +01:00
TheZoq2 9a8c2a03eb Don't throw away the right hand time of changes 2023-12-05 14:29:46 +01:00
Frans Skarman 3851c4e06c
Merge pull request #35 from ThePerfectComputer/scopefix
Add workaround for files produced by verilator which have annonymous scopes
2023-12-05 13:29:26 +00:00
TheZoq2 9ba53df728 Add workaround for files produced by verilator which have annonymous scopes 2023-11-03 19:54:26 +01:00
TheZoq2 7a2bed42a3 Add some clones and expose SignalErrors 2023-10-25 17:55:05 +02:00
TheZoq2 d42d01f9c3 Expose index 2023-10-23 18:15:30 +02:00
Frans Skarman f3e45f8497
Merge pull request #20 from ThePerfectComputer/name_width_separation
Separate name from width
2023-10-23 13:28:02 +00:00
TheZoq2 e7f2f661df Separate name from width 2023-10-23 15:27:47 +02:00
Frans Skarman 4f31ec842f
Merge pull request #33 from oscargus/bumpdependencies
Bump dependencies
2023-10-16 09:02:37 +00:00
Oscar Gustafsson a8ee52a11e Bump dependencies 2023-10-15 22:05:52 +02:00
Frans Skarman e3c60600f4
Merge pull request #32 from oscargus/maxsize
Increase maximum bit width
2023-10-11 08:20:08 +00:00
Oscar Gustafsson 9c54c3a295 Increase maximum bit width 2023-10-11 09:54:59 +02:00
Frans Skarman b9c507c9d8
Merge pull request #29 from oscargus/realparsing
Add support for parsing real values (to a String)
2023-10-10 14:15:50 +00:00
Oscar Gustafsson b886e5d26d Add support for parsing real values (to a String) 2023-10-10 16:08:09 +02:00
Frans Skarman 01eacd4028
Merge pull request #28 from ThePerfectComputer/revert-27-realvalues
Revert "Add support for real values"
2023-10-10 13:18:31 +00:00
Frans Skarman a1890a63dc
Revert "Add support for real values" 2023-10-10 13:15:17 +00:00
Frans Skarman a1bf2cb15f
Merge pull request #27 from oscargus/realvalues
Add support for real values
2023-10-10 13:11:04 +00:00
Oscar Gustafsson 77b1aa0a03 Add support for real values 2023-10-10 12:52:59 +02:00
Frans Skarman eaea65f7ea
Merge pull request #26 from oscargus/signaltype
Add signal type query
2023-10-09 15:10:05 +00:00
Oscar Gustafsson dba5773ebe Add signal type query 2023-10-09 16:20:12 +02:00
Frans Skarman 0df20db383
Merge pull request #25 from oscargus/signaltypes
Add all signal types in standard
2023-10-09 12:38:13 +00:00
Oscar Gustafsson b576e1b57d Add all signal types in standard 2023-10-09 14:04:59 +02:00
Frans Skarman 6c76a066e3
Merge pull request #24 from oscargus/clonetimescale
Add Clone, Eq, PartialEq to Timescale
2023-10-09 07:23:44 +00:00
Oscar Gustafsson 4e8c8e4896 Add derive cases and fmt to Timescale 2023-10-08 20:34:04 +02:00
Frans Skarman 5a7675d0be
Merge pull request #21 from oscargus/fork
Add support for fork scopes
2023-10-06 14:42:53 +00:00
Frans Skarman 76da883dae
Merge pull request #19 from oscargus/bitparsing
Remove identifier from bit string and guarantee lower case
2023-10-06 08:56:14 +00:00
Oscar Gustafsson 5ee512aff6 Remove identifier from bit string and guarantee lower case 2023-10-06 10:53:14 +02:00
Oscar Gustafsson dad11e2f57 Add support for fork scopes 2023-10-06 08:23:59 +02:00
ThePerfectComputer bc7c6913ce
Fix typos 2023-09-30 12:27:14 -04:00
ThePerfectComputer 506abf9058
Update with status and vision 2023-09-30 12:25:58 -04:00
ThePerfectComputer 7b9090f5af
Add screenshot and improve description 2023-09-28 23:33:48 -04:00
ThePerfectComputer 64372871a0
Update README.md
Correct surfer link
2023-09-28 21:10:07 -04:00
Frans Skarman fccd65ef8f
Merge pull request #18 from oscargus/cleanup
Cleanup
2023-09-27 07:40:33 +00:00
Oscar Gustafsson 0795465c36 Clippy automatic fixes 2023-09-26 13:26:38 +02:00
Oscar Gustafsson 5758e77371 Run cargo-fmt 2023-09-26 13:16:14 +02:00
Frans Skarman 22eaf8da15
Merge pull request #15 from oscargus/vhdl
Add support for VHDL std_ulogic
2023-09-26 11:05:21 +00:00
Frans Skarman 806ad9be3c
Merge pull request #12 from oscargus/keepindex
Keep index as part of signal name
2023-09-26 11:03:10 +00:00
Oscar Gustafsson a7537206e2 Add support for VHDL std_ulogic 2023-09-21 12:08:12 +02:00
Oscar Gustafsson 0897bbae66 Keep index as part of signal name 2023-09-20 14:46:13 +02:00
Frans Skarman e46e0b0cc5
Merge pull request #13 from oscargus/commenttypo
Allow comments in scope (fix typo)
2023-09-19 13:00:01 +00:00
Frans Skarman de6fae83c4
Merge pull request #14 from TheZoq2/main
Add real_idx function to signal
2023-09-19 12:59:18 +00:00
Frans Skarman 14a314a2f3
Merge pull request #11 from oscargus/removedeprecation
Replace deprecated method
2023-09-18 17:37:43 +00:00
Oscar Gustafsson 03705f263d Allow comments in scope (fix typo) 2023-09-13 14:18:18 +02:00
Frans Skarman 878b06e666
Merge pull request #1 from LucasKl/main
Add real_idx function to signal
2023-09-11 08:55:19 +00:00
Lucas Klemmer f2689885be Add real_idx function to signal 2023-09-10 16:23:04 +02:00
Frans Skarman 1534924d05
Merge pull request #10 from oscargus/surferreadme
Update README with more information about surfer
2023-09-07 11:10:25 +00:00
Oscar Gustafsson 73332179ee Replace deprecated method 2023-09-06 13:02:21 +02:00
Oscar Gustafsson c0d245c0e5 Update README with more information about surfer 2023-09-06 12:53:38 +02:00
ThePerfectComputer 4f88eab5de
Update README.md 2023-07-25 13:10:14 -04:00
ThePerfectComputer fc169c2e3b
Merge pull request #8 from TheZoq2/expose_stuff
I haven't worked on this in nearly a year. At this point, I might as well merge your pull request. Thanks.
2023-07-01 20:20:05 -04:00
TheZoq2 a8edb3d602 expse paths and fix warnings 2023-06-16 17:08:20 +02:00
TheZoq2 53bbacd261 Expose signal time stamp 2023-05-01 17:37:24 +02:00
TheZoq2 79300afd43 Make generic over readers 2023-01-09 18:45:10 +01:00
TheZoq2 bc73db5dba Do not include range in name 2023-01-03 16:33:44 +01:00
TheZoq2 3da3579213 Add more timing information 2022-12-23 11:34:48 +01:00
TheZoq2 b162d67a9d impl traits required for maps for idxes 2022-12-22 18:11:19 +01:00
ThePerfectComputer 540ed13e2c
Update README.md 2022-10-26 14:21:19 -04:00
Yehowshua Immanuel 38579f08e9 update UI 2022-10-26 14:07:58 -04:00
ThePerfectComputer f95db0cd2d
Merge pull request #6 from ThePerfectComputer/refactor-signal
Refactor signal
2022-10-26 02:23:08 -04:00
Yehowshua Immanuel b4cbbf29cc remove some dead code in comments and add querying values on timeline back to vcd example 2022-10-26 02:19:59 -04:00
Yehowshua Immanuel 9f18b166a5 sane import and export behaviors 2022-10-26 01:16:39 -04:00
Yehowshua Immanuel d343b6f5ff I broke something... 2022-10-25 22:53:19 -04:00
Yehowshua Immanuel 746c25b1ba change visibility of and rename Signal to SignalEnum 2022-10-25 21:32:22 -04:00
Yehowshua Immanuel 6abcd7ce32 fix some errors/typos and prep for refactoring 2022-10-25 21:09:19 -04:00
ThePerfectComputer ea5675d920
Update README.md 2022-10-20 09:26:58 -04:00
Yehowshua Immanuel d5db622db3 update UI pic 2022-09-15 20:00:26 -04:00
Yehowshua Immanuel 61a68a4fbe public members in ScopeIdx seem to be useful in a frontend GUI 2022-09-15 10:08:17 -04:00
Yehowshua Immanuel 9033f977d5 it seems to be useful to make SignalIdx content public for a GUI frontend 2022-09-15 09:17:42 -04:00
Yehowshua Immanuel 284e97efd2 support properly printing root scopes in example vcd 2022-09-14 19:31:26 -04:00
Yehowshua Immanuel 6b6321557b - propagate renaming for crate to fastwave_backend
- repair scope printout from example/vcd.rs
 - update and improve instruction for running examples in README
2022-09-14 16:54:35 -04:00
Yehowshua Immanuel 4243551d76 proper handling of licenses 2022-09-14 16:28:12 -04:00
Yehowshua Immanuel 06e0aae26f update TODO in README 2022-09-14 13:37:50 -04:00
Yehowshua Immanuel a0713c1f38 add docs and change examples to reflect changing interfaces 2022-09-13 19:35:23 -04:00
Yehowshua Immanuel 320b0d348d notable restructuring 2022-09-09 02:59:33 -04:00
Yehowshua Immanuel 4c1af97760 some docs and create lib 2022-09-08 15:02:41 -04:00
Yehowshua Immanuel 5700db83a0 support for strings seems to work 2022-09-08 09:59:24 -04:00
Yehowshua Immanuel cd6ea71b8d improve README 2022-09-06 10:29:36 -04:00
Yehowshua Immanuel 1ec7b67a4b begin implementation for string lookups on timeline and rename functions to differentiate between strings and nums 2022-09-04 18:52:32 -04:00
Yehowshua Immanuel f4e27ffcb6 - improve to-do list in README
- move code that exercises searching for values at specific time
   within signals from src/vcd/parse.rs to sr/vcd/main.rs
 - make necessary struct and enum fields public as well as possibly the
   structs and enums themselves
2022-09-02 17:00:14 -04:00
ThePerfectComputer c25353073b
Merge pull request #5 from ThePerfectComputer/signal_query
Signal query
2022-09-02 15:08:26 -04:00
Yehowshua Immanuel aeb796c46b ordered search now working 2022-09-02 15:05:33 -04:00
Yehowshua Immanuel 223d36c1a6 successful integration of untested query timeline function 2022-08-22 11:14:53 -04:00
Yehowshua Immanuel 02e28f0357 more restrictive exports 2022-08-22 10:14:26 -04:00
Yehowshua Immanuel fd60e95961 change lookup_errors to signal_errors 2022-08-22 10:11:19 -04:00
Yehowshua Immanuel 3d2b7f8028 maintain consistency wit num_bits naming 2022-08-22 10:08:27 -04:00
Yehowshua Immanuel 37cf2b165c remove some unused fields from struct match arms and eliminate warning in main 2022-08-22 10:03:29 -04:00
Yehowshua Immanuel 0acd49f66c correct lengths of u8 LE encoded timestamp being recorded onto byte_len_of_(num/string)_tmstmp_vals_on_tmln 2022-08-22 09:56:22 -04:00
Yehowshua Immanuel 91dc24a9ba Compute num_bytes ahead of time; Start removing undeeded signals from match arms 2022-08-22 09:48:56 -04:00
Yehowshua Immanuel 8dab46a0cb - moved dereferencing alias to VCD impl
- added function to help with reconstructing timestamps and numerical signal values
2022-08-21 19:51:51 -04:00
Yehowshua Immanuel ea6701f104 now parsing single bit 0|1|x|z|u 2022-08-20 02:58:48 -04:00
Yehowshua Immanuel e2c02316af fixed some reference errors 2022-08-19 21:13:29 -04:00
Yehowshua Immanuel 15a2564f13 non trivial re-factor 2022-08-19 20:13:46 -04:00
Yehowshua Immanuel cbd8be1708 - update README to reflect correct performance numbers
- add tasks to README
 - repair bug in events.rs that prevented most events in VCD file from being parsed
 - add some initial code for timeline scrubbing
2022-08-17 22:10:00 -04:00
Yehowshua Immanuel 3a4ed56532 it's a tilde Robert 2022-08-11 20:10:14 -04:00
Yehowshua Immanuel 4d09750186 fix typos and improve organization in README 2022-08-11 20:04:06 -04:00
Yehowshua Immanuel 7736c713e7 move fastwave up to top on table 2022-08-11 19:59:41 -04:00
Yehowshua Immanuel 55fc5b586e expand on performance information in README 2022-08-11 19:58:43 -04:00
Yehowshua Immanuel fd31ec1358 notable near 2x speedup in load time, and near 3x reduction in memory consumption 2022-08-11 19:58:21 -04:00
Yehowshua Immanuel def4b26005 now all calls to next_word and curr_word should be macros where possible 2022-08-11 18:33:26 -04:00
Yehowshua Immanuel c53c9684e6 presumably using macros everywhere now 2022-08-11 17:35:40 -04:00
Yehowshua Immanuel 0946d13e6e separated out utilities and started tightening visibility modifiers 2022-08-08 19:45:14 -04:00
Yehowshua Immanuel 9f2b349029 typos in README 2022-08-07 19:09:02 -04:00
ThePerfectComputer 3b0159ad70
Merge pull request #4 from ThePerfectComputer/test_ordered_binary_lookup
Test ordered binary lookup
2022-08-07 19:06:23 -04:00
Yehowshua Immanuel a7d2b11998 now running much faster, but due for refactor 2022-08-07 18:57:58 -04:00
Yehowshua Immanuel b86e5fd3e4 ordered binary search working - but need to be sped up 2022-08-06 21:40:33 -04:00
Yehowshua Immanuel f1074e4340 1 was the correct depth to traverse 2022-08-04 20:32:01 -04:00
Yehowshua Immanuel 2953c27e84 update README tasks 2022-08-04 20:29:49 -04:00
Yehowshua Immanuel 2f512e24de add info for negative testing 2022-08-04 14:29:29 -04:00
Yehowshua Immanuel 773ac96b0f need name of two function calls up the stack 2022-08-04 14:28:58 -04:00
ThePerfectComputer 3ec1aa44e9
Merge pull request #3 from ThePerfectComputer/backtrace
Backtrace
2022-08-04 14:16:06 -04:00
Yehowshua Immanuel e8c215fbcf still not getting line numbers in release mode 2022-08-04 14:01:46 -04:00
Yehowshua Immanuel ba9208277b trying to get filename and linenumber 2022-08-04 13:19:52 -04:00
Yehowshua Immanuel fa25bad391 don't use #function in errors 2022-08-04 11:36:26 -04:00
Yehowshua Immanuel 21f0682500 fixed most warnings 2022-08-02 19:31:35 -04:00
Yehowshua Immanuel e931d9a825 refactor parse a bit 2022-08-02 16:40:40 -04:00
Yehowshua Immanuel b685bf8be7 handle case of signal events with bitwidths shorter than nominal signal length 2022-08-02 11:51:07 -04:00
ThePerfectComputer d70a09e255
Merge pull request #2 from ThePerfectComputer/new_parser
New parser
2022-08-01 17:00:00 -04:00
Yehowshua Immanuel e9aa4735dd things seem pretty stable now 2022-08-01 16:52:42 -04:00
Yehowshua Immanuel 8f763ae52b cargo test not passing 2022-08-01 12:31:43 -04:00
Yehowshua Immanuel 557f385336 update README 2022-08-01 12:29:25 -04:00
Yehowshua Immanuel 232b026f62 things are much faster now, but x/z values not yet supported 2022-08-01 10:11:05 -04:00
Yehowshua Immanuel 0052baf196 add support for mismatching bitdwidths 2022-07-30 19:58:54 -04:00
Yehowshua Immanuel eb379e4ce6 update README 2022-07-28 10:43:58 -04:00
Yehowshua Immanuel 504913c719 still broken - but I need to save progress 2022-07-27 09:35:44 -04:00
Yehowshua Immanuel 18a69872ab saving progress so far 2022-07-25 21:16:15 -04:00
Yehowshua Immanuel 3658833af3 wow - things are really working 2022-07-20 10:38:56 -04:00
Yehowshua Immanuel d8989d9c76 I broke something 2022-07-19 22:05:00 -04:00
ThePerfectComputer ca9fca5379
Merge pull request #1 from ThePerfectComputer/add-license-1
Create LICENSE
2022-07-19 13:49:52 -04:00
ThePerfectComputer 711d9ca055
Create LICENSE 2022-07-19 13:49:39 -04:00
Yehowshua Immanuel 24622c71c2 getting started on parsing timeline events 2022-07-19 13:48:22 -04:00
Yehowshua Immanuel 932250e416 some re-org and cleanup 2022-07-18 12:53:44 -04:00
Yehowshua Immanuel 298bbe1969 able to parse all signal trees 2022-07-15 20:48:02 -04:00
Yehowshua Immanuel 217dea1d41 good stopping point for today 2022-07-14 18:52:12 -04:00
Yehowshua Immanuel 8bcd2bc8ec seems to be able to parse signal tree 2022-07-14 16:46:11 -04:00
Yehowshua Immanuel 49d103fd56 some incomplete changes 2022-07-12 20:02:45 -04:00
Yehowshua Immanuel 29d72b6e9c postpone adding date support for ncsim, quartus, treadle, and vivado 2022-06-24 22:22:55 -04:00
Void User 21661d7967 no longer need a list of files in the REAMDE 2022-06-23 20:56:44 -04:00
Void User 77235a19bc missed some files by committing in the wrong folder 2022-06-23 20:54:27 -04:00
Void User 8b9114499e a bit of restructuring to support more modular tests 2022-06-23 20:52:39 -04:00
Yehowshua Immanuel dbae68ba3b finish splitting up parse.rs 2022-06-21 21:06:51 -04:00
Yehowshua Immanuel f5bb8d5a7c some re-org 2022-06-19 09:44:57 -04:00
Yehowshua Immanuel 5445891b8d add tests 2022-06-18 01:00:01 -04:00
Yehowshua Immanuel a37c4c0f95 now using ParseResult as parser return type exclusively 2022-06-17 18:16:51 -04:00
Yehowshua Immanuel b25cdaa170 now parsing headers fully 2022-06-12 22:52:24 -04:00
Yehowshua Immanuel e7fb766302 now parsing version 2022-06-12 15:32:00 -04:00
Yehowshua Immanuel 14af6e94e3 now parsing date more robustly 2022-06-11 00:01:53 -04:00
Yehowshua Immanuel 4c7417c729 date parser needs some more work 2022-06-08 21:45:47 -04:00
Yehowshua Immanuel 1c006441fb restructuring and WIP 2022-06-03 21:06:46 -04:00
Yehowshua Immanuel c65bdfefac restructure 1 2022-06-03 12:06:20 -04:00
Yehowshua Immanuel 43563d0d7c nearly as fast as wc and now yield words 2022-06-02 20:02:09 -04:00
Yehowshua Immanuel 08c5058208 now using pointer and string slices 2022-06-02 16:51:56 -04:00
Yehowshua Immanuel c04bf2273e will probably abandon this 2022-05-28 21:37:43 -04:00
58 changed files with 4390 additions and 370 deletions

38
.gitignore vendored
View file

@ -1,2 +1,40 @@
/target
Cargo.lock
# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
# End of https://www.toptal.com/developers/gitignore/api/macos

View file

@ -1,12 +1,16 @@
[package]
name = "VCDViewer"
name = "fastwave_backend"
version = "0.1.0"
edition = "2021"
[profile.release]
debug = 1
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num = "0.4"
clap = { version = "3.1.8", features = ["derive"] }
clap = { version = "4.4.6", features = ["derive"] }
chrono = "0.4"
function_name = "0.3.0"
# TODO : remove itertools once date parser is reworked.
itertools = "0.11"

674
LICENSE-GPLV3 Normal file
View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

2
LICENSE-YEHOWSHUA Normal file
View file

@ -0,0 +1,2 @@
This codebase is also licensed to Yehowshua Immanuel to use
in any manner Yehowshua Immanuel sees fit.

176
README.md
View file

@ -1,71 +1,131 @@
# Disclaimer
PROPRIETARY - Copyright - Yehowshua Immanuel
Copyright(2023) - Yehowshua Immanuel
# The Beginnings of a high-performance, low memory footprint VCD Viewer in Rust for massive multi-GB waveforms
# Vision
Imagine being able to visualize a CPU pipeline diagram by merely loading a simulation waveform dump, sprinkling in a bit of code, and dragging and dropping some diagram blocks into the visualizer. This project aims to offer such an experience.
## Current Features
- very fast
- loads 400MB of VCD waveform per second on an 8 core 2017 desktop CPU with NVMe storage
- consumes roughly between 10 - 50MB of memory per GB of waveform
Since this project is written in Rust, it should also be able to run in the browser via web-assembly.
## Planed Features
- elegant/pretty UI
- can be easily ported to work in browser via webassembly
- allows high-performance custom Rust plugins to manipulate and
generate new waveforms live
# Status
As of January 2024, work on the Fastwave Backend is stalled. It has been a fun journey watching Fastwave enable the first iterations of the [surfer waveform viewer](https://surfer-project.org). Now surfer uses an even better backend called [Wellen](https://github.com/ekiwi/wellen?tab=readme-ov-file). Go check it out! I hear it's really good. Perhaps I will soon archive the Fastwave Backend.
## Running
Browser demo: https://app.surfer-project.org/
Make sure you have a test vcd file to get you started. You can grab
a large VCD file from
Screenshot of surfer frontend below:
<img width="1609" alt="image" src="https://github.com/ThePerfectComputer/FastWaveBackend/assets/103011002/63deffc0-98b0-405f-b093-74d10b422b01">
# A High performance, VCD Parser written in Rust
I hope for a GUI that eventually looks like the following...
![](assets/FastWaveMockup.png)
# Current Features
- Robust Error Handling
FastWave currently offers highly robust error handling(at least on the sample
VCD files in this repository) which GTKWave doesn't have. For
example, selecting the ``UTILIZATON_ENABLED`` signal after opening
[test2x2_regex22_string1.vcd](./test-vcd-files/xilinx_isim/test2x2_regex22_string1.vcd)
(one of the sample xilinx vcd test files) in GtkWave, will crash GtkWave since
this signal is malformed. FastWave on the otherhand simply notes that the
signal is malformed.
# Performance
## Methods
I have been testing performance on a 3.04GB VCD file that can be found
[here](https://drive.google.com/file/d/1pfm2qo2l8fGTHHJ8TLrg1vSGaV_TUbp2/view?usp=sharing).
For getting the GtkWave results, I fired up GtkWave, clicked on
``File``->``Open New Window``, and selected the VCD file linked to above.
I started my stopwatch as soon as I pressed enter to beging loading the VCD
file, and stopped my stopwatch once the GtkWave titlebar reached 100%.
To get the memory consumption, I opened Activity Monitor on Mac, and recorded
the GtkWave memory usage before and after loading the large VCD file, and
took the difference.
I noticed that when running FastWave in the VsCode terminal as opposed
to the MacOS system terminal or the Lapce terminal, FastWave is notably
slower.
## Results
LZ4 should really improve memory usage once I add it. Note that GTKWave uses LZ4 on the fly.
| Software | Time(s) | Memory(MB) |
|----------|---------|------------|
| FastWave | ~27.30 | 1100+ |
| GtkWave | ~30 | 89.8 |
# Current Limitations
Unable to handle VCD files that have signals with more than
2^32 - 1 = 4,294,967,295 deltas/changes.
# Running
This repository comes with several smaller VCD files emitted from
various EDA tools. If you want a larger VCD file, grab one from
[here](https://drive.google.com/file/d/1pfm2qo2l8fGTHHJ8TLrg1vSGaV_TUbp2/view?usp=sharing).
Here are some examples you can run:
The first build of the program may take some time.
``cargo run --release test-vcd-files/aldec/SPI_Write.vcd``
1. ``cargo run --release --example parse_vcd tests/vcd-files/aldec/SPI_Write.vcd``
2. ``cargo run --release --example vcd``
You can run all the tests with ``cargo test``
# Testing on Bad Files
You may wish to test the parser on a malformed VCD just to make
sure that the parser gives useful/sane errors.
Here's a command to test on a malformed VCD:
``cargo run --release --example parse_vcd tests/vcd-files/VCD_file_with_errors.vcd``
# TODO
- [x] We need a way to merge lines.
- [ ] Consider what to do with don't care values
will probably just convert them to strings for now.
- [ ] Test for speed and see if stream of bytes is helpful
- [ ] Include line and possible column numbers
- [ ] Change states to lowercase
- [ ] We need to start regression testing the parser over all files
- [ ] Take a look at GTKWave parser to compare effificiency.
- [ ] Send survey to community channel.
# Probably No Longer Needed
- [ ] Should insert nodes in BFS order
## Features and Other
- [ ] add documenting comments
- [ ] make signal query private until later?
- [ ] add signal query code to example once interface is finalized
- [ ] change crate name to vcd_fast_parse
- [ ] perhaps we should be looking up signal values on the VCD class
- [ ] perhaps we should be returning signal trees from the VCD class
- [ ] add lz4 compression support and compare memory perf before and after
- [ ] may need to refactor with allow for get_mut for dynamic
compression-decompression for multiple signal structs
at once to allow for multi-threading
- [ ] test against large waveforms from the
[verilog-vcd-parser](https://github.com/ben-marshall/verilog-vcd-parser)
tool
- [ ] (a bit of work) consolidate error messages in validation phase
- [ ] once the access patterns of the GUI frontend are well understood,
create specific functions and restrict as many types to private
as possible
- [ ] Print out git commit or release number.
- [ ] do a read through all the code - look for uneeded code
- [ ] Move part of the performance section to another markdown file.
# Files
- ./test-vcd-files/aldec/SPI_Write.vcd
- ./test-vcd-files/ghdl/alu.vcd
- ./test-vcd-files/ghdl/idea.vcd
- ./test-vcd-files/ghdl/pcpu.vcd
- ./test-vcd-files/gtkwave-analyzer/perm_current.vcd
- ./test-vcd-files/icarus/CPU.vcd
- ./test-vcd-files/icarus/rv32_soc_TB.vcd
- ./test-vcd-files/icarus/test1.vcd
- ./test-vcd-files/model-sim/CPU_Design.msim.vcd
- ./test-vcd-files/model-sim/clkdiv2n_tb.vcd
- ./test-vcd-files/my-hdl/Simple_Memory.vcd
- ./test-vcd-files/my-hdl/sigmoid_tb.vcd
- ./test-vcd-files/my-hdl/top.vcd
- ./test-vcd-files/ncsim/ffdiv_32bit_tb.vcd
- ./test-vcd-files/quartus/mipsHardware.vcd
- ./test-vcd-files/quartus/wave_registradores.vcd
- ./test-vcd-files/questa-sim/dump.vcd
- ./test-vcd-files/questa-sim/test.vcd
- ./test-vcd-files/riviera-pro/dump.vcd
- ./test-vcd-files/systemc/waveform.vcd
- ./test-vcd-files/treadle/GCD.vcd
- ./test-vcd-files/vcs/Apb_slave_uvm_new.vcd
- ./test-vcd-files/vcs/datapath_log.vcd
- ./test-vcd-files/vcs/processor.vcd
- ./test-vcd-files/verilator/swerv1.vcd
- ./test-vcd-files/verilator/vlt_dump.vcd
- ./test-vcd-files/vivado/iladata.vcd
- ./test-vcd-files/xilinx_isim/test.vcd
- ./test-vcd-files/xilinx_isim/test1.vcd
- ./test-vcd-files/xilinx_isim/test2x2_regex22_string1.vcd
## Repairs
- [ ] replace str bracket indices with get(slice)
- [ ] make a custom date parser for possibly up to 18 different versions(that is, for each possible tool).
- [ ] Consolidate error messages and add cursors throughout.
- [ ] Add file and line to the enum errors.
- [ ] Fix warnings especially usage and restriction warnings once I'm
able to successfully parse all sample VCDs.
## Code Consistency
- [ ] Change error messages to line and filenames. Go through all calls to unwrap.
- [ ] search for any unwraps or any direct vectors indexing
- [ ] Handle TODOs
- [ ] Remove debug code/comments.
## Documentation
- [ ] Document indexing structure with diagram and possibly include the rational
## Marketing
- [ ] Send survey to community

BIN
assets/FastWaveMockup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

33
examples/parse_vcd.rs Normal file
View file

@ -0,0 +1,33 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use clap::Parser;
use std::fs::File;
use fastwave_backend::parse_vcd;
#[derive(Parser)]
struct Cli {
/// The path to the file to read
path: std::path::PathBuf,
}
fn main() -> std::io::Result<()> {
let args = Cli::parse();
use std::time::Instant;
let now = Instant::now();
let file = File::open(&args.path)?;
parse_vcd(file).unwrap();
let elapsed = now.elapsed();
println!(
"Parsed VCD file {} : {:.2?}",
&args.path.as_os_str().to_str().unwrap(),
elapsed
);
Ok(())
}

81
examples/vcd.rs Normal file
View file

@ -0,0 +1,81 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use std::fs::File;
use fastwave_backend::{parse_vcd, ScopeIdx, SignalIdx, VCD};
fn indented_print(indent: u8, name: &String) {
for _ in 0..indent {
print!(" |");
}
print!("---");
println!("{name}");
}
// TODO: refactor into more general visitor pattern that takes a
// function as an argument.
fn visit_all_scopes(vcd: &VCD) {
fn visit_all_scope_children(root_idx: ScopeIdx, vcd: &VCD, indent: u8) {
if vcd.child_scopes_by_idx(root_idx).is_empty() {
} else {
for child_scope_idx in vcd.child_scopes_by_idx(root_idx) {
indented_print(indent, vcd.scope_name_by_idx(child_scope_idx));
for signal_idx in vcd.get_children_signal_idxs(child_scope_idx) {
let signal = vcd.signal_from_signal_idx(signal_idx);
let SignalIdx(idx) = signal_idx;
indented_print(indent + 1, &format!("{},{}", signal.name(), idx));
}
visit_all_scope_children(child_scope_idx, vcd, indent + 1);
}
}
}
for root_scope_idx in vcd.root_scopes_by_idx() {
indented_print(0, vcd.scope_name_by_idx(root_scope_idx));
visit_all_scope_children(root_scope_idx, vcd, 1u8);
}
}
fn main() -> std::io::Result<()> {
use std::time::Instant;
// we start by printing out the entire signal tree of
// a parsed VCD
let now = Instant::now();
let file_path = "tests/vcd-files/icarus/CPU.vcd";
let file = File::open(file_path)?;
let vcd = parse_vcd(file).unwrap();
let elapsed = now.elapsed();
println!("Parsed VCD file {} : {:.2?}", file_path, elapsed);
println!("Printing Scopes");
visit_all_scopes(&vcd);
println!("Done Printing Scopes");
println!();
// we then parse another VCD, print its signal tree and
// query some values on its timeline
let now = Instant::now();
let file_path = "tests/vcd-files/amaranth/up_counter.vcd";
let file = File::open(file_path)?;
let vcd = parse_vcd(file).unwrap();
let elapsed = now.elapsed();
println!("Parsed VCD file {} : {:.2?}", file_path, elapsed);
println!("Printing Scopes");
visit_all_scopes(&vcd);
println!("Done Printing Scopes");
let state_signal = vcd.signal_from_signal_idx(SignalIdx(4));
let name = state_signal.name();
let timestamps = vec![31499_000u32, 31500_000u32, 57760_000u32];
for timestamp in timestamps {
let time = num::BigUint::from(timestamp);
let val = state_signal.query_string_val_on_tmln(&time, &vcd).unwrap();
println!("Signal `{name}` has value `{val}` at time `{time}`");
}
Ok(())
}

12
src/lib.rs Normal file
View file

@ -0,0 +1,12 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
mod vcd;
pub use vcd::parse::parse_vcd;
pub use vcd::signal::{Signal, SignalType, SignalValue, SignalErrors};
pub use vcd::types::{Metadata, Timescale, Version};
pub use vcd::types::{ScopeIdx, SignalIdx, VCD};
pub use num::BigUint;

View file

@ -1,305 +0,0 @@
use std::io::prelude::*;
use std::io;
use std::fs::File;
use std::collections::BTreeMap;
use chrono::prelude::*;
use ::function_name::named;
use num::*;
use clap::Parser;
#[derive(Parser)]
struct Cli {
/// The path to the file to read
#[clap(parse(from_os_str))]
path: std::path::PathBuf}
// TODO: implement any timescales greater than a second
#[derive(Debug)]
enum Timescale {ps, ns, us, ms, s, unit}
#[derive(Debug)]
struct Scope_Idx(usize);
#[derive(Debug)]
struct Signal_Idx(usize);
#[derive(Debug)]
struct Version(String);
#[derive(Debug)]
struct Metadata {
date : Option<DateTime<Utc>>,
version : Option<Version>,
timescale : (Option<u32>, Timescale)}
#[derive(Debug)]
enum SignalGeneric{
Signal{
name : String,
timeline : BTreeMap<BigInt, BigInt>,
scope_parent : Scope_Idx},
SignalAlias{
name : String,
signal_alias : Signal_Idx}
}
#[derive(Debug)]
struct Scope {
name : String,
child_signals : Vec<Signal_Idx>,
child_scopes : Vec<Scope_Idx>}
#[derive(Debug)]
struct VCD {
metadata : Metadata,
all_signals : Vec<SignalGeneric>,
// the root scope should always be placed at index 0
all_scopes : Vec<Scope>}
#[derive(Debug)]
enum Date_Parser_State {Begin, Parsing}
#[derive(Debug)]
enum Version_Parser_State {Begin, Parsing}
#[derive(Debug)]
enum Timescale_Parser_State {Begin, Parsing}
#[derive(Debug)]
enum Signal_Tree_Parser_State {Begin, Parsing}
#[derive(Debug)]
enum Parser_State {
Date(Date_Parser_State),
Version(Version_Parser_State),
Timescale(Timescale_Parser_State),
Signal_Tree(Signal_Tree_Parser_State),
Parse_Signal_Values}
struct VCD_Parser<'a> {
vcd_parser_state : Parser_State,
buffer : Option<String>,
vcd : &'a mut VCD,
curr_scope : Option<&'a Scope>,
curr_parent_scope : Option<&'a Scope>}
impl VCD {
pub fn new() -> Self {
let metadata = Metadata {
date : None,
version : None,
timescale : (None, Timescale::unit)};
VCD {
metadata : metadata,
all_signals : Vec::<SignalGeneric>::new(),
all_scopes : Vec::<Scope>::new()}
}
}
impl<'a> VCD_Parser<'a> {
pub fn new(vcd : &'a mut VCD) -> Self {
VCD_Parser {
vcd_parser_state : Parser_State::Date(Date_Parser_State::Begin),
buffer : None,
vcd : vcd,
curr_scope : None,
curr_parent_scope : None
}
}
pub fn parse_word(&mut self, word : &str) -> Result<(), String> {
let mut state = &mut self.vcd_parser_state;
let t = &self.vcd;
match state {
Parser_State::Date(_) => self.parse_date(word),
Parser_State::Version(_) => self.parse_version(word),
Parser_State::Timescale(_) => self.parse_timescale(word),
// TODO : Enable the following in production
// _ => Err(format!("parser in bad state : {state:?}"))
// TODO : Disable the following in production
_ => {
Err(format!("parser in bad state : {state:?}; {t:?}"))
}
}
}
#[named]
pub fn parse_date(&mut self, word : &str) -> Result<(), String> {
let mut state = &mut self.vcd_parser_state;
match state {
Parser_State::Date(Date_Parser_State::Begin) =>
match word {
"$date" => {
*state = Parser_State::Date(Date_Parser_State::Parsing);
Ok(())
}
_ => {
*state = Parser_State::Version(Version_Parser_State::Begin);
self.parse_version(word)
}
}
Parser_State::Date(Date_Parser_State::Parsing) =>
match word {
"$end" => {
let s = self.buffer.take().unwrap();
let dt = Utc.datetime_from_str(s.as_str(), "%a %b %e %T %Y")
.expect(&format!("invalid date {s}").as_str());
*state = Parser_State::Version(Version_Parser_State::Begin);
self.vcd.metadata.date = Some(dt);
Ok(())
}
_ => {
if let Some(ref mut buffer) = self.buffer {
buffer.push_str(" ");
buffer.push_str(word);
}
else {
self.buffer = Some(word.to_string());
}
Ok(())
}
}
_ => Err(format!("{state:?} should be unreachable within {}.",function_name!())),
}
}
#[named]
pub fn parse_version(&mut self, word : &str) -> Result<(), String> {
let mut state = &mut self.vcd_parser_state;
match state {
Parser_State::Version(Version_Parser_State::Begin) =>
match word {
"$version" => {
*state = Parser_State::Version(Version_Parser_State::Parsing);
Ok(())
}
_ => {
*state = Parser_State::Timescale(Timescale_Parser_State::Begin);
Ok(())
}
}
Parser_State::Version(Version_Parser_State::Parsing) =>
match word {
"$end" => {
let s = self.buffer.take().unwrap();
self.vcd.metadata.version = Some(Version(s));
*state = Parser_State::Timescale(Timescale_Parser_State::Begin);
Ok(())
}
_ => {
if let Some(ref mut buffer) = self.buffer {
buffer.push_str(" ");
buffer.push_str(word);
}
else {
self.buffer = Some(word.to_string());
}
Ok(())
}
}
_ => Err(format!("{state:?} should be unreachable within {}.",function_name!())),
}
}
#[named]
pub fn parse_timescale(&mut self, word : &str) -> Result<(), String> {
let mut state = &mut self.vcd_parser_state;
match state {
Parser_State::Timescale(Timescale_Parser_State::Begin) =>
match word {
"$timescale" => {
*state = Parser_State::Timescale(Timescale_Parser_State::Parsing);
Ok(())
}
_ => {
*state = Parser_State::Signal_Tree(Signal_Tree_Parser_State::Begin);
Ok(())
}
}
Parser_State::Timescale(Timescale_Parser_State::Parsing) =>
match word {
"$end" => {
let s = self.buffer.take().unwrap();
let s = s.split_ascii_whitespace();
let s = s.collect::<Vec<&str>>();
let scalar = s[0].to_string().parse::<u32>().unwrap();
let unit = s[1];
let unit = match unit {
"ps" => Ok(Timescale::ps),
"ns" => Ok(Timescale::ns),
"us" => Ok(Timescale::us),
"ms" => Ok(Timescale::ms),
"s" => Ok(Timescale::s),
// TODO : see if there is a way to easily print out all enum variants
// _ => Err(format!("{word} is not a valid unit of time in {Timescale}"))
_ => Err(format!("{unit} is not a valid unit"))
}.unwrap();
dbg!(s);
self.vcd.metadata.timescale = (Some(scalar), unit);
*state = Parser_State::Timescale(Timescale_Parser_State::Begin);
Ok(())
}
_ => {
if let Some(ref mut buffer) = self.buffer {
buffer.push_str(" ");
buffer.push_str(word);
}
else {
self.buffer = Some(word.to_string());
}
Ok(())
}
}
_ => Err(format!("{state:?} should be unreachable within {}.",function_name!())),
}
}
}
fn yield_word_and_apply(file : File, mut f : impl FnMut(&str) -> Result<(), String>) {
let mut reader = io::BufReader::new(file);
let mut buffer = String::new();
let mut EOF = false;
let line_chunk_size = 25;
while {!EOF} {
for _ in 0..line_chunk_size {
let bytes_read = reader.read_line(&mut buffer).unwrap();
if bytes_read == 0 {
EOF = true;
break
}
}
let words = buffer.split_ascii_whitespace();
for word in words {
f(word).unwrap();
}
buffer.clear();
}
}
fn main() -> std::io::Result<()> {
let args = Cli::parse();
let file = File::open(&args.path)?;
let mut vcd = VCD::new();
let mut parser = VCD_Parser::new(&mut vcd);
yield_word_and_apply(file, |word| {parser.parse_word(word)});
dbg!(&vcd);
Ok(())
}

10
src/vcd.rs Normal file
View file

@ -0,0 +1,10 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
pub(crate) mod parse;
mod reader;
pub(crate) mod signal;
pub(crate) mod types;
mod utilities;

35
src/vcd/parse.rs Normal file
View file

@ -0,0 +1,35 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
mod combinator_atoms;
mod events;
mod metadata;
mod scopes;
mod types;
pub fn parse_vcd(file: impl std::io::Read) -> Result<super::types::VCD, String> {
let mut word_gen = super::reader::WordReader::new(file);
let header = metadata::parse_metadata(&mut word_gen)?;
// later, we'll need to map parsed ascii symbols to their
// respective signal indexes
let mut signal_map = std::collections::HashMap::new();
// after we parse metadata, we form the VCD object
let mut vcd = super::types::VCD {
metadata: header,
tmstmps_encoded_as_u8s: vec![],
all_signals: vec![],
all_scopes: vec![],
root_scopes: vec![],
largest_timestamp: None,
};
scopes::parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?;
events::parse_events(&mut word_gen, &mut vcd, &mut signal_map)?;
Ok(vcd)
}

View file

@ -0,0 +1,83 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use super::super::reader::{next_word, WordReader};
use super::types::ParseResult;
pub(super) fn digit(chr: u8) -> bool {
let zero = b'0';
let nine = b'9';
(chr >= zero) && (nine >= chr)
}
pub(super) fn take_until(word: &str, pattern: u8) -> ParseResult<'_> {
let mut new_start = 0;
for chr in word.as_bytes() {
if *chr == pattern {
break;
} else {
new_start += 1;
}
}
ParseResult {
matched: &word[0..new_start],
residual: &word[new_start..],
}
}
pub(super) fn take_while(word: &str, cond: fn(u8) -> bool) -> ParseResult<'_> {
let mut new_start = 0;
for chr in word.as_bytes() {
if cond(*chr) {
new_start += 1;
} else {
break;
}
}
ParseResult {
matched: &word[0..new_start],
residual: &word[new_start..],
}
}
pub(super) fn tag<'a>(word: &'a str, pattern: &'a str) -> ParseResult<'a> {
let lhs = word.as_bytes().iter();
let rhs = pattern.as_bytes();
let iter = lhs.zip(rhs);
let mut new_start = 0;
let mut res = true;
for (c_lhs, c_rhs) in iter {
res = res && (c_lhs == c_rhs);
if !res {
break;
}
new_start += 1;
}
ParseResult {
matched: &word[0..new_start],
residual: &word[new_start..],
}
}
pub(super) fn ident<R: std::io::Read>(
word_reader: &mut WordReader<R>,
keyword: &str,
) -> Result<(), String> {
// let keyword = "module";
let (word, cursor) = next_word!(word_reader)?;
if word == keyword {
Ok(())
} else {
let err = format!("found keyword `{word}` but expected `{keyword}` on {cursor:?}");
Err(err)
}
}

546
src/vcd/parse/events.rs Normal file
View file

@ -0,0 +1,546 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use num::BigUint;
use std::collections::HashMap;
use super::super::reader::{next_word, Cursor, Line, Word, WordReader};
use super::super::signal::{LsbIdxOfTmstmpValOnTmln, SignalEnum};
use super::super::types::{SignalIdx, VCD};
use super::super::utilities::{binary_str_to_vec_u8, BinaryParserErrTypes};
pub(super) fn parse_events<R: std::io::Read>(
word_reader: &mut WordReader<R>,
vcd: &mut VCD,
signal_map: &mut HashMap<String, SignalIdx>,
) -> Result<(), String> {
let mut curr_tmstmp_lsb_idx = 0u32;
let mut curr_tmstmp_len_u8 = 0u8;
loop {
let next_word = word_reader.next_word();
// The following is the only case where eof is not an error.
// If we've reached the end of the file, then there is obviously
// nothing left to do...
if next_word.is_none() {
break;
};
let (word, cursor) = next_word.unwrap();
let Cursor(Line(_), Word(word_in_line_idx)) = cursor;
// we only want to match on the first word in a line
if word_in_line_idx != 1 {
continue;
}
match &word[0..1] {
"$" => {}
"#" => {
let value = &word[1..];
let (f, l) = (file!(), line!());
let value_biguint = BigUint::parse_bytes(value.as_bytes(), 10)
.ok_or(())
.map_err(|_| {
format!(
"Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}"
)
})?;
let mut value = value_biguint.to_bytes_le();
// TODO : u32 helps with less memory, but should ideally likely be
// configurable.
curr_tmstmp_len_u8 = u8::try_from(value.len()).map_err(|_| {
format!(
"Error near {}:{}. Failed to convert from usize to u8.",
file!(),
line!()
)
})?;
curr_tmstmp_lsb_idx =
u32::try_from(vcd.tmstmps_encoded_as_u8s.len()).map_err(|_| {
format!(
"Error near {}:{}. Failed to convert from usize to u32.",
file!(),
line!()
)
})?;
vcd.tmstmps_encoded_as_u8s.append(&mut value);
vcd.largest_timestamp = Some(value_biguint);
}
// handle the case of an n bit signal whose value must be parsed
"b" => {
let binary_value = &word[1..];
let observed_num_bits = u32::try_from(binary_value.len()).map_err(|_| {
format!(
"Error near {}:{}, {cursor:?}. \
Found signal with more than 2^32 - 1 bits.",
file!(),
line!()
)
})?;
let mut value_u8: Vec<u8> = Vec::new();
let mut value_string = String::new();
let mut store_as_string = false;
// If we encounter other values than 0 or 1, we can recover from
// the error and store the value as a string.
// Or else, we propagate up other errors.
match binary_str_to_vec_u8(binary_value) {
Ok(result) => {
value_u8 = result;
}
Err(
BinaryParserErrTypes::XValue
| BinaryParserErrTypes::ZValue
| BinaryParserErrTypes::UValue
| BinaryParserErrTypes::WValue
| BinaryParserErrTypes::HValue
| BinaryParserErrTypes::DashValue
| BinaryParserErrTypes::LValue,
) => {
store_as_string = true;
// Turn to lower case for consistency
value_string = binary_value.to_ascii_lowercase();
}
Err(e) => {
let (f, l) = (file!(), line!());
Err(e).map_err(|e| {
format!("Error near {f}:{l}. Error {e:?} at {cursor:?}.")
})?;
}
}
// this word should be the signal alias
let (word, cursor) = next_word!(word_reader)?;
// lookup signal idx
let signal_idx = signal_map.get(word).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {word} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
SignalEnum::Data {
name,
signal_type,
ref mut signal_error,
num_bits,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
string_vals,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
..
} => {
// we've already identified in a prior loop iteration that the signal has
// an error
if signal_error.is_some() {
continue;
}
// Get the observed number of bits for the value parsed earlier
// and verify that it is not greater than the numbits declared
// when the signal was declared.
// Also account for the error case of a bitwidth of `None`
match num_bits {
Some(ref num_bits) => {
if observed_num_bits > *num_bits {
let (f, l) = (file!(), line!());
let msg = format!("\
Error near {f}:{l}. The bitwidth for signal {name} \
of signal_type {signal_type:?} is expected to be `{num_bits}` not \
`{observed_num_bits}`. \
This error occurred while parsing the vcd file at \
{cursor:?}");
*signal_error = Some(msg);
continue;
}
}
None => {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {signal_type:?}. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
Err(msg)?;
}
};
if store_as_string {
lsb_indxs_of_string_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
string_vals.push(value_string);
Ok(())
} else {
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
let mut curr_num_bytes =
u16::try_from(value_u8.len()).map_err(|_| {
format!(
"Error near {}:{}. \
Found signal {name} with with value change of greater \
than 2^32 - 1 bits on {cursor:?}.",
file!(),
line!()
)
})?;
nums_encoded_as_fixed_width_le_u8.append(&mut value_u8);
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
Ok(())
}
}
SignalEnum::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
// handle the case of a one bit signal whose value is set to `0`
"0" => {
// lookup signal idx
let hash = &word[1..];
let signal_idx = signal_map.get(hash).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
SignalEnum::Data {
name,
signal_type,
ref mut signal_error,
num_bits,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
continue;
}
// Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None`
match num_bits {
Some(ref num_bits) => {
if *num_bits != 1 {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
of signal_type {signal_type:?} is expected to be `1` not \
`{num_bits}`. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
*signal_error = Some(msg);
continue;
}
}
None => {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {signal_type:?}. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
Err(msg)?;
}
};
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
nums_encoded_as_fixed_width_le_u8.push(0u8);
let mut curr_num_bytes = 1;
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
Ok(())
}
SignalEnum::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
"1" => {
// lokup signal idx
let hash = &word[1..];
let signal_idx = signal_map.get(hash).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
SignalEnum::Data {
name,
signal_type,
ref mut signal_error,
num_bits,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
continue;
}
// Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None`
match num_bits {
Some(ref num_bits) => {
if *num_bits != 1 {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
of signal_type {signal_type:?} is expected to be `1` not \
`{num_bits}`. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
*signal_error = Some(msg);
continue;
}
}
None => {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {signal_type:?}. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
Err(msg)?;
}
};
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
nums_encoded_as_fixed_width_le_u8.push(1u8);
let mut curr_num_bytes = 1;
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
Ok(())
}
SignalEnum::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
// other one bit cases
"x" | "X" | "z" | "Z" | "u" | "U" | "h" | "H" | "l" | "L" | "w" | "W" | "-" => {
// Select value and turn to lowercase for consistency
let val = word[0..1].to_ascii_lowercase();
// lokup signal idx
let hash = &word[1..];
let signal_idx = signal_map.get(hash).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
SignalEnum::Data {
name,
signal_type,
ref mut signal_error,
num_bits,
string_vals,
byte_len_of_string_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
continue;
}
// Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None`
match num_bits {
Some(ref num_bits) => {
if *num_bits != 1 {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
of signal_type {signal_type:?} is expected to be `1` not \
`{num_bits}`. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
*signal_error = Some(msg);
continue;
}
}
None => {
let (f, l) = (file!(), line!());
let msg = format!(
"\
Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {signal_type:?}. \
This error occurred while parsing the vcd file at \
{cursor:?}"
);
Err(msg)?;
}
};
// record timestamp at which this event occurs
lsb_indxs_of_string_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// record value
string_vals.push(val);
Ok(())
}
SignalEnum::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
// Store real values as a string as well and let the user parse it to an f64
"s" | " S" | "r" | "R" => {
let val = word[1..].to_string();
let (hash, cursor) = next_word!(word_reader)?;
// lokup signal idx
let signal_idx = signal_map.get(hash).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
SignalEnum::Data {
ref mut signal_error,
string_vals,
byte_len_of_string_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
continue;
}
// record timestamp at which this event occurs
lsb_indxs_of_string_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// record string value
string_vals.push(val);
Ok(())
}
SignalEnum::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
_ => {}
}
}
Ok(())
}

335
src/vcd/parse/metadata.rs Normal file
View file

@ -0,0 +1,335 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use chrono::prelude::{DateTime, Utc};
use itertools::Itertools;
use super::super::reader::{next_word, Cursor, WordReader};
use super::super::types::{Metadata, Timescale, Version};
use super::combinator_atoms::{digit, tag, take_until, take_while};
use super::types::ParseResult;
pub(super) fn parse_date(
word_and_ctx1: (&str, &Cursor),
word_and_ctx2: (&str, &Cursor),
word_and_ctx3: (&str, &Cursor),
word_and_ctx4: (&str, &Cursor),
word_and_ctx5: (&str, &Cursor),
) -> Result<DateTime<Utc>, String> {
let day = {
// check for another word in the file
let (word, cursor) = word_and_ctx1;
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
if !days.contains(&word) {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{word} is not a valid weekday : expected one of {days:?}\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
word.to_string()
};
let month = {
// check for another word in the file
let (word, cursor) = word_and_ctx2;
let months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
];
if !months.contains(&word) {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{word} is not a valid month : expected one of {months:?}\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
word.to_string()
};
let date = {
// check for another word in the file
let (word, cursor) = word_and_ctx3;
let date: u8 = match word.to_string().parse() {
Ok(date) => date,
Err(e) => return Err(format!("Error near {}:{}. {e}", file!(), line!())),
};
if date > 31 {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{word} is not a valid date : must be between 0 and 31\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
date.to_string()
};
let (hh, mm, ss) = {
// get hour
let (word, cursor) = word_and_ctx4;
let res = take_until(word, b':');
res.assert_match()?;
let hh: u8 = res
.matched
.to_string()
.parse()
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if hh > 23 {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{hh} is not a valid hour : must be between 0 and 23\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
// get minute
let word = &res.residual[1..]; // chop off colon which is at index 0
let res = take_until(word, b':');
res.assert_match()?;
let mm: u8 = res
.matched
.to_string()
.parse()
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if mm > 60 {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{mm} is not a valid minute : must be between 0 and 60\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
// get second
// let ss : u8 = remainder.to_string().parse().unwrap();
res.assert_residual()?;
let residual = &res.residual[1..]; // chop of colon which is at index 0
let ss: u8 = residual
.to_string()
.parse()
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if ss > 60 {
let msg = format!("Error near {}:{}.", file!(), line!());
let msg2 = format!("{ss} is not a valid second : must be between 0 and 60\n");
let msg3 = format!("failure location: {cursor:?}");
return Err(format!("{}{}{}", msg, msg2, msg3));
}
(hh.to_string(), mm.to_string(), ss.to_string())
};
let year = {
// check for another word in the file
let (word, _) = word_and_ctx5;
word.to_string()
};
// unfortunately, the minutes, seconds, and hour could occur in an
// unexpected order
let full_date = format!("{day} {month} {date} {hh}:{mm}:{ss} {year}");
let full_date = DateTime::parse_from_str(full_date.as_str(), "%a %b %e %T %Y");
if full_date.is_ok() {
return Ok(full_date.unwrap().into());
}
Err(format!(
"Error near {}:{}. Failed to parse date.",
file!(),
line!()
))
}
pub(super) fn parse_version<R: std::io::Read>(
word_reader: &mut WordReader<R>,
) -> Result<Version, String> {
let mut version = String::new();
loop {
let (word, _) = next_word!(word_reader)?;
if word == "$end" {
// truncate trailing whitespace
let version = version[0..(version.len() - 1)].to_string();
return Ok(Version(version));
} else {
version.push_str(word);
version.push(' ');
}
}
}
pub(super) fn parse_timescale<R: std::io::Read>(
word_reader: &mut WordReader<R>,
) -> Result<(Option<u32>, Timescale), String> {
// we might see `1ps $end` or `1 ps $end`
// first get timescale
let (word, _) = next_word!(word_reader)?;
let ParseResult { matched, residual } = take_while(word, digit);
let scalar = matched;
let scalar: u32 = scalar
.to_string()
.parse()
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
let timescale = {
if residual.is_empty() {
let (word, _) = next_word!(word_reader)?;
let unit = match word {
"fs" => Ok(Timescale::Fs),
"ps" => Ok(Timescale::Ps),
"ns" => Ok(Timescale::Ns),
"us" => Ok(Timescale::Us),
"ms" => Ok(Timescale::Ms),
"s" => Ok(Timescale::S),
_ => Err(format!(
"Error near {}:{}. Unknown unit {word}.",
file!(),
line!()
)),
}?;
(Some(scalar), unit)
} else {
let unit = match residual {
"fs" => Ok(Timescale::Fs),
"ps" => Ok(Timescale::Ps),
"ns" => Ok(Timescale::Ns),
"us" => Ok(Timescale::Us),
"ms" => Ok(Timescale::Ms),
"s" => Ok(Timescale::S),
_ => Err(format!(
"Error near {}:{}. Unknown unit {residual}.",
file!(),
line!()
)),
}?;
(Some(scalar), unit)
}
};
// then check for the `$end` keyword
let (word, _) = next_word!(word_reader)?;
tag(word, "$end").assert_match()?;
Ok(timescale)
}
pub(super) fn parse_metadata<R: std::io::Read>(
word_reader: &mut WordReader<R>,
) -> Result<Metadata, String> {
let mut metadata = Metadata {
date: None,
version: None,
timescale: (None, Timescale::Unit),
};
loop {
// check for another word in the file
let (word, _) = next_word!(word_reader)?;
let ParseResult { matched, residual } = tag(word, "$");
match matched {
// we hope that this word stars with a `$`
"$" => {
match residual {
"date" => {
// a date is typically composed of the 5 following words which can
// occur in any order:
// {Day, Month, Date(number in month), hh:mm:ss, year}.
// Thus, we must lookahead read the 5 next words, and try our date
// parser on 5! = 120 permutations of the 5 words.
//
// It is also possible that within each permutation, the hours,
// minutes, and seconds could be in an unusual order, which means
// that we may search up to 6 different permutations oh hh::mm:ss,
// for an upper bound total of 720 permutations
//
// While looking ahead, if one of the 5 words in `$end`, we have to
// immediately stop trying to get more words.
let mut found_end = false;
let mut lookahead_5_words: Vec<(String, Cursor)> = Vec::new();
for _ in 0..5 {
let (word, cursor) = next_word!(word_reader)?;
let word = word.to_string();
match word.as_str() {
"$end" => {
found_end = true;
break;
}
_ => {
lookahead_5_words.push((word, cursor));
}
};
}
// we no longer attempt to parse date if we weren't able to lookahead 5
// words
if found_end {
continue;
}
let permutations = lookahead_5_words
.iter()
.permutations(lookahead_5_words.len());
// go ahead and search for a match amongst permuted date text
for mut permutations in permutations {
let (w1, s1) = permutations.pop().unwrap();
let arg_1 = (&w1[..], s1);
let (w2, s2) = permutations.pop().unwrap();
let arg_2 = (&w2[..], s2);
let (w3, s3) = permutations.pop().unwrap();
let arg_3 = (&w3[..], s3);
let (w4, s4) = permutations.pop().unwrap();
let arg_4 = (&w4[..], s4);
let (w5, s5) = permutations.pop().unwrap();
let arg_5 = (&w5[..], s5);
let parsed_date = parse_date(arg_1, arg_2, arg_3, arg_4, arg_5);
// store date and exit loop if a match is found
if parsed_date.is_ok() {
metadata.date = Some(parsed_date?);
break;
}
}
}
"version" => {
let version = parse_version(word_reader);
if version.is_ok() {
metadata.version = Some(version.unwrap());
}
}
"timescale" => {
let timescale = parse_timescale(word_reader);
if timescale.is_ok() {
metadata.timescale = timescale.unwrap();
}
}
"scope" => break,
"var" => break,
// we keep searching for words until we've found one of the following
// keywords, ["version", "timescale", "scope", "var"]
_ => {}
}
}
// if word does not start with `$`, then we keep looping
_ => {}
}
}
Ok(metadata)
}

522
src/vcd/parse/scopes.rs Normal file
View file

@ -0,0 +1,522 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
/// part of the vcd parser that handles parsing the signal tree and
/// building the resulting signal tree
use std::collections::HashMap;
use super::super::reader::{curr_word, next_word, WordReader};
use super::super::signal::{SignalEnum, SignalType};
use super::super::types::{Scope, ScopeIdx, SignalIdx, VCD};
use super::combinator_atoms::{ident, tag};
use super::types::ParseResult;
pub(super) fn parse_var<R: std::io::Read>(
word_reader: &mut WordReader<R>,
parent_scope_idx: ScopeIdx,
vcd: &mut VCD,
signal_map: &mut HashMap<String, SignalIdx>,
path: &Vec<String>,
) -> Result<(), String> {
let (word, cursor) = next_word!(word_reader)?;
let expected_types = [
"event",
"integer",
"parameter",
"real",
"realtime",
"reg",
"string",
"supply0",
"supply1",
"time",
"tri",
"triand",
"trior",
"trireg",
"tri0",
"tri1",
"wand",
"wire",
"wor",
];
// $var parameter 3 a IDLE $end
// ^^^^^^^^^ - var_type
let var_type = match word {
"event" => Ok(SignalType::Event),
"integer" => Ok(SignalType::Integer),
"parameter" => Ok(SignalType::Parameter),
"real" => Ok(SignalType::Real),
"realtime" => Ok(SignalType::RealTime),
"reg" => Ok(SignalType::Reg),
"string" => Ok(SignalType::Str),
"supply0" => Ok(SignalType::Supply0),
"supply1" => Ok(SignalType::Supply1),
"tri" => Ok(SignalType::Tri),
"triand" => Ok(SignalType::TriAnd),
"trior" => Ok(SignalType::TriOr),
"trireg" => Ok(SignalType::TriReg),
"tri0" => Ok(SignalType::Tri0),
"tri1" => Ok(SignalType::Tri1),
"time" => Ok(SignalType::Time),
"wand" => Ok(SignalType::WAnd),
"wire" => Ok(SignalType::Wire),
"wor" => Ok(SignalType::WOr),
_ => {
let err = format!(
"Error near {}:{} \
found keyword `{word}` but expected one of \
{expected_types:?} on {cursor:?}",
file!(),
line!()
);
Err(err)
}
}?;
let (word, cursor) = next_word!(word_reader)?;
let parse_err = format!("failed to parse as usize on {cursor:?}");
// $var parameter 3 a IDLE $end
// ^ - num_bits
let num_bits = match var_type {
SignalType::Event
| SignalType::Integer
| SignalType::Parameter
| SignalType::Reg
| SignalType::Supply0
| SignalType::Supply1
| SignalType::Tri
| SignalType::TriAnd
| SignalType::TriOr
| SignalType::TriReg
| SignalType::Tri0
| SignalType::Tri1
| SignalType::Time
| SignalType::WAnd
| SignalType::Wire
| SignalType::WOr => {
let num_bits = word
.parse::<usize>()
.unwrap_or_else(|_| panic!("{}", parse_err));
let num_bits = u32::try_from(num_bits).map_err(|_| {
format!(
"Error near {}:{} while parsing vcd file at {cursor:?}. \
This signal has {num_bits} > 2^32 - 1 bits.",
file!(),
line!()
)
})?;
Some(num_bits)
}
// for strings, reals, and realtimes we don't really care what the number of bits is
_ => None,
};
// $var parameter 3 a IDLE $end
// ^ - signal_alias
let (word, _) = next_word!(word_reader)?;
let signal_alias = word.to_string();
// $var parameter 3 a IDLE $end
// ^^^^ - full_signal_name(can extend until $end)
let mut full_signal_name = Vec::<String>::new();
let mut size = None;
loop {
let (word, _) = next_word!(word_reader)?;
match word {
"$end" => break,
other if other.starts_with('[') => size = Some(other.to_string()),
_ => full_signal_name.push(word.to_string()),
}
}
let full_signal_name = full_signal_name.join(" ");
let num_bytes = if num_bits.is_some() {
let bytes_required = SignalEnum::bytes_required(num_bits.unwrap(), &full_signal_name)?;
Some(bytes_required)
} else {
None
};
// Is the current variable an alias to a signal already encountered?
// if so, handle ref_signal_idx accordingly, if not, add signal to hash
// map
let (signal, signal_idx) = match signal_map.get(&signal_alias) {
Some(ref_signal_idx) => {
let signal_idx = SignalIdx(vcd.all_signals.len());
let signal = SignalEnum::Alias {
name: full_signal_name.clone(),
path: path
.iter()
.cloned()
.chain([full_signal_name])
.collect::<Vec<String>>(),
signal_alias: *ref_signal_idx,
};
(signal, signal_idx)
}
None => {
let signal_idx = SignalIdx(vcd.all_signals.len());
signal_map.insert(signal_alias.to_string(), signal_idx);
let signal = SignalEnum::Data {
name: full_signal_name.clone(),
path: path
.iter()
.cloned()
.chain([full_signal_name])
.collect::<Vec<String>>(),
signal_type: var_type,
index: size,
signal_error: None,
num_bits,
num_bytes,
self_idx: signal_idx,
nums_encoded_as_fixed_width_le_u8: vec![],
string_vals: vec![],
lsb_indxs_of_num_tmstmp_vals_on_tmln: vec![],
byte_len_of_num_tmstmp_vals_on_tmln: vec![],
byte_len_of_string_tmstmp_vals_on_tmln: vec![],
lsb_indxs_of_string_tmstmp_vals_on_tmln: vec![],
};
(signal, signal_idx)
}
};
vcd.all_signals.push(signal);
let ScopeIdx(parent_scope_idx_usize) = parent_scope_idx;
let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx_usize).unwrap();
parent_scope.child_signals.push(signal_idx);
Ok(())
}
/// Sometimes, variables can be listed outside of scopes.
/// We call these orphaned vars.
fn parse_orphaned_vars<R: std::io::Read>(
word_reader: &mut WordReader<R>,
vcd: &mut VCD,
signal_map: &mut HashMap<String, SignalIdx>,
) -> Result<(), String> {
// create scope for unscoped signals if such a scope does not
// yet exist
let scope_name = "Orphaned Signals";
// set default scope_idx to the count of existing scope as we
// generally set scope.self_idx to the number of existing scopes
// when that particular scope was inserted
let mut scope_idx = ScopeIdx(vcd.all_scopes.len());
// Override scope_idx if we find a scope named "Orphaned Signals"
// already exists
let mut scope_already_exists = false;
for scope in &vcd.all_scopes {
if scope.name == scope_name {
scope_idx = scope.self_idx;
scope_already_exists = true;
break;
}
}
if !scope_already_exists {
vcd.all_scopes.push(Scope {
name: scope_name.to_string(),
self_idx: scope_idx,
child_signals: vec![],
child_scopes: vec![],
});
vcd.root_scopes.push(scope_idx);
}
// we can go ahead and parse the current var as we've already encountered
// "$var" before now.
parse_var(word_reader, scope_idx, vcd, signal_map, &vec![])?;
loop {
let (word, cursor) = next_word!(word_reader)?;
match word {
"$var" => {
parse_var(word_reader, scope_idx, vcd, signal_map, &vec![])?;
}
"$scope" => break,
_ => {
let msg = format!(
"Error near {}:{}.\
Expected $scope or $var, found \
{word} at {cursor:?}",
file!(),
line!()
);
Err(msg)?;
}
};
}
Ok(())
}
fn parse_scopes_inner<R: std::io::Read>(
word_reader: &mut WordReader<R>,
parent_scope_idx: Option<ScopeIdx>,
vcd: &mut VCD,
signal_map: &mut HashMap<String, SignalIdx>,
path: &Vec<String>,
) -> Result<(), String> {
// $scope module reg_mag_i $end
// ^^^^^^ - module keyword
let (keyword, cursor) = next_word!(word_reader)?;
let expected = ["module", "begin", "task", "function", "fork"];
if expected.contains(&keyword) {
Ok(())
} else {
let err = format!(
"Error near {}:{}. \
found keyword `{keyword}` but expected one of \
{expected:?} on {cursor:?}",
file!(),
line!()
);
Err(err)
}?;
// $scope module reg_mag_i $end
// ^^^^^^^^^ - scope name
let (scope_name, _) = next_word!(word_reader)?;
// In some cases there are VCD files which have scopes without names.
// since these occur in the wild, we'll tolerate them even if it is unclear
// if it is supported or not by the spec.
if scope_name != "$end" {
let mut path = path.clone();
path.push(scope_name.to_string());
let curr_scope_idx = ScopeIdx(vcd.all_scopes.len());
// register this scope as a child of the current parent scope
// if there is a parent scope, or else we register this scope as
// root scope
match parent_scope_idx {
Some(ScopeIdx(parent_scope_idx)) => {
let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap();
parent_scope.child_scopes.push(curr_scope_idx);
}
None => vcd.root_scopes.push(curr_scope_idx),
}
// add this scope to list of existing scopes
vcd.all_scopes.push(Scope {
name: scope_name.to_string(),
self_idx: curr_scope_idx,
child_signals: vec![],
child_scopes: vec![],
});
// $scope module reg_mag_i $end
// ^^^^ - end keyword
ident(word_reader, "$end")?;
loop {
let (word, cursor) = next_word!(word_reader)?;
let ParseResult { matched, residual } = tag(word, "$");
match matched {
// we hope that this word starts with a `$`
"$" => {
match residual {
"scope" => {
// recursive - parse inside of current scope tree
parse_scopes_inner(
word_reader,
Some(curr_scope_idx),
vcd,
signal_map,
&path,
)?;
}
"var" => {
parse_var(word_reader, curr_scope_idx, vcd, signal_map, &path)?;
}
"upscope" => {
ident(word_reader, "$end")?;
break;
}
// we ignore comments
"comment" => loop {
if ident(word_reader, "$end").is_ok() {
break;
}
},
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{residual}` but expected \
`$scope`, `$var`, `$comment`, or `$upscope` \
on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{matched}` but \
expected `$` on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
} else {
// We'll be conservative and only allow new scopes in this case, and make the nameless
// scope completely transparent. I.e.
// $scope module a $end
// $scope module $end
// $scope module b $end
// ...
// $upscope
// $upscope
// $upscope
// will create `a.b`
loop {
let (word, cursor) = next_word!(word_reader)?;
let ParseResult { matched, residual } = tag(word, "$");
match matched {
// we hope that this word starts with a `$`
"$" => {
match residual {
"scope" => {
// recursive - parse inside of current scope tree
parse_scopes_inner(
word_reader,
parent_scope_idx,
vcd,
signal_map,
&path,
)?;
}
"upscope" => {
ident(word_reader, "$end")?;
break;
}
// we ignore comments
"comment" => loop {
if ident(word_reader, "$end").is_ok() {
break;
}
},
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{residual}` in annonyoums scope but expected \
`$scope`, `$comment`, or `$upscope` \
on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{matched}` but \
expected `$` on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
}
Ok(())
}
pub(super) fn parse_scopes<R: std::io::Read>(
word_reader: &mut WordReader<R>,
vcd: &mut VCD,
signal_map: &mut HashMap<String, SignalIdx>,
) -> Result<(), String> {
// get the current word
let (word, _) = curr_word!(word_reader)?;
// we may have orphaned vars that occur before the first scope
if word == "$var" {
parse_orphaned_vars(word_reader, vcd, signal_map)?;
}
// get the current word
let (word, cursor) = curr_word!(word_reader)?;
// the current word should be "scope", as `parse_orphaned_vars`(if it
// was called), should have terminated upon encountering "$scope".
// If `parse_orphaned_vars` was not called, `parse_scopes` should still
// have only been called if the caller encountered the word "$scope"
if word != "$scope" {
let msg = format!(
"Error near {}:{}.\
Expected $scope or $var, found \
{word} at {cursor:?}",
file!(),
line!()
);
return Err(msg);
}
// now for the interesting part
parse_scopes_inner(word_reader, None, vcd, signal_map, &vec![])?;
// let err = format!("reached end of file without parser leaving {}", function_name!());
let expected_keywords = ["$scope", "$enddefinitions"];
// there could be multiple signal trees, and unfortunately, we
// can't merge the earlier call to `parse_scopes_inner` into this loop
// because this loop gets a word from `next_word` instead of
// `curr_word()`.
loop {
let (word, cursor) = next_word!(word_reader)?;
match word {
"$scope" => {
parse_scopes_inner(word_reader, None, vcd, signal_map, &vec![])?;
}
"$enddefinitions" => {
ident(word_reader, "$end")?;
break;
}
"$comment" => {
// although we don't store comments, we still need to advance the
// word_reader cursor to the end of the comment
loop {
if ident(word_reader, "$end").is_ok() {
break;
}
}
}
_ => {
let err = format!(
"Error near {}:{} \
found keyword `{word}` but expected one of \
{expected_keywords:?} on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
Ok(())
}

27
src/vcd/parse/types.rs Normal file
View file

@ -0,0 +1,27 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
#[derive(Debug)]
pub(super) struct ParseResult<'a> {
pub(super) matched: &'a str,
pub(super) residual: &'a str,
}
impl<'a> ParseResult<'a> {
pub(super) fn assert_match(&self) -> Result<&str, String> {
if self.matched.is_empty() {
Err("no match".to_string())
} else {
Ok(self.matched)
}
}
pub(super) fn assert_residual(&self) -> Result<&str, String> {
if self.residual.is_empty() {
Err("no residual".to_string())
} else {
Ok(self.residual)
}
}
}

131
src/vcd/reader.rs Normal file
View file

@ -0,0 +1,131 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use std::collections::VecDeque;
use std::io;
use std::io::BufRead;
use std::slice;
use std::str;
#[derive(Debug, Clone)]
pub(super) struct Line(pub(super) usize);
#[derive(Debug, Clone)]
pub(super) struct Word(pub(super) usize);
#[derive(Debug, Clone)]
pub(super) struct Cursor(pub(super) Line, pub(super) Word);
pub(super) struct WordReader<R: io::Read> {
reader: io::BufReader<R>,
eof: bool,
buffers: Vec<String>,
curr_line: usize,
str_slices: VecDeque<(*const u8, usize, Cursor)>,
curr_slice: Option<(*const u8, usize, Cursor)>,
}
impl<R: std::io::Read> WordReader<R> {
pub(super) fn new(file: R) -> WordReader<R> {
let reader = io::BufReader::new(file);
WordReader {
reader,
eof: false,
buffers: vec![],
curr_line: 0,
str_slices: VecDeque::new(),
curr_slice: None,
}
}
pub(super) fn next_word(&mut self) -> Option<(&str, Cursor)> {
// although reaching the eof is not technically an error, in most cases,
// we treat it like one in the rest of the codebase.
// if there are no more words in the buffer, attempt to read more content
// from the file
if self.str_slices.is_empty() {
self.buffers.clear();
if self.eof {
return None;
}
let num_buffers = 10;
for buf_idx in 0..num_buffers {
self.buffers.push(String::new());
self.curr_line += 1;
let bytes_read = self.reader.read_line(&mut self.buffers[buf_idx]).unwrap();
// if we've reached the end of the file on the first attempt to read
// a line in this for loop, no further attempts are necessary and we
if bytes_read == 0 {
self.eof = true;
break;
}
let words = self.buffers[buf_idx].split_ascii_whitespace();
for word in words.enumerate() {
let (word_idx, word) = word;
let position = Cursor(Line(self.curr_line), Word(word_idx + 1));
self.str_slices
.push_back((word.as_ptr(), word.len(), position))
}
}
}
// if after we've attempted to read in more content from the file,
// there are still no words...
if self.str_slices.is_empty() {
return None;
}
// if we make it here, we return the next word
unsafe {
let (ptr, len, position) = self.str_slices.pop_front().unwrap();
let slice = slice::from_raw_parts(ptr, len);
self.curr_slice = Some((ptr, len, position.clone()));
return Some((str::from_utf8(slice).unwrap(), position));
};
}
pub(super) fn curr_word(&mut self) -> Option<(&str, Cursor)> {
match &self.curr_slice {
Some(slice) => unsafe {
let (ptr, len, position) = slice.clone();
let slice = slice::from_raw_parts(ptr, len);
return Some((str::from_utf8(slice).unwrap(), position));
},
None => None,
}
}
}
macro_rules! next_word {
($word_reader:ident) => {
$word_reader.next_word().ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Did not expect to reach end of file here.",
file!(),
line!()
)
})
};
}
macro_rules! curr_word {
($word_reader:ident) => {
$word_reader.curr_word().ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. A call to curr_word! shouldn't \
fail unless next_word has not yet been invoked.",
file!(),
line!()
)
})
};
}
pub(super) use curr_word;
pub(super) use next_word;

715
src/vcd/signal.rs Normal file
View file

@ -0,0 +1,715 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use super::types;
use super::types::SignalIdx;
use num::BigUint;
// Index to the least significant byte of a timestamp
// value on the timeline
#[derive(Debug, Copy, Clone)]
pub struct LsbIdxOfTmstmpValOnTmln(pub(super) u32);
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum SignalType {
Event,
Integer,
Parameter,
Real,
RealTime,
Reg,
Str,
Supply0,
Supply1,
Time,
Tri,
TriAnd,
TriOr,
TriReg,
Tri0,
Tri1,
WAnd,
Wire,
WOr,
}
#[derive(Debug, PartialEq)]
pub enum SignalValue {
BigUint(BigUint),
String(String),
}
pub struct QueryResult<T> {
pub current: Option<(TimeStamp, T)>,
pub next: Option<TimeStamp>,
}
pub struct Signal<'a>(pub(super) &'a SignalEnum);
impl<'a> Signal<'a> {
pub fn name(&self) -> String {
let Signal(signal_enum) = &self;
signal_enum.name()
}
pub fn name_with_index(&self) -> String {
let Signal(signal_enum) = &self;
signal_enum.name_with_index()
}
pub fn index(&self) -> Option<String> {
let Signal(signal_enum) = &self;
signal_enum.index()
}
pub fn path(&self) -> &[String] {
match self.0 {
SignalEnum::Data { path, .. } => path,
SignalEnum::Alias { path, .. } => path,
}
}
pub fn signal_type(&self) -> Option<&SignalType> {
let Signal(signal_enum) = &self;
signal_enum.signal_type()
}
pub fn real_idx(&self) -> SignalIdx {
match self.0 {
SignalEnum::Data { self_idx, .. } => *self_idx,
SignalEnum::Alias { signal_alias, .. } => *signal_alias,
}
}
pub fn num_bits(&self) -> Option<u32> {
let Signal(signal_enum) = &self;
signal_enum.bits_required()
}
// NOTE: (zoq) I am removing thse because they aren't used in Surfer so I can't test them
// properly
/*
pub fn query_string_val_on_tmln(
&self,
desired_time: &BigUint,
vcd: &types::VCD,
) -> Result<String, SignalErrors> {
let Signal(signal_enum) = &self;
signal_enum
.query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|QueryResult{current, next: _}| current.map(|c| c.1))
}
pub fn query_num_val_on_tmln(
&self,
desired_time: &BigUint,
vcd: &types::VCD,
) -> Result<Option<BigUint>, SignalErrors> {
let Signal(signal_enum) = &self;
signal_enum
.query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|QueryResult{current, next: _}| current.map(|c| c.1))
}
*/
pub fn query_val_on_tmln(
&self,
desired_time: &BigUint,
vcd: &types::VCD,
) -> Result<QueryResult<SignalValue>, SignalErrors> {
let Signal(signal_enum) = &self;
let num_query_out = signal_enum.query_num_val_on_tmln(
desired_time,
&vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals,
);
let str_query_out = signal_enum.query_string_val_on_tmln(
desired_time,
&vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals,
);
// Both num and str will return the newest value that is closest to
// the desired time. If both have valid values, select the most recent
// one
match (num_query_out, str_query_out) {
(Ok(num_result), Ok(str_result)) => {
let next = match (num_result.next, str_result.next) {
(Some(n), Some(s)) => Some(n.min(s)),
(Some(n), None) => Some(n),
(None, Some(s)) => Some(s),
(None, None) => None,
};
match (num_result.current, str_result.current) {
(Some((num_time, num_value)), Some((str_time, str_value))) => {
if num_time > str_time {
Ok(QueryResult {
current: Some((num_time, SignalValue::BigUint(num_value))),
next,
})
} else {
Ok(QueryResult {
current: Some((str_time, SignalValue::String(str_value))),
next,
})
}
}
(Some((num_time, num_val)), None) => Ok(QueryResult {
current: Some((num_time, SignalValue::BigUint(num_val))),
next,
}),
(None, Some((str_time, str_value))) => Ok(QueryResult {
current: Some((str_time, SignalValue::String(str_value))),
next,
}),
(None, None) => Ok(QueryResult {
current: None,
next,
}),
}
}
(_e, Err(e)) => Err(e),
(Err(e), _e) => Err(e),
}
}
}
#[derive(Debug)]
pub(super) enum SignalEnum {
Data {
name: String,
path: Vec<String>,
signal_type: SignalType,
/// The optional [start:end] part of the signal name that is sometimes
/// added to signals
index: Option<String>,
/// I've seen a 0 bit signal parameter in a xilinx
/// simulation before that gets assigned 1 bit values.
/// I consider this to be bad behavior. We capture such
/// errors in the following type:
signal_error: Option<String>,
num_bits: Option<u32>,
num_bytes: Option<u16>,
/// TODO : may be able to remove self_idx
self_idx: SignalIdx,
/// A signal may take on a new value and hold that value
/// for sometime. We only need to record the value of a signal
/// when it changes(the is what VCDs tend to do).
/// A signal may need x amount of bytes to record its largest
/// possible value, so we record every single value of a given
/// signal as a sequence of x number of u8s.
/// For example, we might find that `my_signal.
/// nums_encoded_as_fixed_width_le_u8`
/// has two 32 bit values, namely, 1 and 2, encoded as follows:
/// my_signal.nums_encoded_as_fixed_width_le_u8 = vec![1u8, 0u8,
/// 0u8, 0u8, 2u8, 0u8, 0u8, 0u8];
nums_encoded_as_fixed_width_le_u8: Vec<u8>,
string_vals: Vec<String>,
/// we could do Vec<(LsbIdxOfTmstmpValOnTmln, u8)>, but I
/// suspect that Vec<LsbIdxOfTmstmpValOnTmln> is more cache
/// friendly. We use ``LsbIdxOfTmstmpValOnTmln`` to index into
/// the LSB of a particular timestamp encoded as the
/// minimum length u8 sequence within
/// ``vcd.tmstmps_encoded_as_u8s``, and we use the values in
/// ``byte_len_of_num_tmstmp_vals_on_tmln`` to determine how
/// many u8 values a particular timestamp is composed of.
lsb_indxs_of_num_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
byte_len_of_num_tmstmp_vals_on_tmln: Vec<u8>,
byte_len_of_string_tmstmp_vals_on_tmln: Vec<u8>,
lsb_indxs_of_string_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
},
Alias {
name: String,
path: Vec<String>,
signal_alias: SignalIdx,
},
}
#[derive(Debug)]
pub enum SignalErrors {
EmptyTimeline,
TimelineNotMultiple,
StrTmlnLenMismatch,
OrderingFailure {
lhs_time: BigUint,
mid_time: BigUint,
rhs_time: BigUint,
},
PointsToAlias,
NoNumBytes,
Other(String),
}
// these are thin type aliases primarily to make code more readable later on
type TimeStamp = BigUint;
type SignalValNum = BigUint;
// getter functions
impl SignalEnum {
pub fn name(&self) -> String {
match self {
SignalEnum::Data { name, .. } => name,
SignalEnum::Alias { name, .. } => name,
}
.clone()
}
pub fn signal_type(&self) -> Option<&SignalType> {
match self {
SignalEnum::Data { signal_type, .. } => Some(signal_type),
// TODO: Follow aliases?
SignalEnum::Alias { .. } => None,
}
.clone()
}
pub fn name_with_index(&self) -> String {
match self {
SignalEnum::Data {
name, index: None, ..
} => format!("{name}"),
SignalEnum::Data {
name,
index: Some(size),
..
} => format!("{name} {size}"),
SignalEnum::Alias { name, .. } => name.clone(),
}
}
pub fn index(&self) -> Option<String> {
match self {
SignalEnum::Data { index, .. } => index.clone(),
SignalEnum::Alias { .. } => None,
}
}
}
// helper functions ultimately used by Signal's query functions later on
impl SignalEnum {
/// Computes the bytes required to store a signal's numerical value
/// using the num_bits which another function would provide from
/// the num_bits field of the Signal::Data variant.
pub(super) fn bytes_required(num_bits: u32, name: &String) -> Result<u16, String> {
let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
let bytes_required = u16::try_from(bytes_required).map_err(|_| {
format!(
"Error near {}:{}. Signal {name} of length num_bits requires \
{bytes_required} > 65536 bytes.",
file!(),
line!()
)
})?;
Ok(bytes_required)
}
/// This function takes an event_idx which(is used to index into the
/// global timeline field of a VCD struct instance) and computes
/// the time pointed at by event_idx.
/// This function also uses the same idx to index into the
/// string_vals field of an instance of the Signal::Data variant
/// and gets a string value.
/// The function returns a tuple of the timestamp and string value.
fn time_and_str_val_at_event_idx(
&self,
event_idx: usize,
tmstmps_encoded_as_u8s: &Vec<u8>,
) -> Result<(TimeStamp, &str), SignalErrors> {
let (
string_vals,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
) = match self {
SignalEnum::Data {
string_vals,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
..
} => Ok((
string_vals,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
)),
SignalEnum::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// get index
let LsbIdxOfTmstmpValOnTmln(timestamp_idx) =
lsb_indxs_of_string_tmstmp_vals_on_tmln[event_idx];
let timestamp_idx = timestamp_idx as usize;
if byte_len_of_string_tmstmp_vals_on_tmln.is_empty() {
return Err(SignalErrors::EmptyTimeline);
}
// form timestamp
let byte_len = byte_len_of_string_tmstmp_vals_on_tmln[event_idx] as usize;
let timestamp = &tmstmps_encoded_as_u8s[timestamp_idx..(timestamp_idx + byte_len)];
let timestamp = BigUint::from_bytes_le(timestamp);
// get signal value
let signal_val = string_vals[event_idx].as_str();
Ok((timestamp, signal_val))
}
/// This function takes an event_idx which(is used to index into the
/// global timeline field of a VCD struct instance) and computes
/// the time pointed at by event_idx.
/// This function also uses the same idx to index into the
/// nums_encoded_as_fixed_width_le_u8 and
/// byte_len_of_num_tmstmp_vals_on_tmln fields of an instance
/// of the Signal::Data variant to compute the signal's corresponding
/// numerical value at the time pointed at by event_didx.
/// The function returns a tuple of the timestamp and numerical
/// value.
fn time_and_num_val_at_event_idx(
&self,
event_idx: usize,
tmstmps_encoded_as_u8s: &Vec<u8>,
) -> Result<(TimeStamp, SignalValNum), SignalErrors> {
let (
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
) = match self {
SignalEnum::Data {
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
..
} => Ok((
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
)),
SignalEnum::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// get index
let LsbIdxOfTmstmpValOnTmln(timestamp_idx) =
lsb_indxs_of_num_tmstmp_vals_on_tmln[event_idx];
let timestamp_idx = timestamp_idx as usize;
// form timestamp
let byte_len = byte_len_of_num_tmstmp_vals_on_tmln[event_idx] as usize;
let timestamp = &tmstmps_encoded_as_u8s[timestamp_idx..(timestamp_idx + byte_len)];
let timestamp = BigUint::from_bytes_le(timestamp);
// get signal value
let bytes_per_value = num_bytes.ok_or_else(|| SignalErrors::NoNumBytes)?;
let bytes_per_value = bytes_per_value as usize;
let start_idx = event_idx * bytes_per_value;
let end_idx = (event_idx + 1) * bytes_per_value;
let signal_val = &nums_encoded_as_fixed_width_le_u8[start_idx..end_idx];
let signal_val = BigUint::from_bytes_le(signal_val);
Ok((timestamp, signal_val))
}
fn bits_required(&self) -> Option<u32> {
match self {
SignalEnum::Data { num_bits, .. } => *num_bits,
// TODO: Follow aliases?
SignalEnum::Alias { .. } => None,
}
}
}
// Val and string query functions.
// Function that take in a desired time on the timeline for a
// specific signal and return a numerical or string value in a Result,
// or an error in a Result.
impl SignalEnum {
pub fn query_string_val_on_tmln(
&self,
desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>,
) -> Result<QueryResult<String>, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
*idx
}
Self::Alias {
name: _,
signal_alias,
path: _,
} => {
let SignalIdx(idx) = signal_alias;
*idx
}
};
// if the signal idx points to data variant of the signal,
// extract:
// 1. the vector of string values
// 2. the vector of indices into timeline where events occur
// for this signal
// else we propagate Err(..).
let (string_vals, lsb_indxs_of_string_tmstmp_vals_on_tmln) = match &all_signals[signal_idx]
{
SignalEnum::Data {
ref string_vals,
ref lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => Ok((string_vals, lsb_indxs_of_string_tmstmp_vals_on_tmln)),
SignalEnum::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail
if lsb_indxs_of_string_tmstmp_vals_on_tmln.is_empty() {
return Ok(QueryResult {
current: None,
next: None
});
}
// the vector of string timeline lsb indices should have the same
// length as the vector of string values
if string_vals.len() != lsb_indxs_of_string_tmstmp_vals_on_tmln.len() {
return Err(SignalErrors::StrTmlnLenMismatch);
}
// check if we're requesting a value that occurs before the recorded
// start of the timeline
let (timeline_start_time, _) =
self.time_and_str_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time {
return Ok(QueryResult {
current: None,
next: Some(timeline_start_time),
});
}
let mut lower_idx = 0usize;
let mut upper_idx = lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1;
let (timeline_end_time, timeline_end_val) =
self.time_and_str_val_at_event_idx(upper_idx, tmstmps_encoded_as_u8s)?;
// check if we're requesting a value that occurs beyond the end of the timeline,
// if so, return the last value in this timeline
if *desired_time > timeline_end_time {
return Ok(QueryResult {
current: Some((timeline_end_time, timeline_end_val.to_string())),
next: None,
});
}
// This while loop is the meat of the lookup. Performance is log2(n),
// where n is the number of events on the timeline.
// We can assume that by the time we get here, that the desired_time
// is an event that occurs on the timeline, given that we handle any events
// occuring after or before the recorded tiimeline in the code above.
while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (curr_time, curr_val) =
self.time_and_str_val_at_event_idx(mid_idx, tmstmps_encoded_as_u8s)?;
let ordering = curr_time.cmp(desired_time);
match ordering {
std::cmp::Ordering::Less => {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1 {
Some(
self.time_and_str_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok(QueryResult {
current: Some((curr_time, curr_val.to_string())),
next: next_time,
});
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
}
}
}
let (left_time, left_val) =
self.time_and_str_val_at_event_idx(lower_idx - 1, tmstmps_encoded_as_u8s)?;
let (right_time, _) =
self.time_and_str_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?;
let ordered_left = left_time < *desired_time;
let ordered_right = *desired_time < right_time;
if !(ordered_left && ordered_right) {
return Err(SignalErrors::OrderingFailure {
lhs_time: left_time,
mid_time: desired_time.clone(),
rhs_time: right_time,
});
}
Ok(QueryResult {
current: Some((left_time, left_val.to_string())),
next: Some(right_time),
})
}
pub fn query_num_val_on_tmln(
&self,
desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>,
) -> Result<QueryResult<BigUint>, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
*idx
}
Self::Alias {
name: _,
path: _,
signal_alias,
} => {
let SignalIdx(idx) = signal_alias;
*idx
}
};
// if the signal idx points to data variant of the signal,
// extract:
// 1. the vector of LE u8 compressed values
// 2. the vector of indices into timeline where events occur
// for this signal
// 3. the number of bytes per value for this signal
// else we propagate Err(..).
let (nums_encoded_as_fixed_width_le_u8, lsb_indxs_of_num_tmstmp_vals_on_tmln, num_bytes) =
match &all_signals[signal_idx] {
SignalEnum::Data {
num_bytes,
ref nums_encoded_as_fixed_width_le_u8,
ref lsb_indxs_of_num_tmstmp_vals_on_tmln,
..
} => {
if num_bytes.is_none() {
return Err(SignalErrors::NoNumBytes);
}
Ok((
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
num_bytes,
))
}
SignalEnum::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail
if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() {
return Ok(QueryResult {
current: None,
next: None
});
}
// assertion that value_sequence is a proper multiple of
// timeline_markers
let bytes_required = num_bytes.ok_or_else(|| {
SignalErrors::Other(format!(
"Error near {}:{}. num_bytes empty.",
file!(),
line!()
))
})?;
if nums_encoded_as_fixed_width_le_u8.len()
!= (lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize))
{
return Err(SignalErrors::TimelineNotMultiple);
}
// check if we're requesting a value that occurs before the recorded
// start of the timeline
let (timeline_start_time, _) =
self.time_and_num_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time {
return Ok(QueryResult {
current: None,
next: Some(timeline_start_time),
});
}
let mut lower_idx = 0usize;
let mut upper_idx = lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1;
let (timeline_end_time, timeline_end_val) =
self.time_and_num_val_at_event_idx(upper_idx, tmstmps_encoded_as_u8s)?;
// check if we're requesting a value that occurs beyond the end of the timeline,
// if so, return the last value in this timeline
if *desired_time > timeline_end_time {
return Ok(QueryResult {
current: Some((timeline_end_time, timeline_end_val)),
next: None,
});
}
// This while loop is the meat of the lookup. Performance is log2(n),
// where n is the number of events on the timeline.
// We can assume that by the time we get here, that the desired_time
// is an event that occurs on the timeline, given that we handle any events
// occuring after or before the recorded tiimeline in the code above.
while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (curr_time, curr_val) =
self.time_and_num_val_at_event_idx(mid_idx, tmstmps_encoded_as_u8s)?;
let ordering = curr_time.cmp(desired_time);
match ordering {
std::cmp::Ordering::Less => {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1 {
Some(
self.time_and_num_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok(QueryResult {
current: Some((curr_time, curr_val)),
next: next_time,
});
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
}
}
}
let (left_time, left_val) =
self.time_and_num_val_at_event_idx(lower_idx - 1, tmstmps_encoded_as_u8s)?;
let (right_time, _) =
self.time_and_num_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?;
let ordered_left = left_time < *desired_time;
let ordered_right = *desired_time < right_time;
if !(ordered_left && ordered_right) {
return Err(SignalErrors::OrderingFailure {
lhs_time: left_time,
mid_time: desired_time.clone(),
rhs_time: right_time,
});
}
return Ok(QueryResult {
current: Some((left_time, left_val)),
next: Some(right_time),
});
}
}

144
src/vcd/types.rs Normal file
View file

@ -0,0 +1,144 @@
// use crate::Signal;
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use super::signal::{Signal, SignalEnum};
use chrono::prelude::{DateTime, Utc};
use num::BigUint;
use std::fmt;
#[derive(Debug, Clone)]
pub struct Version(pub String);
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Timescale {
Fs,
Ps,
Ns,
Us,
Ms,
S,
Unit,
}
impl fmt::Display for Timescale {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Timescale::Fs => write!(f, "fs"),
Timescale::Ps => write!(f, "ps"),
Timescale::Ns => write!(f, "ns"),
Timescale::Us => write!(f, "μs"),
Timescale::Ms => write!(f, "ms"),
Timescale::S => write!(f, "s"),
Timescale::Unit => write!(f, "unit"),
}
}
}
#[derive(Debug)]
pub struct Metadata {
pub date: Option<DateTime<Utc>>,
pub version: Option<Version>,
pub timescale: (Option<u32>, Timescale),
}
// We do a lot of arena allocation in this codebase.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ScopeIdx(pub usize);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SignalIdx(pub usize);
#[derive(Debug)]
pub(super) struct Scope {
pub(super) name: String,
pub(super) self_idx: ScopeIdx,
pub(super) child_signals: Vec<SignalIdx>,
pub(super) child_scopes: Vec<ScopeIdx>,
}
#[derive(Debug)]
pub struct VCD {
pub metadata: Metadata,
// Since we only need to store values when there is an actual change
// in the timeline, we keep a vector that stores the time at which an
// event occurs. Time t is always stored/encoded as the minimum length sequence
// of u8.
// We essentially fill ``tmstmps_encoded_as_u8s`` with BigUints converted
// to sequences of little endian u8s.
// It is up to the signals to keep track of the start/stop indices in the
// vector of u8s that constitute a timestamp value. Signals don't have to
// keep track of all timestamp values, a given signal only needs to keep
// track of the timestamps at which the given signal value changes.
pub(super) tmstmps_encoded_as_u8s: Vec<u8>,
pub(super) all_signals: Vec<SignalEnum>,
pub(super) all_scopes: Vec<Scope>,
pub(super) root_scopes: Vec<ScopeIdx>,
pub(super) largest_timestamp: Option<BigUint>,
}
impl VCD {
pub fn root_scopes_by_idx(&self) -> Vec<ScopeIdx> {
self.root_scopes.clone()
}
pub fn child_scopes_by_idx(&self, scope_idx: ScopeIdx) -> Vec<ScopeIdx> {
let ScopeIdx(idx) = scope_idx;
let scope = &self.all_scopes[idx];
scope.child_scopes.clone()
}
pub fn get_children_signal_idxs(&self, scope_idx: ScopeIdx) -> Vec<SignalIdx> {
let ScopeIdx(idx) = scope_idx;
let scope = &self.all_scopes[idx];
scope.child_signals.clone()
}
pub fn scope_name_by_idx(&self, scope_idx: ScopeIdx) -> &String {
let ScopeIdx(idx) = scope_idx;
let scope = &self.all_scopes[idx];
&scope.name
}
pub fn signal_from_signal_idx(&self, idx: SignalIdx) -> Signal<'_> {
let SignalIdx(idx) = idx;
let signal_enum = &self.all_signals[idx];
return Signal(signal_enum);
}
/// We take in a Signal and attempt to de-alias that signal if it is of
/// variant ``Signal::Alias``. If it is of variant ``Signal::Alias`` and points to
/// another alias, that's an error. Otherwise, we return the ``Signal::Data``
/// pointed to by the ``Signal::Alias``.
/// If the Signal is of varint ``Signal::Data``, then that can be returned directly.
pub(super) fn dealiasing_signal_idx_to_signal_lookup_mut<'a>(
&'a mut self,
idx: &SignalIdx,
) -> Result<&'a mut SignalEnum, String> {
// get the signal pointed to be SignalIdx from the arena
let SignalIdx(idx) = idx;
let signal = &self.all_signals[*idx];
// dereference signal if Signal::Alias, or keep idx if Signal::Data
let signal_idx = match signal {
SignalEnum::Data { self_idx, .. } => *self_idx,
SignalEnum::Alias { signal_alias, .. } => *signal_alias,
};
// Should now point to Signal::Data variant, or else there's an error
let SignalIdx(idx) = signal_idx;
let signal = self.all_signals.get_mut(idx).unwrap();
match signal {
SignalEnum::Data { .. } => Ok(signal),
SignalEnum::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
pub fn max_timestamp(&self) -> &Option<BigUint> {
&self.largest_timestamp
}
}

86
src/vcd/utilities.rs Normal file
View file

@ -0,0 +1,86 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
#[derive(Debug)]
pub(super) enum BinaryParserErrTypes {
XValue,
ZValue,
UValue,
HValue,
LValue,
DashValue,
WValue,
OtherValue(char),
TooLong,
}
// We build a quick and not so dirty bit string parser.
fn base2_str_to_byte(word: &[u8]) -> Result<u8, BinaryParserErrTypes> {
let mut val = 0u8;
// shouldn't have more than 8 chars in str
let len = word.len();
if len > 8 {
return Err(BinaryParserErrTypes::TooLong);
}
let bit_lut = [
0b0000_0001u8,
0b0000_0010u8,
0b0000_0100u8,
0b0000_1000u8,
0b0001_0000u8,
0b0010_0000u8,
0b0100_0000u8,
0b1000_0000u8,
];
for (idx, chr) in word.iter().rev().enumerate() {
match chr {
b'1' => val |= bit_lut[idx],
b'0' => {}
b'x' | b'X' => return Err(BinaryParserErrTypes::XValue),
b'z' | b'Z' => return Err(BinaryParserErrTypes::ZValue),
b'u' | b'U' => return Err(BinaryParserErrTypes::UValue),
b'l' | b'L' => return Err(BinaryParserErrTypes::LValue),
b'h' | b'H' => return Err(BinaryParserErrTypes::HValue),
b'w' | b'W' => return Err(BinaryParserErrTypes::WValue),
b'-' => return Err(BinaryParserErrTypes::DashValue),
_ => return Err(BinaryParserErrTypes::OtherValue(*chr as char)),
}
}
Ok(val)
}
pub(super) fn binary_str_to_vec_u8(binary_str: &str) -> Result<Vec<u8>, BinaryParserErrTypes> {
let mut vec_u8: Vec<u8> = Vec::new();
let binary_str_as_bytes = binary_str.as_bytes();
let mut tail_idx = binary_str_as_bytes.len();
// clamp head if provided binary str is less than 8 long
let mut head_idx = if tail_idx >= 8 {
binary_str_as_bytes.len() - 8
} else {
0
};
while tail_idx > 0 {
let curr_b_val = &binary_str_as_bytes[head_idx..tail_idx];
let val_u8 = base2_str_to_byte(curr_b_val)?;
vec_u8.push(val_u8);
if head_idx < 8 {
head_idx = 0
} else {
head_idx -= 8;
}
if tail_idx < 8 {
tail_idx = 0
} else {
tail_idx -= 8;
}
}
Ok(vec_u8)
}

View file

@ -1,4 +0,0 @@
Icarus,Verilator,GHDL,VCS,QuestaSim,ModelSim,Quartus,SystemC,Treadle,Aldec,Riviera-PRO,MyHDL,ncsim,xilinx_isim,vivado,GTKWave-Analyzer
https://github.com/dpretet/vcd/blob/master/test1.vcd,https://github.com/wavedrom/vcd-samples/blob/trunk/swerv1.vcd,https://raw.githubusercontent.com/AdoobII/idea_21s/main/vhdl/idea.vcd,https://raw.githubusercontent.com/ameyjain/8-bit-Microprocessor/master/8-bit%20microprocessor/processor.vcd,https://github.com/mr-gaurav/Sequence-Counter/blob/main/test.vcd,https://github.com/Mohammad-Heydariii/Digital-Systems-Lab-Course/blob/main/Lab_project4/modelsim_files/clkdiv2n_tb.vcd,https://github.com/PedroTLemos/ProjetoInfraHard/blob/master/mipsHardware.vcd,https://github.com/jroslindo/Mips-Systemc/blob/main/REGISTRADORES_32_bits/wave_registradores.vcd,https://github.com/chipsalliance/treadle/blob/master/src/test/resources/GCD.vcd,https://github.com/SVeilleux9/FPGA-GPIO-Extender/blob/main/Firmware/aldec/SPI_Write/SPI_Write.vcd,https://github.com/prathampathak/Tic-Tac-Tao/blob/main/dump.vcd,https://github.com/aibtw/myHdl_Projects/blob/main/SimpleMemory/Simple_Memory.vcd,https://github.com/amiteee78/RTL_design/blob/master/ffdiv_32bit/ffdiv_32bit_prop_binom/run_cad/ffdiv_32bit_tb.vcd,https://github.com/mukul54/qrs-peak-fpga/blob/master/utkarsh/utkarsh.sim/sim_1/behav/xsim/test.vcd,https://github.com/saharmalmir/Eth2Ser/blob/master/UART2ETH.runs/impl_1/iladata.vcd,https://github.com/Asfagus/Network-Switch/blob/main/perm_current.vcd
https://github.com/ombhilare999/riscv-core/blob/master/src/rv32_soc_TB.vcd,https://github.com/bigBrain1901/nPOWER-ISA-5-STAGE-PIPELINED-CPU/blob/master/post_compile_files/vlt_dump.vcd,https://github.com/gaoqqt2n/CPU/blob/master/SuperPipelineCPU/vcdfile/pcpu.vcd,https://raw.githubusercontent.com/Akashay-Singla/RISC-V/main/Pipeline/datapath_log.vcd,https://github.com/SparshAgarwal/Computer-Architecture/blob/master/hw3/hw3_1/dump.vcd,https://github.com/sh619/Songyu_Huang-Chisel/blob/main/MU0_final_version/simulation/qsim/CPU_Design.msim.vcd,,https://github.com/amrhas/PDRNoC/blob/VCRouter/noctweak/Debug/waveform.vcd.vcd,,,,https://github.com/Abhishek010397/Programming-RISC-V/blob/master/top.vcd,,https://github.com/DanieleParravicini/regex_coprocessor/blob/master/scripts/sim/test2x2_regex22_string1.vcd,https://github.com/BradMcDanel/multiplication-free-dnn/blob/master/verilog/iladata.vcd,
https://github.com/b06902044/computer_architecture/blob/main/CPU.vcd,,https://github.com/charlycop/VLSI-1/blob/master/EXEC/ALU/alu.vcd,https://raw.githubusercontent.com/sathyapriyanka/APB_UVC_UVM/main/Apb_slave_uvm_new.vcd,,,,,,,,https://github.com/DarthSkipper/myHDL_Sigmoid/blob/master/out/testbench/sigmoid_tb.vcd,,https://github.com/pabloec1729/Hashes-generator/blob/master/RTL/velocidad/test.vcd,,
1 Icarus Verilator GHDL VCS QuestaSim ModelSim Quartus SystemC Treadle Aldec Riviera-PRO MyHDL ncsim xilinx_isim vivado GTKWave-Analyzer
2 https://github.com/dpretet/vcd/blob/master/test1.vcd https://github.com/wavedrom/vcd-samples/blob/trunk/swerv1.vcd https://raw.githubusercontent.com/AdoobII/idea_21s/main/vhdl/idea.vcd https://raw.githubusercontent.com/ameyjain/8-bit-Microprocessor/master/8-bit%20microprocessor/processor.vcd https://github.com/mr-gaurav/Sequence-Counter/blob/main/test.vcd https://github.com/Mohammad-Heydariii/Digital-Systems-Lab-Course/blob/main/Lab_project4/modelsim_files/clkdiv2n_tb.vcd https://github.com/PedroTLemos/ProjetoInfraHard/blob/master/mipsHardware.vcd https://github.com/jroslindo/Mips-Systemc/blob/main/REGISTRADORES_32_bits/wave_registradores.vcd https://github.com/chipsalliance/treadle/blob/master/src/test/resources/GCD.vcd https://github.com/SVeilleux9/FPGA-GPIO-Extender/blob/main/Firmware/aldec/SPI_Write/SPI_Write.vcd https://github.com/prathampathak/Tic-Tac-Tao/blob/main/dump.vcd https://github.com/aibtw/myHdl_Projects/blob/main/SimpleMemory/Simple_Memory.vcd https://github.com/amiteee78/RTL_design/blob/master/ffdiv_32bit/ffdiv_32bit_prop_binom/run_cad/ffdiv_32bit_tb.vcd https://github.com/mukul54/qrs-peak-fpga/blob/master/utkarsh/utkarsh.sim/sim_1/behav/xsim/test.vcd https://github.com/saharmalmir/Eth2Ser/blob/master/UART2ETH.runs/impl_1/iladata.vcd https://github.com/Asfagus/Network-Switch/blob/main/perm_current.vcd
3 https://github.com/ombhilare999/riscv-core/blob/master/src/rv32_soc_TB.vcd https://github.com/bigBrain1901/nPOWER-ISA-5-STAGE-PIPELINED-CPU/blob/master/post_compile_files/vlt_dump.vcd https://github.com/gaoqqt2n/CPU/blob/master/SuperPipelineCPU/vcdfile/pcpu.vcd https://raw.githubusercontent.com/Akashay-Singla/RISC-V/main/Pipeline/datapath_log.vcd https://github.com/SparshAgarwal/Computer-Architecture/blob/master/hw3/hw3_1/dump.vcd https://github.com/sh619/Songyu_Huang-Chisel/blob/main/MU0_final_version/simulation/qsim/CPU_Design.msim.vcd https://github.com/amrhas/PDRNoC/blob/VCRouter/noctweak/Debug/waveform.vcd.vcd https://github.com/Abhishek010397/Programming-RISC-V/blob/master/top.vcd https://github.com/DanieleParravicini/regex_coprocessor/blob/master/scripts/sim/test2x2_regex22_string1.vcd https://github.com/BradMcDanel/multiplication-free-dnn/blob/master/verilog/iladata.vcd
4 https://github.com/b06902044/computer_architecture/blob/main/CPU.vcd https://github.com/charlycop/VLSI-1/blob/master/EXEC/ALU/alu.vcd https://raw.githubusercontent.com/sathyapriyanka/APB_UVC_UVM/main/Apb_slave_uvm_new.vcd https://github.com/DarthSkipper/myHDL_Sigmoid/blob/master/out/testbench/sigmoid_tb.vcd https://github.com/pabloec1729/Hashes-generator/blob/master/RTL/velocidad/test.vcd

77
tests/files.rs Normal file
View file

@ -0,0 +1,77 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
// TODO: we should eventually be able to only test on just
// the files const
pub const FILES: [&str; 31] = [
"./tests/vcd-files/aldec/SPI_Write.vcd",
"./tests/vcd-files/ghdl/alu.vcd",
"./tests/vcd-files/ghdl/idea.vcd",
"./tests/vcd-files/ghdl/pcpu.vcd",
"./tests/vcd-files/gtkwave-analyzer/perm_current.vcd",
"./tests/vcd-files/icarus/CPU.vcd",
"./tests/vcd-files/icarus/rv32_soc_TB.vcd",
"./tests/vcd-files/icarus/test1.vcd",
"./tests/vcd-files/model-sim/CPU_Design.msim.vcd",
"./tests/vcd-files/model-sim/clkdiv2n_tb.vcd",
"./tests/vcd-files/my-hdl/Simple_Memory.vcd",
"./tests/vcd-files/my-hdl/sigmoid_tb.vcd",
"./tests/vcd-files/my-hdl/top.vcd",
"./tests/vcd-files/ncsim/ffdiv_32bit_tb.vcd",
"./tests/vcd-files/quartus/mipsHardware.vcd",
"./tests/vcd-files/quartus/wave_registradores.vcd",
"./tests/vcd-files/questa-sim/dump.vcd",
"./tests/vcd-files/questa-sim/test.vcd",
"./tests/vcd-files/riviera-pro/dump.vcd",
"./tests/vcd-files/systemc/waveform.vcd",
"./tests/vcd-files/treadle/GCD.vcd",
"./tests/vcd-files/vcs/Apb_slave_uvm_new.vcd",
"./tests/vcd-files/vcs/datapath_log.vcd",
"./tests/vcd-files/vcs/processor.vcd",
"./tests/vcd-files/verilator/swerv1.vcd",
"./tests/vcd-files/verilator/vlt_dump.vcd",
"./tests/vcd-files/vivado/iladata.vcd",
"./tests/vcd-files/xilinx_isim/test.vcd",
"./tests/vcd-files/xilinx_isim/test1.vcd",
// TODO : add signal ignore list to handle bitwidth mismatches
"./tests/vcd-files/xilinx_isim/test2x2_regex22_string1.vcd",
"./tests/vcd-files/scope_with_comment.vcd",
];
pub const GOOD_DATE_FILES: [&str; 24] = [
"./test-vcd-files/aldec/SPI_Write.vcd",
"./test-vcd-files/ghdl/alu.vcd",
"./test-vcd-files/ghdl/idea.vcd",
"./test-vcd-files/ghdl/pcpu.vcd",
"./test-vcd-files/gtkwave-analyzer/perm_current.vcd",
"./test-vcd-files/icarus/CPU.vcd",
"./test-vcd-files/icarus/rv32_soc_TB.vcd",
"./test-vcd-files/icarus/test1.vcd",
"./test-vcd-files/model-sim/CPU_Design.msim.vcd",
"./test-vcd-files/model-sim/clkdiv2n_tb.vcd",
"./test-vcd-files/my-hdl/Simple_Memory.vcd",
"./test-vcd-files/my-hdl/sigmoid_tb.vcd",
"./test-vcd-files/my-hdl/top.vcd",
"./test-vcd-files/questa-sim/dump.vcd",
"./test-vcd-files/questa-sim/test.vcd",
"./test-vcd-files/riviera-pro/dump.vcd",
"./test-vcd-files/vcs/Apb_slave_uvm_new.vcd",
"./test-vcd-files/vcs/datapath_log.vcd",
"./test-vcd-files/vcs/processor.vcd",
"./test-vcd-files/verilator/swerv1.vcd",
"./test-vcd-files/verilator/vlt_dump.vcd",
"./test-vcd-files/xilinx_isim/test.vcd",
"./test-vcd-files/xilinx_isim/test1.vcd",
"./test-vcd-files/xilinx_isim/test2x2_regex22_string1.vcd",
];
pub const BAD_DATE_FILES: [&str; 6] = [
"./test-vcd-files/ncsim/ffdiv_32bit_tb.vcd",
"./test-vcd-files/quartus/mipsHardware.vcd",
"./test-vcd-files/quartus/wave_registradores.vcd",
"./test-vcd-files/systemc/waveform.vcd",
"./test-vcd-files/treadle/GCD.vcd",
"./test-vcd-files/vivado/iladata.vcd",
];

22
tests/integration_test.rs Normal file
View file

@ -0,0 +1,22 @@
// Copyright (C) 2022 Yehowshua Immanuel
// This program is distributed under both the GPLV3 license
// and the YEHOWSHUA license, both of which can be found at
// the root of the folder containing the sources for this program.
use std::fs::File;
mod files;
use files::*;
#[test]
fn parse_all_VCDs() {
// see if we can parse all signal trees successfully
for file_name in FILES {
let file = File::open(file_name).unwrap();
let vcd = fastwave_backend::parse_vcd(file);
if !vcd.is_ok() {
dbg!(file_name);
vcd.unwrap();
}
}
}

View file

@ -0,0 +1,92 @@
$date
Thu Dec 17 17:19:03 2020
$end
$version
Aldec HDL Simulator Version 10.03.3558
$end
$timescale
1 ps
$end
$scope module tb $end
$scope module t $end
$var wire 1 ! CLK $end
$var wire 1 " LED $end
$var wire 1 # PIN_10 $end
$var wire 1 $ PIN_11 $end
$var wire 1 % PIN_12 $end
$var wire 1 & PIN_13 $end
$var wire 1 ' SPI_In $end
$var wire 1 ( SPI_Out $end
$var wire 1 ) SPI_Data_Available $end
$var wire 1 * RegMap_In $end
$var wire 1 + RegMap_Out $end
$var wire 1 , RegMap_Data_Available $end
$var wire 8 - AddrBus [7:0] $end
$var wire 8 . DataBus [7:0] $end
$scope module controller $end
$var wire 1 ! clk $end
$var wire 1 " LED $end
$var wire 8 . DataBus [7:0] $end
$var wire 1 ) SPI_Data_Available $end
$var wire 1 , RegMap_Data_available $end
$var wire 8 / addr [7:0] $end
$var wire 8 0 data [7:0] $end
$var wire 1 1 BusActive $end
$var reg 8 2 AddrBus [7:0] $end
$var reg 1 3 SPI_In $end
$var reg 1 4 SPI_Out $end
$var reg 1 5 RegMap_In $end
$var reg 1 6 RegMap_Out $end
$var reg 1 7 LED_state $end
$var reg 3 8 block [2:0] $end
$var reg 3 9 doing [2:0] $end
$var parameter 3 : IDLE $end
$var parameter 3 ; READ_ADDR $end
$var parameter 3 < READ_DATA $end
$var parameter 3 = TX $end
$var parameter 3 > SPI $end
$upscope $end
$scope module reg_mag_i $end
$var wire 1 ! clk $end
$var wire 1 + RegMap_Out $end
$var wire 1 * RegMap_In $end
$var wire 8 - AddrBus [7:0] $end
$var wire 8 . DataBus [7:0] $end
$var wire 1 ? r_w $end
$var wire 1 @ outputData $end
$var wire 1 A inputData $end
$var reg 1 B RegMap_Data_Available $end
$var reg 8 C inData [7:0] $end
$var reg 8 D inAddr [7:0] $end
$var reg 8 E outData [7:0] $end
$var reg 1 F addr_rcv $end
$var reg 1 G data_rcv $end
$var reg 2 H state [1:0] $end
$var parameter 2 I INIT $end
$var parameter 2 J IDLE $end
$var parameter 2 K RX $end
$var parameter 2 L TX $end
$var parameter 32 M MAXADDRESS $end
$upscope $end
$scope module SPI_i $end
$var wire 1 ! clk $end
$var wire 1 # SCK $end
$var wire 1 $ SSEL $end
$var wire 1 % MOSI $end
$var wire 1 & MISO $end
$var wire 1 ' SPI_In $end
$var wire 1 ( SPI_Out $end
$var wire 8 - AddrBus [7:0] $end
$var wire 8 . DataBus [7:0] $end
$var wire 1 N SCK_risingedge $end
$var wire 1 O SCK_fallingedge $end
$var wire 1 P SSEL_active $end
$var wire 1 Q MOSI_data $end
$var reg 1 R SPI_Data_Available $end

View file

@ -0,0 +1,287 @@
$comment Generated by Amaranth $end
$date 2022-07-13 18:48:57.685239 $end
$timescale 1 ps $end
$scope module bench $end
$scope module top $end
$var wire 1 ! clk $end
$var wire 1 " rst $end
$var wire 1 # ovf $end
$var wire 16 $ count $end
$var string 1 % state $end
$var wire 1 & en $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
$dumpvars
0!
0"
0#
b0 $
sTOP/0 %
0&
$end
#500000
1!
#1000000
0!
#1500000
1!
#2000000
0!
#2500000
1!
#3000000
0!
#3500000
1!
#4000000
0!
#4500000
1!
#5000000
0!
#5500000
1!
#6000000
0!
#6500000
1!
#7000000
0!
#7500000
1!
#8000000
0!
#8500000
1!
#9000000
0!
#9500000
1!
#10000000
0!
#10500000
1!
#11000000
0!
#11500000
1!
#12000000
0!
#12500000
1!
#13000000
0!
#13500000
1!
#14000000
0!
#14500000
1!
#15000000
0!
#15500000
1!
#16000000
0!
#16500000
1!
#17000000
0!
#17500000
1!
#18000000
0!
#18500000
1!
#19000000
0!
#19500000
1!
#20000000
0!
#20500000
1!
#21000000
0!
#21500000
1!
#22000000
0!
#22500000
1!
#23000000
0!
#23500000
1!
#24000000
0!
#24500000
1!
#25000000
0!
#25500000
1!
#26000000
0!
#26500000
1!
#27000000
0!
#27500000
1!
#28000000
0!
#28500000
1!
#29000000
0!
#29500000
1!
#30000000
0!
#30500000
1&
1!
#31000000
0!
#31500000
sBOTTOM/2 %
b1 $
1!
#32000000
0!
#32500000
b10 $
1!
#33000000
0!
#33500000
b11 $
1!
#34000000
0!
#34500000
b100 $
1!
#35000000
0!
#35500000
b101 $
1!
#36000000
0!
#36500000
b110 $
1!
#37000000
0!
#37500000
b111 $
1!
#38000000
0!
#38500000
b1000 $
1!
#39000000
0!
#39500000
b1001 $
1!
#40000000
0!
#40500000
b1010 $
1!
#41000000
0!
#41500000
b1011 $
1!
#42000000
0!
#42500000
b1100 $
1!
#43000000
0!
#43500000
b1101 $
1!
#44000000
0!
#44500000
b1110 $
1!
#45000000
0!
#45500000
b1111 $
1!
#46000000
0!
#46500000
b10000 $
1!
#47000000
0!
#47500000
b10001 $
1!
#48000000
0!
#48500000
b10010 $
1!
#49000000
0!
#49500000
b10011 $
1!
#50000000
0!
#50500000
b10100 $
1!
#51000000
0!
#51500000
b10101 $
1!
#52000000
0!
#52500000
b10110 $
1!
#53000000
0!
#53500000
b10111 $
1!
#54000000
0!
#54500000
b11000 $
1!
#55000000
0!
#55500000
1#
b11001 $
1!
#56000000
0!
#56500000
sTOP/0 %
0#
b0 $
1!
#57000000
0!
#57500000
sBOTTOM/2 %
b1 $
1!
#58000000

View file

@ -0,0 +1,296 @@
$date
Sat Dec 26 15:33:14 2020
$end
$version
ModelSim Version 10.5b
$end
$timescale
1ns
$end
$scope module clkdiv2n_tb $end
$comment foo $end
$var reg 1 ! clk $end
$var reg 1 " reset $end
$var wire 1 # clk_out $end
$scope module t1 $end
$var parameter 32 $ WIDTH $end
$var parameter 32 % N $end
$var wire 1 & clk $end
$var wire 1 ' reset $end
$var wire 1 # clk_out $end
$var reg 3 ( r_reg [2:0] $end
$var wire 1 ) r_nxt [2] $end
$var wire 1 * r_nxt [1] $end
$var wire 1 + r_nxt [0] $end
$var reg 1 , clk_track $end
$upscope $end
$comment foo $end
$upscope $end
$comment foo $end
$enddefinitions $end
#0
$comment foo $end
$dumpvars
0!
x"
bx (
x,
b11 $
b110 %
x#
x+
x*
x)
x'
0&
$end
#5
1"
1'
b0 (
0,
1+
0*
0)
0#
#10
1!
1&
#15
0"
0'
#20
0!
0&
#30
1!
1&
b1 (
0+
1*
#40
0!
0&
#50
1!
1&
b10 (
1+
#60
0!
0&
#70
1!
1&
b11 (
0+
0*
1)
#80
0!
0&
#90
1!
1&
b100 (
1+
#100
0!
0&
#110
1!
1&
b101 (
0+
1*
#120
0!
0&
#130
1!
1&
b0 (
1,
1+
0*
0)
1#
#140
0!
0&
#150
1!
1&
b1 (
0+
1*
#160
0!
0&
#170
1!
1&
b10 (
1+
#180
0!
0&
#190
1!
1&
b11 (
0+
0*
1)
#200
0!
0&
#210
1!
1&
b100 (
1+
#220
0!
0&
#230
1!
1&
b101 (
0+
1*
#240
0!
0&
#250
1!
1&
b0 (
0,
1+
0*
0)
0#
#260
0!
0&
#270
1!
1&
b1 (
0+
1*
#280
0!
0&
#290
1!
1&
b10 (
1+
#300
0!
0&
#310
1!
1&
b11 (
0+
0*
1)
#320
0!
0&
#330
1!
1&
b100 (
1+
#340
0!
0&
#350
1!
1&
b101 (
0+
1*
#360
0!
0&
#370
1!
1&
b0 (
1,
1+
0*
0)
1#
#380
0!
0&
#390
1!
1&
b1 (
0+
1*
#400
0!
0&
#410
1!
1&
b10 (
1+
#420
0!
0&
#430
1!
1&
b11 (
0+
0*
1)
#440
0!
0&
#450
1!
1&
b100 (
1+
#460
0!
0&
#470
1!
1&
b101 (
0+
1*
#480
0!
0&
#490
1!
1&
b0 (
0,
1+
0*
0)
0#
#500
0!
0&
#510
1!
1&
b1 (
0+
1*

View file

@ -0,0 +1,17 @@
Icarus,https://github.com/dpretet/vcd/blob/master/test1.vcd,https://github.com/ombhilare999/riscv-core/blob/master/src/rv32_soc_TB.vcd,https://github.com/b06902044/computer_architecture/blob/main/CPU.vcd
Verilator,https://github.com/wavedrom/vcd-samples/blob/trunk/swerv1.vcd,https://github.com/bigBrain1901/nPOWER-ISA-5-STAGE-PIPELINED-CPU/blob/master/post_compile_files/vlt_dump.vcd,
GHDL,https://raw.githubusercontent.com/AdoobII/idea_21s/main/vhdl/idea.vcd,https://github.com/gaoqqt2n/CPU/blob/master/SuperPipelineCPU/vcdfile/pcpu.vcd,https://github.com/charlycop/VLSI-1/blob/master/EXEC/ALU/alu.vcd
VCS,https://raw.githubusercontent.com/ameyjain/8-bit-Microprocessor/master/8-bit%20microprocessor/processor.vcd,https://raw.githubusercontent.com/Akashay-Singla/RISC-V/main/Pipeline/datapath_log.vcd,https://raw.githubusercontent.com/sathyapriyanka/APB_UVC_UVM/main/Apb_slave_uvm_new.vcd
QuestaSim,https://github.com/mr-gaurav/Sequence-Counter/blob/main/test.vcd,https://github.com/SparshAgarwal/Computer-Architecture/blob/master/hw3/hw3_1/dump.vcd,
ModelSim,https://github.com/Mohammad-Heydariii/Digital-Systems-Lab-Course/blob/main/Lab_project4/modelsim_files/clkdiv2n_tb.vcd,https://github.com/sh619/Songyu_Huang-Chisel/blob/main/MU0_final_version/simulation/qsim/CPU_Design.msim.vcd,
Quartus,https://github.com/PedroTLemos/ProjetoInfraHard/blob/master/mipsHardware.vcd,,
SystemC,https://github.com/jroslindo/Mips-Systemc/blob/main/REGISTRADORES_32_bits/wave_registradores.vcd,https://github.com/amrhas/PDRNoC/blob/VCRouter/noctweak/Debug/waveform.vcd.vcd,
Treadle,https://github.com/chipsalliance/treadle/blob/master/src/test/resources/GCD.vcd,,
Aldec,https://github.com/SVeilleux9/FPGA-GPIO-Extender/blob/main/Firmware/aldec/SPI_Write/SPI_Write.vcd,,
Riviera-PRO,https://github.com/prathampathak/Tic-Tac-Tao/blob/main/dump.vcd,,
MyHDL,https://github.com/aibtw/myHdl_Projects/blob/main/SimpleMemory/Simple_Memory.vcd,https://github.com/Abhishek010397/Programming-RISC-V/blob/master/top.vcd,https://github.com/DarthSkipper/myHDL_Sigmoid/blob/master/out/testbench/sigmoid_tb.vcd
ncsim,https://github.com/amiteee78/RTL_design/blob/master/ffdiv_32bit/ffdiv_32bit_prop_binom/run_cad/ffdiv_32bit_tb.vcd,,
xilinx_isim,https://github.com/mukul54/qrs-peak-fpga/blob/master/utkarsh/utkarsh.sim/sim_1/behav/xsim/test.vcd,https://github.com/DanieleParravicini/regex_coprocessor/blob/master/scripts/sim/test2x2_regex22_string1.vcd,https://github.com/pabloec1729/Hashes-generator/blob/master/RTL/velocidad/test.vcd
vivado,https://github.com/saharmalmir/Eth2Ser/blob/master/UART2ETH.runs/impl_1/iladata.vcd,https://github.com/BradMcDanel/multiplication-free-dnn/blob/master/verilog/iladata.vcd,
GTKWave-Analyzer,https://github.com/Asfagus/Network-Switch/blob/main/perm_current.vcd,,
Amaranth,Locally Simulated File,,
1 Icarus https://github.com/dpretet/vcd/blob/master/test1.vcd https://github.com/ombhilare999/riscv-core/blob/master/src/rv32_soc_TB.vcd https://github.com/b06902044/computer_architecture/blob/main/CPU.vcd
2 Verilator https://github.com/wavedrom/vcd-samples/blob/trunk/swerv1.vcd https://github.com/bigBrain1901/nPOWER-ISA-5-STAGE-PIPELINED-CPU/blob/master/post_compile_files/vlt_dump.vcd
3 GHDL https://raw.githubusercontent.com/AdoobII/idea_21s/main/vhdl/idea.vcd https://github.com/gaoqqt2n/CPU/blob/master/SuperPipelineCPU/vcdfile/pcpu.vcd https://github.com/charlycop/VLSI-1/blob/master/EXEC/ALU/alu.vcd
4 VCS https://raw.githubusercontent.com/ameyjain/8-bit-Microprocessor/master/8-bit%20microprocessor/processor.vcd https://raw.githubusercontent.com/Akashay-Singla/RISC-V/main/Pipeline/datapath_log.vcd https://raw.githubusercontent.com/sathyapriyanka/APB_UVC_UVM/main/Apb_slave_uvm_new.vcd
5 QuestaSim https://github.com/mr-gaurav/Sequence-Counter/blob/main/test.vcd https://github.com/SparshAgarwal/Computer-Architecture/blob/master/hw3/hw3_1/dump.vcd
6 ModelSim https://github.com/Mohammad-Heydariii/Digital-Systems-Lab-Course/blob/main/Lab_project4/modelsim_files/clkdiv2n_tb.vcd https://github.com/sh619/Songyu_Huang-Chisel/blob/main/MU0_final_version/simulation/qsim/CPU_Design.msim.vcd
7 Quartus https://github.com/PedroTLemos/ProjetoInfraHard/blob/master/mipsHardware.vcd
8 SystemC https://github.com/jroslindo/Mips-Systemc/blob/main/REGISTRADORES_32_bits/wave_registradores.vcd https://github.com/amrhas/PDRNoC/blob/VCRouter/noctweak/Debug/waveform.vcd.vcd
9 Treadle https://github.com/chipsalliance/treadle/blob/master/src/test/resources/GCD.vcd
10 Aldec https://github.com/SVeilleux9/FPGA-GPIO-Extender/blob/main/Firmware/aldec/SPI_Write/SPI_Write.vcd
11 Riviera-PRO https://github.com/prathampathak/Tic-Tac-Tao/blob/main/dump.vcd
12 MyHDL https://github.com/aibtw/myHdl_Projects/blob/main/SimpleMemory/Simple_Memory.vcd https://github.com/Abhishek010397/Programming-RISC-V/blob/master/top.vcd https://github.com/DarthSkipper/myHDL_Sigmoid/blob/master/out/testbench/sigmoid_tb.vcd
13 ncsim https://github.com/amiteee78/RTL_design/blob/master/ffdiv_32bit/ffdiv_32bit_prop_binom/run_cad/ffdiv_32bit_tb.vcd
14 xilinx_isim https://github.com/mukul54/qrs-peak-fpga/blob/master/utkarsh/utkarsh.sim/sim_1/behav/xsim/test.vcd https://github.com/DanieleParravicini/regex_coprocessor/blob/master/scripts/sim/test2x2_regex22_string1.vcd https://github.com/pabloec1729/Hashes-generator/blob/master/RTL/velocidad/test.vcd
15 vivado https://github.com/saharmalmir/Eth2Ser/blob/master/UART2ETH.runs/impl_1/iladata.vcd https://github.com/BradMcDanel/multiplication-free-dnn/blob/master/verilog/iladata.vcd
16 GTKWave-Analyzer https://github.com/Asfagus/Network-Switch/blob/main/perm_current.vcd
17 Amaranth Locally Simulated File