in pratica con il testbench tu 'manualmente' assegni dei valori agli ingressi in modo da verificare che i risultati siano quelli attesi.
Dal punto di vista del codice, provo a farti 1 esempio:
se precedentemente avevi scritto
entity example is
port(a, b: in bit;
c: out bit
);
end example;
per il testbench devi assegnare tutti i possibili valori ad a e b (se fossero tanti basta un sottoinsieme dei + significativi), così che simulando puoi fare un'analisi dell'uscita. Un file testbench non è altro che un altro file vhdl che nella sua dichiarazione non ha bisogno di porte perchè non necessita di comunicare con l'esterni. Quindi avrai:
entity example_tb is
end example_tb;
dopodichè nell'architettura inizi richiamando il componente di cui vuoi fare la simulazione con le relative porte:
architecture test of example_tb is
component example
port(a, b: in bit:
c: out bit
);
end component;
dopodichè dichiari i segnali per fare la simulazione:
signal a_s, b_s, c_s: bit;
finite le dichiarazioni, passi all'architettura vera e propria, iniziando con la port map con cui associ i segnali che hai dichiarato a componente:
begin
uut: example port map(a, b, c);
si continua associando ad ogni segnale di input un processo in cui appunto assegni tutti i valori possibili per la simulazione
a_p: process
begin
a_s <= '0';
wait for 15 ns;
a_s <= '1';
wait for 20 ns;
end process;
b_p: process
begin
b_s <= '0';
wait for 5 ns;
b_s <= '1';
wait for 10 ns;
end process;
end test;
i ns dei wait for li devi scegliere sempre in modo tale che tu dalla simulazione possa ottenere più combinazioni possibili, per esempio in questo caso se c fosse un and di a e b, associando gli stessi ns, simulando anche per 500 ns avrei sempre 2 combinazione: a = 0 e b = 0 (c = 0) e a = 1 e b = 1 (c = 1).
Oddio spero di essere stata chiara, se hai qualche dubbio più specifico ... io ho cercato di inquadrare in generale l'argomento testbench, ho approfittato per ripetere ^^