From db82579d3c023c441c895d26d32de3fa039eafa4 Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Fri, 11 Nov 2022 16:28:55 -0800 Subject: Starting to write a real memory subsystem for biggolf. --- infra/arbiter.h | 31 ++++++------------- memory/cache.h | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory/dram.h | 50 +++++++++++++++++++++++++++--- memory/line.h | 4 +-- 4 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 memory/cache.h diff --git a/infra/arbiter.h b/infra/arbiter.h index 79a9920..63ddbd6 100644 --- a/infra/arbiter.h +++ b/infra/arbiter.h @@ -3,53 +3,40 @@ #include #include +#include "infra/port.h" #include "infra/sim.h" namespace infra { template struct priority_arbiter : public sim { - std::array, peers> peerp; + std::array *, peers> peerp; port *outp = nullptr; void clock() { for (unsigned int i = 0; i < peers; ++i) { - if (outp->can_write() && peerp[i].can_read()) - outp->write(peerp[i].read()); + if (outp->can_write() && peerp[i]->can_read()) + outp->write(peerp[i]->read()); } } }; template struct round_robin_arbiter : public sim { - std::array, peers> peerp; + std::array *, peers> peerp; port *outp = nullptr; unsigned int initial = 0; void clock() { bool initially_empty = outp->can_write(); for (unsigned int i = initial; i < peers; ++i) { - if (outp->can_write() && peerp[i].can_read()) - outp->write(peerp[i].read()); + if (outp->can_write() && peerp[i]->can_read()) + outp->write(peerp[i]->read()); } for (unsigned int i = 0; i < initial; ++i) { - if (outp->can_write() && peerp[i].can_read()) - outp->write(peerp[i].read()); + if (outp->can_write() && peerp[i]->can_read()) + outp->write(peerp[i]->read()); } if (initially_empty && !outp->can_write()) if (++initial == peers) initial = 0; } }; - - template struct shared_bus : public sim { - std::array, peers> peerp; - port *outp = nullptr; - - void clock() { - for (unsigned int i = 0; i < peers; ++i) { - if (peerp[i].can_read()) { - assert(outp->can_write()); - outp->write(peerp[i].read()); - } - } - } - }; } diff --git a/memory/cache.h b/memory/cache.h new file mode 100644 index 0000000..8554910 --- /dev/null +++ b/memory/cache.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include "memory/dram.h" +#include "memory/line.h" + +namespace memory { + template struct inline_cache { + static constexpr std::uint64_t SETS = 1 << SETS_LOG2; + static constexpr std::uint64_t LINE_SET_OFFSET_MASK = SETS - 1; + + std::uint64_t last_serial = 0; + + struct tag { + std::uint64_t serial = 0; + std::uint64_t line_address; + infra::transaction transaction; + }; + + typedef std::array set_tags; + typedef std::array set_data; + + std::array tags; + std::array data; + + void handle_response(const dram::response &r) { + auto set_address = r.line_address & LINE_SET_OFFSET_MASK; + auto &stags = tags[set_address]; + auto &sdata = data[set_address]; + for (unsigned int i = 0; i < WAYS; ++i) { + auto &stag = stags[i]; + if (stag.serial && stag.line_address == r.line_address) { + handle_response(r, stag, sdata[i]); + return; + } + } + std::uint64_t min_serial = ~(std::uint64_t)0; + unsigned int victim; + for (unsigned int i = 0; i < WAYS; ++i) { + auto &stag = stags[i]; + if (stag.serial < min_serial) { + min_serial = stag.serial; + victim = i; + } + } + handle_response(r, stags[victim], sdata[victim]); + } + + void handle_response(const dram::response &r, tag &t, line &d) { + t.serial = ++last_serial; + t.line_address = r.line_address; + t.transaction = r.transaction; + d = r.data; + } + + std::optional probe(std::uint64_t address) { + auto line_address = address >> LINE_BYTES_LOG2; + auto set_address = line_address & LINE_SET_OFFSET_MASK; + auto &stags = tags[set_address]; + for (unsigned int i = 0; i < WAYS; ++i) { + auto &stag = stags[i]; + if (stag.serial && stag.line_address == line_address) + return stag; + } + return {}; + } + + std::optional fetchline(line &dst, std::uint64_t address) { + auto line_address = address >> LINE_BYTES_LOG2; + auto set_address = line_address & LINE_SET_OFFSET_MASK; + auto &stags = tags[set_address]; + auto &sdata = data[set_address]; + for (unsigned int i = 0; i < WAYS; ++i) { + auto &stag = stags[i]; + if (stag.serial && stag.line_address == line_address) { + stag.serial = ++last_serial; + dst = sdata[i]; + return stag; + } + } + return {}; + } + + std::optional fetch(unsigned int &dst, std::uint64_t address) { + line data; + if (auto tag = fetchline(data, address); tag.has_value()) { + auto line_offset = address & LINE_BYTE_OFFSET_MASK; + dst = data[line_offset]; + return tag; + } + return {}; + } + }; +} diff --git a/memory/dram.h b/memory/dram.h index f59c7a6..31aea12 100644 --- a/memory/dram.h +++ b/memory/dram.h @@ -21,6 +21,8 @@ namespace memory { typedef std::array page; + const unsigned int latency; + std::map image; struct response { @@ -38,11 +40,26 @@ namespace memory { infra::port *responsep = nullptr; }; - infra::port commandp; + infra::port *commandp = nullptr; + + unsigned int progress = 0; + std::optional current_command; + + dram(unsigned int latency=0) + : latency(latency) + { } void clock() { - if (commandp.can_read()) { - const auto &c = commandp.peek(); + if (!current_command.has_value() && commandp->can_read()) { + current_command = commandp->read(); + progress = latency; + } + if (current_command.has_value()) { + if (progress) { + --progress; + return; + } + const auto &c = *current_command; if (!c.responsep || c.responsep->can_write()) { auto page_address = c.line_address >> PAGE_LINES_LOG2; auto page_line = c.line_address & PAGE_LINE_OFFSET_MASK; @@ -76,7 +93,7 @@ namespace memory { c.responsep->write(std::move(r)); } } - commandp.discard(); + current_command.reset(); } } } @@ -92,5 +109,30 @@ namespace memory { return; } } + + unsigned int fetch(std::uint64_t address) const { + auto page_address = address >> PAGE_LINES_LOG2; + if (auto i = image.find(page_address); i != image.end()) { + auto &page = i->second; + auto line_address = (address & PAGE_LINE_OFFSET_MASK) >> LINE_BYTES_LOG2; + auto &line = page[line_address]; + auto word_address = address & LINE_BYTE_OFFSET_MASK; + return line[word_address]; + } + return 0; + } + + void store(std::uint64_t address, unsigned int value) { + auto page_address = address >> PAGE_LINES_LOG2; + auto [p, emplaced] = image.try_emplace(page_address); + auto &page = p->second; + if (emplaced) + for (unsigned int i = 0; i < PAGE_LINES; ++i) + page[i].fill(0); + auto line_address = (address & PAGE_LINE_OFFSET_MASK) >> LINE_BYTES_LOG2; + auto &line = page[line_address]; + auto word_address = address & LINE_BYTE_OFFSET_MASK; + line[word_address] = value; + } }; } diff --git a/memory/line.h b/memory/line.h index 3377ec8..82b85f5 100644 --- a/memory/line.h +++ b/memory/line.h @@ -7,9 +7,9 @@ #include "infra/sim.h" namespace memory { - constexpr std::uint64_t LINE_BYTES_LOG2 = 4; + constexpr std::uint64_t LINE_BYTES_LOG2 = 2; constexpr std::uint64_t LINE_BYTES = 1 << LINE_BYTES_LOG2; constexpr std::uint64_t LINE_BYTE_OFFSET_MASK = LINE_BYTES - 1; - typedef std::array line; + typedef std::array line; } -- cgit v1.2.3