diff --git a/ports/risc-v64/gnu/example_build/do_irq.c b/ports/risc-v64/gnu/example_build/do_irq.c new file mode 100644 index 000000000..8f1a45eac --- /dev/null +++ b/ports/risc-v64/gnu/example_build/do_irq.c @@ -0,0 +1,90 @@ +// See LICENSE for license details. + +#ifndef RISCV_CSR_ENCODING_H +#define RISCV_CSR_ENCODING_H + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_SUM 0x00040000 +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define MIP_SSIE (1 << IRQ_S_SOFT) +#define MIP_HSIE (1 << IRQ_H_SOFT) +#define MIP_MSIE (1 << IRQ_M_SOFT) +#define MIP_STIE (1 << IRQ_S_TIMER) +#define MIP_HTIE (1 << IRQ_H_TIMER) +#define MIP_MTIE (1 << IRQ_M_TIMER) +#define MIP_SEIE (1 << IRQ_S_EXT) +#define MIP_HEIE (1 << IRQ_H_EXT) +#define MIP_MEIE (1 << IRQ_M_EXT) + +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +# define MAX_HARTS 1 + +#ifndef __ASSEMBLER__ + +#ifdef __GNUC__ +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) + +#define swap_csr(reg, val) ({ unsigned long __tmp; \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ + __tmp; }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ + __tmp; }) + +#define rdtime() read_csr(time) +#define rdcycle() read_csr(cycle) +#define rdinstret() read_csr(instret) + +#endif + +#endif + +#endif + +void do_irq(void) +{ + /* get irq num do something */ + + /* clear external interrupt pending */ + clear_csr(mip, MIP_MEIE); +} \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/sample_threadx.c b/ports/risc-v64/gnu/example_build/sample_threadx.c new file mode 100644 index 000000000..0bc015119 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* 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" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* 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; +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE] __attribute__ ( (section( ".heap" )) ); + + +/* 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", memory_area, 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) + { + + /* 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) + { + + /* 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) + 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) + { + + /* 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)) + 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) + { + + /* 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) + { + + /* 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) + { + + /* 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-v64/gnu/example_build/sample_threadx.ld b/ports/risc-v64/gnu/example_build/sample_threadx.ld new file mode 100755 index 000000000..23dc76759 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/sample_threadx.ld @@ -0,0 +1,189 @@ +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x20000; +/* _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x1000000; */ +/*_HEAP_SIZE = 0x20000;*/ + +_EL0_STACK_SIZE = DEFINED(_EL0_STACK_SIZE) ? _EL0_STACK_SIZE : 1024; +_EL1_STACK_SIZE = DEFINED(_EL1_STACK_SIZE) ? _EL1_STACK_SIZE : 2048; +_EL2_STACK_SIZE = DEFINED(_EL2_STACK_SIZE) ? _EL2_STACK_SIZE : 1024; + +/* Define Memories in the system */ +MEMORY +{ + psu_ddr_0_MEM_0 : ORIGIN = 0x83f40000 , LENGTH = 0x000c0000 +} + +/* Specify the default entry point to the program */ + +/*ENTRY(_vector_table)*/ +ENTRY(_start) + +/* Define the sections, and where they are mapped in memory */ + +SECTIONS +{ +.text : { + KEEP (*(.vectors)) + *(.boot) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.gnu_warning) + *(.gcc_execpt_table) + *(.glue_7) + *(.glue_7t) + *(.ARM.extab) + *(.gnu.linkonce.armextab.*) +} > psu_ddr_0_MEM_0 + +.init (ALIGN(64)) : { + KEEP (*(.init)) +} > psu_ddr_0_MEM_0 + +.fini (ALIGN(64)) : { + KEEP (*(.fini)) +} > psu_ddr_0_MEM_0 + +.interp : { + KEEP (*(.interp)) +} > psu_ddr_0_MEM_0 + +.note-ABI-tag : { + KEEP (*(.note-ABI-tag)) +} > psu_ddr_0_MEM_0 + +.rodata : { + . = ALIGN(64); + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.srodata*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > psu_ddr_0_MEM_0 + +.rodata1 : { + . = ALIGN(64); + __rodata1_start = .; + *(.rodata1) + *(.rodata1.*) + __rodata1_end = .; +} > psu_ddr_0_MEM_0 + +.data : { + . = ALIGN(64); + _data = .; + *(.data) + *(.data.*) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.d.*) + *(.jcr) + *(.got) + *(.got.plt) + _edata = .; +} > psu_ddr_0_MEM_0 + +.data1 : { + . = ALIGN(64); + __data1_start = .; + *(.data1) + *(.data1.*) + __data1_end = .; +} > psu_ddr_0_MEM_0 + +.got : { + *(.got) +} > psu_ddr_0_MEM_0 + +.got1 : { + *(.got1) +} > psu_ddr_0_MEM_0 + +.got2 : { + *(.got2) +} > psu_ddr_0_MEM_0 + +.ctors : { + . = ALIGN(64); + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > psu_ddr_0_MEM_0 + +.dtors : { + . = ALIGN(64); + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + __DTOR_END__ = .; + ___DTORS_END___ = .; +} > psu_ddr_0_MEM_0 + +.fixup : { + __fixup_start = .; + *(.fixup) + __fixup_end = .; +} > psu_ddr_0_MEM_0 + +.eh_frame : { + *(.eh_frame) +} > psu_ddr_0_MEM_0 + +.eh_framehdr : { + __eh_framehdr_start = .; + *(.eh_framehdr) + __eh_framehdr_end = .; +} > psu_ddr_0_MEM_0 + +.gcc_except_table : { + *(.gcc_except_table) +} > psu_ddr_0_MEM_0 + +.bss (NOLOAD) : { + . = ALIGN(64); + _bss = .; + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(64); + _ebss = .; +} > psu_ddr_0_MEM_0 + +/*_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );*/ + _data_lma = LOADADDR(.data); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(64); + _heap = .; + HeapBase = .; + _heap_start = .; + *(.heap*) + /*. += _HEAP_SIZE;*/ + /*_heap_size = _HEAP_SIZE; */ + _heap_end = .; + HeapLimit = .; +} > psu_ddr_0_MEM_0 + +.stack (NOLOAD) : { + . = ALIGN(64); + _stack_end_end = .; + . += _STACK_SIZE; + _stack_top = .; +} > psu_ddr_0_MEM_0 + __RAM_segment_used_end__ = .; +} + diff --git a/ports/risc-v64/gnu/example_build/start.S b/ports/risc-v64/gnu/example_build/start.S new file mode 100755 index 000000000..10efd747d --- /dev/null +++ b/ports/risc-v64/gnu/example_build/start.S @@ -0,0 +1,143 @@ +/* + * FreeRTOS V202111.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://www.github.com/FreeRTOS + * + * 1 tab == 4 spaces! + */ + +#include "tx_arch_header.h" +#include "tx_port.h" +.org 0 + .section .vectors, "ax" + .globl _start + .type _start,@function +_start: + .cfi_startproc + .cfi_undefined ra +.option push +.option norelax +// la gp, __global_pointer$ +.option pop + + // Continue primary hart + csrr a0, mhartid + li a1, PRIM_HART + bne a0, a1, secondary + + 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 + + // enable interrupt + li x3, 0x880 + csrw mie, x3 + + // invalidate all memory for BTB,BHT,DCACHE,ICACHE + li x3, 0x30013 + csrs mcor, x3 + // enable ICACHE,DCACHE,BHT,BTB,RAS,WA + li x3, 0x7f + csrs mhcr, x3 + // enable data_cache_prefetch, amr + li x3, 0x610c + csrs mhint, x3 #mhint + # enable fp + li x3, 0x1 << 13 + csrs mstatus, x3 + + // Primary hart + la sp, _stack_top + + // Load data section + la a0, _data_lma + la a1, _data + la a2, _edata + bgeu a1, a2, 2f +1: + LOAD t0, (a0) + STORE t0, (a1) + addi a0, a0, REGBYTES + addi a1, a1, REGBYTES + bltu a1, a2, 1b +2: + + // Clear bss section + la a0, _bss + la a1, _ebss + bgeu a0, a1, 2f +1: + // reduce branch time, be sure about bss alignment in linker script + STORE zero, 0x00 (a0) + STORE zero, 0x08 (a0) + STORE zero, 0x10 (a0) + STORE zero, 0x18 (a0) + STORE zero, 0x20 (a0) + STORE zero, 0x28 (a0) + STORE zero, 0x30 (a0) + STORE zero, 0x38 (a0) + addi a0, a0, REGBYTES * 8 + bltu a0, a1, 1b +2: + + // argc, argv, envp is 0 + li a0, 0 + li a1, 0 + li a2, 0 + jal main +1: + wfi + j 1b + +secondary: + // TODO: Multicore is not supported + wfi + j secondary + .cfi_endproc diff --git a/ports/risc-v64/gnu/example_build/tx_arch_header.h b/ports/risc-v64/gnu/example_build/tx_arch_header.h new file mode 100644 index 000000000..93602b8f0 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/tx_arch_header.h @@ -0,0 +1,19 @@ +#ifndef TX_ARCH_HEADER_H +#define TX_ARCH_HEADER_H + +#ifdef __ASSEMBLER__ +#define CONS(NUM, TYPE)NUM +#else +#define CONS(NUM, TYPE)NUM##TYPE +#endif /* __ASSEMBLER__ */ + +#define THEAD_C906 + +#define CLINT_ADDR CONS(0x74000000, UL) +#define CLINT_MSIP CONS(0x0000, UL) +#define CLINT_MTIMECMP CONS(0x4000, UL) +#undef CLINT_MTIME +#define configMTIMECMP_BASE_ADDRESS ( CLINT_ADDR + CLINT_MTIMECMP ) +#define PRIM_HART 0 + +#endif // !TX_ARCH_HEADER_H \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/tx_initialize_low_level.S b/ports/risc-v64/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 000000000..6efc27baa --- /dev/null +++ b/ports/risc-v64/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,221 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" +.global __RAM_segment_used_end__ +.global tx_port_setup_timer + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* 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 */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .global _tx_initialize_low_level +_tx_initialize_low_level: + sd sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer + + la t0, __RAM_segment_used_end__ // Pickup first free address + addi t0, t0, 8 // add 8 + sd t0, _tx_initialize_unused_memory, t1 // Save unused memory address + +#ifdef __riscv_flen + fscsr x0 +#endif + + // set up interrupt and timer(mtime) +#if( portasmHAS_SIFIVE_CLINT != 0 ) + /* If there is a clint use a unified interrupt/trap entry */ + la t0, threadx_trap_entry + csrw mtvec, t0 +#endif + + addi sp, sp, -1*REGBYTES + STORE ra, 1*REGBYTES(sp) // save ra + call tx_port_setup_timer // setup timer interrupt + LOAD ra, 1*REGBYTES(sp) + addi sp, sp, 1*REGBYTES + ret + + + /* Define the actual timer interrupt/exception handler. */ + +.global do_irq // user irq handler, irq num can be got by PLIC +#ifdef THEAD_C906 +.global pulMachineTimerCompareRegisterL +.global pulMachineTimerCompareRegisterH +#else +.global pullMachineTimerCompareRegister +#endif +.global pullNextTime +.global uxTimerIncrementsForOneTick /* size_t type so 32-bit on 32-bit core and 64-bits on 64-bit core. */ +.global _tx_timer_interrupt +.global _tx_thread_context_save +.global _tx_thread_context_restore + +.global threadx_trap_entry + +.align 8 +.func +threadx_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(224 for riscv64) + + call _tx_thread_context_save // Call ThreadX context save + + /* exception handle part */ + + csrr a0, mcause + csrr a1, mepc + +test_if_asynchronous: + srli a2, a0, __riscv_xlen - 1 /* MSB of mcause is 1 if handing an asynchronous interrupt - shift to LSB to clear other bits. */ + beq a2, x0, handle_synchronous /* Branch past interrupt handing if not asynchronous. */ + + /* unmodified exception return address(mepc) was saved in tx_thread_context_save.S */ + +handle_asynchronous: + +#if( portasmHAS_MTIME != 0 ) + + test_if_mtimer: /* If there is a CLINT then the mtimer is used to generate the tick interrupt. */ + + addi t0, x0, 1 /* t0 = 1*/ + + slli t0, t0, __riscv_xlen - 1 /* LSB is already set, shift into MSB. Shift 31 on 32-bit or 63 on 64-bit cores. */ + addi t1, t0, 7 /* 0x8000[]0007 == machine timer interrupt. */ + bne a0, t1, test_if_external_interrupt + + #ifdef THEAD_C906 + LOAD t0, pulMachineTimerCompareRegisterL /* Load address of compare Lo register into t0. */ + #else + LOAD t0, pullMachineTimerCompareRegister /* Load address of compare register into t0. */ + #endif + + LOAD t1, pullNextTime /* Load the address of ullNextTime into t1. */ + + #ifdef THEAD_C906 + /* Update the 64-bit mtimer compare match value in two 32-bit writes. */ + ld t2, 0(t1) /* Load ullNextTime into t2. */ + sw t2, 0(t0) /* Store ullNextTime into compare register L. */ + srli t3, t2, 32 + sw t3, 4(t0) /* Store ullNextTime into compare register H. */ + ld t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */ + add t4, t0, t2 /* Add ullNextTime to the timer increments for one tick. */ + sd t4, 0(t1) /* Store ullNextTime. */ + #else + /* Update the 64-bit mtimer compare match value. */ + ld t2, 0(t1) /* Load ullNextTime into t2. */ + sd t2, 0(t0) /* Store ullNextTime into compare register. */ + ld t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */ + add t4, t0, t2 /* Add ullNextTime to the timer increments for one tick. */ + sd t4, 0(t1) /* Store ullNextTime. */ + #endif + + // use _tx_thread_system_stack_ptr, switched in tx_thread_context_save.S + jal _tx_timer_interrupt /* threadx do not return yield flag */ + j context_restore /* jump to _tx_thread_context_restore */ + + test_if_external_interrupt: /* If there is a CLINT and the mtimer interrupt is not pending then check to see if an external interrupt is pending. */ + addi t1, t1, 4 /* 0x80000007 + 4 = 0x8000000b == Machine external interrupt. */ + bne a0, t1, as_yet_unhandled /* Something as yet unhandled. */ + +#endif /* portasmHAS_MTIME */ + + // use _tx_thread_system_stack_ptr, switched in tx_thread_context_save.S + jal do_irq /* Jump to the interrupt handler if there is no CLINT or if there is a CLINT and it has been determined that an external interrupt is pending. */ + j context_restore /* jump to _tx_thread_context_restore */ + +handle_synchronous: + addi a1, a1, 4 /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */ + + /* Save updated exception return address. */ + LOAD t0, _tx_thread_current_ptr // Pickup current thread pointer + beqz t0, _tx_thread_idle_system // If NULL, idle system was interrupted + STORE a1, 30*REGBYTES(sp) // Save new mepc again + /* idle system do not need save mepc as it will directly jump to schedule */ +_tx_thread_idle_system: + +test_if_environment_call: + li t0, 11 /* 11 == environment call. */ + bne a0, t0, is_exception /* Not an M environment call, so some other exception. */ + j context_restore /* jump to _tx_thread_context_restore */ + +is_exception: + csrr t0, mcause /* For viewing in the debugger only. */ + csrr t1, mepc /* For viewing in the debugger only */ + csrr t2, mstatus + + j is_exception /* No other exceptions handled yet. */ + +as_yet_unhandled: + csrr t0, mcause /* For viewing in the debugger only. */ + + j as_yet_unhandled + +context_restore: + j _tx_thread_context_restore /* Jump to ThreadX context restore function. Note: this does not return!*/ + + .endfunc \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/tx_port_setup_timer.c b/ports/risc-v64/gnu/example_build/tx_port_setup_timer.c new file mode 100644 index 000000000..08b6a90c8 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/tx_port_setup_timer.c @@ -0,0 +1,60 @@ +// setup timer interrupt(mtime) +#include "tx_port.h" +#include "tx_api.h" +#include "tx_arch_header.h" + +ULONG64 ullNextTime = 0ULL; +const ULONG64 *pullNextTime = &ullNextTime; +const ULONG64 uxTimerIncrementsForOneTick = (ULONG64)((configSYS_CLOCK_HZ) / (TX_TIMER_TICKS_PER_SECOND)); +UINT const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS; + +#if (defined THEAD_C906) && (!defined configMTIME_BASE_ADDRESS) && (configMTIMECMP_BASE_ADDRESS != 0) +volatile UINT *pulMachineTimerCompareRegisterL = NULL; +volatile UINT *pulMachineTimerCompareRegisterH = NULL; +#else +volatile uint64_t *pullMachineTimerCompareRegister = NULL; +#error define failed--------------------------------- +#endif + +void tx_port_setup_timer(void) +{ + + asm volatile("csrc mstatus, %0" ::"r"(0x08)); // disable enterrupt + +#ifdef THEAD_C906 + /* If there is a CLINT then it is ok to use the default implementation + in this file, otherwise vPortSetupTimerInterrupt() must be implemented to + configure whichever clock is to be used to generate the tick interrupt. */ + uint64_t ullCurrentTime; + volatile uint32_t ulHartId; + asm volatile("csrr %0, mhartid" : "=r"(ulHartId)); + + // 32bit IO bus, need to get hi/lo seperately + pulMachineTimerCompareRegisterL = + (volatile uint32_t *)(ullMachineTimerCompareRegisterBase + (ulHartId * sizeof(uint64_t))); + pulMachineTimerCompareRegisterH = + (volatile uint32_t *)(ullMachineTimerCompareRegisterBase + sizeof(uint32_t) + (ulHartId * sizeof(uint64_t))); + + asm volatile("rdtime %0" : "=r"(ullCurrentTime)); + + ullNextTime = (uint64_t)ullCurrentTime; + ullNextTime += (uint64_t)uxTimerIncrementsForOneTick; + *pulMachineTimerCompareRegisterL = (uint32_t)(ullNextTime & 0xFFFFFFFF); + *pulMachineTimerCompareRegisterH = (uint32_t)(ullNextTime >> 32); + + /* Prepare the time to use after the next tick interrupt. */ + ullNextTime += (uint64_t)uxTimerIncrementsForOneTick; +#endif + +#if ((configMTIME_BASE_ADDRESS != 0) && (configMTIMECMP_BASE_ADDRESS != 0) || (defined THEAD_C906)) + { + /* Enable mtime and external interrupts. */ + asm volatile("csrs mie, %0" ::"r"(0x880)); + } +#else + { + /* Enable external interrupts. */ + __asm volatile("csrs mie, %0" ::"r"(0x800)); + } +#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */ +} diff --git a/ports/risc-v64/gnu/src/tx_initialize_low_level.S b/ports/risc-v64/gnu/src/tx_initialize_low_level.S deleted file mode 100644 index de6203a92..000000000 --- a/ports/risc-v64/gnu/src/tx_initialize_low_level.S +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - * 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 - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - .section .data - .global __tx_free_memory_start -__tx_free_memory_start: - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level RISC-V64/GNU */ -/* 6.2.1 */ -/* AUTHOR */ -/* */ -/* Scott Larson, Microsoft Corporation */ -/* */ -/* 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 */ -/* */ -/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ -/* */ -/**************************************************************************/ -/* VOID _tx_initialize_low_level(VOID) -{ */ - .global _tx_initialize_low_level -_tx_initialize_low_level: - sd sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer - - la t0, __tx_free_memory_start // Pickup first free address - sd t0, _tx_initialize_unused_memory, t1 // Save unused memory address - -#ifdef __riscv_flen - fscsr x0 -#endif - - ret - - - /* Define the actual timer interrupt/exception handler. */ - - .global timer1_plic_IRQHandler - //.global __minterrupt_000007 - //EXTWEAK __require_minterrupt_vector_table -timer1_plic_IRQHandler: -//__minterrupt_000007: - //REQUIRE __require_minterrupt_vector_table - - - /* Before calling _tx_thread_context_save, we have to allocate an interrupt - stack frame and save the current value of x1 (ra). */ -//#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) -// addi sp, sp, -520 // Allocate space for all registers - with floating point enabled -//#else -// addi sp, sp, -256 // Allocate space for all registers - without floating point enabled -//#endif -// sd x1, 224(sp) // Store RA -// call _tx_thread_context_save // Call ThreadX context save - - /* Call the ThreadX timer routine. */ - call _tx_timer_interrupt // Call timer interrupt handler - call timer1_interrupt - ret - /* Timer interrupt processing is done, jump to ThreadX context restore. */ -// j _tx_thread_context_restore // Jump to ThreadX context restore function. Note: this does not return! diff --git a/ports/risc-v64/gnu/src/tx_thread_context_restore.S b/ports/risc-v64/gnu/src/tx_thread_context_restore.S index 64da25b4c..2718b15d8 100644 --- a/ports/risc-v64/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v64/gnu/src/tx_thread_context_restore.S @@ -148,8 +148,16 @@ _tx_thread_context_restore: LOAD t0, 30*REGBYTES(sp) // Recover mepc csrw mepc, t0 // Setup mepc + +#if defined(__riscv_float_abi_double) || defined(__riscv_float_abi_single) + li t0, 0x1880 // Set MPP, MPIE + csrs mstatus, t0 // avoid to clear FPU field + li t0, 0x77f + csrc mstatus, t0 +#else li t0, 0x1880 // Prepare MPIP csrw mstatus, t0 // Enable MPIP +#endif LOAD x1, 28*REGBYTES(sp) // Recover RA LOAD x5, 19*REGBYTES(sp) // Recover t0 @@ -258,8 +266,16 @@ _tx_thread_no_preempt_restore: LOAD t0, 240(sp) // Recover mepc csrw mepc, t0 // Setup mepc + +#if defined(__riscv_float_abi_double) || defined(__riscv_float_abi_single) + li t0, 0x1880 // Set MPP, MPIE + csrs mstatus, t0 // avoid to clear FPU field + li t0, 0x77f + csrc mstatus, t0 +#else li t0, 0x1880 // Prepare MPIP csrw mstatus, t0 // Enable MPIP +#endif LOAD x1, 28*REGBYTES(sp) // Recover RA LOAD x5, 19*REGBYTES(sp) // Recover t0 diff --git a/ports/risc-v64/gnu/src/tx_thread_schedule.S b/ports/risc-v64/gnu/src/tx_thread_schedule.S index 343da393c..0feeef1c7 100644 --- a/ports/risc-v64/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v64/gnu/src/tx_thread_schedule.S @@ -200,8 +200,16 @@ _tx_thread_schedule_loop: LOAD t0, 30*REGBYTES(sp) // Recover mepc csrw mepc, t0 // Store mepc + +#if defined(__riscv_float_abi_double) || defined(__riscv_float_abi_single) + li t0, 0x1880 // Set MPP, MPIE + csrs mstatus, t0 // avoid to clear FPU field + li t0, 0x77f + csrc mstatus, t0 +#else li t0, 0x1880 // Prepare MPIP csrw mstatus, t0 // Enable MPIP +#endif LOAD x1, 28*REGBYTES(sp) // Recover RA LOAD x5, 19*REGBYTES(sp) // Recover t0