summaryrefslogtreecommitdiff
path: root/uarch/core.cpp
blob: 8b2db9bbb888924861aeb493078a3b1feb89da57 (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <fmt/format.h>
#include <iostream>

#include "uarch/core.h"

fetch_stage::fetch_stage(core &c)
    : c(c)
    , pc(c.checker.pc)
{ }

decode_stage::decode_stage(core &c)
    : c(c)
    , acc(c.checker.acc)
    , link(c.checker.link)
    , mq(c.checker.mq)
    , pc(c.checker.pc)
    , ctlregs(c.checker.ctlregs)
    , icount(c.checker.icount)
{ }

void fetch_stage::clock() {
    if (c.fetch_restarto.has_value()) {
        auto &r = *c.fetch_restarto;
        pte(r.tr, "!");
        gen = r.new_gen;
        pc = r.new_pc;
        didrestart = true;
        outstandingfill = false;
        c.fetch_restarto.reset();
    }

    if (c.fetch_mem_responsep.can_read()) {
        auto r = c.fetch_mem_responsep.read();
        cache.handle_response(r);
    }

    if (c.fetch_bundlep.can_write()) {
        fetch_bundle b;
        if (auto t = cache.fetchline(b.data, pc); t.has_value()) {
            b.tr = infra::pt::toplevel();
            b.gen = gen;
            b.pc = pc;
            if (didrestart)
                infra::pt::event(b.tr, ">", now-1, "");
            pte(b.tr, "F", fmt::format("pc={:05o}", b.pc));
            c.fetch_bundlep.write(std::move(b));
            pc = (pc & 070000) | (((pc & ~memory::LINE_BYTE_OFFSET_MASK) + memory::LINE_BYTES) & 007777);
            didrestart = false;
            outstandingfill = false;
        }
    }

    if (!outstandingfill && c./*fetch_*/mem_commandp.can_write() && !cache.probe(pc)) {
        memory::dram::command fr;
        fr.transaction = infra::pt::toplevel();
        fr.line_address = pc >> memory::LINE_BYTES_LOG2;
        fr.responsep = &c.fetch_mem_responsep;
        c./*fetch_*/mem_commandp.write(std::move(fr));
        outstandingfill = true;
    }
}

void decode_stage::clock() {
    bool progress = ctlregs[HALTED];

    if (!ctlregs[HALTED] && c.fetch_bundlep.can_read()) {
        auto b = c.fetch_bundlep.peek();

        if (b.gen != gen)
            goto bail_out;

        if ((b.pc >> memory::LINE_BYTES_LOG2) != (pc >> memory::LINE_BYTES_LOG2)) {
            pte(b.tr, "~");
            fetch_restart r;
            r.tr = b.tr;
            r.new_gen = ++gen;
            r.new_pc = pc;
            c.fetch_restarto = std::move(r);
            c.fetch_bundlep.discard();
            goto bail_out;
        }

        progress = true;

        auto tr = infra::pt::child(b.tr);

        pte(tr, "E");

        inst = decode(ctlregs[FLAGS],
                      pc,
                      b.data[pc & memory::LINE_BYTE_OFFSET_MASK],
                      interrupt);
        auto next_pc = inst.next_pc;

        if (inst.need_indirect_load) {
            auto addr = c.mem.fetch(inst.init_address.value());
            if (inst.need_autoinc_store) {
                addr = (addr + 1) & 07777;
                c.mem.store(*inst.init_address, addr);
            }
            auto df = (ctlregs[FLAGS] & FLAG_DF) >> FLAG_DF_SHIFT;
            inst.final_address = (df << 12) | addr;
        } else {
            assert(!inst.need_autoinc_store);
        }

        pte(tr, "", inst.disasm());

        if (inst.need_exec_load)
            inst.data = c.mem.fetch(inst.final_address.value());

        if (inst.need_read_acc)
            inst.acc = acc;
        if (inst.need_read_link)
            inst.link = link;
        if (inst.need_read_mq)
            inst.mq = mq;
        if (inst.read_ctlreg.has_value())
            inst.ctlval = ctlregs[*inst.read_ctlreg];

        inst.execute();

        if (inst.need_write_acc)
            acc = inst.acc.value();
        if (inst.need_write_link)
            link = inst.link.value();
        if (inst.need_write_mq)
            mq = inst.mq.value();
        if (inst.write_ctlreg.has_value())
            ctlregs[*inst.write_ctlreg] = inst.ctlval.value();

        if (inst.need_exec_store)
            c.mem.store(inst.final_address.value(), inst.data.value());

        assert(inst.next_pc == next_pc || inst.possibly_redirects);
        pc = inst.next_pc;

        if ((b.pc >> memory::LINE_BYTES_LOG2) != (pc >> memory::LINE_BYTES_LOG2))
            c.fetch_bundlep.discard();
    }
bail_out:

    if (progress)
        interrupt = c.system.interact(icount++, ctlregs);

    if (c.checker.icount != icount) {
        assert(c.checker.icount + 1 == icount);

        c.checker.execute();
        assert(c.checker.icount == icount);
//        std::cerr << fmt::format("icount={:} pc={:05o} checkerpc={:05o}\n", icount, pc, c.checker.pc);
        assert(pc == c.checker.pc);
        assert(acc == c.checker.acc);
        assert(link == c.checker.link);
        assert(mq == c.checker.mq);
        assert(ctlregs == c.checker.ctlregs);
        if (inst.init_address.has_value())
            assert(c.mem.fetch(*inst.init_address) == c.checker.mem.fetch(*inst.init_address));
        if (inst.final_address.has_value())
            assert(c.mem.fetch(*inst.final_address) == c.checker.mem.fetch(*inst.final_address));
        assert(c.mem.fetch(pc) == c.checker.mem.fetch(pc));
    }
}