summaryrefslogtreecommitdiff
path: root/memory/dram.h
blob: f59c7a6730146194f1d9aa06ae1ac8a458e796ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#pragma once

#include <array>
#include <cstdint>
#include <cstring>
#include <istream>
#include <map>
#include <string>
#include <utility>

#include "memory/line.h"

namespace memory {
    struct dram : public infra::sim {
        static constexpr std::uint64_t PAGE_LINES_LOG2 = 20 - LINE_BYTES_LOG2;
        static constexpr std::uint64_t PAGE_LINES = 1 << PAGE_LINES_LOG2;
        static constexpr std::uint64_t PAGE_LINE_OFFSET_MASK = PAGE_LINES - 1;
        static constexpr std::uint64_t PAGE_BYTES_LOG2 = PAGE_LINES_LOG2 + LINE_BYTES_LOG2;
        static constexpr std::uint64_t PAGE_BYTES = 1 << PAGE_BYTES_LOG2;
        static constexpr std::uint64_t PAGE_BYTE_OFFSET_MASK = PAGE_BYTES - 1;

        typedef std::array<line, PAGE_LINES> page;

        std::map<std::uint64_t, page> image;

        struct response {
            infra::transaction transaction;
            std::uint64_t line_address;
            line data;
        };

        struct command {
            infra::transaction transaction;
            std::uint64_t line_address;
            line data;
            std::array<bool, LINE_BYTES> mask;
            bool write = false;
            infra::port<response> *responsep = nullptr;
        };

        infra::port<command> commandp;

        void clock() {
            if (commandp.can_read()) {
                const auto &c = commandp.peek();
                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;
                    if (c.write) {
                        pte(c.transaction, "s", fmt::format("store {:x}-{:x}", page_address, page_line));
                        if (c.responsep) {
                            response r;
                            r.transaction = c.transaction;
                            r.line_address = c.line_address;
                            r.data = c.data;
                            c.responsep->write(std::move(r));
                        }
                        auto [p, emplaced] = image.try_emplace(page_address);
                        if (emplaced)
                            for (unsigned int i = 0; i < PAGE_LINES; ++i)
                                p->second[i].fill(0);
                        auto &l = p->second[page_line];
                        for (unsigned int i = 0; i < LINE_BYTES; ++i)
                            if (c.mask[i])
                                l[i] = c.data[i];
                    } else {
                        pte(c.transaction, "f", fmt::format("fill {:x}-{:x}", page_address, page_line));
                        if (c.responsep) {
                            response r;
                            r.transaction = c.transaction;
                            r.line_address = c.line_address;
                            if (auto p = image.find(page_address); p != image.end())
                                r.data = p->second[page_line];
                            else
                                r.data.fill(0);
                            c.responsep->write(std::move(r));
                        }
                    }
                    commandp.discard();
                }
            }
        }

        void load(std::istream &fh) {
            for (unsigned int page = 0; ; ++page) {
                auto [p, emplaced] = image.try_emplace(page);
                if (emplaced)
                    for (unsigned int i = 0; i < PAGE_LINES; ++i)
                        p->second[i].fill(0);
                for (unsigned int line = 0; line < PAGE_LINES; ++line)
                    if (!fh.read(reinterpret_cast<char *>(p->second[line].data()), LINE_BYTES))
                        return;
            }
        }
    };
}