之前我们写过OR的裸机程序,写过基于OR的linux设备驱动程序,也反汇编过OR的机器码。
本小节,我们将通过一个简单的实验,对OR的汇编(指令集)做一个简单的梳理和测试。
要想了解OR的指令集,其实只要查查OpenRISC architecture manual就可以了,但是不是最好的熟悉方式,也没有必要将其所有指令集记下来。我认为,通过一个实际的工程或者例子,从中了解OR的指令集是比较好的一种方式。
那么,通过什么例子呢?
一般RISC的指令集包括,运算指令,分支指令,和访存指令,三类。所以我们这个实验最好能用到这三类指令,碰巧,我最近在测试DDR控制器,所以我们下面将通过一个读写内存的例子来熟悉OR的指令集。
在开始编码之前,我们首先需要考虑如下几个问题:
a,如何对汇编源程序(.S文件)进行编译,生成机器码。
b,如何加载机器码使OR的指令总线能读到。
c,如何查看机器码的执行过程和结果。
根据前面,我们积累下来的经验,可知,利用bootrom进行RTL仿真,可以很好的解决上面三个问题。并且都是自动化的。
在了解并解决了上面几个问题之后,剩下的就是具体的操作了。
a,修改soc-design/orpsocv2/sw/bootrom目录下的board.h
#ifndef _BOARD_H_ #define _BOARD_H_ //#define IN_CLK 50000000 // Hz #define IN_CLK 66666667 // Hz #define PRELOAD_RAM #define BOOTROM_MEM_TEST // // ROM bootloader // // Uncomment the appropriate bootloader define. This will effect the bootrom.S // file, which is compiled and converted into Verilog for inclusion at // synthesis time. See bootloader/bootloader.S for details on each option. #ifndef PRELOAD_RAM #define BOOTROM_SPI_FLASH //#define BOOTROM_GOTO_RESET //#define BOOTROM_LOOP_AT_ZERO //#define BOOTROM_LOOP_IN_ROM #else #ifdef BOOTROM_MEM_TEST #define BOOTROM_MEM_TEST1 #else #define BOOTROM_GOTO_RESET #endif #endif // Address bootloader should start from in FLASH // Last 256KB of 2MB flash - offset 0x1c0000 (2MB-256KB) #define BOOTROM_ADDR_BYTE2 0x1c #define BOOTROM_ADDR_BYTE1 0x00 #define BOOTROM_ADDR_BYTE0 0x00 // Causes SPI bootloader to loop if SPI didn‘t give correct size of image #define SPI_RETRY_IF_INSANE_SIZEWORD // // Defines for each core (memory map base, OR1200 interrupt line number, etc.) // #define SDRAM_BASE 0x0 #define GPIO_0_BASE 0x91000000 #define UART0_BASE 0x90000000 #define UART0_IRQ 2 #define UART0_BAUD_RATE 115200 #define SPI0_BASE 0xb0000000 #define SPI0_IRQ 6 #define I2C_0_BASE 0xa0000000 #define I2C_0_IRQ 10 #define I2C_1_BASE 0xa1000000 #define I2C_1_IRQ 11 #define ETH0_BASE 0x92000000 #define ETH0_IRQ 4 #define ETH_MACADDR0 0x00 #define ETH_MACADDR1 0x12 #define ETH_MACADDR2 0x34 #define ETH_MACADDR3 0x56 #define ETH_MACADDR4 0x78 #define ETH_MACADDR5 0x9a // // OR1200 tick timer period define // #define TICKS_PER_SEC 100 // // CFI flash controller base // #define CFI_CTRL_BASE 0xf0000000 // // UART driver configuration // #define UART_NUM_CORES 1 #define UART_BASE_ADDRESSES_CSV UART0_BASE #define UART_BAUD_RATES_CSV UART0_BAUD_RATE // // i2c_master_slave core driver configuration // #define I2C_MASTER_SLAVE_NUM_CORES 2 #define I2C_MASTER_SLAVE_BASE_ADDRESSES_CSV I2C_0_BASE, I2C_1_BASE #endif
b,修改soc-design/orpsocv2/sw/bootrom目录下的bootrom.S
这个文件,是需要我们自己手动编写OR的汇编了。
需要注意的有几点。
首先,如何让仿真自动退出:使用“l.nop 1”这条指令,具体原因可参考or1200_monitor.v文件中的相关内容。
其次,如何判断读写内存的成功与失败,分别使用“l.nop 4”和“l.nop 3”。
最后,这些汇编,都很容易理解,唯一需要注意的是OR支持延迟槽。
下面是具体代码清单:
////////////////////////////////////////////////////////////////////// /// //// /// bootrom Rill 2014-04-28 //// /// //// /// Assembly programs to be embedded inside system to aid boot //// /// //// /// //// ////////////////////////////////////////////////////////////////////// // Defines for which bootrom app to use are in board.h - TODO: use the // processed orspoc-defines.v file for this define. It makes more sense // as this software ends up as gates. #include "board.h" #ifdef BOOTROM_MEM_TEST1 #define RAM_LOAD_BASE 0x100000 #define RESET_ADDR 0x100 #define RAM_LOAD_VALUE 0x5555 #define RAM_LOAD_SIZE 0xc boot_init: l.movhi r0, 0 /*0*/ l.movhi r1, RAM_LOAD_BASE /*1*/ mem_write: l.movhi r6, 0 /*2*/ /* counter*/ l.ori r7, r0, RAM_LOAD_SIZE /*3*/ //l.movhi r7, RAM_LOAD_SIZE l.movhi r8, 0 /*4*/ /* load addr index*/ write: l.ori r3,r0, RAM_LOAD_VALUE /*5*/ /* Read a byte into r3 */ l.add r8, r1, r6 /*6*/ /* Calculate store address */ l.sw 0(r8), r3 /*7*/ /* Write byte to memory */ l.addi r6, r6, 4 /*8*/ /* Increment counter */ l.sfeq r6, r7 /*9*/ /* Check if we‘ve finished loading the words */ l.bnf write /*10*/ /* Continue copying if not last word */ l.nop /*11*/ mem_read: l.movhi r6, 0 l.ori r7, r0, RAM_LOAD_SIZE l.movhi r8, 0 read: l.add r8, r1, r6 l.lwz r3, 0(r8) l.j check l.nop read2: l.addi r6, r6, 4 l.sfeq r6, r7 l.bnf read l.nop l.j success l.nop check: l.ori r5,r0, RAM_LOAD_VALUE l.sfeq r3, r5 l.bnf error l.nop l.j read2 l.nop error: l.nop 3 l.j error l.nop 1 success: l.nop 4 l.j success l.nop 1 #endif
c,创建C语言裸机测试目录和程序
由于我们想利用ORPSoC现成的测试环境,所以我们还需要编写一个假的,只是为了满足原有的测试环境的目录和文件。
在soc-design/orpsocv2/board/xilinx/ml501/sw/tests目录下创建目录和文件:
mkdir mem cd mem touch mem.c cp ../../Makefile .
编辑mem.c:
/* * Rill * april/28/2014 */ int main() { while(1);//we run instructions in bootrom ONLY. return 0; }
由于我们只运行bootrom.S中的指令,所以mem.c里面写什么语句不重要。
d,测试与验证
完成编码之后,我们就可以运行我们刚才写的汇编程序了,方法我们之前已经介绍过多次了,这里重复如下:
在 soc-design/orpsocv2/board/xilinx/ml501/sim/run目录下执行如下:
make rtl-test TEST=mem VCD=1
很快,我们就可以仿真结束,然后,我们就可以查看仿真日志文件和仿真波形。
从中可以看出,对内存的读写都是正确的。
or1200_monitor输出的日志文件:mem-general.log
58598000.0 ps: l.nop 4 putc (U) 58718000.0 ps: l.nop exit (00005555)
vcd波形文件:
对于cpu来说,对所有外设(uart,i2c,vga......)的控制都可以通过load/store来完成,和读写内存的实质是完全相同的,所以,通过上面的汇编,我们只要改变一下读写地址修改成对应外设的地址,我们就可以实现对该设备的控制了。
当然,编写出优秀的汇编程序也不是一件太容易的事情,这就需要详细阅读OR的架构手册,掌握GPR和SPR的使用方法。甚至,我们还要查看or32-elf-asm来了解更准确的OR的指令集。
OpenRisc-67-OR的汇编,码迷,mamicode.com
原文地址:http://blog.csdn.net/rill_zhen/article/details/24661175