Major revision of load/store interface

This commit is contained in:
Florian Zaruba 2017-06-20 16:31:08 +02:00
parent 7137e964fb
commit 81c18a9871
5 changed files with 181 additions and 118 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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
// ------------------