From 3379b39105134eb2e949dc7765dbeb7b6978079e Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 28 Oct 2025 14:53:43 +0100 Subject: [PATCH] fixed linux, enhanced qemu --- CMakeLists.txt | 10 +++++- DEBUG_ON_QEMU.md | 42 +++++++++++++++++++++++++ bsp_linux/main.c | 6 ++-- bsp_z7/hardware/interfaces.c | 4 +++ bsp_z7/hardware/uart.c | 9 +++--- bsp_z7/main.c | 60 +++++++++++++++++++++--------------- mission/mission.c | 15 ++------- mission_rust/src/lib.rs | 10 +++--- 8 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 DEBUG_ON_QEMU.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 92375df..79fe3cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ set(ROMEO_WARNING_FLAGS -Wall -Wextra -Wpedantic) - #-Werror) + #-Werror) #TODO fix Xilinx stuff # CMake options which are only available when crosscompiling if (${CMAKE_CROSSCOMPILING}) @@ -25,9 +25,17 @@ if (${CMAKE_CROSSCOMPILING}) if(${ZYNQ_UART} STREQUAL UART0) add_compile_definitions(ZYNQ_USE_UART0) endif() + + option(ZYNQ_SEMIHOSTING "enable semihosting for emulation" OFF) + + if(${ZYNQ_SEMIHOSTING}) + add_compile_definitions(ZYNQ_SEMIHOSTING) + endif() else() unset(ZYNQ_UART) unset(ZYNQ_UART CACHE) + unset(ZYNQ_SEMIHOSTING) + unset(ZYNQ_SEMIHOSTING CACHE) endif() # Add main executable diff --git a/DEBUG_ON_QEMU.md b/DEBUG_ON_QEMU.md new file mode 100644 index 0000000..12cc1d6 --- /dev/null +++ b/DEBUG_ON_QEMU.md @@ -0,0 +1,42 @@ +# Qemu + +qemu is an emulator which can be used to run code compiled for a different architecture (say ARMv7 as on zynq) on the host architecture (the PC you are using). + +This is mainly useful for testing platform dependent code, in our case the operating system abstraction. This code can not be tested on linux, as the FreeRTOS port used is a different one. + +By selecting the `ZYNQ_SEMIHOSTING` in cmake, the semihosting interface is enabled. This allows the software to signal an exit condition to qemu, which in turn makes qemu exit with the corresponding exit code. This functionality is used for unit testing, where the exit condition is needed to signal success or failure of the unit tests. + +# Run on qemu + +The full call to run the romeo-obsw on qemu is: + +`qemu-system-arm -semihosting -nographic -monitor none -serial null -serial stdio -machine xilinx-zynq-a9 -m 500M -kernel romeo-obsw` + +Where the arguments are the following: +* `qemu-system-arm`: the qemu emulating arm processors +* `-semihosting`: enables the emulated software to communicate with the host. This is only used to allow the software to exit including returning an exit code +* `-nographic`: disable qemus graphical interface +* `-monitor none`: disable qemu monitoring (not used) +* `-serial null`: add first UART, do not connect it. +* `-serial stdio`: add second UART, connect it to the console of the host +* `-machine xilinx-zynq-a9`: select zynq as emulation target +* `-m 500M`: set RAM size +* `-device loader,addr=0x0000012c,data=0x00001234,data-len=4`: set data at the memory location `data`. This is used by the FSW to detect if it is runing on qemu or on a real zynq (where this memory location will not be 0x00001234) +* `-kernel romeo-obsw`: which program to run + +The first UART can be connected to a serial device on the host. Add `-chardev serial,id=serial0,path="/dev/ttyUSB0"` where `"/dev/ttyUSB0"` is the path to the serial device. Then, change the first invocation of `-serial` from `-serial null` to `-serial chardev:serial0` + + +# Debug on qemu + +Basically the same call as above, only `-kernel` is replaced by `-s -S`, which enables debugging and halts the CPU. + +`qemu-system-arm -semihosting -nographic -monitor none -serial null -serial stdio -machine xilinx-zynq-a9 -m 500M -device loader,addr=0x0000012c,data=0x00001234,data-len=4 -s -S` + +After qemu is started, connect to the gdb server: +```sh +arm-none-eabi-gdb romeo-obsw +>target remote :1234 +>load +>cont +``` \ No newline at end of file diff --git a/bsp_linux/main.c b/bsp_linux/main.c index bca8d6a..918acee 100644 --- a/bsp_linux/main.c +++ b/bsp_linux/main.c @@ -14,13 +14,15 @@ int ai_family = AF_UNSPEC; void mission(void); -int get_descriptor_rw() { return 1; } void done() { - printf("done.\n"); exit(0); } +void done_error() { + exit(1); +} + int test_socket(); // TODO link to GCC's personality or make the linux build not use it? diff --git a/bsp_z7/hardware/interfaces.c b/bsp_z7/hardware/interfaces.c index 76fa071..8e47112 100644 --- a/bsp_z7/hardware/interfaces.c +++ b/bsp_z7/hardware/interfaces.c @@ -30,6 +30,10 @@ int hw_device_open(const char *path, size_t path_len) { uart0_enable_receiver(); return UART_0; } + if (compare_string_chars("debug/uart🚀", path, path_len) == 1) { + uart0_enable_receiver(); + return UART_0; + } if (compare_string_chars("uart1", path, path_len) == 1) { uart1_enable_receiver(); return UART_1; diff --git a/bsp_z7/hardware/uart.c b/bsp_z7/hardware/uart.c index de9107c..da5b018 100644 --- a/bsp_z7/hardware/uart.c +++ b/bsp_z7/hardware/uart.c @@ -69,13 +69,12 @@ void uart1_handle_interrupt(void *) { // available into the stack uint8_t RecievedByte; BaseType_t xHigherPriorityTaskWoken; - + while (XUartPs_IsReceiveData(XPS_UART1_BASEADDR)) { RecievedByte = XUartPs_ReadReg(XPS_UART1_BASEADDR, XUARTPS_FIFO_OFFSET); xQueueSendToBackFromISR(uart1_receive_queue, &RecievedByte, &xHigherPriorityTaskWoken); } - /* Clear the interrupt status. */ XUartPs_WriteReg(XPS_UART1_BASEADDR, XUARTPS_ISR_OFFSET, IsrStatus); @@ -145,7 +144,8 @@ void uart1_enable_receiver() { } int uart0_read(char *ptr, int len) { - // TODO for blocking, if first call was successfull, further calls need to be delay=0 + // TODO for blocking, if first call was successfull, further calls need to be + // delay=0 int received = 0; while (len > 0) { BaseType_t result = xQueueReceive(uart0_receive_queue, ptr, 0); @@ -160,7 +160,8 @@ int uart0_read(char *ptr, int len) { } int uart1_read(char *ptr, int len) { - // TODO for blocking, if first call was successfull, further calls need to be delay=0 + // TODO for blocking, if first call was successfull, further calls need to be + // delay=0 int received = 0; while (len > 0) { BaseType_t result = xQueueReceive(uart1_receive_queue, ptr, 0); diff --git a/bsp_z7/main.c b/bsp_z7/main.c index ed9b8b6..3c906be 100644 --- a/bsp_z7/main.c +++ b/bsp_z7/main.c @@ -47,28 +47,24 @@ XScuGic xInterruptController; extern SemaphoreHandle_t malloc_mutex; -int get_descriptor_rw() { - return 1; -} - /*-----------------------------------------------------------*/ void mission(void); void initFreeRTOSHelper(); - int main(void) { - // Enable UARTs, so qemu knows we use them (should already be enabled by fsbl on actual hw) - XUartPs_WriteReg(XPS_UART0_BASEADDR, XUARTPS_CR_OFFSET, XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN); - XUartPs_WriteReg(XPS_UART1_BASEADDR, XUARTPS_CR_OFFSET, XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN); - - + // Enable UARTs, so qemu knows we use them (should already be enabled by fsbl + // on actual hw) + XUartPs_WriteReg(XPS_UART0_BASEADDR, XUARTPS_CR_OFFSET, + XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN); + XUartPs_WriteReg(XPS_UART1_BASEADDR, XUARTPS_CR_OFFSET, + XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN); /* Configure the hardware ready to run. */ prvSetupHardware(); - mission(); + mission(); } static void prvSetupHardware(void) { @@ -127,32 +123,45 @@ void vInitialiseTimerForRunTimeStats(void) { XScuWdt_Start(&xWatchDogInstance); } -// Marker for debugging sessions -// __attribute__((noinline)) void done() { asm(""); } - -// For qemu -// see https://github.com/ARM-software/abi-aa -> Miscellaneous material -> Semihosting for AArch32 and AArch64 +#ifndef ZYNQ_SEMIHOSTING void done() { + while (1) + ; +} + +void done_error() { + // makes no difference + done(); +} + +#else // enable semihosting interface for done() +void done() { + + // Call semihosting interface to signal exit + // see https://github.com/ARM-software/abi-aa -> Miscellaneous material -> + // Semihosting for AArch32 and AArch64 register int reg0 asm("r0"); register int reg1 asm("r1"); - reg0 = 0x18; // SYS_EXIT + reg0 = 0x18; // SYS_EXIT reg1 = 0x20026; // ADP_Stopped_ApplicationExit - asm("svc 0x123456"); + asm("svc 0x123456"); // syscall to semihosting interface } -// same as done, will make qemu return 1 void done_error() { + // same as done(), will make qemu return 1 register int reg0 asm("r0"); register int reg1 asm("r1"); - reg0 = 0x18; // SYS_EXIT + reg0 = 0x18; // SYS_EXIT reg1 = 0x20023; // ADP_Stopped_RunTimeErrorUnknown - asm("svc 0x123456"); + asm("svc 0x123456"); // syscall to semihosting interface } +#endif /* SEMIHOSTING */ + void vApplicationIdleHook(void) { volatile size_t xFreeHeapSpace, xMinimumEverFreeHeapSpace; @@ -198,7 +207,9 @@ void vApplicationTickHook(void) { vBasicStreamBufferSendFromISR(); #if (configUSE_QUEUE_SETS == 1) - { vQueueSetAccessQueueSetFromISR(); } + { + vQueueSetAccessQueueSetFromISR(); + } #endif /* Test flop alignment in interrupts - calling printf from an interrupt @@ -209,7 +220,9 @@ is BAD! */ UBaseType_t uxSavedInterruptStatus; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { sprintf(cBuf, "%1.3f", 1.234); } + { + sprintf(cBuf, "%1.3f", 1.234); + } portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); configASSERT(strcmp(cBuf, "1.234") == 0); @@ -218,4 +231,3 @@ is BAD! */ } #endif } - diff --git a/mission/mission.c b/mission/mission.c index 64f52b0..4a8284b 100644 --- a/mission/mission.c +++ b/mission/mission.c @@ -8,6 +8,7 @@ #include "task.h" #include + #include #include @@ -15,16 +16,8 @@ void rust_main(void); - - - - - - - void test_hardware() { - int fd0 = hw_device_open("uart0", 5); write(fd0, "UART0\n", 6); int fd1 = hw_device_open("uart1", 5); @@ -52,8 +45,7 @@ void test_hardware() { // to be implemented by bsp (do not return from it!) void done_error(); - -void init_task(void* _) { +void init_task(void *_) { (void)_; rust_main(); } @@ -61,7 +53,6 @@ void init_task(void* _) { #define STARTUP_MESSAGE1 "\nROMEO embedded obsw\nRelease: " #define STARTUP_MESSAGE2 "\nBuild time: " - void mission(void) { write(1, STARTUP_MESSAGE1, strlen(STARTUP_MESSAGE1)); @@ -70,10 +61,8 @@ void mission(void) { write(1, BUILD_TIME_STRING, strlen(BUILD_TIME_STRING)); write(1, "\n", 1); - test_hardware(); - freertos_init_and_start_scheduling(init_task); /* If all is well, the scheduler will now be running, and the following diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 21cc3e5..bfcc749 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -186,20 +186,20 @@ static THREAD_3: StaticReadOnceLock< )); extern "C" { - fn done(); + fn done() -> !; } fn init_task() -> ! { sifln!("Mission enter"); - unsafe{done()}; + // unsafe{done()}; - panic!("tests done"); + // panic!("Oh no!"); let commands = [dh::Command(0), dh::Command(1), dh::Command(2)]; let mut debugger = DeviceHandlerDebugger::new( EchoHandler { buffer: [0; 10] }, - HardwareInterface::new("uart0").unwrap(), + HardwareInterface::new("debug/uart🚀").unwrap(), &commands, &[], Duration::from_millis(500), @@ -208,7 +208,7 @@ fn init_task() -> ! { debugger.run().unwrap(); - + panic!("done"); let test1 = TEST1.take().unwrap(); let test2 = TEST2.take().unwrap();