页次: 1
源码:
D1_FreeRTOS.rar
注:用的交叉编译工具链是windows版本:
riscv64-elf-mingw-20200720.rar
求指点
由于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”异常了,但是还是会卡死。
看到全志的论坛上,有大佬移植了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”异常的原因是什么啊?以及线程卡死,是否与其有关系呢?另外,上述的移植过程还有哪里需要完善和改进吗?请大佬指点。
Makefile :
#
# Top makefile
#
CROSS ?= riscv64-unknown-elf-
NAME := D1
#
# System environment variable.
#
#
# System environment variable.
#
HOSTOS := windows
#
# Load default variables.
#
ASFLAGS := -g -ggdb -Wall -O3
CFLAGS := -g -ggdb -Wall -O3
CXXFLAGS := -g -ggdb -Wall -O3
LDFLAGS := -T 03_MCU/Linker_file/link.ld -nostdlib
ARFLAGS := -rcs
OCFLAGS := -v -O binary
ODFLAGS :=
MCFLAGS := -march=rv64gcvxthead -mabi=lp64d -mcmodel=medlow -fno-stack-protector
LIBDIRS :=
LIBS :=
INCDIRS :=
SRCDIRS :=
ifeq ($(strip $(HOSTOS)), linux)
MKSUNXI := 00_boot0/tools/linux/mksunxi
endif
ifeq ($(strip $(HOSTOS)), windows)
MKSUNXI := 00_boot0/tools/windows/mksunxi
endif
#
# Add external library
#
INCDIRS += 00_boot0/include \
00_boot0 \
02_CPU \
03_MCU \
03_MCU/MCU_drivers \
03_MCU/startup \
04_GEC \
05_UserBoard \
06_SoftComponent \
07_APPprg
SRCDIRS += 00_boot0 \
02_CPU \
03_MCU/MCU_drivers \
03_MCU/startup \
04_GEC \
05_UserBoard \
06_SoftComponent \
07_APPprg
#
# You shouldn't need to change anything below this point.
#
AS := $(CROSS)gcc -x assembler-with-cpp
CC := $(CROSS)gcc
CXX := $(CROSS)g++
LD := $(CROSS)ld
AR := $(CROSS)ar
OC := $(CROSS)objcopy
OD := $(CROSS)objdump
MKDIR := mkdir
CP := cp -af
RM := rm -fr
CD := cd
FIND := find
#
# X variables
#
X_ASFLAGS := $(MCFLAGS) $(ASFLAGS)
X_CFLAGS := $(MCFLAGS) $(CFLAGS)
X_CXXFLAGS := $(MCFLAGS) $(CXXFLAGS)
X_LDFLAGS := $(LDFLAGS)
X_OCFLAGS := $(OCFLAGS)
X_LIBDIRS := $(LIBDIRS)
X_LIBS := $(LIBS) -lgcc
X_OUT := Debug
X_OBJ := obj
X_NAME := $(patsubst %, $(X_OUT)/%, $(NAME))
X_INCDIRS := $(patsubst %, -I %, $(INCDIRS))
X_SRCDIRS := $(patsubst %, %, $(SRCDIRS))
X_OBJDIRS := $(patsubst %, obj/%, $(X_SRCDIRS))
X_SFILES := $(foreach dir, $(X_SRCDIRS), $(wildcard $(dir)/*.S))
X_CFILES := $(foreach dir, $(X_SRCDIRS), $(wildcard $(dir)/*.c))
X_CPPFILES := $(foreach dir, $(X_SRCDIRS), $(wildcard $(dir)/*.cpp))
SFILENDIR := $(notdir $(X_SFILES))
CFILENDIR := $(notdir $(X_CFILES))
CPPFILENDIR := $(notdir $(X_CPPFILES))
X_SDEPS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o.d))
X_CDEPS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o.d))
X_CPPDEPS := $(patsubst %, obj/%, $(CPPFILENDIR:.cpp=.o.d))
X_DEPS := $(X_SDEPS) $(X_CDEPS) $(X_CPPDEPS)
X_SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
X_COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
X_CPPOBJS := $(patsubst %, obj/%, $(CPPFILENDIR:.cpp=.o))
X_OBJS := $(X_SOBJS) $(X_COBJS) $(X_CPPOBJS)
VPATH := $(SRCDIRS)
.PHONY: all clean
all : $(X_NAME)
$(X_NAME) : $(X_OBJS)
@echo [LD] Linking $@.elf
@$(CC) $(X_LDFLAGS) $(X_LIBDIRS) -Wl,--cref,-Map=$@.map $^ -o $@.elf $(X_LIBS)
@echo [OC] Objcopying $@.bin
@$(OC) $(X_OCFLAGS) $@.elf $@.bin
@$(OC) $@.elf -O ihex $@.hex
@$(OD) -D $@.elf > $@.lst
@echo Make header information for brom booting
@$(MKSUNXI) $@.bin
$(X_SOBJS) : obj/%.o : %.S
@echo [AS] $<
@$(AS) $(X_ASFLAGS) -MD -MP -MF $@.d $(X_INCDIRS) -c -O0 $< -o $@
$(X_COBJS) : obj/%.o : %.c
@echo [CC] $<
@$(CC) $(X_CFLAGS) -MD -MP -MF $@.d $(X_INCDIRS) -c -O0 $< -o $@
$(X_CPPOBJS) : obj/%.o : %.cpp
@echo [CXX] $<
@$(CXX) $(X_CXXFLAGS) -MD -MP -MF $@.d $(X_INCDIRS) -c -O0 $< -o $@
clean:
@$(RM) obj $(X_OUT)
#
# Include the dependency files, should be place the last of makefile
#
sinclude $(shell $(MKDIR) $(X_OBJ) $(X_OUT)) $(X_DEPS)
安装平头哥官网提供的riscv64 windows版的交叉编译工具链,通过cmd make命令编译工程,出现如下错误:
Makeflie如下:
Makefile.rar
求大佬解答
参考xboot的代码,加了GPIO中断的相关代码,但是触发不了中断,不知道是哪里出了问题,求帮助。d1-baremetal.rar
楼主,还有这个问题: https://whycan.com/t_6706.html ,有时候DDR信息能输出完整,有时候输出又不完整,很奇怪。
找到问题在哪了,在拷贝代码到DDR中时,地址弄错了,
这里的address应该等于__image_file_start - __spl_start,
而我之前address是__image_file_start
这里主要实现的是将Boot0部分链接到了SRAM里,
而其他程序链接到DDR里,在加载程序到DDR时只加载用户代码,
flash使用的是nor flash,使用xboot大佬的xfel工具进行烧写,这里附上代码。
参考 https://whycan.com/t_5060.html 以及xboot大佬的D1裸机程序,想自己实现一个裸机程序,功能是Boot0部分链接到了SRAM里,而其他程序链接到DDR里,在加载程序到DDR时只加载用户代码,初始化时钟,串口和DDR已经可以了,但是将用户代码拷贝到DDR中后,却跳转不过去,检查了半天,也不知道哪里出现了错误,望解答。
看了xboot大佬的D1裸机程序,有几处看的很迷糊,由于学艺不精,所以来论坛提问,望解惑。
主要问题是在start.s文件中,有几处汇编代码不是很清楚,
1.
此处代码是为了判断当前运行地址是否是链接地址,根据代码:t0中存的是_start的地址,t1中存的是_image_start地址中的内容(8字节),请问是t0中存的链接地址,t1中存的是当前运行地址吗?为啥当前运行地址是存储在_image_start的地址中?
2.
此处拷贝ddr的bin文件,用到了函数memcpy,该函数的第三个参数是len,为什么a2是通过计算_ddr_bin_start和_ddr_bin_end地址中的内容的差来得到长度的呢?不应该是_ddr_bin_end的地址-_ddr_bin_start的地址吗?
fel 模式下下载boot0代码,ddr初始化不成功,这个感觉像主频不匹配?好想下载到nand或者sd卡中自启动试试,有没有办法操作一下
页次: 1