aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/macro_turing.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/macro_turing.h')
-rw-r--r--src/video_core/macro_turing.h237
1 files changed, 0 insertions, 237 deletions
diff --git a/src/video_core/macro_turing.h b/src/video_core/macro_turing.h
deleted file mode 100644
index 18966fc083..0000000000
--- a/src/video_core/macro_turing.h
+++ /dev/null
@@ -1,237 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
-// SPDX-License-Identifier: GPL-3.0-or-later
-// Instruction semantics adapted from Mesa mme_tu104_sim.c and mme_tu104.c:
-// Copyright © 2022 Collabora Ltd. SPDX-License-Identifier: MIT
-#pragma once
-
-#include <algorithm>
-#include <array>
-#include <bit>
-#include <cstdint>
-#include <span>
-#include <optional>
-#include <vector>
-#include <string_view>
-
-namespace Tegra {
-
-// TU104/GA10x MME has two ALUs and two outputs per 96-bit instruction.
-class TuringMacroInterpreter {
-public:
- using Word = std::uint32_t;
- struct Result {
- bool success;
- Word pc;
- std::size_t consumed;
- std::string_view error;
- };
-
- template <typename Read, typename Emit>
- Result Execute(std::span<const Word> code, Word start,
- std::span<const Word> parameters, Read read, Emit emit) {
- return Execute(code, start, parameters, read, emit,
- []() -> std::optional<std::vector<Word>> { return std::nullopt; });
- }
-
- template <typename Read, typename Emit, typename Barrier>
- Result Execute(std::span<const Word> code, Word start,
- std::span<const Word> parameters, Read read, Emit emit, Barrier barrier) {
- std::vector<Word> dma_parameters;
- std::span<const Word> fifo = parameters;
- std::size_t fifo_index{};
- bool dma_fifo{};
- std::array<Word, 24> regs{};
- Word initialized{}, carry{}, method{}, increment{};
- Word pc = start, loop_count{}, loop_start{}, loop_end{};
- std::size_t consumed{};
- bool end_next{}, stopped{};
- std::string_view error;
- const auto fail = [&](std::string_view message) { if (error.empty()) error = message; };
- for (Word steps = 0; steps < 1000000; ++steps) {
- if (static_cast<std::size_t>(pc) * 3 + 2 >= code.size()) {
- fail("instruction outside uploaded RAM");
- break;
- }
- const std::array<Word, 3> bits{code[pc * 3 + 2], code[pc * 3 + 1], code[pc * 3]};
- const auto field = [&](Word low, Word count) {
- std::uint64_t value = bits[low / 32];
- if (low / 32 < 2) value |= std::uint64_t{bits[low / 32 + 1]} << 32;
- return Word((value >> (low % 32)) & ((std::uint64_t{1} << count) - 1));
- };
- const std::array<Word, 2> imm{field(30, 16), field(66, 16)};
- const std::array<Word, 2> op{field(10, 5), field(46, 5)};
- const std::array<Word, 2> dst{field(15, 5), field(51, 5)};
- const std::array<Word, 2> src_a{field(20, 5), field(56, 5)};
- const std::array<Word, 2> src_b{field(25, 5), field(61, 5)};
- const std::array<Word, 2> out_method{field(82, 3), field(89, 3)};
- const std::array<Word, 2> out_emit{field(85, 4), field(92, 4)};
- const Word pred_reg = field(5, 5), pred_mode = field(1, 4);
- std::array<Word, 2> load{}, alu{};
- for (Word i = 0; i < 2; ++i) {
- const Word reg = 28 + i, out = 3 + i;
- const bool needed = pred_reg == reg || src_a[0] == reg || src_a[1] == reg ||
- src_b[0] == reg || src_b[1] == reg || out_method[0] == out ||
- out_method[1] == out || out_emit[0] == out || out_emit[1] == out;
- if (needed) {
- if (fifo_index >= fifo.size()) { fail("parameter FIFO underflow"); break; }
- load[i] = fifo[fifo_index++];
- if (!dma_fifo) ++consumed;
- }
- }
- // Both ALUs read the register file before either destination is
- // committed. Carry chaining remains explicit between the lanes.
- const auto input_regs = regs;
- const Word input_initialized = initialized;
- const auto get = [&](Word reg, Word lane) -> Word {
- if (reg < 24) {
- if (!(input_initialized & (1U << reg))) fail("uninitialized register read");
- return input_regs[reg];
- }
- switch (reg) {
- case 24: return 0;
- case 25: return Word(std::int32_t(std::int16_t(imm[lane])));
- case 26: return Word(std::int32_t(std::int16_t(imm[1 - lane])));
- case 27: return (imm[0] << 16) | imm[1];
- case 28: return load[0];
- case 29: return load[1];
- default: fail("invalid source register"); return 0;
- }
- };
- constexpr std::array<std::string_view, 16> predicates{
- "UUUU", "TTTT", "FFFF", "TTUU", "FFUU", "TFUU", "TUUU", "FUUU",
- "UUTT", "UUTF", "UUTU", "UUFT", "UUFF", "UUFU", "UUUT", "UUUF"};
- Word mask = 15;
- if (pred_mode) {
- const char skipped = get(pred_reg, 0) ? 'T' : 'F';
- mask = 0;
- for (Word i = 0; i < 4; ++i)
- if (predicates[pred_mode][i] != skipped) mask |= 1U << i;
- }
- Word next_pc = pc + 1;
- bool branch_cancels_end{};
- for (Word lane = 0; lane < 2 && error.empty(); ++lane) {
- if (!(mask & (1U << lane))) continue;
- const Word x = get(src_a[lane], lane), y = get(src_b[lane], lane);
- Word value{};
- const auto condition = [&](Word comparison) {
- switch (comparison) {
- case 17: case 25: return std::int32_t(x) < std::int32_t(y);
- case 18: case 26: return x < y;
- case 19: case 27: return std::int32_t(x) <= std::int32_t(y);
- case 20: case 28: return x <= y;
- default: return x == y;
- }
- };
- switch (op[lane]) {
- case 0: value = x + y; carry = value < x; break;
- case 1: value = x + y + carry; break;
- case 2: value = x - y; carry = value > x; break;
- case 3: value = x - y - carry; break;
- case 4: case 6: {
- const std::uint64_t a = op[lane] == 4 ? std::uint64_t(std::int64_t(std::int32_t(x))) : x;
- const std::uint64_t b = op[lane] == 4 ? std::uint64_t(std::int64_t(std::int32_t(y))) : y;
- const auto product = a * b;
- value = Word(product); carry = Word(product >> 32); break;
- }
- case 5: value = carry; break;
- case 7: {
- if (x != 0x1000 || y != 1) {
- fail("unsupported extended operation");
- break;
- }
- auto data = barrier();
- if (!data) { fail("macro DMA barrier failed"); break; }
- if (!data->empty()) {
- dma_parameters = std::move(*data);
- fifo = dma_parameters;
- fifo_index = 0;
- dma_fifo = true;
- }
- break;
- }
- case 8: value = Word(std::countl_zero(x)); break;
- case 9: value = x << (y & 31); break;
- case 10: value = x >> (y & 31); break;
- case 11: value = Word(std::int32_t(x) >> (y & 31)); break;
- case 12: value = x & y; break;
- case 13: value = ~(x & y); break;
- case 14: value = x | y; break;
- case 15: value = x ^ y; break;
- case 16: {
- const Word dest = (imm[lane] >> 10) & 63, count = (imm[lane] >> 5) & 31;
- const Word source = imm[lane] & 31;
- if (dest >= 32) { fail("invalid merge destination"); break; }
- const Word bits_mask = (1U << count) - 1;
- value = (x & ~(bits_mask << dest)) | (((y >> source) & bits_mask) << dest);
- break;
- }
- case 17: case 18: case 19: case 20: case 21:
- value = condition(op[lane]) ? ~0U : 0; break;
- case 22: value = read((x + y) & 0xffff); break;
- case 23:
- if (loop_count || imm[lane] < 2) { fail("invalid loop"); break; }
- // The native SDK emits zero-count loops around FIFO reads.
- // Unlike Mesa's test simulator, these must skip the body.
- if (x == 0) { next_pc = pc + imm[lane]; break; }
- loop_count = x - 1; loop_start = pc; loop_end = pc + imm[lane] - 1;
- break;
- case 24: next_pc = pc + (imm[lane] & 0x7fff); break;
- case 25: case 26: case 27: case 28: case 29:
- if (condition(op[lane]) == bool(imm[lane] & 0x8000)) {
- const auto offset = std::int32_t(imm[lane] << 19) >> 19;
- if (offset == -4096) stopped = true;
- else {
- next_pc = Word(std::int32_t(pc) + offset);
- // Native back edges use bit 14 to suppress the
- // fallthrough termination when the branch is taken.
- branch_cancels_end = (imm[lane] & 0x4000) != 0;
- }
- }
- break;
- case 30:
- if (x >= dram.size()) { fail("data RAM read out of bounds"); break; }
- value = dram[x]; break;
- case 31:
- if (x >= dram.size()) { fail("data RAM write out of bounds"); break; }
- dram[x] = y; break;
- }
- alu[lane] = value;
- if (dst[lane] < 24) { regs[dst[lane]] = value; initialized |= 1U << dst[lane]; }
- else if (dst[lane] != 24) fail("invalid destination register");
- }
- const auto output = [&](Word selector) -> Word {
- switch (selector) {
- case 1: return alu[0]; case 2: return alu[1];
- case 3: return load[0]; case 4: return load[1];
- case 5: return imm[0]; case 6: return imm[1];
- case 8: return imm[0] >> 12; case 9: return imm[1] >> 12;
- case 10: return (imm[0] << 16) | imm[1];
- default: fail("invalid output selector"); return 0;
- }
- };
- for (Word lane = 0; lane < 2 && error.empty(); ++lane) {
- if (!(mask & (4U << lane))) continue;
- if (out_method[lane]) {
- const Word address = output(out_method[lane]);
- method = address & 0xfff; increment = (address >> 12) & 15;
- }
- if (out_emit[lane]) {
- const Word value = output(out_emit[lane]);
- if (error.empty() && !emit(method, value)) fail("unsupported emitted method");
- method += increment;
- }
- }
- if (!error.empty()) break;
- if (end_next || stopped) return {true, pc, consumed, {}};
- end_next = field(0, 1) != 0 && !branch_cancels_end;
- if (loop_count && pc == loop_end) { --loop_count; next_pc = loop_start + 1; }
- pc = next_pc;
- }
- if (error.empty()) error = "instruction limit exceeded";
- return {false, pc, consumed, error};
- }
-
- std::array<Word, 0xc00> dram{};
-};
-
-} // namespace Tegra