mor1kx-bemicrocv/ip/altera/ddr3/altera_merlin_traffic_limiter.sv

788 lines
36 KiB
Systemverilog
Raw Normal View History

2016-08-04 19:22:38 +02:00
// (C) 2001-2015 Altera Corporation. All rights reserved.
// Your use of Altera Corporation's design tools, logic functions and other
// software and tools, and its AMPP partner logic functions, and any output
// files any of the foregoing (including device programming or simulation
// files), and any associated documentation or information are expressly subject
// to the terms and conditions of the Altera Program License Subscription
// Agreement, Altera MegaCore Function License Agreement, or other applicable
// license agreement, including, without limitation, that your use is for the
// sole purpose of programming logic devices manufactured by Altera and sold by
// Altera or its authorized distributors. Please refer to the applicable
// agreement for further details.
// $Id: //acds/rel/15.1/ip/merlin/altera_merlin_traffic_limiter/altera_merlin_traffic_limiter.sv#1 $
// $Revision: #1 $
// $Date: 2015/08/09 $
// $Author: swbranch $
// -----------------------------------------------------
// Merlin Traffic Limiter
//
// Ensures that non-posted transaction responses are returned
// in order of request. Out-of-order responses can happen
// when a master does a non-posted transaction on a slave
// while responses are pending from a different slave.
//
// Examples:
// 1) read to any latent slave, followed by a read to a
// variable-latent slave
// 2) read to any fixed-latency slave, followed by a read
// to another fixed-latency slave whose fixed latency is smaller.
// 3) non-posted write to any latent slave, followed by a non-posted
// write or read to any variable-latent slave.
//
// This component has two implementation modes that ensure
// response order, controlled by the REORDER parameter.
//
// 0) Backpressure to prevent a master from switching slaves
// until all outstanding responses have returned. We also
// have to suppress the non-posted transaction, obviously.
//
// 1) Reorder the responses as they return using a memory
// block.
// -----------------------------------------------------
`timescale 1 ns / 1 ns
// altera message_off 10036
module altera_merlin_traffic_limiter
#(
parameter
PKT_TRANS_POSTED = 1,
PKT_DEST_ID_H = 0,
PKT_DEST_ID_L = 0,
PKT_SRC_ID_H = 0,
PKT_SRC_ID_L = 0,
PKT_BYTE_CNT_H = 0,
PKT_BYTE_CNT_L = 0,
PKT_BYTEEN_H = 0,
PKT_BYTEEN_L = 0,
PKT_TRANS_WRITE = 0,
PKT_TRANS_READ = 0,
ST_DATA_W = 72,
ST_CHANNEL_W = 32,
MAX_OUTSTANDING_RESPONSES = 1,
PIPELINED = 0,
ENFORCE_ORDER = 1,
// -------------------------------------
// internal: allows optimization between this
// component and the demux
// -------------------------------------
VALID_WIDTH = 1,
// -------------------------------------
// Prevents all RAW and WAR hazards by waiting for
// responses to return before issuing a command
// with different direction.
//
// This is intended for Avalon masters which are
// connected to AXI slaves, because of the differing
// ordering models for the protocols.
//
// If PREVENT_HAZARDS is 1, then the current implementation
// needs to know whether incoming writes will be posted or
// not at compile-time. Only one of SUPPORTS_POSTED_WRITES
// and SUPPORTS_NONPOSTED_WRITES can be 1.
//
// When PREVENT_HAZARDS is 0 there is no such restriction.
//
// It is possible to be less restrictive for memories.
// -------------------------------------
PREVENT_HAZARDS = 0,
// -------------------------------------
// Used only when hazard prevention is on, but may be used
// for optimization work in the future.
// -------------------------------------
SUPPORTS_POSTED_WRITES = 1,
SUPPORTS_NONPOSTED_WRITES = 0,
// -------------------------------------------------
// Enables the reorder buffer which allows a master to
// switch slaves while responses are pending.
// Reponses will be reordered following command issue order.
// -------------------------------------------------
REORDER = 0
)
(
// -------------------
// Clock & Reset
// -------------------
input clk,
input reset,
// -------------------
// Command
// -------------------
input cmd_sink_valid,
input [ST_DATA_W-1 : 0] cmd_sink_data,
input [ST_CHANNEL_W-1 : 0] cmd_sink_channel,
input cmd_sink_startofpacket,
input cmd_sink_endofpacket,
output cmd_sink_ready,
output reg [VALID_WIDTH-1 : 0] cmd_src_valid,
output reg [ST_DATA_W-1 : 0] cmd_src_data,
output reg [ST_CHANNEL_W-1 : 0] cmd_src_channel,
output reg cmd_src_startofpacket,
output reg cmd_src_endofpacket,
input cmd_src_ready,
// -------------------
// Response
// -------------------
input rsp_sink_valid,
input [ST_DATA_W-1 : 0] rsp_sink_data,
input [ST_CHANNEL_W-1 : 0] rsp_sink_channel,
input rsp_sink_startofpacket,
input rsp_sink_endofpacket,
output reg rsp_sink_ready,
output reg rsp_src_valid,
output reg [ST_DATA_W-1 : 0] rsp_src_data,
output reg [ST_CHANNEL_W-1 : 0] rsp_src_channel,
output reg rsp_src_startofpacket,
output reg rsp_src_endofpacket,
input rsp_src_ready
);
// -------------------------------------
// Local Parameters
// -------------------------------------
localparam DEST_ID_W = PKT_DEST_ID_H - PKT_DEST_ID_L + 1;
localparam COUNTER_W = log2ceil(MAX_OUTSTANDING_RESPONSES + 1);
localparam PAYLOAD_W = ST_DATA_W + ST_CHANNEL_W + 4;
localparam NUMSYMBOLS = PKT_BYTEEN_H - PKT_BYTEEN_L + 1;
localparam MAX_DEST_ID = 1 << (DEST_ID_W);
localparam PKT_BYTE_CNT_W = PKT_BYTE_CNT_H - PKT_BYTE_CNT_L + 1;
// -------------------------------------------------------
// Memory Parameters
// ------------------------------------------------------
localparam MAX_BYTE_CNT = 1 << (PKT_BYTE_CNT_W);
localparam MAX_BURST_LENGTH = log2ceil(MAX_BYTE_CNT/NUMSYMBOLS);
// Memory stores packet width, including sop and eop
localparam MEM_W = ST_DATA_W + ST_CHANNEL_W + 1 + 1;
localparam MEM_DEPTH = MAX_OUTSTANDING_RESPONSES * (MAX_BYTE_CNT/NUMSYMBOLS);
// -----------------------------------------------------
// Input Stage
//
// Figure out if the destination id has changed
// -----------------------------------------------------
wire stage1_dest_changed;
wire stage1_trans_changed;
wire [PAYLOAD_W-1 : 0] stage1_payload;
wire in_nonposted_cmd;
reg [ST_CHANNEL_W-1:0] last_channel;
wire [DEST_ID_W-1 : 0] dest_id;
reg [DEST_ID_W-1 : 0] last_dest_id;
reg was_write;
wire is_write;
wire suppress;
wire save_dest_id;
wire suppress_change_dest_id;
wire suppress_max_outstanding;
wire suppress_change_trans_but_not_dest;
wire suppress_change_trans_for_one_slave;
generate if (PREVENT_HAZARDS == 1) begin : convert_posted_to_nonposted_block
assign in_nonposted_cmd = 1'b1;
end else begin : non_posted_cmd_assignment_block
assign in_nonposted_cmd = (cmd_sink_data[PKT_TRANS_POSTED] == 0);
end
endgenerate
// ------------------------------------
// Optimization: for the unpipelined case, we can save the destid if
// this is an unsuppressed nonposted command. This eliminates
// dependence on the backpressure signal.
//
// Not a problem for the pipelined case.
// ------------------------------------
generate
if (PIPELINED) begin : pipelined_save_dest_id
assign save_dest_id = cmd_sink_valid & cmd_sink_ready & in_nonposted_cmd;
end else begin : unpipelined_save_dest_id
assign save_dest_id = cmd_sink_valid & ~(suppress_change_dest_id | suppress_max_outstanding) & in_nonposted_cmd;
end
endgenerate
always @(posedge clk, posedge reset) begin
if (reset) begin
last_dest_id <= 0;
last_channel <= 0;
was_write <= 0;
end
else if (save_dest_id) begin
last_dest_id <= dest_id;
last_channel <= cmd_sink_channel;
was_write <= is_write;
end
end
assign dest_id = cmd_sink_data[PKT_DEST_ID_H:PKT_DEST_ID_L];
assign is_write = cmd_sink_data[PKT_TRANS_WRITE];
assign stage1_dest_changed = (last_dest_id != dest_id);
assign stage1_trans_changed = (was_write != is_write);
assign stage1_payload = {
cmd_sink_data,
cmd_sink_channel,
cmd_sink_startofpacket,
cmd_sink_endofpacket,
stage1_dest_changed,
stage1_trans_changed };
// -----------------------------------------------------
// (Optional) pipeline between input and output
// -----------------------------------------------------
wire stage2_valid;
reg stage2_ready;
wire [PAYLOAD_W-1 : 0] stage2_payload;
generate
if (PIPELINED == 1) begin : pipelined_limiter
altera_avalon_st_pipeline_base
#(
.BITS_PER_SYMBOL(PAYLOAD_W)
) stage1_pipe (
.clk (clk),
.reset (reset),
.in_ready (cmd_sink_ready),
.in_valid (cmd_sink_valid),
.in_data (stage1_payload),
.out_valid (stage2_valid),
.out_ready (stage2_ready),
.out_data (stage2_payload)
);
end else begin : unpipelined_limiter
assign stage2_valid = cmd_sink_valid;
assign stage2_payload = stage1_payload;
assign cmd_sink_ready = stage2_ready;
end
endgenerate
// -----------------------------------------------------
// Output Stage
// -----------------------------------------------------
wire [ST_DATA_W-1 : 0] stage2_data;
wire [ST_CHANNEL_W-1:0] stage2_channel;
wire stage2_startofpacket;
wire stage2_endofpacket;
wire stage2_dest_changed;
wire stage2_trans_changed;
reg has_pending_responses;
reg [COUNTER_W-1 : 0] pending_response_count;
reg [COUNTER_W-1 : 0] next_pending_response_count;
wire nonposted_cmd;
wire nonposted_cmd_accepted;
wire response_accepted;
wire response_sink_accepted;
wire response_src_accepted;
wire count_is_1;
wire count_is_0;
reg internal_valid;
wire [VALID_WIDTH-1:0] wide_valid;
assign { stage2_data,
stage2_channel,
stage2_startofpacket,
stage2_endofpacket,
stage2_dest_changed,
stage2_trans_changed } = stage2_payload;
generate if (PREVENT_HAZARDS == 1) begin : stage2_nonposted_block
assign nonposted_cmd = 1'b1;
end else begin
assign nonposted_cmd = (stage2_data[PKT_TRANS_POSTED] == 0);
end
endgenerate
assign nonposted_cmd_accepted = nonposted_cmd && internal_valid && (cmd_src_ready && cmd_src_endofpacket);
// -----------------------------------------------------------------------------
// Use the sink's control signals here, because write responses may be dropped
// when hazard prevention is on.
//
// When case REORDER, move all side to rsp_source as all packets from rsp_sink will
// go in the reorder memory.
// One special case when PREVENT_HAZARD is on, need to use reorder_memory_valid
// as the rsp_source will drop
// -----------------------------------------------------------------------------
assign response_sink_accepted = rsp_sink_valid && rsp_sink_ready && rsp_sink_endofpacket;
// Avoid Qis warning when incase, no REORDER, the signal reorder_mem_valid is not in used.
wire reorder_mem_out_valid;
wire reorder_mem_valid;
generate
if (REORDER) begin
assign reorder_mem_out_valid = reorder_mem_valid;
end else begin
assign reorder_mem_out_valid = '0;
end
endgenerate
assign response_src_accepted = reorder_mem_out_valid & rsp_src_ready & rsp_src_endofpacket;
assign response_accepted = (REORDER == 1) ? response_src_accepted : response_sink_accepted;
always @* begin
next_pending_response_count = pending_response_count;
if (nonposted_cmd_accepted)
next_pending_response_count = pending_response_count + 1'b1;
if (response_accepted)
next_pending_response_count = pending_response_count - 1'b1;
if (nonposted_cmd_accepted && response_accepted)
next_pending_response_count = pending_response_count;
end
assign count_is_1 = (pending_response_count == 1);
assign count_is_0 = (pending_response_count == 0);
// ------------------------------------------------------------------
// count_max_reached : count if maximum command reach to backpressure
// ------------------------------------------------------------------
reg count_max_reached;
always @(posedge clk, posedge reset) begin
if (reset) begin
pending_response_count <= 0;
has_pending_responses <= 0;
count_max_reached <= 0;
end
else begin
pending_response_count <= next_pending_response_count;
// synthesis translate_off
if (count_is_0 && response_accepted)
$display("%t: %m: Error: unexpected response: pending_response_count underflow", $time());
// synthesis translate_on
has_pending_responses <= has_pending_responses
&& ~(count_is_1 && response_accepted && ~nonposted_cmd_accepted)
|| (count_is_0 && nonposted_cmd_accepted && ~response_accepted);
count_max_reached <= (next_pending_response_count == MAX_OUTSTANDING_RESPONSES);
end
end
wire suppress_prevent_harzard_for_particular_destid;
wire this_destid_trans_changed;
genvar j;
generate
if (REORDER) begin: fifo_dest_id_write_read_control_reorder_on
wire [COUNTER_W - 1 : 0] current_trans_seq_of_this_destid;
wire [MAX_DEST_ID - 1 : 0] current_trans_seq_of_this_destid_valid;
wire [MAX_DEST_ID - 1 : 0] responses_arrived;
reg [COUNTER_W - 1:0] trans_sequence;
wire [MAX_DEST_ID - 1 : 0] trans_sequence_we;
wire [COUNTER_W : 0] trans_sequence_plus_trans_type;
wire current_trans_type_of_this_destid;
wire [COUNTER_W : 0] current_trans_seq_of_this_destid_plus_trans_type [MAX_DEST_ID];
// ------------------------------------------------------------
// Control write trans_sequence to fifos
//
// 1. when command accepted, read destid from command packet,
// write this id to the fifo (each fifo for each desitid)
// 2. when response acepted, read the destid from response packet,
// will know which sequence of this response, write it to
// correct segment in memory.
// what if two commands go to same slave, the two sequences
// go time same fifo, this even helps us to maintain order
// when two commands same thread to one slave.
// -----------------------------------------------------------
wire [DEST_ID_W - 1 : 0] rsp_sink_dest_id;
wire [DEST_ID_W - 1 : 0] cmd_dest_id;
assign rsp_sink_dest_id = rsp_sink_data[PKT_SRC_ID_H : PKT_SRC_ID_L];
// write in fifo the trans_sequence and type of transaction
assign trans_sequence_plus_trans_type = {stage2_data[PKT_TRANS_WRITE], trans_sequence};
// read the cmd_dest_id from output of pipeline stage so that either
// or not, it wont affect how we write to fifo
assign cmd_dest_id = stage2_data[PKT_DEST_ID_H : PKT_DEST_ID_L];
// -------------------------------------
// Get the transaction_seq for that dest_id
// -------------------------------------
wire [COUNTER_W - 1: 0] trans_sequence_rsp;
wire [COUNTER_W : 0] trans_sequence_rsp_plus_trans_type;
wire [COUNTER_W - 1: 0] trans_sequence_rsp_this_destid_waiting;
wire [COUNTER_W : 0] sequence_and_trans_type_this_destid_waiting;
wire trans_sequence_rsp_this_destid_waiting_valid;
assign trans_sequence_rsp_plus_trans_type = current_trans_seq_of_this_destid_plus_trans_type[rsp_sink_dest_id];
assign trans_sequence_rsp = trans_sequence_rsp_plus_trans_type[COUNTER_W - 1: 0];
// do I need to check if this fifo is valid, it should be always valid, unless a command not yet sent
// and response comes back which means something weird happens.
// It is worth to do an assertion but now to avoid QIS warning, just do as normal ST handshaking
// check valid and ready
for (j = 0; j < MAX_DEST_ID; j = j+1)
begin : write_and_read_trans_sequence
assign trans_sequence_we[j] = (cmd_dest_id == j) && nonposted_cmd_accepted;
assign responses_arrived[j] = (rsp_sink_dest_id == j) && response_sink_accepted;
end
// --------------------------------------------------------------------
// This is array of fifos, which will be created base on how many slaves
// that this master can see (max dest_id_width)
// Each fifo, will store the trans_sequence, which go to that slave
// On the response path, based in the response from which slave
// the fifo of that slave will be read, to check the sequences.
// and this sequence is the write address to the memory
// -----------------------------------------------------------------------------------
// There are 3 sequences run around the limiter, they have a relationship
// And this is how the key point of reorder work:
//
// trans_sequence : command sequence, each command go thru the limiter
// will have a sequence to show their order. A simple
// counter from 0 go up and repeat.
// trans_sequence_rsp : response sequence, each response that go back to limiter,
// will be read from trans_fifos to know their sequence.
// expect_trans_sequence : Expected sequences for response that the master is waiting
// The limiter will hold this sequence and wait until exactly response
// for this sequence come back (trans_sequence_rsp)
// aka: if trans_sequence_rsp back is same as expect_trans_sequence
// then it is correct order, else response store in memory and
// send out to master later, when expect_trans_sequence match.
// ------------------------------------------------------------------------------------
for (j = 0;j < MAX_DEST_ID; j = j+1) begin : trans_sequence_per_fifo
altera_avalon_sc_fifo
#(
.SYMBOLS_PER_BEAT (1),
.BITS_PER_SYMBOL (COUNTER_W + 1), // one bit extra to store type of transaction
.FIFO_DEPTH (MAX_OUTSTANDING_RESPONSES),
.CHANNEL_WIDTH (0),
.ERROR_WIDTH (0),
.USE_PACKETS (0),
.USE_FILL_LEVEL (0),
.EMPTY_LATENCY (1),
.USE_MEMORY_BLOCKS (0),
.USE_STORE_FORWARD (0),
.USE_ALMOST_FULL_IF (0),
.USE_ALMOST_EMPTY_IF (0)
) dest_id_fifo
(
.clk (clk),
.reset (reset),
.in_data (trans_sequence_plus_trans_type),
.in_valid (trans_sequence_we[j]),
.in_ready (),
.out_data (current_trans_seq_of_this_destid_plus_trans_type[j]),
.out_valid (current_trans_seq_of_this_destid_valid[j]),
.out_ready (responses_arrived[j]),
.csr_address (2'b00), // (terminated)
.csr_read (1'b0), // (terminated)
.csr_write (1'b0), // (terminated)
.csr_readdata (), // (terminated)
.csr_writedata (32'b00000000000000000000000000000000), // (terminated)
.almost_full_data (), // (terminated)
.almost_empty_data (), // (terminated)
.in_startofpacket (1'b0), // (terminated)
.in_endofpacket (1'b0), // (terminated)
.out_startofpacket (), // (terminated)
.out_endofpacket (), // (terminated)
.in_empty (1'b0), // (terminated)
.out_empty (), // (terminated)
.in_error (1'b0), // (terminated)
.out_error (), // (terminated)
.in_channel (1'b0), // (terminated)
.out_channel () // (terminated)
);
end // block: trans_sequence_per_fifo
// -------------------------------------------------------
// Calculate the transaction sequence, just simple increase
// when each commands pass by
// --------------------------------------------------------
always @(posedge clk or posedge reset)
begin
if (reset) begin
trans_sequence <= '0;
end else begin
if (nonposted_cmd_accepted)
trans_sequence <= ( (trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : trans_sequence + 1'b1;
end
end
// -------------------------------------
// Control Memory for reorder responses
// -------------------------------------
wire [COUNTER_W - 1 : 0] next_rd_trans_sequence;
reg [COUNTER_W - 1 : 0] rd_trans_sequence;
reg [COUNTER_W - 1 : 0] next_expected_trans_sequence;
reg [COUNTER_W - 1 : 0] expect_trans_sequence;
wire [ST_DATA_W - 1 : 0] reorder_mem_data;
wire [ST_CHANNEL_W - 1 : 0] reorder_mem_channel;
wire reorder_mem_startofpacket;
wire reorder_mem_endofpacket;
wire reorder_mem_ready;
// -------------------------------------------
// Data to write and read from reorder memory
// Store everything includes channel, sop, eop
// -------------------------------------------
reg [MEM_W - 1 : 0] mem_in_rsp_sink_data;
reg [MEM_W - 1 : 0] reorder_mem_out_data;
always_comb
begin
mem_in_rsp_sink_data = {rsp_sink_data, rsp_sink_channel, rsp_sink_startofpacket, rsp_sink_endofpacket};
end
assign next_rd_trans_sequence = ((rd_trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : rd_trans_sequence + 1'b1;
assign next_expected_trans_sequence = ((expect_trans_sequence + 1'b1) == MAX_OUTSTANDING_RESPONSES) ? '0 : expect_trans_sequence + 1'b1;
always_ff @(posedge clk, posedge reset)
begin
if (reset) begin
rd_trans_sequence <= '0;
expect_trans_sequence <= '0;
end else begin
if (rsp_src_ready && reorder_mem_valid) begin
if (reorder_mem_endofpacket == 1) begin //endofpacket
expect_trans_sequence <= next_expected_trans_sequence;
rd_trans_sequence <= next_rd_trans_sequence;
end
end
end
end // always_ff @
// For PREVENT_HAZARD,
// Case: Master Write to S0, read S1, and Read S0 back but if Write for S0
// not yet return then we need to backpressure this, else read S0 might take over write
// This is more checking after the fifo destid, as read S1 is inserted in midle
// when see new packet, try to look at the fifo for that slave id, check if it
// type of transaction
assign sequence_and_trans_type_this_destid_waiting = current_trans_seq_of_this_destid_plus_trans_type[cmd_dest_id];
assign current_trans_type_of_this_destid = sequence_and_trans_type_this_destid_waiting[COUNTER_W];
assign trans_sequence_rsp_this_destid_waiting_valid = current_trans_seq_of_this_destid_valid[cmd_dest_id];
// it might waiting other sequence, check if different type of transaction as only for PREVENT HAZARD
// if comming comamnd to one slave and this slave is still waiting for response from previous command
// which has diiferent type of transaction, we back-pressure this command to avoid HAZARD
assign suppress_prevent_harzard_for_particular_destid = (current_trans_type_of_this_destid != is_write) & trans_sequence_rsp_this_destid_waiting_valid;
// -------------------------------------
// Memory for reorder buffer
// -------------------------------------
altera_merlin_reorder_memory
#(
.DATA_W (MEM_W),
.ADDR_H_W (COUNTER_W),
.ADDR_L_W (MAX_BURST_LENGTH),
.NUM_SEGMENT (MAX_OUTSTANDING_RESPONSES),
.DEPTH (MEM_DEPTH)
) reorder_memory
(
.clk (clk),
.reset (reset),
.in_data (mem_in_rsp_sink_data),
.in_valid (rsp_sink_valid),
.in_ready (reorder_mem_ready),
.out_data (reorder_mem_out_data),
.out_valid (reorder_mem_valid),
.out_ready (rsp_src_ready),
.wr_segment (trans_sequence_rsp),
.rd_segment (expect_trans_sequence)
);
// -------------------------------------
// Output from reorder buffer
// -------------------------------------
assign reorder_mem_data = reorder_mem_out_data[MEM_W -1 : ST_CHANNEL_W + 2];
assign reorder_mem_channel = reorder_mem_out_data[ST_CHANNEL_W + 2 - 1 : 2];
assign reorder_mem_startofpacket = reorder_mem_out_data[1];
assign reorder_mem_endofpacket = reorder_mem_out_data[0];
// -------------------------------------
// Because use generate statment
// so move all rsp_src_xxx controls here
// -------------------------------------
always_comb begin
cmd_src_data = stage2_data;
rsp_src_valid = reorder_mem_valid;
rsp_src_data = reorder_mem_data;
rsp_src_channel = reorder_mem_channel;
rsp_src_startofpacket = reorder_mem_startofpacket;
rsp_src_endofpacket = reorder_mem_endofpacket;
// -------------------------------------
// Forces commands to be non-posted if hazard prevention
// is on, also drops write responses
// -------------------------------------
rsp_sink_ready = reorder_mem_ready; // now it takes ready signal from the memory not direct from master
if (PREVENT_HAZARDS == 1) begin
cmd_src_data[PKT_TRANS_POSTED] = 1'b0;
if (rsp_src_data[PKT_TRANS_WRITE] == 1'b1 && SUPPORTS_POSTED_WRITES == 1 && SUPPORTS_NONPOSTED_WRITES == 0) begin
rsp_src_valid = 1'b0;
rsp_sink_ready = 1'b1;
end
end
end // always_comb
end // block: fifo_dest_id_write_read_control_reorder_on
endgenerate
// -------------------------------------
// Pass-through command and response
// -------------------------------------
always_comb
begin
cmd_src_channel = stage2_channel;
cmd_src_startofpacket = stage2_startofpacket;
cmd_src_endofpacket = stage2_endofpacket;
end // always_comb
// -------------------------------------
// When there is no REORDER requirement
// Just pass through signals
// -------------------------------------
generate
if (!REORDER) begin : use_selector_or_pass_thru_rsp
always_comb begin
cmd_src_data = stage2_data;
// pass thru almost signals
rsp_src_valid = rsp_sink_valid;
rsp_src_data = rsp_sink_data;
rsp_src_channel = rsp_sink_channel;
rsp_src_startofpacket = rsp_sink_startofpacket;
rsp_src_endofpacket = rsp_sink_endofpacket;
// -------------------------------------
// Forces commands to be non-posted if hazard prevention
// is on, also drops write responses
// -------------------------------------
rsp_sink_ready = rsp_src_ready; // take care this, should check memory empty
if (PREVENT_HAZARDS == 1) begin
cmd_src_data[PKT_TRANS_POSTED] = 1'b0;
if (rsp_sink_data[PKT_TRANS_WRITE] == 1'b1 && SUPPORTS_POSTED_WRITES == 1 && SUPPORTS_NONPOSTED_WRITES == 0) begin
rsp_src_valid = 1'b0;
rsp_sink_ready = 1'b1;
end
end
end // always_comb
end // if (!REORDER)
endgenerate
// --------------------------------------------------------
// Backpressure & Suppression
// --------------------------------------------------------
// ENFORCE_ORDER: unused option, always is 1, remove it
// Now the limiter will suppress when max_outstanding reach
// --------------------------------------------------------
generate
if (ENFORCE_ORDER) begin : enforce_order_block
assign suppress_change_dest_id = (REORDER == 1) ? 1'b0 : nonposted_cmd && has_pending_responses &&
(stage2_dest_changed || (PREVENT_HAZARDS == 1 && stage2_trans_changed));
end else begin : no_order_block
assign suppress_change_dest_id = 1'b0;
end
endgenerate
// ------------------------------------------------------------
// Even we allow change slave while still have pending responses
// But one special case, when PREVENT_HAZARD=1, we still allow
// switch slave while type of transaction change (RAW, WAR) but
// only to different slaves.
// if to same slave, we still need back pressure that to make
// sure no racing
// ------------------------------------------------------------
generate
if (REORDER) begin : prevent_hazard_block
assign suppress_change_trans_but_not_dest = nonposted_cmd && has_pending_responses &&
!stage2_dest_changed && (PREVENT_HAZARDS == 1 && stage2_trans_changed);
end else begin : no_hazard_block
assign suppress_change_trans_but_not_dest = 1'b0; // no REORDER, the suppress_changes_destid take care of this.
end
endgenerate
generate
if (REORDER) begin : prevent_hazard_block_for_particular_slave
assign suppress_change_trans_for_one_slave = nonposted_cmd && has_pending_responses && (PREVENT_HAZARDS == 1 && suppress_prevent_harzard_for_particular_destid);
end else begin : no_hazard_block_for_particular_slave
assign suppress_change_trans_for_one_slave = 1'b0; // no REORDER, the suppress_changes_destid take care of this.
end
endgenerate
// ------------------------------------------
// Backpressure when max outstanding transactions are reached
// ------------------------------------------
generate
if (REORDER) begin : max_outstanding_block
assign suppress_max_outstanding = count_max_reached;
end else begin
assign suppress_max_outstanding = 1'b0;
end
endgenerate
assign suppress = suppress_change_trans_for_one_slave | suppress_change_dest_id | suppress_max_outstanding;
assign wide_valid = { VALID_WIDTH {stage2_valid} } & stage2_channel;
always @* begin
stage2_ready = cmd_src_ready;
internal_valid = stage2_valid;
// --------------------------------------------------------
// change suppress condidtion, in case REODER it will alllow changing slave
// even still have pending transactions.
// -------------------------------------------------------
if (suppress) begin
stage2_ready = 0;
internal_valid = 0;
end
if (VALID_WIDTH == 1) begin
cmd_src_valid = {VALID_WIDTH{1'b0}};
cmd_src_valid[0] = internal_valid;
end else begin
// -------------------------------------
// Use the one-hot channel to determine if the destination
// has changed. This results in a wide valid bus
// -------------------------------------
cmd_src_valid = wide_valid;
if (nonposted_cmd & has_pending_responses) begin
if (!REORDER) begin
cmd_src_valid = wide_valid & last_channel;
// -------------------------------------
// Mask the valid signals if the transaction type has changed
// if hazard prevention is enabled
// -------------------------------------
if (PREVENT_HAZARDS == 1)
cmd_src_valid = wide_valid & last_channel & { VALID_WIDTH {!stage2_trans_changed} };
end else begin // else: !if(!REORDER) if REORDER happen
if (PREVENT_HAZARDS == 1)
cmd_src_valid = wide_valid & { VALID_WIDTH {!suppress_change_trans_for_one_slave} };
if (suppress_max_outstanding) begin
cmd_src_valid = {VALID_WIDTH {1'b0}};
end
end
end
end
end
// --------------------------------------------------
// Calculates the log2ceil of the input value.
//
// This function occurs a lot... please refactor.
// --------------------------------------------------
function integer log2ceil;
input integer val;
integer i;
begin
i = 1;
log2ceil = 0;
while (i < val) begin
log2ceil = log2ceil + 1;
i = i << 1;
end
end
endfunction
endmodule