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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sw-sysemu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ set(CORE_SYSEMU_SOURCES
insns/zifencei.cpp
memory/main_memory.cpp
pma.cpp
boot-protocol.cpp
mmu.cpp
msgport.cpp
processor.cpp
Expand Down
10 changes: 10 additions & 0 deletions sw-sysemu/boot-protocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------*/

#include "emu_defines.h"

#if EMU_ERBIUM
#include "boot-protocol_er.cpp"
#endif
44 changes: 44 additions & 0 deletions sw-sysemu/boot-protocol_er.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------*/

#include <cinttypes>

#include "system.h"
#include "processor.h"
#include "emu_gio.h"

namespace bemu {

void System::apply_boot_protocol(uint64_t payload_pc, uint64_t payload_sp)
{
constexpr uint64_t MRAM_BASE = 0x40000000ULL;
constexpr uint64_t BOOT_PROT_SIZE = 0x100;
constexpr uint64_t PC_OFFSET = 0x28;
constexpr uint64_t SP_OFFSET = 0x20;

// Write PC and SP to MRAM boot vector addresses
memory.init(noagent, MRAM_BASE + PC_OFFSET, sizeof(payload_pc), &payload_pc);
memory.init(noagent, MRAM_BASE + SP_OFFSET, sizeof(payload_sp), &payload_sp);

uint64_t pc = payload_pc;
uint64_t sp = payload_sp;

// Sanity checks
if (pc >= MRAM_BASE && pc < MRAM_BASE + BOOT_PROT_SIZE)
LOG_AGENT(WARN, noagent, "Payload PC (0x%" PRIx64 ") points into boot protocol region", pc);
if (sp >= MRAM_BASE && sp < MRAM_BASE + BOOT_PROT_SIZE)
LOG_AGENT(WARN, noagent, "Payload SP (0x%" PRIx64 ") points into boot protocol region", sp);

// Set enabled harts
for (unsigned h = 0; h < EMU_NUM_THREADS; ++h) {
if (cpu[h].is_nonexistent() || cpu[h].is_unavailable())
continue;
cpu[h].pc = pc;
cpu[h].npc = pc;
cpu[h].xregs[2] = sp;
}
}

} // namespace bemu
7 changes: 5 additions & 2 deletions sw-sysemu/examples/common/erbium.ld
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ BOOTROM_BASE = 0x0002008000;
BOOTROM_SIZE = 8K;
SRAM_BASE = 0x000200C000;
SRAM_SIZE = 4K;
MRAM_BASE = 0x0040000000;
MRAM_SIZE = 16M;
MSYS_BASE = 0x0040000000;
MSYS_SIZE = 256;
MRAM_BASE = 0x0040000100;
MRAM_SIZE = 0xFFFF00;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 1K;
MAX_THREADS = 16;

MEMORY {
BOOTROM (rx) : org = BOOTROM_BASE, len = BOOTROM_SIZE
SRAM (rwx) : org = SRAM_BASE, len = SRAM_SIZE
MSYS (r) : org = MSYS_BASE, len = MSYS_SIZE
MRAM (rwx) : org = MRAM_BASE, len = MRAM_SIZE
Comment thread
AFOliveira marked this conversation as resolved.
}

Expand Down
8 changes: 8 additions & 0 deletions sw-sysemu/pma_er.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "esrs.h"
#include "system.h"
#include "utility.h"
#include "emu_gio.h"

namespace bemu {

Expand Down Expand Up @@ -174,6 +175,13 @@ uint64_t pma_check_data_access(const Hart& cpu, uint64_t vaddr,
}

if (paddr_is_mram(addr)) {
// Boot protocol: warn if software writes to the reserved region.
if (addr < (MRAM_BASE + 0x100)
&& data_access_is_write(macc)
&& macc != Mem_Access_CacheOp) {
WARN_HART(memory, cpu, "Store to boot protocol region (0x%" PRIx64 ")", addr);
}

uint16_t mprot = cpu.chip->neigh_esrs[neigh_index(cpu)].mprot;

Privilege mode = effective_execution_mode(cpu, macc);
Expand Down
25 changes: 25 additions & 0 deletions sw-sysemu/sys_emu/sys_emu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ sys_emu::sys_emu(const sys_emu_cmd_options &cmd_options, api_communicate *api_co
single_step.reset();

if (cmd_options.elf_files.empty() && cmd_options.file_load_files.empty() &&
#if EMU_ERBIUM
cmd_options.boot_elf.empty() &&
#endif
cmd_options.mem_desc_file.empty() && cmd_options.api_comm_path.empty() && g_preload->empty()) {
LOG_AGENT(FTL, agent, "%s", "Need an ELF file, a file load, a mem_desc file or runtime API!");
}
Expand Down Expand Up @@ -286,6 +289,20 @@ sys_emu::sys_emu(const sys_emu_cmd_options &cmd_options, api_communicate *api_co
}
}

#if EMU_ERBIUM
// Load boot ELF (--boot_elf): loads ELF and captures entry point for boot protocol
uint64_t elf_entry = 0;
if (!cmd_options.boot_elf.empty()) {
LOG_AGENT(INFO, agent, "Loading boot ELF: \"%s\"", cmd_options.boot_elf.c_str());
try {
elf_entry = chip.load_elf(cmd_options.boot_elf.c_str());
}
catch (...) {
LOG_AGENT(FTL, agent, "Error loading boot ELF \"%s\"", cmd_options.boot_elf.c_str());
}
}
#endif

// Parses the ELF files and memory description
for (const auto &elf: cmd_options.elf_files) {
LOG_AGENT(INFO, agent, "Loading ELF: \"%s\"", elf.c_str());
Expand Down Expand Up @@ -493,6 +510,14 @@ sys_emu::sys_emu(const sys_emu_cmd_options &cmd_options, api_communicate *api_co
chip.end_warm_reset(shire);
}

#if EMU_ERBIUM
// Apply boot protocol if enabled (--boot_elf, --payload_pc, or --payload_sp)
if (!cmd_options.boot_elf.empty() || cmd_options.payload_pc || cmd_options.payload_sp) {
uint64_t pc = cmd_options.payload_pc ? cmd_options.payload_pc : elf_entry;
chip.apply_boot_protocol(pc, cmd_options.payload_sp);
}
#endif

// Initialize xregs passed to command line
for (auto &info: cmd_options.set_xreg) {
chip.cpu[info.thread].xregs[info.xreg] = info.value;
Expand Down
6 changes: 6 additions & 0 deletions sw-sysemu/sys_emu/sys_emu.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ struct sys_emu_cmd_options {

std::vector<set_xreg_info> set_xreg;

#if EMU_ERBIUM
std::string boot_elf;
uint64_t payload_pc = 0;
uint64_t payload_sp = 0;
#endif

bool coherency_check = false;
uint64_t max_cycles = 10000000;
bool mins_dis = false;
Expand Down
24 changes: 24 additions & 0 deletions sw-sysemu/sys_emu/sys_emu_parse_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ static const char * help_msg =
#if EMU_HAS_SVCPROC
" -sp_reset_pc <addr> Sets Service Processor boot program counter (default: 0x40000000)\n"
#endif // EMU_HAS_SVCPROC
#if EMU_ERBIUM
" -boot_elf <path> Load ELF and use its entry point as payload PC for the boot protocol\n"
" -payload_pc <addr> Override payload PC written to MRAM boot vector (hex)\n"
" -payload_sp <addr> Override payload SP written to MRAM boot vector (hex)\n"
#endif // EMU_ERBIUM
" -set_xreg <t>,<r>,<val> Sets the xregister (integer) <r> of thread <t> to value <val>. <t> can be 'sp' for the Service Processor\n"
#endif // SDK_RELEASE
" -max_cycles <cycles> Stops execution after provided number of cycles (default: 10M)\n"
Expand Down Expand Up @@ -185,6 +190,11 @@ sys_emu::parse_command_line_arguments(int argc, char* argv[])
{"reset_pc", required_argument, nullptr, 0},
#if EMU_HAS_SVCPROC
{"sp_reset_pc", required_argument, nullptr, 0},
#endif
#if EMU_ERBIUM
{"boot_elf", required_argument, nullptr, 0},
{"payload_pc", required_argument, nullptr, 0},
{"payload_sp", required_argument, nullptr, 0},
#endif
{"set_xreg", required_argument, nullptr, 0},
#endif
Expand Down Expand Up @@ -390,6 +400,20 @@ sys_emu::parse_command_line_arguments(int argc, char* argv[])
{
sscanf(optarg, "%" PRIx64, &cmd_options.sp_reset_pc);
}
#endif
#if EMU_ERBIUM
else if (!strcmp(name, "boot_elf"))
{
cmd_options.boot_elf = std::string(optarg);
}
else if (!strcmp(name, "payload_pc"))
{
sscanf(optarg, "%" PRIx64, &cmd_options.payload_pc);
}
else if (!strcmp(name, "payload_sp"))
{
sscanf(optarg, "%" PRIx64, &cmd_options.payload_sp);
}
#endif
else if (!strcmp(name, "set_xreg"))
{
Expand Down
7 changes: 4 additions & 3 deletions sw-sysemu/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ void System::config_simulated_harts(unsigned shire, uint32_t minionmask,
}


void System::load_elf(std::istream& stream)
uint64_t System::load_elf(std::istream& stream)
{
ELFIO::elfio elf;
elf.load(stream);
Expand Down Expand Up @@ -606,16 +606,17 @@ void System::load_elf(std::istream& stream)
memory.init(noagent, lma, sec->get_size(), sec->get_data());
}
}
return elf.get_entry();
}


void System::load_elf(const char* filename)
uint64_t System::load_elf(const char* filename)
{
std::ifstream file;

file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file.open(filename, std::ios::in | std::ios::binary);
load_elf(file);
return load_elf(file);
}


Expand Down
9 changes: 7 additions & 2 deletions sw-sysemu/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,13 @@ class System {
void init(Stepping);

// Preload memory
void load_elf(std::istream&);
void load_elf(const char* filename);
uint64_t load_elf(std::istream&);
uint64_t load_elf(const char* filename);

#if EMU_ERBIUM
// Boot protocol
void apply_boot_protocol(uint64_t payload_pc, uint64_t payload_sp);
#endif
void load_raw(const char* filename, unsigned long long addr);

// Reset state
Expand Down
27 changes: 27 additions & 0 deletions sw-sysemu/tests/erbium/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,33 @@ run/%: $(BUILD_DIR)/%.elf
echo " UNKNOWN"; exit 1; \
fi

# Boot protocol tests: re-run all tests with --boot_elf
# The ELF entry point is used as the payload PC automatically.
run-boot-protocol: $(tests_elf)
@npass=0; nfail=0; \
for test in $(tests); do \
echo "+ running $$test (boot protocol)"; \
cat "$(INPUT_DIR)/$$test.in" 2>/dev/null | \
$(EMU) -l $(EMU_ARGS) --boot_elf $(BUILD_DIR)/$$test.elf > $(BUILD_DIR)/$$test.bp.out 2>&1; \
if grep -q "Signal end test with FAIL" $(BUILD_DIR)/$$test.bp.out; then \
echo " FAIL"; \
nfail=$$((nfail + 1)); \
elif grep -q "Signal end test with PASS" $(BUILD_DIR)/$$test.bp.out; then \
echo " PASS"; \
npass=$$((npass + 1)); \
else \
echo " UNKNOWN (no pass/fail signal)"; \
nfail=$$((nfail + 1)); \
fi; \
done; \
echo ""; \
if [ $$nfail -eq 0 ]; then \
echo "SUCCESS: $(num_tests) boot protocol | $$npass passed | $$nfail failed"; \
else \
echo "FAILURE: $(num_tests) boot protocol | $$npass passed | $$nfail failed"; \
exit 1; \
fi

# GDB tests (explicit list)
gdb_tests := thread_disable_consistency
num_gdb_tests := $(words $(gdb_tests))
Expand Down
7 changes: 5 additions & 2 deletions sw-sysemu/tests/erbium/common/erbium.ld
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ BOOTROM_BASE = 0x0002008000;
BOOTROM_SIZE = 8K;
SRAM_BASE = 0x000200C000;
SRAM_SIZE = 4K;
MRAM_BASE = 0x0040000000;
MRAM_SIZE = 16M;
MSYS_BASE = 0x0040000000;
MSYS_SIZE = 256;
MRAM_BASE = 0x0040000100;
MRAM_SIZE = 0xFFFF00;

/* Stack configuration */
MAX_THREADS = 16;
Expand All @@ -22,6 +24,7 @@ STACK_SIZE_LOG2 = 6;
MEMORY {
BOOTROM (rx) : org = BOOTROM_BASE, len = BOOTROM_SIZE
SRAM (rwx) : org = SRAM_BASE, len = SRAM_SIZE
MSYS (r) : org = MSYS_BASE, len = MSYS_SIZE
MRAM (rwx) : org = MRAM_BASE, len = MRAM_SIZE
}

Expand Down
2 changes: 1 addition & 1 deletion sw-sysemu/tests/erbium/src/all_harts.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

#define ESR_THREAD0_DISABLE 0x80F40240ULL
#define ESR_THREAD1_DISABLE 0x80F40010ULL
#define MRAM_BASE 0x40000000ULL
#define MRAM_BASE 0x40000100ULL /* past 256-byte boot protocol region */
#define NUM_HARTS 16

#define CSR_FLB 0x820
Expand Down
35 changes: 35 additions & 0 deletions sw-sysemu/tests/erbium/src/boot_protocol_sp.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------*/

/*
* Test: --payload_sp sets the initial stack pointer
*
* This is a standalone assembly test (no boot.S) that checks SP at the
* very first instruction. Must be run with:
* --boot_elf <this_elf> --payload_sp 200c100
*
* The boot protocol sets SP before the hart starts executing, so we
* check it immediately at entry before anything can overwrite it.
*/

.section .text.init, "ax", @progbits
.globl _boot
_boot:
li t0, 0x200c100
bne sp, t0, fail

/* PASS */
li a7, 0x1feed000
csrw 0x8d0, a7 /* validation0 */
j done

fail:
/* FAIL */
li a7, 0x50bad000
csrw 0x8d0, a7 /* validation0 */

done:
wfi
j done
31 changes: 31 additions & 0 deletions sw-sysemu/tests/erbium/src/pma_mram_boot_region.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------*/

/*
* Test: Store to MRAM boot protocol region succeeds with warning
*
* The first 256 bytes of MRAM (0x40000000-0x400000FF) are reserved for
* the boot protocol. Stores are allowed (no fault) but the emulator
* logs a warning. Verify the store actually lands in memory.
*/

#include "test.h"
#include <stdint.h>

#define MRAM_BOOT_REGION 0x40000020ull /* Payload SP offset */
#define TEST_PATTERN 0xDEADBEEFCAFEFEEDull

int main() {
volatile uint64_t *boot_region = (volatile uint64_t *)MRAM_BOOT_REGION;

*boot_region = TEST_PATTERN;

if (*boot_region == TEST_PATTERN) {
TEST_PASS;
}

TEST_FAIL;
return 0;
}
2 changes: 1 addition & 1 deletion sw-sysemu/tests/erbium/src/pma_mram_rw.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include "test.h"
#include <stdint.h>

#define MRAM_BASE 0x40000000ull
#define MRAM_BASE 0x40000100ull /* past 256-byte boot protocol region */
#define TEST_PATTERN 0xDEADBEEFCAFEFEEDull

int main() {
Expand Down
Loading
Loading