※ The full source code is here.
Continue my last post, this post would show how to make FreeRTOS run on WCH CH32V307 RISC-V MCU based on the direct-interrupt mode.
一. Modify the startup assembly.
we want the RISC-V runs on direct mode, so the most inportant is to set the chip runs on the mode.
Change the end of startup code from:
:
csrs mstatus, t0 la t0, _vector_base ori t0, t0, 3 csrw mtvec, t0 jal SystemInit la t0, main csrw mepc, t0 mret
: csrs mstatus, t0 la t0, _exception_base ori t0, t0, 0 /*exceptions mode*/ csrw mtvec, t0 jal SystemInit jal main
Exception_Handler: : csrr t0, mcause ble t0, x0, interrupt_handler /* Check interrupt */ li t1, 11 /* Find an M mode ecall (11) */ beq t1, t0, ecall_m_handler : ecall_m_handler: : j Ecall_M_Handler interrupt_handler: /* Home made vector table */ slli t0, t0, 3 /* t0 = t0 * 8 */ la t1, _vector_base add t1, t1, t0 lw t0, 4(sp) addi sp, sp, 8 jr t1
:
BaseType_t xPortStartScheduler( void ) { extern void xPortStartFirstTask( void ); #if( configASSERT_DEFINED == 1 ) { volatile uint32_t mtvec = 0; /* Check the least significant two bits of mtvec are 0b11 - indicating multiply vector mode. */ __asm volatile( "csrr %0, mtvec" : "=r"( mtvec ) ); configASSERT( ( mtvec & 0x03UL ) == 0x0 ); /* Check alignment of the interrupt stack - which is the same as the stack that was being used by main() prior to the scheduler being started. */ :
: store_x sp, 0( t0 ) /* Write sp to first TCB member. */ csrr a1, mepc store_x a1, 0( sp ) /* Save updated exception return address. */ addi a1, x0, 0x20 csrs 0x804, a1 load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ :
: store_x sp, 0( t0 ) /* Write sp to first TCB member. */ csrr a0, mcause csrr a1, mepc store_x a1, 0( sp ) j handle_synchronous handle_synchronous: addi a1, a1, 4 /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */ store_x a1, 0( sp ) /* Save updated exception return address. */ test_if_environment_call: li t0, 11 /* 11 == environment call. */ bne a0, t0, is_exception /* Not an M environment call, so some other exception. */ load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ jal vTaskSwitchContext
The function, vPortSetupTimerInterrupt in port.c, originally is :
void vPortSetupTimerInterrupt( void ) { /* set software is lowest priority */ NVIC_SetPriority(Software_IRQn,0xf0); NVIC_EnableIRQ(Software_IRQn); /* set systick is lowest priority */ NVIC_SetPriority(SysTicK_IRQn,0xf0); NVIC_EnableIRQ(SysTicK_IRQn); SysTick->CTLR= 0; SysTick->SR = 0; SysTick->CNT = 0; SysTick->CMP = configCPU_CLOCK_HZ/configTICK_RATE_HZ; SysTick->CTLR= 0xf; }
It prepares the software_interrupt for triggering the context-switching function, freertos_risc_v_trap_handler. it is useless for now :
void vPortSetupTimerInterrupt( void ) { /* Configure SysTick and interrupts. */ SysTick->SR = 0UL; SysTick->CTLR = 0UL; SysTick->CNT = 0UL; NVIC_EnableIRQ(SysTicK_IRQn); SysTick->CMP = (uint64_t)((configCPU_CLOCK_HZ / (configTICK_RATE_HZ * 8)) - 1); SysTick->CTLR = 0x1A; /* COUNTDOWN | AUTO RELOAD | HCLK/8 | INT */ SysTick->CTLR |= 0x20; /* INIT */ SysTick->CTLR |= 0x01; /* EN */ }
And add the interrrupt callback functions in the ch32v30x_it.c, to deal with the two interrupts, system-timers and Ecall_M_Handler :
__NAKED void Ecall_M_Handler(void) { /* Use naked function to generate a short call, without saving stack.*/ asm("j freertos_risc_v_trap_handler"); } extern void vPortSysTick_Handler(void); __IRQ void SysTick_Handler(void) { vPortSysTick_Handler(); }
(of course, we need to rename the function SysTick_Handler in port.c as vPortSysTick_Handler)
四. Test the porting workable :
1. we need to prepare two tasks to verify this porting workable. Below is the task1's implementation, to print out the incremental values :
void task1_task(void *pvParameters) { uint32_t ii = 0; while(1) { xSemaphoreTake(g_mutex, portMAX_DELAY); printf("ch32v307_FreeRTOS task1 entry, %u\r\n", ii++); xSemaphoreGive(g_mutex); const TickType_t task1_delay = 250 / portTICK_PERIOD_MS; GPIO_SetBits(GPIOE, GPIO_Pin_11); vTaskDelay(task1_delay); GPIO_ResetBits(GPIOE, GPIO_Pin_11); vTaskDelay(task1_delay); } }
For task2, it is the same but with the different task_delay value and different GPIO_Pin number.
2. We need to check the interrupt mechansm works on this porting, thus we register the exernal-interrupt and its callback (for we push the button):
main.c :
void EXTI0_INT_INIT(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIO_InitStructure); /* GPIOA ----> EXTI_Line0 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
ch32v30x_it.c :
__IRQ void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0)!=RESET) { printf("Run at EXTI\r\n"); EXTI_ClearITPendingBit(EXTI_Line0); /* Clear Flag */ } }
The main function is straightforward, to setup the used GPIOs, register the external interrupt, initialize UART and FreeRTOS, and create the FreeRTOS tasks.
int main(void) { g_mutex = xSemaphoreCreateMutex(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf("FreeRTOS Kernel Version:%s\r\n",tskKERNEL_VERSION_NUMBER); GPIO_Toggle_INIT(); EXTI0_INT_INIT(); /* create two task */ xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2", (uint16_t )TASK2_STK_SIZE, (void* )NULL, (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t* )&Task2Task_Handler); xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); vTaskStartScheduler(); while(1) { printf("shouldn't run at here!!\n"); } }
While I press the button, the uart output is like below :
The incremental values show the system has run over 17 days (1478504 seconds), it is the evidence for this porting is stable enough.
Reference :
The RISC-V Instruction Set ManualVolume II: Privileged Architecture
RISC-V Exception and Interrupt implementation
FreeRTOS on CH32V307 :: its the reference code
沒有留言:
張貼留言