A testbench for one sub-entity in my system currently defines a helper process to generate a clock-like waveform at the command of the main stimulus process. A simplified version of it is:
shared variable gen_period : time := 10 us; signal gen_all : boolean := false; -- wavegen enabled signal gen_A : boolean := false; -- run this signal signal A_in : std_logic; -- manual input (wavegen disabled) signal A_gen : std_logic; -- generated output (internal) signal A_out : std_logic; -- final output ... A_generate: process if gen_A then A_gen <= '1'; wait for gen_period/2; A_gen <= '0'; wait for gen_period/2; else A_gen <= '0'; wait for 1 ns; end if; end process; A_out <= A_gen when gen_all else A_in;
The full system has a few more signals, but this is the basic idea.
gen_all applies to all signals and allows a manual signal pattern to be applied when disabled, while
gen_A is basically a clock enable for that specific signal. The other important feature is that the period of the clock is variable.
Putting this all together it means that the main testbench stimulus process can either manually generate edges or can just request a pulse train at a specific interval and then wait for a longer time (typically
gen_period * N) to generate multiple pulses. It works quite well.
Now though I'm interested in generalising this a bit, in particular to instantiate several such independent generators in the testbench for a higher-level design that contains multiple of the original component. However I'm having trouble finding the right way to do so.
My first attempt was to wrap the code above into its own entity, declaring all but
A_gen as ports of the entity. Doing this required changing
gen_period from a variable to a signal, since AFAIK ports must be signals.
Unfortunately, back in the original testbench stimulus process, whenever it tried to assign a new period it had to do this as a signal assignment, which didn't take effect until later. Additionally the
wait for gen_period * 5 calls appeared to be using the original value for gen_period rather than one just assigned. (This is not surprising behaviour for signals, but in this case it's undesired.)
Is there a better way to encapsulate this functionality, and retain instant behaviour of the variable? The code is only going to be used in a testbench, so it does not need to be synthesisable. I'm using Xilinx ISim.
(I realise that I can do a
for .. generate to instantiate multiple instances in the higher level testbench, but this doesn't allow me to share the code between separate testbenches, which is also desirable to avoid duplication.)
I can live with having only one instance of the
gen_period variable shared between all generators in the high level testbench, but I would prefer to have one per generator instance.
Since the answers seem to be focusing on "clock" rather than "waveform", it seems that I need to clarify the usage a bit more. This is inside a testbench:
stim_proc : process begin -- (reset and other setup instructions here) -- generate "slow" pulse train gen_all <= true; gen_period := 250 ns; gen_A <= true; wait for gen_period * 20; gen_A <= false; wait for gen_period; -- (perform tests on logic for slow pulses here) -- generate "fast" pulse train gen_period := 10 ns; gen_A <= true; wait for gen_period * 50; gen_A <= false; wait for gen_period; -- (perform tests on logic for fast pulses here) -- generate asymmetric pulses gen_all <= false; A_in <= '1'; wait for 100 ns; A_in <= '0'; wait for 200 ns; A_in <= '1'; wait for 150 ns; A_in <= '0'; wait for 50 ns; -- (perform tests on logic for asymmetric pulses here) -- etc end process;
These are the simple ones; there are a few more complex ones involving multiple signals happening simultaneously, but that's not really important for this question. The important point is that this is all sequential code and both the pulse generation and the waiting need to act on the most recently set period, not any delayed value.