blob: af5e6d5e8af2569428b43425c5d23f761db8b0f5 [file] [log] [blame]
/*
*
* This file is part of the Elpis processor project.
*
* Copyright © 2020-present. All rights reserved.
* Authors: Aurora Tomas and Rodrigo Huerta.
*
* This file is licensed under both the BSD-3 license for individual/non-commercial
* use. Full text of both licenses can be found in LICENSE file.
*/
`default_nettype none
`ifdef TESTS
`include "elpis/definitions.v"
`else
`include "/project/openlane/user_proj_example/../../verilog/rtl/elpis/definitions.v"
`endif
module tlb #(parameter CORE_ID=0, parameter CACHE_TYPE=`CACHE_TYPE_ICACHE)(
input clk,
input reset,
input[31:0] virtual_addr,
input[31:0] virtual_addr_write_in,
input privilege_mode, // 0 - users, 1 - system
input tlb_we,
input tlb_re,
input[19:0] physical_addr_write_in,
input[19:0] physical_addr_in,
output [19:0] physical_addr_out,
output hit_tlb,
output exc_protected_page
);
reg[19:0] tlb_pages[0:`NUM_TLB_LINES-1];
reg[7:0] tlb_frames[0:`NUM_TLB_LINES-1];
reg tlbValidBits[0:`NUM_TLB_LINES-1];
reg tlb_lru[0:`NUM_TLB_LINES-1];
wire[19:0] page;
wire[19:0] candidate_physical_addr_out;
assign exc_protected_page = (tlb_we && !privilege_mode) ? 1'b1 : 1'b0;
assign page = tlb_we ? virtual_addr_write_in[31:12] : virtual_addr[31:12];
wire[$clog2(`NUM_TLB_LINES)-1:0] id_candidate_write;
reg[$clog2(`NUM_TLB_LINES)-1:0] id_candidate_read, id_candidate_write_empty, id_candidate_write_no_empty, id_last_accessed;
reg found_read, found_write_empty;
integer i;
always@(*) begin : get_id_read
id_candidate_read = 4'b0;
found_read = 1'b0;
for (i=0; i < `NUM_TLB_LINES; i=i+1) begin
if ((page == tlb_pages[i]) && tlbValidBits[i]) begin
id_candidate_read = i[3:0];
found_read = 1'b1;
end
end
end
integer j;
always@(*) begin : get_id_write_empty
id_candidate_write_empty = 0;
found_write_empty = 0;
for (j=0; j < `NUM_TLB_LINES; j=j+1) begin
if ((tlbValidBits[j] == 0) && !found_write_empty) begin
found_write_empty = 1'b1;
id_candidate_write_empty = j[3:0];
end
end
end
integer k, n_1_lru;
always@(*) begin : get_id_candidate_write_no_empty
id_candidate_write_no_empty = 0;
for (k=0; k < `NUM_TLB_LINES; k=k+1) begin
if (tlb_lru[k] == 1'b0) begin
id_candidate_write_no_empty = k[$clog2(`NUM_TLB_LINES)-1:0];
end
end
end
integer u;
always@(posedge clk) begin : count_n_1_lru
if (reset) begin
n_1_lru = 0;
end else begin
n_1_lru = n_1_lru + (tlb_we && privilege_mode);
end
end
integer c;
always@(posedge clk) begin : plru_management
if (reset) begin
for (c=0; c < `NUM_TLB_LINES; c=c+1) begin
tlb_lru[c] <= 1'b0;
end
end else begin
if(n_1_lru == `NUM_TLB_LINES) begin
for (c=0; c < `NUM_TLB_LINES; c=c+1) begin
tlb_lru[c] <= id_last_accessed == c;
end
end
tlb_lru[id_last_accessed] <= 1'b1;
end
end
assign hit_tlb = found_read || privilege_mode;
assign candidate_physical_addr_out = (tlb_re && !privilege_mode && found_read) ? {tlb_frames[id_candidate_read], virtual_addr[11:0]} : (privilege_mode ? virtual_addr[19:0] : 20'b0);
assign physical_addr_out = candidate_physical_addr_out;
assign id_candidate_write = found_write_empty ? id_candidate_write_empty : id_candidate_write_no_empty;
integer l;
always@(posedge clk) begin : write_tlb_structures
if (reset) begin
for (l=0; l < `NUM_TLB_LINES; l=l+1) begin
tlbValidBits[l] <= 0;
end
end else begin
if (tlb_we && privilege_mode) begin // TLBwrite
tlb_pages[id_candidate_write] <= page;
tlb_frames[id_candidate_write] <= physical_addr_write_in[19:12];
tlbValidBits[id_candidate_write] <= 1'b1;
id_last_accessed <= id_candidate_write;
end else if (tlb_re && !privilege_mode && found_read) begin // TLBRead
id_last_accessed <= id_candidate_read;
end
end
end
endmodule