-from nmigen import Elaboratable, Module, Signal
-
-
-class L1Cache(Elaboratable):
- def __init__(self, config):
- self.config = config
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):
@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
--- /dev/null
+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))
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+# FIXME(programmerjake): add tests for config.py
--- /dev/null
+# FIXME(programmerjake): add tests for memory_op.py
--- /dev/null
+# FIXME(programmerjake): add tests for memory_pipe.py
--- /dev/null
+# FIXME(programmerjake): add tests for memory_queue.py
--- /dev/null
+# FIXME(programmerjake): add tests for memory_queue_entry.py