summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--BigGolf135
-rw-r--r--Makefile52
-rw-r--r--infra/arbiter.h42
-rw-r--r--infra/pipetrace.cpp9
-rw-r--r--infra/pipetrace.h37
-rw-r--r--infra/port.h45
-rw-r--r--infra/queue.h29
-rw-r--r--infra/sim.cpp9
-rw-r--r--infra/sim.h38
-rw-r--r--infra/stat.h35
-rw-r--r--isa/checker.cpp44
-rw-r--r--isa/decode.cpp96
-rw-r--r--isa/isa.h44
-rw-r--r--memory/dram.h96
-rw-r--r--memory/line.h15
-rwxr-xr-xpt104
-rwxr-xr-xtest58
17 files changed, 888 insertions, 0 deletions
diff --git a/BigGolf b/BigGolf
new file mode 100644
index 0000000..9a308bd
--- /dev/null
+++ b/BigGolf
@@ -0,0 +1,135 @@
1The "Big Golf" Microarchitecture
2
3
4
5Allowed memory combinations:
6 * Any two loads
7 * Any two stores with different addresses (n.b. LLC is limited to 1 eviction per cycle)
8 * Any load with any younger store
9
10Instruction opcodes:
11 0 AND logical AND from memory to accumulator
12 1 TAD Two's-complement ADd from memory to accumulator
13 2 ISZ Increment and Skip if Zero
14 3 DCA Deposit and Clear Accumulator
15 4 JMS JuMp Subroutine
16 5 JMP JuMP
17 6 IOT In-Out Transfer (device accesses)
18 7 OPR microsequenced OPeRations (miscellaneous, like clear/rotate/etc)
19
20Memory transactions: Opcodes that do it: (second set is the indirect versions)
21 * Fetch instruction 01234567 01234567
22 * Indirect address load 0123
23 * Autoincrement store 0123
24 * Execution load 012 012 45
25 * Execution store 234 234
26
27
28
29┌─────┐ ┌──────┐ ┌────┐
30│Fetch├──────►│Decode│ ┌►│Exec│
31└─────┘ └──────┘ │ └────┘
32
33 next_pc ┌──init_indirect_load │ init_execution_store
34 │ init_execution_load──┤ retire
35 │ init_execution_store │
36 │ retire │
37 │ rubberband_stall(1/2)│
38 │ │
39 │ ┌───────┐ │
40 └►│Autoinc│ │
41 └───────┘ │
42
43 ┌──init_autoinc_store │
44 │ init_execution_load──┤
45 │ init_execution_store │
46 │ retire │
47 │ │
48 │ ┌─────┐ │
49 └►│Indir│ │
50 └─────┘ │
51
52 init_execution_load──┘
53 init_execution_store
54 retire
55
56
57
58Possible arbitration techniques:
59 * Rubberband stalling in Decode + positional arbitration
60 * Age/address/operation comparison without rubberbanding
61 * Longer clock cycles, or
62 * Extra cycle
63
64What to do with cache misses?
65 * Stall entire pipeline to maintain simpler ordering constraints
66 * If only loads are missing, allow everything else to proceed?
67 * Always allow Fetch to proceed?
68
69Need separate logic to detect SMC clobbers *anyway*
70
71
72
73OPR opcodes:
74
75 "group 1"
76 _0___1___2_ _3_ _4_ _5_ _6_ _7_ _8_ _9_ _10 _11
77 | | | | | | |RAR|RAL| 0 | |
78 | 1 1 1 | 0 |CLA|CLL|CMA|CML|RTR|RTL| 1 |IAC|
79 |___|___|___|___|___|___|___|___|___|___|___|___|
80
81 CLA CLear Accumulator
82 CLL CLear Link
83 CMA CoMplement Accumulator
84 CML CoMplement Link
85 RAR Rotate Accumulator Right (if bit 10 is 0)
86 RAL Rotate Accumulator Left (if bit 10 is 0)
87 RTR Rotate (Twice) accumulator and link Right (if bit 10 is 1)
88 RTL Rotate (Twice) accumulator and link Left (if bit 10 is 1)
89 IAC Increment ACcumulator
90 BSW Byte Swap word in accumulator (if bits 8 and 9 are 0, and bit 10 is 1)
91
92 Logical order of operations:
93 CLA, CLL
94 CMA, CML
95 IAC
96 RAR, RAL, RTR, RTL, BSW
97
98 "group 2"
99 _0___1___2_ _3_ _4_ _5_ _6_ _7_ _8_ _9_ _10 _11
100 | | | |SMA|SZA|SNL| 0 | | | |
101 | 1 1 1 | 1 |CLA|SPA|SNA|SZL| 1 |OSR|HLT| 0 |
102 |___|___|___|___|___|___|___|___|___|___|___|___|
103
104 SMA Skip on Minus Accumulator (skip if high bit of accumulator is set) (if bit 8 is 0)
105 SPA Skip on Plus Accumulator (skip if high bit of accumulator is clear) (if bit 8 is 1)
106 SZA Skip on Zero Accumulator (if bit 8 is 0)
107 SNA Skip on Nonzero Accumulator (if bit 8 is 1)
108 SNL Skip on Nonzero Link (if bit 8 is 0)
109 SZL Skip on Zero Link (if bit 8 is 1)
110 OSR bitwise Or Switch Register into accumulator
111 HLT HaLT processor
112 CLA CLear Accumulator
113
114 Logical order of operations:
115 SMA, SZA, SNL
116 SPA, SNA, SZL
117 CLA
118 OSR, HLT
119
120 "mq"
121 _0___1___2_ _3_ _4_ _5_ _6_ _7_ _8_ _9_ _10 _11
122 | | | | | | | | | | |
123 | 1 1 1 | 1 |CLA|MQA| |MQL| | | | 1 |
124 |___|___|___|___|___|___|___|___|___|___|___|___|
125
126 CLA CLear Accumulator
127 MQL MQ Loads from Accumulator
128 MQA bitwise or MQ into Accumulator
129
130 bits 6,8,9,10 are used for extended arithmetic instructions
131 see https://homepage.divms.uiowa.edu/~jones/pdp8/refcard/74.html
132
133 Logical order of operations:
134 CLA
135 MQA, MQL (simultaneous parallel assignment)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..863826d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,52 @@
1BUILD := build
2
3override PACKAGES := fmt
4
5WARNINGS := -Wall -Werror
6OPTIMIZE := -Og -flto
7DEBUG := -g
8
9CXX := g++
10CXXFLAGS := $(WARNINGS) $(OPTIMIZE) $(DEBUG)
11
12SED := sed
13
14XXD := xxd
15
16override CXXFLAGS += -std=c++20
17
18override COMPILE_FLAGS := -MMD -MP -I.
19override LINK_FLAGS :=
20
21ifneq "$(strip $(PACKAGES))" ""
22 override COMPILE_FLAGS += $(shell pkg-config --cflags $(PACKAGES))
23 override LINK_FLAGS += -Wl,--start-group $(shell pkg-config --libs $(PACKAGES)) -Wl,--end-group
24endif
25
26default: $(BUILD)/minigolf
27.PHONY: default
28
29clean:
30 rm -rf $(BUILD)
31.PHONY: clean
32
33.SUFFIXES:
34
35override SOURCES := $(shell find -\( -name build -prune -\) -o -\( -name \*.cpp -print -\))
36
37override OBJECTS := $(addprefix $(BUILD)/, $(addsuffix .o, $(basename $(SOURCES))))
38override DEPENDS := $(addprefix $(BUILD)/, $(addsuffix .d, $(basename $(SOURCES))))
39
40-include $(DEPENDS)
41
42$(BUILD)/%.o: %.cpp
43 @mkdir -p $(dir $@)
44 $(CXX) $(CXXFLAGS) $(COMPILE_FLAGS) -c -o $@ $<
45
46$(BUILD)/minigolf: $(OBJECTS)
47 @mkdir -p $(dir $@)
48 $(CXX) $(CXXFLAGS) -o $@ -Wl,--start-group $+ -Wl,--end-group $(LINK_FLAGS)
49
50$(BUILD)/%.bin: %.hex
51 @mkdir -p $(dir $@)
52 $(SED) -e "s/\\s*#.*//" $< | $(XXD) -r -p > $@
diff --git a/infra/arbiter.h b/infra/arbiter.h
new file mode 100644
index 0000000..5dd1647
--- /dev/null
+++ b/infra/arbiter.h
@@ -0,0 +1,42 @@
1#pragma once
2
3#include <cassert>
4#include <optional>
5#include <utility>
6
7#include "infra/sim.h"
8
9namespace infra {
10 template<typename T, unsigned int peers> struct priority_arbiter : public sim {
11 std::array<port<T>, peers> peerp;
12 port<T> *outp = nullptr;
13
14 void clock() {
15 for (unsigned int i = 0; i < peers; ++i) {
16 if (outp->can_write() && peerp[i].can_read())
17 outp->write(peerp[i].read());
18 }
19 }
20 };
21
22 template<typename T, unsigned int peers> struct round_robin_arbiter : public sim {
23 std::array<port<T>, peers> peerp;
24 port<T> *outp = nullptr;
25 unsigned int initial = 0;
26
27 void clock() {
28 bool initially_empty = outp->can_write();
29 for (unsigned int i = initial; i < peers; ++i) {
30 if (outp->can_write() && peerp[i].can_read())
31 outp->write(peerp[i].read());
32 }
33 for (unsigned int i = 0; i < initial; ++i) {
34 if (outp->can_write() && peerp[i].can_read())
35 outp->write(peerp[i].read());
36 }
37 if (initially_empty && !outp->can_write())
38 if (++initial == peers)
39 initial = 0;
40 }
41 };
42}
diff --git a/infra/pipetrace.cpp b/infra/pipetrace.cpp
new file mode 100644
index 0000000..e6642ef
--- /dev/null
+++ b/infra/pipetrace.cpp
@@ -0,0 +1,9 @@
1#include <cstdint>
2#include <ostream>
3
4#include "infra/pipetrace.h"
5
6namespace infra {
7 std::ostream *pt::ptfile = nullptr;
8 std::uint64_t pt::next_record = 0;
9}
diff --git a/infra/pipetrace.h b/infra/pipetrace.h
new file mode 100644
index 0000000..656b9b9
--- /dev/null
+++ b/infra/pipetrace.h
@@ -0,0 +1,37 @@
1#pragma once
2
3#include <cstdint>
4#include <fmt/format.h>
5#include <ostream>
6#include <string>
7
8namespace infra {
9 struct transaction {
10 std::uint64_t record = ~(std::uint64_t)0;
11 };
12
13 struct pt {
14 static std::ostream *ptfile;
15
16 static std::uint64_t next_record;
17
18 static transaction toplevel() {
19 transaction t;
20 t.record = next_record++;
21 return t;
22 }
23
24 static transaction child(const transaction &p) {
25 transaction t;
26 t.record = next_record++;
27 if (ptfile)
28 *ptfile << fmt::format("{} parent {}\n", t.record, p.record);
29 return t;
30 }
31
32 static void event(const transaction &t, const char *event, std::uint64_t time, const std::string &data) {
33 if (ptfile)
34 *ptfile << fmt::format("@{} {} {} {}\n", time, t.record, event, data);
35 }
36 };
37}
diff --git a/infra/port.h b/infra/port.h
new file mode 100644
index 0000000..06a3aa5
--- /dev/null
+++ b/infra/port.h
@@ -0,0 +1,45 @@
1#pragma once
2
3#include <cassert>
4#include <optional>
5#include <utility>
6
7#include "infra/sim.h"
8
9namespace infra {
10 template<typename T> struct port : public sim {
11 std::optional<T> consumer_side;
12 std::optional<T> producer_side;
13
14 bool can_read() { return consumer_side.has_value(); }
15 bool can_write() { return !producer_side.has_value(); }
16
17 T read() {
18 assert(can_read());
19 auto x = std::move(*consumer_side);
20 consumer_side.reset();
21 return x;
22 }
23
24 const T & peek() {
25 assert(can_read());
26 return *consumer_side;
27 }
28
29 void discard() {
30 consumer_side.reset();
31 }
32
33 void write(T &&x) {
34 assert(can_write());
35 producer_side = std::move(x);
36 }
37
38 void unclock() {
39 if (!consumer_side && producer_side) {
40 consumer_side = std::move(*producer_side);
41 producer_side.reset();
42 }
43 }
44 };
45}
diff --git a/infra/queue.h b/infra/queue.h
new file mode 100644
index 0000000..1e490bc
--- /dev/null
+++ b/infra/queue.h
@@ -0,0 +1,29 @@
1#pragma once
2
3#include <cassert>
4#include <deque>
5#include <optional>
6#include <utility>
7
8#include "infra/port.h"
9#include "infra/sim.h"
10
11namespace infra {
12 template<typename T, unsigned int size> struct queue : public sim {
13 port<T> input;
14 port<T> *output = nullptr;
15 std::deque<T> elements;
16
17 void clock() {
18 if (input.can_read() && elements.size() < size) {
19 auto x = input.read();
20 elements.emplace_back(std::move(x));
21 }
22 if (output->can_write() && !elements.empty()) {
23 auto &x = elements.front();
24 output->write(std::move(x));
25 elements.pop_front();
26 }
27 }
28 };
29}
diff --git a/infra/sim.cpp b/infra/sim.cpp
new file mode 100644
index 0000000..21acc8c
--- /dev/null
+++ b/infra/sim.cpp
@@ -0,0 +1,9 @@
1#include <cstdint>
2#include <vector>
3
4#include "infra/sim.h"
5
6namespace infra {
7 std::vector<sim *> sim::sims;
8 std::uint64_t sim::now = 0;
9}
diff --git a/infra/sim.h b/infra/sim.h
new file mode 100644
index 0000000..185916a
--- /dev/null
+++ b/infra/sim.h
@@ -0,0 +1,38 @@
1#pragma once
2
3#include <cstdint>
4#include <string>
5#include <vector>
6
7#include "infra/pipetrace.h"
8
9namespace infra {
10 struct sim {
11 virtual void clock() {}
12 virtual void unclock() {}
13
14 static std::vector<sim *> sims;
15
16 static std::uint64_t now;
17
18 sim() {
19 sims.emplace_back(this);
20 }
21
22 virtual ~sim() {
23 std::erase(sims, this);
24 }
25
26 static void advance() {
27 for (auto &s : sims)
28 s->clock();
29 for (auto &s : sims)
30 s->unclock();
31 ++now;
32 }
33
34 void pte(const transaction &t, const char *event, const std::string &data) {
35 pt::event(t, event, now, data);
36 }
37 };
38}
diff --git a/infra/stat.h b/infra/stat.h
new file mode 100644
index 0000000..f1ca75a
--- /dev/null
+++ b/infra/stat.h
@@ -0,0 +1,35 @@
1#pragma once
2
3#include <fmt/format.h>
4#include <utility>
5
6#include "infra/sim.h"
7
8namespace infra {
9 struct stat : public sim {
10 std::string name;
11 std::uint64_t numerator = 0;
12 std::uint64_t denominator = 0;
13
14 stat(std::string name)
15 : name(std::move(name))
16 { }
17
18 ~stat() {
19 fmt::print("# {} {}\n", name, (double)numerator/(double)denominator);
20 }
21
22 void unclock() {
23 ++denominator;
24 }
25
26 stat & operator++() {
27 ++numerator;
28 return *this;
29 }
30
31 stat & operator++(int) {
32 return operator++();
33 }
34 };
35}
diff --git a/isa/checker.cpp b/isa/checker.cpp
new file mode 100644
index 0000000..cd802a8
--- /dev/null
+++ b/isa/checker.cpp
@@ -0,0 +1,44 @@
1#include <cassert>
2
3#include "isa/isa.h"
4
5void checker::execute() {
6 assert(!halt);
7 auto int_enable_delay = ctlregs[ctlreg::INT_ENABLE] >> 1;
8 if (ctlregs[ctlreg::INT_ENABLE] & 1) {
9 // check for interrupt
10 }
11 ctlregs[ctlreg::INT_ENABLE] = (int_enable_delay << 1) | int_enable_delay;
12 inst = decode(ctlregs[ctlreg::DATA_INSTRUCTION_FIELD_BUFFER],
13 pc,
14 mem.fetch(pc));
15 auto next_pc = inst.next_pc;
16 if (inst.need_indirect_load) {
17 auto addr = mem.fetch(inst.init_address.value());
18 if (inst.need_autoinc_store)
19 mem.store(*inst.init_address, (addr + 1) & 07777);
20 inst.final_address = addr;
21 } else {
22 assert(!inst.need_autoinc_store);
23 }
24 if (inst.need_exec_load)
25 inst.data = mem.fetch(inst.final_address.value());
26 if (inst.need_read_acc)
27 inst.acc = acc;
28 if (inst.need_read_link)
29 inst.link = link;
30 if (inst.read_ctlreg.has_value())
31 inst.ctlval = ctlregs[*inst.read_ctlreg];
32 inst.execute();
33 if (inst.need_write_acc)
34 acc = inst.acc.value();
35 if (inst.need_write_link)
36 link = inst.link.value();
37 if (inst.write_ctlreg.has_value())
38 ctlregs[*inst.write_ctlreg] = inst.ctlval.value();
39 if (inst.need_exec_store)
40 mem.store(inst.final_address.value(), inst.data.value());
41 assert(inst.next_pc == next_pc || inst.possibly_redirects);
42 pc = inst.next_pc;
43 halt = inst.halt;
44}
diff --git a/isa/decode.cpp b/isa/decode.cpp
new file mode 100644
index 0000000..8a85d41
--- /dev/null
+++ b/isa/decode.cpp
@@ -0,0 +1,96 @@
1#include <cassert>
2
3#include "isa/isa.h"
4
5instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits)
6{
7 instruction_context inst;
8
9 auto df = dfifb >> 3;
10 auto ifb = dfifb & 00007;
11
12 inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777);
13
14 switch (bits >> 9) {
15 case 0: // AND
16 inst.need_exec_load = true;
17 inst.need_read_acc = true;
18 inst.need_write_acc = true;
19 inst.ef = [](auto &ctx) {
20 ctx.acc = ctx.acc.value() & ctx.data.value();
21 };
22 break;
23 case 1: // TAD
24 inst.need_exec_load = true;
25 inst.need_read_acc = true;
26 inst.need_read_link = true;
27 inst.need_write_acc = true;
28 inst.need_write_link = true;
29 inst.ef = [](auto &ctx) {
30 unsigned int sum = (ctx.link.value() << 12) + ctx.acc.value() + ctx.data.value();
31 ctx.link = (sum >> 12) & 1;
32 ctx.acc = sum & 07777;
33 };
34 break;
35 case 2: // ISZ
36 inst.need_exec_load = true;
37 inst.need_exec_store = true;
38 inst.possibly_redirects = true;
39 inst.ef = [](auto &ctx) {
40 ctx.data = (ctx.data.value() + 1) & 07777;
41 if (*ctx.data)
42 ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777);
43 };
44 break;
45 case 3: // DCA
46 inst.need_read_acc = true;
47 inst.need_write_acc = true;
48 inst.need_exec_store = true;
49 inst.ef = [](auto &ctx) {
50 ctx.data = ctx.acc.value();
51 ctx.acc = 0;
52 };
53 break;
54 case 4: // JMS
55 inst.need_exec_store = true;
56 inst.possibly_redirects = true;
57 inst.ef = [ifb](auto &ctx) {
58 ctx.data = ctx.next_pc;
59 ctx.next_pc = (ifb << 12) | ((ctx.final_address.value() + 1) & 07777);
60 };
61 break;
62 case 5: // JMP
63 inst.possibly_redirects = true;
64 inst.ef = [ifb](auto &ctx) {
65 ctx.next_pc = (ifb << 12) | (ctx.final_address.value() & 07777);
66 };
67 break;
68 case 6: // IOT
69 inst.ef = [bits](auto &ctx) {
70 assert(false);
71 };
72 break;
73 case 7: // OPR
74 inst.ef = [bits](auto &ctx) {
75 assert(false);
76 };
77 break;
78 }
79
80 // Instructions with memory operands may be direct or indirect
81 if (inst.need_exec_load || inst.need_exec_store || inst.possibly_redirects) {
82 auto addr = (df << 12) | ((bits & 00200) ? (next_pc & 07600) : 0) | (bits & 00177);
83 if (bits & 00400) {
84 inst.need_indirect_load = true;
85 inst.init_address = addr;
86 } else {
87 inst.final_address = addr;
88 }
89 }
90
91 // Non-jump indirect memory operands may be autoincrementing depending on operand bits
92 if (!inst.possibly_redirects && inst.need_indirect_load && ((bits & 00170) == 00010))
93 inst.need_autoinc_store = true;
94
95 return inst;
96}
diff --git a/isa/isa.h b/isa/isa.h
new file mode 100644
index 0000000..3effb5b
--- /dev/null
+++ b/isa/isa.h
@@ -0,0 +1,44 @@
1#pragma once
2
3#include <functional>
4#include <optional>
5
6#include "infra/pipetrace.h"
7
8enum class ctlreg {
9 DATA_INSTRUCTION_FIELD_BUFFER, // (df << 3) | if_buffer
10 DATA_INSTRUCTION_FIELD_SAVED, // (df_saved << 3) | if_saved
11 INT_ENABLE, // (int_enable_delay << 1) | int_enable
12};
13
14struct instruction_context {
15 infra::transaction transaction;
16
17 // Known statically at decode time
18 bool need_indirect_load = false; // final_address = mem[init_address]
19 bool need_autoinc_store = false; // mem[init_address] += 1
20 bool need_exec_load = false; // data = mem[final_address]
21 bool need_read_acc = false; // acc = %acc
22 bool need_read_link = false; // link = %link
23 std::optional<ctlreg> read_ctlreg; // ctlval = %[read_ctlreg]
24 bool need_write_acc = false; // %acc = acc
25 bool need_write_link = false; // %link = link
26 std::optional<ctlreg> write_ctlreg; // %[write_ctlreg] = ctlval
27 bool need_exec_store = false; // mem[final_address] = data
28 bool possibly_redirects = false; // %pc = next_pc
29
30 std::function<void(instruction_context &ctx)> ef;
31 void execute() { ef(*this); }
32
33 // May change over the lifetime of the instruction execution
34 unsigned int next_pc; // includes IF
35 std::optional<unsigned int> init_address; // includes DF
36 std::optional<unsigned int> final_address; // includes DF
37 std::optional<unsigned int> ctlval;
38 std::optional<unsigned int> data;
39 std::optional<unsigned int> acc;
40 std::optional<bool> link;
41 bool halt = false;
42};
43
44instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits);
diff --git a/memory/dram.h b/memory/dram.h
new file mode 100644
index 0000000..f59c7a6
--- /dev/null
+++ b/memory/dram.h
@@ -0,0 +1,96 @@
1#pragma once
2
3#include <array>
4#include <cstdint>
5#include <cstring>
6#include <istream>
7#include <map>
8#include <string>
9#include <utility>
10
11#include "memory/line.h"
12
13namespace memory {
14 struct dram : public infra::sim {
15 static constexpr std::uint64_t PAGE_LINES_LOG2 = 20 - LINE_BYTES_LOG2;
16 static constexpr std::uint64_t PAGE_LINES = 1 << PAGE_LINES_LOG2;
17 static constexpr std::uint64_t PAGE_LINE_OFFSET_MASK = PAGE_LINES - 1;
18 static constexpr std::uint64_t PAGE_BYTES_LOG2 = PAGE_LINES_LOG2 + LINE_BYTES_LOG2;
19 static constexpr std::uint64_t PAGE_BYTES = 1 << PAGE_BYTES_LOG2;
20 static constexpr std::uint64_t PAGE_BYTE_OFFSET_MASK = PAGE_BYTES - 1;
21
22 typedef std::array<line, PAGE_LINES> page;
23
24 std::map<std::uint64_t, page> image;
25
26 struct response {
27 infra::transaction transaction;
28 std::uint64_t line_address;
29 line data;
30 };
31
32 struct command {
33 infra::transaction transaction;
34 std::uint64_t line_address;
35 line data;
36 std::array<bool, LINE_BYTES> mask;
37 bool write = false;
38 infra::port<response> *responsep = nullptr;
39 };
40
41 infra::port<command> commandp;
42
43 void clock() {
44 if (commandp.can_read()) {
45 const auto &c = commandp.peek();
46 if (!c.responsep || c.responsep->can_write()) {
47 auto page_address = c.line_address >> PAGE_LINES_LOG2;
48 auto page_line = c.line_address & PAGE_LINE_OFFSET_MASK;
49 if (c.write) {
50 pte(c.transaction, "s", fmt::format("store {:x}-{:x}", page_address, page_line));
51 if (c.responsep) {
52 response r;
53 r.transaction = c.transaction;
54 r.line_address = c.line_address;
55 r.data = c.data;
56 c.responsep->write(std::move(r));
57 }
58 auto [p, emplaced] = image.try_emplace(page_address);
59 if (emplaced)
60 for (unsigned int i = 0; i < PAGE_LINES; ++i)
61 p->second[i].fill(0);
62 auto &l = p->second[page_line];
63 for (unsigned int i = 0; i < LINE_BYTES; ++i)
64 if (c.mask[i])
65 l[i] = c.data[i];
66 } else {
67 pte(c.transaction, "f", fmt::format("fill {:x}-{:x}", page_address, page_line));
68 if (c.responsep) {
69 response r;
70 r.transaction = c.transaction;
71 r.line_address = c.line_address;
72 if (auto p = image.find(page_address); p != image.end())
73 r.data = p->second[page_line];
74 else
75 r.data.fill(0);
76 c.responsep->write(std::move(r));
77 }
78 }
79 commandp.discard();
80 }
81 }
82 }
83
84 void load(std::istream &fh) {
85 for (unsigned int page = 0; ; ++page) {
86 auto [p, emplaced] = image.try_emplace(page);
87 if (emplaced)
88 for (unsigned int i = 0; i < PAGE_LINES; ++i)
89 p->second[i].fill(0);
90 for (unsigned int line = 0; line < PAGE_LINES; ++line)
91 if (!fh.read(reinterpret_cast<char *>(p->second[line].data()), LINE_BYTES))
92 return;
93 }
94 }
95 };
96}
diff --git a/memory/line.h b/memory/line.h
new file mode 100644
index 0000000..3377ec8
--- /dev/null
+++ b/memory/line.h
@@ -0,0 +1,15 @@
1#pragma once
2
3#include <array>
4#include <cstdint>
5
6#include "infra/port.h"
7#include "infra/sim.h"
8
9namespace memory {
10 constexpr std::uint64_t LINE_BYTES_LOG2 = 4;
11 constexpr std::uint64_t LINE_BYTES = 1 << LINE_BYTES_LOG2;
12 constexpr std::uint64_t LINE_BYTE_OFFSET_MASK = LINE_BYTES - 1;
13
14 typedef std::array<std::uint8_t, LINE_BYTES> line;
15}
diff --git a/pt b/pt
new file mode 100755
index 0000000..206795f
--- /dev/null
+++ b/pt
@@ -0,0 +1,104 @@
1#!/usr/bin/ruby
2
3$filter = ARGV
4
5$parents = {}
6$events = {}
7$has = {}
8$data = {}
9$horiz = {}
10$maxtime = -1
11
12$stats = false
13
14$stdin.each_line do | line |
15 case line
16
17 when /^(\d+) parent (\d+)$/
18 child = $1.to_i
19 parent = $2.to_i
20 $parents[child] = parent
21
22 when /^@(\d+) (\d+) (\S*) (.*)$/
23 time = $1.to_i
24 rec = $2.to_i
25 event = $3
26 data = $4
27 if event.size > 0
28 $events[rec] ||= {}
29 $events[rec][time] = event
30 $has[rec] ||= {}
31 $has[rec][event] = true
32 $horiz[event] ||= ""
33 $horiz[event] = $horiz[event].ljust(time)
34 $horiz[event][time] = event
35 end
36 if data.size > 0
37 $data[rec] ||= ""
38 $data[rec] += " #{event}@#{time}:" if event.size > 0
39 $data[rec] += " #{data}"
40 end
41 $maxtime = [$maxtime, time+1].max
42
43 when /^#\s*(.*)$/
44 $stats = true
45 $stdout.write("#{$1}\n")
46
47 else
48 raise "Unexpected line: #{line}"
49 end
50end
51
52$stdout.write("\n") if $stats
53
54$hier = {}
55$hier_direct = {}
56
57$events.each_key do | rec |
58 subhier = {}
59 $hier_direct[rec] = subhier
60 if $parents.key?(rec)
61 $hier_direct[$parents[rec]][rec] = subhier
62 else
63 $hier[rec] = subhier
64 end
65end
66
67$order = []
68
69def flatten(hier)
70 hier.each do | rec, subhier |
71 $order << rec
72 flatten(subhier)
73 end
74end
75flatten($hier)
76
77rwidth = $order.map { | x | x.to_s.size }.max
78
79$horiz.keys.sort.each do | occ |
80 $stdout.write(" " * rwidth + " #{$horiz[occ].ljust($maxtime)}")
81 count = $horiz[occ].delete(" ").size
82 $stdout.write(" #{($maxtime.to_f / count.to_f).round(2).to_s.rjust(5)} cyc/evt\n")
83end
84$stdout.write("\n")
85
86mwidth = 0
87
88$order.each do | rec |
89 estr = ""
90 filter_match = $filter.empty?
91 $has[rec].each_key do | event |
92 filter_match ||= $filter.include?(event)
93 end
94 next unless filter_match
95 $events[rec].keys.sort.each do | time |
96 estr = estr.ljust(time + 1, estr.size == 0 ? " " : "-")
97 estr[time] = $events[rec][time] if $events[rec][time].size > 0
98 end
99 estr += " " * 5
100 estr = estr.ljust(mwidth - 1)
101 estr = estr.ljust(estr.size + 20 - estr.size % 20)
102 mwidth = [mwidth, estr.size].max
103 $stdout.write(rec.to_s.rjust(rwidth) + ": #{estr}#{$data[rec]}\n")
104end
diff --git a/test b/test
new file mode 100755
index 0000000..d70c720
--- /dev/null
+++ b/test
@@ -0,0 +1,58 @@
1#!/bin/bash
2
3set -eu
4
5FILTER=()
6
7while [[ $# != 0 ]]; do
8 if [[ $1 == "-h" ]]; then
9 cat <<END
10Usage: ./test [options ...] [tests ...]
11
12Options:
13 -h This help message
14 -e Show emit rows
15 -f Show fetch rows
16 -i Show instruction rows
17 -m Show rows with memory traffic
18 -r Show rows with writeback (retire)
19
20For the filtering options: if one or more filters are set, then rows which
21match any filter are shown. If no filter is set, then all rows are shown.
22END
23 exit
24 elif [[ $1 == "-e" ]]; then
25 shift
26 FILTER+=("*")
27 elif [[ $1 == "-f" ]]; then
28 shift
29 FILTER+=("F")
30 elif [[ $1 == "-i" ]]; then
31 shift
32 FILTER+=("D")
33 elif [[ $1 == "-m" ]]; then
34 shift
35 FILTER+=("f" "s")
36 elif [[ $1 == "-r" ]]; then
37 shift
38 FILTER+=("W")
39 else
40 break
41 fi
42done
43
44if [[ $# == 0 ]]; then
45 for TEST in $(ls -1 tests); do
46 set -- "$@" "${TEST%.hex}"
47 done
48fi
49
50make
51
52for TEST in "$@"; do
53 make "build/tests/$TEST.bin"
54 (
55 echo "$TEST"
56 ./procmodel "build/tests/$TEST.bin" | ./pt "${FILTER[@]}"
57 ) | less -S
58done