From 1d09edef0da3bae395a4f32f0160d1cf591a9b18 Mon Sep 17 00:00:00 2001 From: cassuto Date: Thu, 23 Apr 2026 22:33:50 -0400 Subject: [PATCH 1/3] add regression test --- tests/regression/Makefile | 4 ++ tests/regression/jalr/Makefile | 21 ++++++++ tests/regression/jalr/common.h | 8 +++ tests/regression/jalr/kernel.cpp | 52 +++++++++++++++++++ tests/regression/jalr/main.cpp | 87 ++++++++++++++++++++++++++++++++ tests/regression/jalr/start.S | 24 +++++++++ 6 files changed, 196 insertions(+) create mode 100644 tests/regression/jalr/Makefile create mode 100644 tests/regression/jalr/common.h create mode 100644 tests/regression/jalr/kernel.cpp create mode 100644 tests/regression/jalr/main.cpp create mode 100644 tests/regression/jalr/start.S diff --git a/tests/regression/Makefile b/tests/regression/Makefile index d08a48f3b6..54fdad66df 100644 --- a/tests/regression/Makefile +++ b/tests/regression/Makefile @@ -5,6 +5,7 @@ NP ?= 4 all: $(MAKE) -C basic + $(MAKE) -C jalr $(MAKE) -C demo $(MAKE) -C dogfood $(MAKE) -C dropout @@ -36,6 +37,7 @@ all-mpi: run-simx: $(MAKE) -C basic run-simx + $(MAKE) -C jalr run-simx $(MAKE) -C demo run-simx $(MAKE) -C dogfood run-simx $(MAKE) -C dropout run-simx @@ -67,6 +69,7 @@ run-simx-mpi: run-rtlsim: $(MAKE) -C basic run-rtlsim + $(MAKE) -C jalr run-rtlsim $(MAKE) -C demo run-rtlsim $(MAKE) -C dogfood run-rtlsim $(MAKE) -C dropout run-rtlsim @@ -88,6 +91,7 @@ run-rtlsim: clean: $(MAKE) -C basic clean + $(MAKE) -C jalr clean $(MAKE) -C demo clean $(MAKE) -C dogfood clean $(MAKE) -C dropout clean diff --git a/tests/regression/jalr/Makefile b/tests/regression/jalr/Makefile new file mode 100644 index 0000000000..70710e2fe5 --- /dev/null +++ b/tests/regression/jalr/Makefile @@ -0,0 +1,21 @@ +ROOT_DIR := $(realpath ../../..) +include $(ROOT_DIR)/config.mk + +PROJECT := jalr + +SRC_DIR := $(VORTEX_HOME)/tests/regression/$(PROJECT) + +SRCS := $(SRC_DIR)/main.cpp + +VX_SRCS := $(SRC_DIR)/kernel.cpp $(VORTEX_HOME)/kernel/src/vx_perf.c $(SRC_DIR)/start.S + +OPTS ?= + +include ../common.mk + +VX_LDFLAGS = -Wl,-Bstatic,--gc-sections,-T,$(VORTEX_HOME)/kernel/scripts/link$(XLEN).ld,--defsym=STARTUP_ADDR=$(STARTUP_ADDR) + +VX_CC = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-gcc +VX_CXX = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-g++ +VX_DP = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-objdump +VX_CP = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-objcopy diff --git a/tests/regression/jalr/common.h b/tests/regression/jalr/common.h new file mode 100644 index 0000000000..91c2ca32bf --- /dev/null +++ b/tests/regression/jalr/common.h @@ -0,0 +1,8 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +typedef struct { + uint64_t dst_addr; +} kernel_arg_t; + +#endif diff --git a/tests/regression/jalr/kernel.cpp b/tests/regression/jalr/kernel.cpp new file mode 100644 index 0000000000..7455ab9b63 --- /dev/null +++ b/tests/regression/jalr/kernel.cpp @@ -0,0 +1,52 @@ +#include +#include "common.h" + +int main() { + kernel_arg_t* __UNIFORM__ arg = (kernel_arg_t*)csr_read(VX_CSR_MSCRATCH); + + if (vx_warp_id() != 0 || vx_thread_id() != 0) + return 0; + + uint32_t value = 0; + asm volatile( + // x1 is prepared so that the misaligned decode path can immediately + // execute a valid JALR to label 3 and produce a deterministic failure + // value instead of aborting in the decoder. + "la x1, 3f\n" + // 1744 is chosen to match the immediate embedded in the overlapped + // misaligned instruction below: jalr x0, -1744(x1). + "addi x1, x1, 1744\n" + "la t0, 1f\n" + // Add 1 on purpose. A correct JALR implementation must clear bit 0 and + // land at 1f. An incorrect implementation enters at 1f + 1 instead. + "addi t0, t0, 1\n" + "jalr x0, t0, 0\n" + ".balign 4\n" + "1:\n" + // 0x00806713 is a hand-picked overlapped instruction word: + // - from the aligned entry at 1f it decodes as: ori x14, x0, 8 + // - from the misaligned entry at 1f + 1 it decodes as: + // jalr x0, -1744(x1) + // + // That lets both fixed and unfixed builds decode valid instructions while + // still producing different results. + ".4byte 0x00806713\n" + // This is the next aligned instruction after the pass-path overlap word. + ".4byte 0x00000093\n" + "j 4f\n" + "3:\n" + // Fail signature used when JALR does not clear bit 0. + "li %[value], 0x2468ace0\n" + "j 5f\n" + "4:\n" + // Pass signature used when JALR correctly clears bit 0. + "li %[value], 0x13579bdf\n" + "5:\n" + : [value] "=r" (value) + : + : "x1", "t0", "x14"); + + uint32_t* dst_ptr = (uint32_t*)arg->dst_addr; + dst_ptr[vx_core_id()] = value; + return 0; +} diff --git a/tests/regression/jalr/main.cpp b/tests/regression/jalr/main.cpp new file mode 100644 index 0000000000..c5cfa71f2a --- /dev/null +++ b/tests/regression/jalr/main.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#define RT_CHECK(_expr) \ + do { \ + int _ret = _expr; \ + if (0 == _ret) \ + break; \ + printf("Error: '%s' returned %d!\n", #_expr, (int)_ret); \ + cleanup(); \ + exit(-1); \ + } while (false) + +static constexpr uint32_t kPassValue = 0x13579bdf; + +const char* kernel_file = "kernel.vxbin"; + +vx_device_h device = nullptr; +vx_buffer_h dst_buffer = nullptr; +vx_buffer_h krnl_buffer = nullptr; +vx_buffer_h args_buffer = nullptr; +kernel_arg_t kernel_arg = {}; + +void cleanup() { + if (device) { + vx_mem_free(dst_buffer); + vx_mem_free(krnl_buffer); + vx_mem_free(args_buffer); + vx_dev_close(device); + } +} + +int main() { + std::cout << "open device connection" << std::endl; + RT_CHECK(vx_dev_open(&device)); + + uint64_t num_cores = 0; + RT_CHECK(vx_dev_caps(device, VX_CAPS_NUM_CORES, &num_cores)); + + uint32_t buf_size = num_cores * sizeof(uint32_t); + std::vector h_dst(num_cores, 0); + + std::cout << "allocate device memory" << std::endl; + RT_CHECK(vx_mem_alloc(device, buf_size, VX_MEM_WRITE, &dst_buffer)); + RT_CHECK(vx_mem_address(dst_buffer, &kernel_arg.dst_addr)); + RT_CHECK(vx_copy_to_dev(dst_buffer, h_dst.data(), 0, buf_size)); + + std::cout << "upload kernel binary" << std::endl; + RT_CHECK(vx_upload_kernel_file(device, kernel_file, &krnl_buffer)); + + std::cout << "upload kernel argument" << std::endl; + RT_CHECK(vx_upload_bytes(device, &kernel_arg, sizeof(kernel_arg_t), &args_buffer)); + + std::cout << "start device" << std::endl; + RT_CHECK(vx_start(device, krnl_buffer, args_buffer)); + RT_CHECK(vx_ready_wait(device, VX_MAX_TIMEOUT)); + + std::cout << "download destination buffer" << std::endl; + RT_CHECK(vx_copy_from_dev(h_dst.data(), dst_buffer, 0, buf_size)); + + int errors = 0; + for (uint64_t i = 0; i < num_cores; ++i) { + if (h_dst[i] != kPassValue) { + std::cout << "error at core " << std::dec << i + << ": actual=0x" << std::hex << h_dst[i] + << ", expected=0x" << kPassValue << std::endl; + ++errors; + } + } + + std::cout << "cleanup" << std::endl; + cleanup(); + + if (errors != 0) { + std::cout << "FAILED!" << std::endl; + return errors; + } + + std::cout << "PASSED!" << std::endl; + return 0; +} diff --git a/tests/regression/jalr/start.S b/tests/regression/jalr/start.S new file mode 100644 index 0000000000..87fd88c703 --- /dev/null +++ b/tests/regression/jalr/start.S @@ -0,0 +1,24 @@ +// Copyright © 2019-2023 +// +// 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. + +#include +#include + +.section .init, "ax" +.global _start +.type _start, @function +_start: + call main + call vx_perf_dump + .insn r 0x0b, 0, 0, x0, x0, x0 +.size _start, .-_start From ca26b3147d6afa4dd14fc5490021a481f05e8bdf Mon Sep 17 00:00:00 2001 From: cassuto Date: Thu, 23 Apr 2026 22:34:15 -0400 Subject: [PATCH 2/3] Fix simx jalr target alignment --- sim/simx/execute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sim/simx/execute.cpp b/sim/simx/execute.cpp index ba36bca151..c24e8d2c85 100644 --- a/sim/simx/execute.cpp +++ b/sim/simx/execute.cpp @@ -466,7 +466,7 @@ instr_trace_t* Emulator::execute(const Instr &instr, uint32_t wid) { continue; rd_data[t].i = next_pc; } - next_pc = rs1_data[thread_last].i + offset; + next_pc = (rs1_data[thread_last].u + offset) & ~Word(1); trace->fetch_stall = true; rd_write = true; } break; @@ -1562,4 +1562,4 @@ instr_trace_t* Emulator::execute(const Instr &instr, uint32_t wid) { } return trace; -} \ No newline at end of file +} From 91e315c4a4efeb80ea768719cebb6d0b5598c8e3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Jun 2026 07:47:47 -0400 Subject: [PATCH 3/3] tests: move JALR regression into conform --- tests/kernel/conform/main.cpp | 4 +- tests/kernel/conform/tests.cpp | 59 +++++++++++++++++++++- tests/kernel/conform/tests.h | 2 + tests/regression/Makefile | 4 -- tests/regression/jalr/Makefile | 21 -------- tests/regression/jalr/common.h | 8 --- tests/regression/jalr/kernel.cpp | 52 ------------------- tests/regression/jalr/main.cpp | 87 -------------------------------- tests/regression/jalr/start.S | 24 --------- 9 files changed, 63 insertions(+), 198 deletions(-) delete mode 100644 tests/regression/jalr/Makefile delete mode 100644 tests/regression/jalr/common.h delete mode 100644 tests/regression/jalr/kernel.cpp delete mode 100644 tests/regression/jalr/main.cpp delete mode 100644 tests/regression/jalr/start.S diff --git a/tests/kernel/conform/main.cpp b/tests/kernel/conform/main.cpp index 0efa00cab5..908754776c 100644 --- a/tests/kernel/conform/main.cpp +++ b/tests/kernel/conform/main.cpp @@ -28,6 +28,8 @@ int main() { errors += test_shfl(); + errors += test_jalr(); + if (0 == errors) { PRINTF("Passed!\n"); } else { @@ -35,4 +37,4 @@ int main() { } return errors; -} \ No newline at end of file +} diff --git a/tests/kernel/conform/tests.cpp b/tests/kernel/conform/tests.cpp index c1b19575de..9af5b87435 100644 --- a/tests/kernel/conform/tests.cpp +++ b/tests/kernel/conform/tests.cpp @@ -401,4 +401,61 @@ int test_shfl() { do_shfl(); vx_tmc_one(); // back to thread0 return check_error(shfl_buffer, 0, num_threads); -} \ No newline at end of file +} + +/////////////////////////////////////////////////////////////////////////////// + +#define JALR_PASS_VALUE 0x13579bdf + +uint32_t __attribute__((noinline)) do_jalr_misaligned() { + uint32_t value = 0; + asm volatile( + // x1 is prepared so that the misaligned decode path can immediately + // execute a valid JALR to label 3 and produce a deterministic failure + // value instead of aborting in the decoder. + "la x1, 3f\n" + // 1744 is chosen to match the immediate embedded in the overlapped + // misaligned instruction below: jalr x0, -1744(x1). + "addi x1, x1, 1744\n" + "la t0, 1f\n" + // Add 1 on purpose. A correct JALR implementation must clear bit 0 and + // land at 1f. An incorrect implementation enters at 1f + 1 instead. + "addi t0, t0, 1\n" + "jalr x0, t0, 0\n" + ".balign 4\n" + "1:\n" + // 0x00806713 is a hand-picked overlapped instruction word: + // - from the aligned entry at 1f it decodes as: ori x14, x0, 8 + // - from the misaligned entry at 1f + 1 it decodes as: + // jalr x0, -1744(x1) + // + // That lets both fixed and unfixed builds decode valid instructions + // while still producing different results. + ".4byte 0x00806713\n" + // This is the next aligned instruction after the pass-path overlap word. + ".4byte 0x00000093\n" + "j 4f\n" + "3:\n" + // Fail signature used when JALR does not clear bit 0. + "li %[value], 0x2468ace0\n" + "j 5f\n" + "4:\n" + // Pass signature used when JALR correctly clears bit 0. + "li %[value], 0x13579bdf\n" + "5:\n" + : [value] "=r" (value) + : + : "x1", "t0", "x14"); + return value; +} + +int test_jalr() { + PRINTF("JALR Test\n"); + vx_tmc_one(); + uint32_t value = do_jalr_misaligned(); + if (value != JALR_PASS_VALUE) { + PRINTF("*** error: jalr value=0x%x, expected=0x%x\n", value, JALR_PASS_VALUE); + return 1; + } + return 0; +} diff --git a/tests/kernel/conform/tests.h b/tests/kernel/conform/tests.h index 52e7df68b3..06576930b5 100644 --- a/tests/kernel/conform/tests.h +++ b/tests/kernel/conform/tests.h @@ -27,4 +27,6 @@ int test_vote(); int test_shfl(); +int test_jalr(); + #endif diff --git a/tests/regression/Makefile b/tests/regression/Makefile index 54fdad66df..d08a48f3b6 100644 --- a/tests/regression/Makefile +++ b/tests/regression/Makefile @@ -5,7 +5,6 @@ NP ?= 4 all: $(MAKE) -C basic - $(MAKE) -C jalr $(MAKE) -C demo $(MAKE) -C dogfood $(MAKE) -C dropout @@ -37,7 +36,6 @@ all-mpi: run-simx: $(MAKE) -C basic run-simx - $(MAKE) -C jalr run-simx $(MAKE) -C demo run-simx $(MAKE) -C dogfood run-simx $(MAKE) -C dropout run-simx @@ -69,7 +67,6 @@ run-simx-mpi: run-rtlsim: $(MAKE) -C basic run-rtlsim - $(MAKE) -C jalr run-rtlsim $(MAKE) -C demo run-rtlsim $(MAKE) -C dogfood run-rtlsim $(MAKE) -C dropout run-rtlsim @@ -91,7 +88,6 @@ run-rtlsim: clean: $(MAKE) -C basic clean - $(MAKE) -C jalr clean $(MAKE) -C demo clean $(MAKE) -C dogfood clean $(MAKE) -C dropout clean diff --git a/tests/regression/jalr/Makefile b/tests/regression/jalr/Makefile deleted file mode 100644 index 70710e2fe5..0000000000 --- a/tests/regression/jalr/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -ROOT_DIR := $(realpath ../../..) -include $(ROOT_DIR)/config.mk - -PROJECT := jalr - -SRC_DIR := $(VORTEX_HOME)/tests/regression/$(PROJECT) - -SRCS := $(SRC_DIR)/main.cpp - -VX_SRCS := $(SRC_DIR)/kernel.cpp $(VORTEX_HOME)/kernel/src/vx_perf.c $(SRC_DIR)/start.S - -OPTS ?= - -include ../common.mk - -VX_LDFLAGS = -Wl,-Bstatic,--gc-sections,-T,$(VORTEX_HOME)/kernel/scripts/link$(XLEN).ld,--defsym=STARTUP_ADDR=$(STARTUP_ADDR) - -VX_CC = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-gcc -VX_CXX = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-g++ -VX_DP = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-objdump -VX_CP = $(RISCV_TOOLCHAIN_PATH)/bin/$(RISCV_PREFIX)-objcopy diff --git a/tests/regression/jalr/common.h b/tests/regression/jalr/common.h deleted file mode 100644 index 91c2ca32bf..0000000000 --- a/tests/regression/jalr/common.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -typedef struct { - uint64_t dst_addr; -} kernel_arg_t; - -#endif diff --git a/tests/regression/jalr/kernel.cpp b/tests/regression/jalr/kernel.cpp deleted file mode 100644 index 7455ab9b63..0000000000 --- a/tests/regression/jalr/kernel.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "common.h" - -int main() { - kernel_arg_t* __UNIFORM__ arg = (kernel_arg_t*)csr_read(VX_CSR_MSCRATCH); - - if (vx_warp_id() != 0 || vx_thread_id() != 0) - return 0; - - uint32_t value = 0; - asm volatile( - // x1 is prepared so that the misaligned decode path can immediately - // execute a valid JALR to label 3 and produce a deterministic failure - // value instead of aborting in the decoder. - "la x1, 3f\n" - // 1744 is chosen to match the immediate embedded in the overlapped - // misaligned instruction below: jalr x0, -1744(x1). - "addi x1, x1, 1744\n" - "la t0, 1f\n" - // Add 1 on purpose. A correct JALR implementation must clear bit 0 and - // land at 1f. An incorrect implementation enters at 1f + 1 instead. - "addi t0, t0, 1\n" - "jalr x0, t0, 0\n" - ".balign 4\n" - "1:\n" - // 0x00806713 is a hand-picked overlapped instruction word: - // - from the aligned entry at 1f it decodes as: ori x14, x0, 8 - // - from the misaligned entry at 1f + 1 it decodes as: - // jalr x0, -1744(x1) - // - // That lets both fixed and unfixed builds decode valid instructions while - // still producing different results. - ".4byte 0x00806713\n" - // This is the next aligned instruction after the pass-path overlap word. - ".4byte 0x00000093\n" - "j 4f\n" - "3:\n" - // Fail signature used when JALR does not clear bit 0. - "li %[value], 0x2468ace0\n" - "j 5f\n" - "4:\n" - // Pass signature used when JALR correctly clears bit 0. - "li %[value], 0x13579bdf\n" - "5:\n" - : [value] "=r" (value) - : - : "x1", "t0", "x14"); - - uint32_t* dst_ptr = (uint32_t*)arg->dst_addr; - dst_ptr[vx_core_id()] = value; - return 0; -} diff --git a/tests/regression/jalr/main.cpp b/tests/regression/jalr/main.cpp deleted file mode 100644 index c5cfa71f2a..0000000000 --- a/tests/regression/jalr/main.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - -#define RT_CHECK(_expr) \ - do { \ - int _ret = _expr; \ - if (0 == _ret) \ - break; \ - printf("Error: '%s' returned %d!\n", #_expr, (int)_ret); \ - cleanup(); \ - exit(-1); \ - } while (false) - -static constexpr uint32_t kPassValue = 0x13579bdf; - -const char* kernel_file = "kernel.vxbin"; - -vx_device_h device = nullptr; -vx_buffer_h dst_buffer = nullptr; -vx_buffer_h krnl_buffer = nullptr; -vx_buffer_h args_buffer = nullptr; -kernel_arg_t kernel_arg = {}; - -void cleanup() { - if (device) { - vx_mem_free(dst_buffer); - vx_mem_free(krnl_buffer); - vx_mem_free(args_buffer); - vx_dev_close(device); - } -} - -int main() { - std::cout << "open device connection" << std::endl; - RT_CHECK(vx_dev_open(&device)); - - uint64_t num_cores = 0; - RT_CHECK(vx_dev_caps(device, VX_CAPS_NUM_CORES, &num_cores)); - - uint32_t buf_size = num_cores * sizeof(uint32_t); - std::vector h_dst(num_cores, 0); - - std::cout << "allocate device memory" << std::endl; - RT_CHECK(vx_mem_alloc(device, buf_size, VX_MEM_WRITE, &dst_buffer)); - RT_CHECK(vx_mem_address(dst_buffer, &kernel_arg.dst_addr)); - RT_CHECK(vx_copy_to_dev(dst_buffer, h_dst.data(), 0, buf_size)); - - std::cout << "upload kernel binary" << std::endl; - RT_CHECK(vx_upload_kernel_file(device, kernel_file, &krnl_buffer)); - - std::cout << "upload kernel argument" << std::endl; - RT_CHECK(vx_upload_bytes(device, &kernel_arg, sizeof(kernel_arg_t), &args_buffer)); - - std::cout << "start device" << std::endl; - RT_CHECK(vx_start(device, krnl_buffer, args_buffer)); - RT_CHECK(vx_ready_wait(device, VX_MAX_TIMEOUT)); - - std::cout << "download destination buffer" << std::endl; - RT_CHECK(vx_copy_from_dev(h_dst.data(), dst_buffer, 0, buf_size)); - - int errors = 0; - for (uint64_t i = 0; i < num_cores; ++i) { - if (h_dst[i] != kPassValue) { - std::cout << "error at core " << std::dec << i - << ": actual=0x" << std::hex << h_dst[i] - << ", expected=0x" << kPassValue << std::endl; - ++errors; - } - } - - std::cout << "cleanup" << std::endl; - cleanup(); - - if (errors != 0) { - std::cout << "FAILED!" << std::endl; - return errors; - } - - std::cout << "PASSED!" << std::endl; - return 0; -} diff --git a/tests/regression/jalr/start.S b/tests/regression/jalr/start.S deleted file mode 100644 index 87fd88c703..0000000000 --- a/tests/regression/jalr/start.S +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2019-2023 -// -// 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. - -#include -#include - -.section .init, "ax" -.global _start -.type _start, @function -_start: - call main - call vx_perf_dump - .insn r 0x0b, 0, 0, x0, x0, x0 -.size _start, .-_start