-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathavm_master2.vhd
More file actions
424 lines (381 loc) · 21 KB
/
Copy pathavm_master2.vhd
File metadata and controls
424 lines (381 loc) · 21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
-------------------------------------------------------------------------------
-- avm_master2 — Randomised Avalon-MM Master Stimulus Generator (Testbench)
--
-- This module exercises an Avalon-MM slave (DUT) by generating a randomised
-- stream of single-word write and read transactions, verifying read-back
-- correctness against an internal shadow memory.
--
-- Test strategy:
-- A 64-bit LFSR ('random' entity) produces pseudo-random fields that are
-- sliced into address, data, byte-enable, and write/read selection signals
-- (see R_ADDRESS, R_DATA, R_BYTEENABLE, R_WRITE subtypes below).
--
-- 1. For each LFSR sample, the module decides whether to WRITE or READ:
-- - WRITE if any byte-lane of the target address has not yet been
-- written (tracked by the 'written' byte-enable accumulator), OR
-- - WRITE if the random 'write_s' flag is asserted (~1/16 chance),
-- which provides occasional overwrites of already-complete
-- addresses to exercise write-after-read sequences, OR
-- - WRITE if the random byte-enable is all-zero (forced to all-ones
-- to avoid a no-op that would never complete the address), OR
-- - READ otherwise (all bytes written; shadow memory holds a valid
-- reference for comparison).
--
-- 2. On a READ, the returned data is compared against the shadow memory
-- ('mem'), which mirrors every accepted write with byte-enable
-- granularity. A mismatch asserts error_o (sticky) and reports the
-- expected and actual values.
--
-- 3. The test terminates after 2**(G_ADDRESS_SIZE + 4) read responses
-- have been verified.
--
-- Limitations / assumptions:
-- - Only SINGLE-WORD (burstcount = 1) transactions are generated and
-- verified. The write_burstcount_i and read_burstcount_i ports are
-- forwarded to the DUT's burstcount input but the stimulus and
-- verification logic does NOT produce multi-beat write bursts or
-- track multi-word read responses. Using values other than X"01"
-- causes Avalon-MM protocol violations (writes) or incorrect
-- verification (reads).
--
-- - The shadow memory is 2**G_ADDRESS_SIZE words. Keep G_ADDRESS_SIZE
-- small (typically 4–8) to avoid excessive simulation memory.
--
-- Debug outputs:
-- address_o — address of the most recently accepted read
-- data_exp_o — expected data from shadow memory for that read
-- data_read_o — actual data returned by the DUT
-- error_o — sticky flag, asserted on first mismatch (cleared by reset)
--
-- Created by Michael Jørgensen in 2022 (mjoergen.github.io/HyperRAM).
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.numeric_std_unsigned.all;
entity avm_master2 is
generic (
G_BURST_WIDTH : natural; -- Width of burstcount
G_ADDRESS_SIZE : natural; -- Address width in bits (shadow RAM = 2**G_ADDRESS_SIZE words)
G_DATA_SIZE : natural -- Data word width in bits (must be a multiple of 8)
);
port (
clk_i : in std_logic;
rst_i : in std_logic;
start_i : in std_logic; -- Pulse high to begin the test
wait_o : out std_logic; -- High while the test is running
write_burstcount_i : in std_logic_vector(G_BURST_WIDTH - 1 downto 0); -- Burstcount forwarded on writes (must be X"01")
read_burstcount_i : in std_logic_vector(G_BURST_WIDTH - 1 downto 0); -- Burstcount forwarded on reads
-- Avalon-MM master interface (directly drives the DUT's slave port)
m_avm_write_o : out std_logic;
m_avm_read_o : out std_logic;
m_avm_address_o : out std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
m_avm_writedata_o : out std_logic_vector(G_DATA_SIZE - 1 downto 0);
m_avm_byteenable_o : out std_logic_vector(G_DATA_SIZE / 8 - 1 downto 0);
m_avm_burstcount_o : out std_logic_vector(G_BURST_WIDTH - 1 downto 0);
m_avm_readdata_i : in std_logic_vector(G_DATA_SIZE - 1 downto 0);
m_avm_readdatavalid_i : in std_logic;
m_avm_waitrequest_i : in std_logic;
-- Debug / verification outputs
address_o : out std_logic_vector(G_ADDRESS_SIZE - 1 downto 0); -- Address of the most recently accepted read
data_exp_o : out std_logic_vector(G_DATA_SIZE - 1 downto 0); -- Expected read data (from shadow memory)
data_read_o : out std_logic_vector(G_DATA_SIZE - 1 downto 0); -- Actual read data (from DUT)
error_o : out std_logic -- Sticky mismatch flag
);
end entity avm_master2;
architecture synthesis of avm_master2 is
-- Byte-enable vector with every bit set: used to test whether all byte-
-- lanes of a given address have been written at least once.
constant C_ALL_ONES : std_logic_vector(G_DATA_SIZE / 8 - 1 downto 0) := (others => '1');
---------------------------------------------------------------------------
-- Shadow memory: mirrors the DUT's memory contents, updated on every
-- accepted write with byte-enable granularity.
---------------------------------------------------------------------------
type mem_type is array (0 to 2 ** G_ADDRESS_SIZE - 1) of std_logic_vector(G_DATA_SIZE - 1 downto 0);
signal mem : mem_type := (others => (others => '0'));
---------------------------------------------------------------------------
-- Per-address byte-enable accumulator: tracks which byte-lanes have been
-- written. A read is only issued once written(addr) = C_ALL_ONES, so the
-- shadow memory holds a complete reference value for the full word.
---------------------------------------------------------------------------
type be_type is array (0 to 2 ** G_ADDRESS_SIZE - 1) of std_logic_vector(G_DATA_SIZE / 8 - 1 downto 0);
signal written : be_type := (others => (others => '0'));
type state_type is (
IDLE_ST, -- Waiting for start_i pulse
WORKING_ST, -- Issuing transactions; bus is idle or a write is pending acceptance
WRITING_ST, -- Part of a burst write
READING_ST, -- A read has been issued; waiting for readdatavalid
DONE_ST -- Test complete; wait_o deasserted
);
signal lfsr_random_s : std_logic_vector(63 downto 0);
signal state : state_type := IDLE_ST;
signal num_read : natural := 0;
---------------------------------------------------------------------------
-- LFSR field mapping
--
-- The 64-bit LFSR output is carved into non-overlapping bit-fields that
-- drive the randomised transaction parameters. Each subtype defines one
-- field's bit range.
--
-- [R_ADDRESS] → random target address (G_ADDRESS_SIZE bits)
-- [R_DATA] → random write data (G_DATA_SIZE bits)
-- [R_BYTEENABLE] → random byte-enable mask (G_DATA_SIZE/8 bits)
-- [R_WRITE] → write-override flag field (4 bits, AND-reduced
-- to a single bit → '1' with ~1/16 probability)
--
-- Total LFSR bits consumed:
-- G_ADDRESS_SIZE + G_DATA_SIZE + G_DATA_SIZE/8 + 4 (must be <= 64)
---------------------------------------------------------------------------
signal address_s : std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
signal data_s : std_logic_vector(G_DATA_SIZE - 1 downto 0);
signal byteenable_s : std_logic_vector(G_DATA_SIZE / 8 - 1 downto 0);
signal write_s : std_logic;
signal lfsr_update_s : std_logic;
subtype R_ADDRESS is natural range G_ADDRESS_SIZE - 1 downto 0;
subtype R_DATA is natural range G_DATA_SIZE + R_ADDRESS'left downto R_ADDRESS'left + 1;
subtype R_BYTEENABLE is natural range G_DATA_SIZE / 8 + R_DATA'left downto R_DATA'left + 1;
subtype R_WRITE is natural range 4 + R_BYTEENABLE'left downto R_BYTEENABLE'left + 1;
signal write_base_addr : std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
signal write_beat_index : std_logic_vector(G_BURST_WIDTH - 1 downto 0);
signal write_burstcount : std_logic_vector(G_BURST_WIDTH - 1 downto 0);
signal read_base_addr : std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
signal read_beat_index : std_logic_vector(G_BURST_WIDTH - 1 downto 0);
signal read_burstcount : std_logic_vector(G_BURST_WIDTH - 1 downto 0);
begin
-- Compile-time validation
assert G_DATA_SIZE >= 8
report "G_DATA_SIZE must be >= 8"
severity failure;
assert G_DATA_SIZE mod 8 = 0
report "G_DATA_SIZE must be a multiple of 8"
severity failure;
assert R_WRITE'left <= 63
report "LFSR field mapping exceeds 64-bit LFSR width; reduce G_ADDRESS_SIZE or G_DATA_SIZE"
severity failure;
assert G_BURST_WIDTH <= 2 ** G_ADDRESS_SIZE
report "G_BURST_WIDTH must be <= 2**G_ADDRESS_SIZE"
severity failure;
---------------------------------------------------------------------------
-- Combinatorial decode of LFSR fields into transaction parameters.
---------------------------------------------------------------------------
address_s <= lfsr_random_s(R_ADDRESS);
data_s <= lfsr_random_s(R_DATA);
byteenable_s <= lfsr_random_s(R_BYTEENABLE);
-- write_s is the AND-reduction of 4 LFSR bits: '1' with ~1/16
-- probability, providing occasional overwrites of already-complete
-- addresses to exercise write-after-read sequences.
write_s <= and(lfsr_random_s(R_WRITE));
---------------------------------------------------------------------------
-- Main FSM process
---------------------------------------------------------------------------
master_proc : process (clk_i)
-- Expected read data (from shadow memory)
variable data_exp_v : std_logic_vector(G_DATA_SIZE - 1 downto 0);
variable read_address_v : std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
variable write_address_v : std_logic_vector(G_ADDRESS_SIZE - 1 downto 0);
begin
if rising_edge(clk_i) then
lfsr_update_s <= '0';
-- Default: deassert master bus request once accepted by the slave
if m_avm_waitrequest_i = '0' then
m_avm_write_o <= '0';
m_avm_read_o <= '0';
end if;
case state is
---------------------------------------------------------------
-- IDLE_ST: Wait for the external start pulse.
---------------------------------------------------------------
when IDLE_ST =>
if start_i = '1' then
wait_o <= '1';
state <= WORKING_ST;
report "Starting";
end if;
---------------------------------------------------------------
-- WORKING_ST / READING_ST: Active test phase.
--
-- WORKING_ST — bus is idle (or a write is pending acceptance).
-- A new transaction may be issued immediately.
-- READING_ST — a read has been issued; the module waits for
-- readdatavalid before issuing the next one.
--
-- Both states share the readdatavalid handler (verification)
-- and the termination check.
---------------------------------------------------------------
when WORKING_ST | READING_ST =>
-- --------------------------------------------------------
-- Read-data verification
-- --------------------------------------------------------
if m_avm_readdatavalid_i = '1' then
read_address_v := read_base_addr + read_beat_index;
data_exp_v := mem(to_integer(read_address_v));
if data_exp_v /= m_avm_readdata_i then
report "Read 0x" & to_hstring(m_avm_readdata_i) &
" from address 0x" & to_hstring(read_address_v) &
", but expected 0x" & to_hstring(data_exp_v)
severity failure;
address_o <= read_base_addr + read_beat_index;
data_exp_o <= data_exp_v;
data_read_o <= m_avm_readdata_i;
error_o <= '1';
end if;
num_read <= num_read + 1;
read_beat_index <= read_beat_index + 1;
if read_beat_index + 1 = read_burstcount then
state <= WORKING_ST;
end if;
end if;
-- --------------------------------------------------------
-- Issue next transaction
--
-- Guard conditions:
-- (a) Bus is free: either the previous transaction was
-- accepted (waitrequest low) or no transaction is
-- pending (write_o and read_o both '0').
-- (b) State permits: either we are in WORKING_ST (bus
-- was already idle) or read data has just arrived
-- this cycle (READING_ST → WORKING_ST transition),
-- allowing back-to-back pipelining.
-- --------------------------------------------------------
if (m_avm_waitrequest_i = '0' or (m_avm_write_o = '0' and m_avm_read_o = '0')) and
((m_avm_readdatavalid_i = '1' and read_beat_index + 1 = read_burstcount) or
state = WORKING_ST) then
if written(to_integer(address_s)) /= C_ALL_ONES or write_s = '1' or byteenable_s = 0 then
-- ---------------------------------------------------
-- WRITE transaction
--
-- Issued when:
-- - Target address not yet fully written (primary
-- reason: ensures every byte-lane is populated
-- before a read is attempted), OR
-- - Random overwrite flag (write_s = '1', ~1/16
-- probability), OR
-- - Byte-enable is all-zero (forced to all-ones
-- full-word write to avoid a no-op that would
-- never mark any byte-lane as written).
-- ---------------------------------------------------
lfsr_update_s <= '1';
m_avm_write_o <= '1';
m_avm_read_o <= '0';
m_avm_address_o <= address_s;
m_avm_writedata_o <= data_s;
m_avm_byteenable_o <= byteenable_s;
m_avm_burstcount_o <= write_burstcount_i;
written(to_integer(address_s)) <= written(to_integer(address_s)) or byteenable_s;
-- Force all-ones when the random byte-enable is zero
if byteenable_s = 0 then
m_avm_byteenable_o <= (others => '1');
written(to_integer(address_s)) <= (others => '1');
end if;
-- Write data to shadow memory
for i in 0 to G_DATA_SIZE / 8 - 1 loop
if byteenable_s(i) = '1' or byteenable_s = 0 then
mem(to_integer(address_s))(8 * i + 7 downto 8 * i) <= data_s(8 * i + 7 downto 8 * i);
end if;
end loop;
if write_burstcount_i > 1 then
write_base_addr <= address_s;
write_beat_index <= (0 => '1', others => '0'); -- Value 1
write_burstcount <= write_burstcount_i;
state <= WRITING_ST;
end if;
else
-- ---------------------------------------------------
-- READ transaction
--
-- Issued only when every byte-lane of the target
-- address has been written (written = C_ALL_ONES), so
-- the shadow memory holds a valid full-word reference.
-- ---------------------------------------------------
lfsr_update_s <= '1';
m_avm_write_o <= '0';
m_avm_read_o <= '1';
m_avm_address_o <= address_s;
m_avm_burstcount_o <= read_burstcount_i;
read_base_addr <= address_s;
read_beat_index <= (others => '0');
read_burstcount <= read_burstcount_i;
state <= READING_ST;
end if;
end if;
-- --------------------------------------------------------
-- Termination check
--
-- Placed last so that its assignments (deassert write/read,
-- transition to DONE_ST) take priority via last-assignment-
-- wins over any transaction issued above on the same cycle.
-- --------------------------------------------------------
if num_read >= 2 ** (G_ADDRESS_SIZE + 4) then
m_avm_write_o <= '0';
m_avm_read_o <= '0';
state <= DONE_ST;
report "Done";
end if;
---------------------------------------------------------------
-- WRITING_ST: Part of write burst
---------------------------------------------------------------
when WRITING_ST =>
if m_avm_waitrequest_i = '0' then
write_address_v := write_base_addr + write_beat_index;
lfsr_update_s <= '1';
m_avm_write_o <= '1';
m_avm_read_o <= '0';
m_avm_writedata_o <= data_s;
m_avm_byteenable_o <= byteenable_s;
written(to_integer(write_address_v)) <= written(to_integer(write_address_v)) or byteenable_s;
-- Force all-ones when the random byte-enable is zero
if byteenable_s = 0 then
m_avm_byteenable_o <= (others => '1');
written(to_integer(write_address_v)) <= (others => '1');
end if;
-- Write data to shadow memory
for i in 0 to G_DATA_SIZE / 8 - 1 loop
if byteenable_s(i) = '1' or byteenable_s = 0 then
mem(to_integer(write_address_v))(8 * i + 7 downto 8 * i) <= data_s(8 * i + 7 downto 8 * i);
end if;
end loop;
if write_beat_index + 1 = write_burstcount then
state <= WORKING_ST;
else
write_beat_index <= write_beat_index + 1;
end if;
end if;
---------------------------------------------------------------
-- DONE_ST: Test complete.
---------------------------------------------------------------
when DONE_ST =>
wait_o <= '0';
when others =>
null;
end case;
-- ---------------------------------------------------------------
-- Synchronous reset
-- ---------------------------------------------------------------
if rst_i = '1' then
wait_o <= '0';
m_avm_write_o <= '0';
m_avm_read_o <= '0';
m_avm_address_o <= (others => '0');
m_avm_writedata_o <= (others => '0');
m_avm_byteenable_o <= (others => '0');
m_avm_burstcount_o <= (others => '0');
address_o <= (others => '0');
data_read_o <= (others => '0');
error_o <= '0';
written <= (others => (others => '0'));
num_read <= 0;
state <= IDLE_ST;
end if;
end if;
end process master_proc;
--------------------------------------
-- Instantiate randon number generator
--------------------------------------
random_inst : entity work.random
port map (
clk_i => clk_i,
rst_i => rst_i,
update_i => lfsr_update_s,
output_o => lfsr_random_s
); -- random_inst : entity work.random is
end architecture synthesis;