108 lines
2.9 KiB
Systemverilog
108 lines
2.9 KiB
Systemverilog
import application_wrapper_cache_pkg::*;
|
|
|
|
module application_wrapper_cache_l1 #(
|
|
parameter CACHELINE_SIZE = 64,
|
|
parameter CACHELINE_COUNT = 64,
|
|
localparam ADDR_WIDTH = 32
|
|
)(
|
|
input logic i_clk,
|
|
input logic i_rst,
|
|
|
|
/* CPU Interface */
|
|
input logic [ADDR_WIDTH-1:0] i_addr,
|
|
input logic i_we,
|
|
input logic [7:0] i_data,
|
|
output logic [7:0] o_data,
|
|
|
|
input logic i_rdy,
|
|
output logic o_rdy,
|
|
|
|
/* MMU Interface */
|
|
input logic [ADDR_WIDTH-1:0] i_phys_address,
|
|
output page_table_entry_t i_table_entry,
|
|
input logic i_mmu_valid,
|
|
|
|
/* Higher level cache interface */
|
|
output logic [ADDR_WIDTH-1:0] o_addr,
|
|
output logic [1:0] o_cache_cmd,
|
|
output logic o_cache_valid,
|
|
|
|
output logic [63:0] o_cache_data,
|
|
input logic [31:0] i_cache_data,
|
|
input logic i_cache_rdy
|
|
);
|
|
|
|
// we have 32 bit addresses, 64 byte cache lines, and 64 total lines.
|
|
// Thats 6 bit for offset, 6 bit for index, and 20 bit for cache.
|
|
|
|
// cache is virtually indexed, physically tagged
|
|
|
|
localparam OFFSET_W = $clog2(CACHELINE_SIZE);
|
|
localparam INDEX_W = $clog2(CACHELINE_COUNT);
|
|
localparam TAG_W = ADDR_WIDTH - INDEX_W - OFFSET_W;
|
|
localparam META_W = 3; // valid, unique, clean
|
|
|
|
logic [OFFSET_W-1:0] offset;
|
|
logic [INDEX_W-1:0] index;
|
|
logic [TAG_W-1:0] tag;
|
|
|
|
assign offset = i_addr[OFFSET_W-1:0];
|
|
assign index = i_addr[INDEX_W+OFFSET_W-1:OFFSET_W];
|
|
assign tag = i_addr[INDEX_W+OFFSET_W+TAG_W-1:INDEX_W+OFFSET_W];
|
|
|
|
// cacheline size is in bytes, not bits
|
|
// direct mapped cache, read one line so we have data ready if its a hit.
|
|
logic [CACHELINE_SIZE*8-1:0] data_array [CACHELINE_COUNT];
|
|
logic [META_W+TAG_W-1:0] meta_tag_array [CACHELINE_COUNT];
|
|
|
|
enum logic [1:0] {IDLE, READY, EVICT, READ} state, state_next;
|
|
|
|
always_ff @(posedge i_clk) begin
|
|
if (i_rst) begin
|
|
state <= IDLE;
|
|
end else begin
|
|
state <= state_next;
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
state_next = state;
|
|
|
|
o_rdy = '0;
|
|
|
|
case (state)
|
|
IDLE: begin
|
|
state_next = READY;
|
|
end
|
|
|
|
READY: begin
|
|
o_rdy = '1;
|
|
end
|
|
|
|
EVICT: begin
|
|
|
|
end
|
|
|
|
READ: begin
|
|
|
|
end
|
|
endcase
|
|
end
|
|
|
|
/*
|
|
|
|
In the ready state, we read from the data array and if the line is valid
|
|
and the tag matches with the address, we present the data to the cpu.
|
|
Otherwise, we lower o_rdy and send the request to the higher level cache.
|
|
|
|
If what we read was valid but the tag didn't match, then we need to evict it.
|
|
If the line was not valid, then we don't need to evict it and can just request
|
|
the new data.
|
|
|
|
One thing that we also need is an MMU. The TLB can be 1 cycle, then if the TLB
|
|
says that we are allowed to read from the cache, we can read from the cache.
|
|
*/
|
|
|
|
|
|
|
|
endmodule |