-
Notifications
You must be signed in to change notification settings - Fork 0
/
axi_addr_counter.sv
264 lines (211 loc) · 7.44 KB
/
axi_addr_counter.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
`timescale 1 ns / 1 ps
/*
This takes an AXI4 burst transaction on the AW channel and spits out a
backpressurable sequence of the individual addresses of each beat, which can
be separately correlated with the data beats.
This DOESN'T initially support any burst mode other than INCR, nor beat size other than
DATA_WIDTH.
Basically, snap the awaddr and awlen, keep adding clog2(DATA_WIDTH) aka awsize
to it outputting the numbers.
*/
module axi_addr_counter #(
localparam ADDR_WIDTH = 12, // DO NOT CHANGE
localparam DATA_WIDTH = 32, // DO NOT CHANGE
localparam SIZE_WIDTH = 3
)
(
input logic clk,
input logic reset,
// AXI AW Lines
input logic [ADDR_WIDTH-1:0] i_awaddr,
input logic [7:0] i_awlen,
input logic [2:0] i_awsize,
input logic [1:0] i_awburst,
input logic i_awvalid,
output logic i_awready,
output logic [ADDR_WIDTH-1:0] o_next_addr_data,
output logic o_next_addr_valid,
input logic o_next_addr_ready
);
// we cut some corners and assume that all bus transactions will be
// for the max width of the bus
localparam ADDR_INCR = ADDR_WIDTH'(DATA_WIDTH / 8);
localparam AWSIZE_EXPECTED = SIZE_WIDTH'($clog2(DATA_WIDTH / 8));
// registered state
logic [8:0] burst_length;
logic [8:0] burst_count;
initial begin
burst_length = 0;
burst_count = 0;
end
logic in_burst;
initial in_burst = 1'b0;
always_comb
in_burst = burst_count < burst_length;
// we are ready for a transaction whenever this counter is done
// and we have room downstream
always_comb
i_awready = o_next_addr_ready && !in_burst;
// we have an address to deliver
always_comb
o_next_addr_valid = in_burst;
always_ff @(posedge clk) begin
if (i_awvalid && i_awready) begin
// new incoming transaction
// reset counters, load first address
// NOTE: o_next_addr_valid will be set on the NEXT
// clock once the burst counters have flopped,
// With some more logic we could save a clock in between
// bursts if we cared to.
burst_count <= 0;
burst_length <= {1'b0,i_awlen} + 1;
o_next_addr_data <= i_awaddr;
end else if (o_next_addr_valid && o_next_addr_ready) begin
// prep following address
burst_count <= burst_count + 1;
burst_length <= burst_length; // is this necessary
o_next_addr_data <= o_next_addr_data + ADDR_INCR;
end
if (reset) begin
burst_count <= 0;
burst_length <= 0;
o_next_addr_data <= 0;
end
end
always @(*)
assert(burst_length <= 256);
always @(*)
assert(burst_count <= 256);
always @(*)
if (o_next_addr_valid)
assert(o_next_addr_data % ADDR_INCR == 0);
logic [ADDR_WIDTH-1:0] implied_burst_span;
// assume the other axi fields are consistent with what we expect
always @(*) begin
if (i_awvalid) begin
implied_burst_span = (ADDR_WIDTH'(i_awlen) + 1) * ADDR_INCR;
`ifdef AXIADDRCOUNTER_FORMAL
// must be INCR burst
assume(i_awburst == 2'b01);
// must be full width
assume(i_awsize == AWSIZE_EXPECTED);
// must be aligned addresses
assume(i_awaddr % ADDR_INCR == 0);
// burst cannot wrap 4096 page boundary
assume(implied_burst_span < ({ADDR_WIDTH{1'b1}} - i_awaddr));
`else
assert(i_awburst == 2'b01);
assert(i_awsize == AWSIZE_EXPECTED);
assert(i_awaddr % ADDR_INCR == 0);
assert(implied_burst_span < ({ADDR_WIDTH{1'b1}} - i_awaddr));
`endif
end
end
`ifdef FORMAL
logic f_past_valid;
initial begin
f_past_valid = 0;
end
always @(posedge clk)
f_past_valid <= 1;
// assume inputs reset properly
always @(posedge clk) begin
if (f_past_valid && $past(reset))
`ifdef AXIADDRCOUNTER_FORMAL
assume(!i_awvalid);
`else
assert(!i_awvalid);
`endif
end
// assume inputs are stable if stalled
always @(posedge clk)
if (f_past_valid && !$past(reset) && $past(i_awvalid) && !$past(i_awready)) begin
`ifdef AXIADDRCOUNTER_FORMAL
assume($stable(i_awaddr));
assume($stable(i_awlen));
assume($stable(i_awsize));
assume($stable(i_awburst));
assume(i_awvalid);
`else
assert($stable(i_awaddr));
assert($stable(i_awlen));
assert($stable(i_awsize));
assert($stable(i_awburst));
assert(i_awvalid);
`endif
end
// assert outputs are stable if stalled
always @(posedge clk)
if (f_past_valid && !$past(reset) && $past(o_next_addr_valid) && !$past(o_next_addr_ready)) begin
assert($stable(o_next_addr_data));
assert(o_next_addr_valid);
end
// assume short bursts to speed up the proofs
`ifdef AXIADDRCOUNTER_FORMAL
always @(*)
assume(i_awlen < 16);
`endif
// assert output is reset
always @(posedge clk)
if (f_past_valid && $past(reset)) begin
assert(!o_next_addr_valid);
end
// assume low first clock
always @(*)
if (!f_past_valid)
assume(i_awvalid == 0);
// assert that burst count never overflows
always @(posedge clk)
assert(burst_count <= burst_length);
logic [31:0] f_addr_count;
initial f_addr_count = 0;
always @(posedge clk) begin
if (f_past_valid && !$past(reset) && !reset)
if (o_next_addr_valid && o_next_addr_ready)
f_addr_count <= f_addr_count + 1;
if (reset)
f_addr_count <= 0;
end
logic [31:0] f_awlen_count;
initial f_awlen_count = 0;
always @(posedge clk) begin
if (f_past_valid && !$past(reset) && !reset)
if (i_awvalid && i_awready)
f_awlen_count <= f_awlen_count + (i_awlen + 1);
if (reset)
f_awlen_count <= 0;
end
// TODO write covers
`ifdef AXIADDRCOUNTER_FORMAL
always @(posedge clk)
cover(!reset && i_awvalid && i_awready && i_awlen > 0);
logic f_was_in_burst;
initial f_was_in_burst = 0;
always @(posedge clk)
if (in_burst)
f_was_in_burst <= 1;
always @(posedge clk)
if (f_past_valid && !$past(reset) && !reset)
cover(burst_count > 8 && burst_count == burst_length && f_addr_count == burst_length);
integer f_iaw_count;
initial f_iaw_count = 0;
always @(posedge clk)
if (f_past_valid && !$past(reset) && !reset)
if (i_awvalid && i_awready)
f_iaw_count <= f_iaw_count + 1;
// try to catch two bursts of 16 based on i_awlen assumption < 16
always @(posedge clk)
if (f_past_valid && !$past(reset) && !reset)
cover(f_addr_count == 32 && f_iaw_count == 2);
integer f_stalled_beats;
initial f_stalled_beats = 0;
always @(posedge clk)
if (o_next_addr_valid && !o_next_addr_ready)
f_stalled_beats <= f_stalled_beats + 1;
// try to catch two bursts of 16 based on i_awlen assumption < 16 with some stalls
always @(posedge clk)
if (f_past_valid && !$past(reset) && !reset)
cover(f_addr_count == 32 && f_iaw_count == 2 && f_stalled_beats > 8);
`endif
`endif
endmodule