imx6ull-qemu 裸机教程1:GPIO,IOMUX,I2C

无意间搜到了韦东山老师的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
imx6ull-qemu 裸机教程1:GPIO,IOMUX,I2Cimx6ull-qemu 裸机教程1:GPIO,IOMUX,I2C
我们关心如下几段内存空间:

  • 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启动代码如下:
imx6ull-qemu 裸机教程1:GPIO,IOMUX,I2C

imx6ull-qemu 裸机教程1:GPIO,IOMUX,I2Cimx6ull-qemu 裸机教程1:GPIO,IOMUX,I2C FRAWSCCC 发布了21 篇原创文章 · 获赞 52 · 访问量 8万+ 私信 关注
上一篇:追踪QEMU中PCI设备的初始化过程


下一篇:虚拟化技术实现 — KVM 的 I/O 虚拟化