Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_library(
src/emulator/execution_context.cpp
src/emulator/minst_record.cpp
src/emulator/reference_executor.cpp
src/emulator/shared_tile.cpp
src/emulator/state.cpp
src/isa/codec.cpp
src/isa/disasm.cpp
Expand All @@ -41,11 +42,11 @@ add_library(linx::model ALIAS linx_model)
add_custom_target(
gen-isa-codec
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tools/isa/gen_minst_codec.py"
--spec "${CMAKE_CURRENT_SOURCE_DIR}/../../isa/v0.57/linxisa-v0.57.json"
--spec "${CMAKE_CURRENT_SOURCE_DIR}/../../isa/v0.58/linxisa-v0.58.json"
--header "${CMAKE_CURRENT_SOURCE_DIR}/include/linx/model/isa/generated_tables.hpp"
--source "${CMAKE_CURRENT_SOURCE_DIR}/src/isa/generated_tables.cpp"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Regenerating committed LinxISA 0.57.1 Minst codec tables")
COMMENT "Regenerating committed LinxISA 0.58.0 Minst codec tables")

target_include_directories(
linx_model
Expand Down Expand Up @@ -115,6 +116,14 @@ add_test(
--manifest
${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/llvm_elf/manifest.json)
set_tests_properties(linx_model_check_llvm_elf_fixtures PROPERTIES LABELS "checks")
add_test(
NAME linx_model_check_pto_v058_shared_fixture
COMMAND
${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tests/checks/check_pto_v058_shared_fixture.py
--fixture
${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/pto_v058_shared_state.json)
set_tests_properties(linx_model_check_pto_v058_shared_fixture PROPERTIES LABELS "checks")

add_executable(linx_model_cli src/linx_model_cli.cpp)
target_link_libraries(linx_model_cli PRIVATE linx_model)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ state.
- `SimQueue<T>` models latency-aware, cycle-visible FIFO movement for value
payloads, `std::unique_ptr<T>`, and `std::shared_ptr<T>`.
- `isa::Minst` is the single in-flight uop payload. Fetch allocates
`MinstPtr`, decode populates generated LinxISA 0.57.1 fields, and retire/flush
`MinstPtr`, decode populates generated LinxISA 0.58.0 fields, and retire/flush
or DFX consumes the same object.
- `ProgramImage` plus the ELF/raw loader and ISA disassembler let the model CLI
load a binary, decode instructions, and print assembly directly from the
Expand Down Expand Up @@ -71,7 +71,7 @@ ctest --test-dir build-sanitize --output-on-failure
semantics
- [`docs/testing.md`](./docs/testing.md) UT / ST / checks / sanitizers
- [`docs/logging.md`](./docs/logging.md) structured log format and packet dump
- [`docs/isa.md`](./docs/isa.md) generated LinxISA 0.57.1 codec and `Minst`
- [`docs/isa.md`](./docs/isa.md) generated LinxISA 0.58.0 codec and `Minst`
packet contract

Doxygen can be generated locally with:
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Within each `Module`, `WorkSelf()` is event-driven:
repository.

- fetch allocates `MinstPtr`
- decode fills generated LinxISA 0.57.1 form metadata and canonical decoded fields
- decode fills generated LinxISA 0.58.0 form metadata and canonical decoded fields
- middle pipeline stages inspect or extend typed views on the same packet
- retire, flush, or DFX consumes and destroys the packet, or converts it to
shared ownership only at terminal boundaries
Expand Down
22 changes: 18 additions & 4 deletions docs/isa.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

## Scope

`linx-model` ships a committed generated LinxISA 0.57.1 codec for `isa::Minst`.
`linx-model` ships a committed generated LinxISA 0.58.0 codec for `isa::Minst`.
The source of truth is:

- `isa/v0.57/linxisa-v0.57.json` from the LinxISA 0.57.1 projection, generated
- `isa/v0.58/linxisa-v0.58.json` from the LinxISA 0.58.0 projection, generated
from the locked PTO-ISA/pto-spec release

The generated C++ tables are committed under:
Expand Down Expand Up @@ -48,8 +48,22 @@ Decoder behavior:
- chooses the unique most-specific form by fixed-bit count
- validates field constraints
- populates `Minst` metadata and typed views
- exposes exactly 761 current LinxISA forms and rejects retired `B.ARG`,
`BSTART.FIXP`, and generic `BSTART.CUBE`/`BSTART.TMA` spellings
- exposes exactly 766 current LinxISA forms and rejects retired `B.ARG`,
`C.B.IOS`, `BSTART.FIXP`, and generic `BSTART.CUBE`/`BSTART.TMA` spellings

## PTO 0.58 Shared state

`emulator::SharedTileBank` models one core-private `S0..S255` bank. Its
destination write path preflights every selected PE before applying one atomic
descriptor-and-payload update. The first write fixes the allocation mask and
per-PE capacity; later subset writes are legal, while allocation expansion or
descriptor drift fails without partial effects. A zero PE mask is a strict
no-op, and uninitialized reads return no value without changing state.

The machine-readable differential contract is committed at
`tests/fixtures/pto_v058_shared_state.json`. It fixes the TLSU state
transitions plus the TMOV, cooperative CUBE, and TGEMV Shared-operand policy so
QEMU, LinxCoreModel, and RTL validation can consume the same cases.

Encoder behavior:

Expand Down
74 changes: 74 additions & 0 deletions include/linx/model/emulator/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

#include <array>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>

namespace linx::model::emulator {

Expand All @@ -16,6 +18,77 @@ constexpr std::size_t kLinxTileMaxIot = 32;
constexpr std::size_t kLinxVecRiMax = kLinxTileMaxIor * 3;
constexpr std::size_t kLinxTileMaxBytes = 64U * 1024U;
constexpr std::size_t kLinxTileMaxWords = kLinxTileMaxBytes / 4U;
constexpr std::size_t kLinxSharedTileCount = 256;
constexpr std::size_t kLinxCorePeCount = 4;
constexpr std::size_t kLinxSharedTileMaxBytes = 8U * 1024U;

struct SharedTileDescriptor {
std::uint32_t dtype = 0;
std::uint16_t valid_cols = 0;
std::uint16_t valid_rows = 0;
std::uint16_t cols = 0;
std::uint16_t rows = 0;

bool operator==(const SharedTileDescriptor &) const = default;
};

struct SharedTileLane {
SharedTileDescriptor descriptor{};
std::vector<std::uint8_t> data{};
};

struct SharedTileVersion {
std::array<SharedTileLane, kLinxCorePeCount> lanes{};
std::uint32_t per_pe_capacity = 0;
std::uint32_t allocated_bytes = 0;
std::uint32_t dtype = 0;
std::uint8_t allocation_mask = 0;
std::uint8_t initialized_mask = 0;
};

enum class SharedTileWriteStatus : std::uint8_t {
Applied,
Noop,
InvalidRegister,
InvalidMask,
InvalidSize,
InvalidDescriptor,
PayloadSizeMismatch,
AllocationExpansion,
DescriptorMismatch,
};

class SharedTileBank {
public:
static std::optional<std::uint32_t> CapacityBytes(std::uint8_t size_code) noexcept;

[[nodiscard]] SharedTileWriteStatus
Write(std::uint16_t shared_id, std::uint8_t pe_mask, std::uint8_t size_code,
const SharedTileDescriptor &descriptor,
const std::array<std::vector<std::uint8_t>, kLinxCorePeCount> &payloads);
[[nodiscard]] SharedTileWriteStatus
Write(std::uint16_t shared_id, std::uint8_t pe_mask, std::uint8_t size_code,
const std::array<SharedTileDescriptor, kLinxCorePeCount> &descriptors,
const std::array<std::vector<std::uint8_t>, kLinxCorePeCount> &payloads);

[[nodiscard]] const SharedTileLane *Read(std::uint16_t shared_id,
std::uint8_t pe_id) const noexcept;
[[nodiscard]] const SharedTileVersion &Version(std::uint16_t shared_id) const noexcept;
void Reset() noexcept;

private:
std::array<SharedTileVersion, kLinxSharedTileCount> versions_{};
};

enum class TileBindingKind : std::uint8_t { Bior, Biot, Bios };
enum class TileOperandSpace : std::uint8_t { ScalarAddress, Local, Shared };
enum class TileOperationKind : std::uint8_t { Tmov, Cube, Tgemv };
enum class TileSharedUse : std::uint8_t { None, Source, Destination };

[[nodiscard]] bool BindingAllows(TileBindingKind binding, TileOperandSpace space) noexcept;
[[nodiscard]] bool ValidateSharedOperation(TileOperationKind operation, TileSharedUse shared_use,
std::uint8_t shared_mask,
std::uint8_t local_mask) noexcept;

struct LinxAcrBlockState {
std::array<std::uint64_t, kLinxQueueCount> tq{};
Expand Down Expand Up @@ -134,6 +207,7 @@ struct LinxState {
std::array<std::uint32_t, 32> tile_reg_bytes{};
std::array<std::uint32_t, kLinxTileMaxWords> tile_acc{};
std::uint32_t tile_acc_bytes = 0;
SharedTileBank shared_tile{};
std::uint64_t bpc = 0;
std::uint64_t insn_pc_next = 0;
std::uint64_t pc = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/emulator/minst_record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void FillOperand(LinxMinstOperandRecordC &dst, const isa::MinstOperand *src) {
[[nodiscard]] std::size_t BytesForLen(std::uint32_t len_bits) {
LINX_MODEL_ASSERT_MSG(len_bits % 8U == 0U, "minst trace len must be byte-aligned");
LINX_MODEL_ASSERT_MSG(len_bits >= 16U && len_bits <= 64U,
"minst trace len must stay within v0.57 fetch sizes");
"minst trace len must stay within Linx variable-length fetch sizes");
return static_cast<std::size_t>(len_bits / 8U);
}

Expand Down
14 changes: 7 additions & 7 deletions src/emulator/reference_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ std::string ResolveBlockKind(const isa::Minst &inst, const LinxState &state) {
inst.mnemonic == "BSTART.TMOV" || inst.mnemonic == "BSTART.TPREFETCH" ||
inst.mnemonic == "BSTART.MGATHER" || inst.mnemonic == "BSTART.MSCATTER" ||
inst.mnemonic == "BSTART.MGATHER.MASK" || inst.mnemonic == "BSTART.MSCATTER.MASK" ||
inst.mnemonic == "BSTART.MGATHER.CAS") {
return "tma";
inst.mnemonic == "BSTART.MGATHER.CAS" || inst.mnemonic == "BSTART.GMOV") {
return "tlsu";
}
if (inst.mnemonic == "SSRSET" || inst.mnemonic == "HL.SSRSET") {
return "sys";
Expand All @@ -110,10 +110,10 @@ int ResolveLaneId(std::string_view block_kind) {

bool IsTileHeader(const isa::Minst &inst) {
const auto block_kind = ResolveBlockKind(inst, LinxState{});
return block_kind == "tma" || block_kind == "cube" || block_kind == "tepl";
return block_kind == "tlsu" || block_kind == "cube" || block_kind == "tepl";
}

bool IsUnsupportedV057Scalar(const isa::Minst &inst) {
bool IsUnsupportedScalar(const isa::Minst &inst) {
return inst.mnemonic == "CASB" || inst.mnemonic == "CASH" || inst.mnemonic == "CASW" ||
inst.mnemonic == "CASD" || inst.mnemonic == "DMA";
}
Expand Down Expand Up @@ -332,14 +332,14 @@ void ReferenceExecutor::Execute(isa::Minst &inst) {
state.pc = inst.next_pc;
} else if (IsTileHeader(inst) || inst.mnemonic == "B.TEXT") {
const auto header_block_kind =
inst.mnemonic == "B.TEXT" ? std::string("tma") : current_block_kind;
inst.mnemonic == "B.TEXT" ? std::string("tlsu") : current_block_kind;
state.block_kind = header_block_kind;
state.pc = inst.next_pc;
commit_record.block_kind[0] = 0;
std::strncpy(commit_record.block_kind, header_block_kind.c_str(),
sizeof(commit_record.block_kind) - 1U);
} else if (IsUnsupportedV057Scalar(inst)) {
inst.annotation = "0.57.1 scalar AMO decoded; reference executor semantics are not implemented";
} else if (IsUnsupportedScalar(inst)) {
inst.annotation = "scalar AMO decoded; reference executor semantics are not implemented";
state.pc = inst.next_pc;
ctx.RequestTerminate(1, std::string("unsupported_instruction:") + std::string(inst.mnemonic));
} else {
Expand Down
Loading
Loading