Get it to kinda work

This commit is contained in:
2026-05-19 19:57:15 -07:00
parent 62a3408eb7
commit 8fd83c2563
3 changed files with 771 additions and 33 deletions

View File

@@ -5,35 +5,36 @@ module application_wrapper_cache_l1 #(
parameter CACHELINE_COUNT = 64,
localparam ADDR_WIDTH = 32
)(
input logic i_clk,
input logic i_rst,
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 [ADDR_WIDTH-1:0] i_addr,
input logic i_we,
input logic i_sync,
input logic [7:0] i_data,
output logic [7:0] o_data,
input logic i_rdy,
output logic o_rdy,
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,
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 [ADDR_WIDTH-1:0] o_cache_addr,
output cache_cmd_e 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
output logic [CACHELINE_SIZE*8-1:0] o_cache_data,
input logic [CACHELINE_SIZE*8-1: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.
// Thats 6 bit for offset, 6 bit for index, and 20 bit for tag.
// cache is virtually indexed, physically tagged
@@ -42,49 +43,292 @@ 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;
typedef struct {
logic [TAG_W-1:0] tag;
logic valid;
logic shared;
logic clean;
} meta_tag_t;
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];
logic [OFFSET_W-1:0] offset, offset_d1;
logic [INDEX_W-1:0] index, index_d1, index_d2;
logic [TAG_W-1:0] tag, tag_d1;
// 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];
meta_tag_t meta_tag_array [CACHELINE_COUNT];
enum logic [1:0] {IDLE, READY, EVICT, READ} state, state_next;
logic [CACHELINE_SIZE*8-1:0] current_data, current_data_next, write_data_prev;
meta_tag_t current_meta_tag, current_meta_tag_next;
logic [OFFSET_W-1:0] read_offset, read_offset_next;
logic [INDEX_W-1:0] read_index, read_index_next;
logic [ADDR_WIDTH-1:0] read_address, read_address_next;
logic [ADDR_WIDTH-1:0] write_address, write_address_next;
logic [CACHELINE_SIZE*8-1:0] write_data;
meta_tag_t write_meta_tag;
logic [INDEX_W-1:0] write_index;
logic data_write_enable;
logic meta_tag_write_enable;
logic we_d1;
logic latched_we, latched_we_next;
logic [7:0] latched_data, latched_data_next;
logic [7:0] data_d1;
// performance counters
logic [31:0] eviction_count, eviction_count_next;
logic [31:0] cache_miss_count, cache_miss_count_next;
// reset counter
logic [INDEX_W-1:0] clear_counter, clear_counter_next;
enum logic [2:0] {RESET, CLEAR, IDLE, READY, EVICT, READ} prev_state, state, state_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
state <= IDLE;
state <= RESET;
current_data <= '0;
tag_d1 <= '0;
index_d1 <= '0;
offset_d1 <= '0;
read_address <= '0;
write_address <= '0;
latched_we <= '0;
latched_data <= '0;
eviction_count <= '0;
cache_miss_count <= '0;
clear_counter <= '0;
end else begin
prev_state <= state;
state <= state_next;
current_data <= current_data_next;
write_data_prev <= write_data;
current_meta_tag <= current_meta_tag_next;
read_offset <= read_offset_next;
read_index <= read_index_next;
read_address <= read_address_next;
write_address <= write_address_next;
if (data_write_enable) begin
data_array[write_index] <= write_data;
end
if (meta_tag_write_enable) begin
meta_tag_array[write_index] <= write_meta_tag;
end
tag_d1 <= tag;
index_d1 <= index;
index_d2 <= index_d1;
offset_d1 <= offset;
we_d1 <= i_we;
data_d1 <= i_data;
latched_we <= latched_we_next;
latched_data <= latched_data_next;
eviction_count <= eviction_count_next;
cache_miss_count <= cache_miss_count_next;
clear_counter <= clear_counter_next;
end
end
always_comb begin
state_next = state;
current_data_next = current_data;
current_meta_tag_next = current_meta_tag;
read_offset_next = read_offset;
read_index_next = read_index;
read_address_next = read_address;
write_address_next = write_address;
latched_we_next = latched_we;
latched_data_next = latched_data;
o_rdy = '0;
o_cache_valid = '0;
o_cache_cmd = CACHE_NONE;
o_cache_addr = '0;
o_cache_data = '0;
// vipt
offset = i_addr[OFFSET_W-1:0];
index = i_addr[INDEX_W+OFFSET_W-1:OFFSET_W];
tag = i_phys_address[INDEX_W+OFFSET_W+TAG_W-1:INDEX_W+OFFSET_W];
write_index = '0;
write_data = '0;
data_write_enable = '0;
write_meta_tag.tag = '0;
write_meta_tag.valid = '0;
write_meta_tag.shared = '0;
write_meta_tag.clean = '0;
meta_tag_write_enable = '0;
o_data = '0;
eviction_count_next = eviction_count;
cache_miss_count_next = cache_miss_count;
clear_counter_next = clear_counter;
case (state)
IDLE: begin
state_next = READY;
RESET: begin
state_next = CLEAR;
end
READY: begin
CLEAR: begin
if (clear_counter == (INDEX_W)'(CACHELINE_COUNT-1)) begin
state_next = IDLE;
end
write_data = '0;
data_write_enable = '1;
meta_tag_write_enable = '1;
write_meta_tag.tag = '0;
write_meta_tag.valid = '0;
write_meta_tag.shared = '0;
write_meta_tag.clean = '0;
write_index = clear_counter;
clear_counter_next = clear_counter + 1;
end
IDLE: begin
state_next = READY;
current_data_next = data_array[index];
current_meta_tag_next = meta_tag_array[index];
o_rdy = '1;
end
EVICT: begin
READY: begin
if (!current_meta_tag.valid || (current_meta_tag.valid && current_meta_tag.tag != tag_d1 && current_meta_tag.clean)) begin
// current line is not valid, just read
// OR current line is valid, but clean so we don't need to write it back.
state_next = READ;
read_index_next = index_d1;
read_offset_next = offset_d1;
read_address_next = {i_phys_address[31:OFFSET_W], (OFFSET_W)'('0)};
latched_we_next = we_d1;
latched_data_next = data_d1;
cache_miss_count_next = cache_miss_count + 1;
end else if (current_meta_tag.valid && current_meta_tag.tag != tag_d1 && !current_meta_tag.clean) begin
// current line was valid, but the wrong tag.
state_next = EVICT;
read_index_next = index_d1;
read_offset_next = offset_d1;
read_address_next = {i_phys_address[31:OFFSET_W], (OFFSET_W)'('0)};
write_address_next = {current_meta_tag.tag, index_d1, (OFFSET_W)'('0)};
latched_we_next = we_d1;
latched_data_next = data_d1;
cache_miss_count_next = cache_miss_count + 1;
eviction_count_next = eviction_count + 1;
end else begin
latched_we_next = i_we;
latched_data_next = i_data;
// always be loading the next data array
current_data_next = data_array[index];
current_meta_tag_next = meta_tag_array[index];
// We are accessing something we just wrote to
if (latched_we) begin
write_data = current_data;
write_data[offset_d1*8 +: 8] = latched_data;
data_write_enable = '1;
meta_tag_write_enable = '1;
write_meta_tag = current_meta_tag;
write_meta_tag.clean = '0;
write_index = index_d1;
if (index == write_index) begin
current_data_next = write_data;
end
end else begin
// we have a possible RAW hazard, but not after READ state
if (prev_state == READY && index_d1 == index_d2) begin
o_data = current_data[offset_d1*8 +: 8];
end else begin
o_data = current_data[offset_d1*8 +: 8];
end
end
o_rdy = '1;
end
end
EVICT: begin
o_cache_addr = write_address;
o_cache_cmd = CACHE_WRITE;
o_cache_valid = '1;
o_cache_data = current_data;
if (i_cache_rdy) begin
state_next = READ;
end
end
READ: begin
o_cache_addr = read_address;
o_cache_cmd = CACHE_READ;
o_cache_valid = '1;
write_index = read_index;
write_data = i_cache_data;
write_meta_tag.tag = read_address[31:INDEX_W+OFFSET_W];
write_meta_tag.valid = '1;
write_meta_tag.shared = '0;
write_meta_tag.clean = ~latched_we; // if we are about to write, then mark dirty
data_write_enable = i_cache_rdy;
meta_tag_write_enable = i_cache_rdy;
if (i_cache_rdy) begin
state_next = READY;
current_data_next = i_cache_data;
current_meta_tag_next = write_meta_tag;
index = write_index;
tag = read_address[31:INDEX_W+OFFSET_W];
offset = read_offset;
end
end
default: begin
state_next = READY;
end
endcase
end
@@ -101,6 +345,20 @@ end
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.
how do we handle writes? Since we take 1 cycle to read from the cache, we cannot
immediately write to the cache line in one cycle, we will have to wait a cycle
in order to determine if the cacheline is valid or not. To do this, we will need
to have it be pipelined, so that we store the data temporarily while we read the
meta_tag array, then if its valid we write to the cache. To avoid RAW hazards, we
also need to store the address and check if we are reading a value we just wrote.
If so, then we return this stored value instead of reading from ram, since we would
be reading at the same time as we are writing, and that could be undefined.
basically if the index matches the previous access, then we have a hazard and need to
use the stored cacheline instead of the cacheline we read from memory, since that hasn't
been updated yet. We don't need to cache metatag since if we just wrote to it, it will
already be dirty anyway.
*/

View File

@@ -10,4 +10,10 @@ package application_wrapper_cache_pkg;
logic write_through;
} page_table_entry_t;
typedef enum logic [1:0] {
CACHE_NONE,
CACHE_READ,
CACHE_WRITE
} cache_cmd_e;
endpackage