UCM108E二次开发代码启动分析
简介
UCM108E内置了一颗RISC-V核心的UC8188MCU,所以其实也是在分析UC8188的启动过程。UC8188 是一款高性能 MCU GNSS 多模卫星导航接收机 SoC 芯片。 芯片集成了 RISCV 32 位通用处理器, 数字基带处理器, 射频前端, 具有 4Mb 内嵌的闪存。 拥有 SPI、I2C、 UARTx2 以及其它丰富的外设。
启动过程分析
- MCU启动一般情况下是从reset中断开始的,我们拿到的这颗MCU也是这样,找到它的启动汇编代码,找到reset中断入口
- 分析这段启动代码
/* reset 中断入口 */
reset_handler:
csrw mtvec, x0
csrci mstatus, 0x08
/* 1. 将所有寄存器置位为0 set all registers to zero */
mv x1, x0
mv x2, x1
mv x3, x1
mv x4, x1
mv x5, x1
mv x6, x1
mv x7, x1
mv x8, x1
mv x9, x1
mv x10, x1
mv x11, x1
mv x12, x1
mv x13, x1
mv x14, x1
mv x15, x1
mv x16, x1
mv x17, x1
mv x18, x1
mv x19, x1
mv x20, x1
mv x21, x1
mv x22, x1
mv x23, x1
mv x24, x1
mv x25, x1
mv x26, x1
mv x27, x1
mv x28, x1
mv x29, x1
mv x30, x1
mv x31, x1
#ifdef ARCH_RISCV_FPU
fssr x0
fmv.s.x f0, x0
fmv.s.x f1, x0
fmv.s.x f2, x0
fmv.s.x f3, x0
fmv.s.x f4, x0
fmv.s.x f5, x0
fmv.s.x f6, x0
fmv.s.x f7, x0
fmv.s.x f8, x0
fmv.s.x f9, x0
fmv.s.x f10,x0
fmv.s.x f11,x0
fmv.s.x f12,x0
fmv.s.x f13,x0
fmv.s.x f14,x0
fmv.s.x f15,x0
fmv.s.x f16,x0
fmv.s.x f17,x0
fmv.s.x f18,x0
fmv.s.x f19,x0
fmv.s.x f20,x0
fmv.s.x f21,x0
fmv.s.x f22,x0
fmv.s.x f23,x0
fmv.s.x f24,x0
fmv.s.x f25,x0
fmv.s.x f26,x0
fmv.s.x f27,x0
fmv.s.x f28,x0
fmv.s.x f29,x0
fmv.s.x f30,x0
fmv.s.x f31,x0
#endif
/* 2. 初始化堆栈 stack initilization */
la x2, _stack_start
_start:
.global _start
/* 3. 将bss段清零 clear BSS */
la x26, _bss_start
la x27, _bss_end
bge x26, x27, zero_loop_end
zero_loop:
sw x0, 0(x26)
addi x26, x26, 4
ble x26, x27, zero_loop
zero_loop_end:
/* 4. 运行全局初始化函数 Run global initialization functions */
li a0, 1 /* set app mode */
call set_program_type
call boot_noop
call boot_strap /* 关闭全局中断 配置时钟和XIP */
call __libc_init_array
j main_entry /* 跳转到main_entry 在下面*/
.section .crt0, "ax"
main_entry:
addi x10, x0, 0
/* Baud Rate 156250
*clock divider, SYSCLK/156250/16-1
*5MHZ 1; 50MHZ 19
* 103.68Mhz clk, 115200 sv model 89
* 19.6608Mhz clk, VHD model, value 4. VHD
* 196/2Mhz VHD model value 84 for 115200
*/
//addi x11, x0, 84 //98Mhz, 1152000 for sim
addi x11, x0, 70 //131.072Mhz, 115200 for sim
//addi x11, x0, 22 //26M DCXO, just leave it here, not necessary
//jal uart_set_cfg
/* jump to main program entry point (argc = argv = 0) */
addi x10, x0, 0
addi x11, x0, 0
jal x1, entry /* 跳转到entry函数 */
jal uart_wait_tx_done;
/* if program exits call exit routine from library */
jal x1, exit
jal x1, exit
- 通过以上的分析,我们看到最后是跳转到
entry
函数去了,这个entry
就是rt-thread的入口函数,接下来,我们看在这个函数里做了哪些事情
- 在entry函数里面其实就是调用了
rtthread_startup()
函数,然后,我们重点分析一下这个函数里面都做了哪些事情
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
rt_hw_board_init();
rt_show_version();
rt_system_timer_init();
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
rt_system_signal_init();
#endif
rt_application_init();
rt_system_timer_thread_init();
rt_thread_idle_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif
rt_system_scheduler_start();
return 0;
}
- 通过对以上的分析,我们大概知道rt-thread创建了几个必要的线程并启动了调度器,这个时候就会启动刚才创建的线程,其中用户关心的则是main线程,我们看下main线程里面做了哪些工作
void main_thread_entry(void* parameter)
{
extern int main(void);
#ifdef RT_USING_COMPONENTS_INIT
rt_components_init();
#endif
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif
#if defined(__CC_ARM) || defined(__CLANG_ARM)
{
extern int $Super$$main(void);
$Super$$main();
}
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__)
main();
#endif
}
- 其实这个线程最终是为了调用
main
函数,也就是我们用户编程的入口函数
int main(void)
{
int_disable();
REG_INT_PEND = 0x0;
#ifdef _WTG_OPEN_
wdt_init(UC_WATCHDOG, 5000);
wdt_enable(UC_WATCHDOG);
#endif
InitUart(UART_BSP_115200);
GnssStart(get_pos, 0x7f, FALSE, NULL);
g_hTaskUartTx = rt_thread_create("Task Uart Tx", TaskUartTx, NULL, TSK_STACK_SIZE_UART_TX, 10, 10);
if(g_hTaskUartTx == RT_NULL)
printf("tx task create failed!rn");
else
{
rt_thread_startup(g_hTaskUartTx);
printf("tx task is start!rn");
}
g_hTaskUartRx = rt_thread_create("Task Uart Rx", TaskUartRx, NULL, TSK_STACK_SIZE_UART_RX, 10, 10);
if(g_hTaskUartRx == RT_NULL)
printf("rx task create failed!rn");
else
{
rt_thread_startup(g_hTaskUartRx);
printf("rx task is start!rn");
}
#ifdef _WTG_OPEN_
wdt_feed(UC_WATCHDOG);
#endif
}
- 分析到这里我们应该清楚的知道UCM108E从上电到
main
的整个运行流程了。
如果大家对rt-thread感兴趣,可以访问官网获取更多学习资料。后续还会分享RISC-V任务切换相关知识。