1 |
3 |
howe.r.j.8 |
-------------------------------------------------------------------------------
|
2 |
|
|
--| @file tb.vhd
|
3 |
|
|
--| @brief Main test bench.
|
4 |
|
|
--|
|
5 |
|
|
--| @author Richard James Howe.
|
6 |
|
|
--| @copyright Copyright 2013,2017 Richard James Howe.
|
7 |
|
|
--| @license MIT
|
8 |
|
|
--| @email howe.r.j.89@gmail.com
|
9 |
|
|
--|
|
10 |
|
|
--| This test bench does quite a lot. It is not like normal VHDL test benches
|
11 |
|
|
--| in the fact that it uses configurable variables that it read in from a
|
12 |
|
|
--| file, which it does in an awkward but usable fashion.
|
13 |
|
|
--|
|
14 |
|
|
--| It also tests multiple modules.
|
15 |
|
|
--|
|
16 |
|
|
--| @todo Optionally, read in from standard input and send the character
|
17 |
|
|
--| over the UART, then print out any received characters to standard out.
|
18 |
|
|
-------------------------------------------------------------------------------
|
19 |
|
|
|
20 |
|
|
library ieee,work;
|
21 |
|
|
use ieee.std_logic_1164.all;
|
22 |
|
|
use ieee.numeric_std.all;
|
23 |
|
|
use ieee.math_real.all;
|
24 |
|
|
use std.textio.all;
|
25 |
|
|
use work.util.all;
|
26 |
|
|
use work.core_pkg.all;
|
27 |
|
|
use work.vga_pkg.all;
|
28 |
|
|
use work.uart_pkg.uart_core;
|
29 |
|
|
|
30 |
|
|
entity tb is
|
31 |
|
|
end tb;
|
32 |
|
|
|
33 |
|
|
architecture testing of tb is
|
34 |
|
|
constant clock_frequency: positive := 100_000_000;
|
35 |
|
|
constant number_of_interrupts: positive := 8;
|
36 |
|
|
constant uart_baud_rate: positive := 115200;
|
37 |
|
|
constant configuration_file_name: string := "tb.cfg";
|
38 |
|
|
constant clk_period: time := 1000 ms / clock_frequency;
|
39 |
|
|
constant uart_tx_time: time := (10*1000 ms) / 115200;
|
40 |
|
|
|
41 |
|
|
-- Test bench configurable options --
|
42 |
|
|
|
43 |
|
|
type configurable_items is record
|
44 |
|
|
number_of_iterations: positive;
|
45 |
|
|
verbose: boolean;
|
46 |
|
|
report_number: natural;
|
47 |
|
|
interactive: boolean;
|
48 |
|
|
end record;
|
49 |
|
|
|
50 |
|
|
function set_configuration_items(ci: configuration_items) return configurable_items is
|
51 |
|
|
variable r: configurable_items;
|
52 |
|
|
begin
|
53 |
|
|
r.number_of_iterations := ci(0).value;
|
54 |
|
|
r.verbose := ci(1).value > 0;
|
55 |
|
|
r.interactive := ci(2).value > 0;
|
56 |
|
|
r.report_number := ci(3).value;
|
57 |
|
|
return r;
|
58 |
|
|
end function;
|
59 |
|
|
|
60 |
|
|
constant configuration_default: configuration_items(0 to 3) := (
|
61 |
|
|
(name => "Cycles ", value => 1000),
|
62 |
|
|
(name => "Verbose ", value => 1),
|
63 |
|
|
(name => "Interact", value => 0),
|
64 |
|
|
(name => "LogFor ", value => 256));
|
65 |
|
|
|
66 |
|
|
-- Test bench configurable options --
|
67 |
|
|
|
68 |
|
|
signal stop: std_ulogic := '0';
|
69 |
|
|
signal debug: cpu_debug_interface;
|
70 |
|
|
|
71 |
|
|
signal clk: std_ulogic := '0';
|
72 |
|
|
signal rst: std_ulogic := '0';
|
73 |
|
|
|
74 |
|
|
-- signal cpu_wait: std_ulogic := '0'; -- CPU wait flag
|
75 |
|
|
|
76 |
|
|
-- Basic I/O
|
77 |
|
|
signal btnu: std_ulogic := '0'; -- button up
|
78 |
|
|
signal btnd: std_ulogic := '0'; -- button down
|
79 |
|
|
signal btnc: std_ulogic := '0'; -- button centre
|
80 |
|
|
signal btnl: std_ulogic := '0'; -- button left
|
81 |
|
|
signal btnr: std_ulogic := '0'; -- button right
|
82 |
|
|
signal sw: std_ulogic_vector(7 downto 0) := (others => '0'); -- switches
|
83 |
|
|
signal an: std_ulogic_vector(3 downto 0) := (others => '0'); -- anodes 8 segment display
|
84 |
|
|
signal ka: std_ulogic_vector(7 downto 0) := (others => '0'); -- kathodes 8 segment display
|
85 |
|
|
signal ld: std_ulogic_vector(7 downto 0) := (others => '0'); -- leds
|
86 |
|
|
|
87 |
|
|
-- VGA
|
88 |
|
|
signal o_vga: vga_physical_interface;
|
89 |
|
|
signal hsync_gone_high: boolean := false;
|
90 |
|
|
signal vsync_gone_high: boolean := false;
|
91 |
|
|
|
92 |
|
|
-- HID
|
93 |
|
|
signal ps2_keyboard_data: std_ulogic := '0';
|
94 |
|
|
signal ps2_keyboard_clk: std_ulogic := '0';
|
95 |
|
|
|
96 |
|
|
-- UART
|
97 |
|
|
signal rx: std_ulogic := '0';
|
98 |
|
|
signal tx: std_ulogic := '0';
|
99 |
|
|
signal dout_ack, dout_stb: std_ulogic := '0';
|
100 |
|
|
signal din_ack, din_stb: std_ulogic := '0';
|
101 |
|
|
signal dout: std_ulogic_vector(7 downto 0) := (others => '0');
|
102 |
|
|
signal din: std_ulogic_vector(7 downto 0) := (others => '0');
|
103 |
|
|
|
104 |
|
|
-- Wave form generator
|
105 |
|
|
signal gen_dout: std_ulogic_vector(15 downto 0) := (others => '0');
|
106 |
|
|
|
107 |
|
|
shared variable cfg: configurable_items := set_configuration_items(configuration_default);
|
108 |
|
|
|
109 |
|
|
signal configured: boolean := false;
|
110 |
|
|
|
111 |
|
|
signal RamCS: std_ulogic := 'X';
|
112 |
|
|
signal MemOE: std_ulogic := 'X'; -- negative logic
|
113 |
|
|
signal MemWR: std_ulogic := 'X'; -- negative logic
|
114 |
|
|
signal MemAdv: std_ulogic := 'X'; -- negative logic
|
115 |
|
|
signal MemWait: std_ulogic := 'X'; -- positive!
|
116 |
|
|
signal FlashCS: std_ulogic := 'X';
|
117 |
|
|
signal FlashRp: std_ulogic := 'X';
|
118 |
|
|
signal MemAdr: std_ulogic_vector(26 downto 1) := (others => 'X');
|
119 |
|
|
signal MemDB: std_logic_vector(15 downto 0) := (others => 'X');
|
120 |
|
|
|
121 |
|
|
begin
|
122 |
|
|
---- Units under test ----------------------------------------------------------
|
123 |
|
|
|
124 |
|
|
MemDB <= (others => '0') when MemOE = '1' else (others => 'Z');
|
125 |
|
|
|
126 |
|
|
uut: entity work.top
|
127 |
|
|
generic map(
|
128 |
|
|
clock_frequency => clock_frequency,
|
129 |
|
|
uart_baud_rate => uart_baud_rate)
|
130 |
|
|
port map(
|
131 |
|
|
debug => debug,
|
132 |
|
|
clk => clk,
|
133 |
|
|
-- rst => rst,
|
134 |
|
|
btnu => btnu,
|
135 |
|
|
btnd => btnd,
|
136 |
|
|
btnc => btnc,
|
137 |
|
|
btnl => btnl,
|
138 |
|
|
btnr => btnr,
|
139 |
|
|
sw => sw,
|
140 |
|
|
an => an,
|
141 |
|
|
ka => ka,
|
142 |
|
|
ld => ld,
|
143 |
|
|
rx => rx,
|
144 |
|
|
tx => tx,
|
145 |
|
|
o_vga => o_vga,
|
146 |
|
|
|
147 |
|
|
ps2_keyboard_data => ps2_keyboard_data,
|
148 |
|
|
ps2_keyboard_clk => ps2_keyboard_clk,
|
149 |
|
|
|
150 |
|
|
RamCS => RamCS,
|
151 |
|
|
MemOE => MemOE,
|
152 |
|
|
MemWR => MemWR,
|
153 |
|
|
MemAdv => MemAdv,
|
154 |
|
|
MemWait => MemWait,
|
155 |
|
|
FlashCS => FlashCS,
|
156 |
|
|
FlashRp => FlashRp,
|
157 |
|
|
MemAdr => MemAdr,
|
158 |
|
|
MemDB => MemDB);
|
159 |
|
|
|
160 |
|
|
uut_util: work.util.util_tb generic map(clock_frequency => clock_frequency);
|
161 |
|
|
uut_vga: work.vga_pkg.vt100_tb generic map(clock_frequency => clock_frequency);
|
162 |
|
|
|
163 |
|
|
-- The "io_pins_tb" works correctly, however GHDL 0.29, compiled under
|
164 |
|
|
-- Windows, cannot fails to simulate this component correctly, and it
|
165 |
|
|
-- crashes. This does not affect the Linux build of GHDL. It has
|
166 |
|
|
-- something to do with 'Z' values for std_ulogic types.
|
167 |
|
|
--
|
168 |
|
|
|
169 |
|
|
uut_io_pins: work.util.io_pins_tb generic map(clock_frequency => clock_frequency);
|
170 |
|
|
|
171 |
|
|
uut_uart: work.uart_pkg.uart_core
|
172 |
|
|
generic map(
|
173 |
|
|
baud_rate => uart_baud_rate,
|
174 |
|
|
clock_frequency => clock_frequency)
|
175 |
|
|
port map(
|
176 |
|
|
clk => clk,
|
177 |
|
|
rst => rst,
|
178 |
|
|
din => din,
|
179 |
|
|
din_stb => din_stb,
|
180 |
|
|
din_ack => din_ack,
|
181 |
|
|
tx => rx,
|
182 |
|
|
rx => tx,
|
183 |
|
|
dout_ack => dout_ack,
|
184 |
|
|
dout_stb => dout_stb,
|
185 |
|
|
dout => dout);
|
186 |
|
|
|
187 |
|
|
------ Simulation only processes ----------------------------------------------
|
188 |
|
|
clk_process: process
|
189 |
|
|
begin
|
190 |
|
|
while stop = '0' loop
|
191 |
|
|
clk <= '1';
|
192 |
|
|
wait for clk_period / 2;
|
193 |
|
|
clk <= '0';
|
194 |
|
|
wait for clk_period / 2;
|
195 |
|
|
end loop;
|
196 |
|
|
wait;
|
197 |
|
|
end process;
|
198 |
|
|
|
199 |
|
|
output_process: process
|
200 |
|
|
variable oline: line;
|
201 |
|
|
variable c: character;
|
202 |
|
|
variable have_char: boolean := true;
|
203 |
|
|
begin
|
204 |
|
|
wait until configured;
|
205 |
|
|
|
206 |
|
|
if not cfg.interactive then
|
207 |
|
|
wait;
|
208 |
|
|
end if;
|
209 |
|
|
|
210 |
|
|
report "WRITING TO STDOUT";
|
211 |
|
|
while stop = '0' loop
|
212 |
|
|
wait until (dout_stb = '1' or stop = '1');
|
213 |
|
|
if stop = '0' then
|
214 |
|
|
c := character'val(to_integer(unsigned(dout)));
|
215 |
|
|
write(oline, c);
|
216 |
|
|
have_char := true;
|
217 |
|
|
if dout = x"0d" then
|
218 |
|
|
writeline(output, oline);
|
219 |
|
|
have_char := false;
|
220 |
|
|
end if;
|
221 |
|
|
wait for clk_period;
|
222 |
|
|
dout_ack <= '1';
|
223 |
|
|
wait for clk_period;
|
224 |
|
|
dout_ack <= '0';
|
225 |
|
|
end if;
|
226 |
|
|
end loop;
|
227 |
|
|
if have_char then
|
228 |
|
|
writeline(output, oline);
|
229 |
|
|
end if;
|
230 |
|
|
wait;
|
231 |
|
|
end process;
|
232 |
|
|
|
233 |
|
|
|
234 |
|
|
-- @note The Input and Output mechanism that allows the tester to
|
235 |
|
|
-- interact with the running simulation needs more work, it is buggy
|
236 |
|
|
-- and experimental, but demonstrates the principle - that a VHDL
|
237 |
|
|
-- test bench can be interacted with at run time.
|
238 |
|
|
input_process: process
|
239 |
|
|
variable c: character := ' ';
|
240 |
|
|
variable iline: line;
|
241 |
|
|
-- variable oline: line;
|
242 |
|
|
variable good: boolean := true;
|
243 |
|
|
begin
|
244 |
|
|
din_stb <= '0';
|
245 |
|
|
din <= x"00";
|
246 |
|
|
wait until configured;
|
247 |
|
|
if not cfg.interactive then
|
248 |
|
|
din_stb <= '1';
|
249 |
|
|
din <= x"AA";
|
250 |
|
|
wait;
|
251 |
|
|
end if;
|
252 |
|
|
|
253 |
|
|
report "READING FROM STDIN";
|
254 |
|
|
while (not endfile(input)) and stop = '0' loop
|
255 |
|
|
readline(input, iline);
|
256 |
|
|
good := true;
|
257 |
|
|
while good and stop = '0' loop
|
258 |
|
|
read(iline, c, good);
|
259 |
|
|
if good then
|
260 |
|
|
report "" & c;
|
261 |
|
|
end if;
|
262 |
|
|
din <=
|
263 |
|
|
std_ulogic_vector(to_unsigned(character'pos(c), din'length));
|
264 |
|
|
din_stb <= '1';
|
265 |
|
|
wait for clk_period;
|
266 |
|
|
din_stb <= '0';
|
267 |
|
|
assert din_ack = '1' severity warning;
|
268 |
|
|
-- wait for 100 us;
|
269 |
|
|
wait for 10 ms;
|
270 |
|
|
end loop;
|
271 |
|
|
end loop;
|
272 |
|
|
-- stop <= '1';
|
273 |
|
|
wait;
|
274 |
|
|
end process;
|
275 |
|
|
|
276 |
|
|
hsync_gone_high <= true when o_vga.hsync = '1' else hsync_gone_high;
|
277 |
|
|
vsync_gone_high <= true when o_vga.vsync = '1' else vsync_gone_high;
|
278 |
|
|
|
279 |
|
|
-- I/O settings go here.
|
280 |
|
|
stimulus_process: process
|
281 |
|
|
variable w: line;
|
282 |
|
|
variable count: integer := 0;
|
283 |
|
|
|
284 |
|
|
function stringify(slv: std_ulogic_vector) return string is
|
285 |
|
|
begin
|
286 |
|
|
return integer'image(to_integer(unsigned(slv)));
|
287 |
|
|
end stringify;
|
288 |
|
|
|
289 |
|
|
procedure element(l: inout line; we: boolean; name: string; slv: std_ulogic_vector) is
|
290 |
|
|
begin
|
291 |
|
|
if we then
|
292 |
|
|
write(l, name & "(" & stringify(slv) & ") ");
|
293 |
|
|
end if;
|
294 |
|
|
end procedure;
|
295 |
|
|
|
296 |
|
|
procedure element(l: inout line; name: string; slv: std_ulogic_vector) is
|
297 |
|
|
begin
|
298 |
|
|
element(l, true, name, slv);
|
299 |
|
|
end procedure;
|
300 |
|
|
|
301 |
|
|
function reportln(debug: cpu_debug_interface; cycles: integer) return line is
|
302 |
|
|
variable l: line;
|
303 |
|
|
begin
|
304 |
|
|
write(l, integer'image(cycles) & ": ");
|
305 |
|
|
element(l, "pc", debug.pc);
|
306 |
|
|
element(l, "insn", debug.insn);
|
307 |
|
|
element(l, "daddr", debug.daddr);
|
308 |
|
|
element(l, "dout", debug.dout);
|
309 |
|
|
return l;
|
310 |
|
|
end function;
|
311 |
|
|
|
312 |
|
|
variable configuration_values: configuration_items(configuration_default'range) := configuration_default;
|
313 |
|
|
begin
|
314 |
|
|
-- write_configuration_tb(configuration_file_name, configuration_default);
|
315 |
|
|
read_configuration_tb(configuration_file_name, configuration_values);
|
316 |
|
|
cfg := set_configuration_items(configuration_values);
|
317 |
|
|
configured <= true;
|
318 |
|
|
|
319 |
|
|
rst <= '1';
|
320 |
|
|
wait for clk_period * 2;
|
321 |
|
|
rst <= '0';
|
322 |
|
|
for i in 0 to cfg.number_of_iterations loop
|
323 |
|
|
if cfg.verbose then
|
324 |
|
|
if count < cfg.report_number then
|
325 |
|
|
w := reportln(debug, count);
|
326 |
|
|
writeline(OUTPUT, w);
|
327 |
|
|
count := count + 1;
|
328 |
|
|
elsif count < cfg.report_number + 1 then
|
329 |
|
|
report "Simulation continuing: Reporting turned off";
|
330 |
|
|
count := count + 1;
|
331 |
|
|
end if;
|
332 |
|
|
end if;
|
333 |
|
|
wait for clk_period * 1;
|
334 |
|
|
end loop;
|
335 |
|
|
|
336 |
|
|
-- It would be nice to test the other peripherals as
|
337 |
|
|
-- well, the CPU-ID should be written to the LED 7 Segment
|
338 |
|
|
-- displays, however we only get the cathode and anode
|
339 |
|
|
-- values out of the unit.
|
340 |
|
|
|
341 |
|
|
assert hsync_gone_high report "HSYNC not active - H2 failed to initialize VGA module";
|
342 |
|
|
assert vsync_gone_high report "VSYNC not active - H2 failed to initialize VGA module";
|
343 |
|
|
|
344 |
|
|
stop <= '1';
|
345 |
|
|
wait;
|
346 |
|
|
end process;
|
347 |
|
|
|
348 |
|
|
end architecture;
|
349 |
|
|
------ END ---------------------------------------------------------------------
|
350 |
|
|
|