From ac37c3a59328a8bb8dc450387e6f58a7e5d1fc8a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 16 Apr 2020 19:37:15 -0700 Subject: [PATCH] add memory_pipe_experiment --- src/soc/memory_pipe_experiment/__init__.py | 6 ++ src/soc/memory_pipe_experiment/config.py | 20 ++++ .../memory_pipe_experiment/l1_cache_memory.py | 92 +++++++++++++++++++ .../test_l1_cache_memory.py | 50 ++++++++++ 4 files changed, 168 insertions(+) create mode 100644 src/soc/memory_pipe_experiment/__init__.py create mode 100644 src/soc/memory_pipe_experiment/config.py create mode 100644 src/soc/memory_pipe_experiment/l1_cache_memory.py create mode 100644 src/soc/memory_pipe_experiment/test_l1_cache_memory.py diff --git a/src/soc/memory_pipe_experiment/__init__.py b/src/soc/memory_pipe_experiment/__init__.py new file mode 100644 index 00000000..14e01321 --- /dev/null +++ b/src/soc/memory_pipe_experiment/__init__.py @@ -0,0 +1,6 @@ +from nmigen import Elaboratable, Module, Signal + + +class L1Cache(Elaboratable): + def __init__(self, config): + self.config = config diff --git a/src/soc/memory_pipe_experiment/config.py b/src/soc/memory_pipe_experiment/config.py new file mode 100644 index 00000000..4a975192 --- /dev/null +++ b/src/soc/memory_pipe_experiment/config.py @@ -0,0 +1,20 @@ +class MemoryPipeConfig: + def __init__(self, *, + bytes_per_cache_line=32, + l1_way_count=8, + l1_set_count=64): + self.bytes_per_cache_line = bytes_per_cache_line + self.l1_way_count = l1_way_count + self.l1_set_count = l1_set_count + + @property + def l1_line_count(self): + return self.l1_way_count * self.l1_set_count + + @property + def l1_byte_count(self): + return self.l1_line_count * self.bytes_per_cache_line + + @property + def bits_per_cache_line(self): + return 8 * self.bytes_per_cache_line diff --git a/src/soc/memory_pipe_experiment/l1_cache_memory.py b/src/soc/memory_pipe_experiment/l1_cache_memory.py new file mode 100644 index 00000000..cb0fc184 --- /dev/null +++ b/src/soc/memory_pipe_experiment/l1_cache_memory.py @@ -0,0 +1,92 @@ +from nmigen import Elaboratable, Signal, Module, Mux, Repl, Array +from nmigen.hdl.mem import ReadPort, WritePort, Memory +from .config import MemoryPipeConfig + + +class L1CacheMemory(Elaboratable): + """ The data memory for the L1 cache. + + It is conceptually organized into `config.l1_way_count` ways, + where each way has `config.l1_sets_count` sets, + where each set is a single cache line of `config.bytes_per_cache_line` + bytes. None of the dimensions must be powers of 2, but must all be at + least 1. + + The memory has a single R/W port that can read or write + (but not both) an entire cache line each cycle. + When writing, writing to each byte can individually be enabled by setting + the corresponding bit in `write_byte_en`. + + The results of reading are available after the next clock edge. + + The address is divided into `set_index` and `way_index`. + + Parameters: + + config: MemoryPipeConfig + The configuration. + + Attributes: + + config: MemoryPipeConfig + The configuration. + set_index: Signal(range(config.l1_set_count)) + The input index of the set to read/write. + way_index: Signal(range(config.l1_way_count)) + The input index of the way to read/write. + write_byte_en: Signal(config.bytes_per_cache_line) + The per-byte write enable inputs. + write_enable: Signal() + The overall write enable input. + Set to 1 to write and to 0 to read. + read_data: Signal(config.bits_per_cache_line) + The read data output. + write_data: Signal(config.bits_per_cache_line) + The write data input. + + """ + + def __init__(self, config: MemoryPipeConfig): + self.config = config + self.set_index = Signal(range(config.l1_set_count), reset_less=True) + self.way_index = Signal(range(config.l1_way_count), + reset_less=True) + self.write_byte_en = Signal(config.bytes_per_cache_line, + reset_less=True) + self.write_enable = Signal(reset_less=True) + self.read_data = Signal(config.bits_per_cache_line, + reset_less=True) + self.write_data = Signal(config.bits_per_cache_line, + reset_less=True) + + def elaborate(self, platform): + m = Module() + read_data_signals = [] + for way in range(self.config.l1_way_count): + way_memory_name = f"way_memory_{way}" + way_memory = Memory(width=self.config.bits_per_cache_line, + depth=self.config.l1_set_count, + name=way_memory_name) + write_port = WritePort(way_memory, granularity=8) + setattr(m.submodules, way_memory_name + '_write_port', write_port) + m.d.comb += write_port.addr.eq(self.set_index) + m.d.comb += write_port.data.eq(self.write_data) + way_enable = Signal(name=f"way_enable_{way}", reset_less=True) + m.d.comb += way_enable.eq(way == self.way_index) + way_write_enable = self.write_enable & way_enable + way_read_enable = ~self.write_enable & way_enable + m.d.comb += write_port.en.eq( + Repl(way_write_enable, + self.config.bytes_per_cache_line + ) & self.write_byte_en) + read_port = ReadPort(way_memory, transparent=False) + setattr(m.submodules, way_memory_name + '_read_port', read_port) + m.d.comb += read_port.addr.eq(self.set_index) + m.d.comb += read_port.en.eq(way_read_enable) + read_data_signals.append(read_port.data) + + last_way_index = Signal.like(self.way_index) + m.d.sync += last_way_index.eq(self.way_index) + read_data = Array(read_data_signals) + m.d.comb += self.read_data.eq(read_data[last_way_index]) + return m diff --git a/src/soc/memory_pipe_experiment/test_l1_cache_memory.py b/src/soc/memory_pipe_experiment/test_l1_cache_memory.py new file mode 100644 index 00000000..3bc9e929 --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_l1_cache_memory.py @@ -0,0 +1,50 @@ +from nmigen.back import rtlil +from nmigen.back.pysim import Simulator, Delay +import unittest +from .config import MemoryPipeConfig +from .l1_cache_memory import L1CacheMemory + + +class TestL1CacheMemory(unittest.TestCase): + def test_l1_cache_memory(self): + config = MemoryPipeConfig(bytes_per_cache_line=4, + l1_way_count=8, + l1_set_count=32) + base_name = "test_l1_cache_memory" + with self.subTest(part="synthesize"): + dut = L1CacheMemory(config) + vl = rtlil.convert(dut) + with open(f"{base_name}.il", "w") as f: + f.write(vl) + dut = L1CacheMemory(config) + sim = Simulator(dut) + clock_period = 1e-6 + sim.add_clock(clock_period) + + def process(): + for set_index in range(config.l1_set_count): + for way_index in range(config.l1_way_count): + yield dut.set_index.eq(set_index) + yield dut.way_index.eq(way_index) + yield dut.write_enable.eq(1) + yield dut.write_byte_en.eq(0xF) + write_data = set_index * 0x10 + way_index + write_data *= 0x00010001 + write_data ^= 0x80808080 + yield dut.write_data.eq(write_data) + yield + yield dut.set_index.eq(set_index) + yield dut.way_index.eq(way_index) + yield dut.write_enable.eq(0) + yield + yield Delay(clock_period / 10) + yield dut.set_index.eq(set_index + 1) + yield dut.way_index.eq(way_index + 1) + yield Delay(clock_period / 10) + read_data = (yield dut.read_data) + self.assertEqual(read_data, write_data) + + sim.add_sync_process(process) + with sim.write_vcd(vcd_file=open(f"{base_name}.vcd", "w"), + gtkw_file=open(f"{base_name}.gtkw", "w")): + sim.run() -- 2.30.2