您尚未登录。

楼主 # 2021-10-26 22:21:02

March
会员
注册时间: 2021-05-28
已发帖子: 61
积分: 23

在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

看到全志的论坛上,有大佬移植了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.移植框架
引用大佬的图:
1_20211026-2044.png
需要重点关注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)线程能正常运行几分钟左右后,便会卡死。
0b02482f3bef640cc6b1d02d5330a7b.png
请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?另外,上述的移植过程还有哪里需要完善和改进吗?请大佬指点。

离线

#1 2021-10-27 15:19:12

tugouxp
会员
注册时间: 2018-10-10
已发帖子: 4
积分: 24

Re: 在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

LOAD ACCESS异常,可能是访问到了错误的地址上去了。
查看MEPC寄存器,定位问题源头
查看MBADADDR寄存器,查看访问的错误地址
实际裸机开发中,这种问题会经常遇到,通常是同样的现象对应的不同的问题,细节很多,不能一概而论,处理器的错误号是有限的,但人犯错的原因千奇百怪,要结合SPEC去推敲,灵活应对,没有成法。

离线

楼主 #2 2021-11-01 21:26:10

March
会员
注册时间: 2021-05-28
已发帖子: 61
积分: 23

Re: 在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

由于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”异常了,但是还是会卡死。

离线

楼主 #3 2021-11-01 21:32:01

March
会员
注册时间: 2021-05-28
已发帖子: 61
积分: 23

Re: 在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

源码:
D1_FreeRTOS.rar
注:用的交叉编译工具链是windows版本:
riscv64-elf-mingw-20200720.rar
求指点

离线

#4 2021-11-08 12:55:43

elliott
会员
注册时间: 2021-07-12
已发帖子: 1
积分: 1

Re: 在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

PMP物理内存保护有没有正确呢

离线

#5 2023-10-25 11:57:35

506826164@qq.com
会员
注册时间: 2022-08-07
已发帖子: 29
积分: 54

Re: 在D1裸机的基础上,加入freertos,请问进入“Load access fault”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?

mtime比较寄存器赋值方式有问题

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn