mor1kx-bemicrocv/ip/altera/ddr3/altera_merlin_reorder_memor...

298 lines
11 KiB
Systemverilog

// (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_reorder_memory.sv#1 $
// $Revision: #1 $
// $Date: 2015/08/09 $
// $Author: swbranch $
// ------------------------------------------------------------------
// Merlin Order Memory: this stores responses from slave
// and do reorder. The memory structure is normal memory
// with many segments for different responses that master
// can handle.
// The number of segment is the number of MAX_OUTSTANDING_RESPONSE
// ------------------------------------------------------------------
`timescale 1 ns / 1 ns
module altera_merlin_reorder_memory
#(
parameter DATA_W = 32,
ADDR_H_W = 4, // width to represent how many segments
ADDR_L_W = 4,
VALID_W = 4,
NUM_SEGMENT = 4,
DEPTH = 16
)
(
// -------------------
// Clock
// -------------------
input clk,
input reset,
// -------------------
// Signals
// -------------------
input [DATA_W - 1 : 0] in_data,
input in_valid,
output in_ready,
output reg [DATA_W - 1 : 0] out_data,
output reg out_valid,
input out_ready,
// --------------------------------------------
// wr_segment: select write portion of memory
// rd_segment: select read portion of memory
// --------------------------------------------
input [ADDR_H_W - 1 : 0] wr_segment,
input [ADDR_H_W - 1 : 0] rd_segment
);
// -------------------------------------
// Local parameter
// -------------------------------------
localparam SEGMENT_W = ADDR_H_W;
wire [ADDR_H_W + ADDR_L_W - 1 : 0] mem_wr_addr;
reg [ADDR_H_W + ADDR_L_W - 1 : 0] mem_rd_addr;
wire [ADDR_L_W - 1 : 0] mem_wr_ptr;
wire [ADDR_L_W - 1 : 0] mem_rd_ptr;
reg [ADDR_L_W - 1 : 0] mem_next_rd_ptr;
reg [DATA_W - 1 : 0] out_payload;
wire [NUM_SEGMENT - 1 : 0] pointer_ctrl_in_ready;
wire [NUM_SEGMENT - 1 : 0] pointer_ctrl_in_valid;
wire [NUM_SEGMENT - 1 : 0] pointer_ctrl_out_valid;
wire [NUM_SEGMENT - 1 : 0] pointer_ctrl_out_ready;
wire [ADDR_L_W - 1 : 0] pointer_ctrl_wr_ptr [NUM_SEGMENT];
wire [ADDR_L_W - 1 : 0] pointer_ctrl_rd_ptr [NUM_SEGMENT];
wire [ADDR_L_W - 1 : 0] pointer_ctrl_next_rd_ptr [NUM_SEGMENT];
// ---------------------------------
// Memory storage
// ---------------------------------
(* ramstyle="no_rw_check" *) reg [DATA_W - 1 : 0] mem [DEPTH - 1 : 0];
always @(posedge clk) begin
if (in_valid && in_ready)
mem[mem_wr_addr] = in_data;
out_payload = mem[mem_rd_addr];
end
//assign mem_rd_addr = {rd_segment, mem_next_rd_ptr};
always_comb
begin
out_data = out_payload;
out_valid = pointer_ctrl_out_valid[rd_segment];
end
// ---------------------------------
// Memory addresses
// ---------------------------------
assign mem_wr_ptr = pointer_ctrl_wr_ptr[wr_segment];
//assign mem_rd_ptr = pointer_ctrl_rd_ptr[rd_segment];
//assign mem_next_rd_ptr = pointer_ctrl_next_rd_ptr[rd_segment];
assign mem_wr_addr = {wr_segment, mem_wr_ptr};
// ---------------------------------------------------------------------------
// Bcos want, empty latency, mean assert read the data will appear on out_data.
// And need to jump around different segment of the memory.
// So when seeing endofpacket for this current segment, the read address
// will jump to next segment at first read address, so that the data will be ready
// it is okay to jump to next segment as this is the sequence of all transaction
// and they just increment. (standing at segment 0, then for sure next segment 1)
// ----------------------------------------------------------------------------
wire endofpacket;
assign endofpacket = out_payload[0];
wire [ADDR_H_W - 1: 0] next_rd_segment;
assign next_rd_segment = ((rd_segment + 1'b1) == NUM_SEGMENT) ? '0 : rd_segment + 1'b1;
always_comb
begin
if (out_valid && out_ready && endofpacket)
begin
mem_next_rd_ptr = pointer_ctrl_rd_ptr[next_rd_segment];
//mem_rd_addr = {rd_segment + 1'b1, mem_next_rd_ptr};
mem_rd_addr = {next_rd_segment, mem_next_rd_ptr};
end
else
begin
mem_next_rd_ptr = pointer_ctrl_next_rd_ptr[rd_segment];
mem_rd_addr = {rd_segment, mem_next_rd_ptr};
end
end
// ---------------------------------
// Output signals
// ---------------------------------
assign in_ready = pointer_ctrl_in_ready[wr_segment];
// ---------------------------------
// Control signals for each segment
// ---------------------------------
genvar j;
generate
for (j = 0; j < NUM_SEGMENT; j = j + 1)
begin : pointer_signal
assign pointer_ctrl_in_valid[j] = (wr_segment == j) && in_valid;
assign pointer_ctrl_out_ready[j] = (rd_segment == j) && out_ready;
end
endgenerate
// ---------------------------------
// Seperate write and read pointer
// for each segment in memory
// ---------------------------------
genvar i;
generate
for (i = 0; i < NUM_SEGMENT; i = i + 1)
begin : each_segment_pointer_controller
memory_pointer_controller
#(
.ADDR_W (ADDR_L_W)
) reorder_memory_pointer_controller
(
.clk (clk),
.reset (reset),
.in_ready (pointer_ctrl_in_ready[i]),
.in_valid (pointer_ctrl_in_valid[i]),
.out_ready (pointer_ctrl_out_ready[i]),
.out_valid (pointer_ctrl_out_valid[i]),
.wr_pointer (pointer_ctrl_wr_ptr[i]),
.rd_pointer (pointer_ctrl_rd_ptr[i]),
.next_rd_pointer (pointer_ctrl_next_rd_ptr[i])
);
end // block: each_segment_pointer_controller
endgenerate
endmodule
module memory_pointer_controller
#(
parameter ADDR_W = 4
)
(
// -------------------
// Clock
// -------------------
input clk,
input reset,
// -------------------
// Signals
// -------------------
output reg in_ready,
input in_valid,
input out_ready,
output reg out_valid,
// -------------------------------
// Output write and read pointer
// -------------------------------
output [ADDR_W - 1 : 0] wr_pointer,
output [ADDR_W - 1 : 0] rd_pointer,
output [ADDR_W - 1 : 0] next_rd_pointer
);
reg [ADDR_W - 1 : 0] incremented_wr_ptr;
reg [ADDR_W - 1 : 0] incremented_rd_ptr;
reg [ADDR_W - 1 : 0] wr_ptr;
reg [ADDR_W - 1 : 0] rd_ptr;
reg [ADDR_W - 1 : 0] next_wr_ptr;
reg [ADDR_W - 1 : 0] next_rd_ptr;
reg full, empty, next_full, next_empty;
reg read, write, internal_out_ready, internal_out_valid;
assign incremented_wr_ptr = wr_ptr + 1'b1;
assign incremented_rd_ptr = rd_ptr + 1'b1;
assign next_wr_ptr = write ? incremented_wr_ptr : wr_ptr;
assign next_rd_ptr = read ? incremented_rd_ptr : rd_ptr;
assign wr_pointer = wr_ptr;
assign rd_pointer = rd_ptr;
assign next_rd_pointer = next_rd_ptr;
// -------------------------------
// Define write and read signals
// --------------------------------
// internal read, if it has any valid data
// and output are ready to accepts data then a read will be performed.
// -------------------------------
//assign read = internal_out_ready && internal_out_valid;
assign read = internal_out_ready && !empty;
assign write = in_ready && in_valid;
always_ff @(posedge clk or posedge reset)
begin
if (reset)
begin
wr_ptr <= 0;
rd_ptr <= 0;
end
else
begin
wr_ptr <= next_wr_ptr;
rd_ptr <= next_rd_ptr;
end
end
// ---------------------------------------------------------------------------
// Generate full/empty signal for memory
// if read and next read pointer same as write, set empty, write will clear empty
// if write and next write pointer same as read, set full, read will clear full
// -----------------------------------------------------------------------------
always_comb
begin
next_full = full;
next_empty = empty;
if (read && !write)
begin
next_full = 1'b0;
if (incremented_rd_ptr == wr_ptr)
next_empty = 1'b1;
end
if (write && !read)
begin
next_empty = 1'b0;
if (incremented_wr_ptr == rd_ptr)
next_full = 1'b1;
end
end // always_comb
always_ff @(posedge clk or posedge reset)
begin
if (reset)
begin
empty <= 1;
full <= 0;
end
else
begin
empty <= next_empty;
full <= next_full;
end
end
// --------------------
// Control signals
// --------------------
always_comb
begin
in_ready = !full;
out_valid = !empty;
internal_out_ready = out_ready;
end // always_comb
endmodule