Get it to ACTUALLY compile :)

This commit is contained in:
2026-05-24 20:30:34 -07:00
parent 6c6c3d295b
commit cdbb6a9720
6 changed files with 76 additions and 498 deletions

View File

@@ -1,493 +0,0 @@
import cocotb
from cocotb.handle import Immediate, LogicArray
from cocotb.simulator import get_sim_time
from cocotb.clock import Clock
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
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
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(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)
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 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

@@ -0,0 +1,34 @@
import cocotb
from cocotb.handle import Immediate
from cocotb.clock import Clock
from cocotb.triggers import Timer, RisingEdge
import logging
import random
logger = logging.getLogger()
logger.setLevel(logging.INFO)
CLK_PERIOD = 5
@cocotb.test
async def test_sanity(dut):
# Request a read from the cache, then request a write to the cache
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
dut.i_cpu_we.value = 0
dut.i_rst.value = Immediate(1)
for _ in range(10):
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await RisingEdge(dut.o_cpu_rdy)
await Timer(10, "us")

View File

@@ -5,9 +5,17 @@ tests:
- "application_wrapper_cache_arrays_test"
sources: "sources.list"
waves: True
- name: "application_wrapper_cache_miss_handler_test"
toplevel: "application_wrapper_cache_miss_handler"
modules:
- "application_wrapper_cache_miss_handler_test"
sources: "sources.list"
waves: True
- name: "application_wrapper_cache_top_test"
toplevel: "application_wrapper_cache_top"
modules:
- "application_wrapper_cache_top_test"
sources: "sources.list"
waves: True

View File

@@ -1,7 +1,13 @@
import application_wrapper_cache_pkg::*;
module application_wrapper_cache_bus_interface #(
parameter DATA_W = 64*8
parameter DATA_W = 64*8,
// these are all wip
localparam REQ_W = 32,
localparam RSP_W = 32,
localparam DAT_W = 512+64,
localparam SNP_W = 32
) (
input logic i_clk,
input logic i_rst,

View File

@@ -1,8 +1,12 @@
module application_wrapper_cache_lru #(
// This should be NUM_WAYS - 1
parameter LRU_W = 3,
parameter NUM_SETS = 64
parameter NUM_SETS = 64,
localparam INDEX_W = $clog2(NUM_SETS)
) (
input logic i_clk,
input logic [INDEX_W-1:0] i_read_index,
input logic i_read_valid,
output logic [LRU_W-1:0] o_read_data,

View File

@@ -10,14 +10,19 @@ module application_wrapper_cache_top #(
localparam TAG_W = 32 - INDEX_W - OFFSET_W,
localparam LRU_W = NUM_WAYS-1,
localparam META_W = TAG_W + 2
localparam META_W = TAG_W + 2,
localparam REQ_W = 32,
localparam RSP_W = 32,
localparam DAT_W = 512+64,
localparam SNP_W = 32
) (
input logic i_clk,
input logic i_rst,
// CPU Interface
input logic [31:0] i_cpu_addr,
input logic i_we,
input logic i_cpu_we,
input logic i_cpu_sync,
input logic [7:0] i_cpu_data,
output logic [7:0] o_cpu_data,
@@ -140,6 +145,11 @@ logic [31:0] snoop_writeback_addr;
logic snoop_writeback_valid;
logic snoop_writeback_done;
logic [DATA_W-1:0] bus_writeback_data;
logic [31:0] bus_writeback_addr;
logic bus_writeback_valid;
logic bus_writeback_done;
logic [31:0] cpu_memory_addr;
logic cpu_memory_valid;
cache_cmd_e cpu_memory_cmd;
@@ -148,6 +158,11 @@ logic [DATA_W-1:0] cpu_memory_data;
logic cpu_memory_done;
cache_resp_e cpu_memory_resp;
logic [31:0] snoop_addr;
snoop_cmd_e snoop_cmd;
logic snoop_valid;
logic [31:0] snoop_memory_addr;
logic snoop_memory_valid;
cache_cmd_e snoop_memory_cmd;
@@ -164,6 +179,8 @@ assign cpu_tag = mmu_phys_address[31:INDEX_W+OFFSET_W];
assign cpu_index = i_cpu_addr[INDEX_W+OFFSET_W-1:OFFSET_W];
assign cpu_offset = i_cpu_addr[OFFSET_W-1:0];
assign o_cpu_rdy = miss_handler_rdy;
application_wrapper_cache_miss_handler #(
.NUM_WAYS (NUM_WAYS),
.NUM_SETS (NUM_SETS)
@@ -250,6 +267,8 @@ application_wrapper_cache_lru #(
.LRU_W (LRU_W),
.NUM_SETS (NUM_SETS)
) u_lru (
.i_clk (i_clk),
.i_read_index (cpu_lru_read_index),
.i_read_valid (cpu_lru_read_valid),
.o_read_data (cpu_lru_read_data),