Compare commits

...

3 Commits

Author SHA1 Message Date
df25550c8a Add cache arrays and test 2026-05-22 22:27:53 -07:00
3ea31e40aa Last commit before I nuke it 2026-05-22 09:10:19 -07:00
8fd83c2563 Get it to kinda work 2026-05-19 19:57:15 -07:00
7 changed files with 772 additions and 114 deletions

View File

@@ -0,0 +1,205 @@
import cocotb
from cocotb.handle import LogicArray
from cocotb.clock import Clock
from cocotb.triggers import ReadOnly, NextTimeStep, RisingEdge
import logging
import random
logger = logging.getLogger()
logger.setLevel(logging.INFO)
CLK_PERIOD = 5
SETS = 64
WAYS = 4
data_arrays = [{}, {}, {}, {}]
meta_arrays = [{}, {}, {}, {}]
@cocotb.test
async def test_sanity(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
for index in range(SETS):
for way in range(WAYS):
data = random.randbytes(64)
meta = random.randint(0, 2**22-1)
data_arrays[way][index] = data
meta_arrays[way][index] = meta
dut.i_cpu_write_data.value = LogicArray.from_bytes(data, byteorder="little")
dut.i_cpu_write_meta.value = meta
dut.i_cpu_write_index.value = index
dut.i_cpu_write_valid.value = 1 << way
await RisingEdge(dut.i_clk)
dut.i_cpu_write_valid.value = 0
await RisingEdge(dut.i_clk)
for index in range(SETS):
dut.i_cpu_read_index.value = index
dut.i_cpu_read_valid.value = 1
await RisingEdge(dut.i_clk)
await ReadOnly()
raw_data = dut.o_cpu_read_data.value
raw_meta = dut.o_cpu_read_meta.value
meta = [int(m) for m in raw_meta]
data_bytes = [v.to_bytes(byteorder="little") for v in raw_data]
expected_data = [data_arrays[way][index] for way in range(WAYS)]
expected_meta = [meta_arrays[way][index] for way in range(WAYS)]
if data_bytes != expected_data:
logger.info("Data Error")
if meta != expected_meta:
logger.info("Meta Error")
await NextTimeStep()
dut.i_cpu_read_valid.value = 0
for index in range(SETS):
for way in range(WAYS):
data = random.randbytes(64)
meta = random.randint(0, 2**22-1)
data_arrays[way][index] = data
meta_arrays[way][index] = meta
dut.i_snoop_write_data.value = LogicArray.from_bytes(data, byteorder="little")
dut.i_snoop_write_meta.value = meta
dut.i_snoop_write_index.value = index
dut.i_snoop_write_valid.value = 1 << way
await RisingEdge(dut.i_clk)
dut.i_snoop_write_valid.value = 0
await RisingEdge(dut.i_clk)
for index in range(SETS):
dut.i_snoop_read_index.value = index
dut.i_snoop_read_valid.value = 1
await RisingEdge(dut.i_clk)
await ReadOnly()
raw_data = dut.o_snoop_read_data.value
raw_meta = dut.o_snoop_read_meta.value
meta = [int(m) for m in raw_meta]
data_bytes = [v.to_bytes(byteorder="little") for v in raw_data]
expected_data = [data_arrays[way][index] for way in range(WAYS)]
expected_meta = [meta_arrays[way][index] for way in range(WAYS)]
if data_bytes != expected_data:
logger.info("Data Error")
if meta != expected_meta:
logger.info("Meta Error")
await NextTimeStep()
dut.i_snoop_read_valid.value = 0
@cocotb.test
async def test_random_access(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
ITERS = 1024
for _ in range(ITERS):
cpu_write_way = random.randint(0, WAYS-1)
cpu_write_set = random.randint(0, SETS-1)
while True:
snoop_write_way = random.randint(0, WAYS-1)
snoop_write_set = random.randint(0, SETS-1)
if snoop_write_way != cpu_write_way and snoop_write_set != cpu_write_set:
break
cpu_write_data = random.randbytes(64)
cpu_write_meta = random.randint(0, 2**22-1)
snoop_write_data = random.randbytes(64)
snoop_write_meta = random.randint(0, 2**22-1)
data_arrays[cpu_write_way][cpu_write_set] = cpu_write_data
meta_arrays[cpu_write_way][cpu_write_set] = cpu_write_meta
data_arrays[snoop_write_way][snoop_write_set] = snoop_write_data
meta_arrays[snoop_write_way][snoop_write_set] = snoop_write_meta
dut.i_cpu_write_data.value = LogicArray.from_bytes(cpu_write_data, byteorder="little")
dut.i_cpu_write_meta.value = cpu_write_meta
dut.i_cpu_write_index.value = cpu_write_set
dut.i_cpu_write_valid.value = 1 << cpu_write_way
dut.i_snoop_write_data.value = LogicArray.from_bytes(snoop_write_data, byteorder="little")
dut.i_snoop_write_meta.value = snoop_write_meta
dut.i_snoop_write_index.value = snoop_write_set
dut.i_snoop_write_valid.value = 1 << snoop_write_way
cpu_read_way = random.randint(0, WAYS-1)
cpu_read_set = random.randint(0, SETS-1)
snoop_read_way = random.randint(0, WAYS-1)
snoop_read_set = random.randint(0, SETS-1)
dut.i_cpu_read_index.value = cpu_read_set
dut.i_snoop_read_index.value = snoop_read_set
dut.i_cpu_read_valid.value = 1
dut.i_snoop_read_valid.value = 1
await RisingEdge(dut.i_clk)
await ReadOnly()
cpu_data = dut.o_cpu_read_data.value[cpu_read_way].to_bytes(byteorder="little")
cpu_meta = int(dut.o_cpu_read_meta.value[cpu_read_way])
snoop_data = dut.o_snoop_read_data.value[snoop_read_way].to_bytes(byteorder="little")
snoop_meta = int(dut.o_snoop_read_meta.value[snoop_read_way])
cpu_expected_data = data_arrays[cpu_read_way][cpu_read_set]
cpu_expected_meta = meta_arrays[cpu_read_way][cpu_read_set]
snoop_expected_data = data_arrays[snoop_read_way][snoop_read_set]
snoop_expected_meta = meta_arrays[snoop_read_way][snoop_read_set]
if cpu_data != cpu_expected_data:
logger.error("CPU Data Error")
if cpu_meta != cpu_expected_meta:
logger.info("CPU Meta Error")
if snoop_data != snoop_expected_data:
logger.error("snoop Data Error")
if snoop_meta != snoop_expected_meta:
logger.info("snoop Meta Error")
await NextTimeStep()

View File

@@ -1,19 +1,493 @@
import cocotb import cocotb
from cocotb.handle import Immediate from cocotb.handle import Immediate, LogicArray
from cocotb.simulator import get_sim_time
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import Timer, RisingEdge, FallingEdge, with_timeout from cocotb.triggers import Timer, RisingEdge, FallingEdge, with_timeout
from enum import IntEnum
from collections import defaultdict
from collections.abc import Mapping
import logging
import random
logger = logging.getLogger()
logger.setLevel(logging.INFO)
CLK_PERIOD = 5 CLK_PERIOD = 5
reference_cache_data = defaultdict(bytearray)
higher_cache_data = defaultdict(bytearray)
async def cpu_sequencer(dut, sequence: Mapping[int, int, bool, bool]):
addr, do, we, sync = sequence[0]
dut.i_addr.value = addr
dut.i_data.value = do
dut.i_we.value = we
dut.i_sync.value = sync
await FallingEdge(dut.i_rst)
index = 1
while index < len(sequence):
await RisingEdge(dut.i_clk)
if not dut.o_rdy.value:
continue
addr, do, we, sync = sequence[index]
dut.i_addr.value = addr
dut.i_data.value = do
dut.i_we.value = we
dut.i_sync.value = sync
index += 1
await Timer(150, "ns")
async def cpu_data_monitor(dut):
previous_address = 0
address = 0
we = 0
previous_we = 0
i_data = 0
previous_i_data = 0
await FallingEdge(dut.i_rst)
while True:
await RisingEdge(dut.i_clk)
if not dut.o_rdy.value:
continue
previous_address = address
previous_we = we
address = int(dut.i_addr.value)
we = int(dut.i_we.value)
previous_i_data = i_data
i_data = int(dut.i_data.value)
data = int(dut.o_data.value)
if previous_address == 0:
continue
# don't care if it was a write
if previous_we:
index = (previous_address // 64) % 64
offset = previous_address % 64
cacheline = reference_cache_data[index]
cacheline[offset] = previous_i_data
logger.debug(f"We saw a write here {index=} {offset=} previous_data={previous_i_data:x}")
else:
index = (previous_address // 64) % 64
offset = previous_address % 64
cacheline = reference_cache_data[index]
expected_data = cacheline[offset]
if (data != expected_data):
logger.error(f"{get_sim_time()} {address=:x} {previous_address=:x} {data=:x} {expected_data=:x}")
async def mmu_sequencer(dut):
while True:
await RisingEdge(dut.i_clk)
dut.i_phys_address.value = dut.i_addr.value
async def handle_higher_level_cache(dut):
dut.i_cache_rdy.value = 0
class CacheCmd(IntEnum):
CACHE_NONE = 0
CACHE_READ = 1
CACHE_WRITE = 2
while True:
await RisingEdge(dut.i_clk)
dut.i_cache_rdy.value = 0
if not dut.o_cache_valid.value:
continue
cmd = CacheCmd(dut.o_cache_cmd.value)
addr = int(dut.o_cache_addr.value)
logger.debug(f"{cmd=} {addr=}")
if cmd == CacheCmd.CACHE_READ:
if addr not in higher_cache_data:
data = bytearray(random.randbytes(64))
higher_cache_data[addr] = data
dut.i_cache_data.value = LogicArray.from_bytes(higher_cache_data[addr] , byteorder="little")
dut.i_cache_rdy.value = 1
reference_cache_data[int(dut.read_index.value)] = higher_cache_data[addr]
await RisingEdge(dut.i_clk)
dut.i_cache_rdy.value = 0
elif cmd == CacheCmd.CACHE_WRITE:
dut.i_cache_rdy.value = 1
data = dut.o_cache_data.value.to_bytes(byteorder="little")
higher_cache_data[addr] = bytearray(data)
await RisingEdge(dut.i_clk)
dut.i_cache_rdy.value = 0
@cocotb.test @cocotb.test
async def sanity_test(dut): async def sanity_test(dut):
expected_cache_misses = 0
expected_evictions = 0
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
cpu_sequence = [
(0x100, 0xaa, True, False),
(0x101, 0xbb, True, False),
(0x100, 0x00, False, False),
(0x101, 0x00, False, False),
(0x200, 0xcc, True, False),
(0x201, 0xdd, True, False),
(0x100, 0x00, False, False),
(0x101, 0x00, False, False),
(0x200, 0x00, False, False),
(0x201, 0x00, False, False),
(0x100, 0x11, True, False),
(0x101, 0x22, True, False),
(0x100, 0x00, False, False),
(0x200, 0x33, True, False),
(0x101, 0x00, False, False),
(0x201, 0x44, True, False),
(0x100, 0x00, False, False),
(0x200, 0x00, False, False),
(0x101, 0x00, False, False),
(0x201, 0x00, False, False),
]
dut.i_rst.value = Immediate(1) dut.i_rst.value = Immediate(1)
for _ in range(10): for _ in range(10):
await RisingEdge(dut.i_clk) await RisingEdge(dut.i_clk)
dut.i_rst.value = 0 dut.i_rst.value = 0
await Timer(1, "us") await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = 2
expected_evictions = 0
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def clean_evict_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
# Read from one cacheline, then read from an aliased cacheline without writing.
# cacheline should be overwritten without evicting
cpu_sequence = [
(0x100, 0x00, False, False),
(0x1100, 0x00, False, False),
]
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = 2
expected_evictions = 0
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def dirty_evict_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
# Read from one cacheline, then read from an aliased cacheline without writing.
# cacheline should be overwritten without evicting
cpu_sequence = [
(0x100, 0x41, True, False),
(0x101, 0x42, True, False),
(0x1100, 0x00, False, False),
(0x1100, 0xaa, True, False),
(0x100, 0x00, False, False)
]
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = 3
expected_evictions = 2
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def long_write_thrash_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_lines_read = 2**20//64
cpu_sequence = [
(i*64, i % 256, True, False)
for i in range(num_lines_read)]
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
# The last 64 lines aren't evicted
expected_cache_misses = num_lines_read
expected_evictions = num_lines_read - 64
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def long_write_read_thrash_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_lines_read = 2**20//64
cpu_sequence = [
(i*64, i % 256, True, False)
for i in range(num_lines_read)]
cpu_sequence.extend([
(i*64, 0, False, False)
for i in range(num_lines_read)])
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = num_lines_read * 2
expected_evictions = num_lines_read
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def long_write_linear_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_bytes_read = 2**16
cpu_sequence = [
(i, i % 256, True, False)
for i in range(num_bytes_read)]
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = num_bytes_read // 64
expected_evictions = num_bytes_read//64 - 64
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def long_write_read_linear_test(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_bytes_read = 2**16
cpu_sequence = [
(i, i % 256, True, False)
for i in range(num_bytes_read)]
cpu_sequence.extend([
(i, 0, False, False)
for i in range(num_bytes_read)])
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = (num_bytes_read // 64) * 2
expected_evictions = num_bytes_read // 64
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def short_write_read_linear_test(dut):
# What makes this test "short" is that we read 64 cachelines,
# so we shouldn't have to make any evictions
# TODO add number of evictions and cachlines loaded as performance counteres
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_bytes_read = 64*64
cpu_sequence = [
(i, i % 256, True, False)
for i in range(num_bytes_read)] # 64 bytes times 64 cachelines
cpu_sequence.extend([
(i, i % 256, False, False)
for i in range(num_bytes_read)]) # 64 bytes times 64 cachelines
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)
expected_cache_misses = num_bytes_read//64
expected_evictions = num_bytes_read//64 - 64
dut_evictions = int(dut.eviction_count.value)
dut_misses = int(dut.cache_miss_count.value)
if dut_evictions != expected_evictions:
logger.error(f"Eviction count mismatch! Expected {expected_evictions}, saw {dut_evictions}")
if dut_misses != expected_cache_misses:
logger.error(f"Miss count mismatch! Expected {expected_cache_misses}, saw {dut_misses}")
@cocotb.test
async def random_access_test(dut):
# Just fully random accesses
# This is also kind of a thrash test since this is not realistic
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(mmu_sequencer(dut))
cocotb.start_soon(handle_higher_level_cache(dut))
cocotb.start_soon(cpu_data_monitor(dut))
num_bytes_read = 2**18
cpu_sequence = [
(random.randint(0, 2**32), random.randint(0, 255), random.randint(0,1), random.randint(0,1))
for _ in range(num_bytes_read)] # 64 bytes times 64 cachelines
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await cpu_sequencer(dut, cpu_sequence)

View File

@@ -1,7 +1,7 @@
tests: tests:
- name: "application_wrapper_cache_l1_test" - name: "application_wrapper_cache_arrays_test"
toplevel: "application_wrapper_cache_l1" toplevel: "application_wrapper_cache_arrays"
modules: modules:
- "application_wrapper_cache_l1_test" - "application_wrapper_cache_arrays_test"
sources: "sources.list" sources: "sources.list"
waves: True waves: True

View File

@@ -0,0 +1,72 @@
module application_wrapper_cache_arrays #(
parameter NUM_WAYS = 4,
parameter NUM_SETS = 64,
localparam DATA_W = 64*8,
localparam OFFSET_W = 6,
localparam INDEX_W = $clog2(NUM_SETS),
localparam TAG_W = 32 - INDEX_W - OFFSET_W,
localparam META_W = TAG_W + 2
) (
input logic i_clk,
input logic [INDEX_W-1:0] i_cpu_read_index,
input logic i_cpu_read_valid,
output logic [DATA_W-1:0] o_cpu_read_data [NUM_WAYS],
output logic [META_W-1:0] o_cpu_read_meta [NUM_WAYS],
input logic [INDEX_W-1:0] i_cpu_write_index,
input logic [NUM_WAYS-1:0] i_cpu_write_valid,
input logic [DATA_W-1:0] i_cpu_write_data,
input logic [META_W-1:0] i_cpu_write_meta,
input logic [INDEX_W-1:0] i_snoop_read_index,
input logic i_snoop_read_valid,
output logic [DATA_W-1:0] o_snoop_read_data [NUM_WAYS],
output logic [META_W-1:0] o_snoop_read_meta [NUM_WAYS],
input logic [INDEX_W-1:0] i_snoop_write_index,
input logic [NUM_WAYS-1:0] i_snoop_write_valid,
input logic [DATA_W-1:0] i_snoop_write_data,
input logic [META_W-1:0] i_snoop_write_meta
);
// memory arrays.
// In order to make these WRITE_FIRST, we put a blocking assignment
// for the write data before the assignment to the read data
logic [DATA_W-1:0] data_arrays [NUM_SETS][NUM_WAYS];
logic [META_W-1:0] meta_arrays [NUM_SETS][NUM_WAYS];
always @(posedge i_clk) begin
for (int i = 0; i < NUM_WAYS; i++) begin
if (i_cpu_write_valid[i]) begin
data_arrays[i_cpu_write_index][i] = i_cpu_write_data;
meta_arrays[i_cpu_write_index][i] = i_cpu_write_meta;
end
if (i_snoop_write_valid[i]) begin
data_arrays[i_snoop_write_index][i] = i_snoop_write_data;
meta_arrays[i_snoop_write_index][i] = i_snoop_write_meta;
end
end
if (i_cpu_read_valid) begin
o_cpu_read_data = data_arrays[i_cpu_read_index];
o_cpu_read_meta = meta_arrays[i_cpu_read_index];
end
if (i_snoop_read_valid) begin
o_snoop_read_data = data_arrays[i_snoop_read_index];
o_snoop_read_meta = meta_arrays[i_snoop_read_index];
end
end
endmodule

View File

@@ -1,108 +0,0 @@
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

View File

@@ -10,4 +10,19 @@ package application_wrapper_cache_pkg;
logic write_through; logic write_through;
} page_table_entry_t; } page_table_entry_t;
typedef enum logic [2:0] {
CACHE_NONE,
CACHE_READ_SHARED,
CACHE_READ_UNIQUE,
CACHE_WRITE,
CACHE_CLEAN_UNIQUE
} cache_cmd_e;
typedef enum logic [1:0] {
MODIFIED,
EXCLUSIVE,
SHARED,
INVALID
} mesi_e;
endpackage endpackage

View File

@@ -1,5 +1,5 @@
cache/application_wrapper_cache_pkg.sv cache/application_wrapper_cache_pkg.sv
cache/application_wrapper_cache_l1.sv cache/application_wrapper_cache_arrays.sv
cache/application_wrapper_mmu.sv cache/application_wrapper_mmu.sv
cache/application_wrapper_cache_top.sv cache/application_wrapper_cache_top.sv