mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 05:37:16 -04:00
Major revision of load/store interface
This commit is contained in:
parent
7137e964fb
commit
81c18a9871
5 changed files with 181 additions and 118 deletions
110
src/load_unit.sv
110
src/load_unit.sv
|
@ -40,6 +40,7 @@ module load_unit (
|
|||
input logic [63:0] paddr_i, // physical address in
|
||||
input logic translation_valid_i,
|
||||
input exception ex_i, // exception which may has happened earlier. for example: mis-aligned exception
|
||||
input logic dtlb_hit_i, // hit on the dtlb, send in the same cycle as the request
|
||||
// address checker
|
||||
output logic [11:0] page_offset_o,
|
||||
input logic page_offset_matches_i,
|
||||
|
@ -94,7 +95,7 @@ module load_unit (
|
|||
// ---------------
|
||||
// Load Control
|
||||
// ---------------
|
||||
always_comb begin : load_controll
|
||||
always_comb begin : load_control
|
||||
// default assignments
|
||||
NS = CS;
|
||||
paddr_n = paddr_q;
|
||||
|
@ -124,8 +125,11 @@ module load_unit (
|
|||
end else begin
|
||||
// put the request in the queue
|
||||
push = 1'b1;
|
||||
// we got a grant so we can send the tag in the next cycle
|
||||
NS = SEND_TAG;
|
||||
if (dtlb_hit_i)
|
||||
// we got a grant and a hit on the DTLB so we can send the tag in the next cycle
|
||||
NS = SEND_TAG;
|
||||
else
|
||||
NS = ABORT_TRANSACTION;
|
||||
end
|
||||
end else begin
|
||||
// wait for the store buffer to train and the page offset to not match anymore
|
||||
|
@ -145,24 +149,21 @@ module load_unit (
|
|||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
// abort the previous request - free the D$ arbiter
|
||||
// we are here because of a TLB miss, we need to abort the current request and give way for the
|
||||
// PTW walker to satisfy the TLB miss
|
||||
WAIT_TRANSLATION: begin
|
||||
// keep the translation request hight to tell the PTW that we want this
|
||||
// translation
|
||||
translation_req_o = 1'b1;
|
||||
// we are not ready here
|
||||
ready_o = 1'b0;
|
||||
// send an abort signal
|
||||
tag_valid_o = 1'b1;
|
||||
kill_req_o = 1'b1;
|
||||
// wait for the translation to become valid and redo the request by going back to idle
|
||||
if (translation_valid_i) begin
|
||||
NS = IDLE;
|
||||
end
|
||||
ABORT_TRANSACTION: begin
|
||||
ready_o = 1'b0;
|
||||
kill_req_o = 1'b1;
|
||||
tag_valid_o = 1'b1;
|
||||
// redo the request by going back to the wait gnt state
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
|
||||
WAIT_GNT: begin
|
||||
// keep the translation request up
|
||||
translation_req_o = 1'b1;
|
||||
// we are waiting for the grant so we are not ready to accept anything new
|
||||
ready_o = 1'b0;
|
||||
// keep the request up
|
||||
|
@ -170,54 +171,51 @@ module load_unit (
|
|||
// we finally got a data grant
|
||||
if (data_gnt_i) begin
|
||||
// so we send the tag in the next cycle
|
||||
NS = SEND_TAG;
|
||||
if (dtlb_hit_i)
|
||||
NS = SEND_TAG;
|
||||
else // should we not have hit on the TLB abort this transaction an retry later
|
||||
NS = ABORT_TRANSACTION;
|
||||
// we store this grant in our queue
|
||||
push = 1'b1;
|
||||
end
|
||||
// otherwise we keep waiting on our grant
|
||||
end
|
||||
|
||||
// we know for sure that the tag we want to send is valid
|
||||
SEND_TAG: begin
|
||||
ready_o = 1'b1;
|
||||
// check if the translation is valid
|
||||
if (translation_valid_i) begin
|
||||
tag_valid_o = 1'b1;
|
||||
// check for an exception which could have occurred
|
||||
if (ex_i.valid) begin
|
||||
// if we got one lets abort this request
|
||||
kill_req_o = 1'b1;
|
||||
end
|
||||
tag_valid_o = 1'b1;
|
||||
|
||||
// we can make a new request here if we got one
|
||||
if (valid_i) begin
|
||||
// start the translation process even though we do not know if the addresses match
|
||||
// this should ease timing
|
||||
translation_req_o = 1'b1;
|
||||
// check if the page offset matches with a store, if it does then stall and wait
|
||||
if (!page_offset_matches_i) begin
|
||||
// make a load request to memory
|
||||
data_req_o = 1'b1;
|
||||
// we got no data grant so wait for the grant before sending the tag
|
||||
if (!data_gnt_i) begin
|
||||
NS = WAIT_GNT;
|
||||
end else begin
|
||||
// put the request in the queue
|
||||
push = 1'b1;
|
||||
// we got a grant so we can send the tag in the next cycle
|
||||
NS = SEND_TAG;
|
||||
end
|
||||
end else begin
|
||||
// wait for the store buffer to train and the page offset to not match anymore
|
||||
NS = WAIT_PAGE_OFFSET;
|
||||
end
|
||||
end
|
||||
// we didn't yet receive a valid translation, the only case where this could happen
|
||||
// is on a TLB miss, therefore kill the transaction and give way for the PTW on the D$
|
||||
end else begin
|
||||
// lets keep translating
|
||||
// check for an exception which could have occurred
|
||||
if (ex_i.valid) begin
|
||||
// if we got one lets abort this request
|
||||
kill_req_o = 1'b1;
|
||||
end
|
||||
|
||||
// we can make a new request here if we got one
|
||||
if (valid_i) begin
|
||||
// start the translation process even though we do not know if the addresses match
|
||||
// this should ease timing
|
||||
translation_req_o = 1'b1;
|
||||
ready_o = 1'b0;
|
||||
NS = WAIT_TRANSLATION;
|
||||
// check if the page offset matches with a store, if it does then stall and wait
|
||||
if (!page_offset_matches_i) begin
|
||||
// make a load request to memory
|
||||
data_req_o = 1'b1;
|
||||
// we got no data grant so wait for the grant before sending the tag
|
||||
if (!data_gnt_i) begin
|
||||
NS = WAIT_GNT;
|
||||
end else begin
|
||||
// put the request in the queue
|
||||
push = 1'b1;
|
||||
// we got a grant so we can send the tag in the next cycle
|
||||
if (dtlb_hit_i)
|
||||
// we got a grant and a hit on the DTLB so we can send the tag in the next cycle
|
||||
NS = SEND_TAG;
|
||||
else // we missed on the TLB -> wait for the translation
|
||||
NS = ABORT_TRANSACTION;
|
||||
end
|
||||
end else begin
|
||||
// wait for the store buffer to train and the page offset to not match anymore
|
||||
NS = WAIT_PAGE_OFFSET;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
53
src/lsu.sv
53
src/lsu.sv
|
@ -124,6 +124,7 @@ module lsu #(
|
|||
logic [63:0] mmu_vaddr;
|
||||
logic [63:0] mmu_paddr;
|
||||
exception mmu_exception;
|
||||
logic dtlb_hit;
|
||||
|
||||
logic ld_valid;
|
||||
logic [TRANS_ID_BITS-1:0] ld_trans_id;
|
||||
|
@ -202,6 +203,7 @@ module lsu #(
|
|||
.lsu_valid_o ( translation_valid ),
|
||||
.lsu_paddr_o ( mmu_paddr ),
|
||||
.lsu_exception_o ( mmu_exception ),
|
||||
.lsu_dtlb_hit_o ( dtlb_hit ), // send in the same cycle as the request
|
||||
// connecting PTW to D$ IF (aka mem arbiter
|
||||
.address_index_o ( address_index_i [0] ),
|
||||
.address_tag_o ( address_tag_i [0] ),
|
||||
|
@ -237,6 +239,7 @@ module lsu #(
|
|||
.paddr_i ( mmu_paddr ),
|
||||
.translation_valid_i ( translation_valid_st ),
|
||||
.ex_i ( mmu_exception ),
|
||||
.dtlb_hit_i ( dtlb_hit ),
|
||||
// Load Unit
|
||||
.page_offset_i ( page_offset ),
|
||||
.page_offset_matches_o ( page_offset_matches ),
|
||||
|
@ -274,6 +277,7 @@ module lsu #(
|
|||
.paddr_i ( mmu_paddr ),
|
||||
.translation_valid_i ( translation_valid_ld ),
|
||||
.ex_i ( mmu_exception ),
|
||||
.dtlb_hit_i ( dtlb_hit ),
|
||||
// to store unit
|
||||
.page_offset_o ( page_offset ),
|
||||
.page_offset_matches_i ( page_offset_matches ),
|
||||
|
@ -323,21 +327,21 @@ module lsu #(
|
|||
// which of the two we are getting
|
||||
lsu_ready_o = ld_ready_o && st_ready_o;
|
||||
// "arbitrate" MMU access
|
||||
translation_req = 1'b0;
|
||||
mmu_vaddr = 64'b0;
|
||||
translation_valid_st = 1'b0;
|
||||
translation_valid_ld = 1'b0;
|
||||
// translation_req = 1'b0;
|
||||
// mmu_vaddr = 64'b0;
|
||||
// translation_valid_st = 1'b0;
|
||||
// translation_valid_ld = 1'b0;
|
||||
|
||||
// this arbitrates access to the MMU
|
||||
if (st_translation_req) begin
|
||||
translation_req = st_translation_req;
|
||||
mmu_vaddr = st_vaddr;
|
||||
translation_valid_st = translation_valid;
|
||||
end else begin
|
||||
translation_req = ld_translation_req;
|
||||
mmu_vaddr = ld_vaddr;
|
||||
translation_valid_ld = translation_valid;
|
||||
end
|
||||
// // this arbitrates access to the MMU
|
||||
// if (ld_translation_req) begin
|
||||
// translation_req = ld_translation_req;
|
||||
// mmu_vaddr = ld_vaddr;
|
||||
// translation_valid_ld = translation_valid;
|
||||
// end else begin
|
||||
// translation_req = st_translation_req;
|
||||
// mmu_vaddr = st_vaddr;
|
||||
// translation_valid_st = translation_valid;
|
||||
// end
|
||||
end
|
||||
|
||||
// determine whether this is a load or store
|
||||
|
@ -346,12 +350,27 @@ module lsu #(
|
|||
ld_valid_i = 1'b0;
|
||||
st_valid_i = 1'b0;
|
||||
|
||||
translation_req = 1'b0;
|
||||
mmu_vaddr = 64'b0;
|
||||
translation_valid_st = 1'b0;
|
||||
translation_valid_ld = 1'b0;
|
||||
|
||||
// check the operator to activate the right functional unit accordingly
|
||||
unique case (fu)
|
||||
// all loads go here
|
||||
LOAD: ld_valid_i = valid;
|
||||
LOAD: begin
|
||||
ld_valid_i = valid;
|
||||
translation_req = ld_translation_req;
|
||||
mmu_vaddr = ld_vaddr;
|
||||
translation_valid_ld = translation_valid;
|
||||
end
|
||||
// all stores go here
|
||||
STORE: st_valid_i = valid;
|
||||
STORE: begin
|
||||
st_valid_i = valid;
|
||||
translation_req = st_translation_req;
|
||||
mmu_vaddr = st_vaddr;
|
||||
translation_valid_st = translation_valid;
|
||||
end
|
||||
// not relevant for the LSU
|
||||
default: ;
|
||||
endcase
|
||||
|
@ -496,7 +515,7 @@ module lsu #(
|
|||
trans_id_n = trans_id_q;
|
||||
be_n = be_q;
|
||||
// get new input data
|
||||
if (lsu_valid_i) begin
|
||||
if (lsu_ready_o) begin
|
||||
valid_n = lsu_valid_i;
|
||||
vaddr_n = vaddr_i;
|
||||
data_n = operand_b_i;
|
||||
|
|
|
@ -51,6 +51,7 @@ module mmu #(
|
|||
output logic lsu_valid_o, // translation is valid
|
||||
output logic [63:0] lsu_paddr_o, // translated address
|
||||
output exception lsu_exception_o, // address translation threw an exception
|
||||
output logic lsu_dtlb_hit_o, // send in the same cycle as the request if translation hits in the DTLB
|
||||
// General control signals
|
||||
input priv_lvl_t priv_lvl_i,
|
||||
input priv_lvl_t ld_st_priv_lvl_i,
|
||||
|
@ -286,6 +287,9 @@ module mmu #(
|
|||
logic dtlb_is_2M_n, dtlb_is_2M_q;
|
||||
logic dtlb_is_1G_n, dtlb_is_1G_q;
|
||||
|
||||
// check if we need to do translation or if we are always ready (e.g.: we are not translating anything)
|
||||
assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1;
|
||||
|
||||
// The data interface is simpler and only consists of a request/response interface
|
||||
always_comb begin : data_interface
|
||||
// save request and DTLB response
|
||||
|
|
|
@ -87,7 +87,7 @@ module scoreboard #(
|
|||
issue_instr_o = decoded_instr_i;
|
||||
// make sure we assign the correct trans ID
|
||||
issue_instr_o.trans_id = issue_pointer_q;
|
||||
issue_instr_valid_o = ~issue_full && decoded_instr_valid_i;
|
||||
issue_instr_valid_o = ~issue_full && decoded_instr_valid_i && !flush_unissued_instr_i;
|
||||
decoded_instr_ack_o = issue_ack_i;
|
||||
end
|
||||
// maintain a FIFO with issued instructions
|
||||
|
@ -99,11 +99,11 @@ module scoreboard #(
|
|||
commit_pointer_n = commit_pointer_q;
|
||||
issue_pointer_n = issue_pointer_q;
|
||||
|
||||
// the decoded instruction we put in there is valid (1st bit)
|
||||
mem_n[issue_pointer_q] = {1'b0, decoded_instr_i};
|
||||
|
||||
// if we got a acknowledge from the issue stage, put this scoreboard entry in the queue
|
||||
if (issue_ack_i) begin
|
||||
// the decoded instruction we put in there is valid (1st bit)
|
||||
mem_n[issue_pointer_q] = {1'b1, decoded_instr_i};
|
||||
// increase the issue counter
|
||||
issue_cnt++;
|
||||
mem_n[issue_pointer_q].issued = 1'b1;
|
||||
|
|
|
@ -43,6 +43,7 @@ module store_unit (
|
|||
input logic [63:0] paddr_i, // physical address in
|
||||
input logic translation_valid_i,
|
||||
input exception ex_i,
|
||||
input logic dtlb_hit_i, // will be one in the same cycle translation_req was asserted if it hits
|
||||
// address checker
|
||||
input logic [11:0] page_offset_i,
|
||||
output logic page_offset_matches_o,
|
||||
|
@ -60,10 +61,9 @@ module store_unit (
|
|||
);
|
||||
assign result_o = 64'b0;
|
||||
|
||||
enum logic {IDLE, TRANSLATE} CS, NS;
|
||||
enum logic [1:0] {IDLE, VALID_STORE, WAIT_TRANSLATION, WAIT_STORE_READY} NS, CS;
|
||||
|
||||
logic [63:0] st_buffer_paddr; // physical address for store
|
||||
logic [63:0] st_buffer_data; // store buffer data out
|
||||
logic [63:0] st_data; // aligned data to store buffer
|
||||
logic st_buffer_valid;
|
||||
// store buffer control signals
|
||||
|
@ -71,10 +71,13 @@ module store_unit (
|
|||
logic st_valid;
|
||||
|
||||
// keep the data and the byte enable for the second cycle (after address translation)
|
||||
logic [63:0] st_data_q, st_data_n;
|
||||
logic [7:0] st_be_n, st_be_q;
|
||||
logic [63:0] st_data_n, st_data_q;
|
||||
logic [7:0] st_be_n, st_be_q;
|
||||
logic [TRANS_ID_BITS-1:0] trans_id_n, trans_id_q;
|
||||
|
||||
assign vaddr_o = vaddr_i;
|
||||
// output assignments
|
||||
assign vaddr_o = vaddr_i; // virtual address
|
||||
assign trans_id_o = trans_id_q; // transaction id from previous cycle
|
||||
|
||||
always_comb begin : store_control
|
||||
translation_req_o = 1'b0;
|
||||
|
@ -82,46 +85,80 @@ module store_unit (
|
|||
valid_o = 1'b0;
|
||||
st_valid = 1'b0;
|
||||
ex_o = ex_i;
|
||||
trans_id_o = trans_id_i;
|
||||
trans_id_n = trans_id_i;
|
||||
NS = CS;
|
||||
|
||||
case (CS)
|
||||
// we got a valid store
|
||||
IDLE: begin
|
||||
if (valid_i) begin
|
||||
// first do address translation, we need to do it in the first cycle since we want to share the MMU
|
||||
// between the load and the store unit. But we only know that when a new request arrives that we are not using
|
||||
// it at the same time.
|
||||
|
||||
NS = VALID_STORE;
|
||||
translation_req_o = 1'b1;
|
||||
|
||||
// check if translation was valid and we have space in the store buffer
|
||||
// otherwise simply stall
|
||||
if (!dtlb_hit_i) begin
|
||||
NS = WAIT_TRANSLATION;
|
||||
end
|
||||
|
||||
if (!st_ready) begin
|
||||
NS = WAIT_STORE_READY;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TRANSLATE: begin
|
||||
// check if translation was valid and we have space in the store buffer
|
||||
// otherwise simply stall
|
||||
if (translation_valid_i && st_ready) begin
|
||||
valid_o = 1'b1;
|
||||
// post this store to the store buffer
|
||||
st_valid = 1'b1;
|
||||
// -----------------
|
||||
// Access Exception
|
||||
// -----------------
|
||||
// we got an address translation exception (access rights)
|
||||
// this will also assert the translation valid
|
||||
if (ex_i.valid) begin
|
||||
// the only difference is that we do not want to store this request
|
||||
st_valid = 1'b0;
|
||||
end
|
||||
// we have another request
|
||||
if (valid_i) begin
|
||||
translation_req_o = 1'b1;
|
||||
// go back to idle
|
||||
end else begin
|
||||
NS = IDLE;
|
||||
end
|
||||
end else begin
|
||||
// keep on translating
|
||||
VALID_STORE: begin
|
||||
valid_o = 1'b1;
|
||||
// post this store to the store buffer
|
||||
st_valid = 1'b1;
|
||||
// -----------------
|
||||
// Access Exception
|
||||
// -----------------
|
||||
// we got an address translation exception (access rights)
|
||||
// this will also assert the translation valid
|
||||
if (ex_i.valid) begin
|
||||
// the only difference is that we do not want to store this request
|
||||
st_valid = 1'b0;
|
||||
end
|
||||
// we have another request
|
||||
if (valid_i) begin
|
||||
|
||||
translation_req_o = 1'b1;
|
||||
ready_o = 1'b0;
|
||||
|
||||
if (!dtlb_hit_i) begin
|
||||
NS = WAIT_TRANSLATION;
|
||||
end
|
||||
|
||||
if (!st_ready) begin
|
||||
NS = WAIT_STORE_READY;
|
||||
end
|
||||
// if we do not have another request go back to idle
|
||||
end else begin
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
// the store queue is currently full
|
||||
WAIT_STORE_READY: begin
|
||||
ready_o = 1'b0;
|
||||
// keep the translation request high
|
||||
translation_req_o = 1'b1;
|
||||
|
||||
if (st_ready && dtlb_hit_i) begin
|
||||
NS = VALID_STORE;
|
||||
end
|
||||
end
|
||||
|
||||
// we didn't receive a valid translation, wait for one
|
||||
// but we know that the store queue is not full as we could only have landed here if
|
||||
// it wasn't full
|
||||
WAIT_TRANSLATION: begin
|
||||
ready_o = 1'b0;
|
||||
translation_req_o = 1'b1;
|
||||
|
||||
if (dtlb_hit_i) begin
|
||||
NS = VALID_STORE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
@ -155,11 +192,12 @@ module store_unit (
|
|||
// store queue write port
|
||||
.valid_i ( st_valid ),
|
||||
.data_i ( st_data_q ),
|
||||
// store buffer in
|
||||
.be_i ( st_be_q ),
|
||||
// store buffer out
|
||||
.paddr_o ( st_buffer_paddr ),
|
||||
.data_o ( st_buffer_data ),
|
||||
.data_o ( ),
|
||||
.valid_o ( st_buffer_valid ),
|
||||
.be_o ( st_be_q ),
|
||||
.be_o ( ),
|
||||
.ready_o ( st_ready ),
|
||||
.*
|
||||
);
|
||||
|
@ -168,11 +206,15 @@ module store_unit (
|
|||
// ---------------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if(~rst_ni) begin
|
||||
st_be_q <= '0;
|
||||
st_data_q <= '0;
|
||||
CS <= IDLE;
|
||||
st_be_q <= '0;
|
||||
st_data_q <= '0;
|
||||
trans_id_q <= '0;
|
||||
end else begin
|
||||
st_be_q <= st_be_n;
|
||||
st_data_q <= st_data_n;
|
||||
CS <= NS;
|
||||
st_be_q <= st_be_n;
|
||||
st_data_q <= st_data_n;
|
||||
trans_id_q <= trans_id_n;
|
||||
end
|
||||
end
|
||||
// ------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue