Skip to content

Commit da69c26

Browse files
authored
[Merge] workflows: Add qemu-eest workflow
workflows: Add qemu-eest workflow
2 parents aebc7dd + d4c2273 commit da69c26

File tree

10 files changed

+329
-60
lines changed

10 files changed

+329
-60
lines changed

.editorconfig

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[*]
2+
cpp_indent_braces=false
3+
cpp_indent_multi_line_relative_to=innermost_parenthesis
4+
cpp_indent_within_parentheses=indent
5+
cpp_indent_preserve_within_parentheses=false
6+
cpp_indent_case_labels=false
7+
cpp_indent_case_contents=true
8+
cpp_indent_case_contents_when_block=false
9+
cpp_indent_lambda_braces_when_parameter=true
10+
cpp_indent_goto_labels=one_left
11+
cpp_indent_preprocessor=leftmost_column
12+
cpp_indent_access_specifiers=false
13+
cpp_indent_namespace_contents=true
14+
cpp_indent_preserve_comments=false
15+
cpp_new_line_before_open_brace_namespace=ignore
16+
cpp_new_line_before_open_brace_type=ignore
17+
cpp_new_line_before_open_brace_function=ignore
18+
cpp_new_line_before_open_brace_block=ignore
19+
cpp_new_line_before_open_brace_lambda=ignore
20+
cpp_new_line_scope_braces_on_separate_lines=false
21+
cpp_new_line_close_brace_same_line_empty_type=false
22+
cpp_new_line_close_brace_same_line_empty_function=false
23+
cpp_new_line_before_catch=true
24+
cpp_new_line_before_else=true
25+
cpp_new_line_before_while_in_do_while=false
26+
cpp_space_before_function_open_parenthesis=remove
27+
cpp_space_within_parameter_list_parentheses=false
28+
cpp_space_between_empty_parameter_list_parentheses=false
29+
cpp_space_after_keywords_in_control_flow_statements=true
30+
cpp_space_within_control_flow_statement_parentheses=false
31+
cpp_space_before_lambda_open_parenthesis=false
32+
cpp_space_within_cast_parentheses=false
33+
cpp_space_after_cast_close_parenthesis=false
34+
cpp_space_within_expression_parentheses=false
35+
cpp_space_before_block_open_brace=true
36+
cpp_space_between_empty_braces=false
37+
cpp_space_before_initializer_list_open_brace=false
38+
cpp_space_within_initializer_list_braces=true
39+
cpp_space_preserve_in_initializer_list=true
40+
cpp_space_before_open_square_bracket=false
41+
cpp_space_within_square_brackets=false
42+
cpp_space_before_empty_square_brackets=false
43+
cpp_space_between_empty_square_brackets=false
44+
cpp_space_group_square_brackets=true
45+
cpp_space_within_lambda_brackets=false
46+
cpp_space_between_empty_lambda_brackets=false
47+
cpp_space_before_comma=false
48+
cpp_space_after_comma=true
49+
cpp_space_remove_around_member_operators=true
50+
cpp_space_before_inheritance_colon=true
51+
cpp_space_before_constructor_colon=true
52+
cpp_space_remove_before_semicolon=true
53+
cpp_space_after_semicolon=false
54+
cpp_space_remove_around_unary_operator=true
55+
cpp_space_around_binary_operator=insert
56+
cpp_space_around_assignment_operator=insert
57+
cpp_space_pointer_reference_alignment=left
58+
cpp_space_around_ternary_operator=insert
59+
cpp_wrap_preserve_blocks=one_liners
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Qemu EESTs on RV32IM
2+
3+
on:
4+
schedule:
5+
- cron: "0 0 */3 * *" # Run every 3 days at midnight UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
build-and-run-rv32im:
10+
name: Build and Run EEST Tests (Ubuntu 25.10)
11+
runs-on: ubuntu-latest
12+
container: ubuntu:25.10
13+
steps:
14+
- name: Install system dependencies
15+
env:
16+
DEBIAN_FRONTEND: noninteractive
17+
TZ: Etc/UTC
18+
run: |
19+
apt update
20+
apt install -y \
21+
build-essential cmake ninja-build \
22+
git git-lfs \
23+
python3 python3-pip pipx \
24+
nodejs npm \
25+
qemu-system-misc
26+
pipx install conan
27+
pipx ensurepath
28+
npm i -g xpm
29+
xpm install @xpack-dev-tools/riscv-none-elf-gcc@latest --global --verbose
30+
echo 'export PATH=$HOME/.local/xPacks/@xpack-dev-tools/riscv-none-elf-gcc/15.2.0-1.1/.content/bin:$PATH' >> ~/.bashrc
31+
echo "PATH=$HOME/.local/xPacks/@xpack-dev-tools/riscv-none-elf-gcc/15.2.0-1.1/.content/bin:$PATH" >> $GITHUB_ENV
32+
33+
- name: Verify tool versions
34+
run: |
35+
riscv-none-elf-gcc --version
36+
riscv-none-elf-g++ --version
37+
cmake --version
38+
ninja --version
39+
git --version
40+
git lfs version
41+
python3 --version
42+
ctest --version
43+
qemu-system-riscv32 --version
44+
45+
- name: Checkout repository
46+
uses: actions/checkout@v4
47+
with:
48+
submodules: recursive
49+
lfs: true
50+
51+
- name: Build EEST blockchain tests
52+
run: |
53+
cd qemu_runner && make xpack-elf-eest
54+
55+
- name: Run EEST blockchain tests
56+
id: run_tests
57+
shell: bash
58+
run: |
59+
set +e
60+
cd qemu_runner
61+
make rerun_ctest 2>&1 | tee output.log
62+
63+
exit_code=${PIPESTATUS[0]}
64+
65+
# Parse test results
66+
if grep -q "tests passed" output.log; then
67+
result_line=$(grep "tests passed" output.log | tail -1)
68+
# Extract numbers: "X% tests passed, Y tests failed out of Z"
69+
passed=$(echo "$result_line" | grep -oP '\d+(?=% tests passed)')
70+
failed=$(echo "$result_line" | grep -oP '\d+(?= tests failed)')
71+
total=$(echo "$result_line" | grep -oP '(?<=out of )\d+')
72+
73+
echo "passed_percent=$passed" >> $GITHUB_OUTPUT
74+
echo "failed=$failed" >> $GITHUB_OUTPUT
75+
echo "total=$total" >> $GITHUB_OUTPUT
76+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
77+
else
78+
echo "passed_percent=0" >> $GITHUB_OUTPUT
79+
echo "failed=-1" >> $GITHUB_OUTPUT
80+
echo "total=0" >> $GITHUB_OUTPUT
81+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
82+
fi
83+
84+
- name: Print test summary
85+
if: always()
86+
shell: bash
87+
run: |
88+
echo "========================================"
89+
echo " EEST TEST SUMMARY"
90+
echo "========================================"
91+
echo ""
92+
echo "Results: ${{ steps.run_tests.outputs.passed_percent }}% passed, ${{ steps.run_tests.outputs.failed }} failed out of ${{ steps.run_tests.outputs.total }}"
93+
echo "========================================"
94+
95+
- name: Upload test logs
96+
if: always()
97+
uses: actions/upload-artifact@v4
98+
with:
99+
name: eest-test-logs
100+
path: |
101+
qemu_runner/output.log
102+
qemu_runner/build/Testing/Temporary/LastTest.log
103+
retention-days: 30
104+
105+
- name: Check failure threshold
106+
if: always()
107+
shell: bash
108+
run: |
109+
failed=${{ steps.run_tests.outputs.failed }}
110+
total=${{ steps.run_tests.outputs.total }}
111+
exit_code=${{ steps.run_tests.outputs.exit_code }}
112+
113+
if [[ "$failed" == "-1" ]]; then
114+
echo "ERROR: Could not parse test results"
115+
exit 1
116+
fi
117+
118+
if (( failed > 5 )); then
119+
echo "Too many failures - ${failed} failed out of ${total}"
120+
echo "failed" > failed.log
121+
exit 1
122+
fi
123+
124+
if (( failed > 0 )); then
125+
echo "WARNING: ${failed} test(s) failed, but within acceptable threshold (<=5)"
126+
else
127+
echo "SUCCESS: All ${total} tests passed!"
128+
fi

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ eest_blockchain_tests eest-blockchain-tests:
33
cmake -B build/eest -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DTESTS_DIR=third_party/eest-fixtures/blockchain_tests
44
cmake --build build/eest
55
ctest --test-dir build/eest --parallel
6+
7+
rv32im_eest_blockchain_tests:
8+
cd qemu_runner && make rv32im_eest_blockchain_tests

cmake/conan.cmake

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,16 @@ endif()
8282
set(CONAN_VERBOSITY "error")
8383
set(CONAN_BINARY_DIR "${CMAKE_BINARY_DIR}/conan2")
8484

85-
if(NOT DEFINED CONAN_PROFILE)
85+
# Check if a custom host profile is provided (e.g., for cross-compilation)
86+
if(DEFINED CONAN_PROFILE_HOST AND EXISTS "${CONAN_PROFILE_HOST}")
87+
message(STATUS "CONAN_PROFILE: Using custom host profile: ${CONAN_PROFILE_HOST}")
88+
# Override the CACHE variables that conan_provider.cmake uses
89+
set(CONAN_HOST_PROFILE "${CONAN_PROFILE_HOST}" CACHE STRING "Conan host profile" FORCE)
90+
set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile" FORCE)
91+
elseif(NOT DEFINED CONAN_PROFILE)
8692
guess_conan_profile()
8793
endif()
88-
message(VERBOSE "CONAN_PROFILE: ${CONAN_PROFILE}")
94+
message(STATUS "CONAN_PROFILE: ${CONAN_PROFILE}")
8995
# set(CONAN_PROFILE_PATH "${CMAKE_SOURCE_DIR}/cmake/profiles/${CONAN_PROFILE}")
9096
# set(CONAN_HOST_PROFILE "${CONAN_PROFILE_PATH}")
9197
# set(CONAN_BUILD_PROFILE "${CONAN_PROFILE_PATH}")

qemu_runner/CMakeLists.txt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,40 @@ if(TESTS_DIR)
101101

102102
include(CTest)
103103

104+
# Get paths relative to build directory for relocatable tests
105+
file(RELATIVE_PATH TESTS_DIR_REL "${CMAKE_CURRENT_BINARY_DIR}" "${TESTS_DIR}")
106+
104107
file(GLOB_RECURSE STATE_TRANSITION_TESTS CONFIGURE_DEPENDS "${TESTS_DIR}/*.json")
105108

106109
foreach(TEST_JSON ${STATE_TRANSITION_TESTS})
107110
file(RELATIVE_PATH TEST_REL_PATH "${TESTS_DIR}" "${TEST_JSON}")
111+
file(RELATIVE_PATH TEST_JSON_REL "${CMAKE_CURRENT_BINARY_DIR}" "${TEST_JSON}")
108112
set(TEST_NAME "state_transition/${TEST_REL_PATH}")
113+
114+
# Create a unique directory for this test to avoid file conflicts
115+
string(REPLACE "/" "_" TEST_DIR_NAME "${TEST_REL_PATH}")
116+
string(REPLACE ".json" "" TEST_DIR_NAME "${TEST_DIR_NAME}")
117+
set(TEST_WORK_DIR "${CMAKE_CURRENT_BINARY_DIR}/test_runs/${TEST_DIR_NAME}")
118+
file(MAKE_DIRECTORY "${TEST_WORK_DIR}")
119+
120+
# Calculate paths relative to the test working directory
121+
file(RELATIVE_PATH Z6M_REL "${TEST_WORK_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/z6m_debug")
122+
file(RELATIVE_PATH TEST_JSON_FROM_WORK "${TEST_WORK_DIR}" "${TEST_JSON}")
123+
124+
# Generate a test runner script to avoid quoting issues
125+
set(TEST_SCRIPT "${TEST_WORK_DIR}/run_test.sh")
126+
file(WRITE "${TEST_SCRIPT}" "#!/bin/bash\n")
127+
file(APPEND "${TEST_SCRIPT}" "set -e\n")
128+
file(APPEND "${TEST_SCRIPT}" "N=$(wc -c < '${TEST_JSON}')\n")
129+
file(APPEND "${TEST_SCRIPT}" "{ printf \"1 %u\\n\" $N; cat '${TEST_JSON}'; } > stdin_payload.bin\n")
130+
file(APPEND "${TEST_SCRIPT}" "exec qemu-system-riscv32 -M virt -m 2G -display none -bios '${CMAKE_CURRENT_BINARY_DIR}/z6m_debug' -monitor none -serial none -chardev file,id=shlog,path=semihost.log -semihosting-config enable=on,target=native,chardev=shlog\n")
131+
file(CHMOD "${TEST_SCRIPT}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
132+
109133
add_test(
110134
NAME ${TEST_NAME}
111-
COMMAND bash -c "N=`wc -c <'${TEST_JSON}'`; \
112-
{ printf '123456789_123456789_123456789_123'; printf '1 %u\\n' $N; cat '${TEST_JSON}'; } | \
113-
qemu-system-riscv32 -M virt -m 2G -nographic \
114-
-bios '$<TARGET_FILE:z6m_debug>' \
115-
-chardev stdio,mux=on,id=stdio0 \
116-
-serial chardev:stdio0 \
117-
-semihosting-config enable=on,chardev=stdio0 \
118-
-mon chardev=stdio0,mode=readline"
135+
COMMAND "${TEST_SCRIPT}"
136+
WORKING_DIRECTORY "${TEST_WORK_DIR}"
119137
)
120138
set_tests_properties(${TEST_NAME} PROPERTIES SKIP_RETURN_CODE 2)
121139
endforeach()
122-
endif()
140+
endif()

qemu_runner/Makefile

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
BUILD_DIR := $(CURDIR)/build
2-
CONAN_PROFILE := conan/profiles/rv32im.sp1.profile
3-
CMAKE_TOOLCHAIN := cmake/rv32im.sp1.cmake
42
ELF := $(BUILD_DIR)/z6m_debug
53

64
CONAN_PROFILE = $(CURDIR)/conan/profiles/riscv32im.conan.profile
@@ -103,9 +101,63 @@ qemu-eest: xpack-elf-eest
103101
-mon chardev=stdio0,mode=readline
104102

105103
rv32im_eest_blockchain_tests: xpack-elf-eest
106-
ctest --test-dir build --parallel
104+
ctest --test-dir build --parallel --output-on-failure
107105

108-
# Clean build artifacts
106+
rerun_ctest:
107+
ctest --test-dir build --parallel --output-on-failure -V --timeout 7200
108+
109+
# rerun-qemu-eest:
110+
# @if [ -z "$(file-name)" ]; then echo "Usage: make qemu-eest file-name=path/to.json"; exit 1; fi
111+
# @N=$$(wc -c <"$(file-name)"); \
112+
# { printf "123456789_123456789_123456789_123"; printf "1 %u\n" $$N; cat "$(file-name)"; } | \
113+
# qemu-system-riscv32 -M virt -m 2G -nographic \
114+
# -bios $(BUILD_DIR)/z6m_debug \
115+
# -chardev stdio,mux=on,id=stdio0 \
116+
# -serial chardev:stdio0 \
117+
# -semihosting-config enable=on,target=native,chardev=stdio0 \
118+
# -monitor none \
119+
# -qmp unix:/tmp/qmp.sock,server=on,wait=off
120+
121+
# rerun-qemu-eest:
122+
# which script;
123+
# @if [ -z "$(file-name)" ]; then echo "Usage: make rerun-qemu-eest file-name=path/to.json"; exit 1; fi
124+
# @N=$$(wc -c <"$(file-name)"); \
125+
# rm -f semihost.log; \
126+
# script -qefc "{ printf '123456789_123456789_123456789_123'; printf '1 %u\n' $$N; cat '$(file-name)'; } | \
127+
# qemu-system-riscv32 -M virt -m 2G -nographic \
128+
# -bios $(BUILD_DIR)/z6m_debug \
129+
# -chardev stdio,mux=on,id=stdio0,signal=off \
130+
# -serial chardev:stdio0 \
131+
# -semihosting-config enable=on,chardev=stdio0,target=native \
132+
# -monitor none" semihost.log
133+
134+
# rerun-qemu-eest:
135+
# @if [ -z "$(file-name)" ]; then echo "Usage: make rerun-qemu-eest file-name=path/to.json"; exit 1; fi
136+
# @N=$$(wc -c <"$(file-name)"); \
137+
# rm -f semihost.log serial.log; \
138+
# { printf "123456789_123456789_123456789_123"; printf "1 %u\n" $$N; cat "$(file-name)"; } | \
139+
# qemu-system-riscv32 -M virt -m 2G -display none \
140+
# -bios $(BUILD_DIR)/z6m_debug \
141+
# -chardev stdio,id=sh0,signal=off \
142+
# -semihosting-config enable=on,target=native,chardev=sh0 \
143+
# -serial none \
144+
# -monitor none
145+
146+
147+
rerun-qemu-eest:
148+
@if [ -z "$(file-name)" ]; then echo "Usage: make rerun-qemu-eest file-name=path/to.json"; exit 1; fi
149+
@N=$$(wc -c <"$(file-name)"); \
150+
rm -f semihost.log serial.log; \
151+
{ printf "1 %u\n" $$N; cat "$(file-name)"; } > stdin_payload.bin; \
152+
qemu-system-riscv32 -M virt -m 2G -display none \
153+
-bios $(BUILD_DIR)/z6m_debug \
154+
-monitor none \
155+
-serial none \
156+
-chardev file,id=shlog,path=semihost.log \
157+
-semihosting-config enable=on,target=native,chardev=shlog
158+
159+
160+
# # Clean build artifacts
109161
.PHONY: clean
110162
clean:
111163
rm -rf $(BUILD_DIR)

qemu_runner/qemu-xpack.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ OUTPUT_ARCH(riscv)
1010
MEMORY
1111
{
1212
/* qemu-system-risc64 virt machine */
13-
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M
13+
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 1024M
1414
}
1515

1616
ENTRY(__entry)

qemu_runner/src/include/semihosting.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,19 @@ namespace sh
132132
return static_cast<int>(h); // -1 on failure
133133
}
134134

135+
inline int open_file_read(std::string path)
136+
{
137+
struct Args
138+
{
139+
const char* name;
140+
int mode;
141+
int name_len;
142+
} a{ path.c_str(), 0, static_cast<int>(path.size()) };
143+
144+
long h = call(SYS_OPEN, &a);
145+
return static_cast<int>(h);
146+
}
147+
135148
// ================= SYS_READ ==========================
136149
// SYS_READ (0x06) = “read N bytes from a file handle”
137150
// NOTE: Does not work without opening a file handle
@@ -166,6 +179,7 @@ namespace sh
166179
{
167180
std::size_t len = (n - off) >= block_size ? block_size : (n - off);
168181
std::size_t got = read_handle(handle, static_cast<char*>(buf) + off, len);
182+
169183
// if (got == 0)
170184
// {
171185
// sys_println("read_exact_tty - got 0, probable EOF");

0 commit comments

Comments
 (0)