Revised mem arbiter with tag valid fix issue #35

This commit is contained in:
Florian Zaruba 2017-05-18 12:27:50 +02:00
parent 08f0570213
commit 85d2ca6821
3 changed files with 197 additions and 76 deletions

View file

@ -8,6 +8,8 @@
* Description: Contains all the necessary defines for Ariane
* in one package.
*/
package ariane_pkg;
// ---------------
// Global Config
@ -88,8 +90,8 @@ package ariane_pkg;
// LSU functions
LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU
} fu_op;
// ---------------
// ID/EX/WB Stage
// ---------------
// IF/ID Stage
// ---------------
// store the decompressed instruction
typedef struct packed {

View file

@ -73,9 +73,6 @@ module lsu #(
output exception lsu_exception_o // to WB, signal exception status LD/ST exception
);
// tag status
enum logic [1:0] { WAIT_TRANSLATION, ABORT_TRANSLATION, VALID_TRANSLATION, NOT_IMPL } tag_status;
mem_if ptw_if(clk_i);
// byte enable based on operation to perform
// data is misaligned
@ -125,35 +122,37 @@ module lsu #(
// ---------------
logic [2:0][63:0] address_i;
logic [2:0][63:0] data_wdata_i;
logic [2:0] data_req_i;
logic [2:0] data_we_i;
logic [2:0][7:0] data_be_i;
logic [2:0] data_gnt_o;
logic [2:0] data_rvalid_o;
logic [2:0] data_req_i;
logic [2:0] data_we_i;
logic [2:0][7:0] data_be_i;
logic [2:0][1:0] data_tag_status_i;
logic [2:0] data_gnt_o;
logic [2:0] data_rvalid_o;
logic [2:0][63:0] data_rdata_o;
// port 0 PTW, port 1 loads, port 2 stores
mem_arbiter mem_arbiter_i (
// to D$
.address_o ( data_if_address_o ),
.data_wdata_o ( data_if_data_wdata_o ),
.data_req_o ( data_if_data_req_o ),
.data_we_o ( data_if_data_we_o ),
.data_be_o ( data_if_data_be_o ),
.data_gnt_i ( data_if_data_gnt_i ),
.data_rvalid_i ( data_if_data_rvalid_i ),
.data_rdata_i ( data_if_data_rdata_i ),
.address_o ( data_if_address_o ),
.data_wdata_o ( data_if_data_wdata_o ),
.data_req_o ( data_if_data_req_o ),
.data_we_o ( data_if_data_we_o ),
.data_be_o ( data_if_data_be_o ),
.data_tag_status_o ( data_if_tag_status_o ),
.data_gnt_i ( data_if_data_gnt_i ),
.data_rvalid_i ( data_if_data_rvalid_i ),
.data_rdata_i ( data_if_data_rdata_i ),
// from PTW, Load logic and store queue
.address_i ( address_i ),
.data_wdata_i ( data_wdata_i ),
.data_req_i ( data_req_i ),
.data_we_i ( data_we_i ),
.data_be_i ( data_be_i ),
.data_gnt_o ( data_gnt_o ),
.data_rvalid_o ( data_rvalid_o ),
.data_rdata_o ( data_rdata_o ),
.flush_ready_o ( ), // TODO: connect, wait for flush to be valid
.address_i ( address_i ),
.data_wdata_i ( data_wdata_i ),
.data_req_i ( data_req_i ),
.data_we_i ( data_we_i ),
.data_be_i ( data_be_i ),
.data_tag_status_i ( data_tag_status_i ),
.data_gnt_o ( data_gnt_o ),
.data_rvalid_o ( data_rvalid_o ),
.data_rdata_o ( data_rdata_o ),
.*
);

View file

@ -17,6 +17,16 @@
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
import ariane_pkg::*;
// ---------------
// D$ Tag Status
// ---------------
`define WAIT_TRANSLATION 2'b00
`define VALID_TRANSLATION 2'b01
`define ABORT_TRANSLATION 2'b10
`define NOT_IMPL 2'b11
module mem_arbiter #(
parameter int NR_PORTS = 3
@ -24,13 +34,14 @@ module mem_arbiter #(
(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
output logic flush_ready_o, // the flush is ready, e.g.: all transaction returned
input logic flush_i,
// slave port
output logic [63:0] address_o,
output logic [63:0] data_wdata_o,
output logic data_req_o,
output logic data_we_o,
output logic [7:0] data_be_o,
output logic [1:0] data_tag_status_o,
input logic data_gnt_i,
input logic data_rvalid_i,
input logic [63:0] data_rdata_i,
@ -40,24 +51,33 @@ module mem_arbiter #(
input logic [NR_PORTS-1:0] data_req_i,
input logic [NR_PORTS-1:0] data_we_i,
input logic [NR_PORTS-1:0][7:0] data_be_i,
input logic [1:0] data_tag_status_i,
output logic [NR_PORTS-1:0] data_gnt_o,
output logic [NR_PORTS-1:0] data_rvalid_o,
output logic [NR_PORTS-1:0][63:0] data_rdata_o
);
localparam DATA_WIDTH = $clog2(NR_PORTS);
logic full_o;
logic empty_o;
logic [DATA_WIDTH-1:0] data_i;
logic push_i;
logic [DATA_WIDTH-1:0] data_o;
logic pop_i;
logic single_element_o;
// registers
enum logic [1:0] {IDLE, WAIT_GNT, WAIT_TAG, WAIT_FLUSH} CS, NS;
// remember the request port in case of a multi-cycle transaction
logic [DATA_WIDTH-1:0] request_port_n, request_port_q;
// local ports
// FIFO control ports
logic full;
logic empty;
logic single_element;
// FIFO input port
logic [DATA_WIDTH-1:0] in_data;
logic push;
// FIFO output port
logic [DATA_WIDTH-1:0] out_data;
logic pop;
logic flush_ready;
// essentially wait for the queue to be empty
// or we just got a grant -> this means we issued a memory request in this cycle
// although we are ready if we only got a single element in the queue and an rvalid
// which means we are getting this element back in this cycle
assign flush_ready_o = (empty_o & ~(|data_gnt_i)) | (single_element_o & data_rvalid_i);
assign flush_ready = (empty & ~(|data_gnt_i)) | (single_element & data_rvalid_i);
fifo #(
.dtype ( logic [DATA_WIDTH-1:0] ),
@ -65,57 +85,146 @@ module mem_arbiter #(
) fifo_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.single_element_o ( single_element_o ),
// the flush is accomplished implicitly by waiting for the flush ready signal
.single_element_o ( single_element ),
// the flush is accomplished implicitly by waiting for the queue to be drained before accepting any new request
.flush_i ( 1'b0 ),
.full_o ( full_o ),
.empty_o ( empty_o ),
.data_i ( data_i ),
.push_i ( push_i ),
.data_o ( data_o ),
.pop_i ( pop_i )
.full_o ( full ),
.empty_o ( empty ),
.data_i ( in_data ),
.push_i ( push ),
.data_o ( out_data ),
.pop_i ( pop )
);
// addressing read and full write
always_comb begin : read_req_write
// default assignment
data_req_o = 1'b0;
address_o = address_i[0];
data_wdata_o = data_wdata_i[0];
data_be_o = data_be_i[0];
data_we_o = data_we_i[0];
data_i = '{default: 0};
push_i = 1'b0;
automatic logic [DATA_WIDTH-1:0] request_index;
// pass through all signals from the correct slave port
address_o = address_i[request_index];
data_req_o = 1'b0;
data_wdata_o = data_wdata_i[request_index];
data_be_o = data_be_i[request_index];
data_we_o = data_we_i[request_index];
data_tag_status_o = data_tag_status_i[request_index];
data_gnt_o[request_index] = data_gnt_i;
in_data = '{default: 0};
push = 1'b0;
request_port_n = request_port_q;
for (int i = 0; i < NR_PORTS; i++)
data_gnt_o[i] = 1'b0;
// only go for a new request if we can wait for the valid e.g.: we have enough space in the buffer
if (~full_o) begin
for (int unsigned i = 0; i < NR_PORTS; i++) begin
if (data_req_i[i] == 1'b1) begin
// pass through all signals from the correct slave port
data_req_o = data_req_i[i];
address_o = address_i[i];
data_wdata_o = data_wdata_i[i];
data_be_o = data_be_i[i];
data_we_o = data_we_i[i];
// wait on the grant
if (data_gnt_i) begin
data_gnt_o[i] = data_gnt_i;
// set the slave on which we are waiting
data_i = i[DATA_WIDTH-1:0];
push_i = 1'b1;
case (CS)
// ----------------------------
// Single-cycle memory requests
// ----------------------------
IDLE: begin
// only go for a new request if we can wait for the valid e.g.: we have enough space in the buffer
if (~full) begin
for (int unsigned i = 0; i < NR_PORTS; i++) begin
if (data_req_i[i] == 1'b1) begin
data_req_o = data_req_i[i];
// save the request port for future states
request_port_n = i;
request_index = i;
// default is that we are waiting for the grant
NS = WAIT_GNT;
// wait for the grant
if (data_gnt_i) begin
// set the slave on which we are waiting
in_data = i[DATA_WIDTH-1:0];
push = 1'b1;
// we got a grant so we need to wait for the tag
NS = WAIT_TAG;
end
// we already got a valid translation so we can proceed normally
// from the request side the thing is over
if (data_tag_status_i[i] == `VALID_TRANSLATION)
NS = IDLE;
break; // break here as this is a priority select
end
end
break; // break here as this is a priority select
end
end
end
// ----------------------------
// Multi-cycle memory requests
// ----------------------------
// do we have an outstanding request e.g.: a request which is waiting for a grant or an tag_valid
// here we need to wait for the tag
WAIT_GNT: begin
// we can check for it since we only stay in this state if didn't yet receive a grant
if (data_gnt_i) begin
// default is that we are waiting for the tag to be there
// if we are waiting for the tag we can't accept any new instructions
NS = WAIT_TAG;
// two possibilities to not go into the tag wait state
// 1. The tag is valid now
// 2. The tag has been aborted
if (data_tag_status_i[request_port_q] inside {`ABORT_TRANSLATION, `VALID_TRANSLATION}) begin
NS = IDLE;
end
end
// or if the request vanished we are going back to idle
if (!data_req_i[request_port_q])
NS = IDLE;
end
// here we are waiting for a valid (or aborted) tag
WAIT_TAG: begin
// if we are waiting for the tag we can't issue a new request
if (data_tag_status_i[request_port_q] inside {`ABORT_TRANSLATION, `VALID_TRANSLATION}) begin
NS = IDLE;
// if we got a valid tag we can make a new request under the same assumption as in the IDLE state
if (~full) begin
for (int unsigned i = 0; i < NR_PORTS; i++) begin
if (data_req_i[i] == 1'b1) begin
data_req_o = data_req_i[i];
// save the request port for future states
request_port_n = i;
request_index = i;
// default is that we are waiting for the grant
NS = WAIT_GNT;
// wait for the grant
if (data_gnt_i) begin
// set the slave on which we are waiting
in_data = i[DATA_WIDTH-1:0];
push = 1'b1;
// we got a grant so we need to wait for the tag
NS = WAIT_TAG;
end
// we already got a valid translation so we can proceed normally
// from the request side the thing is over
if (data_tag_status_i[i] == `VALID_TRANSLATION)
NS = IDLE;
break; // break here as this is a priority select
end
end
end else begin
// the FIFO is full so go to IDLE and wait for it
NS = IDLE;
end
end
end
// ----------------------------
// Flush logic
// ----------------------------
// here we are waiting for the FIFO to drain until we are ready to accept new requests
WAIT_FLUSH: begin
// if the flush has finished go to IDLE
if (flush_ready)
NS = IDLE;
end
default : /* default */;
endcase
// if we got a flush and we are not ready for the flush wait and for it and don't accept any incoming data
// e.g.: jump to the flush wait state
if (flush_i && !flush_ready)
NS = WAIT_FLUSH;
end
// results, listening on the input signals of the slave port
always_comb begin : slave_read_port
pop_i = 1'b0;
pop = 1'b0;
// default assignment
for (int i = 0; i < NR_PORTS; i++) begin
data_rvalid_o[i] = 1'b0;
@ -125,9 +234,20 @@ module mem_arbiter #(
// if there is a valid signal the FIFO should not be empty anyway
if (data_rvalid_i) begin
// pass the read to the appropriate slave
pop_i = 1'b1;
data_rvalid_o[data_o] = data_rvalid_i;
data_rdata_o[data_o] = data_rdata_i;
pop = 1'b1;
data_rvalid_o[out_data] = data_rvalid_i;
data_rdata_o[out_data] = data_rdata_i;
end
end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
CS <= IDLE;
request_port_q <= 1'b0;
end else begin
CS <= NS;
request_port_q <= request_port_n;
end
end