diff --git a/src/store_buffer.sv b/src/store_buffer.sv index 9b3ad755f..e99a41e81 100644 --- a/src/store_buffer.sv +++ b/src/store_buffer.sv @@ -49,6 +49,9 @@ module store_buffer ( input logic data_gnt_i, input logic data_rvalid_i ); + // depth of store-buffer + localparam int unsigned DEPTH = 8; + // we need to keep the tag portion of the address for a cycle later logic [43:0] address_tag_n, address_tag_q; logic tag_valid_n, tag_valid_q; @@ -70,77 +73,89 @@ module store_buffer ( logic [7:0] be; logic valid; // entry is valid logic is_speculative; // set if the entry isn't committed yet - } commit_queue_n, commit_queue_q; + } commit_queue_n [DEPTH-1:0], commit_queue_q [DEPTH-1:0]; + + logic [$clog2(DEPTH)-1:0] read_pointer_n, read_pointer_q; + logic [$clog2(DEPTH)-1:0] commit_pointer_n, commit_pointer_q; + logic [$clog2(DEPTH)-1:0] write_pointer_n, write_pointer_q; // those signals can directly be output to the memory - assign address_index_o = commit_queue_q.address[11:0]; + assign address_index_o = commit_queue_q[read_pointer_q].address[11:0]; // if we got a new request we already saved the tag from the previous cycle assign address_tag_o = address_tag_q; - assign data_wdata_o = commit_queue_q.data; - assign data_be_o = commit_queue_q.be; assign tag_valid_o = tag_valid_q; + assign data_wdata_o = commit_queue_q[read_pointer_q].data; + assign data_be_o = commit_queue_q[read_pointer_q].be; // we will never kill a request in the store buffer since we already know that the translation is valid // e.g.: a kill request will only be necessary if we are not sure if the requested memory address will result in a TLB fault assign kill_req_o = 1'b0; + // no store is pending if we don't have any uncommitted data, e.g.: the queue is either not valid or the entry is // speculative (it can be flushed) - assign no_st_pending_o = !commit_queue_q.valid || commit_queue_q.is_speculative; + assign no_st_pending_o = (!commit_queue_q[commit_pointer_q].valid || commit_queue_q[commit_pointer_q].is_speculative); + assign data_we_o = 1'b1; // we will always write in the store queue // memory interface always_comb begin : store_if - // if there is no commit pending and the uncommitted queue is empty as well we can accept the request - // if we got a grant this implies that the value was not speculative anymore and that we - // do not need to save the values anymore since the memory already processed them - automatic logic ready = !commit_queue_q.valid || data_gnt_i; - ready_o = ready && !flush_i; + // we are ready if the top-most entry is not valid + ready_o = !commit_queue_q[write_pointer_q].valid; + // default assignments + read_pointer_n = read_pointer_q; + write_pointer_n = write_pointer_q; + commit_pointer_n = commit_pointer_q; - address_tag_n = address_tag_q; - commit_queue_n = commit_queue_q; - tag_valid_n = 1'b0; + address_tag_n = address_tag_q; + commit_queue_n = commit_queue_q; - data_we_o = 1'b1; // we will always write in the store queue - data_req_o = 1'b0; + tag_valid_n = 1'b0; + data_req_o = 1'b0; // there should be no commit when we are flushing - if (!flush_i) begin - // if the entry in the commit queue is valid and not speculative anymore - // we can issue this instruction - // we can issue it as soon as the commit_i goes high or any number of cycles later - // by looking at the is_speculative flag - if (commit_queue_q.valid && (!commit_queue_q.is_speculative || commit_i)) begin - data_req_o = 1'b1; - if (data_gnt_i) begin - // we can evict it from the commit buffer - commit_queue_n.valid = 1'b0; - // save the tag portion - address_tag_n = commit_queue_q.address[55:12]; - // signal a valid tag the cycle afterwards - tag_valid_n = 1'b1; - end + // if the entry in the commit queue is valid and not speculative anymore we can issue this instruction + if (commit_queue_q[read_pointer_q].valid && !commit_queue_q[read_pointer_q].is_speculative) begin + data_req_o = 1'b1; + if (data_gnt_i) begin + // we can evict it from the commit buffer + commit_queue_n[read_pointer_q].valid = 1'b0; + // save the tag portion + address_tag_n = commit_queue_q[read_pointer_q].address[55:12]; + // signal a valid tag the cycle afterwards + tag_valid_n = 1'b1; + // advance the read_pointer + read_pointer_n = read_pointer_q + 1'b1; end - // we ignore the rvalid signal for now as we assume that the store - // happened end + // we ignore the rvalid signal for now as we assume that the store + // happened // shift the store request from the speculative buffer // to the non-speculative if (commit_i) begin - commit_queue_n.is_speculative = 1'b0; + commit_queue_n[commit_pointer_q].is_speculative = 1'b0; + commit_pointer_n = commit_pointer_q + 1'b1; end // LSU interface // we are ready to accept a new entry and the input data is valid - if (ready && valid_i) begin - commit_queue_n.address = paddr_i; - commit_queue_n.data = data_i; - commit_queue_n.be = be_i; - commit_queue_n.valid = 1'b1; - commit_queue_n.is_speculative = 1'b1; + if (ready_o && valid_i) begin + commit_queue_n[write_pointer_q].address = paddr_i; + commit_queue_n[write_pointer_q].data = data_i; + commit_queue_n[write_pointer_q].be = be_i; + commit_queue_n[write_pointer_q].valid = 1'b1; + commit_queue_n[write_pointer_q].is_speculative = 1'b1; + // advance the write pointer + write_pointer_n = write_pointer_q + 1; end - // when we flush evict the speculative store - if (flush_i && commit_queue_q.is_speculative) begin - commit_queue_n.valid = 1'b0; + // when we flush evict the speculative stores + if (flush_i) begin + for (int unsigned i = 0; i < DEPTH; i++) begin + if (commit_queue_q[i].is_speculative) begin + commit_queue_n[i].valid = 1'b0; + end + end + + write_pointer_n = commit_pointer_q; end end @@ -163,10 +178,13 @@ module store_buffer ( always_comb begin : address_checker page_offset_matches_o = 1'b0; // check if the LSBs are identical and the entry is valid - if ((page_offset_i[11:3] == commit_queue_q.address[11:3]) && commit_queue_q.valid) begin - page_offset_matches_o = 1'b1; + for (int unsigned i = 0; i < DEPTH; i++) begin + if ((page_offset_i[11:3] == commit_queue_q[i].address[11:3]) && commit_queue_q[i].valid) begin + page_offset_matches_o = 1'b1; + break; + end end - + // or it matches with the entry we are currently putting into the queue if ((page_offset_i[11:3] == paddr_i[11:3]) && valid_i) begin page_offset_matches_o = 1'b1; end @@ -176,15 +194,22 @@ module store_buffer ( // registers always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ if(~rst_ni) begin - address_tag_q <= 'b0; - tag_valid_q <= 1'b0; - commit_queue_q <= '{default: 0}; + address_tag_q <= 'b0; + tag_valid_q <= 1'b0; + commit_queue_q <= '{default: 0}; + read_pointer_q <= '0; + write_pointer_q <= '0; + commit_pointer_q <= '0; end else begin - commit_queue_q <= commit_queue_n; - tag_valid_q <= tag_valid_n; - address_tag_q <= address_tag_n; + commit_queue_q <= commit_queue_n; + tag_valid_q <= tag_valid_n; + address_tag_q <= address_tag_n; + read_pointer_q <= read_pointer_n; + write_pointer_q <= write_pointer_n; + commit_pointer_q <= commit_pointer_n; end end + `ifndef SYNTHESIS `ifndef verilator // assert that commit is never set when we are flushing this would be counter intuitive diff --git a/src/store_unit.sv b/src/store_unit.sv index ea81187b1..ab52a5133 100644 --- a/src/store_unit.sv +++ b/src/store_unit.sv @@ -87,47 +87,50 @@ module store_unit ( IDLE: begin if (valid_i) begin - NS = VALID_STORE; + NS = VALID_STORE; translation_req_o = 1'b1; - + pop_st_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; + pop_st_o = 1'b0; end if (!st_ready) begin NS = WAIT_STORE_READY; + pop_st_o = 1'b0; end end end VALID_STORE: begin - ready_o = 1'b0; + // ready_o = 1'b0; valid_o = 1'b1; - // post this store to the store buffer + // post this store to the store buffer if we are not flushing if (!flush_i) st_valid = 1'b1; - pop_st_o = 1'b1; + // we have another request + if (valid_i) begin - // // we have another request - // if (valid_i) begin + translation_req_o = 1'b1; + NS = VALID_STORE; + pop_st_o = 1'b1; - // translation_req_o = 1'b1; + if (!dtlb_hit_i) begin + NS = WAIT_TRANSLATION; + pop_st_o = 1'b0; + end - // 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 - NS = IDLE; + if (!st_ready) begin + pop_st_o = 1'b0; + 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 @@ -137,7 +140,7 @@ module store_unit ( translation_req_o = 1'b1; if (st_ready && dtlb_hit_i) begin - NS = VALID_STORE; + NS = IDLE; end end @@ -149,7 +152,7 @@ module store_unit ( translation_req_o = 1'b1; if (dtlb_hit_i) begin - NS = VALID_STORE; + NS = IDLE; end end endcase