From d459b30def9e777045974242dfb7cbf3ba9dd030 Mon Sep 17 00:00:00 2001 From: RuoyuZhou Date: Wed, 12 Aug 2026 01:32:27 +0800 Subject: [PATCH] fix: align Shared Tile PE mask ordering Signed-off-by: RuoyuZhou --- docs/isa.md | 3 +- src/emulator/shared_tile.cpp | 12 ++++--- tests/checks/check_pto_v058_shared_fixture.py | 4 +++ tests/fixtures/pto_v058_shared_state.json | 13 +++---- tests/unit/emulator_test.cpp | 34 +++++++++---------- tests/unit/minst_test.cpp | 16 ++++----- 6 files changed, 46 insertions(+), 36 deletions(-) diff --git a/docs/isa.md b/docs/isa.md index 5717a6a..a7a0cf4 100644 --- a/docs/isa.md +++ b/docs/isa.md @@ -58,7 +58,8 @@ 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. +no-op, and uninitialized reads return no value without changing state. PE-mask +bits are architectural-order: bit 3 selects PE0 through bit 0 selecting PE3. The machine-readable differential contract is committed at `tests/fixtures/pto_v058_shared_state.json`. It fixes the TLSU state diff --git a/src/emulator/shared_tile.cpp b/src/emulator/shared_tile.cpp index 784e17a..80a2396 100644 --- a/src/emulator/shared_tile.cpp +++ b/src/emulator/shared_tile.cpp @@ -9,6 +9,10 @@ namespace { constexpr std::uint8_t kValidPeMask = (1U << kLinxCorePeCount) - 1U; +constexpr std::uint8_t PeMaskBit(std::size_t pe) noexcept { + return static_cast(1U << (kLinxCorePeCount - 1U - pe)); +} + bool DescriptorValid(const SharedTileDescriptor &descriptor, std::uint32_t capacity) noexcept { if (descriptor.rows == 0 || descriptor.cols == 0 || descriptor.valid_rows > descriptor.rows || descriptor.valid_cols > descriptor.cols) { @@ -60,7 +64,7 @@ SharedTileBank::Write(std::uint16_t shared_id, std::uint8_t pe_mask, std::uint8_ } for (std::size_t pe = 0; pe < kLinxCorePeCount; ++pe) { - const auto bit = static_cast(1U << pe); + const auto bit = PeMaskBit(pe); if ((pe_mask & bit) == 0U) { continue; } @@ -84,7 +88,7 @@ SharedTileBank::Write(std::uint16_t shared_id, std::uint8_t pe_mask, std::uint8_ version.per_pe_capacity = *capacity; version.allocated_bytes = *capacity * static_cast(std::popcount(pe_mask)); for (std::size_t pe = 0; pe < kLinxCorePeCount; ++pe) { - if ((pe_mask & static_cast(1U << pe)) != 0U) { + if ((pe_mask & PeMaskBit(pe)) != 0U) { version.dtype = descriptors[pe].dtype; break; } @@ -92,7 +96,7 @@ SharedTileBank::Write(std::uint16_t shared_id, std::uint8_t pe_mask, std::uint8_ } for (std::size_t pe = 0; pe < kLinxCorePeCount; ++pe) { - const auto bit = static_cast(1U << pe); + const auto bit = PeMaskBit(pe); if ((pe_mask & bit) == 0U) { continue; } @@ -109,7 +113,7 @@ const SharedTileLane *SharedTileBank::Read(std::uint16_t shared_id, return nullptr; } const auto &version = versions_[shared_id]; - if ((version.initialized_mask & static_cast(1U << pe_id)) == 0U) { + if ((version.initialized_mask & PeMaskBit(pe_id)) == 0U) { return nullptr; } return &version.lanes[pe_id]; diff --git a/tests/checks/check_pto_v058_shared_fixture.py b/tests/checks/check_pto_v058_shared_fixture.py index 1e64709..f6be180 100644 --- a/tests/checks/check_pto_v058_shared_fixture.py +++ b/tests/checks/check_pto_v058_shared_fixture.py @@ -45,6 +45,10 @@ def main() -> int: _require(fixture.get("schema") == "linx-pto-v058-shared-state-v1", "unexpected schema") _require(fixture.get("pto_isa_release") == "0.58.0", "fixture must target PTO ISA 0.58.0") _require(fixture.get("pe_count") == 4, "fixture must use the architectural four-PE Core") + _require( + fixture.get("pe_mask_bits") == {"3": "PE0", "2": "PE1", "1": "PE2", "0": "PE3"}, + "PE mask must map bit3=PE0 through bit0=PE3", + ) _require(fixture.get("shared_tile_ids") == [0, 255], "fixture must cover S0..S255") _require(fixture.get("tsize_bytes_per_pe") == EXPECTED_SIZE_BYTES, "TSize table drifted") diff --git a/tests/fixtures/pto_v058_shared_state.json b/tests/fixtures/pto_v058_shared_state.json index 7a2d22e..5469e91 100644 --- a/tests/fixtures/pto_v058_shared_state.json +++ b/tests/fixtures/pto_v058_shared_state.json @@ -3,6 +3,7 @@ "pto_isa_release": "0.58.0", "pto_spec_commit": "8a77c9f0eab36cc41051519366ff163171f81463", "pe_count": 4, + "pe_mask_bits": {"3": "PE0", "2": "PE1", "1": "PE2", "0": "PE3"}, "shared_tile_ids": [0, 255], "tsize_bytes_per_pe": { "1": 128, @@ -27,8 +28,8 @@ "shared_tid": 0, "pe_mask": "0b0101", "tsize_code": 2, - "descriptors": {"0": {"rows": 8, "cols": 8}, "2": {"rows": 4, "cols": 16}}, - "payload_bytes": {"0": 256, "2": 256} + "descriptors": {"1": {"rows": 8, "cols": 8}, "3": {"rows": 4, "cols": 16}}, + "payload_bytes": {"1": 256, "3": 256} }, "expected": { "status": "applied", @@ -41,7 +42,7 @@ "id": "tlsu-subset-update", "operation": "TLSU", "precondition": {"allocation_mask": "0b0101", "initialized_mask": "0b0101"}, - "input": {"shared_tid": 0, "pe_mask": "0b0001", "tsize_code": 2, "payload_bytes": {"0": 256}}, + "input": {"shared_tid": 0, "pe_mask": "0b0001", "tsize_code": 2, "payload_bytes": {"3": 256}}, "expected": {"status": "applied", "allocation_mask": "0b0101", "initialized_mask": "0b0101"} }, { @@ -55,20 +56,20 @@ "id": "tlsu-descriptor-mismatch-atomic", "operation": "TLSU", "precondition": {"allocation_mask": "0b0101", "initialized_mask": "0b0101"}, - "input": {"shared_tid": 0, "pe_mask": "0b0101", "tsize_code": 2, "descriptors": {"0": {"rows": 8, "cols": 8}, "2": {"rows": 8, "cols": 8}}}, + "input": {"shared_tid": 0, "pe_mask": "0b0101", "tsize_code": 2, "descriptors": {"1": {"rows": 8, "cols": 8}, "3": {"rows": 8, "cols": 8}}}, "expected": {"status": "rejected", "reason": "descriptor-mismatch", "state_unchanged": true} }, { "id": "tlsu-payload-mismatch-atomic", "operation": "TLSU", "precondition": {"allocation_mask": "0b0101", "initialized_mask": "0b0101"}, - "input": {"shared_tid": 0, "pe_mask": "0b0101", "tsize_code": 2, "payload_bytes": {"0": 256, "2": 128}}, + "input": {"shared_tid": 0, "pe_mask": "0b0101", "tsize_code": 2, "payload_bytes": {"1": 256, "3": 128}}, "expected": {"status": "rejected", "reason": "payload-size-mismatch", "state_unchanged": true} }, { "id": "tlsu-undefined-read", "operation": "TLSU", - "input": {"shared_tid": 255, "pe_mask": "0b0001", "access": "read"}, + "input": {"shared_tid": 255, "pe_id": 3, "access": "read"}, "expected": {"status": "uninitialized", "state_unchanged": true} }, { diff --git a/tests/unit/emulator_test.cpp b/tests/unit/emulator_test.cpp index b5d232d..329d61a 100644 --- a/tests/unit/emulator_test.cpp +++ b/tests/unit/emulator_test.cpp @@ -69,7 +69,7 @@ int TestStateReset() { return 0; } -int TestV058SharedTileState() { +int TestSharedTileState() { SharedTileBank bank; const SharedTileDescriptor descriptor{ .dtype = 3, @@ -100,40 +100,40 @@ int TestV058SharedTileState() { bank.Version(7).allocation_mask != 0 || bank.Version(7).initialized_mask != 0) { return 31; } - if (bank.Write(7, 0x3, 1, descriptors, payloads) != SharedTileWriteStatus::Applied || - bank.Version(7).allocation_mask != 0x3 || bank.Version(7).initialized_mask != 0x3 || + if (bank.Write(7, 0xc, 1, descriptors, payloads) != SharedTileWriteStatus::Applied || + bank.Version(7).allocation_mask != 0xc || bank.Version(7).initialized_mask != 0xc || bank.Version(7).allocated_bytes != 256 || bank.Read(7, 1)->descriptor.valid_rows != 2) { return 32; } - if (bank.Read(7, 2) != nullptr || bank.Version(7).initialized_mask != 0x3) { + if (bank.Read(7, 2) != nullptr || bank.Version(7).initialized_mask != 0xc) { return 33; } const auto lane1_before = bank.Read(7, 1)->data; payloads[0].assign(128, 0x44); - if (bank.Write(7, 0x1, 1, descriptor, payloads) != SharedTileWriteStatus::Applied || + if (bank.Write(7, 0x8, 1, descriptor, payloads) != SharedTileWriteStatus::Applied || bank.Read(7, 0)->data.front() != 0x44 || bank.Read(7, 1)->data != lane1_before || - bank.Version(7).allocation_mask != 0x3) { + bank.Version(7).allocation_mask != 0xc) { return 34; } const auto lane0_before = bank.Read(7, 0)->data; payloads[0].assign(128, 0x66); payloads[1].assign(1, 0x77); - if (bank.Write(7, 0x3, 1, descriptors, payloads) != SharedTileWriteStatus::PayloadSizeMismatch || + if (bank.Write(7, 0xc, 1, descriptors, payloads) != SharedTileWriteStatus::PayloadSizeMismatch || bank.Read(7, 0)->data != lane0_before || bank.Read(7, 1)->data != lane1_before) { return 35; } payloads[1].assign(128, 0x22); payloads[2].assign(128, 0x55); - if (bank.Write(7, 0x4, 1, descriptor, payloads) != SharedTileWriteStatus::AllocationExpansion || - bank.Version(7).allocation_mask != 0x3 || bank.Read(7, 2) != nullptr) { + if (bank.Write(7, 0x2, 1, descriptor, payloads) != SharedTileWriteStatus::AllocationExpansion || + bank.Version(7).allocation_mask != 0xc || bank.Read(7, 2) != nullptr) { return 36; } auto mismatched = descriptor; mismatched.dtype = 9; - if (bank.Write(7, 0x1, 1, mismatched, payloads) != SharedTileWriteStatus::DescriptorMismatch || + if (bank.Write(7, 0x8, 1, mismatched, payloads) != SharedTileWriteStatus::DescriptorMismatch || bank.Read(7, 0)->descriptor.dtype != 3 || bank.Read(7, 0)->data.front() != 0x44) { return 37; } @@ -145,7 +145,7 @@ int TestV058SharedTileState() { return 0; } -int TestV058BindingPolicy() { +int TestSharedTileBindingPolicy() { if (!linx::model::emulator::BindingAllows(TileBindingKind::Bior, TileOperandSpace::ScalarAddress) || linx::model::emulator::BindingAllows(TileBindingKind::Bior, TileOperandSpace::Local) || @@ -173,7 +173,7 @@ int TestV058BindingPolicy() { return 0; } -int TestV058BiosDecode() { +int TestSharedTileBindingDecode() { Minst inst; if (DecodeMinstPacked(0x00001013ULL, 32, inst) != MinstCodecStatus::Ok || inst.mnemonic != "B.IOS" || inst.form_id != "11ff57a2e635") { @@ -201,7 +201,7 @@ int TestV058BiosDecode() { return 0; } -int TestV058TileHeadersAndUnsupportedScalar() { +int TestTileHeadersAndUnsupportedScalar() { { const auto bytes = EncodedBytes(BuildZeroInst("3c9e83c5a42f")); // BSTART.TPREFETCH if (bytes.empty()) { @@ -429,13 +429,13 @@ int main() { if (const int rc = TestStateReset(); rc != 0) { return rc; } - if (const int rc = TestV058SharedTileState(); rc != 0) { + if (const int rc = TestSharedTileState(); rc != 0) { return rc; } - if (const int rc = TestV058BindingPolicy(); rc != 0) { + if (const int rc = TestSharedTileBindingPolicy(); rc != 0) { return rc; } - if (const int rc = TestV058BiosDecode(); rc != 0) { + if (const int rc = TestSharedTileBindingDecode(); rc != 0) { return rc; } if (const int rc = TestMinstRecordAdapter(); rc != 0) { @@ -456,7 +456,7 @@ int main() { if (const int rc = TestMinstRecordDumpFormatting(); rc != 0) { return rc; } - if (const int rc = TestV058TileHeadersAndUnsupportedScalar(); rc != 0) { + if (const int rc = TestTileHeadersAndUnsupportedScalar(); rc != 0) { return rc; } return 0; diff --git a/tests/unit/minst_test.cpp b/tests/unit/minst_test.cpp index 27decb1..e0bca9e 100644 --- a/tests/unit/minst_test.cpp +++ b/tests/unit/minst_test.cpp @@ -255,7 +255,7 @@ int RunCoverageRoundTripSmoke() { return 0; } -int RunV058DeltaDecodeSmoke() { +int RunActiveDeltaDecodeSmoke() { struct ExpectedForm { std::string_view uid; std::string_view mnemonic; @@ -313,7 +313,7 @@ int RunV058DeltaDecodeSmoke() { return 0; } -int RunV058TeplSelectorSmoke() { +int RunTeplSelectorSmoke() { const auto *form = LookupFormByUid("f0754cf51a8d"); if (form == nullptr || form->mnemonic != "BSTART.TEPL") { return 70; @@ -500,14 +500,14 @@ int main() { std::cerr << "RunAsmTemplateReplacementSmoke failed with code " << asm_replace << '\n'; return 6; } - const auto v058_delta = RunV058DeltaDecodeSmoke(); - if (v058_delta != 0) { - std::cerr << "RunV058DeltaDecodeSmoke failed with code " << v058_delta << '\n'; + const auto active_delta = RunActiveDeltaDecodeSmoke(); + if (active_delta != 0) { + std::cerr << "RunActiveDeltaDecodeSmoke failed with code " << active_delta << '\n'; return 7; } - const auto v058_tepl = RunV058TeplSelectorSmoke(); - if (v058_tepl != 0) { - std::cerr << "RunV058TeplSelectorSmoke failed with code " << v058_tepl << '\n'; + const auto tepl_selector = RunTeplSelectorSmoke(); + if (tepl_selector != 0) { + std::cerr << "RunTeplSelectorSmoke failed with code " << tepl_selector << '\n'; return 8; } return 0;