1 |
32 |
redbear |
// (C) 2001-2017 Intel Corporation. All rights reserved.
|
2 |
|
|
// Your use of Intel Corporation's design tools, logic functions and other
|
3 |
|
|
// software and tools, and its AMPP partner logic functions, and any output
|
4 |
|
|
// files any of the foregoing (including device programming or simulation
|
5 |
|
|
// files), and any associated documentation or information are expressly subject
|
6 |
|
|
// to the terms and conditions of the Intel Program License Subscription
|
7 |
|
|
// Agreement, Intel MegaCore Function License Agreement, or other applicable
|
8 |
|
|
// license agreement, including, without limitation, that your use is for the
|
9 |
|
|
// sole purpose of programming logic devices manufactured by Intel and sold by
|
10 |
|
|
// Intel or its authorized distributors. Please refer to the applicable
|
11 |
|
|
// agreement for further details.
|
12 |
|
|
|
13 |
|
|
|
14 |
|
|
// $Id: //acds/rel/17.0std/ip/merlin/altera_merlin_traffic_limiter/altera_merlin_traffic_limiter.sv#1 $
|
15 |
|
|
// $Revision: #1 $
|
16 |
|
|
// $Date: 2017/01/22 $
|
17 |
|
|
// $Author: swbranch $
|
18 |
|
|
|
19 |
|
|
// -----------------------------------------------------
|
20 |
|
|
// Merlin Traffic Limiter
|
21 |
|
|
//
|
22 |
|
|
// Ensures that non-posted transaction responses are returned
|
23 |
|
|
// in order of request. Out-of-order responses can happen
|
24 |
|
|
// when a master does a non-posted transaction on a slave
|
25 |
|
|
// while responses are pending from a different slave.
|
26 |
|
|
//
|
27 |
|
|
// Examples:
|
28 |
|
|
// 1) read to any latent slave, followed by a read to a
|
29 |
|
|
// variable-latent slave
|
30 |
|
|
// 2) read to any fixed-latency slave, followed by a read
|
31 |
|
|
// to another fixed-latency slave whose fixed latency is smaller.
|
32 |
|
|
// 3) non-posted write to any latent slave, followed by a non-posted
|
33 |
|
|
// write or read to any variable-latent slave.
|
34 |
|
|
//
|
35 |
|
|
// This component has two implementation modes that ensure
|
36 |
|
|
// response order, controlled by the REORDER parameter.
|
37 |
|
|
//
|
38 |
|
|
// 0) Backpressure to prevent a master from switching slaves
|
39 |
|
|
// until all outstanding responses have returned. We also
|
40 |
|
|
// have to suppress the non-posted transaction, obviously.
|
41 |
|
|
//
|
42 |
|
|
// 1) Reorder the responses as they return using a memory
|
43 |
|
|
// block.
|
44 |
|
|
// -----------------------------------------------------
|
45 |
|
|
|
46 |
|
|
`timescale 1 ns / 1 ns
|
47 |
|
|
|
48 |
|
|
// altera message_off 10036
|
49 |
|
|
module altera_merlin_traffic_limiter
|
50 |
|
|
#(
|
51 |
|
|
parameter
|
52 |
|
|
PKT_TRANS_POSTED = 1,
|
53 |
|
|
PKT_DEST_ID_H = 0,
|
54 |
|
|
PKT_DEST_ID_L = 0,
|
55 |
|
|
PKT_SRC_ID_H = 0,
|
56 |
|
|
PKT_SRC_ID_L = 0,
|
57 |
|
|
PKT_BYTE_CNT_H = 0,
|
58 |
|
|
PKT_BYTE_CNT_L = 0,
|
59 |
|
|
PKT_BYTEEN_H = 0,
|
60 |
|
|
PKT_BYTEEN_L = 0,
|
61 |
|
|
PKT_TRANS_WRITE = 0,
|
62 |
|
|
PKT_TRANS_READ = 0,
|
63 |
|
|
ST_DATA_W = 72,
|
64 |
|
|
ST_CHANNEL_W = 32,
|
65 |
|
|
|
66 |
|
|
MAX_OUTSTANDING_RESPONSES = 1,
|
67 |
|
|
PIPELINED = 0,
|
68 |
|
|
ENFORCE_ORDER = 1,
|
69 |
|
|
|
70 |
|
|
// -------------------------------------
|
71 |
|
|
// internal: allows optimization between this
|
72 |
|
|
// component and the demux
|
73 |
|
|
// -------------------------------------
|
74 |
|
|
VALID_WIDTH = 1,
|
75 |
|
|
|
76 |
|
|
// -------------------------------------
|
77 |
|
|
// Prevents all RAW and WAR hazards by waiting for
|
78 |
|
|
// responses to return before issuing a command
|
79 |
|
|
// with different direction.
|
80 |
|
|
//
|
81 |
|
|
// This is intended for Avalon masters which are
|
82 |
|
|
// connected to AXI slaves, because of the differing
|
83 |
|
|
// ordering models for the protocols.
|
84 |
|
|
//
|
85 |
|
|
// If PREVENT_HAZARDS is 1, then the current implementation
|
86 |
|
|
// needs to know whether incoming writes will be posted or
|
87 |
|
|
// not at compile-time. Only one of SUPPORTS_POSTED_WRITES
|
88 |
|
|
// and SUPPORTS_NONPOSTED_WRITES can be 1.
|
89 |
|
|
//
|
90 |
|
|
// When PREVENT_HAZARDS is 0 there is no such restriction.
|
91 |
|
|
//
|
92 |
|
|
// It is possible to be less restrictive for memories.
|
93 |
|
|
// -------------------------------------
|
94 |
|
|
PREVENT_HAZARDS = 0,
|
95 |
|
|
|
96 |
|
|
// -------------------------------------
|
97 |
|
|
// Used only when hazard prevention is on, but may be used
|
98 |
|
|
// for optimization work in the future.
|
99 |
|
|
// -------------------------------------
|
100 |
|
|
SUPPORTS_POSTED_WRITES = 1,
|
101 |
|
|
SUPPORTS_NONPOSTED_WRITES = 0,
|
102 |
|
|
|
103 |
|
|
// -------------------------------------------------
|
104 |
|
|
// Enables the reorder buffer which allows a master to
|
105 |
|
|
// switch slaves while responses are pending.
|
106 |
|
|
// Reponses will be reordered following command issue order.
|
107 |
|
|
// -------------------------------------------------
|
108 |
|
|
REORDER = 0
|
109 |
|
|
)
|
110 |
|
|
(
|
111 |
|
|
// -------------------
|
112 |
|
|
// Clock & Reset
|
113 |
|
|
// -------------------
|
114 |
|
|
input clk,
|
115 |
|
|
input reset,
|
116 |
|
|
|
117 |
|
|
// -------------------
|
118 |
|
|
// Command
|
119 |
|
|
// -------------------
|
120 |
|
|
input cmd_sink_valid,
|
121 |
|
|
input [ST_DATA_W-1 : 0] cmd_sink_data,
|
122 |
|
|
input [ST_CHANNEL_W-1 : 0] cmd_sink_channel,
|
123 |
|
|
input cmd_sink_startofpacket,
|
124 |
|
|
input cmd_sink_endofpacket,
|
125 |
|
|
output cmd_sink_ready,
|
126 |
|
|
|
127 |
|
|
output reg [VALID_WIDTH-1 : 0] cmd_src_valid,
|
128 |
|
|
output reg [ST_DATA_W-1 : 0] cmd_src_data,
|
129 |
|
|
output reg [ST_CHANNEL_W-1 : 0] cmd_src_channel,
|
130 |
|
|
output reg cmd_src_startofpacket,
|
131 |
|
|
output reg cmd_src_endofpacket,
|
132 |
|
|
input cmd_src_ready,
|
133 |
|
|
|
134 |
|
|
// -------------------
|
135 |
|
|
// Response
|
136 |
|
|
// -------------------
|
137 |
|
|
input rsp_sink_valid,
|
138 |
|
|
input [ST_DATA_W-1 : 0] rsp_sink_data,
|
139 |
|
|
input [ST_CHANNEL_W-1 : 0] rsp_sink_channel,
|
140 |
|
|
input rsp_sink_startofpacket,
|
141 |
|
|
input rsp_sink_endofpacket,
|
142 |
|
|
output reg rsp_sink_ready,
|
143 |
|
|
|
144 |
|
|
output reg rsp_src_valid,
|
145 |
|
|
output reg [ST_DATA_W-1 : 0] rsp_src_data,
|
146 |
|
|
output reg [ST_CHANNEL_W-1 : 0] rsp_src_channel,
|
147 |
|
|
output reg rsp_src_startofpacket,
|
148 |
|
|
output reg rsp_src_endofpacket,
|
149 |
|
|
input rsp_src_ready
|
150 |
|
|
);
|
151 |
|
|
|
152 |
|
|
// -------------------------------------
|
153 |
|
|
// Local Parameters
|
154 |
|
|
// -------------------------------------
|
155 |
|
|
localparam DEST_ID_W = PKT_DEST_ID_H - PKT_DEST_ID_L + 1;
|
156 |
|
|
localparam COUNTER_W = log2ceil(MAX_OUTSTANDING_RESPONSES + 1);
|
157 |
|
|
localparam PAYLOAD_W = ST_DATA_W + ST_CHANNEL_W + 4;
|
158 |
|
|
localparam NUMSYMBOLS = PKT_BYTEEN_H - PKT_BYTEEN_L + 1;
|
159 |
|
|
localparam MAX_DEST_ID = 1 << (DEST_ID_W);
|
160 |
|
|
localparam PKT_BYTE_CNT_W = PKT_BYTE_CNT_H - PKT_BYTE_CNT_L + 1;
|
161 |
|
|
|
162 |
|
|
// -------------------------------------------------------
|
163 |
|
|
// Memory Parameters
|
164 |
|
|
// ------------------------------------------------------
|
165 |
|
|
localparam MAX_BYTE_CNT = 1 << (PKT_BYTE_CNT_W);
|
166 |
|
|
localparam MAX_BURST_LENGTH = log2ceil(MAX_BYTE_CNT/NUMSYMBOLS);
|
167 |
|
|
|
168 |
|
|
// Memory stores packet width, including sop and eop
|
169 |
|
|
localparam MEM_W = ST_DATA_W + ST_CHANNEL_W + 1 + 1;
|
170 |
|
|
localparam MEM_DEPTH = MAX_OUTSTANDING_RESPONSES * (MAX_BYTE_CNT/NUMSYMBOLS);
|
171 |
|
|
|
172 |
|
|
// -----------------------------------------------------
|
173 |
|
|
// Input Stage
|
174 |
|
|
//
|
175 |
|
|
// Figure out if the destination id has changed
|
176 |
|
|
// -----------------------------------------------------
|
177 |
|
|
wire stage1_dest_changed;
|
178 |
|
|
wire stage1_trans_changed;
|
179 |
|
|
wire [PAYLOAD_W-1 : 0] stage1_payload;
|
180 |
|
|
wire in_nonposted_cmd;
|
181 |
|
|
reg [ST_CHANNEL_W-1:0] last_channel;
|
182 |
|
|
wire [DEST_ID_W-1 : 0] dest_id;
|
183 |
|
|
reg [DEST_ID_W-1 : 0] last_dest_id;
|
184 |
|
|
reg was_write;
|
185 |
|
|
wire is_write;
|
186 |
|
|
wire suppress;
|
187 |
|
|
wire save_dest_id;
|
188 |
|
|
|
189 |
|
|
wire suppress_change_dest_id;
|
190 |
|
|
wire suppress_max_outstanding;
|
191 |
|
|
wire suppress_change_trans_but_not_dest;
|
192 |
|
|
wire suppress_change_trans_for_one_slave;
|
193 |
|
|
|
194 |
|
|
generate if (PREVENT_HAZARDS == 1) begin : convert_posted_to_nonposted_block
|
195 |
|
|
assign in_nonposted_cmd = 1'b1;
|
196 |
|
|
end else begin : non_posted_cmd_assignment_block
|
197 |
|
|
assign in_nonposted_cmd = (cmd_sink_data[PKT_TRANS_POSTED] == 0);
|
198 |
|
|
end
|
199 |
|
|
endgenerate
|
200 |
|
|
|
201 |
|
|
// ------------------------------------
|
202 |
|
|
// Optimization: for the unpipelined case, we can save the destid if
|
203 |
|
|
// this is an unsuppressed nonposted command. This eliminates
|
204 |
|
|
// dependence on the backpressure signal.
|
205 |
|
|
//
|
206 |
|
|
// Not a problem for the pipelined case.
|
207 |
|
|
// ------------------------------------
|
208 |
|
|
generate
|
209 |
|
|
if (PIPELINED) begin : pipelined_save_dest_id
|
210 |
|
|
assign save_dest_id = cmd_sink_valid & cmd_sink_ready & in_nonposted_cmd;
|
211 |
|
|
end else begin : unpipelined_save_dest_id
|
212 |
|
|
assign save_dest_id = cmd_sink_valid & ~(suppress_change_dest_id | suppress_max_outstanding) & in_nonposted_cmd;
|
213 |
|
|
end
|
214 |
|
|
endgenerate
|
215 |
|
|
|
216 |
|
|
always @(posedge clk, posedge reset) begin
|
217 |
|
|
if (reset) begin
|
218 |
|
|
last_dest_id <= 0;
|
219 |
|
|
last_channel <= 0;
|
220 |
|
|
was_write <= 0;
|
221 |
|
|
end
|
222 |
|
|
else if (save_dest_id) begin
|
223 |
|
|
last_dest_id <= dest_id;
|
224 |
|
|
last_channel <= cmd_sink_channel;
|
225 |
|
|
was_write <= is_write;
|
226 |
|
|
end
|
227 |
|
|
end
|
228 |
|
|
|
229 |
|
|
assign dest_id = cmd_sink_data[PKT_DEST_ID_H:PKT_DEST_ID_L];
|
230 |
|
|
assign is_write = cmd_sink_data[PKT_TRANS_WRITE];
|
231 |
|
|
assign stage1_dest_changed = (last_dest_id != dest_id);
|
232 |
|
|
assign stage1_trans_changed = (was_write != is_write);
|
233 |
|
|
|
234 |
|
|
assign stage1_payload = {
|
235 |
|
|
cmd_sink_data,
|
236 |
|
|
cmd_sink_channel,
|
237 |
|
|
cmd_sink_startofpacket,
|
238 |
|
|
cmd_sink_endofpacket,
|
239 |
|
|
stage1_dest_changed,
|
240 |
|
|
stage1_trans_changed };
|
241 |
|
|
|
242 |
|
|
// -----------------------------------------------------
|
243 |
|
|
// (Optional) pipeline between input and output
|
244 |
|
|
// -----------------------------------------------------
|
245 |
|
|
wire stage2_valid;
|
246 |
|
|
reg stage2_ready;
|
247 |
|
|
wire [PAYLOAD_W-1 : 0] stage2_payload;
|
248 |
|
|
|
249 |
|
|
generate
|
250 |
|
|
if (PIPELINED == 1) begin : pipelined_limiter
|
251 |
|
|
altera_avalon_st_pipeline_base
|
252 |
|
|
#(
|
253 |
|
|
.BITS_PER_SYMBOL(PAYLOAD_W)
|
254 |
|
|
) stage1_pipe (
|
255 |
|
|
.clk (clk),
|
256 |
|
|
.reset (reset),
|
257 |
|
|
.in_ready (cmd_sink_ready),
|
258 |
|
|
.in_valid (cmd_sink_valid),
|
259 |
|
|
.in_data (stage1_payload),
|
260 |
|
|
.out_valid (stage2_valid),
|
261 |
|
|
.out_ready (stage2_ready),
|
262 |
|
|
.out_data (stage2_payload)
|
263 |
|
|
);
|
264 |
|
|
end else begin : unpipelined_limiter
|
265 |
|
|
assign stage2_valid = cmd_sink_valid;
|
266 |
|
|
assign stage2_payload = stage1_payload;
|
267 |
|
|
assign cmd_sink_ready = stage2_ready;
|
268 |
|
|
end
|
269 |
|
|
endgenerate
|
270 |
|
|
|
271 |
|
|
// -----------------------------------------------------
|
272 |
|
|
// Output Stage
|
273 |
|
|
// -----------------------------------------------------
|
274 |
|
|
wire [ST_DATA_W-1 : 0] stage2_data;
|
275 |
|
|
wire [ST_CHANNEL_W-1:0] stage2_channel;
|
276 |
|
|
wire stage2_startofpacket;
|
277 |
|
|
wire stage2_endofpacket;
|
278 |
|
|
wire stage2_dest_changed;
|
279 |
|
|
wire stage2_trans_changed;
|
280 |
|
|
reg has_pending_responses;
|
281 |
|
|
reg [COUNTER_W-1 : 0] pending_response_count;
|
282 |
|
|
reg [COUNTER_W-1 : 0] next_pending_response_count;
|
283 |
|
|
wire nonposted_cmd;
|
284 |
|
|
wire nonposted_cmd_accepted;
|
285 |
|
|
wire response_accepted;
|
286 |
|
|
wire response_sink_accepted;
|
287 |
|
|
wire response_src_accepted;
|
288 |
|
|
wire count_is_1;
|
289 |
|
|
wire count_is_0;
|
290 |
|
|
reg internal_valid;
|
291 |
|
|
wire [VALID_WIDTH-1:0] wide_valid;
|
292 |
|
|
|
293 |
|
|
assign { stage2_data,
|
294 |
|
|
stage2_channel,
|
295 |
|
|
stage2_startofpacket,
|
296 |
|
|
stage2_endofpacket,
|
297 |
|
|
stage2_dest_changed,
|
298 |
|
|
stage2_trans_changed } = stage2_payload;
|
299 |
|
|
|
300 |
|
|
generate if (PREVENT_HAZARDS == 1) begin : stage2_nonposted_block
|
301 |
|
|
assign nonposted_cmd = 1'b1;
|
302 |
|
|
end else begin
|
303 |
|
|
assign nonposted_cmd = (stage2_data[PKT_TRANS_POSTED] == 0);
|
304 |
|
|
end
|
305 |
|
|
endgenerate
|
306 |
|
|
|
307 |
|
|
assign nonposted_cmd_accepted = nonposted_cmd && internal_valid && (cmd_src_ready && cmd_src_endofpacket);
|
308 |
|
|
|
309 |
|
|
// -----------------------------------------------------------------------------
|
310 |
|
|
// Use the sink's control signals here, because write responses may be dropped
|
311 |
|
|
// when hazard prevention is on.
|
312 |
|
|
//
|
313 |
|
|
// When case REORDER, move all side to rsp_source as all packets from rsp_sink will
|
314 |
|
|
// go in the reorder memory.
|
315 |
|
|
// One special case when PREVENT_HAZARD is on, need to use reorder_memory_valid
|
316 |
|
|
// as the rsp_source will drop
|
317 |
|
|
// -----------------------------------------------------------------------------
|
318 |
|
|
|
319 |
|
|
assign response_sink_accepted = rsp_sink_valid && rsp_sink_ready && rsp_sink_endofpacket;
|
320 |
|
|
// Avoid Qis warning when incase, no REORDER, the signal reorder_mem_valid is not in used.
|
321 |
|
|
wire reorder_mem_out_valid;
|
322 |
|
|
wire reorder_mem_valid;
|
323 |
|
|
generate
|
324 |
|
|
if (REORDER) begin
|
325 |
|
|
assign reorder_mem_out_valid = reorder_mem_valid;
|
326 |
|
|
end else begin
|
327 |
|
|
assign reorder_mem_out_valid = '0;
|
328 |
|
|
end
|
329 |
|
|
endgenerate
|
330 |
|
|
|
331 |
|
|
assign response_src_accepted = reorder_mem_out_valid & rsp_src_ready & rsp_src_endofpacket;
|
332 |
|
|
assign response_accepted = (REORDER == 1) ? response_src_accepted : response_sink_accepted;
|
333 |
|
|
|
334 |
|
|
always @* begin
|
335 |
|
|
next_pending_response_count = pending_response_count;
|
336 |
|
|
|
337 |
|
|
if (nonposted_cmd_accepted)
|
338 |
|
|
next_pending_response_count = pending_response_count + 1'b1;
|
339 |
|
|
if (response_accepted)
|
340 |
|
|
next_pending_response_count = pending_response_count - 1'b1;
|
341 |
|
|
if (nonposted_cmd_accepted && response_accepted)
|
342 |
|
|
next_pending_response_count = pending_response_count;
|
343 |
|
|
end
|
344 |
|
|
|
345 |
|
|
assign count_is_1 = (pending_response_count == 1);
|
346 |
|
|
assign count_is_0 = (pending_response_count == 0);
|
347 |
|
|
// ------------------------------------------------------------------
|
348 |
|
|
// count_max_reached : count if maximum command reach to backpressure
|
349 |
|
|
// ------------------------------------------------------------------
|
350 |
|
|
reg count_max_reached;
|
351 |
|
|
always @(posedge clk, posedge reset) begin
|
352 |
|
|
if (reset) begin
|
353 |
|
|
pending_response_count <= 0;
|
354 |
|
|
has_pending_responses <= 0;
|
355 |
|
|
count_max_reached <= 0;
|
356 |
|
|
end
|
357 |
|
|
else begin
|
358 |
|
|
pending_response_count <= next_pending_response_count;
|
359 |
|
|
// synthesis translate_off
|
360 |
|
|
if (count_is_0 && response_accepted)
|
361 |
|
|
$display("%t: %m: Error: unexpected response: pending_response_count underflow", $time());
|
362 |
|
|
// synthesis translate_on
|
363 |
|
|
has_pending_responses <= has_pending_responses
|
364 |
|
|
&& ~(count_is_1 && response_accepted && ~nonposted_cmd_accepted)
|
365 |
|
|
|| (count_is_0 && nonposted_cmd_accepted && ~response_accepted);
|
366 |
|
|
count_max_reached <= (next_pending_response_count == MAX_OUTSTANDING_RESPONSES);
|
367 |
|
|
|
368 |
|
|
end
|
369 |
|
|
end
|
370 |
|
|
|
371 |
|
|
wire suppress_prevent_harzard_for_particular_destid;
|
372 |
|
|
wire this_destid_trans_changed;
|
373 |
|
|
genvar j;
|
374 |
|
|
generate
|
375 |
|
|
if (REORDER) begin: fifo_dest_id_write_read_control_reorder_on
|
376 |
|
|
wire [COUNTER_W - 1 : 0] current_trans_seq_of_this_destid;
|
377 |
|
|
wire [MAX_DEST_ID - 1 : 0] current_trans_seq_of_this_destid_valid;
|
378 |
|
|
wire [MAX_DEST_ID - 1 : 0] responses_arrived;
|
379 |
|
|
reg [COUNTER_W - 1:0] trans_sequence;
|
380 |
|
|
wire [MAX_DEST_ID - 1 : 0] trans_sequence_we;
|
381 |
|
|
|
382 |
|
|
wire [COUNTER_W : 0] trans_sequence_plus_trans_type;
|
383 |
|
|
wire current_trans_type_of_this_destid;
|
384 |
|
|
wire [COUNTER_W : 0] current_trans_seq_of_this_destid_plus_trans_type [MAX_DEST_ID];
|
385 |
|
|
// ------------------------------------------------------------
|
386 |
|
|
// Control write trans_sequence to fifos
|
387 |
|
|
//
|
388 |
|
|
// 1. when command accepted, read destid from command packet,
|
389 |
|
|
// write this id to the fifo (each fifo for each desitid)
|
390 |
|
|
// 2. when response acepted, read the destid from response packet,
|
391 |
|
|
// will know which sequence of this response, write it to
|
392 |
|
|
// correct segment in memory.
|
393 |
|
|
// what if two commands go to same slave, the two sequences
|
394 |
|
|
// go time same fifo, this even helps us to maintain order
|
395 |
|
|
// when two commands same thread to one slave.
|
396 |
|
|
// -----------------------------------------------------------
|
397 |
|
|
wire [DEST_ID_W - 1 : 0] rsp_sink_dest_id;
|
398 |
|
|
wire [DEST_ID_W - 1 : 0] cmd_dest_id;
|
399 |
|
|
assign rsp_sink_dest_id = rsp_sink_data[PKT_SRC_ID_H : PKT_SRC_ID_L];
|
400 |
|
|
|
401 |
|
|
// write in fifo the trans_sequence and type of transaction
|
402 |
|
|
assign trans_sequence_plus_trans_type = {stage2_data[PKT_TRANS_WRITE], trans_sequence};
|
403 |
|
|
|
404 |
|
|
// read the cmd_dest_id from output of pipeline stage so that either
|
405 |
|
|
// or not, it wont affect how we write to fifo
|
406 |
|
|
assign cmd_dest_id = stage2_data[PKT_DEST_ID_H : PKT_DEST_ID_L];
|
407 |
|
|
// -------------------------------------
|
408 |
|
|
// Get the transaction_seq for that dest_id
|
409 |
|
|
// -------------------------------------
|
410 |
|
|
wire [COUNTER_W - 1: 0] trans_sequence_rsp;
|
411 |
|
|
wire [COUNTER_W : 0] trans_sequence_rsp_plus_trans_type;
|
412 |
|
|
wire [COUNTER_W - 1: 0] trans_sequence_rsp_this_destid_waiting;
|
413 |
|
|
wire [COUNTER_W : 0] sequence_and_trans_type_this_destid_waiting;
|
414 |
|
|
wire trans_sequence_rsp_this_destid_waiting_valid;
|
415 |
|
|
assign trans_sequence_rsp_plus_trans_type = current_trans_seq_of_this_destid_plus_trans_type[rsp_sink_dest_id];
|
416 |
|
|
assign trans_sequence_rsp = trans_sequence_rsp_plus_trans_type[COUNTER_W - 1: 0];
|
417 |
|
|
|
418 |
|
|
// do I need to check if this fifo is valid, it should be always valid, unless a command not yet sent
|
419 |
|
|
// and response comes back which means something weird happens.
|
420 |
|
|
// It is worth to do an assertion but now to avoid QIS warning, just do as normal ST handshaking
|
421 |
|
|
// check valid and ready
|
422 |
|
|
|
423 |
|
|
for (j = 0; j < MAX_DEST_ID; j = j+1)
|
424 |
|
|
begin : write_and_read_trans_sequence
|
425 |
|
|
assign trans_sequence_we[j] = (cmd_dest_id == j) && nonposted_cmd_accepted;
|
426 |
|
|
assign responses_arrived[j] = (rsp_sink_dest_id == j) && response_sink_accepted;
|
427 |
|
|
end
|
428 |
|
|
|
429 |
|
|
// --------------------------------------------------------------------
|
430 |
|
|
// This is array of fifos, which will be created base on how many slaves
|
431 |
|
|
// that this master can see (max dest_id_width)
|
432 |
|
|
// Each fifo, will store the trans_sequence, which go to that slave
|
433 |
|
|
// On the response path, based in the response from which slave
|
434 |
|
|
// the fifo of that slave will be read, to check the sequences.
|
435 |
|
|
// and this sequence is the write address to the memory
|
436 |
|
|
// -----------------------------------------------------------------------------------
|
437 |
|
|
// There are 3 sequences run around the limiter, they have a relationship
|
438 |
|
|
// And this is how the key point of reorder work:
|
439 |
|
|
//
|
440 |
|
|
// trans_sequence : command sequence, each command go thru the limiter
|
441 |
|
|
// will have a sequence to show their order. A simple
|
442 |
|
|
// counter from 0 go up and repeat.
|
443 |
|
|
// trans_sequence_rsp : response sequence, each response that go back to limiter,
|
444 |
|
|
// will be read from trans_fifos to know their sequence.
|
445 |
|
|
// expect_trans_sequence : Expected sequences for response that the master is waiting
|
446 |
|
|
// The limiter will hold this sequence and wait until exactly response
|
447 |
|
|
// for this sequence come back (trans_sequence_rsp)
|
448 |
|
|
// aka: if trans_sequence_rsp back is same as expect_trans_sequence
|
449 |
|
|
// then it is correct order, else response store in memory and
|
450 |
|
|
// send out to master later, when expect_trans_sequence match.
|
451 |
|
|
// ------------------------------------------------------------------------------------
|
452 |
|
|
for (j = 0;j < MAX_DEST_ID; j = j+1) begin : trans_sequence_per_fifo
|
453 |
|
|
altera_avalon_sc_fifo
|
454 |
|
|
#(
|
455 |
|
|
.SYMBOLS_PER_BEAT (1),
|
456 |
|
|
.BITS_PER_SYMBOL (COUNTER_W + 1), // one bit extra to store type of transaction
|
457 |
|
|
.FIFO_DEPTH (MAX_OUTSTANDING_RESPONSES),
|
458 |
|
|
.CHANNEL_WIDTH (0),
|
459 |
|
|
.ERROR_WIDTH (0),
|
460 |
|
|
.USE_PACKETS (0),
|
461 |
|
|
.USE_FILL_LEVEL (0),
|
462 |
|
|
.EMPTY_LATENCY (1),
|
463 |
|
|
.USE_MEMORY_BLOCKS (0),
|
464 |
|
|
.USE_STORE_FORWARD (0),
|
465 |
|
|
.USE_ALMOST_FULL_IF (0),
|
466 |
|
|
.USE_ALMOST_EMPTY_IF (0)
|
467 |
|
|
) dest_id_fifo
|
468 |
|
|
(
|
469 |
|
|
.clk (clk),
|
470 |
|
|
.reset (reset),
|
471 |
|
|
.in_data (trans_sequence_plus_trans_type),
|
472 |
|
|
.in_valid (trans_sequence_we[j]),
|
473 |
|
|
.in_ready (),
|
474 |
|
|
.out_data (current_trans_seq_of_this_destid_plus_trans_type[j]),
|
475 |
|
|
.out_valid (current_trans_seq_of_this_destid_valid[j]),
|
476 |
|
|
.out_ready (responses_arrived[j]),
|
477 |
|
|
.csr_address (2'b00), // (terminated)
|
478 |
|
|
.csr_read (1'b0), // (terminated)
|
479 |
|
|
.csr_write (1'b0), // (terminated)
|
480 |
|
|
.csr_readdata (), // (terminated)
|
481 |
|
|
.csr_writedata (32'b00000000000000000000000000000000), // (terminated)
|
482 |
|
|
.almost_full_data (), // (terminated)
|
483 |
|
|
.almost_empty_data (), // (terminated)
|
484 |
|
|
.in_startofpacket (1'b0), // (terminated)
|
485 |
|
|
.in_endofpacket (1'b0), // (terminated)
|
486 |
|
|
.out_startofpacket (), // (terminated)
|
487 |
|
|
.out_endofpacket (), // (terminated)
|
488 |
|
|
.in_empty (1'b0), // (terminated)
|
489 |
|
|
.out_empty (), // (terminated)
|
490 |
|
|
.in_error (1'b0), // (terminated)
|
491 |
|
|
.out_error (), // (terminated)
|
492 |
|
|
.in_channel (1'b0), // (terminated)
|
493 |
|
|
.out_channel () // (terminated)
|
494 |
|
|
);
|
495 |
|
|
end // block: trans_sequence_per_fifo
|
496 |
|
|
|
497 |
|
|
// -------------------------------------------------------
|
498 |
|
|
// Calculate the transaction sequence, just simple increase
|
499 |
|
|
// when each commands pass by
|
500 |
|
|
// --------------------------------------------------------
|
501 |
|
|
always @(posedge clk or posedge reset)
|
502 |
|
|
begin
|
503 |
|
|
if (reset) begin
|
504 |
|
|
trans_sequence <= '0;
|
505 |
|
|
end else begin
|
506 |
|
|
if (nonposted_cmd_accepted)
|
507 |
|
|
trans_sequence <= ( (trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : trans_sequence + 1'b1;
|
508 |
|
|
end
|
509 |
|
|
end
|
510 |
|
|
|
511 |
|
|
// -------------------------------------
|
512 |
|
|
// Control Memory for reorder responses
|
513 |
|
|
// -------------------------------------
|
514 |
|
|
wire [COUNTER_W - 1 : 0] next_rd_trans_sequence;
|
515 |
|
|
reg [COUNTER_W - 1 : 0] rd_trans_sequence;
|
516 |
|
|
reg [COUNTER_W - 1 : 0] next_expected_trans_sequence;
|
517 |
|
|
reg [COUNTER_W - 1 : 0] expect_trans_sequence;
|
518 |
|
|
wire [ST_DATA_W - 1 : 0] reorder_mem_data;
|
519 |
|
|
wire [ST_CHANNEL_W - 1 : 0] reorder_mem_channel;
|
520 |
|
|
wire reorder_mem_startofpacket;
|
521 |
|
|
wire reorder_mem_endofpacket;
|
522 |
|
|
wire reorder_mem_ready;
|
523 |
|
|
// -------------------------------------------
|
524 |
|
|
// Data to write and read from reorder memory
|
525 |
|
|
// Store everything includes channel, sop, eop
|
526 |
|
|
// -------------------------------------------
|
527 |
|
|
reg [MEM_W - 1 : 0] mem_in_rsp_sink_data;
|
528 |
|
|
reg [MEM_W - 1 : 0] reorder_mem_out_data;
|
529 |
|
|
always_comb
|
530 |
|
|
begin
|
531 |
|
|
mem_in_rsp_sink_data = {rsp_sink_data, rsp_sink_channel, rsp_sink_startofpacket, rsp_sink_endofpacket};
|
532 |
|
|
end
|
533 |
|
|
|
534 |
|
|
assign next_rd_trans_sequence = ((rd_trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : rd_trans_sequence + 1'b1;
|
535 |
|
|
assign next_expected_trans_sequence = ((expect_trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : expect_trans_sequence + 1'b1;
|
536 |
|
|
|
537 |
|
|
always_ff @(posedge clk, posedge reset)
|
538 |
|
|
begin
|
539 |
|
|
if (reset) begin
|
540 |
|
|
rd_trans_sequence <= '0;
|
541 |
|
|
expect_trans_sequence <= '0;
|
542 |
|
|
end else begin
|
543 |
|
|
if (rsp_src_ready && reorder_mem_valid) begin
|
544 |
|
|
if (reorder_mem_endofpacket == 1) begin //endofpacket
|
545 |
|
|
expect_trans_sequence <= next_expected_trans_sequence;
|
546 |
|
|
rd_trans_sequence <= next_rd_trans_sequence;
|
547 |
|
|
end
|
548 |
|
|
end
|
549 |
|
|
end
|
550 |
|
|
end // always_ff @
|
551 |
|
|
|
552 |
|
|
// For PREVENT_HAZARD,
|
553 |
|
|
// Case: Master Write to S0, read S1, and Read S0 back but if Write for S0
|
554 |
|
|
// not yet return then we need to backpressure this, else read S0 might take over write
|
555 |
|
|
// This is more checking after the fifo destid, as read S1 is inserted in midle
|
556 |
|
|
// when see new packet, try to look at the fifo for that slave id, check if it
|
557 |
|
|
// type of transaction
|
558 |
|
|
assign sequence_and_trans_type_this_destid_waiting = current_trans_seq_of_this_destid_plus_trans_type[cmd_dest_id];
|
559 |
|
|
assign current_trans_type_of_this_destid = sequence_and_trans_type_this_destid_waiting[COUNTER_W];
|
560 |
|
|
assign trans_sequence_rsp_this_destid_waiting_valid = current_trans_seq_of_this_destid_valid[cmd_dest_id];
|
561 |
|
|
// it might waiting other sequence, check if different type of transaction as only for PREVENT HAZARD
|
562 |
|
|
// if comming comamnd to one slave and this slave is still waiting for response from previous command
|
563 |
|
|
// which has diiferent type of transaction, we back-pressure this command to avoid HAZARD
|
564 |
|
|
assign suppress_prevent_harzard_for_particular_destid = (current_trans_type_of_this_destid != is_write) & trans_sequence_rsp_this_destid_waiting_valid;
|
565 |
|
|
|
566 |
|
|
// -------------------------------------
|
567 |
|
|
// Memory for reorder buffer
|
568 |
|
|
// -------------------------------------
|
569 |
|
|
altera_merlin_reorder_memory
|
570 |
|
|
#(
|
571 |
|
|
.DATA_W (MEM_W),
|
572 |
|
|
.ADDR_H_W (COUNTER_W),
|
573 |
|
|
.ADDR_L_W (MAX_BURST_LENGTH),
|
574 |
|
|
.NUM_SEGMENT (MAX_OUTSTANDING_RESPONSES),
|
575 |
|
|
.DEPTH (MEM_DEPTH)
|
576 |
|
|
) reorder_memory
|
577 |
|
|
(
|
578 |
|
|
.clk (clk),
|
579 |
|
|
.reset (reset),
|
580 |
|
|
.in_data (mem_in_rsp_sink_data),
|
581 |
|
|
.in_valid (rsp_sink_valid),
|
582 |
|
|
.in_ready (reorder_mem_ready),
|
583 |
|
|
.out_data (reorder_mem_out_data),
|
584 |
|
|
.out_valid (reorder_mem_valid),
|
585 |
|
|
.out_ready (rsp_src_ready),
|
586 |
|
|
.wr_segment (trans_sequence_rsp),
|
587 |
|
|
.rd_segment (expect_trans_sequence)
|
588 |
|
|
);
|
589 |
|
|
// -------------------------------------
|
590 |
|
|
// Output from reorder buffer
|
591 |
|
|
// -------------------------------------
|
592 |
|
|
assign reorder_mem_data = reorder_mem_out_data[MEM_W -1 : ST_CHANNEL_W + 2];
|
593 |
|
|
assign reorder_mem_channel = reorder_mem_out_data[ST_CHANNEL_W + 2 - 1 : 2];
|
594 |
|
|
assign reorder_mem_startofpacket = reorder_mem_out_data[1];
|
595 |
|
|
assign reorder_mem_endofpacket = reorder_mem_out_data[0];
|
596 |
|
|
|
597 |
|
|
// -------------------------------------
|
598 |
|
|
// Because use generate statment
|
599 |
|
|
// so move all rsp_src_xxx controls here
|
600 |
|
|
// -------------------------------------
|
601 |
|
|
always_comb begin
|
602 |
|
|
cmd_src_data = stage2_data;
|
603 |
|
|
rsp_src_valid = reorder_mem_valid;
|
604 |
|
|
rsp_src_data = reorder_mem_data;
|
605 |
|
|
rsp_src_channel = reorder_mem_channel;
|
606 |
|
|
rsp_src_startofpacket = reorder_mem_startofpacket;
|
607 |
|
|
rsp_src_endofpacket = reorder_mem_endofpacket;
|
608 |
|
|
// -------------------------------------
|
609 |
|
|
// Forces commands to be non-posted if hazard prevention
|
610 |
|
|
// is on, also drops write responses
|
611 |
|
|
// -------------------------------------
|
612 |
|
|
rsp_sink_ready = reorder_mem_ready; // now it takes ready signal from the memory not direct from master
|
613 |
|
|
if (PREVENT_HAZARDS == 1) begin
|
614 |
|
|
cmd_src_data[PKT_TRANS_POSTED] = 1'b0;
|
615 |
|
|
|
616 |
|
|
if (rsp_src_data[PKT_TRANS_WRITE] == 1'b1 && SUPPORTS_POSTED_WRITES == 1 && SUPPORTS_NONPOSTED_WRITES == 0) begin
|
617 |
|
|
rsp_src_valid = 1'b0;
|
618 |
|
|
rsp_sink_ready = 1'b1;
|
619 |
|
|
end
|
620 |
|
|
end
|
621 |
|
|
end // always_comb
|
622 |
|
|
|
623 |
|
|
end // block: fifo_dest_id_write_read_control_reorder_on
|
624 |
|
|
endgenerate
|
625 |
|
|
|
626 |
|
|
// -------------------------------------
|
627 |
|
|
// Pass-through command and response
|
628 |
|
|
// -------------------------------------
|
629 |
|
|
|
630 |
|
|
always_comb
|
631 |
|
|
begin
|
632 |
|
|
cmd_src_channel = stage2_channel;
|
633 |
|
|
cmd_src_startofpacket = stage2_startofpacket;
|
634 |
|
|
cmd_src_endofpacket = stage2_endofpacket;
|
635 |
|
|
end // always_comb
|
636 |
|
|
|
637 |
|
|
// -------------------------------------
|
638 |
|
|
// When there is no REORDER requirement
|
639 |
|
|
// Just pass through signals
|
640 |
|
|
// -------------------------------------
|
641 |
|
|
generate
|
642 |
|
|
if (!REORDER) begin : use_selector_or_pass_thru_rsp
|
643 |
|
|
always_comb begin
|
644 |
|
|
cmd_src_data = stage2_data;
|
645 |
|
|
// pass thru almost signals
|
646 |
|
|
rsp_src_valid = rsp_sink_valid;
|
647 |
|
|
rsp_src_data = rsp_sink_data;
|
648 |
|
|
rsp_src_channel = rsp_sink_channel;
|
649 |
|
|
rsp_src_startofpacket = rsp_sink_startofpacket;
|
650 |
|
|
rsp_src_endofpacket = rsp_sink_endofpacket;
|
651 |
|
|
// -------------------------------------
|
652 |
|
|
// Forces commands to be non-posted if hazard prevention
|
653 |
|
|
// is on, also drops write responses
|
654 |
|
|
// -------------------------------------
|
655 |
|
|
rsp_sink_ready = rsp_src_ready; // take care this, should check memory empty
|
656 |
|
|
if (PREVENT_HAZARDS == 1) begin
|
657 |
|
|
cmd_src_data[PKT_TRANS_POSTED] = 1'b0;
|
658 |
|
|
|
659 |
|
|
if (rsp_sink_data[PKT_TRANS_WRITE] == 1'b1 && SUPPORTS_POSTED_WRITES == 1 && SUPPORTS_NONPOSTED_WRITES == 0) begin
|
660 |
|
|
rsp_src_valid = 1'b0;
|
661 |
|
|
rsp_sink_ready = 1'b1;
|
662 |
|
|
end
|
663 |
|
|
end
|
664 |
|
|
end // always_comb
|
665 |
|
|
end // if (!REORDER)
|
666 |
|
|
endgenerate
|
667 |
|
|
|
668 |
|
|
// --------------------------------------------------------
|
669 |
|
|
// Backpressure & Suppression
|
670 |
|
|
// --------------------------------------------------------
|
671 |
|
|
// ENFORCE_ORDER: unused option, always is 1, remove it
|
672 |
|
|
// Now the limiter will suppress when max_outstanding reach
|
673 |
|
|
// --------------------------------------------------------
|
674 |
|
|
generate
|
675 |
|
|
if (ENFORCE_ORDER) begin : enforce_order_block
|
676 |
|
|
assign suppress_change_dest_id = (REORDER == 1) ? 1'b0 : nonposted_cmd && has_pending_responses &&
|
677 |
|
|
(stage2_dest_changed || (PREVENT_HAZARDS == 1 && stage2_trans_changed));
|
678 |
|
|
end else begin : no_order_block
|
679 |
|
|
assign suppress_change_dest_id = 1'b0;
|
680 |
|
|
end
|
681 |
|
|
endgenerate
|
682 |
|
|
|
683 |
|
|
// ------------------------------------------------------------
|
684 |
|
|
// Even we allow change slave while still have pending responses
|
685 |
|
|
// But one special case, when PREVENT_HAZARD=1, we still allow
|
686 |
|
|
// switch slave while type of transaction change (RAW, WAR) but
|
687 |
|
|
// only to different slaves.
|
688 |
|
|
// if to same slave, we still need back pressure that to make
|
689 |
|
|
// sure no racing
|
690 |
|
|
// ------------------------------------------------------------
|
691 |
|
|
|
692 |
|
|
generate
|
693 |
|
|
if (REORDER) begin : prevent_hazard_block
|
694 |
|
|
assign suppress_change_trans_but_not_dest = nonposted_cmd && has_pending_responses &&
|
695 |
|
|
!stage2_dest_changed && (PREVENT_HAZARDS == 1 && stage2_trans_changed);
|
696 |
|
|
end else begin : no_hazard_block
|
697 |
|
|
assign suppress_change_trans_but_not_dest = 1'b0; // no REORDER, the suppress_changes_destid take care of this.
|
698 |
|
|
end
|
699 |
|
|
endgenerate
|
700 |
|
|
|
701 |
|
|
generate
|
702 |
|
|
if (REORDER) begin : prevent_hazard_block_for_particular_slave
|
703 |
|
|
assign suppress_change_trans_for_one_slave = nonposted_cmd && has_pending_responses && (PREVENT_HAZARDS == 1 && suppress_prevent_harzard_for_particular_destid);
|
704 |
|
|
end else begin : no_hazard_block_for_particular_slave
|
705 |
|
|
assign suppress_change_trans_for_one_slave = 1'b0; // no REORDER, the suppress_changes_destid take care of this.
|
706 |
|
|
end
|
707 |
|
|
endgenerate
|
708 |
|
|
|
709 |
|
|
// ------------------------------------------
|
710 |
|
|
// Backpressure when max outstanding transactions are reached
|
711 |
|
|
// ------------------------------------------
|
712 |
|
|
generate
|
713 |
|
|
if (REORDER) begin : max_outstanding_block
|
714 |
|
|
assign suppress_max_outstanding = count_max_reached;
|
715 |
|
|
end else begin
|
716 |
|
|
assign suppress_max_outstanding = 1'b0;
|
717 |
|
|
end
|
718 |
|
|
endgenerate
|
719 |
|
|
|
720 |
|
|
assign suppress = suppress_change_trans_for_one_slave | suppress_change_dest_id | suppress_max_outstanding;
|
721 |
|
|
assign wide_valid = { VALID_WIDTH {stage2_valid} } & stage2_channel;
|
722 |
|
|
|
723 |
|
|
always @* begin
|
724 |
|
|
stage2_ready = cmd_src_ready;
|
725 |
|
|
internal_valid = stage2_valid;
|
726 |
|
|
// --------------------------------------------------------
|
727 |
|
|
// change suppress condidtion, in case REODER it will alllow changing slave
|
728 |
|
|
// even still have pending transactions.
|
729 |
|
|
// -------------------------------------------------------
|
730 |
|
|
if (suppress) begin
|
731 |
|
|
stage2_ready = 0;
|
732 |
|
|
internal_valid = 0;
|
733 |
|
|
end
|
734 |
|
|
|
735 |
|
|
if (VALID_WIDTH == 1) begin
|
736 |
|
|
cmd_src_valid = {VALID_WIDTH{1'b0}};
|
737 |
|
|
cmd_src_valid[0] = internal_valid;
|
738 |
|
|
end else begin
|
739 |
|
|
// -------------------------------------
|
740 |
|
|
// Use the one-hot channel to determine if the destination
|
741 |
|
|
// has changed. This results in a wide valid bus
|
742 |
|
|
// -------------------------------------
|
743 |
|
|
cmd_src_valid = wide_valid;
|
744 |
|
|
if (nonposted_cmd & has_pending_responses) begin
|
745 |
|
|
if (!REORDER) begin
|
746 |
|
|
cmd_src_valid = wide_valid & last_channel;
|
747 |
|
|
// -------------------------------------
|
748 |
|
|
// Mask the valid signals if the transaction type has changed
|
749 |
|
|
// if hazard prevention is enabled
|
750 |
|
|
// -------------------------------------
|
751 |
|
|
if (PREVENT_HAZARDS == 1)
|
752 |
|
|
cmd_src_valid = wide_valid & last_channel & { VALID_WIDTH {!stage2_trans_changed} };
|
753 |
|
|
end else begin // else: !if(!REORDER) if REORDER happen
|
754 |
|
|
if (PREVENT_HAZARDS == 1)
|
755 |
|
|
cmd_src_valid = wide_valid & { VALID_WIDTH {!suppress_change_trans_for_one_slave} };
|
756 |
|
|
if (suppress_max_outstanding) begin
|
757 |
|
|
cmd_src_valid = {VALID_WIDTH {1'b0}};
|
758 |
|
|
end
|
759 |
|
|
|
760 |
|
|
end
|
761 |
|
|
end
|
762 |
|
|
end
|
763 |
|
|
end
|
764 |
|
|
|
765 |
|
|
// --------------------------------------------------
|
766 |
|
|
// Calculates the log2ceil of the input value.
|
767 |
|
|
//
|
768 |
|
|
// This function occurs a lot... please refactor.
|
769 |
|
|
// --------------------------------------------------
|
770 |
|
|
function integer log2ceil;
|
771 |
|
|
input integer val;
|
772 |
|
|
integer i;
|
773 |
|
|
|
774 |
|
|
begin
|
775 |
|
|
i = 1;
|
776 |
|
|
log2ceil = 0;
|
777 |
|
|
|
778 |
|
|
while (i < val) begin
|
779 |
|
|
log2ceil = log2ceil + 1;
|
780 |
|
|
i = i << 1;
|
781 |
|
|
end
|
782 |
|
|
end
|
783 |
|
|
endfunction
|
784 |
|
|
|
785 |
|
|
endmodule
|
786 |
|
|
|
787 |
|
|
|