在第二部分中,已经介绍了基于OpenMIPS的SOPC的结构,本文介绍如何将该SOPC下载到Altera的DE2上进行验证,我们的验证程序是模拟一个操作系统的启动过程,包括两个部分:BootLoader、SimpleOS,大家一看名字就理解了,前者是用来加载操作系统的,后者当然就是一个简单的操作系统了,这个操作系统有多简单呢?答:非常简单,只是读取串口收到的数据,然后把数据再发送回去。 BootLoader与SimpleOS都存储在DE2上的flash中,DE2具有4MB的nor flash,可以用来作启动盘,通过SOPC的结构可以知道,flash控制器连接到Wishbone互联矩阵的s3,所以flash的寻址空间是从0x30000000开始的,这也就要求我们的OpenMIPS启动后,要从0x30000000地址处取指,也就是从flash的偏移地址0处开始取指执行。 BootLoader与SimpleOS的代码在flash中的存放方式如下:
BootLoader放在flash从0x0开始的位置,在flash的0x300处存放的是SimpleOS的长度信息,然后从0x304处开始存放SimpleOS。
OpenMIPS启动后,BootLoader首先读取0x300处的长度信息length,然后根据该信息,将从0x304处开始length个字,复制到SDRAM从0x0开始的地方,复制结束后,跳转到0x0地址,将控制权交给SimpleOS,这个过程模拟了目前操作系统的启动过程。
SimpleOS实现了UART的回显,当PC通过UART给OpenMIPS输入数据时,会引发OpenMIPS的中断,OpenMIPS中断处理程序将该数据回送回来。所以该程序还验证了OpenMIPS中断功能的实现与否。
本次试验使用的软硬件工具如下:
- 软件:quartusii 10.1 sp1、DE2的附带光盘、串口调试助手、Ubuntu虚拟机、GCC
- 硬件:Altera的DE2评估板
好了,关于我们实验的基础知识就介绍了完了,下面开始进入正题,分以下几步:
- 硬件连接
- 测试程序讲解
- Makefile与链接文件
- 测试程序编译
- flash烧录
- OpenMIPS工程建立
- 下载测试
1、硬件连接 很简单喽,如下,DE2与PC机的连接如下:
2、测试程序讲解
就两个程序BootLoader.asm、SimpleOS.asm,分别介绍,首先是BootLoader.asm,它的作用的就是读取flash的0x304地址处的一个字,其中是SimpleOS的长度信息length,然后从flash的0x304地址开始的length个字复制到SDRAM中,在我们的SOPC中,SDRAM是挂接在Wishbone互联矩阵的s0接口,所以SDRAM的地址是从0x00000000开始的,BootLoader在复制完毕后,会让处理器跳转到0x0处开始执行,这样就将控制器交给了刚刚复制到SDRAM的SimpleOS。代码如下: .set noat .set noreorder .set nomacro .org 0x0 .text .align 4 .global _start _start: ###################### 设置UART控制器 ##################### lui $1,0x1000 ori $1,$1,0x0003 ori $2,$0,0x80 sb $2,0x0($1) # 向0x10000003地址写入0x80,UART控制器连接到 # Wishbone总线互联矩阵的s1,所以其地址是从0x10000000开始, # 通过查询UART的手册可知,偏移为3的地址对应的是line control寄存器 # 向该寄存器写入0x80,表示下面可以设置分频值(divisor latch) # 通过UART的手册可知,分频值等于系统时钟/(16*波特率),此处设置波特率 # 为9600,DE2上的时钟是27MHz,所以分频值为0xB0
lui $1,0x1000 ori $1,$1,0x0001 ori $2,$0,0x00 sb $2,0x0($1) # 向0x10000001地址写入0x00,此时的line control寄存器是0x80 # 通过查询UART的手册可知,此时向0x1写入0x00,就是设置分频值的MSB # 为0x00
lui $1,0x1000 ori $1,$1,0x0000 ori $2,$0,0xB0 sb $2,0x0($1) # 向0x10000000地址写入0xB0,此时的line control寄存器是0x80 # 通过查询UART的手册可知,此时向0x0写入0xB0,就是设置分频值的LSB # 为0xB0
lui $1,0x1000 ori $1,$1,0x0003 ori $2,$0,0x03 sb $2,0x0($1) # 向0x10000003地址写入0x03,对应的是line control寄存器, # 就是设置串口通信参数,8bit,没有奇偶校验位,1bit停止位
###################### 设置GPIO控制器 ##################### lui $1,0x2000 ori $1,$1,0x0008 lui $2,0xffff ori $2,$2,0xffff sw $2,0x0($1) # 向0x20000008地址写入0xffffffff,GPIO控制器连接到 # Wishbone总线互联矩阵的s2,所以其地址是从0x20000000开始, # 通过查询GPIO的手册可知,偏移为8的地址对应的是RGPIO_OE寄存器 # 向该寄存器写入0xffffffff,表示GPIO的32个输出都使能
lui $1,0x2000 ori $1,$1,0x000c lui $2,0x0000 ori $2,$2,0x0000 sw $2,0x0($1) # 向0x2000000c地址写入0x00000000, # 通过查询GPIO的手册可知,偏移为c的地址对应的是RGPIO_INTE寄存器 # 向该寄存器写入0x00000000,表示中断禁止
###################### 等待SDRAM初始化完毕 ##################### ###在前面介绍过,SDRAM的sdram_init_done输出连接到GPIO,所以可以通过GPIO的输入判断 ###SDRAM是否初始化完毕 _waiting_sdram_init_done: lui $1,0x2000 ori $1,$1,0x0000 lw $4,0x0($1) srl $4,$4,0x10 andi $4,$4,0x0001 beq $4,$0,_waiting_sdram_init_done nop
###################### 打印启动开始信息 ##################### li $1,0x1 la $2,_BootBeginInfoStr la $3,_BootBeginInfoStrLen lb $5,0x0($3) 1: lb $4,0x0($2) jal _print # 通过串口打印寄存器r4的值 addi $2,$2,0x1 bne $5,$0,1b subu $5,$5,$1
###################### 获取SimpleOS的长度 ##################### li $5,0x4 lui $1,0x3000 ori $1,$1,0x0300 # SimpleOS的长度信息存储在flash的0x300处 lw $1,0x0($1) # r1: rom length nop
###################### 从flash向SDRAM复制SimpleOS ################### lui $2,0x0000 # r2: destination address lui $3,0x3000 ori $3,$3,0x0304 # r3: source address 1: lw $4,0x0($3) nop sw $4,0x0($2) addi $2,$2,0x4 addi $3,$3,0x4 nop bgez $1,1b subu $1,$1,$5
###################### 打印启动结束信息 ################### li $1,0x1 la $2,_BootEndInfoStr la $3,_BootEndInfoStrLen lb $5,0x0($3) 1: lb $4,0x0($2) jal _print addi $2,$2,0x1 bne $5,$0,1b sub $5,$5,$1
###################### 跳转到SDRAM ################### jr $0 nop
###################### 串口输出函数 ################### _print: lui $6,0x1000 ori $6,$6,0x0 sb $4,0x0($6) # 向地址0x10000000写入r8的值,0x10000000对应的就是 # 发送缓冲地址 _waiting_transmit_done: lui $6,0x1000 ori $6,$6,0x0005 lb $7,0x0($6) # 读取地址0x10000005的值,对应的是line state寄存器的值
andi $7,$7,0x20 beq $7,$0,_waiting_transmit_done # 如果line state寄存器的第5bit为1,表示发送完毕 nop jr $31 nop
###################### 一些预定义常量 ################### .data _BootBeginInfoStr: .ascii "Loading OS into SDRAM...\n" _BootBeginInfoStrLen: .byte 26 _BootEndInfoStr: .ascii "Load OS into SDRAM DONE!!!\n" _BootEndInfoStrLen: .byte 28
SimpleOS.asm的主要作用是当串口收中断时,在中断处理例程中将串口收的数据再通过串口发送出去,实现回显功能,其代码如下:
.org 0x0 .set noat .set noreorder .set nomacro .global _start _start: ######################## SimpleOS的启动代码 ############################ ori $1,$0,0x100 jr $1 # 跳转到0x100处,因为0x100地址以下是一些中断处理的入口
######################## 外部中断处理例程 ############################ .org 0x20 # 0x20是外部中断的处理例程入口地址 mfc0 $1,$12,0x0 andi $4,$1,0x0100 bne $4,$0,_int2 # 判断是否是UART输入中断,如果是,则跳转到_int2 nop eret
######################## UART控制器初始化 ############################ .org 0x100 lui $1,0x1000 ori $1,$1,0x0003 ori $2,$0,0x80 sb $2,0x0($1)
lui $1,0x1000 ori $1,$1,0x0001 ori $2,$0,0x00 sb $2,0x0($1) # MSB of divisor latch
lui $1,0x1000 ori $1,$1,0x0000 ori $2,$0,0xB0 sb $2,0x0($1) # LSB of divisor latch
lui $1,0x1000 ori $1,$1,0x0003 ori $2,$0,0x03 sb $2,0x0($1) # 8bit, no parity, 1 stop bit
lui $1,0x1000 ori $1,$1,0x0001 ori $2,$0,0x01 sb $2,0x0($1) # 设置UART控制器在收到数据时送出中断信号
###################### 设置OpenMIPS的status寄存器 ##################### lui $1,0x1000 ori $1,$1,0x0101 # 使能串口中断,在OpenMIPS_min_sopc中串口中断对应的是 # 中断输入的第1bit mtc0 $1,$12,0x0
###################### 循环等待 ##################### _loop: j _loop nop
###################### 处理串口中断的函数 ################# _int2: lui $1,0x1000 ori $1,$1,0x0005 lb $3,0x0($1) # get line status register andi $3,$3,0x01 # 通过line status寄存器是否有数据输入 beq $3,$0,_end # 如果没有则跳转到_end,结束中断处理 nop
_sendback: # 有输入数据,将输入数据再通过串口输出 lui $1,0x1000 ori $1,$1,0x0000 lb $2,0x0($1) sb $2,0x0($1)
_loop2: lui $1,0x1000 ori $1,$1,0x0005 lb $2,0x0($1) # get line status register
andi $2,$2,0x20 beq $2,$0,_loop2 nop
lui $1,0x1000 ori $1,$1,0x0005 lb $3,0x0($1) andi $3,$3,0x01 bne $3,$0,_sendback nop
_end: eret nop
3、Makefile与链接文件
好了,在上一节介绍了测试程序,本节给出编译上面的测试程序需要的Makefile与链接文件,注意的是:编译BootLoader与SimpleOS需要不同的Makefile与链接文件,下面主要给出编译BootLoader时要用的Makefile与链接脚本,对编译SimpleOS的不同之处,会给出说明。 首先是链接文件,命名为ram.ld,内容如下:
MEMORY { ram : ORIGIN = 0x30000000 , LENGTH = 0x00000300 }
SECTIONS { .text : { *(.text) } > ram
.data : { *(.data) } > ram
.bss : { *(.bss) } > ram
.stack ALIGN(0x10) (NOLOAD): { *(.stack) _ram_end = .; } > ram }
ENTRY (_start)
这个链接文件是编译BootLoader时需要的,因为BootLoader在flash中执行,而flash的起始地址是0x30000000,所以在上面的链接文件中ram的ORIGIN是0x30000000。相应的,编译SimpleOS时的链接文件就要改变,因为SimpleOS是在SDRAM中运行,SDRAM的起始地址是0x00000000,所以编译SimpleOS的链接文件中的ram的ORIGIN是0x00000000,这一点要注意区分。 编译BootLoader时的Makefile文件如下:
ifndef CROSS_COMPILE CROSS_COMPILE = mips-sde-elf- endif CC = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump
OBJECTS = BootLoader.o
exportCROSS_COMPILE
# ******************** # Rules of Compilation # ********************
all: BootLoader.om BootLoader.bin BootLoader.asm
%.o: %.S $(CC) -mips32 $< -o [email protected] BootLoader.om: ram.ld $(OBJECTS) $(LD) -T ram.ld $(OBJECTS) -o [email protected] BootLoader.bin: BootLoader.om $(OBJCOPY) -O binary $< [email protected] BootLoader.asm: BootLoader.om $(OBJDUMP) -D $< > [email protected] clean: rm -f *.o *.om *.bin *.data *.mif *.asm
注意上面在编译的时候加上-mips32选项。最后得到的二进制文件BootLoader.bin。编译SimpleOS对应的Makefile与上面类似,只需要将BootLoader都改为SimpleOS即可。
4、测试程序编译
好了,有了源程序、Makefile、链接文件,我们可以编译测试程序了。要在Ubuntu虚拟机中编译,虚拟机中要已经安装好了增对MIPS架构的GCC编译器,具体安装方法我就不再详述了,这篇博文已经很长了,可以参考http://bbs.eetop.cn/thread-429161-1-1.html,在OpenMIPS教学版的文档中也就介绍http://bbs.elecfans.com/jishu_411089_1_1.html。 在Ubuntu中,将Makefile、链接文件、源程序放在一个文件夹下(当然BootLoader与SimpleOS要分别编译,所以需要两个文件夹),打开终端,使用cd命令进入BootLoader所在目录,输入make all,然后再进入SimpleOS所在目录,输入make all,这样我们就编译结束了。 编译结束后,得到两个文件:BootLoader.bin、SimpleOS.bin。在本文最开始的时候介绍过,flash中存放是有规范的,所以此时需要利用我们提供的一个小工具BinMerge.exe,将编译得到两个文件合并起来,并且符合规范。步骤如下:将编译得到的两个bin文件与BinMerge.exe复制到同一个目录下,然后打开终端,进入这个目录,输入如下命令:
./BinMerge.exe –f SimpleOS.bin –o OSImage.bin
如此,就得到最终的二进制文件OSImage.bin。
5、flash烧录
在之前的介绍中可以知道,OpenMIPS从flash启动,所以需要将测试程序烧录到flash中。DE2评估板上提供了一个4MB的flash,且是nor flash,可以作为启动盘使用。在烧录程序到flash之前,需要先erase flash,这是flash的特性决定的。 首先打开quartusii的programmer工具,选择DE2_USB_API.sof,作为要下载目标板上的文件(注意不要在路径中有中文),该文件是DE2附带光盘提供的(在OpenMIPS实践版的发布中也包含了该文件)。如下:
下载完成后,打开DE2附带光盘的DE2_Control_Panel.exe程序(在OpenMIPS实践版的发布中也包含了该文件),选择Open菜单,点击Open USB Port,然后点击FLASH这个Tab,点击Chip Erase按钮,将erase flash,该过程大约40秒钟。
然后选中File Length前面的复选框,点击“Write a File to FLASH”按钮,选择在上一步节中得到的OSImage.bin文件,就会将该文件写入flash中。这样flash就烧录完毕了。
注意此时需要点击Open菜单,然后点击“Close USB Port”,否则下面的步骤无法进行。
6、OpenMIPS工程建立
打开quartusii,新建一个工程文件,添加OpenMIPS实践版的rtl目录下、min_sopc目录下的所有文件,如下:
使用cycloneii的EP2C35F672C6作为目标器件,这是DE2上的FPGA型号,这样就完成了quartusii工程建立,然后可以编译工程,编译完成后设置引脚信息,其中使用DE2上的27MHz时钟作为输入时钟,GPIO口连接到4个7段数码管,OpenMIPS的reset输入连接到拨码开关sw17,同时还还设置flash、SDRAM的连接。
在OpenMISP实践版的tools/Altera/DE2目录下提供了一个文件“引脚配置.txt”,其中有DE2上的引脚配置文件,可以直接将该文件复制到OpenMIPS_min_sopc.qsf中,即可完成引脚配置。配置完引脚后,需要再次编译工程。得到OpenMIPS_min_sopc.sof文件。
7、下载测试
打开quartueii的programmer工具,选择在10.2节得到的OpenMIPS_min_sopc.sof,将其下载到DE2上,下载完成后,拨动SW17开关,先向上拨一下,对应reset为高电平,OpenMIPS在复位,打开串口调试程序,然后再将sw17拨到下面,对应reset无效,此时OpenMIPS开始工作。会出现如下界面:
上面是BootLoader启动界面,下面是SimpleOS运行时的回显界面:
好了,小伙伴们,OpenMIPS实践版验证结束了,下一步就是移植ucos-II了,未完待续哦!