-
-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathtb.vhd
407 lines (357 loc) · 12.3 KB
/
tb.vhd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
-------------------------------------------------------------------------------
--| @file tb.vhd
--| @brief Main test bench.
--|
--| @author Richard James Howe.
--| @copyright Copyright 2013-2019 Richard James Howe.
--| @license MIT
--| @email howe.r.j.89@gmail.com
--|
--| This test bench does quite a lot. It is not like normal VHDL test benches
--| in the fact that it uses configurable variables that it reads in from a
--| file, which it does in an awkward but usable fashion. It also has a
--| partially working way of connecting a simulated UART to STDIN/STDOUT, which
--| is a work in progress.
--|
--| It also tests multiple modules.
--|
-------------------------------------------------------------------------------
library ieee,work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;
use work.util.all;
use work.core_pkg.all;
use work.vga_pkg.all;
entity tb is
end tb;
architecture testing of tb is
constant g: common_generics := (
clock_frequency => 100_000_000,
asynchronous_reset => true,
delay => 0 ns);
constant number_of_interrupts: positive := 8;
constant uart_baud: positive := 115200;
constant configuration_file_name: string := "tb.cfg";
constant uart_tx_time: time := (10*1000 ms) / 115200;
constant uart_default_input: std_ulogic_vector(7 downto 0) := x"AA";
constant reset_period_us: natural := 1;
constant jitter_on: boolean := false;
constant clock_period: time := 1000 ms / g.clock_frequency;
constant tb_vga_on: boolean := false;
constant tb_uart_on: boolean := false;
constant tb_util_on: boolean := false;
-- Test bench configurable options --
type configurable_items is record
number_of_iterations: natural; -- 0 == loop forever
verbose: boolean;
report_number: natural;
interactive: boolean;
input_wait_for: time;
end record;
function set_configuration_items(ci: configuration_items) return configurable_items is
variable r: configurable_items;
begin
r.number_of_iterations := ci(0).value;
r.verbose := ci(1).value > 0;
r.interactive := ci(2).value > 0;
r.input_wait_for := ci(3).value * 1 ms;
r.report_number := ci(4).value;
return r;
end function;
constant configuration_default: configuration_items(0 to 4) := (
(name => "Cycles ", value => 1000),
(name => "Verbose ", value => 1),
(name => "Interact", value => 0),
(name => "InWaitMs", value => 8),
(name => "LogFor ", value => 256));
-- Test bench configurable options --
signal stop: boolean := false;
signal dbgi: cpu_debug_interface;
signal clk: std_ulogic := '0';
signal rst: std_ulogic := '0';
-- Basic I/O
signal btnu: std_ulogic := '0'; -- button up
signal btnd: std_ulogic := '0'; -- button down
signal btnc: std_ulogic := '0'; -- button centre
signal btnl: std_ulogic := '0'; -- button left
signal btnr: std_ulogic := '0'; -- button right
signal sw: std_ulogic_vector(7 downto 0) := (others => '0'); -- switches
signal an: std_ulogic_vector(3 downto 0) := (others => '0'); -- anodes 8 segment display
signal ka: std_ulogic_vector(7 downto 0) := (others => '0'); -- kathodes 8 segment display
signal ld: std_ulogic_vector(7 downto 0) := (others => '0'); -- leds
-- VGA
signal o_vga: vga_physical_interface;
signal hsync_gone_high: boolean := false;
signal vsync_gone_high: boolean := false;
-- HID
signal ps2_keyboard_data: std_ulogic := '0';
signal ps2_keyboard_clk: std_ulogic := '0';
-- UART
signal rx: std_ulogic := '0';
signal tx: std_ulogic := '0';
signal dout_ack, dout_stb: std_ulogic := '0';
signal din_ack, din_stb: std_ulogic := '0';
signal dout: std_ulogic_vector(7 downto 0) := (others => '0');
signal din: std_ulogic_vector(7 downto 0) := (others => '0');
-- Wave form generator
signal gen_dout: std_ulogic_vector(15 downto 0) := (others => '0');
shared variable cfg: configurable_items := set_configuration_items(configuration_default);
signal configured: boolean := false;
signal ram_cs: std_ulogic := 'X';
signal mem_oe: std_ulogic := 'X'; -- negative logic
signal mem_wr: std_ulogic := 'X'; -- negative logic
signal mem_adv: std_ulogic := 'X'; -- negative logic
signal mem_wait: std_ulogic := 'X'; -- positive!
signal flash_cs: std_ulogic := 'X';
signal flash_rp: std_ulogic := 'X';
signal mem_addr: std_ulogic_vector(26 downto 1) := (others => 'X');
signal mem_data: std_logic_vector(15 downto 0) := (others => 'X');
begin
---- Units under test ----------------------------------------------------------
mem_data <= (others => '0') when mem_oe = '1' else (others => 'Z');
uut: entity work.top
generic map(
g => g,
reset_period_us => reset_period_us,
uart_baud => uart_baud)
port map(
debug => dbgi,
clk => clk,
-- rst => rst,
btnu => btnu,
btnd => btnd,
btnc => btnc,
btnl => btnl,
btnr => btnr,
sw => sw,
an => an,
ka => ka,
ld => ld,
rx => rx,
tx => tx,
o_vga => o_vga,
ps2_keyboard_data => ps2_keyboard_data,
ps2_keyboard_clk => ps2_keyboard_clk,
ram_cs => ram_cs,
mem_oe => mem_oe,
mem_wr => mem_wr,
mem_adv => mem_adv,
mem_wait => mem_wait,
flash_cs => flash_cs,
flash_rp => flash_rp,
mem_addr => mem_addr,
mem_data => mem_data);
-- NB. It would be nice to configure these as off/on, as well as
-- controlling how long they run for from here.
util_g: if tb_util_on generate uut_util: work.util.util_tb generic map(g => g); end generate;
vga_g: if tb_vga_on generate uut_vga: work.vga_pkg.vt100_tb generic map(g => g); end generate;
uart_g: if tb_uart_on generate uut_uart: work.uart_pkg.uart_tb generic map(g => g); end generate;
uart_0_blk: block
signal uart_clock_rx_we, uart_clock_tx_we, uart_control_we: std_ulogic := '0';
signal uart_reg: std_ulogic_vector(15 downto 0);
begin
uart_0: work.uart_pkg.uart_core
generic map (g => g, baud => uart_baud)
port map (
clk => clk,
rst => rst,
tx_di => din,
tx_we => din_stb,
tx_ok => din_ack,
tx => rx,
rx => tx,
rx_ok => open,
rx_nd => dout_stb,
rx_do => dout,
rx_re => dout_ack,
reg => uart_reg,
clock_reg_tx_we => uart_clock_tx_we,
clock_reg_rx_we => uart_clock_rx_we,
control_reg_we => uart_control_we);
end block;
------ Simulation only processes ----------------------------------------------
clk_process: process
variable seed1, seed2 : positive;
variable r : real;
variable jit_high, jit_low: time := 0 ns;
begin
while not stop loop
if jitter_on then
uniform(seed1, seed2, r);
jit_high := r * g.delay;
uniform(seed1, seed2, r);
jit_low := r * g.delay;
uniform(seed1, seed2, r);
if r < 0.5 then jit_high := -jit_high; end if;
uniform(seed1, seed2, r);
if r < 0.5 then jit_low := -jit_low; end if;
else
jit_high := 0 ns;
jit_low := 0 ns;
end if;
clk <= '1';
wait for (clock_period / 2) + jit_high;
clk <= '0';
wait for (clock_period / 2) + jit_low;
end loop;
report "clk_process end";
wait;
end process;
output_process: process
variable oline: line;
variable c: character;
variable have_char: boolean := true;
begin
wait until configured;
if not cfg.interactive then
report "Output turned off";
report "output_process end";
wait;
end if;
report "Writing to STDOUT";
while not stop loop
wait until (dout_stb = '1' or stop);
if not stop then
c := character'val(to_integer(unsigned(dout)));
write(oline, c);
have_char := true;
if dout = x"0d" then
writeline(output, oline);
have_char := false;
end if;
wait for clock_period;
dout_ack <= '1';
wait for clock_period;
dout_ack <= '0';
end if;
end loop;
if have_char then
writeline(output, oline);
end if;
report "output_process end";
wait;
end process;
-- The Input and Output mechanism that allows the tester to
-- interact with the running simulation needs more work, it is buggy
-- and experimental, but demonstrates the principle - that a VHDL
-- test bench can be interacted with at run time.
input_process: process
variable c: character := ' ';
variable iline: line;
-- variable oline: line;
variable good: boolean := true;
variable eoi: boolean := false;
begin
din_stb <= '0';
din <= x"00";
wait until configured;
if not cfg.interactive then
din_stb <= '1';
din <= uart_default_input;
report "input process non-interactive";
report "input_process end";
wait;
end if;
report "Waiting for " & time'image(cfg.input_wait_for) & " (before reading from STDIN)";
wait for cfg.input_wait_for;
report "Reading from STDIN (Hit EOF/CTRL-D/CTRL-Z After entering a line)";
while (not endfile(input)) and not stop and eoi = false loop
report "readline...";
readline(input, iline);
good := true;
while good and not stop loop
read(iline, c, good);
if good then
report "" & c;
else
report "EOL/EOI";
c := LF;
eoi := true;
end if;
din <= std_ulogic_vector(to_unsigned(character'pos(c), din'length));
din_stb <= '1';
wait for clock_period;
din_stb <= '0';
wait for 100 us;
end loop;
end loop;
report "input_process end";
wait;
end process;
hsync_gone_high <= true when o_vga.hsync = '1' else hsync_gone_high;
vsync_gone_high <= true when o_vga.vsync = '1' else vsync_gone_high;
-- I/O settings go here.
stimulus_process: process
variable w: line;
variable count: integer := 0;
function stringify(slv: std_ulogic_vector) return string is
begin
return integer'image(to_integer(unsigned(slv)));
end stringify;
procedure element(l: inout line; we: boolean; name: string; slv: std_ulogic_vector) is
begin
if we then
write(l, name & "(" & stringify(slv) & ") ");
end if;
end procedure;
procedure element(l: inout line; name: string; slv: std_ulogic_vector) is
begin
element(l, true, name, slv);
end procedure;
function reportln(debug: cpu_debug_interface; cycles: integer) return line is
variable l: line;
begin
write(l, integer'image(cycles) & ": ");
element(l, "pc", debug.pc);
element(l, "insn", debug.insn);
element(l, "daddr", debug.daddr);
element(l, "dout", debug.dout);
return l;
end function;
variable configuration_values: configuration_items(configuration_default'range) := configuration_default;
begin
-- write_configuration_tb(configuration_file_name, configuration_default);
read_configuration_tb(configuration_file_name, configuration_values);
cfg := set_configuration_items(configuration_values);
configured <= true;
rst <= '1';
wait for clock_period * 2;
rst <= '0';
if cfg.number_of_iterations = 0 then
report "RUNNING FOREVER: number of iterations is zero" severity warning;
report "stimulus_process end";
wait;
end if;
for i in 0 to cfg.number_of_iterations loop
if cfg.verbose then
if count < cfg.report_number then
w := reportln(dbgi, count);
writeline(OUTPUT, w);
count := count + 1;
elsif count < cfg.report_number + 1 then
report "Simulation continuing: Reporting turned off";
count := count + 1;
end if;
end if;
wait for clock_period * 1;
end loop;
-- These HSYNC and VSYNC asserts are included under the assumption
-- that the image running on the H2 CPU will initiate the VGA, if
-- it does not (perhaps because it is running it's own initialization
-- routines), then the HSYNC or VSYNC will never go high - so this is
-- not necessarily an error.
--
-- It would be nice to test the other peripherals as
-- well, the CPU-ID should be written to the LED 7 Segment
-- displays, however we only get the cathode and anode
-- values out of the unit.
assert hsync_gone_high report "HSYNC not active - H2 failed to initialize VGA module";
assert vsync_gone_high report "VSYNC not active - H2 failed to initialize VGA module";
stop <= true;
report "stimulus_process end";
wait;
end process;
end architecture;
------ END ---------------------------------------------------------------------