天天看点

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

在第二部分中,已经介绍了基于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中的存放方式如下:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

       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评估板

       好了,关于我们实验的基础知识就介绍了完了,下面开始进入正题,分以下几步:

  1. 硬件连接
  2. 测试程序讲解
  3. Makefile与链接文件
  4. 测试程序编译
  5. flash烧录
  6. OpenMIPS工程建立
  7. 下载测试

1、硬件连接        很简单喽,如下,DE2与PC机的连接如下:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

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实践版的发布中也包含了该文件)。如下:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

       下载完成后,打开DE2附带光盘的DE2_Control_Panel.exe程序(在OpenMIPS实践版的发布中也包含了该文件),选择Open菜单,点击Open USB Port,然后点击FLASH这个Tab,点击Chip Erase按钮,将erase flash,该过程大约40秒钟。

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

       然后选中File Length前面的复选框,点击“Write a File to FLASH”按钮,选择在上一步节中得到的OSImage.bin文件,就会将该文件写入flash中。这样flash就烧录完毕了。

       注意此时需要点击Open菜单,然后点击“Close USB Port”,否则下面的步骤无法进行。

6、OpenMIPS工程建立

       打开quartusii,新建一个工程文件,添加OpenMIPS实践版的rtl目录下、min_sopc目录下的所有文件,如下:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

       使用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开始工作。会出现如下界面:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

       上面是BootLoader启动界面,下面是SimpleOS运行时的回显界面:

自制处理器OpenMIPS移植ucos-II过程之3——DE2验证OpenMIPS

      好了,小伙伴们,OpenMIPS实践版验证结束了,下一步就是移植ucos-II了,未完待续哦!