diff --git a/src/load_unit.sv b/src/load_unit.sv index 68b061ce5..15f2aa692 100644 --- a/src/load_unit.sv +++ b/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 diff --git a/src/lsu.sv b/src/lsu.sv index 4a06c6aae..ad9637e29 100644 --- a/src/lsu.sv +++ b/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; diff --git a/src/mmu.sv b/src/mmu.sv index cb2b2aa5f..08c15daf8 100644 --- a/src/mmu.sv +++ b/src/mmu.sv @@ -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 diff --git a/src/scoreboard.sv b/src/scoreboard.sv index 2e5494a67..ae4df0731 100644 --- a/src/scoreboard.sv +++ b/src/scoreboard.sv @@ -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; diff --git a/src/store_unit.sv b/src/store_unit.sv index 2a1c0303b..e5ebd7f94 100644 --- a/src/store_unit.sv +++ b/src/store_unit.sv @@ -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 // ------------------