1
|
-------------------------------------------------------------------------------
|
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
|
|