From 3452bbeab8cec280a96f2ff5ff4406bfdedc195c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 17 Apr 2020 17:05:56 -0700 Subject: [PATCH] adding WIP memory_pipe_experiment --- src/soc/memory_pipe_experiment/__init__.py | 6 -- src/soc/memory_pipe_experiment/config.py | 28 ++++++++- src/soc/memory_pipe_experiment/memory_op.py | 38 ++++++++++++ src/soc/memory_pipe_experiment/memory_pipe.py | 15 +++++ .../memory_pipe_experiment/memory_queue.py | 49 +++++++++++++++ .../memory_queue_entry.py | 59 +++++++++++++++++++ src/soc/memory_pipe_experiment/test_config.py | 1 + .../memory_pipe_experiment/test_memory_op.py | 1 + .../test_memory_pipe.py | 1 + .../test_memory_queue.py | 1 + .../test_memory_queue_entry.py | 1 + 11 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 src/soc/memory_pipe_experiment/memory_op.py create mode 100644 src/soc/memory_pipe_experiment/memory_pipe.py create mode 100644 src/soc/memory_pipe_experiment/memory_queue.py create mode 100644 src/soc/memory_pipe_experiment/memory_queue_entry.py create mode 100644 src/soc/memory_pipe_experiment/test_config.py create mode 100644 src/soc/memory_pipe_experiment/test_memory_op.py create mode 100644 src/soc/memory_pipe_experiment/test_memory_pipe.py create mode 100644 src/soc/memory_pipe_experiment/test_memory_queue.py create mode 100644 src/soc/memory_pipe_experiment/test_memory_queue_entry.py diff --git a/src/soc/memory_pipe_experiment/__init__.py b/src/soc/memory_pipe_experiment/__init__.py index 14e01321..e69de29b 100644 --- a/src/soc/memory_pipe_experiment/__init__.py +++ b/src/soc/memory_pipe_experiment/__init__.py @@ -1,6 +0,0 @@ -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 index 4a975192..76c02947 100644 --- a/src/soc/memory_pipe_experiment/config.py +++ b/src/soc/memory_pipe_experiment/config.py @@ -2,10 +2,32 @@ class MemoryPipeConfig: def __init__(self, *, bytes_per_cache_line=32, l1_way_count=8, - l1_set_count=64): + l1_set_count=64, + fu_op_id_shape=range(32), + fu_op_id_nop_value=0, + physical_address_bits=48, + memory_queue_chunk_size=4, + memory_queue_entry_count=8): self.bytes_per_cache_line = bytes_per_cache_line self.l1_way_count = l1_way_count self.l1_set_count = l1_set_count + self.fu_op_id_shape = fu_op_id_shape + self.fu_op_id_nop_value = fu_op_id_nop_value + self.physical_address_bits = physical_address_bits + self.memory_queue_chunk_size = memory_queue_chunk_size + self.memory_queue_entry_count = memory_queue_entry_count + + def memory_queue_chunk_entries_start_index(self, chunk_index): + """ entry index of the first memory queue entry in the chunk `chunk_index`. """ + return self.memory_queue_chunk_size * chunk_index + + def memory_queue_entry_index(self, chunk_index, index_in_chunk): + return self.memory_queue_chunk_size * chunk_index + index_in_chunk + + def memory_queue_chunk_entries_end_index(self, chunk_index): + """ one past the end entry index for in the chunk `chunk_index`. """ + v = self.memory_queue_chunk_size * (chunk_index + 1) + return min(v, self.memory_queue_entry_count) @property def l1_line_count(self): @@ -18,3 +40,7 @@ class MemoryPipeConfig: @property def bits_per_cache_line(self): return 8 * self.bytes_per_cache_line + + @property + def memory_queue_chunk_count(self): + return self.memory_queue_entry_count // self.memory_queue_chunk_size diff --git a/src/soc/memory_pipe_experiment/memory_op.py b/src/soc/memory_pipe_experiment/memory_op.py new file mode 100644 index 00000000..43bcaeb2 --- /dev/null +++ b/src/soc/memory_pipe_experiment/memory_op.py @@ -0,0 +1,38 @@ +import enum +from nmutil.iocontrol import Object +from nmigen import Signal +from .config import MemoryPipeConfig + + +class MemoryOpKind(enum.IntEnum): + Fence = enum.auto() + Read = enum.auto() + Write = enum.auto() + AMO = enum.auto() + LoadLinked = enum.auto() + StoreConditional = enum.auto() + + +class MemoryOpData(Object): + def __init__(self, config: MemoryPipeConfig): + self.config = config + Object.__init__(self) + self.kind = Signal(MemoryOpKind) + self.is_cachable = Signal() + self.blocks_combining_with_earlier_reads = Signal() + self.blocks_combining_with_earlier_writes = Signal() + self.blocks_combining_with_later_reads = Signal() + self.blocks_combining_with_later_writes = Signal() + self.is_speculative = Signal() + self.physical_address = Signal(config.physical_address_bits) + self.byte_mask = Signal(config.bytes_per_cache_line) + self.fu_op_id = Signal(config.fu_op_id_shape, + reset=self.config.fu_op_id_nop_value) + + @property + def is_empty(self): + self.fu_op_id == self.config.fu_op_id_nop_value + + def eq_empty(self): + """ assign self to the canonical empty value. """ + return self.eq(MemoryOpData(self.config)) diff --git a/src/soc/memory_pipe_experiment/memory_pipe.py b/src/soc/memory_pipe_experiment/memory_pipe.py new file mode 100644 index 00000000..9b8f5ee2 --- /dev/null +++ b/src/soc/memory_pipe_experiment/memory_pipe.py @@ -0,0 +1,15 @@ +from nmigen import Elaboratable, Signal, Module, Mux, Repl, Array +from .config import MemoryPipeConfig +from .l1_cache_memory import L1CacheMemory + + +class MemoryPipe(Elaboratable): + def __init__(self, config: MemoryPipeConfig): + self.config = config + self.l1_cache_memory = L1CacheMemory(config) + # FIXME(programmerjake): add MemoryQueue as submodule and wire everything up + + def elaborate(self, platform): + m = Module() + m.submodules.l1_cache_memory = self.l1_cache_memory + return m diff --git a/src/soc/memory_pipe_experiment/memory_queue.py b/src/soc/memory_pipe_experiment/memory_queue.py new file mode 100644 index 00000000..32f0698c --- /dev/null +++ b/src/soc/memory_pipe_experiment/memory_queue.py @@ -0,0 +1,49 @@ +from nmigen import Elaboratable, Module +from .config import MemoryPipeConfig +from .memory_queue_entry import MemoryQueueEntry +from typing import Optional + + +class MemoryQueueChunk(Elaboratable): + next_back_chunk: Optional["MemoryQueueChunk"] + + def __init__(self, config: MemoryPipeConfig, chunk_index: int): + self.config = config + self.chunk_index = chunk_index + start = config.memory_queue_chunk_entries_start_index(chunk_index) + end = config.memory_queue_chunk_entries_end_index(chunk_index) + self.entries = [MemoryQueueEntry(config) + for i in range(start, end)] + + def elaborate(self, platform): + m = Module() + for i in range(len(self.entries)): + entry = self.entries[i] + entry_index = self.config.memory_queue_entry_index( + self.chunk_index, i) + setattr(m.submodules, f"entry_{entry_index}", entry) + if self.next_back_chunk is not None and i < len(self.next_back_chunk.entries): + m.d.comb += entry.next_back_chunks_next_op.eq( + self.next_back_chunk.entries[i]) + else: + m.d.comb += entry.next_back_chunks_next_op.eq_empty() + return m + + +class MemoryQueue(Elaboratable): + def __init__(self, config: MemoryPipeConfig): + self.config = config + self.chunks = [MemoryQueueChunk(config, i) + for i in range(config.memory_queue_chunk_count)] + self.entries = [] + for chunk in self.chunks: + self.entries.extend(chunk.entries) + + def elaborate(self, platform): + m = Module() + for i in range(self.config.memory_queue_chunk_count): + chunk = self.chunks[i] + setattr(m.submodules, f"chunk_{i}", chunk) + if i > 0: + self.chunks[i - 1].next_back_chunk = chunk + return m diff --git a/src/soc/memory_pipe_experiment/memory_queue_entry.py b/src/soc/memory_pipe_experiment/memory_queue_entry.py new file mode 100644 index 00000000..d7f04f8e --- /dev/null +++ b/src/soc/memory_pipe_experiment/memory_queue_entry.py @@ -0,0 +1,59 @@ +from nmigen import Elaboratable, Module, Signal +from .config import MemoryPipeConfig +from .memory_op import MemoryOpData + + +class MemoryQueueEntryComb(Elaboratable): + """ Combinatorial state calculation for a memory queue entry, without shifting. """ + + def __init__(self, config: MemoryPipeConfig): + self.config = config + self.op = MemoryOpData(config) + self.op_out = MemoryOpData(config) + + def elaborate(self, platform): + m = Module() + + kind = self.op.kind + is_cachable = self.op.is_cachable + is_acquire_operation = self.op.is_acquire_operation + is_release_operation = self.op.is_release_operation + is_speculative = self.op.is_speculative + physical_address = self.op.physical_address + byte_mask = self.op.byte_mask + fu_op_id = self.op.fu_op_id + + # FIXME(programmerjake): wire up actual operations + + m.d.comb += self.op_out.kind.eq(kind) + m.d.comb += self.op_out.is_cachable.eq(is_cachable) + m.d.comb += self.op_out.is_acquire_operation.eq(is_acquire_operation) + m.d.comb += self.op_out.is_release_operation.eq(is_release_operation) + m.d.comb += self.op_out.is_speculative.eq(is_speculative) + m.d.comb += self.op_out.physical_address.eq(physical_address) + m.d.comb += self.op_out.byte_mask.eq(byte_mask) + m.d.comb += self.op_out.fu_op_id.eq(fu_op_id) + return m + + +class MemoryQueueEntry(Elaboratable): + def __init__(self, config: MemoryPipeConfig): + self.config = config + self.op = MemoryOpData(config) + self.next_op = MemoryOpData(config) + + """ `next_op` of corresponding memory queue entry in the next chunk towards the back of the queue. """ + self.next_back_chunks_next_op = MemoryOpData(config) + self.do_shift = Signal() + self.entry_comb = MemoryQueueEntryComb(config) + + def elaborate(self, platform): + m = Module() + m.submodules.entry_comb = self.entry_comb + m.d.comb += self.entry_comb.op.eq(self.op) + m.d.comb += self.next_op.eq(self.entry_comb.op_out) + with m.If(self.do_shift): + m.d.sync += self.op.eq(self.next_back_chunks_next_op) + with m.Else(): + m.d.sync += self.op.eq(self.next_op) + return m diff --git a/src/soc/memory_pipe_experiment/test_config.py b/src/soc/memory_pipe_experiment/test_config.py new file mode 100644 index 00000000..fd25a71d --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_config.py @@ -0,0 +1 @@ +# FIXME(programmerjake): add tests for config.py diff --git a/src/soc/memory_pipe_experiment/test_memory_op.py b/src/soc/memory_pipe_experiment/test_memory_op.py new file mode 100644 index 00000000..74644816 --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_memory_op.py @@ -0,0 +1 @@ +# FIXME(programmerjake): add tests for memory_op.py diff --git a/src/soc/memory_pipe_experiment/test_memory_pipe.py b/src/soc/memory_pipe_experiment/test_memory_pipe.py new file mode 100644 index 00000000..10bfa874 --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_memory_pipe.py @@ -0,0 +1 @@ +# FIXME(programmerjake): add tests for memory_pipe.py diff --git a/src/soc/memory_pipe_experiment/test_memory_queue.py b/src/soc/memory_pipe_experiment/test_memory_queue.py new file mode 100644 index 00000000..1a7aa76e --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_memory_queue.py @@ -0,0 +1 @@ +# FIXME(programmerjake): add tests for memory_queue.py diff --git a/src/soc/memory_pipe_experiment/test_memory_queue_entry.py b/src/soc/memory_pipe_experiment/test_memory_queue_entry.py new file mode 100644 index 00000000..9ea6521f --- /dev/null +++ b/src/soc/memory_pipe_experiment/test_memory_queue_entry.py @@ -0,0 +1 @@ +# FIXME(programmerjake): add tests for memory_queue_entry.py -- 2.30.2