Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for dynamic seed generation. #1089

Merged
merged 4 commits into from
Jan 13, 2025
Merged

Added support for dynamic seed generation. #1089

merged 4 commits into from
Jan 13, 2025

Conversation

LarsAsplund
Copy link
Collaborator

This PR adds a unique base seed to every simulation from which new unique seed can be generated for the random number generators in the testbench. The feature doesn't not require any specific randomization solution, it works with OSVVM, UVVM, ieee.math_real.uniform. Some more details below:

Seeds for Random Number Generation

The Python test runner automatically generates a 64-bit base seed, which serves as the foundation for deriving new seeds to initialize one or more random number generators (RNGs) within the simulation. This base seed is calculated using the system time and a thread identifier, ensuring that it varies between simulations. This variability enhances test coverage since randomized test will cover different areas in each simulation. By running the same randomized test in several parallel threads but with different base seeds we can also shorten the execution time while maintaining the same level of test coverage.

Note

VHDL-2019 introduces an interface for obtaining the system time. However, as of this writing, support for this feature is limited, and where available, it only offers second-level resolution. If used for base seed generation, simulations in parallel threads would receive the same base seed when started simultaneously.

To address this, the Python test runner uses system time with microsecond resolution combined with an additional thread identifier as the source of entropy. This approach ensures the uniqueness of base seeds across parallel threads.

The base seed is provided via the runner_cfg generic and new derived seeds can be obtained by calling the get_seed function with runner_cfg and a salt string. The salt string is hashed together with the base seed to ensure the uniqueness of each derived seed (with very high probability). The salt parameter can be omitted if only a single seed is needed:

constant global_seed : string := get_seed(runner_cfg, "Optional salt");

A seed can also be obtained without referencing runner_cfg, which is particularly useful when the seed is created within a process that is not part of the top-level testbench. However, this is only possible after test_runner_setup has been executed. To prevent race conditions, the get_seed procedure will block execution until test_runner_setup has completed.

randomizing_process : process is
  variable local_seed : string_seed_t; -- string_seed_t = string(1 to 16)
begin
  get_seed(local_seed, salt => randomizing_process'path_name);

In the previous examples, the get_seed subprograms returned seeds as strings. However, get_seed subprograms are also available for integer seeds and 64-bit seeds of unsigned and signed type. To avoid any ambiguity that may arise, the following function aliases are defined: get_string_seed, get_integer_seed, get_unsigned_seed, and get_signed_seed. Additionally, the get_uniform_seed procedure is provided to support the standard uniform procedure in ieee.math_real. The uniform procedure requires two positive seeds, seed1 and seed2, each with its own specific legal range that is smaller than the full positive range.

get_uniform_seed(seed1, seed2, "Optional salt");

uniform(seed1, seed2, a_random_value);
uniform(seed1, seed2, another_random_value);

Reproducibility

The seed used for a test is logged in the test output file (<output path>/output.txt) and, in the event of a test failure, it is also displayed on the console:

> python run.py
Starting lib.tb_seed.Test that passes
Output file: C:\github\vunit\docs\run\src\vunit_out\test_output\lib.tb_seed.Test_that_passes_4125d67fe52dadd934f892b1209f41e7a94a39bd\output.txt
pass (P=1 S=0 F=0 T=2) lib.tb_seed.Test that passes (0.7 s)

(13:26:06) Starting lib.tb_seed.Test that fails
Output file: C:\github\vunit\docs\run\src\vunit_out\test_output\lib.tb_seed.Test_that_fails_0f173e63967af845f06d4a86c622bba76f3ffb3d\output.txt
Seed for lib.tb_seed.Test that fails: fb19f3cca859d69c
0 fs - default - ERROR - Something bad happened
C:\github\vunit\vunit\vhdl\core\src\core_pkg.vhd:85:7:@0ms:(report failure): Stop simulation on log level error
ghdl:error: report failed
ghdl:error: simulation failed
fail (P=1 S=0 F=1 T=2) lib.tb_seed.Test that fails (0.7 s)

==== Summary ========================================
pass lib.tb_seed.Test that passes (0.7 s)
fail lib.tb_seed.Test that fails (0.7 s)

pass 1 of 2
fail 1 of 2

Total time was 1.4 s
Elapsed time was 1.4 s

Some failed!

To reproduce the failing test setup and verify a bug fix, the failing seed can be specified using the --seed option:

> python run.py "lib.tb_seed.Test that fails" --seed fb19f3cca859d69c

@LarsAsplund LarsAsplund force-pushed the seed_support branch 7 times, most recently from 93623f9 to 4b67054 Compare January 13, 2025 10:35
This was referenced Jan 13, 2025
@LarsAsplund LarsAsplund merged commit 64c8587 into master Jan 13, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant