forked from pulp-platform/axi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tb_axi_lite_regs.sv
362 lines (336 loc) · 13.3 KB
/
tb_axi_lite_regs.sv
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
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <[email protected]>
// Directed Random Verification Testbench for `axi_lite_regs`.
`include "axi/assign.svh"
/// Testbench for validating the module `axi_lite_regs`.
module tb_axi_lite_regs #(
/// Define the parameter `RegNumBytes` of the DUT.
parameter int unsigned TbRegNumBytes = 32'd200,
/// Define the parameter `AxiReadOnly` of the DUT.
parameter logic [TbRegNumBytes-1:0] TbAxiReadOnly = {{TbRegNumBytes-18{1'b0}}, 18'b101110111111000000},
/// Define the parameter `PrivProtOnly` of the DUT.
parameter bit TbPrivProtOnly = 1'b0,
/// Define the parameter `SecuProtOnly` of the DUT.
parameter bit TbSecuProtOnly = 1'b0,
/// Number of random writes generated by the testbench AXI4-Lite random master.
parameter int unsigned TbNoWrites = 32'd1000,
/// Number of random reads generated by the testbench AXI4-Lite random master.
parameter int unsigned TbNoReads = 32'd1500
);
// AXI configuration
localparam int unsigned AxiAddrWidth = 32'd32; // Axi Address Width
localparam int unsigned AxiDataWidth = 32'd32; // Axi Data Width
localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8;
// timing parameters
localparam time CyclTime = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
typedef logic [7:0] byte_t;
typedef logic [AxiAddrWidth-1:0] axi_addr_t;
typedef logic [AxiDataWidth-1:0] axi_data_t;
typedef logic [AxiStrbWidth-1:0] axi_strb_t;
localparam axi_addr_t StartAddr = axi_addr_t'(0);
localparam axi_addr_t EndAddr =
axi_addr_t'(StartAddr + TbRegNumBytes + TbRegNumBytes/5);
localparam byte_t [TbRegNumBytes-1:0] RegRstVal = '0;
typedef axi_test::axi_lite_rand_master #(
// AXI interface parameters
.AW ( AxiAddrWidth ),
.DW ( AxiDataWidth ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime ),
.MIN_ADDR ( StartAddr ),
.MAX_ADDR ( EndAddr ),
.MAX_READ_TXNS ( 10 ),
.MAX_WRITE_TXNS ( 10 )
) rand_lite_master_t;
// -------------
// DUT signals
// -------------
logic clk;
// DUT signals
logic rst_n;
logic end_of_sim;
// Register signals
byte_t [TbRegNumBytes-1:0] reg_d, reg_q;
logic [TbRegNumBytes-1:0] wr_active, rd_active, reg_load;
// -------------------------------
// AXI Interfaces
// -------------------------------
AXI_LITE #(
.AXI_ADDR_WIDTH ( AxiAddrWidth ),
.AXI_DATA_WIDTH ( AxiDataWidth )
) master ();
AXI_LITE_DV #(
.AXI_ADDR_WIDTH ( AxiAddrWidth ),
.AXI_DATA_WIDTH ( AxiDataWidth )
) master_dv (clk);
`AXI_LITE_ASSIGN(master, master_dv)
// -------------------------------
// AXI Rand Masters and Slaves
// -------------------------------
// Masters control simulation run time
initial begin : proc_generate_axi_traffic
automatic rand_lite_master_t lite_axi_master = new ( master_dv, "Lite Master");
automatic axi_data_t data = '0;
automatic axi_pkg::resp_t resp = '0;
end_of_sim <= 1'b0;
lite_axi_master.reset();
@(posedge rst_n);
repeat (5) @(posedge clk);
// Test known register.
// Write to it.
lite_axi_master.write(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0),
axi_data_t'(64'hDEADBEEFDEADBEEF), axi_strb_t'(8'hFF), resp);
if (TbPrivProtOnly || TbSecuProtOnly) begin
assert (resp == axi_pkg::RESP_SLVERR) else
$fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted.");
end else begin
assert (resp == axi_pkg::RESP_OKAY) else
$fatal(1, "Access should be granted");
end
// Read from it.
lite_axi_master.read(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0), data, resp);
if (TbPrivProtOnly || TbSecuProtOnly) begin
// Expect error response
assert (resp == axi_pkg::RESP_SLVERR) else
$fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted.");
assert (data == axi_data_t'(32'hBA5E1E55)) else
$fatal(1, "Data is unexpected, should be axi_data_t'(32'hBA5E1E55).");
end else begin
assert (resp == axi_pkg::RESP_OKAY) else
$fatal(1, "Access should be granted.");
// Checking of the expecetd read data is handled in `proc_check_read_data`.
end
// Let random stimuli application checking is separate.
lite_axi_master.run(TbNoReads, TbNoWrites);
end_of_sim <= 1'b1;
end
initial begin : proc_generate_direct_load
for (int unsigned i = 0; i < TbRegNumBytes; i++) begin
automatic int unsigned j = i;
fork
toggle_load(j);
join_none
end
end
task automatic toggle_load(int unsigned byte_i);
byte_t rand_val;
int unsigned rand_wait;
@(posedge rst_n);
reg_load[byte_i] = 1'b0;
reg_d[byte_i] = 8'h00;
@(posedge clk);
forever begin
rand_wait = $urandom_range(0, 20);
repeat (rand_wait) @(posedge clk);
rand_val = byte_t'($urandom());
reg_d[byte_i] <= #ApplTime rand_val;
reg_load[byte_i] <= #ApplTime 1'b1;
@(posedge clk);
reg_d[byte_i] <= #ApplTime '0;
reg_load[byte_i] <= #ApplTime 1'b0;
end
endtask : toggle_load
initial begin : proc_check_read_data
automatic axi_data_t exp_rdata[$];
automatic axi_pkg::resp_t exp_resp[$];
@(posedge rst_n);
forever begin
@(posedge clk);
#(TestTime);
// Push the expected data back.
if (master.ar_valid && master.ar_ready) begin
automatic int unsigned ar_idx = ((master.ar_addr - StartAddr)
>> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth));
automatic axi_data_t r_data = axi_data_t'(0);
automatic axi_pkg::resp_t r_resp = axi_pkg::RESP_SLVERR;
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
if ((ar_idx+i) < TbRegNumBytes) begin
r_data[8*i+:8] = reg_q[ar_idx+i];
r_resp = axi_pkg::RESP_OKAY;
end else begin
r_data[8*i+:8] = 8'hxx;
end
end
// check the right protection access
if (TbPrivProtOnly && !master.ar_prot[0]) begin
r_resp = axi_pkg::RESP_SLVERR;
end
if (TbSecuProtOnly && !master.ar_prot[1]) begin
r_resp = axi_pkg::RESP_SLVERR;
end
exp_resp.push_back(r_resp);
exp_rdata.push_back(r_data);
end
// compare data if there is a value expected
if (master.r_valid && master.r_ready) begin
automatic axi_data_t r_data = exp_rdata.pop_front();
if (master.r_resp == axi_pkg::RESP_OKAY) begin
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
automatic byte_t exp_byte = r_data[8*i+:8];
if (exp_byte !== 8'hxx) begin
assert (master.r_data[8*i+:8] == exp_byte) else
$error("Unexpected read data: exp: %0h observed: %0h", r_data, master.r_data);
assert (master.r_resp == axi_pkg::RESP_OKAY);
end
end
end else if (master.r_resp == axi_pkg::RESP_SLVERR) begin
assert (master.r_data == axi_data_t'(32'hBA5E1E55));
end else begin
$error("Slave responded with false response: %0h", master.r_resp);
end
end
end
end
axi_pkg::resp_t b_resp_queue[$];
// Only works as module expects both AW & W valid before it does something
initial begin : proc_check_write_data
@(posedge rst_n);
forever begin
#TestTime;
// AW and W is launched, setup the test tasks
if (master.aw_valid && master.aw_ready && master.w_valid && master.w_ready) begin
automatic int unsigned aw_idx = ((master.aw_addr - StartAddr)
>> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth));
automatic axi_pkg::resp_t exp_b_resp = (aw_idx < TbRegNumBytes) ?
axi_pkg::RESP_OKAY : axi_pkg::RESP_SLVERR;
automatic bit all_ro = 1'b1;
// check for errors from wrong access protection
if (TbPrivProtOnly && !master.aw_prot[0]) begin
exp_b_resp = axi_pkg::RESP_SLVERR;
end
if (TbSecuProtOnly && !master.aw_prot[1]) begin
exp_b_resp = axi_pkg::RESP_SLVERR;
end
// Check if all accesses bytes are read only
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
if (!TbAxiReadOnly[aw_idx+i]) begin
all_ro = 1'b0;
end
end
if (all_ro) begin
exp_b_resp = axi_pkg::RESP_SLVERR;
end
// do the actual write checking
if (exp_b_resp == axi_pkg::RESP_OKAY) begin
// go through every byte
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
if ((aw_idx+i) < TbRegNumBytes) begin
if (master.w_strb[i]) begin
automatic int unsigned j = aw_idx + i;
automatic byte_t exp_byte = master.w_data[8*i+:8];
fork
check_q(j, exp_byte);
join_none
end
assert (master.w_strb[i] == wr_active[aw_idx+i]);
end
end
end
b_resp_queue.push_back(exp_b_resp);
end
@(posedge clk);
end
end
initial begin : proc_check_b
automatic axi_pkg::resp_t exp_b_resp;
@(posedge rst_n);
forever begin
#TestTime;
if (master.b_valid && master.b_ready) begin
if (b_resp_queue.size()) begin
exp_b_resp = b_resp_queue.pop_front();
assert (exp_b_resp == master.b_resp) else
$error("Unexpected B response: EXP: %b ACT: %b", exp_b_resp, master.b_resp);
end else begin
$error("B response even no AW was sent.");
end
end
@(posedge clk);
end
end
// check that the expected register byte has been written
task automatic check_q(int unsigned byte_i, byte_t exp_byte);
automatic byte_t save_byte = (reg_load[byte_i]) ? reg_d[byte_i] : reg_q[byte_i];
@(posedge clk);
#TestTime;
if (!TbAxiReadOnly[byte_i]) begin
assert(exp_byte == reg_q[byte_i]) else
$error("AXI write was not registered at byte: %0d", byte_i);
end else begin
assert (reg_q[byte_i] == save_byte) else
$error("AXI write onto read only byte: %0d SAVE: %h EXP: %h ACT %h",
byte_i, save_byte, exp_byte, reg_q[byte_i]);
end
endtask : check_q
// Some assertions for additional checking.
default disable iff (~rst_n);
for (genvar i = 0; i < TbRegNumBytes; i++) begin : gen_check_ro_bytes
if (TbAxiReadOnly[i]) begin : gen_check_ro
ro_assert_no_load: assert property (@(posedge clk)
((wr_active[i] && !reg_load[i]) |=> $stable(reg_q[i]))) else
$fatal(1, "ReadOnly byte %0d changed from AXI write, while not direct loaded.", i);
ro_assert_load: assert property (@(posedge clk)
((wr_active[i] && reg_load[i]) |=> (reg_q[i] === $past(reg_d[i])))) else
$fatal(1, "ReadOnly byte %0d changed from AXI write, while direct loaded.", i);
end
load_assert_no_axi: assert property (@(posedge clk)
(reg_load[i] && !TbAxiReadOnly[i] |-> !wr_active[i])) else
$fatal(1, "Byte %0d, AXI write onto non read-only and direct load are both active!", i);
load_assert_data: assert property (@(posedge clk)
(reg_load[i] |=> reg_q[i] === $past(reg_d[i]))) else
$fatal(1, "Byte %0d, direct load was executed falsely.", i);
stable_assert: assert property (@(posedge clk)
(!reg_load[i] && !wr_active[i]) |=> $stable(reg_q[i])) else
$fatal(1, "Byte %0d is unstable, when no AXI write or direct load.", i);
end
initial begin : proc_stop_sim
wait (end_of_sim);
repeat (1000) @(posedge clk);
$display("Simulation stopped as Master transferred its data.");
$stop();
end
//-----------------------------------
// Clock generator
//-----------------------------------
clk_rst_gen #(
.ClkPeriod ( CyclTime ),
.RstClkCycles ( 5 )
) i_clk_gen (
.clk_o (clk),
.rst_no(rst_n)
);
//-----------------------------------
// DUT
//-----------------------------------
axi_lite_regs_intf #(
.REG_NUM_BYTES ( TbRegNumBytes ),
.AXI_ADDR_WIDTH ( AxiAddrWidth ),
.AXI_DATA_WIDTH ( AxiDataWidth ),
.PRIV_PROT_ONLY ( TbPrivProtOnly ),
.SECU_PROT_ONLY ( TbSecuProtOnly ),
.AXI_READ_ONLY ( TbAxiReadOnly ),
.REG_RST_VAL ( RegRstVal )
) i_axi_lite_regs (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.slv ( master ),
.wr_active_o ( wr_active ),
.rd_active_o ( rd_active ),
.reg_d_i ( reg_d ),
.reg_load_i ( reg_load ),
.reg_q_o ( reg_q )
);
endmodule