-
Notifications
You must be signed in to change notification settings - Fork 1
/
util.vhd
335 lines (304 loc) · 11.2 KB
/
util.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
-- File: util.vhd
-- Author: Richard James Howe
-- Repository: https://github.com/howerj/bit-serial
-- Email: [email protected]
-- License: MIT
--
-- Description: Utility module, mostly taken from another project
-- of mine, <https://github.com/howerj/forth-cpu>.
--
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
package util is
-- Not all modules will need every generic specified here, even so it
-- is easier to group the common generics in one structure.
type common_generics is record
clock_frequency: positive; -- clock frequency of module clock
delay: time; -- gate delay for simulation purposes
asynchronous_reset: boolean; -- use asynchronous reset if true
end record;
constant default_settings: common_generics := (
clock_frequency => 100_000_000,
delay => 0 ns,
asynchronous_reset => true
);
type file_format is (FILE_HEX, FILE_BINARY, FILE_DECIMAL, FILE_NONE);
component single_port_block_ram is
generic (g: common_generics;
addr_length: positive := 12;
data_length: positive := 16;
file_name: string := "memory.bin";
file_type: file_format := FILE_BINARY);
port (
clk: in std_ulogic;
dwe: in std_ulogic;
dre: in std_ulogic;
addr: in std_ulogic_vector(addr_length - 1 downto 0);
din: in std_ulogic_vector(data_length - 1 downto 0);
dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
end component;
function hex_char_to_std_ulogic_vector_tb(hc: character) return std_ulogic_vector;
-- synthesis translate_off
subtype configuration_name is string(1 to 8);
type configuration_item is record
name: configuration_name;
value: integer;
end record;
type configuration_items is array(integer range <>) of configuration_item;
function search_configuration_tb(find_me: configuration_name; ci: configuration_items) return integer;
procedure read_configuration_tb(file_name: string; ci: inout configuration_items);
procedure write_configuration_tb(file_name: string; ci: configuration_items);
-- synthesis translate_on
end;
package body util is
function hex_char_to_std_ulogic_vector_tb(hc: character) return std_ulogic_vector is
variable slv: std_ulogic_vector(3 downto 0);
begin
case hc is
when '0' => slv := "0000";
when '1' => slv := "0001";
when '2' => slv := "0010";
when '3' => slv := "0011";
when '4' => slv := "0100";
when '5' => slv := "0101";
when '6' => slv := "0110";
when '7' => slv := "0111";
when '8' => slv := "1000";
when '9' => slv := "1001";
when 'A' => slv := "1010";
when 'a' => slv := "1010";
when 'B' => slv := "1011";
when 'b' => slv := "1011";
when 'C' => slv := "1100";
when 'c' => slv := "1100";
when 'D' => slv := "1101";
when 'd' => slv := "1101";
when 'E' => slv := "1110";
when 'e' => slv := "1110";
when 'F' => slv := "1111";
when 'f' => slv := "1111";
when others => slv := "XXXX";
end case;
assert (slv /= "XXXX") report " not a valid hex character: " & hc severity failure;
return slv;
end;
-- synthesis translate_off
-- Find a string in a configuration items array, or returns -1 on
-- failure to find the string.
function search_configuration_tb(find_me: configuration_name; ci: configuration_items) return integer is
begin
for i in ci'range loop
if ci(i).name = find_me then
return i;
end if;
end loop;
return -1;
end;
-- VHDL provides quite a limited set of options for dealing with
-- operations that are not synthesizeable but would be useful for
-- use in test benches. This method provides a crude way of reading
-- in configurable options. It has a very strict format.
--
-- The format is line oriented, it expects a string on a line
-- with a length equal to the "configuration_name" type, which
-- is a subtype of "string". It finds the corresponding record
-- in configuration_items if it exists. It then reads in an
-- integer from the next line and sets the record for it.
--
-- Any deviation from this format causes an error and the simulation
-- to halt, whilst not a good practice to do error checking with asserts
-- there is no better way in VHDL in this case. The only sensible
-- action on an error would for the configuration file to be fixed
-- anyway.
--
-- Comment lines and variable length strings would be nice, but
-- are too much of a hassle.
--
-- The configuration function only deal with part of the configuration
-- process, it does not deal with deserialization into structures
-- more useful to the user - that is into individual signals.
--
procedure read_configuration_tb(file_name: string; ci: inout configuration_items) is
file in_file: text is in file_name;
variable in_line: line;
variable d: integer;
variable s: configuration_name;
variable index: integer;
begin
while not endfile(in_file) loop
readline(in_file, in_line);
read(in_line, s);
index := search_configuration_tb(s, ci);
assert index >= 0 report "Unknown configuration item: " & s severity failure;
readline(in_file, in_line);
read(in_line, d);
ci(index).value := d;
report "Config Item: '" & ci(index).name & "' = " & integer'image(ci(index).value);
end loop;
file_close(in_file);
end procedure;
procedure write_configuration_tb(file_name: string; ci: configuration_items) is
file out_file: text is out file_name;
variable out_line: line;
begin
for i in ci'range loop
write(out_line, ci(i).name);
writeline(out_file, out_line);
write(out_line, ci(i).value);
writeline(out_file, out_line);
end loop;
end procedure;
-- synthesis translate_on
end;
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
use work.util.all;
entity single_port_block_ram is
generic (g: common_generics;
addr_length: positive := 12;
data_length: positive := 16;
file_name: string := "memory.bin";
file_type: file_format := FILE_BINARY);
port (
clk: in std_ulogic;
dwe: in std_ulogic;
dre: in std_ulogic;
addr: in std_ulogic_vector(addr_length - 1 downto 0);
din: in std_ulogic_vector(data_length - 1 downto 0);
dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0'));
end entity;
-- The function `initialize_ram` does what it says, it initializes a Block RAM from a file, which
-- is read off of disk. Unfortunately this version does not synthesize in Xilinx ISE 14.7 (and likely
-- never will) because of an indefinite loop, failing with the error "Non-static loop limit exceeded".
--
-- This is a shame as this version is more flexible in what it can handle.
--
-- The function allows multiple file types to be used to initialize the RAM:
--
-- * FILE_HEX; A file containing hexadecimal values, one per line.
-- * FILE_BINARY; A file containing binary values, one per line.
-- * FILE_DECIMAL; A file consisting of signed decimal values, one per line.
-- * FILE_NONE; No file, all RAM contents will be initialized to 0.
--
-- ----------------------------------------------------------------------------------------------------------
--
-- impure function initialize_ram(the_file_name: in string; the_file_type: in file_format) return ram_type is
-- variable ram_data: ram_type;
-- file in_file: text is in the_file_name;
-- variable input_line: line;
-- variable tmp: bit_vector(data_length - 1 downto 0);
-- variable int: integer;
-- variable i: integer;
-- variable good: boolean;
-- variable c: character;
-- variable slv: std_ulogic_vector(data_length - 1 downto 0);
-- begin
-- i := 0;
-- while i < ram_size loop
-- if the_file_type = FILE_NONE then
-- ram_data(i) := (others => '0');
-- i := i + 1;
-- elsif not endfile(in_file) then
-- readline(in_file,input_line);
-- if the_file_type = FILE_BINARY then
-- read(input_line, tmp);
-- ram_data(i) := std_ulogic_vector(to_stdlogicvector(tmp));
-- i := i + 1;
-- elsif the_file_type = FILE_DECIMAL then
-- good := true;
-- while good and i < ram_size loop
-- read(input_line, int, good);
-- if good then
-- if int < 0 then
-- int := (2**data_length) + int;
-- end if;
-- assert int < (2**data_length) and int >= 0 severity failure;
-- ram_data(i) := std_ulogic_vector(to_unsigned(int, tmp'length));
-- i := i + 1;
-- end if;
-- end loop;
-- elsif the_file_type = FILE_HEX then -- hexadecimal
-- assert (data_length mod 4) = 0 report "(data_length%4)!=0" severity failure;
-- for j in 1 to (data_length/4) loop
-- c:= input_line((data_length/4) - j + 1);
-- slv((j*4)-1 downto (j*4)-4) := hex_char_to_std_ulogic_vector_tb(c);
-- end loop;
-- ram_data(i) := slv;
-- i := i + 1;
-- else
-- report "Incorrect file type given: " & file_format'image(the_file_type) severity failure;
-- end if;
-- else
-- ram_data(i) := (others => '0');
-- i := i + 1;
-- end if;
-- end loop;
-- file_close(in_file);
-- return ram_data;
-- end function;
--
-- ----------------------------------------------------------------------------------------------------------
architecture behav of single_port_block_ram is
constant ram_size: positive := 2 ** addr_length;
type ram_type is array ((ram_size - 1 ) downto 0) of std_ulogic_vector(data_length - 1 downto 0);
impure function initialize_ram(the_file_name: in string; the_file_type: in file_format) return ram_type is
variable ram_data: ram_type;
file in_file: text is in the_file_name;
variable input_line: line;
variable tmp: bit_vector(data_length - 1 downto 0);
variable int: integer;
variable c: character;
variable slv: std_ulogic_vector(data_length - 1 downto 0);
begin
for i in 0 to ram_size - 1 loop
if the_file_type = FILE_NONE then
ram_data(i) := (others => '0');
elsif not endfile(in_file) then
readline(in_file,input_line);
if the_file_type = FILE_BINARY then
read(input_line, tmp);
ram_data(i) := std_ulogic_vector(to_stdlogicvector(tmp));
elsif the_file_type = FILE_DECIMAL then
read(input_line, int);
if int < 0 then
int := (2 ** data_length) + int;
end if;
assert int < (2 ** data_length) and int >= 0 severity failure;
ram_data(i) := std_ulogic_vector(to_unsigned(int, tmp'length));
elsif the_file_type = FILE_HEX then -- hexadecimal
assert (data_length mod 4) = 0 report "(data_length % 4) != 0" severity failure;
for j in 1 to (data_length / 4) loop
c:= input_line((data_length / 4) - j + 1);
slv((j * 4) - 1 downto (j * 4) - 4) := hex_char_to_std_ulogic_vector_tb(c);
end loop;
ram_data(i) := slv;
else
report "Incorrect file type given: " & file_format'image(the_file_type) severity failure;
end if;
else
ram_data(i) := (others => '0');
end if;
end loop;
file_close(in_file);
return ram_data;
end function;
shared variable ram: ram_type := initialize_ram(file_name, file_type);
begin
block_ram: process(clk)
begin
if rising_edge(clk) then
if dwe = '1' then
ram(to_integer(unsigned(addr))) := din;
end if;
if dre = '1' then
dout <= ram(to_integer(unsigned(addr))) after g.delay;
else
dout <= (others => '0') after g.delay;
end if;
end if;
end process;
end architecture;