diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/board.c b/ports/risc-v32/gnu/example_build/qemu_virt/board.c new file mode 100644 index 000000000..fc75de668 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/board.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include "hwtimer.h" +#include "uart.h" +#include +#include + +void *memset(const void *des, int c,size_t n) +{ + if((des == NULL) || n <=0) + return (void*)des; + char* t = (char*)des; + int i; + for(i=0;i + +static inline uint32_t riscv_get_core() +{ + uint32_t x; + asm volatile("csrr %0, mhartid" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_mstatus() +{ + uint32_t x; + asm volatile("csrr %0, mstatus" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mstatus(uint32_t x) +{ + asm volatile("csrw mstatus, %0" : : "r" (x)); +} + +static inline void riscv_writ_mepc(uint32_t x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sstatus() +{ + uint32_t x; + asm volatile("csrr %0, sstatus" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sstatus(uint32_t x) +{ + asm volatile("csrw sstatus, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sip() +{ + uint32_t x; + asm volatile("csrr %0, sip" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sip(uint32_t x) +{ + asm volatile("csrw sip, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sie() +{ + uint32_t x; + asm volatile("csrr %0, sie" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sie(uint32_t x) +{ + asm volatile("csrw sie, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_mie() +{ + uint32_t x; + asm volatile("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mie(uint32_t x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +static inline void riscv_writ_sepc(uint32_t x) +{ + asm volatile("csrw sepc, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sepc() +{ + uint32_t x; + asm volatile("csrr %0, sepc" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_medeleg() +{ + uint32_t x; + asm volatile("csrr %0, medeleg" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_medeleg(uint32_t x) +{ + asm volatile("csrw medeleg, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_mideleg() +{ + uint32_t x; + asm volatile("csrr %0, mideleg" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mideleg(uint32_t x) +{ + asm volatile("csrw mideleg, %0" : : "r" (x)); +} + +static inline void riscv_writ_stvec(uint32_t x) +{ + asm volatile("csrw stvec, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_stvec() +{ + uint32_t x; + asm volatile("csrr %0, stvec" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_stimecmp() +{ + uint32_t x; + asm volatile("csrr %0, 0x14d" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_stimecmp(uint32_t x) +{ + asm volatile("csrw 0x14d, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_menvcfg() +{ + uint32_t x; + asm volatile("csrr %0, 0x30a" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_menvcfg(uint32_t x) +{ + asm volatile("csrw 0x30a, %0" : : "r" (x)); +} + +static inline void riscv_writ_pmpcfg0(uint32_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r" (x)); +} + +static inline void riscv_writ_pmpaddr0(uint32_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r" (x)); +} + +static inline void riscv_writ_satp(uint32_t x) +{ + asm volatile("csrw satp, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_satp() +{ + uint32_t x; + asm volatile("csrr %0, satp" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_scause() +{ + uint32_t x; + asm volatile("csrr %0, scause" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_stval() +{ + uint32_t x; + asm volatile("csrr %0, stval" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mcounteren(uint32_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_mcounteren() +{ + uint32_t x; + asm volatile("csrr %0, mcounteren" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_time() +{ + uint32_t x; + asm volatile("csrr %0, time" : "=r" (x) ); + return x; +} + +static inline void riscv_sintr_on() +{ + uint32_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +static inline void riscv_sintr_off() +{ + uint32_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +static inline int riscv_sintr_get() +{ + uint32_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if(x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +static inline void riscv_mintr_on() +{ + uint32_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +static inline void riscv_mintr_off() +{ + uint32_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +static inline int riscv_mintr_get() +{ + uint32_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if(x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint32_t riscv_get_sp() +{ + uint32_t x; + asm volatile("mv %0, sp" : "=r" (x) ); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint32_t riscv_get_tp() +{ + uint32_t x; + asm volatile("mv %0, tp" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_tp(uint32_t x) +{ + asm volatile("mv tp, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_ra() +{ + uint32_t x; + asm volatile("mv %0, ra" : "=r" (x) ); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c new file mode 100644 index 000000000..f21dbb26b --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c @@ -0,0 +1,393 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" +#include "uart.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + +char *_to_str(ULONG val) +{ + static char buf[11]; /* 10 digits max + '\0' */ + char *p = buf + sizeof(buf) - 1; + + *p = '\0'; + do { + *--p = '0' + (val % 10); + val /= 10; + } while (val); + + return p; +} + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + CHAR *pointer = TX_NULL; + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", first_unused_memory, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + puts("[Thread] : thread_0_entry is here!"); + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + puts("[Thread] : thread_1_entry is here!"); + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) { + puts("[Thread 1] ERROR: Failed to send message!"); + break; + } + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + puts("[Thread] : thread_2_entry is here!"); + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)){ + puts("[Thread 2] ERROR: Failed to receive message ! Expected # "); + uart_puts(_to_str(thread_2_messages_received)); + puts(", but got # "); + uart_puts(_to_str(received_message)); + break; + } + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + puts("[Thread] : thread_3_and_4_entry is here!"); + + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + puts("[Thread] : thread_5_entry is here!"); + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + puts("[Thread] : thread_6_and_7_entry is here!"); + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/entry.s b/ports/risc-v32/gnu/example_build/qemu_virt/entry.s new file mode 100644 index 000000000..9b202ca16 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/entry.s @@ -0,0 +1,58 @@ + +.section .text +.align 4 +.global _start +.extern main +.extern _sysstack_start +.extern _bss_start +.extern _bss_end +_start: + csrr t0, mhartid + bne t0, zero, 1f + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + la t0, _sysstack_start + li t1, 0x1000 + add sp, t0, t1 + la t0, _bss_start + la t1, _bss_end +_bss_clean_start: + bgeu t0, t1, _bss_clean_end + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean_start +_bss_clean_end: + call main +1: + /* todo smp */ + wfi + j 1b diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c new file mode 100644 index 000000000..33c49b638 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "tx_port.h" +#include "csr.h" +#include "hwtimer.h" + +#define CLINT (0x02000000L) +#define CLINT_TIME (CLINT+0xBFF8) +#define CLINT_TIMECMP(hart_id) (CLINT+0x4000+8*(hart_id)) + + +int hwtimer_init(void) +{ + int hart = riscv_get_core(); + uint64_t time = *((uint64_t*)CLINT_TIME); + *((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} + +int hwtimer_handler(void) +{ + int hart = riscv_get_core(); + uint64_t time = *((uint64_t*)CLINT_TIME); + *((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} + diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h new file mode 100644 index 000000000..966b1abfb --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/hwtimer.h @@ -0,0 +1,23 @@ + +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_HWTIMER_H +#define RISCV_HWTIMER_H + +#include + +#define TICKNUM_PER_SECOND 10000000 +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10) + +int hwtimer_init(void); +int hwtimer_handler(void); + +#endif diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/link.lds b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds new file mode 100644 index 000000000..522f90d96 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds @@ -0,0 +1,49 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY( _start ) + +SECTIONS +{ + /* + * ensure that entry.S / _entry is at 0x80000000, + * where qemu's -kernel jumps. + */ + . = 0x80000000; + + .text : { + *(.text .text.*) + . = ALIGN(0x1000); + PROVIDE(etext = .); + } + + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ + . = ALIGN(16); + *(.rodata .rodata.*) + } + + .data : { + . = ALIGN(16); + *(.sdata .sdata.*) /* do not need to distinguish this from .data */ + . = ALIGN(16); + *(.data .data.*) + } + + .bss : { + . = ALIGN(16); + _bss_start = .; + *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ + . = ALIGN(16); + *(.bss .bss.*) + _bss_end = .; + } + + .stack : { + . = ALIGN(4096); + _sysstack_start = .; + . += 0x1000; + _sysstack_end = .; + } + + PROVIDE(_end = .); +} diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/plic.c b/ports/risc-v32/gnu/example_build/qemu_virt/plic.c new file mode 100644 index 000000000..01e5c71a4 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/plic.c @@ -0,0 +1,72 @@ +#include "plic.h" +#include +irq_callback callbacks[MAX_CALLBACK_NUM]; + +void plic_irq_enable(int irqno) +{ + int hart = riscv_get_core(); + *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); + return; +} + +void plic_irq_disable(int irqno) +{ + int hart = riscv_get_core(); + *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); + return; +} + +void plic_prio_set(int irqno, int prio) +{ + PLIC_SET_PRIO(irqno, prio); +} + +int plic_prio_get(int irqno) +{ + return PLIC_GET_PRIO(irqno); +} + +int plic_register_callback(int irqno, irq_callback callback) +{ + if(!(irqno >=0 && irqno < MAX_CALLBACK_NUM)) + return -1; + callbacks[irqno] = callback; + return 0; +} + +int plic_unregister_callback(int irqno) +{ + return plic_register_callback(irqno, NULL); +} + +int plic_init(void) +{ + for(int i=0;i + +#define PLIC 0x0c000000L +#define PLIC_PRIORITY (PLIC + 0x0) +#define PLIC_PENDING (PLIC + 0x1000) +#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) +#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) +#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) +#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) +#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) +#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) +#define PLIC_MCOMPLETE(hart) (PLIC + 0x200004 + (hart)*0x2000) +#define PLIC_SCOMPLETE(hart) (PLIC + 0x201004 + (hart)*0x2000) + + +#define PLIC_GET_PRIO(irqno) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4)) +#define PLIC_SET_PRIO(irqno, prio) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4) = (prio)) + +#define MAX_CALLBACK_NUM 128 +typedef int (*irq_callback)(int irqno); + +void plic_irq_enable(int irqno); +void plic_irq_disable(int irqno); +int plic_prio_get(int irqno); +void plic_prio_set(int irqno, int prio); +int plic_register_callback(int irqno, irq_callback callback); +int plic_unregister_callback(int irqno); +int plic_init(void); +int plic_claim(void); +void plic_complete(int irqno); + +int plic_irq_intr(void); + +#endif + diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c new file mode 100644 index 000000000..a2733e02a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c @@ -0,0 +1,67 @@ +#include "csr.h" +#include +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include +#include + +#define OS_IS_INTERUPT(mcause) (mcause & 0x80000000u) +#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) +#define OS_IS_TICK_INT(mcause) (mcause == 0x80000007u) +#define OS_IS_SOFT_INT(mcause) (mcause == 0x80000003u) +#define OS_IS_EXT_INT(mcause) (mcause == 0x8000000bu) +#define OS_IS_TRAP_USER(mcause) (mcause == 0x0000000bu) +extern void _tx_timer_interrupt(void); + +extern int uart_putc(int ch); + +static void print_hex(uintptr_t val) +{ + char digits[] = "0123456789ABCDEF"; + uart_putc('0'); + uart_putc('x'); + for(int i = (sizeof(uintptr_t)*2) - 1; i >= 0; i--) { + int d = (val >> (i*4)) & 0xF; + uart_putc(digits[d]); + } + uart_putc('\n'); +} + +void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) +{ + // uart_puts("DEBUG : threadx/ports/risc-v32/gnu/example_build/qemu_virt/trap.c, trap_handler\n"); + if(OS_IS_INTERUPT(mcause)) + { + if(OS_IS_TICK_INT(mcause)) + { + hwtimer_handler(); + _tx_timer_interrupt(); + } + else if(OS_IS_EXT_INT(mcause)) + { + int ret = plic_irq_intr(); + if(ret) + { + puts("[INTERRUPT]: handler irq error!"); + while(1) ; + } + } + else + { + puts("[INTERRUPT]: now can't deal with the interrupt!"); + while(1) ; + } + } + else + { + puts("[EXCEPTION] : Unkown Error!!"); + puts("mcause:"); + print_hex(mcause); + puts("mepc:"); + print_hex(mepc); + puts("mtval:"); + print_hex(mtval); + while(1) ; + } +} diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S b/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S new file mode 100644 index 000000000..62bb9abbe --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (c) 2025 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "csr.h" +#include "tx_port.h" + + .section .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* trap_entry RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for riscv processor trap handle */ +/* It will do the contex save and call c trap_handler and do contex */ +/* load */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* trap_handler */ +/* */ +/* CALLED BY */ +/* */ +/* hardware exception */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 12-29-2025 Akif Ejaz Adapted for RV32 from RV64 port */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + .global trap_entry + .extern trap_handler + .extern _tx_thread_context_restore + trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -65*REGBYTES // Allocate space for all registers - with floating point enabled +#else + addi sp, sp, -32*REGBYTES // Allocate space for all registers - without floating point enabled +#endif + + STORE x1, 28*REGBYTES(sp) // Store RA, 28*REGBYTES(because call will override ra [ra is a calle register in riscv]) + + call _tx_thread_context_save + + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -4 + sw ra, 0(sp) + call trap_handler + lw ra, 0(sp) + addi sp, sp, 4 + call _tx_thread_context_restore + // it will nerver return +_err: + wfi + j _err + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 12-29-2025 Akif Ejaz Adapted for RV32 from RV64 port */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern _end + .extern board_init +_tx_initialize_low_level: + +/* debug print + .section .rodata +debug_str_init: + .string "DEBUG : threadx/ports/risc-v32/gnu/example_build/qemu_virt/tx_initialize_low_level.S, _tx_initialize_low_level\n" +*/ + .section .text + + la t0, _tx_thread_system_stack_ptr + sw sp, 0(t0) // Save system stack pointer + + la t0, _end // Pickup first free address + la t1, _tx_initialize_unused_memory + sw t0, 0(t1) // Save unused memory address + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE ) + csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit + li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + csrrs zero, mie, t0 // set mie +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv + fscsr x0 +#endif + addi sp, sp, -4 + sw ra, 0(sp) + call board_init +/* debug print + la a0, debug_str_init + call uart_puts +*/ + lw ra, 0(sp) + addi sp, sp, 4 + la t0, trap_entry + csrw mtvec, t0 + ret diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.c b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c new file mode 100644 index 000000000..a175b7d25 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c @@ -0,0 +1,102 @@ +#include "uart.h" +#include "csr.h" +#include "plic.h" +#include + +// the UART control registers are memory-mapped +// at address UART0. this macro returns the +// address of one of the registers. +#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) + +// the UART control registers. +// some have different meanings for +// read vs write. +// see http://byterunner.com/16550.html +#define RHR 0 // receive holding register (for input bytes) +#define THR 0 // transmit holding register (for output bytes) +#define IER 1 // interrupt enable register +#define IER_RX_ENABLE (1<<0) +#define IER_TX_ENABLE (1<<1) +#define FCR 2 // FIFO control register +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs +#define ISR 2 // interrupt status register +#define LCR 3 // line control register +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate +#define LSR 5 // line status register +#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR +#define LSR_TX_IDLE (1<<5) // THR can accept another character to send + +#define ReadReg(reg) (*(Reg(reg))) +#define WriteReg(reg, v) (*(Reg(reg)) = (v)) + +int uart_init(void) +{ + // disable interrupts. + WriteReg(IER, 0x00); + + // special mode to set baud rate. + WriteReg(LCR, LCR_BAUD_LATCH); + + // LSB for baud rate of 38.4K. + WriteReg(0, 0x03); + + // MSB for baud rate of 38.4K. + WriteReg(1, 0x00); + + // leave set-baud mode, + // and set word length to 8 bits, no parity. + WriteReg(LCR, LCR_EIGHT_BITS); + + // reset and enable FIFOs. + WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + + // enable transmit and receive interrupts. + // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); + + //enable UART0 in PLIC + plic_irq_enable(UART0_IRQ); + + //set UART0 priority in PLIC + plic_prio_set(UART0_IRQ, 1); + + //register callback for UART0 + //plic_register_callback(UART0_IRQ, uart_intr); + puts("[UART0] : Uart Init Done, this is Test output!"); + return 0; +} + +void uart_putc_nolock(int ch) +{ + // wait for Transmit Holding Empty to be set in LSR. + while((ReadReg(LSR) & LSR_TX_IDLE) == 0) + ; + WriteReg(THR, ch); + return; +} + +int uart_putc(int ch) +{ + int intr_enable = riscv_mintr_get(); + riscv_mintr_off(); + uart_putc_nolock(ch); + riscv_mintr_restore(intr_enable); + return 1; +} + +int uart_puts(const char* str) +{ + int i; + int intr_enable = riscv_mintr_get(); + riscv_mintr_off(); + for(i=0;str[i]!=0;i++) + { + uart_putc_nolock(str[i]); + } + uart_putc_nolock('\n'); + riscv_mintr_restore(intr_enable); + return i; +} + + diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h new file mode 100644 index 000000000..19e8f73da --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_UART_H +#define RISCV_UART_H + +#define UART0 0x10000000L +#define UART0_IRQ 10 + +#define puts uart_puts +int uart_init(void); +int uart_putc(int ch); +void uart_putc_nolock(int ch); +int uart_puts(const char* str); +#endif