mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 21:57:11 -04:00
Revised mem arbiter with tag valid fix issue #35
This commit is contained in:
parent
08f0570213
commit
85d2ca6821
3 changed files with 197 additions and 76 deletions
|
@ -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 {
|
||||
|
|
49
src/lsu.sv
49
src/lsu.sv
|
@ -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 ),
|
||||
.*
|
||||
);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue