看到全志的论坛上,有大佬移植了freertos到D1哪吒开发板上( https://bbs.aw-ol.com/topic/231/freertos-10-4-3%E5%9C%A8riscv-t-head-c906-%E5%B9%B3%E5%8F%B0%E4%B8%8A%E7%A7%BB%E6%A4%8D%E8%BF%87%E7%A8%8B?_=1635232850765 ),想在之前xboot大佬弄的D1的裸机的基础上( https://whycan.com/t_6683.html ),加入freertos。先说说我移植的过程,再说现在还存在的问题。(这里没有sbi,整个FreeRTOS系统运行在M模式,且使用的是windows版的交叉编译工具链)
移植过程:
1.移植框架
引用大佬的图:
需要重点关注portASM.S、start.S、FreeRTOSConfig.h以及freertos_risc_v_chip_specific_extensions.h这几个文件。
portASM.S:主要用于上下文切换;start.S:启动文件;FreeRTOSConfig.h:配置文件,主要是一些宏的定义;freertos_risc_v_chip_specific_extensions.h:RISC-V 端口还需要一个额外的头文件,额外的头文件描述了芯片特定的细节。
2.FreeRTOSConfig.h的设置
FreeRTOSConfig.h其实是一个backup,这里面的的选项在 ./FreeRTOS/Source/include/FreeRTOS.h文件里面都有默认的配置,这里选用.\FreeRTOSv202107.00\FreeRTOS\Demo\RISC-V_RV32M1_Vega_GCC_Eclipse\projects\RTOSDemo_ri5cy文件加下的FreeRTOSConfig.h,修改相关宏,以适应D1.
主要修改如下:
(1)设置mtime寄存器信息
#define configMTIME_BASE_ADDRESS ( 0x1400BFF8 ) //MTIME基地址
#define configMTIMECMP_BASE_ADDRESS ( 0x14004000 ) //MTIME比较寄存器基地址
若无mtime,则将上面的宏定义均设为0。
注:在全志论坛上,有问过MTIME的基地址,想知道具体内容请看https://bbs.aw-ol.com/topic/231/freertos-10-4-3%E5%9C%A8riscv-t-head-c906-%E5%B9%B3%E5%8F%B0%E4%B8%8A%E7%A7%BB%E6%A4%8D%E8%BF%87%E7%A8%8B?_=1635232850765
(2)设置cpu频率和时间滴答的频率
#define configCPU_CLOCK_HZ 24000000
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
(3)设置最小栈大小限制和堆的总大小
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 100 * 1024 ) )
完整FreeRTOSConfig.h如下:
FreeRTOSConfig.rar
3.选择freertos_risc_v_chip_specific_extensions.h文件
官方提供了两类freertos_risc_v_chip_specific_extensions.h供我们选择,一类是拥有CLINT,但没有其他寄存器扩展,另一类是没有CLINT,但有额外的寄存器扩展,这里选择前者,感兴趣的同学可以分别看看这两类文件分别干了些啥,这里就不多赘述了。
4.中断堆栈设置
(1)使用静态分配的数组作为中断堆栈
在FreeRTOSConfig.h 中定义 configISR_STACK_SIZE_WORDS为要分配的中断堆栈的大小。注意,大小是按字定义的,而不是字节。
如
#define configISR_STACK_SIZE_WORDS (500)
(2)或在链接文件中定义中断堆栈
如:
.stack : ALIGN(0x10)
{
__stack_bottom = .;
. += STACK_SIZE;
__stack_top = .;
__freertos_irq_stack_top= .; /* ADDED THIS LINE. */
} > ram
5.设置外部中断处理程序
假设外部中断处理程序为handle_trap,在makefile中增加如下编译参数:-DportasmHANDLE_INTERRUPT=handle_trap
6.install freertos_risc_v_trap_handler()
freertos_risc_v_trap_handler()并且是所有中断和异常的中央入口点,若有CLINT,则会自动install freertos_risc_v_trap_handler(),代码实现在portASM.S文件中,若无CLINT,则需要手install freertos_risc_v_trap_handler()
7.宏定义 __riscv_xlen 指定指令长度
CFLAGS += -D__riscv_xlen=64 -D__riscv64
8.时基
选择mtime作为时基(若没有mtime,则可自己选择一个定时器),在mtime初始化之前(初始化在函数vPortSetupTimerInterrupt()中实现),需要开启mtime。
void clint_timer_init(void)
{
csr_clear(mie, MIE_MTIE | MIE_MSIE);
write32(CLINT + 0x4000, + 24000000);
write32(CLINT + 0x4004, 0);
csr_set(mie, MIE_MTIE);
}
注:在启动文件中,已完成时钟的配置,用24M驱动mtime,外部中断也已在启动文件中开启。
9.start.S
#include <my_linkage.h>
#include <riscv64.h>
.global _start
_start:
/* Boot head information for BROM */
.long 0x0300006f
.byte 'e', 'G', 'O', 'N', '.', 'B', 'T', '0'
.long 0x12345678 /* checksum */
.long __spl_size /* spl size */
.long 0x30 /* boot header size */
.long 0x30303033 /* boot header version */
.long 0x00020000 /* return value */
.long 0x00020000 /* run address */
.long 0x0 /* eGON version */
.byte 0x00, 0x00, 0x00, 0x00 /* platform information - 8byte */
.byte 0x34, 0x2e, 0x30, 0x00
/*
* The actual reset code
*/
reset:
/* Mask all interrupts */
csrw mideleg, zero
csrw medeleg, zero
csrw mie, zero
csrw mip, zero
/* Setup exception vectors */
la t1, _image_start
LREG t1, (t1)
la t2, _start
sub t0, t1, t2
la a0, vectors
add a0, a0, t0
csrw mtvec, a0
/* Enable FPU and accelerator if present */
li t0, MSTATUS_FS | MSTATUS_XS
csrs mstatus, t0
/* Enable theadisaee */
li t1, 0x1 << 22
.word 0x7c032073 /* csrs mxstatus, t1 */
/* Invaild icache/dcache/btb/bht */
li t1, 0x30013
.word 0x7c232073 /* csrs mcor, t1 */
/* Check processor id and initialized */
csrr t0, mhartid
bnez t0, _avoid
la t0, _start
la t1, _image_start
LREG t1, (t1)
beq t0, t1, _avoid
/* Initial system jtag, uart and clock */
/*call sys_jtag_init*/
call sys_uart_init
call sys_clock_init
/* Copy ddr bin to 0x00030000 */
la t1, _ddr_bin_start
LREG t1, (t1)
la t2, _ddr_bin_end
LREG t2, (t2)
sub a2, t2, t1
la t1, _image_start
LREG t1, (t1)
la t2, _ddr_bin_start
LREG t2, (t2)
sub t0, t2, t1
la a1, _start
add a1, a1, t0
li a0, 0x00030000
call my_memcpy
/* Initial ddr controller */
call sys_dram_init
_avoid:
nop
/* Initialize global pointer */
.option push
.option norelax
la t0, _global_pointer$
LREG gp, (t0)
.option pop
/* Initialize stacks */
la t1, _stack_start
LREG t1, (t1)
la t2, _stack_end
LREG t2, (t2)
sub t0, t2, t1
csrr t3, mhartid
li t4, 1
div t0, t0, t4
mul t0, t0, t3
sub sp, t2, t0
/* Check processor id, and startup slave cores */
csrr a0, mhartid
beqz a0, 2f
1: nop
j 1b
2: nop
/* Copyself to link address */
la t0, _start
la t1, _image_start
LREG t1, (t1)
beq t0, t1, 1f
call sys_copyself
1: nop
/* Clear bss section */
la a0, _bss_start
LREG a0, (a0)
la a2, _bss_end
LREG a2, (a2)
sub a2, a2, a0
li a1, 0
call my_memset
li t0,0x800
csrs mie,t0 //开全局中断
li t0, 0x88 /* 加载立即数0x88到t0中*/
csrs mstatus, t0 /* 开机器模式中断*/
/* Call _main */
la t1, _image_start
LREG t1, (t1)
la t2, _start
sub t0, t1, t2
la a0, _main
add a0, a0, t0
jr a0
_main:
call main
/*
* Exception vectors.
*/
.align 4
.globl vectors
vectors:
addi sp, sp, -(32 * REGSZ)
SREG x1, 1 * REGSZ(sp)
SREG x2, 2 * REGSZ(sp)
SREG x3, 3 * REGSZ(sp)
SREG x4, 4 * REGSZ(sp)
SREG x5, 5 * REGSZ(sp)
SREG x6, 6 * REGSZ(sp)
SREG x7, 7 * REGSZ(sp)
SREG x8, 8 * REGSZ(sp)
SREG x9, 9 * REGSZ(sp)
SREG x10, 10 * REGSZ(sp)
SREG x11, 11 * REGSZ(sp)
SREG x12, 12 * REGSZ(sp)
SREG x13, 13 * REGSZ(sp)
SREG x14, 14 * REGSZ(sp)
SREG x15, 15 * REGSZ(sp)
SREG x16, 16 * REGSZ(sp)
SREG x17, 17 * REGSZ(sp)
SREG x18, 18 * REGSZ(sp)
SREG x19, 19 * REGSZ(sp)
SREG x20, 20 * REGSZ(sp)
SREG x21, 21 * REGSZ(sp)
SREG x22, 22 * REGSZ(sp)
SREG x23, 23 * REGSZ(sp)
SREG x24, 24 * REGSZ(sp)
SREG x25, 25 * REGSZ(sp)
SREG x26, 26 * REGSZ(sp)
SREG x27, 27 * REGSZ(sp)
SREG x28, 28 * REGSZ(sp)
SREG x29, 29 * REGSZ(sp)
SREG x30, 30 * REGSZ(sp)
SREG x31, 31 * REGSZ(sp)
csrr a0, mcause
csrr a1, mepc
mv a2, sp
call handle_trap
csrw mepc, a0
LREG x1, 1 * REGSZ(sp)
LREG x2, 2 * REGSZ(sp)
LREG x3, 3 * REGSZ(sp)
LREG x4, 4 * REGSZ(sp)
LREG x5, 5 * REGSZ(sp)
LREG x6, 6 * REGSZ(sp)
LREG x7, 7 * REGSZ(sp)
LREG x8, 8 * REGSZ(sp)
LREG x9, 9 * REGSZ(sp)
LREG x10, 10 * REGSZ(sp)
LREG x11, 11 * REGSZ(sp)
LREG x12, 12 * REGSZ(sp)
LREG x13, 13 * REGSZ(sp)
LREG x14, 14 * REGSZ(sp)
LREG x15, 15 * REGSZ(sp)
LREG x16, 16 * REGSZ(sp)
LREG x17, 17 * REGSZ(sp)
LREG x18, 18 * REGSZ(sp)
LREG x19, 19 * REGSZ(sp)
LREG x20, 20 * REGSZ(sp)
LREG x21, 21 * REGSZ(sp)
LREG x22, 22 * REGSZ(sp)
LREG x23, 23 * REGSZ(sp)
LREG x24, 24 * REGSZ(sp)
LREG x25, 25 * REGSZ(sp)
LREG x26, 26 * REGSZ(sp)
LREG x27, 27 * REGSZ(sp)
LREG x28, 28 * REGSZ(sp)
LREG x29, 29 * REGSZ(sp)
LREG x30, 30 * REGSZ(sp)
LREG x31, 31 * REGSZ(sp)
addi sp, sp, (32 * REGSZ)
mret
.weak handle_trap
handle_trap:
1:
j 1b
/*
* The location of section
*/
.align 3
_image_start:
RVPTR __image_start
_image_end:
RVPTR __image_end
_global_pointer$:
RVPTR __global_pointer$
_data_start:
RVPTR __data_start
_data_end:
RVPTR __data_end
_bss_start:
RVPTR __bss_start
_bss_end:
RVPTR __bss_end
_stack_start:
RVPTR __stack_start
_stack_end:
RVPTR __stack_end
_ddr_bin_start:
RVPTR __ddr_bin_start
_ddr_bin_end:
RVPTR __ddr_bin_end
10.handle_trap实现
void defaultProgarm(void);
uint64_t handle_trap(uint64_t Mcause,uint64_t epc)
{
if(Mcause & (1UL << 63))
{
printf("enter if\n");
unsigned long cause = Mcause & ~(1UL << 63);
unsigned long pending = csr_read(mip) & (1 << cause);
switch(cause)
{
case 0: /* User software interrupt */
case 1: /* Supervisor software interrupt */
case 2: /* Hypervisor software interrupt */
case 3: /* Machine software interrupt */
case 4: /* User timer interrupt */
case 5: /* Supervisor timer interrupt */
case 6: /* Hypervisor timer interrupt */
case 7: /* Machine timer interrupt */
csr_clear(mip, pending);
defaultProgarm();
break;
case 8: /* User external interrupt */
case 9: /* Supervisor external interrupt */
case 10: /* Hypervisor external interrupt */
case 11: /* Machine external interrupt */
csr_clear(mip, pending);
system_irqhandler();
break;
default:
break;
}
}
else
{
printf("Mcause=%x\n",Mcause);
switch(Mcause)
{
case 0x0: /* Misaligned fetch */
case 0x1: /* Fetch access */
case 0x2: /* Illegal instruction */
case 0x3: /* Breakpoint */
defaultProgarm();
break;
case 0x4: /* Misaligned load */
defaultProgarm();
break;
case 0x5: /* Load acces */
defaultProgarm();
break;
case 0x6: /* Misaligned store */
defaultProgarm();
break;
case 0x7: /* Store accesss */
case 0x8: /* User ecall */
case 0x9: /* Supervisor ecall */
case 0xa: /* Hypervisor ecall */
case 0xb: /* Machine ecall */
defaultProgarm();
break;
default:
defaultProgarm();
break;
}
}
return epc;
}
void defaultProgarm(void)
{
printf("defaultProgarm\n");
}
11.创建线程,开启调度
创建三个线程,分别是红灯(1s反转)、LD(3s反转)、绿灯线程(2s反转),然后开启调度。
int main(void)
{
app_init();
}
void app_init(void)
{
//(1)【根据实际需要增删】声明主函数使用的变量(声明时不准赋值)
//(2)初始化全局变量和关总中断
DISABLE_INTERRUPTS;
//(3)【根据实际需要增删】 初始化外设模块
int_init(); //PLIC初始化
clint_timer_init(); //mtime初始化
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
//(4)【根据实际需要增删】 给有关变量赋初值
//(5)【根据实际需要增删】 使能模块中断
//(6)【不变】开总中断
ENABLE_INTERRUPTS;
//(7)【根据实际需要增删】线程创建,不能放在步骤1-6之间
TaskHandle_t thd_redlight = NULL;
TaskHandle_t thd_greenlight = NULL;
TaskHandle_t thd_bluelight = NULL;
xTaskCreate((void *)thread_redlight, "redlight",128, NULL,1, &thd_redlight); //线程栈128*4=512b
xTaskCreate((void *)thread_greenlight, "greenlight",128, NULL,1, &thd_greenlight);
xTaskCreate((void *)thread_bluelight, "bluelight",128, NULL,1, &thd_bluelight);
vTaskStartScheduler(); //启动调度器,开始执行任务
for( ;; )
{
printf("不应该到此处\n");
}
}
存在的问题:
(1)在芯片上电启动后,会开始进入“Load access fault”异常,然后线程会正常调度;
(2)线程能正常运行几分钟左右后,便会卡死。
请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?另外,上述的移植过程还有哪里需要完善和改进吗?请大佬指点。
离线
LOAD ACCESS异常,可能是访问到了错误的地址上去了。
查看MEPC寄存器,定位问题源头
查看MBADADDR寄存器,查看访问的错误地址
实际裸机开发中,这种问题会经常遇到,通常是同样的现象对应的不同的问题,细节很多,不能一概而论,处理器的错误号是有限的,但人犯错的原因千奇百怪,要结合SPEC去推敲,灵活应对,没有成法。
离线
由于C906并没有将Mtime映射出来,所以要修改vPortSetupTimerInterrupt函数,如下:
void vPortSetupTimerInterrupt( void )
{
volatile uint32_t ulHartId;
__asm volatile( "csrr %0, mhartid" : "=r"( ulHartId ) );
pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );
ullNextTime = counter();
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
*pullMachineTimerCompareRegister = ullNextTime;
/* Prepare the time to use after the next tick interrupt. */
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
}
static inline uint64_t counter(void)
{
uint64_t cnt;
__asm__ __volatile__("csrr %0, time\n" : "=r"(cnt) :: "memory");
return cnt;
}
现在线程不会进入“Load access fault”异常了,但是还是会卡死。
离线
源码:
D1_FreeRTOS.rar
注:用的交叉编译工具链是windows版本:
riscv64-elf-mingw-20200720.rar
求指点
离线
PMP物理内存保护有没有正确呢
离线
mtime比较寄存器赋值方式有问题
离线