无意间搜到了韦东山老师的6ul网站,上面有一个6ul的qemu仿真器,下载下来用了用,非常好用,有UI,比原装的qemu-system-arm提供的6ul开发板多了很多功能。
下面贴出的就是韦东山老师的qemu网站:
百问网imx6ull-qemu
但是默认的跑了linux,没有裸机的例程。所以本文写了几个裸机的程序以供参考学习6ul soc上一些外设IP。目的是以最简单的代码来帮助对6ul感兴趣的朋友属性IP的使用。
本教程源码
目标实现以下模块的裸机驱动教程:
- GPIO LED
- GPIO BUTTON
- I2C AT24CXX EEPROM
- GPT定时器
- USDHC SD卡
- eLCDIF
- QuadSPI Flash
注:本文中的驱动只适用于QEMU仿真器上使用,不一定能在真实的芯片上跑起来。原因主要是跟clock,timing有关。因为qemu是纯软件的东西,qemu并没有对timing有严格的要求,所以在本文中所有驱动都没有对clock和timing进行处理。
文章目录
1. 6UL SOC 启动代码编写
1.1 System memory map
首先看看6UL SOC的memory map
我们关心如下几段内存空间:
- 0x0000_0000 - 0x0001_7FFF BootROM。 6ul真实芯片启动的第一条代码是从BootROM 0地址启动,6ul qemu第一条指令也是从0地址开始启动。但是不同的是,6ul qemu并没有BootROM的启动代码,所以我们编写的裸机程序的代码从0地址开始链接,这段ROM空间我们可以用作text段。在真实的芯片上,BootROM会从不同的启动设备上读出Boot Image,典型的如Uboot,然后把Boot Image扔到DDR或者OCRAM上去跑。
- 0x0090_0000 - 0x0097_FFFF OCRAM。这段OCRAM我们可以用来存放data段,bss段和stack段。
- 0x8000_0000 - 0xFFFF_FFFF DDR。这段内存可以用作通用内存,暂时在本文的代码中没用到。
1.2 链接文件
6ul_bare_metal/6ul_bare_metal.ld
ENTRY(reset)
SECTIONS
{
. = 0x00000000;
.startup . : { start.o(.text) }
.text : { *(.text) }
. = 0x00900000;
.data : { *(.data) }
.bss : { *(.bss COMMON) }
. = ALIGN(8);
. = . + 0x8000; /* 32kB of stack memory */
svc_stack_top = .;
}
正如上一节所述,test位于BootROM上,data,bss和stack段位于OCRAM上。其中:
- start.o是启动文件及异常向量表, 所以将其链接到最顶端。
- stack占用32KB大小内存。
1.3 启动汇编代码
6ul_bare_metal/start.S
.align 4
.global reset
.global c_entry
.section .isr_vector
.text
reset:
B reset_handler
B .
B . //SVC
B .
B .
B .
B . //IRQ
B . //FIQ
reset_handler:
ldr r0, =0x00900000
mcr p15,0,r0,c12,c0,0
ldr sp, =svc_stack_top
bl c_entry
b .
reset处存放了arm向量表,除了reset之外其他都是一个死循环。本文所有代码作了以下简化:
- 不使用任何中断,所有驱动都查询中断标志位的方式。
- 所有代码运行在特权模式下。
每一个demo都实现了一种外设,在运行模式下切换没有什么意义,增加了demo的复杂性,所以对所有裸机demo做了以上简化。
启动代码很简单,0地址就是一条B reset_handler的代码,然后reset handler设了一下特权模式下的栈指针,就跳转c函数的入口c_entry中。以下是start.s编译器编出来的代码。
00000000 <reset>:
0: ea000006 b 20 <reset_handler>
4: eafffffe b 4 <reset+0x4>
8: eafffffe b 8 <reset+0x8>
c: eafffffe b c <reset+0xc>
10: eafffffe b 10 <reset+0x10>
14: eafffffe b 14 <reset+0x14>
18: eafffffe b 18 <reset+0x18>
1c: eafffffe b 1c <reset+0x1c>
00000020 <reset_handler>:
20: e3a00609 mov r0, #9437184 ; 0x900000
24: ee0c0f10 mcr 15, 0, r0, cr12, cr0, {0}
28: e59fd004 ldr sp, [pc, #4] ; 34 <reset_handler+0x14>
2c: eb000034 bl 104 <c_entry>
30: eafffffe b 30 <reset_handler+0x10>
34: 00908008 addseq r8, r0, r8
使用cgdb debug启动代码如下: