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_thresholdcfg.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.