Skip to content
STIMSMITH

gen_instr function

CodeArtifact WIKI v1 · 5/26/2026

The `gen_instr` function is a RISCV-DV generator routine shown in DVCon proceedings as the implementation used to randomize an initial instruction dump. It configures allowed instructions, randomizes `instr_list` either sequentially or in parallel depending on configuration thresholds, removes trailing branch instructions, and temporarily disables and re-enables garbage collection around the generation work.

Overview

gen_instr is presented in Listing 9, "Parallelizing Randomization of the Initial Instruction Dump," as a function that randomizes the initial instruction list used by the RISCV-DV generator. The function signature shown is:

void gen_instr(bool no_branch = false,
               bool no_load_store = true,
               bool is_debug_program = false)

The function begins by disabling garbage collection, calls setup_allowed_instr(no_branch, no_load_store), and asserts that instr_list.length != 0 before randomizing the list. After randomization, it removes any branch instruction that remains at the end of the list, then re-enables garbage collection.

Sequential and parallel execution paths

gen_instr chooses between sequential and parallel randomization based on two configuration values:

  • cfg.par_instr_threshold
  • cfg.par_num_threads

If instr_list.length <= cfg.par_instr_threshold or cfg.par_num_threads == 1, the function iterates over instr_list and calls:

randomize_instr(instr, is_debug_program);

for each instruction.

Otherwise, the function parallelizes the work across cfg.par_num_threads. It computes instr_count, divides the instruction list into groups using instr_grp_length = instr_count / cfg.par_num_threads, creates a Fork for each range, and randomizes instr_list[i] inside each fork. The last group is extended to instr_count to cover any remainder. Each fork is assigned thread affinity with new_fork.set_thread_affinity(forks.length), appended to the fork list, and later joined with foreach (f; forks) f.join().

End-of-list branch handling

After the randomization phase, gen_instr enforces a constraint that a branch instruction must not be the last instruction. The listing comments explain the reason: there is no forward branch target for a final branch instruction. The function repeatedly shortens instr_list while the final instruction category is riscv_instr_category_t.BRANCH, stopping if the list becomes empty.

Performance context

The function appears in the context of RISCV-DV performance optimization. Profiling identified that RISCV-DV dumps a large non-directed instruction stream into instruction lists for the main program and sub-programs, and that most effort in this area is spent on randomization and solving constraints. The paper characterizes the first two major generator bottlenecks, including this non-directed instruction-stream dump, as linear in algorithmic complexity and suitable for multicore parallelization.

The same paper notes that RISCV-DV is highly parameterized, so component execution time can vary significantly with user-selected parameters. For profiling, the authors used the riscv_instr_base_test with seven directed streams covering the spectrum of possible RISC-V instruction categories.

Profiling considerations

The paper shows uvm_trace("GEN INSTR", "START", UVM_DEBUG) and uvm_trace("GEN INSTR", "DONE", UVM_DEBUG) around a loop that calls randomize_instr, producing log messages tagged GEN INSTR. It explains that uvm_trace provides macro-level profiling and that each invocation performs an operating-system call to fetch the current clock time, so excessive use can increase runtime. For micro-level profiling, the paper points to gprof.

CITATIONS

9 sources
9 citations
[1] The `gen_instr` function is shown as Listing 9, titled "Parallelizing Randomization of the Initial Instruction Dump," with parameters `no_branch`, `no_load_store`, and `is_debug_program`. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[2] `gen_instr` disables garbage collection, sets up allowed instructions, asserts a non-empty `instr_list`, and randomizes instructions. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[3] `gen_instr` uses a sequential path when `instr_list.length <= cfg.par_instr_threshold` or `cfg.par_num_threads == 1`, calling `randomize_instr(instr, is_debug_program)` for each instruction. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[4] When parallelized, `gen_instr` divides `instr_list` across `cfg.par_num_threads`, creates forked workers, randomizes `instr_list[i]` in each worker, assigns thread affinity, appends forks to a list, and joins them. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[5] `gen_instr` removes trailing branch instructions because a final branch instruction would have no forward branch target, then re-enables garbage collection. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[6] RISCV-DV profiling identified non-directed instruction-stream dumping into main-program and sub-program instruction lists as a major bottleneck, with most effort spent on randomization and constraint solving. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[7] The paper describes the first two RISCV-DV bottlenecks as linear in algorithmic complexity and suitable for multicore parallelization. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[8] RISCV-DV execution time varies significantly with user-selected parameters, and the profiling example used `riscv_instr_base_test` with seven directed streams covering RISC-V instruction categories. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings
[9] The paper shows `uvm_trace` calls around instruction generation with the `GEN INSTR` tag and explains that `uvm_trace` provides macro-level profiling but incurs an operating-system call to fetch current clock time. [PDF] Crafting a Million Instructions/Sec RISCV-DV - DVCon Proceedings