From c9b255cab95b3f61cd08df6afd5a624282bf9624 Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Tue, 31 Mar 2026 12:20:36 -0300 Subject: [PATCH 1/7] feat: combine interpreter and checkpoint --- Makefile | 2 +- helper_scripts/generate_EmulatorConstants.lua | 32 ++-- helper_scripts/generate_SendCmioResponse.sh | 2 +- helper_scripts/generate_UArchStep.sh | 4 +- shasum-download | 4 +- src/AccessLogs.sol | 85 ++++++---- src/AdvanceStatus.sol | 76 +++++++++ src/EmulatorCompat.sol | 84 ++++++++-- src/EmulatorConstants.sol | 32 ++-- src/SendCmioResponse.sol | 12 +- src/UArchStep.sol | 64 +++++--- templates/AccessLogs.sol.template | 147 +++++++++++++----- templates/EmulatorConstants.sol.template | 5 + test/UArchInterpret.t.sol | 21 ++- 14 files changed, 434 insertions(+), 136 deletions(-) create mode 100644 src/AdvanceStatus.sol diff --git a/Makefile b/Makefile index 06b293f..f9255fd 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ TEST_DIR := test DOWNLOADDIR := downloads SRC_DIR := src -EMULATOR_VERSION ?= v0.19.0 +EMULATOR_VERSION ?= v0.20.0-test2 EMULATOR_TAG ?= TESTS_DATA_FILE ?= machine-emulator-tests-data.deb diff --git a/helper_scripts/generate_EmulatorConstants.lua b/helper_scripts/generate_EmulatorConstants.lua index eff52ba..082685e 100755 --- a/helper_scripts/generate_EmulatorConstants.lua +++ b/helper_scripts/generate_EmulatorConstants.lua @@ -15,34 +15,44 @@ end local out = io.stdout - - +out:write(' bytes32 constant UARCH_PRISTINE_STATE_HASH = 0x' .. hexstring(cartesi.UARCH_PRISTINE_STATE_HASH) .. ';\n') out:write(' uint64 constant UARCH_CYCLE_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_cycle")) .. ';\n') +out:write(' uint64 constant UARCH_CYCLE_MAX = 0x' .. hex(cartesi.UARCH_CYCLE_MAX) .. ';\n') out:write(' uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_halt_flag")) .. ';\n') out:write(' uint64 constant UARCH_PC_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_pc")) .. ';\n') out:write(' uint64 constant UARCH_X0_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("uarch_x0")) .. ';\n') out:write(' uint64 constant UARCH_SHADOW_START_ADDRESS = 0x' .. hex(cartesi.UARCH_SHADOW_START_ADDRESS) .. ';\n') out:write(' uint64 constant UARCH_SHADOW_LENGTH = 0x' .. hex(cartesi.UARCH_SHADOW_LENGTH) .. ';\n') +out:write(' uint64 constant AR_SHADOW_TLB_START = 0x' .. hex(cartesi.AR_SHADOW_TLB_START) .. ';\n') +out:write(' uint64 constant AR_SHADOW_TLB_LENGTH = 0x' .. hex(cartesi.AR_SHADOW_TLB_LENGTH) .. ';\n') out:write(' uint64 constant UARCH_RAM_START_ADDRESS = 0x' .. hex(cartesi.UARCH_RAM_START_ADDRESS) .. ';\n') out:write(' uint64 constant UARCH_RAM_LENGTH = 0x' .. hex(cartesi.UARCH_RAM_LENGTH) .. ';\n') out:write(' uint64 constant UARCH_STATE_START_ADDRESS = 0x' .. hex(cartesi.UARCH_STATE_START_ADDRESS) .. ';\n') -out:write(' uint8 constant UARCH_STATE_LOG2_SIZE = ' .. cartesi.UARCH_STATE_LOG2_SIZE .. ';\n') -out:write(' bytes32 constant UARCH_PRISTINE_STATE_HASH = 0x' .. hexstring(cartesi.UARCH_PRISTINE_STATE_HASH) .. ';\n') out:write(' uint64 constant UARCH_ECALL_FN_HALT = ' .. cartesi.UARCH_ECALL_FN_HALT .. ';\n') out:write(' uint64 constant UARCH_ECALL_FN_PUTCHAR = ' .. cartesi.UARCH_ECALL_FN_PUTCHAR .. ';\n') +out:write(' uint64 constant UARCH_ECALL_FN_MARK_DIRTY_PAGE = ' .. cartesi.UARCH_ECALL_FN_MARK_DIRTY_PAGE .. ';\n') +out:write(' uint64 constant UARCH_ECALL_FN_WRITE_TLB = ' .. cartesi.UARCH_ECALL_FN_WRITE_TLB .. ';\n') out:write(' uint64 constant HTIF_YIELD = 0x' .. hex(cartesi.machine:get_reg_address("htif_iyield")) .. ';\n') out:write(' uint64 constant IFLAGS_Y_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("iflags_Y")) .. ';\n') out:write(' uint64 constant HTIF_FROMHOST_ADDRESS = 0x' .. hex(cartesi.machine:get_reg_address("htif_fromhost")) .. ';\n') +out:write(' uint64 constant HTIF_TOHOST_ADDRESS = 0x' .. + hex(cartesi.machine:get_reg_address("htif_tohost")) .. ';\n') out:write(' uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x' .. hex(cartesi.CMIO_YIELD_REASON_ADVANCE_STATE) .. ';\n') -out:write(' uint32 constant TREE_LOG2_WORD_SIZE = 0x' .. hex(cartesi.TREE_LOG2_WORD_SIZE) .. ';\n') -out:write(' uint32 constant TREE_WORD_SIZE = uint32(1) << TREE_LOG2_WORD_SIZE;\n') -out:write(' uint64 constant PMA_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_START) .. ';\n') -out:write(' uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n') -out:write(' uint64 constant PMA_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_START) .. ';\n') -out:write(' uint8 constant PMA_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.PMA_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n') -out:close() +out:write(' uint32 constant HASH_TREE_LOG2_WORD_SIZE = 0x' .. hex(cartesi.HASH_TREE_LOG2_WORD_SIZE) .. ';\n') +out:write(' uint32 constant HASH_TREE_WORD_SIZE = uint32(1) << HASH_TREE_LOG2_WORD_SIZE;\n') +out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED = 0x' .. + hex(cartesi.CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED) .. ';\n') +out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_RX_REJECTED = 0x' .. + hex(cartesi.CMIO_YIELD_MANUAL_REASON_RX_REJECTED) .. ';\n') +out:write(' uint16 constant CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION = 0x' .. + hex(cartesi.CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION) .. ';\n') +out:write(' uint8 constant UARCH_STATE_LOG2_SIZE = ' .. cartesi.UARCH_STATE_LOG2_SIZE .. ';\n') +out:write(' uint64 constant AR_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.AR_CMIO_RX_BUFFER_START) .. ';\n') +out:write(' uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n') +out:write(' uint64 constant AR_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_START) .. ';\n') +out:write(' uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n') out:close() diff --git a/helper_scripts/generate_SendCmioResponse.sh b/helper_scripts/generate_SendCmioResponse.sh index 857ecaa..4fe7081 100755 --- a/helper_scripts/generate_SendCmioResponse.sh +++ b/helper_scripts/generate_SendCmioResponse.sh @@ -40,7 +40,7 @@ cpp_src=`echo "${BASH_REMATCH[1]}" \ | $SED "/ uint32 length);/d" \ | $SED "s/machine_merkle_tree::get_log2_word_size()/TREE_LOG2_WORD_SIZE/g" \ | $SED -E "s/($COMPAT_FNS)/EmulatorCompat.\1/g" \ - | $SED "s/writeMemoryWithPadding(a, PMA_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size);/a.writeRegion(Memory.regionFromPhysicalAddress(PMA_CMIO_RX_BUFFER_START.toPhysicalAddress(),Memory.alignedSizeFromLog2(uint8(writeLengthLog2Size - TREE_LOG2_WORD_SIZE))),dataHash);"/g \ + | $SED "s/writeMemoryWithPadding(a, AR_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size);/a.writeRegion(Memory.regionFromPhysicalAddress(AR_CMIO_RX_BUFFER_START.toPhysicalAddress(),Memory.alignedSizeFromLog2(uint8(writeLengthLog2Size - HASH_TREE_LOG2_WORD_SIZE))),dataHash);"/g \ | $SED -E "s/($CONSTANTS)([^a-zA-Z])/EmulatorConstants.\1\2/g" \ | $SED "s/void send_cmio_response(STATE_ACCESS a, uint16 reason, bytes data, uint32 dataLength) {/function sendCmioResponse(AccessLogs.Context memory a, uint16 reason, bytes32 dataHash, uint32 dataLength) internal pure {/" \ | $SED "s/const uint64/uint64/g" \ diff --git a/helper_scripts/generate_UArchStep.sh b/helper_scripts/generate_UArchStep.sh index d923a81..fa1ebf2 100755 --- a/helper_scripts/generate_UArchStep.sh +++ b/helper_scripts/generate_UArchStep.sh @@ -45,10 +45,10 @@ pattern="namespace cartesi \{(.*)\}" # replace cpp specific syntaxes with solidity ones cpp_src=`echo "${BASH_REMATCH[1]}" \ | $SED "/template/d" \ - | $SED "/note = a.make_scoped_note/d" \ + | $SED "/[[maybe_unused]]/d" \ | $SED "/(void) note/d" \ | $SED "s/constexpr//g" \ - | $SED "s/UarchState &a/AccessLogs.Context memory a/g" \ + | $SED "s/const UarchState a/AccessLogs.Context memory a/g" \ | $SED "s/::/./g" \ | $SED "s/UINT64_MAX/type(uint64).max/g" \ | $SED -E "s/UArchStepStatus uarch_step/static inline UArchStepStatus step/g" \ diff --git a/shasum-download b/shasum-download index e84816d..4aa192e 100644 --- a/shasum-download +++ b/shasum-download @@ -1,2 +1,2 @@ -127e5430d6ca80c65f1943d2b72fe4fd0b970f444376cf6f8663f3ace496f5ff downloads/machine-emulator-tests-data.deb -154e2da0e3bb531a8b6e1f9ec64cb90a55cea245aeece2685aa34378d1cf9ea1 downloads/uarch-riscv-tests-json-logs.tar.gz +c55574322e29dcc232ef1eb3db41caeadc49c998487b76ab112161c9d7014f6c downloads/machine-emulator-tests-data.deb +70a5fae1254763e85889dafeda84b86bf501db832063ab8c58a0c6e0727e3ad2 downloads/uarch-riscv-tests-json-logs.tar.gz diff --git a/src/AccessLogs.sol b/src/AccessLogs.sol index 719c362..6a6a0d4 100644 --- a/src/AccessLogs.sol +++ b/src/AccessLogs.sol @@ -62,12 +62,6 @@ library AccessLogs { return end2; } - /// @dev bytes buffer layout is the same for `readWord` and `writeWord`, - /// [32 bytes as read data], [59 * 32 bytes as sibling hashes] - - // - // Read methods - // function readRegion( AccessLogs.Context memory a, Memory.Region memory region @@ -90,30 +84,6 @@ library AccessLogs { return readRegion(a, r); } - function readWord( - AccessLogs.Context memory a, - Memory.PhysicalAddress readAddress - ) internal pure returns (uint64) { - (Memory.PhysicalAddress leafAddress, uint64 wordOffset) = - readAddress.truncateToLeaf(); - - Memory.Region memory region = Memory.regionFromStride( - Memory.strideFromLeafAddress(leafAddress), - Memory.alignedSizeFromLog2(0) - ); - - bytes32 leaf = a.buffer.consumeBytes32(); - bytes32 rootHash = - a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf))); - require(a.currentRootHash == rootHash, "Read word root doesn't match"); - - bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset); - return machineWordToSolidityUint64(word); - } - - // - // Write methods - // function writeRegion( AccessLogs.Context memory a, Memory.Region memory region, @@ -141,6 +111,36 @@ library AccessLogs { writeRegion(a, r, newHash); } + /// @dev bytes buffer layout is the same for `readWord` and `writeWord`, + /// [32 bytes as read data], [59 * 32 bytes as sibling hashes] + + // + // Read methods + // + function readWord( + AccessLogs.Context memory a, + Memory.PhysicalAddress readAddress + ) internal pure returns (uint64) { + (Memory.PhysicalAddress leafAddress, uint64 wordOffset) = + readAddress.truncateToLeaf(); + + Memory.Region memory region = Memory.regionFromStride( + Memory.strideFromLeafAddress(leafAddress), + Memory.alignedSizeFromLog2(0) + ); + + bytes32 leaf = a.buffer.consumeBytes32(); + bytes32 rootHash = + a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf))); + require(a.currentRootHash == rootHash, "Read word root doesn't match"); + + bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset); + return machineWordToSolidityUint64(word); + } + + // + // Write methods + // function writeWord( AccessLogs.Context memory a, Memory.PhysicalAddress writeAddress, @@ -190,4 +190,29 @@ library AccessLogs { return (leaf & mask) | toWrite; } + + /// @notice Writes 4 64-bit words (32 bytes total) to a single leaf in memory + /// @dev This function is specifically designed for writing TLB entries and similar structures + function write4Words( + AccessLogs.Context memory a, + Memory.Stride writeStride, + uint64 word0, + uint64 word1, + uint64 word2, + uint64 word3 + ) internal pure { + // Flip endianess and pack data + bytes8 w0 = solidityUint64ToMachineWord(word0); + bytes8 w1 = solidityUint64ToMachineWord(word1); + bytes8 w2 = solidityUint64ToMachineWord(word2); + bytes8 w3 = solidityUint64ToMachineWord(word3); + bytes32 packed = bytes32(abi.encodePacked(w0, w1, w2, w3)); + // compute hash and write region + bytes32 newHash = keccak256(abi.encodePacked(packed)); + writeRegion( + a, + Memory.regionFromStride(writeStride, Memory.alignedSizeFromLog2(0)), + newHash + ); + } } diff --git a/src/AdvanceStatus.sol b/src/AdvanceStatus.sol new file mode 100644 index 0000000..3df320b --- /dev/null +++ b/src/AdvanceStatus.sol @@ -0,0 +1,76 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 +// +// 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. +// + +/// @title AdvanceStatus +/// @notice Return advance status + +pragma solidity ^0.8.0; + +import "./EmulatorCompat.sol"; +import "./EmulatorConstants.sol"; + +library AdvanceStatus { + enum Status { + NOT_YIELDED, + ACCEPTED, + REJECTED, + EXCEPTION + } + + /* + typedef struct cmt_io_yield { + uint8_t dev; + uint8_t cmd; + uint16_t reason; + uint32_t data; + } cmt_io_yield_t; + */ + + error InvalidReason(uint16 reason); + + function advanceStatus(AccessLogs.Context memory a) + internal + pure + returns (Status) + { + if (!EmulatorCompat.readIflagsY(a)) { + return Status.NOT_YIELDED; + } + + // the following two approaches are equivalent: + // 1. swap the whole struct and then extract the reason + // 2. extract the reason from the struct and then swap the value + // EmulatorCompat.readWord already swaps the struct, so we can extract the reason directly + + uint64 tohost = + EmulatorCompat.readWord(a, EmulatorConstants.HTIF_TOHOST_ADDRESS); + uint16 reason = uint16(tohost >> 32); + + if (reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED) { + return Status.ACCEPTED; + } else if ( + reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_RX_REJECTED + ) { + return Status.REJECTED; + } else if ( + reason == EmulatorConstants.CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION + ) { + return Status.EXCEPTION; + } else { + revert InvalidReason(reason); + } + } +} diff --git a/src/EmulatorCompat.sol b/src/EmulatorCompat.sol index 1e08456..ba3a4d7 100644 --- a/src/EmulatorCompat.sol +++ b/src/EmulatorCompat.sol @@ -20,8 +20,28 @@ import "./AccessLogs.sol"; library EmulatorCompat { using AccessLogs for AccessLogs.Context; + using Buffer for Buffer.Context; using Memory for uint64; + function getCheckpointHash(AccessLogs.Context memory a) + internal + pure + returns (bytes32) + { + bytes32 checkpointHash = a.buffer.consumeBytes32(); + bytes32 hashOfCheckpointHash = a.readLeaf( + Memory.strideFromLeafAddress( + EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + ) + ); + require( + keccak256(abi.encodePacked(checkpointHash)) == hashOfCheckpointHash, + "checkpoint hash mismatch" + ); + + return checkpointHash; + } + function readCycle(AccessLogs.Context memory a) internal pure @@ -35,12 +55,19 @@ library EmulatorCompat { function readHaltFlag(AccessLogs.Context memory a) internal pure - returns (bool) + returns (uint64) + { + return a.readWord( + EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress() + ); + } + + function writeHaltFlag(AccessLogs.Context memory a, uint64 val) + internal + pure { - return ( - a.readWord( - EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress() - ) != 0 + a.writeWord( + EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress(), val ); } @@ -82,9 +109,15 @@ library EmulatorCompat { ); } - function setHaltFlag(AccessLogs.Context memory a) internal pure { - a.writeWord( - EmulatorConstants.UARCH_HALT_FLAG_ADDRESS.toPhysicalAddress(), 1 + function setCheckpointHash( + AccessLogs.Context memory a, + bytes32 checkpointHash + ) internal pure { + a.writeLeaf( + Memory.strideFromLeafAddress( + EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + ), + keccak256(abi.encodePacked(checkpointHash)) ); } @@ -280,7 +313,40 @@ library EmulatorCompat { revert(text); } - function putChar(AccessLogs.Context memory a, uint8 c) internal pure {} + function putCharECALL(AccessLogs.Context memory a, uint8 c) internal pure {} + + function markDirtyPageECALL( + AccessLogs.Context memory a, + uint64 paddr, + uint64 pma_index + ) internal pure {} + + function writeTlbECALL( + AccessLogs.Context memory a, + uint64 setIndex, + uint64 slotIndex, + uint64 vaddrPage, + uint64 vpOffset, + uint64 pmaIndex + ) internal pure { + // compute physical address of the TLB slot + uint64 paddress = EmulatorConstants.AR_SHADOW_TLB_START + + (setIndex * EmulatorConstants.TLB_SET_LENGTH) + + (slotIndex * EmulatorConstants.TLB_SLOT_LENGTH); + + // compute stride from physical address (stride = address / leaf_size) + Memory.Stride writeStride = + Memory.strideFromLeafAddress(paddress.toPhysicalAddress()); + + // write the 4 words + a.write4Words( + writeStride, + vaddrPage, + vpOffset, + pmaIndex, + 0 // zero_padding_ + ); + } function uint32Log2(uint32 value) internal pure returns (uint32) { require(value > 0, "EmulatorCompat: log2(0) is undefined"); diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index f38a9f4..d333846 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -24,32 +24,46 @@ pragma solidity ^0.8.0; library EmulatorConstants { // START OF AUTO-GENERATED CODE + bytes32 constant UARCH_PRISTINE_STATE_HASH = + 0xa73214303203557aea4dd151d18244b87f90532fc40e0f25c8795456e33f2b8f; uint64 constant UARCH_CYCLE_ADDRESS = 0x400008; + uint64 constant UARCH_CYCLE_MAX = 0x100000; uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x400000; uint64 constant UARCH_PC_ADDRESS = 0x400010; uint64 constant UARCH_X0_ADDRESS = 0x400018; uint64 constant UARCH_SHADOW_START_ADDRESS = 0x400000; uint64 constant UARCH_SHADOW_LENGTH = 0x1000; + uint64 constant AR_SHADOW_TLB_START = 0x1000; + uint64 constant AR_SHADOW_TLB_LENGTH = 0x6000; uint64 constant UARCH_RAM_START_ADDRESS = 0x600000; uint64 constant UARCH_RAM_LENGTH = 0x200000; uint64 constant UARCH_STATE_START_ADDRESS = 0x400000; uint8 constant UARCH_STATE_LOG2_SIZE = 22; - bytes32 constant UARCH_PRISTINE_STATE_HASH = - 0x1bbf39b2c4324c9c8862b8e5550fe35b06ebccb0dcd2c3f114cc1411813ca5fc; uint64 constant UARCH_ECALL_FN_HALT = 1; uint64 constant UARCH_ECALL_FN_PUTCHAR = 2; + uint64 constant UARCH_ECALL_FN_MARK_DIRTY_PAGE = 3; + uint64 constant UARCH_ECALL_FN_WRITE_TLB = 4; uint64 constant HTIF_YIELD = 0x348; - uint64 constant IFLAGS_Y_ADDRESS = 0x2f8; + uint64 constant IFLAGS_Y_ADDRESS = 0x300; uint64 constant HTIF_FROMHOST_ADDRESS = 0x330; + uint64 constant HTIF_TOHOST_ADDRESS = 0x328; uint8 constant CMIO_YIELD_REASON_ADVANCE_STATE = 0x0; - uint32 constant TREE_LOG2_WORD_SIZE = 0x5; - uint32 constant TREE_WORD_SIZE = uint32(1) << TREE_LOG2_WORD_SIZE; - uint64 constant PMA_CMIO_RX_BUFFER_START = 0x60000000; - uint8 constant PMA_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; - uint64 constant PMA_CMIO_TX_BUFFER_START = 0x60800000; - uint8 constant PMA_CMIO_TX_BUFFER_LOG2_SIZE = 0x15; + uint32 constant HASH_TREE_LOG2_WORD_SIZE = 0x5; + uint32 constant HASH_TREE_WORD_SIZE = uint32(1) << HASH_TREE_LOG2_WORD_SIZE; + uint16 constant CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED = 0x1; + uint16 constant CMIO_YIELD_MANUAL_REASON_RX_REJECTED = 0x2; + uint16 constant CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION = 0x4; + uint64 constant AR_CMIO_RX_BUFFER_START = 0x60000000; + uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; + uint64 constant AR_CMIO_TX_BUFFER_START = 0x60800000; + uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x15; // END OF AUTO-GENERATED CODE uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; + + uint64 constant TLB_SLOT_LENGTH = 32; + uint64 constant TLB_SET_SIZE = 256; + uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; + uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; } diff --git a/src/SendCmioResponse.sol b/src/SendCmioResponse.sol index 4724762..3c35cc3 100644 --- a/src/SendCmioResponse.sol +++ b/src/SendCmioResponse.sol @@ -42,8 +42,10 @@ library SendCmioResponse { if (dataLength > 0) { // Find the write length: the smallest power of 2 that is >= dataLength and >= tree leaf size uint32 writeLengthLog2Size = EmulatorCompat.uint32Log2(dataLength); - if (writeLengthLog2Size < EmulatorConstants.TREE_LOG2_WORD_SIZE) { - writeLengthLog2Size = EmulatorConstants.TREE_LOG2_WORD_SIZE; // minimum write size is the tree leaf size + if ( + writeLengthLog2Size < EmulatorConstants.HASH_TREE_LOG2_WORD_SIZE + ) { + writeLengthLog2Size = EmulatorConstants.HASH_TREE_LOG2_WORD_SIZE; // minimum write size is the tree leaf size } if ( EmulatorCompat.uint32ShiftLeft(1, writeLengthLog2Size) @@ -53,7 +55,7 @@ library SendCmioResponse { } if ( writeLengthLog2Size - > EmulatorConstants.PMA_CMIO_RX_BUFFER_LOG2_SIZE + > EmulatorConstants.AR_CMIO_RX_BUFFER_LOG2_SIZE ) { EmulatorCompat.throwRuntimeError( a, "CMIO response data is too large" @@ -61,12 +63,12 @@ library SendCmioResponse { } a.writeRegion( Memory.regionFromPhysicalAddress( - EmulatorConstants.PMA_CMIO_RX_BUFFER_START.toPhysicalAddress( + EmulatorConstants.AR_CMIO_RX_BUFFER_START.toPhysicalAddress( ), Memory.alignedSizeFromLog2( uint8( writeLengthLog2Size - - EmulatorConstants.TREE_LOG2_WORD_SIZE + - EmulatorConstants.HASH_TREE_LOG2_WORD_SIZE ) ) ), diff --git a/src/UArchStep.sol b/src/UArchStep.sol index b26c38c..9ada8bd 100644 --- a/src/UArchStep.sol +++ b/src/UArchStep.sol @@ -574,8 +574,8 @@ library UArchStep { uint8 rd = operandRd(insn); uint8 rs1 = operandRs1(insn); if (rd != 0) { - uint64 rs1val = EmulatorCompat.readX(a, rs1); - if (int64(rs1val) < imm) { + int64 rs1val = int64(EmulatorCompat.readX(a, rs1)); + if (rs1val < imm) { EmulatorCompat.writeX(a, rd, 1); } else { EmulatorCompat.writeX(a, rd, 0); @@ -1012,7 +1012,7 @@ library UArchStep { return advancePc(a, pc); } - function executeFENCE(AccessLogs.Context memory a, uint64 pc) + function executeFENCE(AccessLogs.Context memory a, uint32 insn, uint64 pc) private pure { @@ -1062,24 +1062,48 @@ library UArchStep { return advancePc(a, pc); } - function executeECALL(AccessLogs.Context memory a, uint64 pc) + function executeECALL(AccessLogs.Context memory a, uint32 insn, uint64 pc) private pure { + // ECALL conventions + // a0--a7 are the same as x10--x17 + // syscall is passed in a7 + // arguments are passed in a0--a5 + // return value is in a0 (and maybe also in a1) uint64 fn = EmulatorCompat.readX(a, 17); // a7 contains the function number if (fn == EmulatorConstants.UARCH_ECALL_FN_HALT) { - return EmulatorCompat.setHaltFlag(a); + return EmulatorCompat.writeHaltFlag(a, 1); } if (fn == EmulatorConstants.UARCH_ECALL_FN_PUTCHAR) { - uint64 character = EmulatorCompat.readX(a, 16); // a6 contains the character to print - EmulatorCompat.putChar(a, uint8(character)); - } else { - EmulatorCompat.throwRuntimeError(a, "unsupported ecall function"); - } - return advancePc(a, pc); - } - - function executeEBREAK(AccessLogs.Context memory a) private pure { + uint64 c = EmulatorCompat.readX(a, 10); // a0 contains the character to print + EmulatorCompat.putCharECALL(a, uint8(c)); // Can be a NOOP in Solidity + return advancePc(a, pc); + } + if (fn == EmulatorConstants.UARCH_ECALL_FN_MARK_DIRTY_PAGE) { + uint64 paddr = EmulatorCompat.readX(a, 10); // a0 contains physical address in page to be marked dirty + uint64 pma_index = EmulatorCompat.readX(a, 11); // a1 contains a index of PMA where page falls + EmulatorCompat.markDirtyPageECALL(a, paddr, pma_index); // This MUST be be a NOOP in Solidity + return advancePc(a, pc); + } + if (fn == EmulatorConstants.UARCH_ECALL_FN_WRITE_TLB) { + uint64 set_index = EmulatorCompat.readX(a, 10); // a0 contains TLB set (code, read, write) + uint64 slot_index = EmulatorCompat.readX(a, 11); // a1 contains slot_index to modify + uint64 vaddr_page = EmulatorCompat.readX(a, 12); // a2 contains vaddr_page to write + uint64 vp_offset = EmulatorCompat.readX(a, 13); // a3 contains vp_offset to write + uint64 pma_index = EmulatorCompat.readX(a, 14); // a4 contains index of PMA where page falls + EmulatorCompat.writeTlbECALL( + a, set_index, slot_index, vaddr_page, vp_offset, pma_index + ); // WARNING: This CANNOT be a NOOP in Solidity + return advancePc(a, pc); + } + EmulatorCompat.throwRuntimeError(a, "unsupported ecall function"); + } + + function executeEBREAK(AccessLogs.Context memory a, uint32 insn, uint64 pc) + private + pure + { EmulatorCompat.throwRuntimeError(a, "uarch aborted"); } @@ -1287,13 +1311,13 @@ library UArchStep { return executeSLTI(a, insn, pc); } if (insnMatchOpcodeFunct3(insn, 0xf, 0x0)) { - return executeFENCE(a, pc); + return executeFENCE(a, insn, pc); } if (insn == uint32(0x73)) { - return executeECALL(a, pc); + return executeECALL(a, insn, pc); } if (insn == uint32(0x100073)) { - return executeEBREAK(a); + return executeEBREAK(a, insn, pc); } EmulatorCompat.throwRuntimeError(a, "illegal instruction"); } @@ -1306,11 +1330,11 @@ library UArchStep { // This must be the first read in order to match the first log access in machine.verify_step_uarch uint64 cycle = EmulatorCompat.readCycle(a); // do not advance if cycle will overflow - if (cycle == type(uint64).max) { + if (cycle >= EmulatorConstants.UARCH_CYCLE_MAX) { return UArchStepStatus.CycleOverflow; } // do not advance if machine is halted - if (EmulatorCompat.readHaltFlag(a)) { + if (EmulatorCompat.readHaltFlag(a) != 0) { return UArchStepStatus.UArchHalted; } // execute next instruction @@ -1328,5 +1352,7 @@ library UArchStep { // Explicit instantiation for uarch_replay_state_access + // Explicit instantiation for collect_uarch_cycle_hashes_state_access + // END OF AUTO-GENERATED CODE } diff --git a/templates/AccessLogs.sol.template b/templates/AccessLogs.sol.template index 1247327..02cbfc7 100644 --- a/templates/AccessLogs.sol.template +++ b/templates/AccessLogs.sol.template @@ -73,14 +73,7 @@ library AccessLogs { return end2; } - //:#ifndef test - - /// @dev bytes buffer layout is the same for `readWord` and `writeWord`, - /// [32 bytes as read data], [59 * 32 bytes as sibling hashes] - // - // Read methods - // function readRegion( AccessLogs.Context memory a, Memory.Region memory region @@ -103,31 +96,6 @@ library AccessLogs { return readRegion(a, r); } - function readWord( - AccessLogs.Context memory a, - Memory.PhysicalAddress readAddress - ) internal pure returns (uint64) { - (Memory.PhysicalAddress leafAddress, uint64 wordOffset) = - readAddress.truncateToLeaf(); - - Memory.Region memory region = Memory.regionFromStride( - Memory.strideFromLeafAddress(leafAddress), - Memory.alignedSizeFromLog2(0) - ); - - bytes32 leaf = a.buffer.consumeBytes32(); - bytes32 rootHash = a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf))); - require( - a.currentRootHash == rootHash, "Read word root doesn't match" - ); - - bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset); - return machineWordToSolidityUint64(word); - } - - // - // Write methods - // function writeRegion( AccessLogs.Context memory a, Memory.Region memory region, @@ -135,7 +103,7 @@ library AccessLogs { ) internal pure { bytes32 oldDrive = a.buffer.consumeBytes32(); (bytes32 rootHash,) = a.buffer.peekRoot(region, oldDrive); - + require( a.currentRootHash == rootHash, "Write region root doesn't match" ); @@ -155,6 +123,41 @@ library AccessLogs { writeRegion(a, r, newHash); } + //:#ifndef test + + /// @dev bytes buffer layout is the same for `readWord` and `writeWord`, + /// [32 bytes as read data], [59 * 32 bytes as sibling hashes] + + // + // Read methods + // + + function readWord( + AccessLogs.Context memory a, + Memory.PhysicalAddress readAddress + ) internal pure returns (uint64) { + (Memory.PhysicalAddress leafAddress, uint64 wordOffset) = + readAddress.truncateToLeaf(); + + Memory.Region memory region = Memory.regionFromStride( + Memory.strideFromLeafAddress(leafAddress), + Memory.alignedSizeFromLog2(0) + ); + + bytes32 leaf = a.buffer.consumeBytes32(); + bytes32 rootHash = a.buffer.getRoot(region, keccak256(abi.encodePacked(leaf))); + require( + a.currentRootHash == rootHash, "Read word root doesn't match" + ); + + bytes8 word = getBytes8FromBytes32AtOffset(leaf, wordOffset); + return machineWordToSolidityUint64(word); + } + + // + // Write methods + // + function writeWord( AccessLogs.Context memory a, Memory.PhysicalAddress writeAddress, @@ -205,13 +208,38 @@ library AccessLogs { return (leaf & mask) | toWrite; } + /// @notice Writes 4 64-bit words (32 bytes total) to a single leaf in memory + /// @dev This function is specifically designed for writing TLB entries and similar structures + function write4Words( + AccessLogs.Context memory a, + Memory.Stride writeStride, + uint64 word0, + uint64 word1, + uint64 word2, + uint64 word3 + ) internal pure { + // Flip endianess and pack data + bytes8 w0 = solidityUint64ToMachineWord(word0); + bytes8 w1 = solidityUint64ToMachineWord(word1); + bytes8 w2 = solidityUint64ToMachineWord(word2); + bytes8 w3 = solidityUint64ToMachineWord(word3); + bytes32 packed = bytes32(abi.encodePacked(w0, w1, w2, w3)); + // compute hash and write region + bytes32 newHash = keccak256(abi.encodePacked(packed)); + writeRegion(a, + Memory.regionFromStride(writeStride, + Memory.alignedSizeFromLog2(0) + ), + newHash); + } //:#else /// @dev This library mocks the `templates/AccessLogs.sol` yet with a very different implementation. - /// `bytes buffer` simulates the memory space with two separate regions. - /// The first 280 bytes are reserved for register space: 0x320 - 0x438 (35 registers * 8 bytes) - /// Following next will be continuous memory space: 0x70000000 - + /// `bytes buffer` simulates the memory space with three separate regions. + // 1. shadow TLB (AR_SHADOW_TLB_LENGTH bytes) + // 2. shadow UArch (UARCH_SHADOW_LENGTH bytes) + // 3. uarch RAM (rest of the bytes - test binary loaded here) function readWord( AccessLogs.Context memory a, @@ -242,11 +270,41 @@ library AccessLogs { } } - function writeRegion( + + /// @notice Writes 4 64-bit words (32 bytes total) to a single leaf in memory + /// @dev This function is specifically designed for writing TLB entries and similar structures + function write4Words( AccessLogs.Context memory a, - Memory.Region memory region, - bytes32 newHash - ) internal pure {} + Memory.Stride writeStride, + uint64 word0, + uint64 word1, + uint64 word2, + uint64 word3 + ) internal pure { + // Flip endianess and pack data + bytes8 w0 = solidityUint64ToMachineWord(word0); + bytes8 w1 = solidityUint64ToMachineWord(word1); + bytes8 w2 = solidityUint64ToMachineWord(word2); + bytes8 w3 = solidityUint64ToMachineWord(word3); + bytes32 packed = bytes32(abi.encodePacked(w0, w1, w2, w3)); + + // Compute the physical address from stride + // For a single leaf, alignedSize = 0, so address = stride << LOG2_LEAF + uint64 physicalAddress = Memory.Stride.unwrap(writeStride) << Memory.LOG2_LEAF; + Memory.PhysicalAddress paddr = Memory.toPhysicalAddress(physicalAddress); + + // Find the offset in the access log buffer that corresponds to the physical address + accessWord(a, paddr); + bytes memory data = a.buffer.data; + uint256 offset = a.buffer.offset; + + // Update the access log buffer + // The 32 is added to offset because we are accessing a byte array. + // And an array in solidity always starts with its length which is a 32 byte-long variable. + assembly { + mstore(add(data, add(offset, 32)), packed) + } + } function accessWord( AccessLogs.Context memory a, @@ -255,12 +313,17 @@ library AccessLogs { uint64 index; uint64 position = Memory.PhysicalAddress.unwrap(paddr); if ( + position >= EmulatorConstants.AR_SHADOW_TLB_START + && position <= EmulatorConstants.AR_SHADOW_TLB_START + EmulatorConstants.AR_SHADOW_TLB_LENGTH + ) { + index = position - EmulatorConstants.AR_SHADOW_TLB_START; + } else if ( position >= EmulatorConstants.UARCH_SHADOW_START_ADDRESS && position <= EmulatorConstants.UARCH_SHADOW_START_ADDRESS + EmulatorConstants.UARCH_SHADOW_LENGTH ) { - index = (position - EmulatorConstants.UARCH_SHADOW_START_ADDRESS); + index = (position - EmulatorConstants.UARCH_SHADOW_START_ADDRESS) + EmulatorConstants.AR_SHADOW_TLB_LENGTH; } else if (position >= EmulatorConstants.UARCH_RAM_START_ADDRESS) { - index = (position - EmulatorConstants.UARCH_RAM_START_ADDRESS) + (35 << 3); + index = (position - EmulatorConstants.UARCH_RAM_START_ADDRESS) + EmulatorConstants.AR_SHADOW_TLB_LENGTH + EmulatorConstants.UARCH_SHADOW_LENGTH; } else { revert("invalid memory access"); } diff --git a/templates/EmulatorConstants.sol.template b/templates/EmulatorConstants.sol.template index b238fe0..3b741ed 100644 --- a/templates/EmulatorConstants.sol.template +++ b/templates/EmulatorConstants.sol.template @@ -27,4 +27,9 @@ library EmulatorConstants { uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; + + uint64 constant TLB_SLOT_LENGTH = 32; + uint64 constant TLB_SET_SIZE = 256; + uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; + uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; } diff --git a/test/UArchInterpret.t.sol b/test/UArchInterpret.t.sol index 56cfc06..ed0ffd1 100644 --- a/test/UArchInterpret.t.sol +++ b/test/UArchInterpret.t.sol @@ -45,7 +45,6 @@ contract UArchInterpretTest is Test { uint256 steps; } - uint8 constant REGISTERS_LENGTH = 35; uint8 constant TEST_STATUS_X = 1; // test result code bytes8 constant TEST_SUCEEDED = 0x00000000be1e7aaa; // Indicates that test has passed @@ -87,11 +86,11 @@ contract UArchInterpretTest is Test { uint64(TEST_SUCEEDED) ); - bool halt = EmulatorCompat.readHaltFlag(a); + bool halt = EmulatorCompat.readHaltFlag(a) != 0; assertTrue(halt, "machine should halt"); uint64 cycle = EmulatorCompat.readCycle(a); - assertEq(cycle, catalog[i].steps, "cycle values should match"); + assertEq(catalog[i].steps, cycle, "cycle values should match"); } } @@ -120,7 +119,7 @@ contract UArchInterpretTest is Test { // reset cycle to 0 EmulatorCompat.writeCycle(a, 0); // set machine to halt - EmulatorCompat.setHaltFlag(a); + EmulatorCompat.writeHaltFlag(a, 1); status = UArchInterpret.interpret(a); @@ -167,9 +166,21 @@ contract UArchInterpretTest is Test { pure returns (AccessLogs.Context memory) { + /* + Memory layout: + - shadow TLB (AR_SHADOW_TLB_LENGTH bytes) + - shadow UArch (UARCH_SHADOW_LENGTH bytes) + - uarch RAM (rest of the bytes - test binary loaded here) + */ return AccessLogs.Context( bytes32(0), - Buffer.Context(new bytes(uint128(REGISTERS_LENGTH) * 8), 0) + Buffer.Context( + new bytes( + EmulatorConstants.AR_SHADOW_TLB_LENGTH + + EmulatorConstants.UARCH_SHADOW_LENGTH + ), + 0 + ) ); } } From b750f387e4893ccfc6325ed6b62483f1fd605ebf Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Thu, 9 Apr 2026 14:57:43 -0300 Subject: [PATCH 2/7] chore: update CHECKPOINT_ADDRESS to REVERT_ROOT_HASH_ADDRESS --- src/EmulatorCompat.sol | 4 ++-- src/EmulatorConstants.sol | 2 +- templates/EmulatorConstants.sol.template | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/EmulatorCompat.sol b/src/EmulatorCompat.sol index ba3a4d7..0354dd9 100644 --- a/src/EmulatorCompat.sol +++ b/src/EmulatorCompat.sol @@ -31,7 +31,7 @@ library EmulatorCompat { bytes32 checkpointHash = a.buffer.consumeBytes32(); bytes32 hashOfCheckpointHash = a.readLeaf( Memory.strideFromLeafAddress( - EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + EmulatorConstants.REVERT_ROOT_HASH_ADDRESS.toPhysicalAddress() ) ); require( @@ -115,7 +115,7 @@ library EmulatorCompat { ) internal pure { a.writeLeaf( Memory.strideFromLeafAddress( - EmulatorConstants.CHECKPOINT_ADDRESS.toPhysicalAddress() + EmulatorConstants.REVERT_ROOT_HASH_ADDRESS.toPhysicalAddress() ), keccak256(abi.encodePacked(checkpointHash)) ); diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index d333846..4a236c1 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -65,5 +65,5 @@ library EmulatorConstants { uint64 constant TLB_SLOT_LENGTH = 32; uint64 constant TLB_SET_SIZE = 256; uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; - uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; + uint64 constant REVERT_ROOT_HASH_ADDRESS = 0xfe0; } diff --git a/templates/EmulatorConstants.sol.template b/templates/EmulatorConstants.sol.template index 3b741ed..2c7bc32 100644 --- a/templates/EmulatorConstants.sol.template +++ b/templates/EmulatorConstants.sol.template @@ -31,5 +31,5 @@ library EmulatorConstants { uint64 constant TLB_SLOT_LENGTH = 32; uint64 constant TLB_SET_SIZE = 256; uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; - uint64 constant CHECKPOINT_ADDRESS = 0x7ffff000; + uint64 constant REVERT_ROOT_HASH_ADDRESS = 0xfe0; } From 9d5ebcd8c3550f27feff18277b35ddf58978cc7d Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Thu, 9 Apr 2026 15:19:25 -0300 Subject: [PATCH 3/7] chore: update emulator tag --- Makefile | 2 +- shasum-download | 4 ++-- src/EmulatorConstants.sol | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index f9255fd..222f933 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ TEST_DIR := test DOWNLOADDIR := downloads SRC_DIR := src -EMULATOR_VERSION ?= v0.20.0-test2 +EMULATOR_VERSION ?= v0.20.0-test3 EMULATOR_TAG ?= TESTS_DATA_FILE ?= machine-emulator-tests-data.deb diff --git a/shasum-download b/shasum-download index 4aa192e..710db2f 100644 --- a/shasum-download +++ b/shasum-download @@ -1,2 +1,2 @@ -c55574322e29dcc232ef1eb3db41caeadc49c998487b76ab112161c9d7014f6c downloads/machine-emulator-tests-data.deb -70a5fae1254763e85889dafeda84b86bf501db832063ab8c58a0c6e0727e3ad2 downloads/uarch-riscv-tests-json-logs.tar.gz +81e2fe10e8fefc7280649176bffb5d15a81561aa5256bfe81c505108aea82267 downloads/machine-emulator-tests-data.deb +a51357d09551aa9ba4b0f3e850f1ba47de7c82922764650014f482c742a8f9aa downloads/uarch-riscv-tests-json-logs.tar.gz diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index 4a236c1..244ae19 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -25,7 +25,7 @@ library EmulatorConstants { // START OF AUTO-GENERATED CODE bytes32 constant UARCH_PRISTINE_STATE_HASH = - 0xa73214303203557aea4dd151d18244b87f90532fc40e0f25c8795456e33f2b8f; + 0xa2f4f0018081d795e47c7feae9300055e8551eda5bd6473e54ca80ece64ea620; uint64 constant UARCH_CYCLE_ADDRESS = 0x400008; uint64 constant UARCH_CYCLE_MAX = 0x100000; uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x400000; @@ -38,7 +38,6 @@ library EmulatorConstants { uint64 constant UARCH_RAM_START_ADDRESS = 0x600000; uint64 constant UARCH_RAM_LENGTH = 0x200000; uint64 constant UARCH_STATE_START_ADDRESS = 0x400000; - uint8 constant UARCH_STATE_LOG2_SIZE = 22; uint64 constant UARCH_ECALL_FN_HALT = 1; uint64 constant UARCH_ECALL_FN_PUTCHAR = 2; uint64 constant UARCH_ECALL_FN_MARK_DIRTY_PAGE = 3; @@ -53,6 +52,7 @@ library EmulatorConstants { uint16 constant CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED = 0x1; uint16 constant CMIO_YIELD_MANUAL_REASON_RX_REJECTED = 0x2; uint16 constant CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION = 0x4; + uint8 constant UARCH_STATE_LOG2_SIZE = 22; uint64 constant AR_CMIO_RX_BUFFER_START = 0x60000000; uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; uint64 constant AR_CMIO_TX_BUFFER_START = 0x60800000; From 1b8f9a8336b9fdbacaa218c49447f4a51f9a30dc Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Thu, 9 Apr 2026 15:34:57 -0300 Subject: [PATCH 4/7] chore: code cleanup --- src/AccessLogs.sol | 2 +- src/EmulatorConstants.sol | 1 - templates/AccessLogs.sol.template | 4 ++-- templates/EmulatorConstants.sol.template | 1 - test/UArchInterpret.t.sol | 4 ++-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/AccessLogs.sol b/src/AccessLogs.sol index 6a6a0d4..05aebf1 100644 --- a/src/AccessLogs.sol +++ b/src/AccessLogs.sol @@ -201,7 +201,7 @@ library AccessLogs { uint64 word2, uint64 word3 ) internal pure { - // Flip endianess and pack data + // Flip endianness and pack data bytes8 w0 = solidityUint64ToMachineWord(word0); bytes8 w1 = solidityUint64ToMachineWord(word1); bytes8 w2 = solidityUint64ToMachineWord(word2); diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index 244ae19..4e476f6 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -59,7 +59,6 @@ library EmulatorConstants { uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x15; // END OF AUTO-GENERATED CODE - uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; uint64 constant TLB_SLOT_LENGTH = 32; diff --git a/templates/AccessLogs.sol.template b/templates/AccessLogs.sol.template index 02cbfc7..2277a59 100644 --- a/templates/AccessLogs.sol.template +++ b/templates/AccessLogs.sol.template @@ -218,7 +218,7 @@ library AccessLogs { uint64 word2, uint64 word3 ) internal pure { - // Flip endianess and pack data + // Flip endianness and pack data bytes8 w0 = solidityUint64ToMachineWord(word0); bytes8 w1 = solidityUint64ToMachineWord(word1); bytes8 w2 = solidityUint64ToMachineWord(word2); @@ -281,7 +281,7 @@ library AccessLogs { uint64 word2, uint64 word3 ) internal pure { - // Flip endianess and pack data + // Flip endianness and pack data bytes8 w0 = solidityUint64ToMachineWord(word0); bytes8 w1 = solidityUint64ToMachineWord(word1); bytes8 w2 = solidityUint64ToMachineWord(word2); diff --git a/templates/EmulatorConstants.sol.template b/templates/EmulatorConstants.sol.template index 2c7bc32..faf7dc7 100644 --- a/templates/EmulatorConstants.sol.template +++ b/templates/EmulatorConstants.sol.template @@ -25,7 +25,6 @@ library EmulatorConstants { // START OF AUTO-GENERATED CODE // END OF AUTO-GENERATED CODE - uint32 constant IFLAGS_Y_SHIFT = 1; uint64 constant LOG2_CYCLES_TO_RESET = 10; uint64 constant TLB_SLOT_LENGTH = 32; diff --git a/test/UArchInterpret.t.sol b/test/UArchInterpret.t.sol index ed0ffd1..c0f1857 100644 --- a/test/UArchInterpret.t.sol +++ b/test/UArchInterpret.t.sol @@ -47,7 +47,7 @@ contract UArchInterpretTest is Test { uint8 constant TEST_STATUS_X = 1; // test result code - bytes8 constant TEST_SUCEEDED = 0x00000000be1e7aaa; // Indicates that test has passed + bytes8 constant TEST_SUCCEEDED = 0x00000000be1e7aaa; // Indicates that test has passed bytes8 constant TEST_FAILED = 0x00000000deadbeef; // Indicates that test has failed string constant CATALOG_PATH = "catalog.json"; string constant JSON_PATH = "./test/uarch-log/"; @@ -83,7 +83,7 @@ contract UArchInterpretTest is Test { assertEq( // read test result from the register x, - uint64(TEST_SUCEEDED) + uint64(TEST_SUCCEEDED) ); bool halt = EmulatorCompat.readHaltFlag(a) != 0; From b7ea28b676d9a1318d1c29da56744005084be89d Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Thu, 9 Apr 2026 17:10:34 -0300 Subject: [PATCH 5/7] chore: point to final emulator tag --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 222f933..2000dd7 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ TEST_DIR := test DOWNLOADDIR := downloads SRC_DIR := src -EMULATOR_VERSION ?= v0.20.0-test3 +EMULATOR_VERSION ?= v0.20.0 EMULATOR_TAG ?= TESTS_DATA_FILE ?= machine-emulator-tests-data.deb From d9429ac2fe72632cb77e091e0bd2d79e22095735 Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Thu, 9 Apr 2026 17:45:40 -0300 Subject: [PATCH 6/7] chore: update download shasum --- shasum-download | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shasum-download b/shasum-download index 710db2f..22fb8d4 100644 --- a/shasum-download +++ b/shasum-download @@ -1,2 +1,2 @@ -81e2fe10e8fefc7280649176bffb5d15a81561aa5256bfe81c505108aea82267 downloads/machine-emulator-tests-data.deb -a51357d09551aa9ba4b0f3e850f1ba47de7c82922764650014f482c742a8f9aa downloads/uarch-riscv-tests-json-logs.tar.gz +fef8844a306d83eef9c30828986645f2b0ff149654490b0f7d47ec2883bcb693 downloads/machine-emulator-tests-data.deb +bc7a0cc14724167c2826967eaf62feb6a2c902b2b45a73f3c684e1c5ceaa8f69 downloads/uarch-riscv-tests-json-logs.tar.gz From fee663e0484eb524b7664e92275c875986e60e4b Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Fri, 10 Apr 2026 08:51:33 -0300 Subject: [PATCH 7/7] chore: generate REVERT_ROOT_HASH_ADDRESS --- helper_scripts/generate_EmulatorConstants.lua | 1 + src/EmulatorConstants.sol | 2 +- templates/EmulatorConstants.sol.template | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helper_scripts/generate_EmulatorConstants.lua b/helper_scripts/generate_EmulatorConstants.lua index 082685e..3e3d9ff 100755 --- a/helper_scripts/generate_EmulatorConstants.lua +++ b/helper_scripts/generate_EmulatorConstants.lua @@ -54,5 +54,6 @@ out:write(' uint64 constant AR_CMIO_RX_BUFFER_START = 0x' .. hex(cartesi.AR_C out:write(' uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_RX_BUFFER_LOG2_SIZE) .. ';\n') out:write(' uint64 constant AR_CMIO_TX_BUFFER_START = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_START) .. ';\n') out:write(' uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x' .. hex(cartesi.AR_CMIO_TX_BUFFER_LOG2_SIZE) .. ';\n') +out:write(' uint64 constant REVERT_ROOT_HASH_ADDRESS = 0x' .. hex(cartesi.AR_SHADOW_REVERT_ROOT_HASH_START) .. ';\n') out:close() diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index 4e476f6..ae89a42 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -57,6 +57,7 @@ library EmulatorConstants { uint8 constant AR_CMIO_RX_BUFFER_LOG2_SIZE = 0x15; uint64 constant AR_CMIO_TX_BUFFER_START = 0x60800000; uint8 constant AR_CMIO_TX_BUFFER_LOG2_SIZE = 0x15; + uint64 constant REVERT_ROOT_HASH_ADDRESS = 0xfe0; // END OF AUTO-GENERATED CODE uint64 constant LOG2_CYCLES_TO_RESET = 10; @@ -64,5 +65,4 @@ library EmulatorConstants { uint64 constant TLB_SLOT_LENGTH = 32; uint64 constant TLB_SET_SIZE = 256; uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; - uint64 constant REVERT_ROOT_HASH_ADDRESS = 0xfe0; } diff --git a/templates/EmulatorConstants.sol.template b/templates/EmulatorConstants.sol.template index faf7dc7..038141e 100644 --- a/templates/EmulatorConstants.sol.template +++ b/templates/EmulatorConstants.sol.template @@ -30,5 +30,4 @@ library EmulatorConstants { uint64 constant TLB_SLOT_LENGTH = 32; uint64 constant TLB_SET_SIZE = 256; uint64 constant TLB_SET_LENGTH = TLB_SET_SIZE * TLB_SLOT_LENGTH; - uint64 constant REVERT_ROOT_HASH_ADDRESS = 0xfe0; }