Repository navigation
Expand file tree
/
Copy pathcmd_processor.h
More file actions
258 lines (228 loc) · 11.4 KB
/
Copy pathcmd_processor.h
File metadata and controls
258 lines (228 loc) · 11.4 KB
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Copyright © 2019-2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
// cmd_processor.h — functional C++ model of the Command Processor.
// Shared by simx and rtlsim; presents the same cp_mmio_* MMIO surface to
// the runtime.
//
// Implements a `tick()`-per-cycle state machine that reads commands from a
// host-pinned ring in DRAM, dispatches them to the right resource (DCR proxy,
// launch, DMA), and publishes the retired sequence number back to a
// host-pinned completion slot.
//
// Address map (CP-internal, 0-based):
// Globals (CP-internal offsets 0x000..0x0FF)
// 0x000 CP_CTRL bit0=enable_global, bit1=reset_all
// 0x004 CP_STATUS bit0=busy, bit1=error
// 0x008 CP_DEV_CAPS {AXI_TID_W:8 | RING_LOG2:8 | NUM_QUEUES:8}
// 0x010 CP_CYCLE_LO
// 0x014 CP_CYCLE_HI
// 0x018 GPU_DEV_CAPS_LO device-config caps, low 32 bits (RO)
// 0x01C GPU_DEV_CAPS_HI device-config caps, high 32 bits (RO)
// 0x020 GPU_ISA_CAPS_LO ISA caps, low 32 bits (RO)
// 0x024 GPU_ISA_CAPS_HI ISA caps, high 32 bits (RO)
// Per queue 0 (CP-internal offsets 0x100..0x13F)
// 0x100/04 Q_RING_BASE_LO/HI
// 0x108/0C Q_HEAD_ADDR_LO/HI (where the CP publishes head)
// 0x110/14 Q_CMPL_ADDR_LO/HI (where the CP publishes seqnum)
// 0x118 Q_RING_SIZE_LOG2
// 0x11C Q_CONTROL bit0=enable, bit1=reset
// 0x120 Q_TAIL_LO (staging)
// 0x124 Q_TAIL_HI (atomic commit)
// 0x128 Q_SEQNUM (RO mirror)
// 0x12C Q_ERROR
// 0x130 Q_LAST_DCR_RSP (RO — latest CMD_DCR_READ response)
//
// CMD_DRAW (OP_DRAW, opcode 0x0C) — device-orchestrated draw.
// A single ring command whose arg0 points at a resident *draw descriptor*:
// uint32_t num_steps; // at offset 0
// cmd_step steps[num_steps]; // at offset 4, 28 B stride
// Each step is a 28-byte cmd record (same byte layout decode_cmd reads):
// {opcode:u8, flags:u8, reserved:u16, arg0:u64, arg1:u64, arg2:u64}. The CP
// walks the steps in order, executing each through the same per-opcode logic
// as the ring (CMD_LAUNCH_QMD drains its kernel via the launch sub-FSM — the
// inter-stage barrier; CMD_DCR_WRITE / CMD_CACHE_FLUSH apply inline), then
// retires the one OP_DRAW. The whole VS→setup→bin→FF-config→FS sequence runs
// from one host doorbell with no host round-trip — the "true GPU" draw. The
// descriptor is an indirect command bundle, so the executed sequence is
// byte-identical to streaming the same commands through the ring.
// ============================================================================
#ifndef VORTEX_COMMAND_PROCESSOR_H
#define VORTEX_COMMAND_PROCESSOR_H
#include <cstdint>
#include <functional>
#include <array>
#include <vm_types.h> // SATP_t / PTE_t / vAddr_t (VM page-table walk)
namespace vortex {
class CommandProcessor {
public:
struct Hooks {
// Read `bytes` bytes from device DRAM at `addr` into `dst`.
// Used for ring-buffer fetches (one cache line at a time).
std::function<void(uint64_t addr, void* dst, std::size_t bytes)> dram_read;
// Write `bytes` bytes from `src` into device DRAM at `addr`.
// Used for completion-slot writebacks (8 B seqnum).
std::function<void(uint64_t addr, const void* src, std::size_t bytes)> dram_write;
// Issue a single DCR write to Vortex (for CMD_DCR_WRITE).
std::function<void(uint32_t addr, uint32_t value)> vortex_dcr_write;
// Issue a single DCR read to Vortex (for CMD_DCR_READ). `tag` is
// placed on the DCR data bus and addresses things like per-core
// CACHE_FLUSH. The backend must block until the response is
// available before returning.
std::function<uint32_t(uint32_t addr, uint32_t tag)> vortex_dcr_read;
// Pulse Vortex's start signal (for CMD_LAUNCH). The launch FSM
// calls this once when transitioning into the "started" state.
std::function<void()> vortex_start;
// Query Vortex's busy state. The launch FSM waits for this to
// rise (kernel actually executing) then fall (kernel done)
// before retiring the CMD_LAUNCH.
std::function<bool()> vortex_busy;
// Backend decodes the MMU fault-report DCRs. Published in DEV_CAPS
// so the runtime does not read them on a target that would leave
// the DCR bus waiting for a response.
bool mmu_fault_report = false;
};
explicit CommandProcessor(const Hooks& hooks);
// ----- Host-facing MMIO surface -----
// Offsets match VX_cp_axil_regfile (CP-internal, 0-based).
// Backends doing MMIO at byte offset 0x1000+ should subtract 0x1000
// on their side before calling these.
void mmio_write(uint32_t off, uint32_t value);
uint32_t mmio_read (uint32_t off) const;
// ----- Sim integration -----
// Advance the CP one functional cycle. Called by the simulator's
// per-cycle loop. Cheap: a small FSM step (single-digit branches).
void tick();
// True iff CP_CTRL.enable_global && Q_CONTROL.enable. The simulator
// can use this to skip tick() when the host hasn't enabled the CP.
bool enabled() const;
// True iff the engine has commands in flight OR ring has pending
// entries. Lets the host's wait loop break early when the CP is idle.
bool busy() const;
private:
// Engine FSM states. DrawStep / DrawLaunchWait walk an OP_DRAW bundle.
enum class EngState { Idle, Decode, Bid, WaitDone, Retire,
DrawStep, DrawLaunchWait };
// KMU launch sub-FSM.
enum class LaunchState { Idle, PulseStart, WaitBusy, WaitDrain };
// Command opcodes: low 8 bits of the on-wire command header.
enum : uint8_t {
OP_NOP = 0x00,
OP_MEM_WRITE = 0x01,
OP_MEM_READ = 0x02,
OP_MEM_COPY = 0x03,
OP_DCR_WRITE = 0x04,
OP_DCR_READ = 0x05,
OP_LAUNCH = 0x06,
OP_FENCE = 0x07,
OP_EVENT_SIG = 0x08,
OP_EVENT_WAIT = 0x09,
OP_CACHE_FLUSH = 0x0A,
OP_LAUNCH_QMD = 0x0B, // atomic launch: KMU descriptor read from memory
OP_DRAW = 0x0C, // device-orchestrated draw: arg0 = draw descriptor addr
};
// Fixed per-step stride inside a draw descriptor: the 28-byte cmd-record
// prefix (opcode/flags/reserved + arg0/arg1/arg2). A uniform stride keeps
// the descriptor walk (and the RTL CP mirror) simple.
static constexpr int DRAW_STEP_BYTES = 28;
static constexpr uint32_t MAX_DRAW_STEPS = 256; // corrupt-count backstop
// CMD_MEM_* header flag (cmd_t.flags bit2 = F_MEM_PHYSICAL): the device
// operand is a physical address — the MMU-aware CP DMA skips translation.
// Used for page-table bootstrap writes and the PT region itself. A
// dedicated bit, distinct from flags bit0 (F_PROFILE) — the addressing
// mode is its own descriptor field (modern-GPU convention).
static constexpr uint8_t MEM_FLAG_PHYSICAL = 0x04;
// Decoded cmd record (matches cmd_t struct layout on-wire).
struct Cmd {
uint8_t opcode;
uint8_t flags;
uint16_t reserved;
uint64_t arg0;
uint64_t arg1;
uint64_t arg2;
};
// ----- Per-queue programmable state (q_state_t mirror) -----
struct Queue {
uint64_t ring_base = 0;
uint64_t head_addr = 0;
uint64_t cmpl_addr = 0;
uint8_t ring_log2 = 16; // 64 KiB default
uint32_t control = 0; // bit0=enable, bits3:2=prio
uint64_t tail = 0;
uint32_t tail_lo_staging = 0;
// CP-tracked state (not host-writable):
uint64_t head = 0; // bytes consumed
uint64_t seqnum = 0; // commands retired
uint32_t error = 0;
};
// ----- Globals -----
uint32_t cp_ctrl_ = 0; // bit0=enable_global
uint64_t satp_ = 0; // CP_SATP — page-table root for the CP DMA's MMU
uint64_t cycle_counter_ = 0;
Queue q0_; // single-queue model
Hooks hooks_;
uint32_t last_dcr_rsp_ = 0; // Q_LAST_DCR_RSP slot (0x130)
// ----- Engine/launch state machines -----
EngState eng_state_ = EngState::Idle;
LaunchState launch_state_ = LaunchState::Idle;
Cmd cur_cmd_{};
bool cur_is_launch_ = false;
bool cur_is_no_resource_ = false;
// ----- Draw-bundle state (OP_DRAW walk) -----
uint64_t draw_phys_ = 0; // descriptor base (translated)
uint32_t draw_num_steps_ = 0;
uint32_t draw_step_ = 0;
Cmd draw_cmd_{}; // decoded current step
// ----- Fetch state -----
// The simulator fetches one cache line at a time when head < tail,
// then walks the CL extracting decoded cmds before fetching the next.
static constexpr std::size_t CL_BYTES = 64;
static constexpr int MAX_CMDS_PER_CL = 5;
std::array<uint8_t, CL_BYTES> cl_buf_{};
int cl_cmd_count_ = 0;
int cl_cmd_slot_ = 0;
bool cl_loaded_ = false;
// Walk `cl_buf_` and populate `decoded_cmds_` / `cl_cmd_count_`.
void unpack_cl();
// Decode a single header at byte offset `off` into a Cmd record;
// returns the size in bytes of the command (so caller can advance).
int decode_cmd(int off, Cmd& out);
// Decode a cmd record from an arbitrary byte buffer (shared by the ring
// unpacker and the OP_DRAW step walk).
static int decode_cmd_bytes(const uint8_t* buf, int len, int off, Cmd& out);
// Execute one DCR_WRITE / DCR_READ / CACHE_FLUSH / EVENT_SIG / MEM_* /
// LAUNCH_QMD step against `c` (shared by the ring Bid path and OP_DRAW).
// Returns true if a kernel launch was kicked (caller must wait for drain).
bool exec_inline_cmd_(const Cmd& c);
// Read the next OP_DRAW step from the descriptor into draw_cmd_.
void draw_load_step_();
// Inverse of decoded helpers: write seqnum to cmpl_addr.
void publish_completion();
// CMD_EVENT_WAIT compare helper — reads cur_cmd_.arg0 from DRAM and
// compares to cur_cmd_.arg1 under the wait_op encoded in arg2[1:0].
bool event_wait_satisfied_();
// CMD_LAUNCH_QMD: read the in-memory KMU descriptor at `qmd_addr` and
// replay its {dcr_addr,value} pairs through the DCR-write hook.
void apply_qmd_(uint64_t qmd_addr);
// Advance the launch FSM one step using cur_cmd_.
void tick_launch();
// Advance the engine FSM one step.
void tick_engine();
// Fetch one CL from ring into cl_buf_ if needed.
void fetch_if_needed();
// VM: translate a device virtual address to physical via a page-table
// walk. A no-op when VM is disabled, SATP is unset/BARE, or physical.
uint64_t cp_translate(uint64_t vaddr, bool physical) const;
};
} // namespace vortex
#endif // VORTEX_COMMAND_PROCESSOR_H