Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod alu;
mod five_stage;
mod memory;
mod simple_ooo;
mod utils;

Expand Down
63 changes: 63 additions & 0 deletions src/memory/mem_fabric.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* Core abstraction capturing what sort of interactions a memory
can handle */
pub trait MemProtocol {
type Req;
type Resp;
}

pub type RequestId = u8;

/* Consumer facing trait that abstracts away all the
core logic of a memory unit */
pub trait Memory<P: MemProtocol> {
fn send(&mut self, req: P::Req) -> RequestId;
fn recv(&mut self) -> Option<P::Resp>;
fn tick(&mut self);
}

/* Simplest memory protocol with straight forward
reads and writes only. */
pub struct SimpleRW;

pub struct ReadReq(pub u32);
pub struct WriteReq(pub u32, pub u32);

pub enum SimpleRWReq {
Read(ReadReq),
Write(WriteReq),
}

impl From<ReadReq> for SimpleRWReq {
fn from(r: ReadReq) -> Self {
Self::Read(r)
}
}
impl From<WriteReq> for SimpleRWReq {
fn from(w: WriteReq) -> Self {
Self::Write(w)
}
}

pub struct ReadResp(pub RequestId, pub u32);
pub struct WriteResp(pub RequestId);

pub enum SimpleRWResp {
Read(ReadResp),
Write(WriteResp),
}

impl From<ReadResp> for SimpleRWResp {
fn from(r: ReadResp) -> Self {
Self::Read(r)
}
}
impl From<WriteResp> for SimpleRWResp {
fn from(w: WriteResp) -> Self {
Self::Write(w)
}
}

impl MemProtocol for SimpleRW {
type Req = SimpleRWReq;
type Resp = SimpleRWResp;
}
2 changes: 2 additions & 0 deletions src/memory/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod mem_fabric;
pub mod simple_cache;
24 changes: 24 additions & 0 deletions src/memory/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Memory hierarchy modelling

This module introduces tools for modelling memory hierarchies.

## Memory fabrics

Here we think of memory modules (of any kind) as an opaque object that exposes one or more ports for communication. These ports can carry different sort of messages depending on the implementation.

We adopt the hierarchic point of view, where there is an "owner" and a "memory" parties, even when in some cases the owner itself might be a memory of another node. The goal of the interface, then, is to expose a simple control surface for the owner.

Memory modules should implement the `Memory<P>` trait, with `P` implementing then `MemoryProtocol` trait, thus modelling the fact that different memory modules might support different semantics. Memory protocols by and large consist of the sort of messages that a memory module can receive and send. Therefore, the `Memory` trait acts as the "stitches" in the memory fabric. In more complicated situations one should create custom tailored messages etc., to provide a safe and constrained environment for passing

Then, memory modules should be modelled by `dyn Memory<P>` trait objects. This interface exposes a singe send and receive channels, which is where communication with the memory happens. The upside of this approach is that memory modules expose a uniform interface. However, the downside is that the number and nature of memory ports is obscured (e.g. `SimpleCache` has a separate read and write port, but these are not exposed to the user, rather the requests are sorted.)


## Simple cache

A simple cache functional cache implementation can be found in `simple_cache.rs`. The main features of this cache are:
- Separate read and write ports,
- Tunable cache geometry
- Cycle-by-cycle simulation of the cache, that is, depending on the cache bank in which a given data item resides the cache access might take more or less time
- Always write-through implementation

The cache implementation is somewhat messy due to the fact that per-cycle simulation was a primary goal. This means that one needs a way to implement co-routines that can release control once they've done one cycle's worth of work, and resume execution. This was achieved by implementing a `Port` struct, which provides a wrapped task queue, and a functional implementation of coroutines. See `utils/step.rs`.
Loading
Loading