                       Microsoft's Azure RTOS ThreadX for RISC-V

                                     32-bit Mode

                                  Using the IAR Tools

1.  Building the ThreadX run-time Library

Building the ThreadX library is easy. First, open the Azure RTOS workspace
azure_rtos.eww. Next, make the tx.ewp project the "active project" in the
IAR Embedded Workbench and select the "Make" button. You should observe
assembly and compilation of a series of ThreadX source files. This
results in the ThreadX run-time library file tx.a, which is needed by
the application.


2.  Demonstration System

The ThreadX demonstration is designed to execute under the IAR
Windows-based RISC-V simulator.

Building the demonstration is easy; simply make the sample_threadx.ewp project
the "active project" in the IAR Embedded Workbench and select the
"Make" button.

You should observe the compilation of sample_threadx.c (which is the demonstration
application) and linking with tx.a. The resulting file sample_threadx.out is a
binary file that can be downloaded and executed on IAR's RISC-V simulator.


3.  System Initialization

The entry point in ThreadX for the RISC-V using IAR tools is at label
__iar_program_start. This is defined within the IAR compiler's startup code. In
addition, this is where all static and global preset C variable
initialization processing takes place.

The ThreadX tx_initialize_low_level.s file is responsible for setting up
various system data structures, and a periodic timer interrupt source.

The _tx_initialize_low_level function inside of tx_initialize_low_level.s
also determines the first available address for use by the application, which
is supplied as the sole input parameter to your application definition function,
tx_application_define. To accomplish this, a section is created in
tx_initialize_low_level.s called FREE_MEM, which must be located after all
other RAM sections in memory.


4.  Register Usage and Stack Frames

The IAR RISC-V compiler assumes that registers t0-t6 and a0-a7 are scratch
registers for each function. All other registers used by a C function must
be preserved by the function. ThreadX takes advantage of this in situations
where a context switch happens as a result of making a ThreadX service call
(which is itself a C function). In such cases, the saved context of a thread
is only the non-scratch registers.

The following defines the saved context stack frames for context switches
that occur as a result of interrupt handling or from thread-level API calls.
All suspended threads have one of these two types of stack frames. The top
of the suspended thread's stack is pointed to by tx_thread_stack_ptr in the
associated thread control block TX_THREAD.



    Offset        Interrupted Stack Frame        Non-Interrupt Stack Frame

     0x00                   1                           0
     0x04                   s11 (x27)                   s11 (x27)
     0x08                   s10 (x26)                   s10 (x26)
     0x0C                   s9  (x25)                   s9  (x25)
     0x10                   s8  (x24)                   s8  (x24)
     0x14                   s7  (x23)                   s7  (x23)
     0x18                   s6  (x22)                   s6  (x22)
     0x1C                   s5  (x21)                   s5  (x21)
     0x20                   s4  (x20)                   s4  (x20)
     0x24                   s3  (x19)                   s3  (x19)
     0x28                   s2  (x18)                   s2  (x18)
     0x2C                   s1  (x9)                    s1  (x9)
     0x30                   s0  (x8)                    s0  (x8)
     0x34                   t6  (x31)                   ra  (x1)
     0x38                   t5  (x30)                   mstatus
     0x3C                   t4  (x29)                   fs0
     0x40                   t3  (x28)                   fs1
     0x44                   t2  (x7)                    fs2
     0x48                   t1  (x6)                    fs3
     0x4C                   t0  (x5)                    fs4
     0x50                   a7  (x17)                   fs5
     0x54                   a6  (x16)                   fs6
     0x58                   a5  (x15)                   fs7
     0x5C                   a4  (x14)                   fs8
     0x60                   a3  (x13)                   fs9
     0x64                   a2  (x12)                   fs10
     0x68                   a1  (x11)                   fs11
     0x6C                   a0  (x10)                   fcsr
     0x70                   ra  (x1)
     0x74                   reserved
     0x78                   mepc
#if __iar_riscv_base_isa == rv32e
     0x7C                   ft0
     0x80                   ft1
     0x84                   ft2
     0x88                   ft3
     0x8C                   ft4
     0x90                   ft5
     0x94                   ft6
     0x98                   ft7
     0x9C                   fs0
     0xA0                   fs1
     0xA4                   fa0
     0xA8                   fa1
     0xAC                   fa2
     0xB0                   fa3
     0xB4                   fa4
     0xB8                   fa5
     0xBC                   fa6
     0xC0                   fa7
     0xC4                   fs2
     0xC8                   fs3
     0xCC                   fs4
     0xD0                   fs5
     0xD4                   fs6
     0xD8                   fs7
     0xDC                   fs8
     0xE0                   fs9
     0xE4                   fs10
     0xE8                   fs11
     0xEC                   ft8
     0xF0                   ft9
     0xF4                   ft10
     0xF8                   ft11
     0xFC                   fcsr
#endif


5.  Improving Performance

The distribution version of ThreadX is built without any compiler
optimizations. This makes it easy to debug because you can trace or set
breakpoints inside of ThreadX itself. Of course, this costs some
performance. To make ThreadX run faster, you can change the project
options to disable debug information and enable the desired
compiler optimizations.

In addition, you can eliminate the ThreadX basic API error checking by
compiling your application code with the symbol TX_DISABLE_ERROR_CHECKING
defined before tx_api.h is included.


6.  Interrupt Handling

ThreadX provides complete and high-performance interrupt handling for RISC-V
targets.The ThreadX general exception handler sample is defined as follows,
where "*" represents the interrupt vector number:

    PUBLIC  _sample_interrupt_handler
    PUBLIC  __minterrupt_00000*
    EXTWEAK __require_minterrupt_vector_table
_sample_interrupt_handler:
__minterrupt_00000*:
    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 __iar_riscv_base_isa == rv32e
    addi    sp, sp, -260                            ; Allocate space for all registers - with floating point enabled
#else
    addi    sp, sp, -128                            ; Allocate space for all registers - without floating point enabled
#endif
    sw      x1, 0x70(sp)                            ; Store RA
    call    _tx_thread_context_save                 ; Call ThreadX context save

    /* Call your ISR processing here!  */
    call    your_ISR_processing

    /* 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!


Some additional conditions:

        1. In the project settings Linker -> Extra Options, --auto_vector_setup should be defined.
        2. The project linker control file should have the following sections to include the vector table:

        define block MVECTOR with alignment = 128 { ro section .mintvec };

        if (isdefinedsymbol(_uses_clic))
        {
          define block MINTERRUPT with alignment = 128 { ro section .mtext };
          define block MINTERRUPTS { block MVECTOR,
                                        block MINTERRUPT };
        }
        else
        {
          define block MINTERRUPTS with maximum size =  64k { ro section .mtext,
                                                                   midway block MVECTOR };
        }

6.1 Sample Timer ISR

The following sample timer ISR using vector 7 is defined in tx_initialize_low_level.s such that timer
functionality is available under IAR simulation:

    PUBLIC  _tx_timer_interrupt_handler
    PUBLIC  __minterrupt_000007
    EXTWEAK __require_minterrupt_vector_table
_tx_timer_interrupt_handler:
__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 __iar_riscv_base_isa == rv32e
    addi    sp, sp, -260                            ; Allocate space for all registers - with floating point enabled
#else
    addi    sp, sp, -128                            ; Allocate space for all registers - without floating point enabled
#endif
    sw      x1, 0x70(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

    /* 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!


7.  Revision History

For generic code revision information, please refer to the readme_threadx_generic.txt
file, which is included in your distribution. The following details the revision
information associated with this specific port of ThreadX:

04-02-2021  Release 6.1.6 changes:
            tx_port.h                           Updated macro definition

08/09/2020  Initial ThreadX version for RISC-V using IAR Tools.


Copyright(c) 1996-2020 Microsoft Corporation


https://azure.com/rtos

