Problems with setting up machine timer in Renesas RISC-V

Hi I am working a port for the Renesas RISC-V.
The problems I am encountering are many, I’ll list them (they are all exclusive, happens only one of them at a time):

  • running the program, after not much time it asserts at line 2448 of tasks.c (plus xTickCount has a value of 0xa5a5a5a5)
  • it goes in a loop where xPendingTicks grows indefinetly and xTickCount is still at value 0x20005a34
  • process ‘\0’ asserts a stack overflow
  • it “ping pongs” in the exception handler between freertos_risc_v_exception_handler and IRQ0 entry in the vector list

All those problems derive from adding the interrupt_mtimer_interrupt_handler inside the vector entry for it (in my case it must be defined inside the IDE, more specifically inside a function named INT_ACLINT_MTIP. It does not work by setting the mtvec for some reason, although setting the mtvec is necessary for handling the exceptions that otherwise setting them through the IDE for entry 0 of the vector table does not work. With does not work I mean I get sent into an are name gp_ExceptVector at position 0x1C, which strangely is where also the INT_ACLINT_MTIP should be).

I’ll add other odd things that happen and that are not clear to me.

  • the stack in my case seems really odd, I mean this is the specific part of the linker script:
PROVIDE(__stack_size = 0x200);
	.stack 0x20006FFC (NOLOAD) : AT(0x20006FFC)
	{
		PROVIDE(__stack = .);
		ASSERT((__stack > (end + __stack_size)), "Error: Too much data - no room left for the stack");
		. = ALIGN(16);
		__freertos_irq_stack_top = .;
	} >RAM

Here is the address space: https://www.renesas.com/en/document/mah/r9a02g021-users-manual-hardware?r=25470171#unique_196

I know that the __freertos_irq_stack_top variable is not set up correctly, but based on the image I attach about the address space, can you help figure this out?

  • when there is an ecall is it right to enter the first entry of the interrupt vector table? Since from the IDE I cannot set up the exception handler (first entry of the interrupt vector table) directly and I need to overwrite the mtvec, is there a way to setup only the exception handler and not the whole interrupt vector table? However even by overwriting the whole mtvec the handler for the machine timer interrupt is invoked somehow.

Just for adding more useful information, here is the main:

#include "r_smc_entry.h"
#include "FreeRTOS.h"
#include "task.h"

volatile uint32_t blinkDelay = 1000; // Initial blink delay of 1 second (1 Hz)
extern volatile void freertos_vector_table(void);
extern volatile void freertos_risc_v_trap_handler(void);

int main(void);

void vTaskFunction1(void *pvParameters) {
	machine_timer_start();

	while(1)
	{
		/* Toggle LED status */
	    PIN_WRITE(LED2) = ~PIN_READ(LED2);

	    /* Delay blinkDelay milliseconds before returning */
	    vTaskDelay(blinkDelay);
	}
}

int main(void)
{
    /* Setup and start the machine timer */
    asm volatile ( "csrw mtvec, %0" : : "r" ( ( uintptr_t ) freertos_vector_table | 0x01 ));

    /* Start SW Interrupt */
    R_Config_ICU_IRQ4_Start();

	/* Create the task(s) */
    int taskReturnValue1 = xTaskCreate(
        vTaskFunction1,       		/* Pointer to the function that implements the task. */
        "Task 1 BLINK",       		/* Text name for the task (useful for debugging). */
        configMINIMAL_STACK_SIZE, 	/* Stack depth in words. */
        NULL,                 		/* Task input parameter (if any). */
        tskIDLE_PRIORITY + 1,     	/* Priority at which the task will run. */
        NULL                  		/* Task handle (not used in this case). */
    );

    if (taskReturnValue1 == pdTRUE)
    	vTaskStartScheduler();

    for (;;);

    return 0;
}

In the r_cg_inthandler.c:

/*
 * INT_ACLINT_MSIP (0x00)
 */
void INT_ACLINT_MSIP(void)
{
    /* Start user code for INT_ACLINT_MSIP. Do not edit comment generated here */
	freertos_risc_v_mtimer_interrupt_handler();
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_ACLINT_MTIP (0x1C)
 */
void INT_ACLINT_MTIP(void)
{
    /* Start user code for INT_ACLINT_MTIP. Do not edit comment generated here */
	freertos_risc_v_mtimer_interrupt_handler();
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR0 (0x4C)
 */
void INT_IELSR0(void)
{
    /* Start user code for INT_IELSR0. Do not edit comment generated here */
	freertos_risc_v_exception_handler();
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR1 (0x50)
 */
void INT_IELSR1(void)
{
    /* Start user code for INT_IELSR1. Do not edit comment generated here */
	freertos_risc_v_interrupt_handler();
    /* End user code. Do not edit comment generated here */
}

I have also tried calling the function that Renesas provides for refreshing machine timer (changing mtimecmp value) but doing so I lose the use of uxTimerIncrementsForOneTick (suggest me if it makes sense making Renesas API use this value).

As I said the problems are many, if you can help in any of them I would be very glad to you.

This is not correct as the function freertos_risc_v_mtimer_interrupt_handler is a naked function and must be installed as the interrupt handler. I suspect that the problem is related to correct installation of interrupt handlers. Are you using vectored mode i.e. do all the interrupts have different entry points or do all the interrupts have same entry point? Can you share your complete interrupt installation code?

Hi and thanks for your answer.
Thank you for let me notice it, I learned something new. My updated code for the interrupt vector table is this one (let me know if it is correct now):

/***********************************************************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products.
* No other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
* applicable laws, including copyright laws. 
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING THIS SOFTWARE, WHETHER EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT.  ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY
* LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE FOR ANY DIRECT,
* INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR
* ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability 
* of this software. By using this software, you agree to the additional terms and conditions found by accessing the 
* following link:
* http://www.renesas.com/disclaimer
*
* Copyright (C) 2021, 2023 Renesas Electronics Corporation. All rights reserved.
***********************************************************************************************************************/

/***********************************************************************************************************************
* File Name        : r_cg_inthandler.c
* Version          : 1.0.30
* Device(s)        : R9A02G0214CNE
* Description      : None
* Creation Date    : 
***********************************************************************************************************************/

/***********************************************************************************************************************
Includes
***********************************************************************************************************************/
#include "r_cg_interrupt_handlers.h"

/* Start user code */
#include "platform.h"

extern void freertos_risc_v_exception_handler;
extern void freertos_risc_v_interrupt_handler;
extern void freertos_risc_v_mtimer_interrupt_handler;
/* End user code */
/*
 * INT_ACLINT_MSIP (0x00)
 */
void INT_ACLINT_MSIP(void)
{
    /* Start user code for INT_ACLINT_MSIP. Do not edit comment generated here */
	asm("j freertos_risc_v_mtimer_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_ACLINT_MTIP (0x1C)
 */
void INT_ACLINT_MTIP(void)
{
    /* Start user code for INT_ACLINT_MTIP. Do not edit comment generated here */
	asm("j freertos_risc_v_mtimer_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR0 (0x4C)
 */
void INT_IELSR0(void)
{
    /* Start user code for INT_IELSR0. Do not edit comment generated here */
	asm("j freertos_risc_v_exception_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR1 (0x50)
 */
void INT_IELSR1(void)
{
    /* Start user code for INT_IELSR1. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR2 (0x54)
 */
void INT_IELSR2(void)
{
    /* Start user code for INT_IELSR2. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR3 (0x58)
 */
void INT_IELSR3(void)
{
    /* Start user code for INT_IELSR3. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR4 (0x5C)
 */
void INT_IELSR4(void)
{
    /* Start user code for INT_IELSR4. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR5 (0x60)
 */
void INT_IELSR5(void)
{
    /* Start user code for INT_IELSR5. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR6 (0x64)
 */
void INT_IELSR6(void)
{
    /* Start user code for INT_IELSR6. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR7 (0x68)
 */
void INT_IELSR7(void)
{
    /* Start user code for INT_IELSR7. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR8 (0x6C)
 */
void INT_IELSR8(void)
{
    /* Start user code for INT_IELSR8. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR9 (0x70)
 */
void INT_IELSR9(void)
{
    /* Start user code for INT_IELSR9. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR10 (0x74)
 */
void INT_IELSR10(void)
{
    /* Start user code for INT_IELSR10. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR11 (0x78)
 */
void INT_IELSR11(void)
{
    /* Start user code for INT_IELSR11. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR12 (0x7C)
 */
void INT_IELSR12(void)
{
    /* Start user code for INT_IELSR12. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR13 (0x80)
 */
void INT_IELSR13(void)
{
    /* Start user code for INT_IELSR13. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR14 (0x84)
 */
void INT_IELSR14(void)
{
    /* Start user code for INT_IELSR14. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR15 (0x88)
 */
void INT_IELSR15(void)
{
    /* Start user code for INT_IELSR15. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR16 (0x8C)
 */
void INT_IELSR16(void)
{
    /* Start user code for INT_IELSR16. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR17 (0x90)
 */
void INT_IELSR17(void)
{
    /* Start user code for INT_IELSR17. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR18 (0x94)
 */
void INT_IELSR18(void)
{
    /* Start user code for INT_IELSR18. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR19 (0x98)
 */
void INT_IELSR19(void)
{
    /* Start user code for INT_IELSR19. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR20 (0x9C)
 */
void INT_IELSR20(void)
{
    /* Start user code for INT_IELSR20. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR21 (0xA0)
 */
void INT_IELSR21(void)
{
    /* Start user code for INT_IELSR21. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR22 (0xA4)
 */
void INT_IELSR22(void)
{
    /* Start user code for INT_IELSR22. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR23 (0xA8)
 */
void INT_IELSR23(void)
{
    /* Start user code for INT_IELSR23. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR24 (0xAC)
 */
void INT_IELSR24(void)
{
    /* Start user code for INT_IELSR24. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR25 (0xB0)
 */
void INT_IELSR25(void)
{
    /* Start user code for INT_IELSR25. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR26 (0xB4)
 */
void INT_IELSR26(void)
{
    /* Start user code for null. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR27 (0xB8)
 */
void INT_IELSR27(void)
{
    /* Start user code for INT_IELSR27. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR28 (0xBC0)
 */
void INT_IELSR28(void)
{
    /* Start user code for INT_IELSR28. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR29 (0xC0)
 */
void INT_IELSR29(void)
{
    /* Start user code for INT_IELSR29. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR30 (0xC4)
 */
void INT_IELSR30(void)
{
    /* Start user code for INT_IELSR30. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_IELSR31 (0xC8)
 */
void INT_IELSR31(void)
{
    /* Start user code for INT_IELSR31. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

/*
 * INT_DUMMY for reserved interrupt source
 */
void INT_DUMMY(void)
{
    /* Start user code for INT_DUMMY. Do not edit comment generated here */
	asm("j freertos_risc_v_interrupt_handler");
    /* End user code. Do not edit comment generated here */
}

The code for the initialization of the vector table is already written by Renesas and something make me suspect that some jumps are hard coded/hardware coded, but I’ll came back later on this.

void initialize_vect(void)
{
    R_CPU_AUX->NMIADDR = (uint32_t)nmi_handler;
    /* The mtvec register must be set even if the interrupt vector table is not used. */
    /* Set the value (address of nvect_function()(0x1C0) >> 6)  to mtvec[31:6] */
    /* mtvec.BASE = 0x07; */
    asm( "li t0, 0x1C2" );
    asm( "csrw mtvec, t0" );    /* Set mtvec. */

    /* The mtvt register must be set when using the interrupt vector table. */
    /* Set the value (address of VECT_SECT(0xC0) >> 6) to mtvt[31:6] */
    /* mtvt.MTVT = 0x03; */
    asm( "li t0, 0xC0" );
    asm( "csrw mtvt, t0" );    /* Set mtvt. */
};

As you can see in the code the line asm( "li t0, 0x1C2" ); (and the next line) sets the value of the mtvec. Even changing the last digit to 3 which would set the first bit does not make any difference.

As you have problably already seen in my main code, I try to change the vector table to my custom one with this line of code:

asm volatile ( "csrw mtvec, %0" : : "r" ( ( uintptr_t ) freertos_vector_table | 0x01 ));

but it is only used for the exception handler (first entry) and the exceptions fall into the Renesas defined vector. For more info here is the code for my custom vector table:

.global freertos_vector_table

.extern freertos_risc_v_trap_handler
.extern freertos_risc_v_exception_handler
.extern freertos_risc_v_interrupt_handler
.extern freertos_risc_v_mtimer_interrupt_handler


.balign 128, 0
.option norvc

freertos_vector_table:
IRQ_0:
    j freertos_risc_v_exception_handler
IRQ_1:
    j freertos_risc_v_interrupt_handler
IRQ_2:
    j freertos_risc_v_interrupt_handler
IRQ_3:
    j freertos_risc_v_interrupt_handler
IRQ_4:
    j freertos_risc_v_interrupt_handler
IRQ_5:
    j freertos_risc_v_interrupt_handler
IRQ_6:
    j freertos_risc_v_interrupt_handler
IRQ_7:
    j freertos_risc_v_mtimer_interrupt_handler
IRQ_8:
    j freertos_risc_v_interrupt_handler
IRQ_9:
    j freertos_risc_v_interrupt_handler
IRQ_10:
    j freertos_risc_v_interrupt_handler
IRQ_11:
    j freertos_risc_v_interrupt_handler
IRQ_12:
    j freertos_risc_v_interrupt_handler
IRQ_13:
    j freertos_risc_v_interrupt_handler
IRQ_14:
    j freertos_risc_v_interrupt_handler
IRQ_15:
    j freertos_risc_v_interrupt_handler
IRQ_LC0:
    j freertos_risc_v_interrupt_handler
IRQ_LC1:
    j freertos_risc_v_interrupt_handler
IRQ_LC2:
    j freertos_risc_v_interrupt_handler
IRQ_LC3:
    j freertos_risc_v_interrupt_handler
IRQ_LC4:
    j freertos_risc_v_interrupt_handler
IRQ_LC5:
    j freertos_risc_v_interrupt_handler
IRQ_LC6:
    j freertos_risc_v_interrupt_handler
IRQ_LC7:
    j freertos_risc_v_interrupt_handler
IRQ_LC8:
    j freertos_risc_v_interrupt_handler
IRQ_LC9:
    j freertos_risc_v_interrupt_handler
IRQ_LC10:
    j freertos_risc_v_interrupt_handler
IRQ_LC11:
    j freertos_risc_v_interrupt_handler
IRQ_LC12:
    j freertos_risc_v_interrupt_handler
IRQ_LC13:
    j freertos_risc_v_interrupt_handler
IRQ_LC14:
    j freertos_risc_v_interrupt_handler
IRQ_LC15:
    j freertos_risc_v_interrupt_handler

Don’t know if the problems are the labels (IRQ_7 for example) but that is only the case of a non standard implementation, which I am starting to suspect is my case.

No, this is not correct. A C function generates epilogue and prologue and in this case epilogue will not be called as the assembly function returns. You either need to mark these functions as naked using compiler specific syntax so that epilogue and prologue are not generated OR you need to install freertos_risc_v_interrupt_handler directly as interrupt handlers.

I do not think that would be the case. Can you share your complete project?

For anyone else who stumbles upon the same issue, here are the things we needed to change:

  1. Renesas adds extra information to some bits of the mcause register, deviating from the RISC-V spec which only expects an exception code and interrupt bit. This causes issues for the FreeRTOS RISC-V port which uses the exception code value 11 (ecall) for yield operation. To resolve this, we need to implement a custom trap handler that correctly extracts the exception code from the mcause register:
#include "portContext.h"

.global freertos_renesas_risc_v_trap_handler

.extern vTaskSwitchContext
.extern xTaskIncrementTick
.extern pullMachineTimerCompareRegister
.extern pullNextTime
.extern uxTimerIncrementsForOneTick

.weak freertos_renesas_risc_v_application_exception_handler
.weak freertos_renesas_risc_v_application_interrupt_handler
/*-----------------------------------------------------------*/

.macro portUPDATE_MTIMER_COMPARE_REGISTER
    load_x a0, pullMachineTimerCompareRegister  /* Load address of compare register into a0. */
    load_x a1, pullNextTime                     /* Load the address of ullNextTime into a1. */

    #if( __riscv_xlen == 32 )

        /* Update the 64-bit mtimer compare match value in two 32-bit writes. */
        li a4, -1
        lw a2, 0(a1)                /* Load the low word of ullNextTime into a2. */
        lw a3, 4(a1)                /* Load the high word of ullNextTime into a3. */
        sw a4, 0(a0)                /* Low word no smaller than old value to start with - will be overwritten below. */
        sw a3, 4(a0)                /* Store high word of ullNextTime into compare register.  No smaller than new value. */
        sw a2, 0(a0)                /* Store low word of ullNextTime into compare register. */
        lw t0, uxTimerIncrementsForOneTick  /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */
        add a4, t0, a2              /* Add the low word of ullNextTime to the timer increments for one tick (assumes timer increment for one tick fits in 32-bits). */
        sltu t1, a4, a2             /* See if the sum of low words overflowed (what about the zero case?). */
        add t2, a3, t1              /* Add overflow to high word of ullNextTime. */
        sw a4, 0(a1)                /* Store new low word of ullNextTime. */
        sw t2, 4(a1)                /* Store new high word of ullNextTime. */

    #endif /* __riscv_xlen == 32 */

    #if( __riscv_xlen == 64 )

        /* Update the 64-bit mtimer compare match value. */
        ld t2, 0(a1)                /* Load ullNextTime into t2. */
        sd t2, 0(a0)                /* 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(a1)                /* Store ullNextTime. */

    #endif /* __riscv_xlen == 64 */
    .endm
/*-----------------------------------------------------------*/

freertos_renesas_risc_v_application_exception_handler:
    csrr t0, mcause     /* For viewing in the debugger only. */
    csrr t1, mepc       /* For viewing in the debugger only */
    csrr t2, mstatus    /* For viewing in the debugger only */
    j .
/*-----------------------------------------------------------*/

freertos_renesas_risc_v_application_interrupt_handler:
    csrr t0, mcause     /* For viewing in the debugger only. */
    csrr t1, mepc       /* For viewing in the debugger only */
    csrr t2, mstatus    /* For viewing in the debugger only */
    j .
/*-----------------------------------------------------------*/

.section .text.freertos_renesas_risc_v_trap_handler
.align 8
freertos_renesas_risc_v_trap_handler:
    portcontextSAVE_CONTEXT_INTERNAL

    csrr a0, mcause
    csrr a1, mepc

    bge a0, x0, synchronous_exception

asynchronous_interrupt:
    store_x a1, 0( sp )                 /* Asynchronous interrupt so save unmodified exception return address. */
    load_x sp, xISRStackTop             /* Switch to ISR stack. */
    j handle_interrupt

synchronous_exception:
    addi a1, a1, 4                      /* Synchronous so update exception return address to the instruction after the instruction that generated the exeption. */
    store_x a1, 0( sp )                 /* Save updated exception return address. */
    load_x sp, xISRStackTop             /* Switch to ISR stack. */
    j handle_exception

handle_interrupt:
    test_if_mtimer:                     /* If there is a CLINT then the mtimer is used to generate the tick interrupt. */
        addi t0, x0, 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, application_interrupt_handler

        portUPDATE_MTIMER_COMPARE_REGISTER
        call xTaskIncrementTick
        beqz a0, processed_source       /* Don't switch context if incrementing tick didn't unblock a task. */
        call vTaskSwitchContext
        j processed_source

application_interrupt_handler:
    call freertos_renesas_risc_v_application_interrupt_handler
    j processed_source

handle_exception:
    /* Lower 16 bits of a0 contains exception code. */
    li t0, 0xffff
    and a0, a0, t0
    li t0, 11                                   /* 11 == environment call. */
    bne a0, t0, application_exception_handler   /* Not an M environment call, so some other exception. */
    call vTaskSwitchContext
    j processed_source

application_exception_handler:
    call freertos_renesas_risc_v_application_exception_handler
    j processed_source                  /* No other exceptions handled yet. */

processed_source:
    portcontextRESTORE_CONTEXT
/*-----------------------------------------------------------*/
  1. The auto-generated code contains an incorrect setup for the interrupt vector table. To rectify this, the nvect section code should be updated as follows:
#define EXVECT_SECT          __attribute__ ((section (".nvect"))) __attribute__((naked)) __attribute__((used))
void nvect_function(void) EXVECT_SECT;
void nvect_function(void)
{
    asm( "j freertos_renesas_risc_v_trap_handler" );
};
  1. The value of configCPU_CLOCK_HZ was incorrect.
2 Likes

Thanks for your help. In case you’re wondering, the value for configCPU_CLOCK_HZ should be set to 48000000.