1
|
-------------------------------------------------------------------------------
|
2
|
--| @file util.vhd
|
3
|
--| @brief A collection of utilities and simple components. The components
|
4
|
--| should be synthesizable, and the functions can be used within synthesizable
|
5
|
--| components, unless marked with a "_tb" suffix (or is the function n_bits).
|
6
|
--| @author Richard James Howe
|
7
|
--| @copyright Copyright 2017 Richard James Howe
|
8
|
--| @license MIT
|
9
|
--| @email howe.r.j.89@gmail.com
|
10
|
-------------------------------------------------------------------------------
|
11
|
library ieee;
|
12
|
use ieee.std_logic_1164.all;
|
13
|
use ieee.numeric_std.all;
|
14
|
use std.textio.all;
|
15
|
|
16
|
package util is
|
17
|
component util_tb is
|
18
|
generic(clock_frequency: positive);
|
19
|
end component;
|
20
|
|
21
|
component clock_source_tb is
|
22
|
generic(clock_frequency: positive; hold_rst: positive := 1);
|
23
|
port(
|
24
|
stop: in std_ulogic := '0';
|
25
|
clk: buffer std_ulogic;
|
26
|
clk_with_jitter: out std_ulogic := '0';
|
27
|
rst: out std_ulogic := '0');
|
28
|
end component;
|
29
|
|
30
|
component reg
|
31
|
generic(N: positive);
|
32
|
port(
|
33
|
clk: in std_ulogic;
|
34
|
rst: in std_ulogic;
|
35
|
we: in std_ulogic;
|
36
|
di: in std_ulogic_vector(N - 1 downto 0);
|
37
|
do: out std_ulogic_vector(N - 1 downto 0));
|
38
|
end component;
|
39
|
|
40
|
component shift_register
|
41
|
generic(N: positive);
|
42
|
port(
|
43
|
clk: in std_ulogic;
|
44
|
rst: in std_ulogic;
|
45
|
we: in std_ulogic;
|
46
|
di: in std_ulogic;
|
47
|
do: out std_ulogic;
|
48
|
|
49
|
-- optional
|
50
|
load_we: in std_ulogic := '0';
|
51
|
load_i: in std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
52
|
load_o: out std_ulogic_vector(N - 1 downto 0));
|
53
|
end component;
|
54
|
|
55
|
component shift_register_tb
|
56
|
generic(clock_frequency: positive);
|
57
|
end component;
|
58
|
|
59
|
component timer_us
|
60
|
generic(clock_frequency: positive; timer_period_us: natural);
|
61
|
port(
|
62
|
clk: in std_ulogic;
|
63
|
rst: in std_ulogic;
|
64
|
co: out std_ulogic);
|
65
|
end component;
|
66
|
|
67
|
component timer_us_tb
|
68
|
generic(clock_frequency: positive);
|
69
|
end component;
|
70
|
|
71
|
component rising_edge_detector is
|
72
|
port(
|
73
|
clk: in std_ulogic;
|
74
|
rst: in std_ulogic;
|
75
|
di: in std_ulogic;
|
76
|
do: out std_ulogic);
|
77
|
end component;
|
78
|
|
79
|
component rising_edge_detector_tb is
|
80
|
generic(clock_frequency: positive);
|
81
|
end component;
|
82
|
|
83
|
component rising_edge_detectors is
|
84
|
generic(N: positive);
|
85
|
port(
|
86
|
clk: in std_ulogic;
|
87
|
rst: in std_ulogic;
|
88
|
di: in std_ulogic_vector(N - 1 downto 0);
|
89
|
do: out std_ulogic_vector(N - 1 downto 0));
|
90
|
end component;
|
91
|
|
92
|
|
93
|
-- @note half_adder test bench is folded in to full_adder_tb
|
94
|
component half_adder is
|
95
|
port(
|
96
|
a: in std_ulogic;
|
97
|
b: in std_ulogic;
|
98
|
sum: out std_ulogic;
|
99
|
carry: out std_ulogic);
|
100
|
end component;
|
101
|
|
102
|
component full_adder is
|
103
|
port(
|
104
|
x: in std_ulogic;
|
105
|
y: in std_ulogic;
|
106
|
z: in std_ulogic;
|
107
|
sum: out std_ulogic;
|
108
|
carry: out std_ulogic);
|
109
|
end component;
|
110
|
|
111
|
component full_adder_tb is
|
112
|
generic(clock_frequency: positive);
|
113
|
end component;
|
114
|
|
115
|
component fifo is
|
116
|
generic (data_width: positive;
|
117
|
fifo_depth: positive);
|
118
|
port (
|
119
|
clk: in std_ulogic;
|
120
|
rst: in std_ulogic;
|
121
|
di: in std_ulogic_vector(data_width - 1 downto 0);
|
122
|
we: in std_ulogic;
|
123
|
re: in std_ulogic;
|
124
|
do: out std_ulogic_vector(data_width - 1 downto 0);
|
125
|
|
126
|
-- optional
|
127
|
full: out std_ulogic := '0';
|
128
|
empty: out std_ulogic := '1');
|
129
|
end component;
|
130
|
|
131
|
component fifo_tb is
|
132
|
generic(clock_frequency: positive);
|
133
|
end component;
|
134
|
|
135
|
component counter is
|
136
|
generic(
|
137
|
N: positive);
|
138
|
port(
|
139
|
clk: in std_ulogic;
|
140
|
rst: in std_ulogic;
|
141
|
ce: in std_ulogic;
|
142
|
cr: in std_ulogic;
|
143
|
dout: out std_ulogic_vector(N - 1 downto 0);
|
144
|
|
145
|
-- optional
|
146
|
load_we: in std_ulogic := '0';
|
147
|
load_i: in std_ulogic_vector(N - 1 downto 0) := (others => '0'));
|
148
|
end component;
|
149
|
|
150
|
component counter_tb is
|
151
|
generic(clock_frequency: positive);
|
152
|
end component;
|
153
|
|
154
|
component lfsr is
|
155
|
generic(constant tap: std_ulogic_vector);
|
156
|
port
|
157
|
(
|
158
|
clk: in std_ulogic;
|
159
|
rst: in std_ulogic;
|
160
|
ce: in std_ulogic := '1';
|
161
|
we: in std_ulogic;
|
162
|
di: in std_ulogic_vector(tap'high + 1 to tap'low);
|
163
|
do: out std_ulogic_vector(tap'high + 1 to tap'low));
|
164
|
end component;
|
165
|
|
166
|
component lfsr_tb is
|
167
|
generic(clock_frequency: positive);
|
168
|
end component;
|
169
|
|
170
|
component io_pins is
|
171
|
generic(
|
172
|
N: positive);
|
173
|
port
|
174
|
(
|
175
|
clk: in std_ulogic;
|
176
|
rst: in std_ulogic;
|
177
|
control: in std_ulogic_vector(N - 1 downto 0);
|
178
|
control_we: in std_ulogic;
|
179
|
din: in std_ulogic_vector(N - 1 downto 0);
|
180
|
din_we: in std_ulogic;
|
181
|
dout: out std_ulogic_vector(N - 1 downto 0);
|
182
|
pins: inout std_logic_vector(N - 1 downto 0));
|
183
|
end component;
|
184
|
|
185
|
component io_pins_tb is
|
186
|
generic(clock_frequency: positive);
|
187
|
end component;
|
188
|
|
189
|
type file_format is (FILE_HEX, FILE_BINARY, FILE_NONE);
|
190
|
|
191
|
component dual_port_block_ram is
|
192
|
generic(addr_length: positive := 12;
|
193
|
data_length: positive := 16;
|
194
|
file_name: string := "memory.bin";
|
195
|
file_type: file_format := FILE_BINARY);
|
196
|
port(
|
197
|
-- port A of dual port RAM
|
198
|
a_clk: in std_ulogic;
|
199
|
a_dwe: in std_ulogic;
|
200
|
a_dre: in std_ulogic;
|
201
|
a_addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
202
|
a_din: in std_ulogic_vector(data_length - 1 downto 0);
|
203
|
a_dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0');
|
204
|
-- port B of dual port RAM
|
205
|
b_clk: in std_ulogic;
|
206
|
b_dwe: in std_ulogic;
|
207
|
b_dre: in std_ulogic;
|
208
|
b_addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
209
|
b_din: in std_ulogic_vector(data_length - 1 downto 0);
|
210
|
b_dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
|
211
|
end component;
|
212
|
|
213
|
component single_port_block_ram is
|
214
|
generic(addr_length: positive := 12;
|
215
|
data_length: positive := 16;
|
216
|
file_name: string := "memory.bin";
|
217
|
file_type: file_format := FILE_BINARY);
|
218
|
port(
|
219
|
clk: in std_ulogic;
|
220
|
dwe: in std_ulogic;
|
221
|
dre: in std_ulogic;
|
222
|
addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
223
|
din: in std_ulogic_vector(data_length - 1 downto 0);
|
224
|
dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
|
225
|
end component;
|
226
|
|
227
|
component data_source is
|
228
|
generic(addr_length: positive := 12;
|
229
|
data_length: positive := 16;
|
230
|
file_name: string := "memory.bin";
|
231
|
file_type: file_format := FILE_BINARY);
|
232
|
port(
|
233
|
clk: in std_ulogic;
|
234
|
rst: in std_ulogic;
|
235
|
|
236
|
ce: in std_ulogic := '1';
|
237
|
cr: in std_ulogic;
|
238
|
|
239
|
load: in std_ulogic_vector(addr_length - 1 downto 0) := (others => '0');
|
240
|
load_we: in std_ulogic := '0';
|
241
|
|
242
|
dout: out std_ulogic_vector(data_length - 1 downto 0));
|
243
|
end component;
|
244
|
|
245
|
component ucpu is
|
246
|
generic(width: positive range 8 to 32 := 8);
|
247
|
port(
|
248
|
clk, rst: in std_ulogic;
|
249
|
|
250
|
pc: out std_ulogic_vector(width - 3 downto 0);
|
251
|
op: in std_ulogic_vector(width - 1 downto 0);
|
252
|
|
253
|
adr: out std_ulogic_vector(width - 3 downto 0);
|
254
|
di: in std_ulogic_vector(width - 1 downto 0);
|
255
|
re, we: out std_ulogic;
|
256
|
do: out std_ulogic_vector(width - 1 downto 0));
|
257
|
end component;
|
258
|
|
259
|
component ucpu_tb is
|
260
|
generic(
|
261
|
clock_frequency: positive;
|
262
|
file_name: string := "ucpu.bin");
|
263
|
end component;
|
264
|
|
265
|
component restoring_divider is
|
266
|
generic(N: positive);
|
267
|
port(
|
268
|
clk: in std_ulogic;
|
269
|
rst: in std_ulogic := '0';
|
270
|
|
271
|
a: in std_ulogic_vector(N - 1 downto 0);
|
272
|
b: in std_ulogic_vector(N - 1 downto 0);
|
273
|
start: in std_ulogic;
|
274
|
done: out std_ulogic;
|
275
|
c: out std_ulogic_vector(N - 1 downto 0));
|
276
|
end component;
|
277
|
|
278
|
component restoring_divider_tb is
|
279
|
generic(clock_frequency: positive);
|
280
|
end component;
|
281
|
|
282
|
component debounce_us is
|
283
|
generic(clock_frequency: positive; timer_period_us: natural);
|
284
|
port(
|
285
|
clk: in std_ulogic;
|
286
|
di: in std_ulogic;
|
287
|
do: out std_ulogic);
|
288
|
end component;
|
289
|
|
290
|
component debounce_block_us is
|
291
|
generic(N: positive; clock_frequency: positive; timer_period_us: natural);
|
292
|
port(
|
293
|
clk: in std_ulogic;
|
294
|
di: in std_ulogic_vector(N - 1 downto 0);
|
295
|
do: out std_ulogic_vector(N - 1 downto 0));
|
296
|
end component;
|
297
|
|
298
|
component debounce_us_tb is
|
299
|
generic(clock_frequency: positive);
|
300
|
end component;
|
301
|
|
302
|
component state_changed is
|
303
|
port(
|
304
|
clk: in std_ulogic;
|
305
|
rst: in std_ulogic;
|
306
|
di: in std_ulogic;
|
307
|
do: out std_ulogic);
|
308
|
end component;
|
309
|
|
310
|
component state_block_changed is
|
311
|
generic(N: positive);
|
312
|
port(
|
313
|
clk: in std_ulogic;
|
314
|
rst: in std_ulogic;
|
315
|
di: in std_ulogic_vector(N - 1 downto 0);
|
316
|
do: out std_ulogic_vector(N - 1 downto 0));
|
317
|
end component;
|
318
|
|
319
|
function max(a: natural; b: natural) return natural;
|
320
|
function min(a: natural; b: natural) return natural;
|
321
|
function n_bits(x: natural) return natural;
|
322
|
function n_bits(x: std_ulogic_vector) return natural;
|
323
|
function reverse (a: in std_ulogic_vector) return std_ulogic_vector;
|
324
|
function invert(slv:std_ulogic_vector) return std_ulogic_vector;
|
325
|
function parity(slv:std_ulogic_vector; even: boolean) return std_ulogic;
|
326
|
function select_bit(indexed, selector: std_ulogic_vector) return std_ulogic;
|
327
|
function priority(order: std_ulogic_vector; high: boolean) return natural;
|
328
|
function priority(order: std_ulogic_vector; high: boolean) return std_ulogic_vector;
|
329
|
function mux(a: std_ulogic_vector; b: std_ulogic_vector; sel: std_ulogic) return std_ulogic_vector;
|
330
|
function mux(a: std_ulogic; b: std_ulogic; sel: std_ulogic) return std_ulogic;
|
331
|
function mux(a, b : std_ulogic_vector) return std_ulogic;
|
332
|
function decode(encoded: std_ulogic_vector) return std_ulogic_vector;
|
333
|
function hex_char_to_std_ulogic_vector(hc: character) return std_ulogic_vector;
|
334
|
function to_std_ulogic_vector(s: string) return std_ulogic_vector;
|
335
|
|
336
|
type ulogic_string is array(integer range <>) of std_ulogic_vector(7 downto 0);
|
337
|
function to_std_ulogic_vector(s: string) return ulogic_string;
|
338
|
|
339
|
--- Not synthesizable ---
|
340
|
|
341
|
subtype configuration_name is string(1 to 8);
|
342
|
|
343
|
type configuration_item is record
|
344
|
name: configuration_name;
|
345
|
value: integer;
|
346
|
end record;
|
347
|
|
348
|
type configuration_items is array(integer range <>) of configuration_item;
|
349
|
|
350
|
function search_configuration_tb(find_me: configuration_name; ci: configuration_items) return integer;
|
351
|
procedure read_configuration_tb(file_name: string; ci: inout configuration_items);
|
352
|
procedure write_configuration_tb(file_name: string; ci: configuration_items);
|
353
|
|
354
|
end;
|
355
|
|
356
|
package body util is
|
357
|
|
358
|
function max(a: natural; b: natural) return natural is
|
359
|
begin
|
360
|
if (a > b) then return a; else return b; end if;
|
361
|
end function;
|
362
|
|
363
|
function min(a: natural; b: natural) return natural is
|
364
|
begin
|
365
|
if (a < b) then return a; else return b; end if;
|
366
|
end function;
|
367
|
|
368
|
function n_bits(x: natural) return natural is
|
369
|
variable x1: natural := max(x, 1) - 1;
|
370
|
variable n: natural := 1;
|
371
|
begin
|
372
|
while x1 > 1 loop
|
373
|
x1 := x1 / 2;
|
374
|
n := n + 1;
|
375
|
end loop;
|
376
|
return n;
|
377
|
end function;
|
378
|
|
379
|
function n_bits(x: std_ulogic_vector) return natural is
|
380
|
begin
|
381
|
return n_bits(x'high);
|
382
|
end function;
|
383
|
|
384
|
-- https://stackoverflow.com/questions/13584307
|
385
|
function reverse (a: in std_ulogic_vector) return std_ulogic_vector is
|
386
|
variable result: std_ulogic_vector(a'range);
|
387
|
alias aa: std_ulogic_vector(a'reverse_range) is a;
|
388
|
begin
|
389
|
for i in aa'range loop
|
390
|
result(i) := aa(i);
|
391
|
end loop;
|
392
|
return result;
|
393
|
end;
|
394
|
|
395
|
function invert(slv: std_ulogic_vector) return std_ulogic_vector is
|
396
|
variable z: std_ulogic_vector(slv'range);
|
397
|
begin
|
398
|
for i in slv'range loop
|
399
|
z(i) := not(slv(i));
|
400
|
end loop;
|
401
|
return z;
|
402
|
end;
|
403
|
|
404
|
function parity(slv: std_ulogic_vector; even: boolean) return std_ulogic is
|
405
|
variable z: std_ulogic := '0';
|
406
|
begin
|
407
|
if not even then
|
408
|
z := '1';
|
409
|
end if;
|
410
|
for i in slv'range loop
|
411
|
z := z xor slv(i);
|
412
|
end loop;
|
413
|
return z;
|
414
|
end;
|
415
|
|
416
|
function select_bit(indexed, selector: std_ulogic_vector) return std_ulogic is
|
417
|
variable z: std_ulogic := 'X';
|
418
|
begin
|
419
|
assert n_bits(indexed) = selector'high + 1 severity failure;
|
420
|
for i in indexed'range loop
|
421
|
if i = to_integer(unsigned(selector)) then
|
422
|
z := indexed(i);
|
423
|
end if;
|
424
|
end loop;
|
425
|
return z;
|
426
|
end;
|
427
|
|
428
|
function priority(order: std_ulogic_vector; high: boolean) return natural is
|
429
|
variable p: natural := 0;
|
430
|
begin
|
431
|
if not high then
|
432
|
for i in order'high + 1 downto 1 loop
|
433
|
if order(i-1) = '1' then
|
434
|
p := i - 1;
|
435
|
end if;
|
436
|
end loop;
|
437
|
else
|
438
|
for i in 1 to order'high + 1 loop
|
439
|
if order(i-1) = '1' then
|
440
|
p := i - 1;
|
441
|
end if;
|
442
|
end loop;
|
443
|
end if;
|
444
|
return p;
|
445
|
end;
|
446
|
|
447
|
function priority(order: std_ulogic_vector; high: boolean) return std_ulogic_vector is
|
448
|
variable length: natural := n_bits(order'length);
|
449
|
begin
|
450
|
return std_ulogic_vector(to_unsigned(priority(order, high), length));
|
451
|
end;
|
452
|
|
453
|
function mux(a: std_ulogic_vector; b: std_ulogic_vector; sel: std_ulogic) return std_ulogic_vector is
|
454
|
variable m: std_ulogic_vector(a'range) := (others => 'X');
|
455
|
begin
|
456
|
if sel = '0' then m := a; else m := b; end if;
|
457
|
return m;
|
458
|
end;
|
459
|
|
460
|
function mux(a: std_ulogic; b: std_ulogic; sel: std_ulogic) return std_ulogic is
|
461
|
variable m: std_ulogic := 'X';
|
462
|
begin
|
463
|
if sel = '0' then m := a; else m := b; end if;
|
464
|
return m;
|
465
|
end;
|
466
|
|
467
|
function mux(a, b : std_ulogic_vector) return std_ulogic is
|
468
|
variable r: std_ulogic_vector(b'length - 1 downto 0) := (others => 'X');
|
469
|
variable i: integer;
|
470
|
begin
|
471
|
r := b;
|
472
|
i := to_integer(unsigned(a));
|
473
|
return r(i);
|
474
|
end;
|
475
|
|
476
|
function decode(encoded : std_ulogic_vector) return std_ulogic_vector is
|
477
|
variable r: std_ulogic_vector((2 ** encoded'length) - 1 downto 0) := (others => '0');
|
478
|
variable i: natural;
|
479
|
begin
|
480
|
i := to_integer(unsigned(encoded));
|
481
|
r(i) := '1';
|
482
|
return r;
|
483
|
end;
|
484
|
|
485
|
function hex_char_to_std_ulogic_vector(hc: character) return std_ulogic_vector is
|
486
|
variable slv: std_ulogic_vector(3 downto 0);
|
487
|
begin
|
488
|
case hc is
|
489
|
when '0' => slv := "0000";
|
490
|
when '1' => slv := "0001";
|
491
|
when '2' => slv := "0010";
|
492
|
when '3' => slv := "0011";
|
493
|
when '4' => slv := "0100";
|
494
|
when '5' => slv := "0101";
|
495
|
when '6' => slv := "0110";
|
496
|
when '7' => slv := "0111";
|
497
|
when '8' => slv := "1000";
|
498
|
when '9' => slv := "1001";
|
499
|
when 'A' => slv := "1010";
|
500
|
when 'a' => slv := "1010";
|
501
|
when 'B' => slv := "1011";
|
502
|
when 'b' => slv := "1011";
|
503
|
when 'C' => slv := "1100";
|
504
|
when 'c' => slv := "1100";
|
505
|
when 'D' => slv := "1101";
|
506
|
when 'd' => slv := "1101";
|
507
|
when 'E' => slv := "1110";
|
508
|
when 'e' => slv := "1110";
|
509
|
when 'F' => slv := "1111";
|
510
|
when 'f' => slv := "1111";
|
511
|
when others => slv := "XXXX";
|
512
|
end case;
|
513
|
assert (slv /= "XXXX") report " not a valid hex character: " & hc severity failure;
|
514
|
return slv;
|
515
|
end;
|
516
|
|
517
|
-- <https://stackoverflow.com/questions/30519849/vhdl-convert-string-to-std-logic-vector>
|
518
|
function to_std_ulogic_vector(s: string) return std_ulogic_vector is
|
519
|
variable ret: std_ulogic_vector(s'length*8-1 downto 0);
|
520
|
begin
|
521
|
for i in s'range loop
|
522
|
ret(i*8+7 downto i*8) := std_ulogic_vector(to_unsigned(character'pos(s(i)), 8));
|
523
|
end loop;
|
524
|
return ret;
|
525
|
end;
|
526
|
|
527
|
function to_std_ulogic_vector(s: string) return ulogic_string is
|
528
|
variable ret: ulogic_string(s'range);
|
529
|
begin
|
530
|
for i in s'range loop
|
531
|
ret(i) := std_ulogic_vector(to_unsigned(character'pos(s(i)), 8));
|
532
|
end loop;
|
533
|
return ret;
|
534
|
end;
|
535
|
|
536
|
--- Not synthesizable ---
|
537
|
|
538
|
-- Find a string in a configuration items array, or returns -1 on
|
539
|
-- failure to find the string.
|
540
|
function search_configuration_tb(find_me: configuration_name; ci: configuration_items) return integer is
|
541
|
begin
|
542
|
for i in ci'range loop
|
543
|
if ci(i).name = find_me then
|
544
|
return i;
|
545
|
end if;
|
546
|
end loop;
|
547
|
return -1;
|
548
|
end;
|
549
|
|
550
|
-- VHDL provides quite a limited set of options for dealing with
|
551
|
-- operations that are not synthesizeable but would be useful for
|
552
|
-- in test benches. This method provides a crude way of reading
|
553
|
-- in configurable options. It has a very strict format.
|
554
|
--
|
555
|
-- The format is line oriented, it expects a string on a line
|
556
|
-- with a length equal to the "configuration_name" type, which
|
557
|
-- is a subtype of "string". It finds the corresponding record
|
558
|
-- in configuration_items if it exists. It then reads in an
|
559
|
-- integer from the next line and sets the record for it.
|
560
|
--
|
561
|
-- Any deviation from this format causes an error and the simulation
|
562
|
-- to halt, whilst not a good practice to do error checking with asserts
|
563
|
-- there is no better way in VHDL in this case. The only sensible
|
564
|
-- action on an error would for the configuration file to be fixed
|
565
|
-- anyway.
|
566
|
--
|
567
|
-- Comment lines and variable length strings would be nice, but
|
568
|
-- are too much of a hassle.
|
569
|
--
|
570
|
-- The configuration function only deal with part of the configuration
|
571
|
-- process, it does not deal with deserialization into structures
|
572
|
-- more useful to the user - like into individual signals.
|
573
|
--
|
574
|
procedure read_configuration_tb(file_name: string; ci: inout configuration_items) is
|
575
|
file in_file: text is in file_name;
|
576
|
variable in_line: line;
|
577
|
variable d: integer;
|
578
|
variable s: configuration_name;
|
579
|
variable index: integer;
|
580
|
begin
|
581
|
while not endfile(in_file) loop
|
582
|
|
583
|
readline(in_file, in_line);
|
584
|
read(in_line, s);
|
585
|
index := search_configuration_tb(s, ci);
|
586
|
|
587
|
assert index >= 0 report "Unknown configuration item: " & s severity failure;
|
588
|
|
589
|
readline(in_file, in_line);
|
590
|
read(in_line, d);
|
591
|
|
592
|
ci(index).value := d;
|
593
|
|
594
|
report "Config Item: '" & ci(index).name & "' = " & integer'image(ci(index).value);
|
595
|
end loop;
|
596
|
file_close(in_file);
|
597
|
end procedure;
|
598
|
|
599
|
procedure write_configuration_tb(file_name: string; ci: configuration_items) is
|
600
|
file out_file: text is out file_name;
|
601
|
variable out_line: line;
|
602
|
begin
|
603
|
for i in ci'range loop
|
604
|
write(out_line, ci(i).name);
|
605
|
writeline(out_file, out_line);
|
606
|
write(out_line, ci(i).value);
|
607
|
writeline(out_file, out_line);
|
608
|
end loop;
|
609
|
end procedure;
|
610
|
|
611
|
end;
|
612
|
|
613
|
------------------------- Utility Test Bench ----------------------------------------
|
614
|
library ieee;
|
615
|
use ieee.std_logic_1164.all;
|
616
|
use work.util.all;
|
617
|
|
618
|
entity util_tb is
|
619
|
generic(clock_frequency: positive);
|
620
|
end entity;
|
621
|
|
622
|
architecture behav of util_tb is
|
623
|
begin
|
624
|
uut_shiftReg: work.util.shift_register_tb generic map(clock_frequency => clock_frequency);
|
625
|
uut_timer_us: work.util.timer_us_tb generic map(clock_frequency => clock_frequency);
|
626
|
uut_full_add: work.util.full_adder_tb generic map(clock_frequency => clock_frequency);
|
627
|
uut_fifo: work.util.fifo_tb generic map(clock_frequency => clock_frequency);
|
628
|
uut_counter: work.util.counter_tb generic map(clock_frequency => clock_frequency);
|
629
|
uut_lfsr: work.util.lfsr_tb generic map(clock_frequency => clock_frequency);
|
630
|
uut_ucpu: work.util.ucpu_tb generic map(clock_frequency => clock_frequency);
|
631
|
uut_rdivider: work.util.restoring_divider_tb generic map(clock_frequency => clock_frequency);
|
632
|
uut_debounce: work.util.debounce_us_tb generic map(clock_frequency => clock_frequency);
|
633
|
uut_rising_edge_detector: work.util.rising_edge_detector_tb generic map(clock_frequency => clock_frequency);
|
634
|
|
635
|
stimulus_process: process
|
636
|
begin
|
637
|
assert max(5, 4) = 5 severity failure;
|
638
|
assert work.util.min(5, 4) = 4 severity failure;
|
639
|
assert n_bits(1) = 1 severity failure;
|
640
|
assert n_bits(2) = 1 severity failure;
|
641
|
assert n_bits(7) = 3 severity failure;
|
642
|
assert n_bits(8) = 3 severity failure;
|
643
|
assert n_bits(9) = 4 severity failure;
|
644
|
assert reverse("1") = "1" severity failure;
|
645
|
assert reverse("0") = "0" severity failure;
|
646
|
assert reverse("10") = "01" severity failure;
|
647
|
assert reverse("11") = "11" severity failure;
|
648
|
assert reverse("0101") = "1010" severity failure;
|
649
|
assert invert("1") = "0" severity failure;
|
650
|
assert invert("0") = "1" severity failure;
|
651
|
assert invert("0101") = "1010" severity failure;
|
652
|
assert select_bit("01000", "01") = '1' severity failure;
|
653
|
assert parity("0", true) = '0' severity failure;
|
654
|
assert parity("1", true) = '1' severity failure;
|
655
|
assert parity("11", true) = '0' severity failure;
|
656
|
assert parity("1010001", true) = '1' severity failure;
|
657
|
assert parity("0", false) = '1' severity failure;
|
658
|
assert parity("1", false) = '0' severity failure;
|
659
|
assert parity("11", false) = '1' severity failure;
|
660
|
assert parity("1010001", false) = '0' severity failure;
|
661
|
assert priority("01001", false) = 1 severity failure;
|
662
|
assert mux("1010", "0101", '0') = "1010" severity failure;
|
663
|
assert mux("1010", "0101", '1') = "0101" severity failure;
|
664
|
assert decode("00") = "0001" severity failure;
|
665
|
assert decode("01") = "0010" severity failure;
|
666
|
assert decode("10") = "0100" severity failure;
|
667
|
assert decode("11") = "1000" severity failure;
|
668
|
-- n_bits(x: std_ulogic_vector) return natural;
|
669
|
-- mux(a, b : std_ulogic_vector) return std_ulogic;
|
670
|
wait;
|
671
|
end process;
|
672
|
end architecture;
|
673
|
|
674
|
------------------------- Function Test Bench ---------------------------------------
|
675
|
|
676
|
------------------------- Test bench clock source -----------------------------------
|
677
|
|
678
|
library ieee;
|
679
|
use ieee.std_logic_1164.all;
|
680
|
use ieee.numeric_std.all;
|
681
|
use ieee.math_real.all;
|
682
|
|
683
|
entity clock_source_tb is
|
684
|
generic(clock_frequency: positive; hold_rst: positive := 1);
|
685
|
port(
|
686
|
stop: in std_ulogic := '0';
|
687
|
clk: buffer std_ulogic;
|
688
|
clk_with_jitter: out std_ulogic := '0';
|
689
|
rst: out std_ulogic := '0');
|
690
|
end entity;
|
691
|
|
692
|
architecture rtl of clock_source_tb is
|
693
|
constant clock_period: time := 1000 ms / clock_frequency;
|
694
|
signal jitter_delay: time := 0 ns;
|
695
|
signal jitter_clk: std_ulogic := '0';
|
696
|
begin
|
697
|
clk_process: process
|
698
|
variable seed1, seed2 : positive;
|
699
|
variable r : real;
|
700
|
begin
|
701
|
while stop = '0' loop
|
702
|
uniform(seed1, seed2, r);
|
703
|
jitter_delay <= r * 1 ns;
|
704
|
|
705
|
clk <= '1';
|
706
|
wait for clock_period / 2;
|
707
|
clk <= '0';
|
708
|
wait for clock_period / 2;
|
709
|
end loop;
|
710
|
wait;
|
711
|
end process;
|
712
|
|
713
|
clk_with_jitter <= transport clk after jitter_delay;
|
714
|
|
715
|
rst_process: process
|
716
|
begin
|
717
|
rst <= '1';
|
718
|
wait for clock_period * hold_rst;
|
719
|
rst <= '0';
|
720
|
wait;
|
721
|
end process;
|
722
|
|
723
|
end architecture;
|
724
|
|
725
|
------------------------- Generic Register of std_ulogic_vector ----------------------
|
726
|
|
727
|
library ieee;
|
728
|
use ieee.std_logic_1164.all;
|
729
|
use ieee.numeric_std.all;
|
730
|
|
731
|
entity reg is
|
732
|
generic(
|
733
|
N: positive);
|
734
|
port
|
735
|
(
|
736
|
clk: in std_ulogic;
|
737
|
rst: in std_ulogic;
|
738
|
we: in std_ulogic;
|
739
|
di: in std_ulogic_vector(N - 1 downto 0);
|
740
|
do: out std_ulogic_vector(N - 1 downto 0));
|
741
|
end entity;
|
742
|
|
743
|
architecture rtl of reg is
|
744
|
signal r_c, r_n: std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
745
|
begin
|
746
|
do <= r_c;
|
747
|
|
748
|
process(rst, clk)
|
749
|
begin
|
750
|
if rst = '1' then
|
751
|
r_c <= (others => '0');
|
752
|
elsif rising_edge(clk) then
|
753
|
r_c <= r_n;
|
754
|
end if;
|
755
|
end process;
|
756
|
|
757
|
process(r_c, di, we)
|
758
|
begin
|
759
|
r_n <= r_c;
|
760
|
if we = '1' then
|
761
|
r_n <= di;
|
762
|
end if;
|
763
|
end process;
|
764
|
end;
|
765
|
|
766
|
------------------------- Generic Register of std_ulogic_vector ----------------------
|
767
|
|
768
|
------------------------- Shift register --------------------------------------------
|
769
|
library ieee;
|
770
|
use ieee.std_logic_1164.all;
|
771
|
use ieee.numeric_std.all;
|
772
|
|
773
|
-- https://stackoverflow.com/questions/36342960/optional-ports-in-vhdl
|
774
|
entity shift_register is
|
775
|
generic(N: positive);
|
776
|
port
|
777
|
(
|
778
|
clk: in std_ulogic;
|
779
|
rst: in std_ulogic;
|
780
|
we: in std_ulogic;
|
781
|
di: in std_ulogic;
|
782
|
do: out std_ulogic;
|
783
|
|
784
|
load_we: in std_ulogic := '0';
|
785
|
load_i: in std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
786
|
load_o: out std_ulogic_vector(N - 1 downto 0));
|
787
|
end entity;
|
788
|
|
789
|
architecture rtl of shift_register is
|
790
|
signal r_c, r_n : std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
791
|
begin
|
792
|
do <= r_c(0);
|
793
|
load_o <= r_c;
|
794
|
|
795
|
process(rst, clk)
|
796
|
begin
|
797
|
if rst = '1' then
|
798
|
r_c <= (others => '0');
|
799
|
elsif rising_edge(clk) then
|
800
|
r_c <= r_n;
|
801
|
end if;
|
802
|
end process;
|
803
|
|
804
|
process(r_c, di, we, load_i, load_we)
|
805
|
begin
|
806
|
if load_we = '1' then
|
807
|
r_n <= load_i;
|
808
|
else
|
809
|
r_n <= "0" & r_c(N - 1 downto 1);
|
810
|
if we = '1' then
|
811
|
r_n(N-1) <= di;
|
812
|
end if;
|
813
|
end if;
|
814
|
end process;
|
815
|
end;
|
816
|
|
817
|
library ieee;
|
818
|
use ieee.std_logic_1164.all;
|
819
|
use ieee.numeric_std.all;
|
820
|
|
821
|
entity shift_register_tb is
|
822
|
generic(clock_frequency: positive);
|
823
|
end entity;
|
824
|
|
825
|
architecture behav of shift_register_tb is
|
826
|
constant N: positive := 8;
|
827
|
constant clock_period: time := 1000 ms / clock_frequency;
|
828
|
signal we: std_ulogic := '0';
|
829
|
signal di: std_ulogic := '0';
|
830
|
signal do: std_ulogic := '0';
|
831
|
|
832
|
signal clk, rst: std_ulogic := '0';
|
833
|
signal stop: std_ulogic := '0';
|
834
|
begin
|
835
|
cs: entity work.clock_source_tb
|
836
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
837
|
port map(stop => stop, clk => clk, rst => rst);
|
838
|
|
839
|
uut: entity work.shift_register
|
840
|
generic map(N => N) port map(clk => clk, rst => rst, we => we, di => di, do => do);
|
841
|
|
842
|
stimulus_process: process
|
843
|
begin
|
844
|
-- put a bit into the shift register and wait
|
845
|
-- for it to come out the other size
|
846
|
wait until rst = '0';
|
847
|
di <= '1';
|
848
|
we <= '1';
|
849
|
wait for clock_period;
|
850
|
di <= '0';
|
851
|
we <= '0';
|
852
|
for I in 0 to 7 loop
|
853
|
assert do = '0' report "bit appeared to quickly";
|
854
|
wait for clock_period;
|
855
|
end loop;
|
856
|
assert do = '1' report "bit disappeared in shift register";
|
857
|
wait for clock_period * 1;
|
858
|
assert do = '0' report "extra bit set in shift register";
|
859
|
stop <= '1';
|
860
|
wait;
|
861
|
end process;
|
862
|
end;
|
863
|
------------------------- Shift register --------------------------------------------
|
864
|
|
865
|
------------------------- Microsecond Timer -----------------------------------------
|
866
|
library ieee;
|
867
|
use ieee.std_logic_1164.all;
|
868
|
use ieee.numeric_std.all;
|
869
|
use work.util.max;
|
870
|
use work.util.n_bits;
|
871
|
|
872
|
entity timer_us is
|
873
|
generic(
|
874
|
clock_frequency: positive;
|
875
|
timer_period_us: positive := 1);
|
876
|
port(
|
877
|
clk: in std_ulogic := 'X';
|
878
|
rst: in std_ulogic := 'X';
|
879
|
co: out std_ulogic := '0');
|
880
|
end timer_us;
|
881
|
|
882
|
architecture rtl of timer_us is
|
883
|
constant cycles: natural := (clock_frequency / 1000000) * timer_period_us;
|
884
|
subtype counter is unsigned(max(1, n_bits(cycles) - 1) downto 0);
|
885
|
signal c_c, c_n: counter := (others => '0');
|
886
|
begin
|
887
|
process (clk, rst)
|
888
|
begin
|
889
|
if rst = '1' then
|
890
|
c_c <= (others => '0');
|
891
|
elsif rising_edge(clk) then
|
892
|
c_c <= c_n;
|
893
|
end if;
|
894
|
end process;
|
895
|
|
896
|
process (c_c)
|
897
|
begin
|
898
|
if c_c = (cycles - 1) then
|
899
|
c_n <= (others => '0');
|
900
|
co <= '1';
|
901
|
else
|
902
|
c_n <= c_c + 1;
|
903
|
co <= '0';
|
904
|
end if;
|
905
|
end process;
|
906
|
end;
|
907
|
|
908
|
library ieee;
|
909
|
use ieee.std_logic_1164.all;
|
910
|
use ieee.numeric_std.all;
|
911
|
|
912
|
entity timer_us_tb is
|
913
|
generic(clock_frequency: positive);
|
914
|
end;
|
915
|
|
916
|
architecture behav of timer_us_tb is
|
917
|
constant clock_period: time := 1000 ms / clock_frequency;
|
918
|
signal co: std_ulogic := 'X';
|
919
|
signal clk, rst: std_ulogic := '0';
|
920
|
signal stop: std_ulogic := '0';
|
921
|
begin
|
922
|
cs: entity work.clock_source_tb
|
923
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
924
|
port map(stop => stop, clk => clk, rst => rst);
|
925
|
|
926
|
uut: entity work.timer_us
|
927
|
generic map(clock_frequency => clock_frequency, timer_period_us => 1)
|
928
|
port map(clk => clk, rst => rst, co => co);
|
929
|
|
930
|
stimulus_process: process
|
931
|
begin
|
932
|
wait for 1 us;
|
933
|
assert co = '0' severity failure;
|
934
|
wait for clock_period;
|
935
|
assert co = '1' severity failure;
|
936
|
stop <= '1';
|
937
|
wait;
|
938
|
end process;
|
939
|
end;
|
940
|
|
941
|
------------------------- Microsecond Timer -----------------------------------------
|
942
|
|
943
|
------------------------- Rising Edge Detector --------------------------------------
|
944
|
library ieee;
|
945
|
use ieee.std_logic_1164.all;
|
946
|
|
947
|
entity rising_edge_detector is
|
948
|
port(
|
949
|
clk: in std_ulogic;
|
950
|
rst: in std_ulogic;
|
951
|
di: in std_ulogic;
|
952
|
do: out std_ulogic);
|
953
|
end;
|
954
|
|
955
|
architecture rtl of rising_edge_detector is
|
956
|
signal sin_0: std_ulogic := '0';
|
957
|
signal sin_1: std_ulogic := '0';
|
958
|
begin
|
959
|
red: process(clk, rst)
|
960
|
begin
|
961
|
if rst = '1' then
|
962
|
sin_0 <= '0';
|
963
|
sin_1 <= '0';
|
964
|
elsif rising_edge(clk) then
|
965
|
sin_0 <= di;
|
966
|
sin_1 <= sin_0;
|
967
|
end if;
|
968
|
end process;
|
969
|
do <= not sin_1 and sin_0;
|
970
|
end architecture;
|
971
|
|
972
|
library ieee;
|
973
|
use ieee.std_logic_1164.all;
|
974
|
use ieee.numeric_std.all;
|
975
|
|
976
|
entity rising_edge_detector_tb is
|
977
|
generic(clock_frequency: positive);
|
978
|
end;
|
979
|
|
980
|
architecture behav of rising_edge_detector_tb is
|
981
|
constant clock_period: time := 1000 ms / clock_frequency;
|
982
|
signal di: std_ulogic := '0';
|
983
|
signal do: std_ulogic := 'X';
|
984
|
|
985
|
signal clk, rst: std_ulogic := '0';
|
986
|
signal stop: std_ulogic := '0';
|
987
|
begin
|
988
|
cs: entity work.clock_source_tb
|
989
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
990
|
port map(stop => stop, clk => clk, rst => rst);
|
991
|
|
992
|
uut: entity work.rising_edge_detector
|
993
|
port map(clk => clk, rst => rst, di => di, do => do);
|
994
|
|
995
|
stimulus_process: process
|
996
|
begin
|
997
|
wait for clock_period * 5;
|
998
|
assert do = '0' severity failure;
|
999
|
wait for clock_period;
|
1000
|
di <= '1';
|
1001
|
wait for clock_period * 0.5;
|
1002
|
assert do = '1' severity failure;
|
1003
|
wait for clock_period * 1.5;
|
1004
|
di <= '0';
|
1005
|
assert do = '0' severity failure;
|
1006
|
wait for clock_period;
|
1007
|
assert do = '0' severity failure;
|
1008
|
|
1009
|
assert stop = '0' report "Test bench not run to completion";
|
1010
|
stop <= '1';
|
1011
|
wait;
|
1012
|
end process;
|
1013
|
end architecture;
|
1014
|
|
1015
|
library ieee;
|
1016
|
use ieee.std_logic_1164.all;
|
1017
|
|
1018
|
entity rising_edge_detectors is
|
1019
|
generic(N: positive);
|
1020
|
port(
|
1021
|
clk: in std_ulogic;
|
1022
|
rst: in std_ulogic;
|
1023
|
di: in std_ulogic_vector(N - 1 downto 0);
|
1024
|
do: out std_ulogic_vector(N - 1 downto 0));
|
1025
|
end entity;
|
1026
|
|
1027
|
architecture structural of rising_edge_detectors is
|
1028
|
begin
|
1029
|
changes: for i in N - 1 downto 0 generate
|
1030
|
d_instance: work.util.rising_edge_detector
|
1031
|
port map(clk => clk, rst => rst, di => di(i), do => do(i));
|
1032
|
end generate;
|
1033
|
end architecture;
|
1034
|
|
1035
|
------------------------- Rising Edge Detector --------------------------------------
|
1036
|
|
1037
|
------------------------- Half Adder ------------------------------------------------
|
1038
|
library ieee;
|
1039
|
use ieee.std_logic_1164.all;
|
1040
|
|
1041
|
entity half_adder is
|
1042
|
port(
|
1043
|
a: in std_ulogic;
|
1044
|
b: in std_ulogic;
|
1045
|
sum: out std_ulogic;
|
1046
|
carry: out std_ulogic);
|
1047
|
end entity;
|
1048
|
|
1049
|
architecture rtl of half_adder is
|
1050
|
begin
|
1051
|
sum <= a xor b;
|
1052
|
carry <= a and b;
|
1053
|
end architecture;
|
1054
|
|
1055
|
------------------------- Half Adder ------------------------------------------------
|
1056
|
|
1057
|
------------------------- Full Adder ------------------------------------------------
|
1058
|
|
1059
|
library ieee;
|
1060
|
use ieee.std_logic_1164.all;
|
1061
|
|
1062
|
entity full_adder is
|
1063
|
port(
|
1064
|
x: in std_ulogic;
|
1065
|
y: in std_ulogic;
|
1066
|
z: in std_ulogic;
|
1067
|
sum: out std_ulogic;
|
1068
|
carry: out std_ulogic);
|
1069
|
end entity;
|
1070
|
|
1071
|
architecture rtl of full_adder is
|
1072
|
signal carry1, carry2, sum1: std_ulogic;
|
1073
|
begin
|
1074
|
ha1: entity work.half_adder port map(a => x, b => y, sum => sum1, carry => carry1);
|
1075
|
ha2: entity work.half_adder port map(a => sum1, b => z, sum => sum, carry => carry2);
|
1076
|
carry <= carry1 or carry2;
|
1077
|
end architecture;
|
1078
|
|
1079
|
library ieee;
|
1080
|
use ieee.std_logic_1164.all;
|
1081
|
|
1082
|
entity full_adder_tb is
|
1083
|
generic(clock_frequency: positive);
|
1084
|
end entity;
|
1085
|
|
1086
|
architecture behav of full_adder_tb is
|
1087
|
constant clock_period: time := 1000 ms / clock_frequency;
|
1088
|
signal x, y, z: std_ulogic := '0';
|
1089
|
signal sum, carry: std_ulogic := '0';
|
1090
|
|
1091
|
type stimulus_data is array (0 to 7) of std_ulogic_vector(2 downto 0);
|
1092
|
type stimulus_result is array (stimulus_data'range) of std_ulogic_vector(0 to 1);
|
1093
|
|
1094
|
constant data: stimulus_data := (
|
1095
|
0 => "000", 1 => "001",
|
1096
|
2 => "010", 3 => "011",
|
1097
|
4 => "100", 5 => "101",
|
1098
|
6 => "110", 7 => "111");
|
1099
|
|
1100
|
constant result: stimulus_result := (
|
1101
|
0 => "00", 1 => "10",
|
1102
|
2 => "10", 3 => "01",
|
1103
|
4 => "10", 5 => "01",
|
1104
|
6 => "01", 7 => "11");
|
1105
|
|
1106
|
signal clk, rst: std_ulogic := '0';
|
1107
|
signal stop: std_ulogic := '0';
|
1108
|
begin
|
1109
|
cs: entity work.clock_source_tb
|
1110
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
1111
|
port map(stop => stop, clk => clk, rst => rst);
|
1112
|
|
1113
|
uut: entity work.full_adder port map(x => x, y => y, z => z, sum => sum, carry => carry);
|
1114
|
|
1115
|
stimulus_process: process
|
1116
|
begin
|
1117
|
wait for clock_period;
|
1118
|
for i in data'range loop
|
1119
|
x <= data(i)(0);
|
1120
|
y <= data(i)(1);
|
1121
|
z <= data(i)(2);
|
1122
|
wait for clock_period;
|
1123
|
assert sum = result(i)(0) and carry = result(i)(1)
|
1124
|
report
|
1125
|
"For: " & std_ulogic'image(x) & std_ulogic'image(y) & std_ulogic'image(z) &
|
1126
|
" Got: " & std_ulogic'image(sum) & std_ulogic'image(carry) &
|
1127
|
" Expected: " & std_ulogic'image(result(i)(0)) & std_ulogic'image(result(i)(1))
|
1128
|
severity failure;
|
1129
|
wait for clock_period;
|
1130
|
end loop;
|
1131
|
|
1132
|
stop <= '1';
|
1133
|
wait;
|
1134
|
end process;
|
1135
|
end architecture;
|
1136
|
|
1137
|
------------------------- Full Adder ------------------------------------------------
|
1138
|
|
1139
|
------------------------- FIFO ------------------------------------------------------
|
1140
|
|
1141
|
-- Originally from http://www.deathbylogic.com/2013/07/vhdl-standard-fifo/
|
1142
|
-- @copyright Public Domain
|
1143
|
-- @todo Add more comments about the FIFOs origin, add assertions test
|
1144
|
-- synthesis, make this more generic (with empty FIFO and FIFO count signals)
|
1145
|
--
|
1146
|
-- The code can be used freely and appears to be public domain, comment
|
1147
|
-- from author is: "You can use any code posted here freely, there is no copyright."
|
1148
|
--
|
1149
|
-- @note The FIFO has been modified from the original to bring it in line with
|
1150
|
-- this projects coding standards.
|
1151
|
|
1152
|
library ieee;
|
1153
|
use ieee.std_logic_1164.all;
|
1154
|
use ieee.numeric_std.all;
|
1155
|
|
1156
|
entity fifo is
|
1157
|
generic(
|
1158
|
data_width: positive;
|
1159
|
fifo_depth: positive);
|
1160
|
port(
|
1161
|
clk: in std_ulogic;
|
1162
|
rst: in std_ulogic;
|
1163
|
we: in std_ulogic;
|
1164
|
di: in std_ulogic_vector (data_width - 1 downto 0);
|
1165
|
re: in std_ulogic;
|
1166
|
do: out std_ulogic_vector (data_width - 1 downto 0);
|
1167
|
empty: out std_ulogic := '1';
|
1168
|
full: out std_ulogic := '0');
|
1169
|
end fifo;
|
1170
|
|
1171
|
architecture behavioral of fifo is
|
1172
|
begin
|
1173
|
|
1174
|
-- memory pointer process
|
1175
|
fifo_proc: process (clk, rst)
|
1176
|
type fifo_memory is array (0 to fifo_depth - 1) of std_ulogic_vector (data_width - 1 downto 0);
|
1177
|
variable memory: fifo_memory;
|
1178
|
|
1179
|
variable head: natural range 0 to fifo_depth - 1;
|
1180
|
variable tail: natural range 0 to fifo_depth - 1;
|
1181
|
|
1182
|
variable looped: boolean;
|
1183
|
begin
|
1184
|
if rst = '1' then
|
1185
|
head := 0;
|
1186
|
tail := 0;
|
1187
|
|
1188
|
looped := false;
|
1189
|
|
1190
|
full <= '0';
|
1191
|
empty <= '1';
|
1192
|
do <= (others => '0');
|
1193
|
elsif rising_edge(clk) then
|
1194
|
do <= (others => '0');
|
1195
|
if re = '1' then
|
1196
|
if looped = true or head /= tail then
|
1197
|
-- update data output
|
1198
|
do <= memory(tail);
|
1199
|
|
1200
|
-- update tail pointer as needed
|
1201
|
if tail = fifo_depth - 1 then
|
1202
|
tail := 0;
|
1203
|
looped := false;
|
1204
|
else
|
1205
|
tail := tail + 1;
|
1206
|
end if;
|
1207
|
end if;
|
1208
|
end if;
|
1209
|
|
1210
|
if we = '1' then
|
1211
|
if looped = false or head /= tail then
|
1212
|
-- write data to memory
|
1213
|
memory(head) := di;
|
1214
|
|
1215
|
-- increment head pointer as needed
|
1216
|
if head = fifo_depth - 1 then
|
1217
|
head := 0;
|
1218
|
|
1219
|
looped := true;
|
1220
|
else
|
1221
|
head := head + 1;
|
1222
|
end if;
|
1223
|
end if;
|
1224
|
end if;
|
1225
|
|
1226
|
-- update empty and full flags
|
1227
|
if head = tail then
|
1228
|
if looped then
|
1229
|
full <= '1';
|
1230
|
else
|
1231
|
empty <= '1';
|
1232
|
end if;
|
1233
|
else
|
1234
|
empty <= '0';
|
1235
|
full <= '0';
|
1236
|
end if;
|
1237
|
end if;
|
1238
|
end process;
|
1239
|
end architecture;
|
1240
|
|
1241
|
library ieee;
|
1242
|
use ieee.std_logic_1164.all;
|
1243
|
use ieee.numeric_std.all;
|
1244
|
|
1245
|
entity fifo_tb is
|
1246
|
generic(clock_frequency: positive);
|
1247
|
end entity;
|
1248
|
|
1249
|
architecture behavior of fifo_tb is
|
1250
|
constant clock_period: time := 1000 ms / clock_frequency;
|
1251
|
constant data_width: positive := 8;
|
1252
|
constant fifo_depth: positive := 16;
|
1253
|
|
1254
|
signal di: std_ulogic_vector(data_width - 1 downto 0) := (others => '0');
|
1255
|
signal re: std_ulogic := '0';
|
1256
|
signal we: std_ulogic := '0';
|
1257
|
|
1258
|
signal do: std_ulogic_vector(data_width - 1 downto 0) := (others => '0');
|
1259
|
signal empty: std_ulogic := '0';
|
1260
|
signal full: std_ulogic := '0';
|
1261
|
|
1262
|
signal clk, rst: std_ulogic := '0';
|
1263
|
signal stop_w: std_ulogic := '0';
|
1264
|
signal stop_r: std_ulogic := '0';
|
1265
|
signal stop: std_ulogic := '0';
|
1266
|
begin
|
1267
|
cs: entity work.clock_source_tb
|
1268
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
1269
|
port map(stop => stop, clk => clk, rst => rst);
|
1270
|
|
1271
|
stop <= '1' when stop_w = '1' and stop_r = '1' else '0';
|
1272
|
|
1273
|
uut: entity work.fifo
|
1274
|
generic map(data_width => data_width, fifo_depth => fifo_depth)
|
1275
|
port map (
|
1276
|
clk => clk,
|
1277
|
rst => rst,
|
1278
|
di => di,
|
1279
|
we => we,
|
1280
|
re => re,
|
1281
|
do => do,
|
1282
|
full => full,
|
1283
|
empty => empty);
|
1284
|
|
1285
|
write_process: process
|
1286
|
variable counter: unsigned (data_width - 1 downto 0) := (others => '0');
|
1287
|
begin
|
1288
|
wait for clock_period * 20;
|
1289
|
|
1290
|
for i in 1 to 32 loop
|
1291
|
counter := counter + 1;
|
1292
|
di <= std_ulogic_vector(counter);
|
1293
|
wait for clock_period * 1;
|
1294
|
we <= '1';
|
1295
|
wait for clock_period * 1;
|
1296
|
we <= '0';
|
1297
|
end loop;
|
1298
|
|
1299
|
wait for clock_period * 20;
|
1300
|
|
1301
|
for i in 1 to 32 loop
|
1302
|
counter := counter + 1;
|
1303
|
di <= std_ulogic_vector(counter);
|
1304
|
wait for clock_period * 1;
|
1305
|
we <= '1';
|
1306
|
wait for clock_period * 1;
|
1307
|
we <= '0';
|
1308
|
end loop;
|
1309
|
|
1310
|
stop_w <= '1';
|
1311
|
wait;
|
1312
|
end process;
|
1313
|
|
1314
|
read_process: process
|
1315
|
begin
|
1316
|
wait for clock_period * 60;
|
1317
|
re <= '1';
|
1318
|
wait for clock_period * 60;
|
1319
|
re <= '0';
|
1320
|
wait for clock_period * 256 * 2;
|
1321
|
re <= '1';
|
1322
|
|
1323
|
stop_r <= '1';
|
1324
|
wait;
|
1325
|
end process;
|
1326
|
end architecture;
|
1327
|
|
1328
|
------------------------- FIFO ------------------------------------------------------
|
1329
|
|
1330
|
------------------------- Free running counter --------------------------------------
|
1331
|
|
1332
|
library ieee;
|
1333
|
use ieee.std_logic_1164.all;
|
1334
|
use ieee.numeric_std.all;
|
1335
|
|
1336
|
entity counter is
|
1337
|
generic(
|
1338
|
N: positive);
|
1339
|
port(
|
1340
|
clk: in std_ulogic;
|
1341
|
rst: in std_ulogic;
|
1342
|
ce: in std_ulogic;
|
1343
|
cr: in std_ulogic;
|
1344
|
dout: out std_ulogic_vector(N - 1 downto 0);
|
1345
|
|
1346
|
load_we: in std_ulogic := '0';
|
1347
|
load_i: in std_ulogic_vector(N - 1 downto 0) := (others => '0'));
|
1348
|
end entity;
|
1349
|
|
1350
|
architecture rtl of counter is
|
1351
|
signal c_c, c_n: unsigned(N - 1 downto 0) := (others => '0');
|
1352
|
begin
|
1353
|
dout <= std_ulogic_vector(c_c);
|
1354
|
|
1355
|
process(clk, rst)
|
1356
|
begin
|
1357
|
if rst = '1' then
|
1358
|
c_c <= (others => '0');
|
1359
|
elsif rising_edge(clk) then
|
1360
|
c_c <= c_n;
|
1361
|
end if;
|
1362
|
end process;
|
1363
|
|
1364
|
process(c_c, cr, ce, load_we, load_i)
|
1365
|
begin
|
1366
|
c_n <= c_c;
|
1367
|
if load_we = '1' then
|
1368
|
c_n <= unsigned(load_i);
|
1369
|
else
|
1370
|
if cr = '1' then
|
1371
|
c_n <= (others => '0');
|
1372
|
elsif ce = '1' then
|
1373
|
c_n <= c_c + 1;
|
1374
|
end if;
|
1375
|
end if;
|
1376
|
end process;
|
1377
|
|
1378
|
end architecture;
|
1379
|
|
1380
|
library ieee;
|
1381
|
use ieee.std_logic_1164.all;
|
1382
|
use ieee.numeric_std.all;
|
1383
|
|
1384
|
entity counter_tb is
|
1385
|
generic(clock_frequency: positive);
|
1386
|
end entity;
|
1387
|
|
1388
|
architecture behavior of counter_tb is
|
1389
|
constant clock_period: time := 1000 ms / clock_frequency;
|
1390
|
constant length: positive := 2;
|
1391
|
|
1392
|
-- inputs
|
1393
|
signal ce: std_ulogic := '0';
|
1394
|
signal cr: std_ulogic := '0';
|
1395
|
|
1396
|
-- outputs
|
1397
|
signal dout: std_ulogic_vector(length - 1 downto 0);
|
1398
|
|
1399
|
-- test data
|
1400
|
type stimulus_data is array (0 to 16) of std_ulogic_vector(1 downto 0);
|
1401
|
type stimulus_result is array (stimulus_data'range) of std_ulogic_vector(0 to 1);
|
1402
|
|
1403
|
constant data: stimulus_data := (
|
1404
|
0 => "00", 1 => "00",
|
1405
|
2 => "01", 3 => "01",
|
1406
|
4 => "00", 5 => "00",
|
1407
|
6 => "10", 7 => "00",
|
1408
|
8 => "01", 9 => "01",
|
1409
|
10 => "11", 11 => "00",
|
1410
|
12 => "01", 13 => "01",
|
1411
|
14 => "01", 15 => "01",
|
1412
|
16 => "01");
|
1413
|
|
1414
|
constant result: stimulus_result := (
|
1415
|
0 => "00", 1 => "00",
|
1416
|
2 => "00", 3 => "01",
|
1417
|
4 => "10", 5 => "10",
|
1418
|
6 => "10", 7 => "00",
|
1419
|
8 => "00", 9 => "01",
|
1420
|
10 => "10", 11 => "00",
|
1421
|
12 => "00", 13 => "01",
|
1422
|
14 => "10", 15 => "11",
|
1423
|
16 => "00");
|
1424
|
|
1425
|
signal clk, rst: std_ulogic := '0';
|
1426
|
signal stop: std_ulogic := '0';
|
1427
|
begin
|
1428
|
cs: entity work.clock_source_tb
|
1429
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
1430
|
port map(stop => stop, clk => clk, rst => rst);
|
1431
|
|
1432
|
uut: entity work.counter
|
1433
|
generic map(
|
1434
|
N => length)
|
1435
|
port map(
|
1436
|
clk => clk,
|
1437
|
rst => rst,
|
1438
|
ce => ce,
|
1439
|
cr => cr,
|
1440
|
dout => dout);
|
1441
|
|
1442
|
stimulus_process: process
|
1443
|
begin
|
1444
|
wait for clock_period;
|
1445
|
for i in data'range loop
|
1446
|
ce <= data(i)(0);
|
1447
|
cr <= data(i)(1);
|
1448
|
wait for clock_period;
|
1449
|
assert dout = result(i)
|
1450
|
report
|
1451
|
"For: ce(" & std_ulogic'image(ce) & ") cr(" & std_ulogic'image(cr) & ") " &
|
1452
|
" Got: " & integer'image(to_integer(unsigned(dout))) &
|
1453
|
" Expected: " & integer'image(to_integer(unsigned(result(i))))
|
1454
|
severity failure;
|
1455
|
end loop;
|
1456
|
stop <= '1';
|
1457
|
wait;
|
1458
|
end process;
|
1459
|
|
1460
|
end architecture;
|
1461
|
|
1462
|
------------------------- Free running counter --------------------------------------
|
1463
|
|
1464
|
------------------------- Linear Feedback Shift Register ----------------------------
|
1465
|
-- For good sources on LFSR see
|
1466
|
-- * https://sites.ualberta.ca/~delliott/ee552/studentAppNotes/1999f/Drivers_Ed/lfsr.html
|
1467
|
-- * https://en.wikipedia.org/wiki/Linear-feedback_shift_register
|
1468
|
--
|
1469
|
-- Some optimal taps
|
1470
|
--
|
1471
|
-- Taps start at the left most std_ulogic element of tap at '0' and proceed to
|
1472
|
-- the highest bit. To instantiate an instance of the LFSR set tap to a
|
1473
|
-- standard logic vector one less the size of LFSR that you want. An 8-bit
|
1474
|
-- LFSR can be made by setting it 'tap' to "0111001". The LFSR will need to
|
1475
|
-- be loaded with a seed value, set 'do' to that value and assert 'we'. The
|
1476
|
-- LFSR will only run when 'ce' is asserted, otherwise it will preserve the
|
1477
|
-- current value.
|
1478
|
--
|
1479
|
-- Number of bits Taps Cycle Time
|
1480
|
-- 8 1,2,3,7 255
|
1481
|
-- 16 1,2,4,15 65535
|
1482
|
-- 32 1,5,6,31 4294967295
|
1483
|
--
|
1484
|
-- This component could be improved a lot, and it could also be used to
|
1485
|
-- calculate CRCs, which are basically the same computation. Its interface
|
1486
|
-- is not the best either, being a free running counter.
|
1487
|
--
|
1488
|
|
1489
|
library ieee;
|
1490
|
use ieee.std_logic_1164.all;
|
1491
|
use ieee.numeric_std.all;
|
1492
|
|
1493
|
entity lfsr is
|
1494
|
generic(constant tap: std_ulogic_vector);
|
1495
|
port
|
1496
|
(
|
1497
|
clk: in std_ulogic;
|
1498
|
rst: in std_ulogic;
|
1499
|
we: in std_ulogic;
|
1500
|
ce: in std_ulogic := '1';
|
1501
|
di: in std_ulogic_vector(tap'high + 1 downto tap'low);
|
1502
|
do: out std_ulogic_vector(tap'high + 1 downto tap'low));
|
1503
|
end entity;
|
1504
|
|
1505
|
architecture rtl of lfsr is
|
1506
|
signal r_c, r_n : std_ulogic_vector(di'range) := (others => '0');
|
1507
|
begin
|
1508
|
do <= r_c;
|
1509
|
|
1510
|
process(rst, clk)
|
1511
|
begin
|
1512
|
if rst = '1' then
|
1513
|
r_c <= (others => '0');
|
1514
|
elsif rising_edge(clk) then
|
1515
|
r_c <= r_n;
|
1516
|
end if;
|
1517
|
end process;
|
1518
|
|
1519
|
process(r_c, di, we, ce)
|
1520
|
begin
|
1521
|
if we = '1' then
|
1522
|
r_n <= di;
|
1523
|
elsif ce = '1' then
|
1524
|
|
1525
|
r_n(r_n'high) <= r_c(r_c'low);
|
1526
|
|
1527
|
for i in tap'high downto tap'low loop
|
1528
|
if tap(i) = '1' then
|
1529
|
r_n(i) <= r_c(r_c'low) xor r_c(i+1);
|
1530
|
else
|
1531
|
r_n(i) <= r_c(i+1);
|
1532
|
end if;
|
1533
|
end loop;
|
1534
|
else
|
1535
|
r_n <= r_c;
|
1536
|
end if;
|
1537
|
end process;
|
1538
|
end architecture;
|
1539
|
|
1540
|
library ieee;
|
1541
|
use ieee.std_logic_1164.all;
|
1542
|
use ieee.numeric_std.all;
|
1543
|
|
1544
|
entity lfsr_tb is
|
1545
|
generic(clock_frequency: positive);
|
1546
|
end entity;
|
1547
|
|
1548
|
architecture behavior of lfsr_tb is
|
1549
|
constant clock_period: time := 1000 ms / clock_frequency;
|
1550
|
signal we: std_ulogic := '0';
|
1551
|
signal do, di: std_ulogic_vector(7 downto 0) := (others => '0');
|
1552
|
|
1553
|
signal clk, rst: std_ulogic := '0';
|
1554
|
signal stop: std_ulogic := '0';
|
1555
|
begin
|
1556
|
cs: entity work.clock_source_tb
|
1557
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
1558
|
port map(stop => stop, clk => clk, rst => rst);
|
1559
|
|
1560
|
uut: entity work.lfsr
|
1561
|
generic map(tap => "0111001")
|
1562
|
port map(clk => clk,
|
1563
|
rst => rst,
|
1564
|
we => we,
|
1565
|
di => di,
|
1566
|
do => do);
|
1567
|
|
1568
|
stimulus_process: process
|
1569
|
begin
|
1570
|
wait for clock_period * 2;
|
1571
|
we <= '1';
|
1572
|
di <= "00000001";
|
1573
|
wait for clock_period;
|
1574
|
we <= '0';
|
1575
|
stop <= '1';
|
1576
|
wait;
|
1577
|
end process;
|
1578
|
|
1579
|
end architecture;
|
1580
|
|
1581
|
------------------------- Linear Feedback Shift Register ----------------------------
|
1582
|
|
1583
|
------------------------- I/O Pin Controller ----------------------------------------
|
1584
|
-- @todo Test this in hardware
|
1585
|
--
|
1586
|
-- This is a simple I/O pin control module, there is a control register which
|
1587
|
-- sets whether the pins are to be read in (control = '0') or set to the value written to
|
1588
|
-- "din" (control = '1').
|
1589
|
|
1590
|
library ieee;
|
1591
|
use ieee.std_logic_1164.all;
|
1592
|
use ieee.numeric_std.all;
|
1593
|
|
1594
|
entity io_pins is
|
1595
|
generic(
|
1596
|
N: positive);
|
1597
|
port
|
1598
|
(
|
1599
|
clk: in std_ulogic;
|
1600
|
rst: in std_ulogic;
|
1601
|
control: in std_ulogic_vector(N - 1 downto 0);
|
1602
|
control_we: in std_ulogic;
|
1603
|
din: in std_ulogic_vector(N - 1 downto 0);
|
1604
|
din_we: in std_ulogic;
|
1605
|
dout: out std_ulogic_vector(N - 1 downto 0);
|
1606
|
pins: inout std_logic_vector(N - 1 downto 0));
|
1607
|
end entity;
|
1608
|
|
1609
|
architecture rtl of io_pins is
|
1610
|
signal control_o: std_ulogic_vector(control'range) := (others => '0');
|
1611
|
signal din_o: std_ulogic_vector(din'range) := (others => '0');
|
1612
|
begin
|
1613
|
|
1614
|
control_r: entity work.reg generic map(N => N) port map(clk => clk, rst => rst, di => control, we => control_we, do => control_o);
|
1615
|
din_r: entity work.reg generic map(N => N) port map(clk => clk, rst => rst, di => din, we => din_we, do => din_o);
|
1616
|
|
1617
|
pins_i: for i in control_o'range generate
|
1618
|
dout(i) <= pins(i) when control_o(i) = '0' else '0';
|
1619
|
pins(i) <= din_o(i) when control_o(i) = '1' else 'Z';
|
1620
|
end generate;
|
1621
|
|
1622
|
end architecture;
|
1623
|
|
1624
|
library ieee;
|
1625
|
use ieee.std_logic_1164.all;
|
1626
|
use ieee.numeric_std.all;
|
1627
|
|
1628
|
entity io_pins_tb is
|
1629
|
generic(clock_frequency: positive);
|
1630
|
end entity;
|
1631
|
|
1632
|
architecture behavior of io_pins_tb is
|
1633
|
constant clock_period: time := 1000 ms / clock_frequency;
|
1634
|
constant N: positive := 8;
|
1635
|
|
1636
|
signal control: std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
1637
|
signal din: std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
1638
|
signal dout: std_ulogic_vector(N - 1 downto 0) := (others => '0');
|
1639
|
signal pins: std_logic_vector(N - 1 downto 0) := (others => 'L'); -- !
|
1640
|
|
1641
|
signal control_we: std_ulogic := '0';
|
1642
|
signal din_we: std_ulogic := '0';
|
1643
|
|
1644
|
|
1645
|
signal clk, rst: std_ulogic := '0';
|
1646
|
signal stop: std_ulogic := '0';
|
1647
|
begin
|
1648
|
cs: entity work.clock_source_tb
|
1649
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
1650
|
port map(stop => stop, clk => clk, rst => rst);
|
1651
|
|
1652
|
uut: entity work.io_pins
|
1653
|
generic map(N => N)
|
1654
|
port map(
|
1655
|
clk => clk,
|
1656
|
rst => rst,
|
1657
|
control => control,
|
1658
|
control_we => control_we,
|
1659
|
din => din,
|
1660
|
din_we => din_we,
|
1661
|
dout => dout,
|
1662
|
pins => pins);
|
1663
|
|
1664
|
stimulus_process: process
|
1665
|
begin
|
1666
|
wait for clock_period * 2;
|
1667
|
control <= x"0f"; -- write lower pins
|
1668
|
control_we <= '1';
|
1669
|
|
1670
|
wait for clock_period;
|
1671
|
din <= x"AA";
|
1672
|
din_we <= '1';
|
1673
|
|
1674
|
wait for clock_period * 2;
|
1675
|
pins <= (others => 'H'); -- !
|
1676
|
wait for clock_period * 2;
|
1677
|
stop <= '1';
|
1678
|
wait;
|
1679
|
end process;
|
1680
|
|
1681
|
end architecture;
|
1682
|
|
1683
|
------------------------- I/O Pin Controller ----------------------------------------
|
1684
|
|
1685
|
------------------------- Single and Dual Port Block RAM ----------------------------
|
1686
|
--|
|
1687
|
--| @warning The function initialize_ram has to be present in each architecture
|
1688
|
--| block ram that uses it (as far as I am aware) which means they could fall
|
1689
|
--| out of sync. This could be remedied with VHDL-2008.
|
1690
|
---------------------------------------------------------------------------------
|
1691
|
|
1692
|
--- Dual Port Model ---
|
1693
|
|
1694
|
library ieee;
|
1695
|
use ieee.std_logic_1164.all;
|
1696
|
use ieee.numeric_std.all;
|
1697
|
use std.textio.all;
|
1698
|
use work.util.all;
|
1699
|
|
1700
|
entity dual_port_block_ram is
|
1701
|
|
1702
|
-- The dual port Block RAM module can be initialized from a file,
|
1703
|
-- or initialized to all zeros. The model can be synthesized (with
|
1704
|
-- Xilinx's ISE) into BRAM.
|
1705
|
--
|
1706
|
-- Valid file_type options include:
|
1707
|
--
|
1708
|
-- FILE_BINARY - A binary file (ASCII '0' and '1', one number per line)
|
1709
|
-- FILE_HEX - A Hex file (ASCII '0-9' 'a-f', 'A-F', one number per line)
|
1710
|
-- FILE_NONE - RAM contents will be defaulted to all zeros, no file will
|
1711
|
-- be read from
|
1712
|
--
|
1713
|
-- @todo Read in actual binary data files, see: https://stackoverflow.com/questions/14173652
|
1714
|
--
|
1715
|
-- The data length must be divisible by 4 if the "hex" option is
|
1716
|
-- given.
|
1717
|
--
|
1718
|
-- These default values for addr_length and data_length have been
|
1719
|
-- chosen so as to fill the block RAM available on a Spartan 6.
|
1720
|
--
|
1721
|
generic(addr_length: positive := 12;
|
1722
|
data_length: positive := 16;
|
1723
|
file_name: string := "memory.bin";
|
1724
|
file_type: file_format := FILE_BINARY);
|
1725
|
port(
|
1726
|
--| Port A of dual port RAM
|
1727
|
a_clk: in std_ulogic;
|
1728
|
a_dwe: in std_ulogic;
|
1729
|
a_dre: in std_ulogic;
|
1730
|
a_addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
1731
|
a_din: in std_ulogic_vector(data_length - 1 downto 0);
|
1732
|
a_dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0');
|
1733
|
--| Port B of dual port RAM
|
1734
|
b_clk: in std_ulogic;
|
1735
|
b_dwe: in std_ulogic;
|
1736
|
b_dre: in std_ulogic;
|
1737
|
b_addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
1738
|
b_din: in std_ulogic_vector(data_length - 1 downto 0);
|
1739
|
b_dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
|
1740
|
end entity;
|
1741
|
|
1742
|
architecture behav of dual_port_block_ram is
|
1743
|
constant ram_size: positive := 2 ** addr_length;
|
1744
|
|
1745
|
type ram_type is array ((ram_size - 1 ) downto 0) of std_ulogic_vector(data_length - 1 downto 0);
|
1746
|
|
1747
|
impure function initialize_ram(file_name: in string; file_type: in file_format) return ram_type is
|
1748
|
variable ram_data: ram_type;
|
1749
|
file in_file: text is in file_name;
|
1750
|
variable input_line: line;
|
1751
|
variable tmp: bit_vector(data_length - 1 downto 0);
|
1752
|
variable c: character;
|
1753
|
variable slv: std_ulogic_vector(data_length - 1 downto 0);
|
1754
|
begin
|
1755
|
for i in 0 to ram_size - 1 loop
|
1756
|
if file_type = FILE_NONE then
|
1757
|
ram_data(i):=(others => '0');
|
1758
|
elsif not endfile(in_file) then
|
1759
|
readline(in_file,input_line);
|
1760
|
if file_type = FILE_BINARY then
|
1761
|
read(input_line, tmp);
|
1762
|
ram_data(i) := std_ulogic_vector(to_stdlogicvector(tmp));
|
1763
|
elsif file_type = FILE_HEX then
|
1764
|
assert (data_length mod 4) = 0 report "(data_length%4)!=0" severity failure;
|
1765
|
for j in 1 to (data_length/4) loop
|
1766
|
c:= input_line((data_length/4) - j + 1);
|
1767
|
slv((j*4)-1 downto (j*4)-4) := hex_char_to_std_ulogic_vector(c);
|
1768
|
end loop;
|
1769
|
ram_data(i) := slv;
|
1770
|
else
|
1771
|
report "Incorrect file type given: " & file_format'image(file_type) severity failure;
|
1772
|
end if;
|
1773
|
else
|
1774
|
ram_data(i) := (others => '0');
|
1775
|
end if;
|
1776
|
end loop;
|
1777
|
file_close(in_file);
|
1778
|
return ram_data;
|
1779
|
end function;
|
1780
|
|
1781
|
shared variable ram: ram_type := initialize_ram(file_name, file_type);
|
1782
|
|
1783
|
begin
|
1784
|
a_ram: process(a_clk)
|
1785
|
begin
|
1786
|
if rising_edge(a_clk) then
|
1787
|
if a_dwe = '1' then
|
1788
|
ram(to_integer(unsigned(a_addr))) := a_din;
|
1789
|
end if;
|
1790
|
if a_dre = '1' then
|
1791
|
a_dout <= ram(to_integer(unsigned(a_addr)));
|
1792
|
else
|
1793
|
a_dout <= (others => '0');
|
1794
|
end if;
|
1795
|
end if;
|
1796
|
end process;
|
1797
|
|
1798
|
b_ram: process(b_clk)
|
1799
|
begin
|
1800
|
if rising_edge(b_clk) then
|
1801
|
if b_dwe = '1' then
|
1802
|
ram(to_integer(unsigned(b_addr))) := b_din;
|
1803
|
end if;
|
1804
|
if b_dre = '1' then
|
1805
|
b_dout <= ram(to_integer(unsigned(b_addr)));
|
1806
|
else
|
1807
|
b_dout <= (others => '0');
|
1808
|
end if;
|
1809
|
end if;
|
1810
|
end process;
|
1811
|
end architecture;
|
1812
|
|
1813
|
--- Single Port Model ---
|
1814
|
|
1815
|
library ieee;
|
1816
|
use ieee.std_logic_1164.all;
|
1817
|
use ieee.numeric_std.all;
|
1818
|
use std.textio.all;
|
1819
|
use work.util.all;
|
1820
|
|
1821
|
entity single_port_block_ram is
|
1822
|
generic(addr_length: positive := 12;
|
1823
|
data_length: positive := 16;
|
1824
|
file_name: string := "memory.bin";
|
1825
|
file_type: file_format := FILE_BINARY);
|
1826
|
port(
|
1827
|
clk: in std_ulogic;
|
1828
|
dwe: in std_ulogic;
|
1829
|
dre: in std_ulogic;
|
1830
|
addr: in std_ulogic_vector(addr_length - 1 downto 0);
|
1831
|
din: in std_ulogic_vector(data_length - 1 downto 0);
|
1832
|
dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
|
1833
|
end entity;
|
1834
|
|
1835
|
architecture behav of single_port_block_ram is
|
1836
|
constant ram_size: positive := 2 ** addr_length;
|
1837
|
|
1838
|
type ram_type is array ((ram_size - 1 ) downto 0) of std_ulogic_vector(data_length - 1 downto 0);
|
1839
|
|
1840
|
impure function initialize_ram(file_name: in string; file_type: in file_format) return ram_type is
|
1841
|
variable ram_data: ram_type;
|
1842
|
file in_file: text is in file_name;
|
1843
|
variable input_line: line;
|
1844
|
variable tmp: bit_vector(data_length - 1 downto 0);
|
1845
|
variable c: character;
|
1846
|
variable slv: std_ulogic_vector(data_length - 1 downto 0);
|
1847
|
begin
|
1848
|
for i in 0 to ram_size - 1 loop
|
1849
|
if file_type = FILE_NONE then
|
1850
|
ram_data(i):=(others => '0');
|
1851
|
elsif not endfile(in_file) then
|
1852
|
readline(in_file,input_line);
|
1853
|
if file_type = FILE_BINARY then
|
1854
|
read(input_line, tmp);
|
1855
|
ram_data(i) := std_ulogic_vector(to_stdlogicvector(tmp));
|
1856
|
elsif file_type = FILE_HEX then -- hexadecimal
|
1857
|
assert (data_length mod 4) = 0 report "(data_length%4)!=0" severity failure;
|
1858
|
for j in 1 to (data_length/4) loop
|
1859
|
c:= input_line((data_length/4) - j + 1);
|
1860
|
slv((j*4)-1 downto (j*4)-4) := hex_char_to_std_ulogic_vector(c);
|
1861
|
end loop;
|
1862
|
ram_data(i) := slv;
|
1863
|
else
|
1864
|
report "Incorrect file type given: " & file_format'image(file_type) severity failure;
|
1865
|
end if;
|
1866
|
else
|
1867
|
ram_data(i) := (others => '0');
|
1868
|
end if;
|
1869
|
end loop;
|
1870
|
file_close(in_file);
|
1871
|
return ram_data;
|
1872
|
end function;
|
1873
|
|
1874
|
shared variable ram: ram_type := initialize_ram(file_name, file_type);
|
1875
|
begin
|
1876
|
block_ram: process(clk)
|
1877
|
begin
|
1878
|
if rising_edge(clk) then
|
1879
|
if dwe = '1' then
|
1880
|
ram(to_integer(unsigned(addr))) := din;
|
1881
|
end if;
|
1882
|
|
1883
|
if dre = '1' then
|
1884
|
dout <= ram(to_integer(unsigned(addr)));
|
1885
|
else
|
1886
|
dout <= (others => '0');
|
1887
|
end if;
|
1888
|
end if;
|
1889
|
end process;
|
1890
|
end architecture;
|
1891
|
|
1892
|
------------------------- Single and Dual Port Block RAM ----------------------------
|
1893
|
|
1894
|
------------------------- Data Source -----------------------------------------------
|
1895
|
--|
|
1896
|
--| This module spits out a bunch of data
|
1897
|
--|
|
1898
|
--| @todo Create a single module that can be used to capture and replay data at
|
1899
|
--| a configurable rate. This could be used as a logger or as a waveform
|
1900
|
--| generator. Depending on the generics used this should synthesize to either
|
1901
|
--| logger, or a data source, or both. A pre-divider could also be supplied as
|
1902
|
--| generic options, to lower the clock rate.
|
1903
|
--|
|
1904
|
|
1905
|
library ieee,work;
|
1906
|
use ieee.std_logic_1164.all;
|
1907
|
use ieee.numeric_std.all;
|
1908
|
use work.util.single_port_block_ram;
|
1909
|
use work.util.counter;
|
1910
|
use work.util.all;
|
1911
|
|
1912
|
entity data_source is
|
1913
|
generic(addr_length: positive := 12;
|
1914
|
data_length: positive := 16;
|
1915
|
file_name: string := "memory.bin";
|
1916
|
file_type: file_format := FILE_BINARY);
|
1917
|
port(
|
1918
|
clk: in std_ulogic;
|
1919
|
rst: in std_ulogic;
|
1920
|
|
1921
|
ce: in std_ulogic := '1';
|
1922
|
cr: in std_ulogic;
|
1923
|
|
1924
|
load: in std_ulogic_vector(addr_length - 1 downto 0) := (others => '0');
|
1925
|
load_we: in std_ulogic := '0';
|
1926
|
|
1927
|
dout: out std_ulogic_vector(data_length - 1 downto 0));
|
1928
|
end entity;
|
1929
|
|
1930
|
architecture structural of data_source is
|
1931
|
signal addr: std_ulogic_vector(addr_length - 1 downto 0);
|
1932
|
begin
|
1933
|
count: work.util.counter
|
1934
|
generic map(
|
1935
|
N => addr_length)
|
1936
|
port map(
|
1937
|
clk => clk,
|
1938
|
rst => rst,
|
1939
|
ce => ce,
|
1940
|
cr => cr,
|
1941
|
dout => addr,
|
1942
|
load_i => load,
|
1943
|
load_we => load_we);
|
1944
|
|
1945
|
ram: work.util.single_port_block_ram
|
1946
|
generic map(
|
1947
|
addr_length => addr_length,
|
1948
|
data_length => data_length,
|
1949
|
file_name => file_name,
|
1950
|
file_type => file_type)
|
1951
|
port map(
|
1952
|
clk => clk,
|
1953
|
addr => addr,
|
1954
|
dwe => '0',
|
1955
|
dre => '1',
|
1956
|
din => (others => '0'),
|
1957
|
dout => dout);
|
1958
|
|
1959
|
end architecture;
|
1960
|
|
1961
|
------------------------- Data Source -----------------------------------------------
|
1962
|
|
1963
|
------------------------- uCPU ------------------------------------------------------
|
1964
|
-- @brief An incredible simple microcontroller
|
1965
|
-- @license MIT
|
1966
|
-- @author Richard James Howe
|
1967
|
-- @copyright Richard James Howe (2017)
|
1968
|
--
|
1969
|
-- Based on:
|
1970
|
-- https://stackoverflow.com/questions/20955863/vhdl-microprocessor-microcontroller
|
1971
|
--
|
1972
|
-- INSTRUCTION CYCLES 87 6543210 OPERATION
|
1973
|
-- ADD WITH CARRY 2 00 ADDRESS A = A + MEM[ADDRESS]
|
1974
|
-- NOR 2 01 ADDRESS A = A NOR MEM[ADDRESS]
|
1975
|
-- STORE 1 10 ADDRESS MEM[ADDRESS] = A
|
1976
|
-- JCC 1 11 ADDRESS IF(CARRY) { PC = ADDRESS, CLEAR CARRY }
|
1977
|
--
|
1978
|
-- It would be interesting to make a customizable CPU in which the
|
1979
|
-- instructions could be customized based upon what. Another interesting
|
1980
|
-- possibility is making a simple assembler purely in VHDL, which should
|
1981
|
-- be possible, but difficult. A single port version would require another
|
1982
|
-- state to fetch the operand and another register, or more states.
|
1983
|
--
|
1984
|
-- @todo Test in hardware, document, make assembler, and a project that
|
1985
|
-- just contains an instantiation of this core.
|
1986
|
--
|
1987
|
|
1988
|
library ieee,work;
|
1989
|
use ieee.std_logic_1164.all;
|
1990
|
use ieee.numeric_std.all;
|
1991
|
|
1992
|
entity ucpu is
|
1993
|
generic(width: positive range 8 to 32 := 8);
|
1994
|
port(
|
1995
|
clk, rst: in std_ulogic;
|
1996
|
|
1997
|
pc: out std_ulogic_vector(width - 3 downto 0);
|
1998
|
op: in std_ulogic_vector(width - 1 downto 0);
|
1999
|
|
2000
|
adr: out std_ulogic_vector(width - 3 downto 0);
|
2001
|
di: in std_ulogic_vector(width - 1 downto 0);
|
2002
|
re, we: out std_ulogic;
|
2003
|
do: out std_ulogic_vector(width - 1 downto 0));
|
2004
|
end entity;
|
2005
|
|
2006
|
architecture rtl of ucpu is
|
2007
|
signal a_c, a_n: unsigned(di'high + 1 downto di'low) := (others => '0'); -- accumulator
|
2008
|
signal pc_c, pc_n: unsigned(pc'range) := (others => '0');
|
2009
|
signal alu: std_ulogic_vector(1 downto 0) := (others => '0');
|
2010
|
signal state_c, state_n: std_ulogic := '0'; -- FETCH/Single cycle instruction or EXECUTE
|
2011
|
begin
|
2012
|
pc <= std_ulogic_vector(pc_n);
|
2013
|
do <= std_ulogic_vector(a_c(do'range));
|
2014
|
alu <= op(op'high downto op'high - 1);
|
2015
|
adr <= op(adr'range);
|
2016
|
we <= '1' when alu = "10" else '0'; -- STORE
|
2017
|
re <= alu(1) nor state_c; -- FETCH for ADD and NOR
|
2018
|
state_n <= alu(1) nor state_c; -- FETCH not taken or FETCH done
|
2019
|
pc_n <= unsigned(op(pc_n'range)) when (alu = "11" and a_c(a_c'high) = '0') else -- Jump when carry set
|
2020
|
pc_c when (state_c = '0' and alu(1) = '0') else -- FETCH
|
2021
|
pc_c + 1; -- EXECUTE
|
2022
|
|
2023
|
process(clk, rst)
|
2024
|
begin
|
2025
|
if rst = '1' then
|
2026
|
a_c <= (others => '0');
|
2027
|
pc_c <= (others => '0');
|
2028
|
state_c <= '0';
|
2029
|
elsif rising_edge(clk) then
|
2030
|
a_c <= a_n;
|
2031
|
pc_c <= pc_n;
|
2032
|
state_c <= state_n;
|
2033
|
end if;
|
2034
|
end process;
|
2035
|
|
2036
|
process(op, alu, di, a_c, state_c)
|
2037
|
begin
|
2038
|
a_n <= a_c;
|
2039
|
|
2040
|
if alu = "11" and a_c(a_c'high) = '0' then a_n(a_n'high) <= '0'; end if;
|
2041
|
|
2042
|
if state_c = '1' then -- EXECUTE for ADD and NOR
|
2043
|
assert alu(1) = '0' severity failure;
|
2044
|
if alu(0) = '0' then a_n <= '0' & a_c(di'range) + unsigned('0' & di); end if;
|
2045
|
if alu(0) = '1' then a_n <= a_c nor '0' & unsigned(di); end if;
|
2046
|
end if;
|
2047
|
end process;
|
2048
|
end architecture;
|
2049
|
|
2050
|
library ieee,work;
|
2051
|
use ieee.std_logic_1164.all;
|
2052
|
use ieee.numeric_std.all;
|
2053
|
use ieee.math_real.all;
|
2054
|
use work.util.all;
|
2055
|
|
2056
|
entity ucpu_tb is
|
2057
|
generic(
|
2058
|
clock_frequency: positive;
|
2059
|
file_name: string := "ucpu.bin");
|
2060
|
end entity;
|
2061
|
|
2062
|
architecture testing of ucpu_tb is
|
2063
|
constant clk_period: time := 1000 ms / clock_frequency;
|
2064
|
|
2065
|
constant data_length: positive := 8;
|
2066
|
constant addr_length: positive := data_length - 2;
|
2067
|
|
2068
|
signal a_addr: std_ulogic_vector(addr_length - 1 downto 0);
|
2069
|
signal a_dout: std_ulogic_vector(data_length - 1 downto 0) := (others => '0');
|
2070
|
|
2071
|
signal b_dwe: std_ulogic;
|
2072
|
signal b_dre: std_ulogic;
|
2073
|
signal b_addr: std_ulogic_vector(addr_length - 1 downto 0);
|
2074
|
signal b_din: std_ulogic_vector(data_length - 1 downto 0);
|
2075
|
signal b_dout: std_ulogic_vector(data_length - 1 downto 0) := (others => '0');
|
2076
|
|
2077
|
signal clk, rst: std_ulogic := '0';
|
2078
|
signal stop: std_ulogic := '0';
|
2079
|
begin
|
2080
|
cs: entity work.clock_source_tb
|
2081
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
2082
|
port map(stop => stop, clk => clk, rst => rst);
|
2083
|
|
2084
|
bram_0: entity work.dual_port_block_ram
|
2085
|
generic map(
|
2086
|
addr_length => addr_length,
|
2087
|
data_length => data_length,
|
2088
|
file_name => file_name,
|
2089
|
file_type => FILE_BINARY)
|
2090
|
port map(
|
2091
|
a_clk => clk,
|
2092
|
a_dwe => '0',
|
2093
|
a_dre => '1',
|
2094
|
a_addr => a_addr,
|
2095
|
a_din => (others => '0'),
|
2096
|
a_dout => a_dout,
|
2097
|
|
2098
|
b_clk => clk,
|
2099
|
b_dwe => b_dwe,
|
2100
|
b_dre => b_dre,
|
2101
|
b_addr => b_addr,
|
2102
|
b_din => b_din,
|
2103
|
b_dout => b_dout);
|
2104
|
|
2105
|
ucpu_0: entity work.ucpu
|
2106
|
generic map(width => data_length)
|
2107
|
port map(
|
2108
|
clk => clk,
|
2109
|
rst => rst,
|
2110
|
pc => a_addr,
|
2111
|
op => a_dout,
|
2112
|
|
2113
|
re => b_dre,
|
2114
|
we => b_dwe,
|
2115
|
di => b_dout,
|
2116
|
do => b_din,
|
2117
|
adr => b_addr);
|
2118
|
|
2119
|
stimulus_process: process
|
2120
|
begin
|
2121
|
wait for clk_period * 1000;
|
2122
|
stop <= '1';
|
2123
|
wait;
|
2124
|
end process;
|
2125
|
|
2126
|
end architecture;
|
2127
|
|
2128
|
------------------------- uCPU ------------------------------------------------------
|
2129
|
|
2130
|
------------------------- Restoring Division ----------------------------------------
|
2131
|
-- @todo Add remainder to output, rename signals, make a
|
2132
|
-- better test bench, add non-restoring division, and describe module
|
2133
|
--
|
2134
|
-- Computes a/b in N cycles
|
2135
|
--
|
2136
|
-- https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division
|
2137
|
--
|
2138
|
--
|
2139
|
library ieee,work;
|
2140
|
use ieee.std_logic_1164.all;
|
2141
|
use ieee.numeric_std.all;
|
2142
|
|
2143
|
entity restoring_divider is
|
2144
|
generic(N: positive);
|
2145
|
port(
|
2146
|
clk: in std_ulogic;
|
2147
|
rst: in std_ulogic := '0';
|
2148
|
|
2149
|
a: in unsigned(N - 1 downto 0);
|
2150
|
b: in unsigned(N - 1 downto 0);
|
2151
|
start: in std_ulogic;
|
2152
|
done: out std_ulogic;
|
2153
|
c: out unsigned(N - 1 downto 0));
|
2154
|
end entity;
|
2155
|
|
2156
|
architecture rtl of restoring_divider is
|
2157
|
signal a_c, a_n: unsigned(a'range) := (others => '0');
|
2158
|
signal b_c, b_n: unsigned(b'range) := (others => '0');
|
2159
|
signal m_c, m_n: unsigned(b'range) := (others => '0');
|
2160
|
signal o_c, o_n: unsigned(c'range) := (others => '0');
|
2161
|
signal e_c, e_n: std_ulogic := '0';
|
2162
|
signal count_c, count_n: unsigned(work.util.n_bits(N) downto 0) := (others => '0');
|
2163
|
begin
|
2164
|
c <= o_n;
|
2165
|
|
2166
|
process(clk, rst)
|
2167
|
begin
|
2168
|
if rst = '1' then
|
2169
|
a_c <= (others => '0');
|
2170
|
b_c <= (others => '0');
|
2171
|
m_c <= (others => '0');
|
2172
|
o_c <= (others => '0');
|
2173
|
e_c <= '0';
|
2174
|
count_c <= (others => '0');
|
2175
|
elsif rising_edge(clk) then
|
2176
|
a_c <= a_n;
|
2177
|
b_c <= b_n;
|
2178
|
m_c <= m_n;
|
2179
|
o_c <= o_n;
|
2180
|
e_c <= e_n;
|
2181
|
count_c <= count_n;
|
2182
|
end if;
|
2183
|
end process;
|
2184
|
|
2185
|
divide: process(a, b, start, a_c, b_c, m_c, e_c, o_c, count_c)
|
2186
|
variable m_v: unsigned(b'range) := (others => '0');
|
2187
|
begin
|
2188
|
done <= '0';
|
2189
|
a_n <= a_c;
|
2190
|
b_n <= b_c;
|
2191
|
m_v := m_c;
|
2192
|
e_n <= e_c;
|
2193
|
o_n <= o_c;
|
2194
|
count_n <= count_c;
|
2195
|
if start = '1' then
|
2196
|
a_n <= a;
|
2197
|
b_n <= b;
|
2198
|
m_v := (others => '0');
|
2199
|
e_n <= '1';
|
2200
|
o_n <= (others => '0');
|
2201
|
count_n <= (others => '0');
|
2202
|
elsif e_c = '1' then
|
2203
|
if count_c(count_c'high) = '1' then
|
2204
|
done <= '1';
|
2205
|
e_n <= '0';
|
2206
|
o_n <= a_c;
|
2207
|
count_n <= (others => '0');
|
2208
|
else
|
2209
|
m_v(b'high downto 1) := m_v(b'high - 1 downto 0);
|
2210
|
m_v(0) := a_c(a'high);
|
2211
|
a_n(a'high downto 1) <= a_c(a'high - 1 downto 0);
|
2212
|
m_v := m_v - b_c;
|
2213
|
if m_v(m_v'high) = '1' then
|
2214
|
m_v := m_v + b_c;
|
2215
|
a_n(0) <= '0';
|
2216
|
else
|
2217
|
a_n(0) <= '1';
|
2218
|
end if;
|
2219
|
count_n <= count_c + 1;
|
2220
|
end if;
|
2221
|
else
|
2222
|
count_n <= (others => '0');
|
2223
|
end if;
|
2224
|
m_n <= m_v;
|
2225
|
end process;
|
2226
|
end architecture;
|
2227
|
|
2228
|
library ieee,work;
|
2229
|
use ieee.std_logic_1164.all;
|
2230
|
use ieee.numeric_std.all;
|
2231
|
use ieee.math_real.all;
|
2232
|
|
2233
|
entity restoring_divider_tb is
|
2234
|
generic(clock_frequency: positive);
|
2235
|
end entity;
|
2236
|
|
2237
|
architecture testing of restoring_divider_tb is
|
2238
|
constant clk_period: time := 1000 ms / clock_frequency;
|
2239
|
constant N: positive := 8;
|
2240
|
|
2241
|
signal a: unsigned(N - 1 downto 0) := (others => '0');
|
2242
|
signal b: unsigned(N - 1 downto 0) := (others => '0');
|
2243
|
signal c: unsigned(N - 1 downto 0) := (others => '0');
|
2244
|
signal start, done: std_ulogic := '0';
|
2245
|
|
2246
|
signal clk, rst: std_ulogic := '0';
|
2247
|
signal stop: std_ulogic := '0';
|
2248
|
begin
|
2249
|
cs: entity work.clock_source_tb
|
2250
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
2251
|
port map(stop => stop, clk => clk, rst => rst);
|
2252
|
|
2253
|
uut: entity work.restoring_divider
|
2254
|
generic map(N => N)
|
2255
|
port map(
|
2256
|
clk => clk,
|
2257
|
rst => rst,
|
2258
|
a => a,
|
2259
|
b => b,
|
2260
|
start => start,
|
2261
|
done => done,
|
2262
|
c => c);
|
2263
|
|
2264
|
stimulus_process: process
|
2265
|
begin
|
2266
|
wait for clk_period * 2;
|
2267
|
|
2268
|
a <= x"64";
|
2269
|
b <= x"0A";
|
2270
|
start <= '1';
|
2271
|
wait for clk_period * 1;
|
2272
|
start <= '0';
|
2273
|
wait until done = '1';
|
2274
|
--assert c = x"0A" severity failure;
|
2275
|
|
2276
|
wait for clk_period * 10;
|
2277
|
b <= x"05";
|
2278
|
start <= '1';
|
2279
|
wait for clk_period * 1;
|
2280
|
start <= '0';
|
2281
|
wait until done = '1';
|
2282
|
--assert c = x"14" severity failure;
|
2283
|
|
2284
|
stop <= '1';
|
2285
|
wait;
|
2286
|
end process;
|
2287
|
|
2288
|
end architecture;
|
2289
|
------------------------- Restoring Divider ---------------------------------------------------
|
2290
|
|
2291
|
------------------------- Debouncer -----------------------------------------------------------
|
2292
|
|
2293
|
library ieee,work;
|
2294
|
use ieee.std_logic_1164.all;
|
2295
|
use ieee.numeric_std.all;
|
2296
|
|
2297
|
entity debounce_us is
|
2298
|
generic(clock_frequency: positive; timer_period_us: natural);
|
2299
|
port(
|
2300
|
clk: in std_ulogic;
|
2301
|
di: in std_ulogic;
|
2302
|
do: out std_ulogic := '0');
|
2303
|
end entity;
|
2304
|
|
2305
|
architecture rtl of debounce_us is
|
2306
|
signal ff: std_ulogic_vector(1 downto 0) := (others => '0');
|
2307
|
signal rst, done: std_ulogic := '0';
|
2308
|
begin
|
2309
|
timer: work.util.timer_us
|
2310
|
generic map(
|
2311
|
clock_frequency => clock_frequency,
|
2312
|
timer_period_us => timer_period_us)
|
2313
|
port map(
|
2314
|
clk => clk,
|
2315
|
rst => rst,
|
2316
|
co => done);
|
2317
|
|
2318
|
process(clk)
|
2319
|
begin
|
2320
|
if rising_edge(clk) then
|
2321
|
ff(0) <= di;
|
2322
|
ff(1) <= ff(0);
|
2323
|
rst <= '0';
|
2324
|
if (ff(0) xor ff(1)) = '1' then
|
2325
|
rst <= '1';
|
2326
|
elsif done = '1' then
|
2327
|
do <= ff(1);
|
2328
|
end if;
|
2329
|
end if;
|
2330
|
end process;
|
2331
|
end architecture;
|
2332
|
|
2333
|
library ieee,work;
|
2334
|
use ieee.std_logic_1164.all;
|
2335
|
use ieee.numeric_std.all;
|
2336
|
|
2337
|
entity debounce_us_tb is
|
2338
|
generic(clock_frequency: positive);
|
2339
|
end entity;
|
2340
|
|
2341
|
architecture testing of debounce_us_tb is
|
2342
|
constant clk_period: time := 1000 ms / clock_frequency;
|
2343
|
|
2344
|
signal di, do: std_ulogic := '0';
|
2345
|
signal clk, rst: std_ulogic := '0';
|
2346
|
signal stop: std_ulogic := '0';
|
2347
|
begin
|
2348
|
cs: entity work.clock_source_tb
|
2349
|
generic map(clock_frequency => clock_frequency, hold_rst => 2)
|
2350
|
port map(stop => stop, clk => clk, rst => rst);
|
2351
|
|
2352
|
uut: work.util.debounce_us
|
2353
|
generic map(clock_frequency => clock_frequency, timer_period_us => 1)
|
2354
|
port map(clk => clk, di => di, do => do);
|
2355
|
|
2356
|
stimulus_process: process
|
2357
|
begin
|
2358
|
wait for clk_period * 2;
|
2359
|
di <= '1';
|
2360
|
|
2361
|
wait for 1.5 us;
|
2362
|
|
2363
|
stop <= '1';
|
2364
|
wait;
|
2365
|
end process;
|
2366
|
end architecture;
|
2367
|
------------------------- Debouncer -----------------------------------------------------------
|
2368
|
|
2369
|
------------------------- Debouncer Block -----------------------------------------------------
|
2370
|
|
2371
|
library ieee,work;
|
2372
|
use ieee.std_logic_1164.all;
|
2373
|
use ieee.numeric_std.all;
|
2374
|
|
2375
|
entity debounce_block_us is
|
2376
|
generic(N: positive; clock_frequency: positive; timer_period_us: natural);
|
2377
|
port(
|
2378
|
clk: in std_ulogic;
|
2379
|
di: in std_ulogic_vector(N - 1 downto 0);
|
2380
|
do: out std_ulogic_vector(N - 1 downto 0));
|
2381
|
end entity;
|
2382
|
|
2383
|
architecture structural of debounce_block_us is
|
2384
|
begin
|
2385
|
debouncer: for i in N - 1 downto 0 generate
|
2386
|
d_instance: work.util.debounce_us
|
2387
|
generic map(
|
2388
|
clock_frequency => clock_frequency,
|
2389
|
timer_period_us => timer_period_us)
|
2390
|
port map(clk => clk, di => di(i), do => do(i));
|
2391
|
end generate;
|
2392
|
end architecture;
|
2393
|
|
2394
|
------------------------- Debouncer Block -----------------------------------------------------
|
2395
|
|
2396
|
------------------------- State Changed -------------------------------------------------------
|
2397
|
library ieee,work;
|
2398
|
use ieee.std_logic_1164.all;
|
2399
|
use ieee.numeric_std.all;
|
2400
|
|
2401
|
entity state_changed is
|
2402
|
port(
|
2403
|
clk: in std_ulogic;
|
2404
|
rst: in std_ulogic;
|
2405
|
di: in std_ulogic;
|
2406
|
do: out std_ulogic);
|
2407
|
end entity;
|
2408
|
|
2409
|
architecture rtl of state_changed is
|
2410
|
signal state_c, state_n: std_ulogic_vector(1 downto 0) := (others => '0');
|
2411
|
begin
|
2412
|
process(clk, rst)
|
2413
|
begin
|
2414
|
if rst = '1' then
|
2415
|
state_c <= (others => '0');
|
2416
|
elsif rising_edge(clk) then
|
2417
|
state_c <= state_n;
|
2418
|
end if;
|
2419
|
end process;
|
2420
|
|
2421
|
do <= '1' when (state_c(0) xor state_c(1)) = '1' else '0';
|
2422
|
|
2423
|
process(di, state_c)
|
2424
|
begin
|
2425
|
state_n(0) <= state_c(1);
|
2426
|
state_n(1) <= di;
|
2427
|
end process;
|
2428
|
end architecture;
|
2429
|
|
2430
|
------------------------- Change State --------------------------------------------------------
|
2431
|
|
2432
|
------------------------- Change State Block --------------------------------------------------
|
2433
|
library ieee,work;
|
2434
|
use ieee.std_logic_1164.all;
|
2435
|
use ieee.numeric_std.all;
|
2436
|
|
2437
|
entity state_block_changed is
|
2438
|
generic(N: positive);
|
2439
|
port(
|
2440
|
clk: in std_ulogic;
|
2441
|
rst: in std_ulogic;
|
2442
|
di: in std_ulogic_vector(N - 1 downto 0);
|
2443
|
do: out std_ulogic_vector(N - 1 downto 0));
|
2444
|
end entity;
|
2445
|
|
2446
|
architecture structural of state_block_changed is
|
2447
|
begin
|
2448
|
changes: for i in N - 1 downto 0 generate
|
2449
|
d_instance: work.util.state_changed
|
2450
|
port map(clk => clk, rst => rst, di => di(i), do => do(i));
|
2451
|
end generate;
|
2452
|
end architecture;
|
2453
|
|
2454
|
------------------------- Change State Block --------------------------------------------------
|