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
|
#pragma once
#include "frontend/bundle.h"
#include "frontend/fetch.h"
#include "infra/port.h"
#include "inst.h"
#include "memory/line.h"
namespace frontend {
struct decode : public infra::sim {
struct restart {
unsigned int new_generation;
std::uint64_t new_pc;
std::uint64_t from_pc;
};
infra::port<restart> restartp;
infra::port<fetch::restart> *fetch_restartp = nullptr;
infra::port<bundle> bundlep;
infra::port<inst> *instp = nullptr;
inst next_inst;
unsigned int generation_up = 0;
unsigned int generation_down = 0;
std::uint64_t pc = 0;
static constexpr unsigned int MAX_INST_SIZE = 64;
static constexpr unsigned int BYTES_PER_CYCLE = 4;
void clock() {
if (restartp.can_read()) {
auto r = restartp.read();
generation_down = r.new_generation;
pc = r.new_pc;
next_inst.size = 0;
for (auto &f : next_inst.field)
f = 0;
fetch::restart fr;
fr.new_generation = ++generation_up;
fr.new_next_line_address = pc >> memory::LINE_BYTES_LOG2;
fr.previous_line_address = r.from_pc >> memory::LINE_BYTES_LOG2;
fetch_restartp->write(std::move(fr));
return;
}
if (next_inst.size >= MAX_INST_SIZE)
return;
if (bundlep.can_read()) {
const auto &b = bundlep.peek();
for (unsigned int i = 0; i < BYTES_PER_CYCLE; ++i) {
auto line = pc >> memory::LINE_BYTES_LOG2;
auto offset = pc & memory::LINE_BYTE_OFFSET_MASK;
if (b.generation == generation_up && b.line_address == line && instp->can_write()) {
decodebyte byte;
std::memcpy(&byte, b.data.data() + offset, sizeof(byte));
pte(b.transaction, "d", fmt::format("decode gen={} pc={:x} byte={:02x}", generation_up, pc, *reinterpret_cast<std::uint8_t *>(&byte)));
++next_inst.size;
if (byte.invert)
next_inst.field[byte.field] = ~next_inst.field[byte.field];
next_inst.field[byte.field] = next_inst.field[byte.field] << 4;
next_inst.field[byte.field] |= byte.bits;
++pc;
if (!byte.hold) {
next_inst.transaction = infra::pt::child(b.transaction);
next_inst.generation = generation_down;
next_inst.linear_next_pc = pc;
next_inst.predicted_next_pc = pc;
pte(next_inst.transaction, "D", fmt::format("decode gen={}", generation_down));
bool jump = false;
std::optional<std::uint64_t> target;
std::optional<bool> taken;
switch (next_inst.field[OPCODE]) {
case OP_JUMP_ABS_IF_ZERO:
jump = true;
if (next_inst.field[FLAGS_DST] & FLAG_IMM1)
target = next_inst.field[SRC1];
if (next_inst.field[FLAGS_DST] & FLAG_IMM2)
taken = next_inst.field[SRC2] == 0;
break;
case OP_JUMP_ABS_IF_NONZERO:
jump = true;
if (next_inst.field[FLAGS_DST] & FLAG_IMM1)
target = next_inst.field[SRC1];
if (next_inst.field[FLAGS_DST] & FLAG_IMM2)
taken = next_inst.field[SRC2] != 0;
break;
}
std::optional<std::uint64_t> redirect;
bool unpredictable = false;
if (jump) {
if (target.has_value()) {
if (taken.has_value()) {
if (taken.value())
redirect = target;
} else if (target.value() < pc) {
redirect = target;
}
} else if (!taken.has_value() || taken.value()) {
unpredictable = true;
}
}
if (redirect.has_value()) {
pte(next_inst.transaction, "", fmt::format("fe predicts jump to {:x}", redirect.value()));
next_inst.predicted_next_pc = pc = redirect.value();
} else if (unpredictable) {
pte(next_inst.transaction, "", "frontend halt due to unpredictable jump");
next_inst.predicted_next_pc.reset();
}
instp->write(std::move(next_inst));
next_inst.size = unpredictable ? MAX_INST_SIZE : 0;
for (auto &f : next_inst.field)
f = 0;
}
}
}
auto line = pc >> memory::LINE_BYTES_LOG2;
if (b.generation == generation_up && b.line_address != line && b.next_line_address != line) {
fetch::restart fr;
fr.new_generation = ++generation_up;
fr.new_next_line_address = pc >> memory::LINE_BYTES_LOG2;
fr.previous_line_address = b.line_address;
fetch_restartp->write(std::move(fr));
}
if (b.generation != generation_up || b.line_address != line)
bundlep.discard();
}
}
};
}
|