MINI2440 QEMU 的 eCos 启动分析
2024-06-27 来源:elecfans
1.总览一下eCos的启动方式
eCos目前默认的启动方式有三种,RAM,ROM,ROMRAM。
RAM启动的意思就是直接在RAM中运行,这种方式一般用于调试,不做必要的硬件初始化。
ROM启动方式的意思是在ROM中直接运行,当然一定要支持XIP的NORFLASH才可以。
ROMRAM的启动方式的意思是,ROM中开始运行然后把所有的代码复制到RAM中继续运行。
2.Mini2440 QEMU的启动方式的选择
MINI2440中目前还没有实现NOR和NAND Flash,(虽然NAND的代码加了,但是里面注名工作不是很正常,我姑且认为不能用吧)
所以,我们必须在RAM中运行eCos,并且还要完成硬件的初始化任务,这也就有了接下来的一大串的问题。
3.MINI2440 QEMU的启动方式分析
我加点注视来解释下面的ROMRAM启动代码:
// Create MMU tables
RAW_LED_MACRO 3
bl hal_mmu_init
上面的语句会创建MMU页表,会把0x30000000映射到0地址,0地址映射到0x80000000
RAW_LED_MACRO 4
// Enable MMU
ldr r2,=10f
#ifdef CYG_HAL_STARTUP_ROMRAM
ldr r1,=__exception_handlers
ldr r9,=0x80000000
sub r1,r2,r1
add r2,r9,r1 // r9 has ROM offset
这里比较重要,首先我们必须知道我们目前还运行在ROM中,经过这两条语句,我们可以确定r2里面存放的是10这个标号相对于
0x80000000的地址。
#endif
ldr r1,=MMU_Control_Init|MMU_Control_M
mcr MMU_CP,0,r1,MMU_Control,c0
mov pc,r2 /* Change address spaces */
在这里我们看到,MMU被映射后我们把NOR的地址从0映射到了0x80000000,所以我们跳到R2时,就是MMU映射以后在NOR中
的运行效果。
nop
nop
nop
10:
RAW_LED_MACRO 5
#ifdef CYG_HAL_STARTUP_ROMRAM
mov r0,r9 // Relocate FLASH/ROM to RAM
ldr r1,=__exception_handlers // ram base & length
ldr r2,=__rom_data_end
20: ldr r3,[r0],#4
str r3,[r1],#4
cmp r1,r2
bne 20b
ldr r0,=30f
mov pc,r0
这部分代码就比较简单,把ROM中的代码复制到RAM中,然后跳过去。
nop
nop
nop
nop
30:
#endif
4. vector的问题
现在来看我们的QEMU的启动,ecos的一个特点是运行位置只是相对于启动位置,所以无论怎样我们的代码第一条是可以在RAM中跑的。
所以我们让ROMRAM代码直接跑在RAM中可以做硬件的初始化。
接下来的一个问题是,如何保证中断向量工作正常,我们再看两段ARM平台初始化的代码吧,(虽然比较枯燥)
这部分叫fix_vectors
.section '.fixed_vectors'
// Interrupt/exception VSR pointers
.globl hal_vsr_table
hal_vsr_table:
.rept 8
.long 0
.endr
.globl hal_dram_size
hal_dram_size:
.long 0
// what, if anything, hal_dram_type means is up to the platform
.globl hal_dram_type
hal_dram_type:
.long 0
这部分是vectors复制
// Reset software interrupt pointer
ldr r0,=CYGHWR_HAL_VECTOR_TABLE_BASE // move vectors
ldr r1,.__exception_handlers
#if defined(CYG_HAL_STARTUP_RAM) && /
!defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
cmp r7,#CPSR_SUPERVISOR_MODE
beq 10f
#endif
ldr r2,[r1,#HAL_ARM_SWI_VECTOR_ADDR] // software interrupt
str r2,[r0,#HAL_ARM_SWI_VECTOR_ADDR]
10:
ldr r2,[r1,#HAL_ARM_IRQ_VECTOR] // IRQ
str r2,[r0,#HAL_ARM_IRQ_VECTOR]
ldr r2,[r1,#HAL_ARM_IRQ_VECTOR_ADDR]
str r2,[r0,#HAL_ARM_IRQ_VECTOR_ADDR]
ldr r2,[r1,#HAL_ARM_FIQ_VECTOR] // FIQ
str r2,[r0,#HAL_ARM_FIQ_VECTOR]
ldr r2,[r1,#HAL_ARM_FIQ_VECTOR_ADDR]
str r2,[r0,#HAL_ARM_FIQ_VECTOR_ADDR]
ldr r2,[r1,#HAL_ARM_PREFETCH_VECTOR] // abort (prefetch)
str r2,[r0,#HAL_ARM_PREFETCH_VECTOR]
ldr r2,[r1,#HAL_ARM_PREFETCH_VECTOR_ADDR]
str r2,[r0,#HAL_ARM_PREFETCH_VECTOR_ADDR]
ldr r2,[r1,#HAL_ARM_ABORT_VECTOR] // abort (data)
str r2,[r0,#HAL_ARM_ABORT_VECTOR]
ldr r2,[r1,#HAL_ARM_ABORT_VECTOR_ADDR]
str r2,[r0,#HAL_ARM_ABORT_VECTOR_ADDR]
这个是link文件
#include MEMORY { ram : ORIGIN = 0, LENGTH = 0x8000000 sram : ORIGIN = 0x40000000, LENGTH = 0x1000 } SECTIONS { SECTIONS_BEGIN SECTION_fixed_vectors (ram, 0x20, LMA_EQ_VMA) SECTION_rom_vectors (ram, 0x8000, LMA_EQ_VMA) SECTION_RELOCS (ram, ALIGN (0x1), LMA_EQ_VMA) SECTION_text (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_fini (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_rodata (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_rodata1 (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_got (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_fixup (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_gcc_except_table (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_data (ram, ALIGN (0x4), LMA_EQ_VMA) SECTION_bss (ram, ALIGN (0x4), LMA_EQ_VMA) CYG_LABEL_DEFN(__heap1) = ALIGN (0x8); SECTIONS_END } 看到了吧,在我们的RAM中留了一块空地叫fix_vector,我们在启动的时候可以把vector复制过去,然后mmu_init以后 这部分刚好满足ARM920T的中断向量地址。庆幸吧,没修改这里就能用了 。 5.代码复制的问题 最后一个问题是,既然我们本来就在RAM中,我们就不用在做代码复制和NOR重新映射了,我们只要定义新的QEMU启动方式就可以旁路掉 这两部分代码了。 最后只要QEMU运行的时候加载elf格式的文件到0x30000000就可以正常的运行代码了。