orpsocv2 从ROM(bootrom)启动分析--以atlys板子的启动为例子
1 复位后的启动地址
1) 复位后,启动地址在or1200_defines.v最后宏定义,atlys板子的目录:orpsocv2\boards\xilinx\atlys\rtl\verilog\include\or1200_defines.v
/////////////////////////////////////////////////////////////////////////////// // Boot Address Selection // // // // Allows a definable boot address, potentially different to the usual reset // // vector to allow for power-on code to be run, if desired. // // // // OR1200_BOOT_ADR should be the 32-bit address of the boot location // // OR1200_BOOT_PCREG_DEFAULT should be ((OR1200_BOOT_ADR-4)>>2) // // // // For default reset behavior uncomment the settings under the "Boot 0x100" // // comment below. // // // /////////////////////////////////////////////////////////////////////////////// // Boot from 0xf0000100 `define OR1200_BOOT_PCREG_DEFAULT 30'h3c00003f `define OR1200_BOOT_ADR 32'hf0000100 // Boot from 0x100 // `define OR1200_BOOT_PCREG_DEFAULT 30'h0000003f // `define OR1200_BOOT_ADR 32'h00000100
OR1200_BOOT_ADR[31:28] (即最高4位)是 wishbone的从机地址,对应了wishbone从机ROM设备的从机地址。
从设备的内部地址是 0x100,即对应OR启动的地址。
OR1200_BOOT_PCREG_DEFAULT = ((OR1200_BOOT_ADR-4)>>2) = (boot_adr >>2) - 4 = (f0000100>>2) - 4 =3c00003f;
在 genpc模块中复位时,即在复位时,当boot_adr = 0xf0000100时,pcreg_default = 3c00003f 计算得到,故在此版本中 OR1200_BOOT_PCREG_DEFAULT 宏并未用到。而在某些版本中是实现的 pcreg_default <= `OR1200_BOOT_PCREG_DEFAULT; //jb 参考书《深入理解OpenRisc 体系结构》中代码是这样的
// // PC register // always @(posedge clk or `OR1200_RST_EVENT rst) // default value if (rst == `OR1200_RST_VALUE) begin pcreg_default <= (boot_adr >>2) - 4; pcreg_select <= 1'b1;// select async. value due to reset state end // selected value (different from default) is written into FF after // reset state else if (pcreg_select) begin // dynamic value can only be assigned to FF out of reset! pcreg_default <= pcreg_boot[31:2]; pcreg_select <= 1'b0; // select FF value end else if (spr_pc_we) begin pcreg_default <= spr_dat_i[31:2]; end else if (no_more_dslot | except_start | !genpc_freeze & !icpu_rty_i & !genpc_refetch) begin pcreg_default <= pc[31:2]; end always @(pcreg_boot or pcreg_default or pcreg_select) if (pcreg_select) // async. value is selected due to reset state pcreg = pcreg_boot[31:2]; else // FF value is selected 2nd clock after reset state pcreg = pcreg_default ;
2)在文件 orpsocv2\boards\xilinx\atlys\rtl\verilog\include\orpsoc-params.v
定义了 rom 的地址宽度和从机地址,注意这里的地址宽度参数重定义为 6,在rom.v 中定义的是5,不要看错了。所以在 rom(bootrom.v)中的指令最多为 2^6 = 64条指令。
3) 第一条指令?
复位后PC=0X100,则传递到rom.v中后,
则wishbone地址输入定义为: input[7:2] wb_adr_i; (共6位地址线)
在文件 orpsocv2\boards\xilinx\atlys\rtl\verilog\orpsoc_top.v 中,实例化rom时,从rom中取指令的地址截取的是 wbs_i_rom_adr_i[7:2](共6位地址线),
其中wbs_i_rom0_addr_width = 6,定义在orpsoc-params .v
所以:在PC=0X100时,传递到ROM中取指令时,取的是 wb_adr_i = 0000_00 (PC[7:2]);
然后在不遇到异常的情况下,PC每次加4。则第二条指令PC = 0X104,传递到ROM中取指令时,取的是 wb_adr_i = 0000_01 (PC[7:2]); 所以bootrom.v 中的指令要在64条以内。
对于原rom.v 中注释指令的反汇编解释:
/*
// Zero r0 and jump to 0x00000100
0 : wb_dat_o <= 32'h18000000; //opcode=0x6; D=0; K=0x0000; --> r0=0x00000000
1 : wb_dat_o <= 32'hA8200000; //opcode=0x2a; rD=r1; A=r0; K= 0x0000; --> r1=r0|0x0000=0x00000000
2 : wb_dat_o <= 32'hA8C00100; //r6 = r0|0x0100=0x0100
3 : wb_dat_o <= 32'44003000; //opcode=0x11; B=0x06; --> PC=r6=0x00000100 (注意最高4位是Wishbone从机地址,这里不是f,即不对应ROM了,而是外部SDRAM,从机地址为0)
4 : wb_dat_o <= 32'h15000000;//opcode=0x15; nop
*/
验证,在Ubuntu虚拟机中通过编译工具查看下,先写出以下汇编代码hyexp.S:
.section .text,"ax" .org 0x100 .global _start _start: l.movhi r0,0 l.ori r1,r0,0 l.ori r6,r0,0x0100 l.jr r6 l.nop 0x0000
汇编工具编译,再反汇编查看:
可看出汇编指令对应的 机器码 和rom中默认的是一样的。故指令的作用如下分析:
第0条:l.movhi r0,0x00 ;; 其中R0恒为0(所以一开始就清R0,
第1条:l.ori r1, r0, 0x00 ;; R1是栈指针SP也清零
第3条:l.ori r6, r0, 0x0100 ;; R6 = R0 | 0X0100 = 0X100;
第4条:l.jr r6 ;; PC = r6 强行跳转,(注意最高4位是Wishbone从机地址,这里不是f,即不对应ROM了,而是外部SDRAM,从机地址为0)
第5条:nop ;; 由于是流水线结构,导致在跳转指令后的那条语句必被执行,所以填充一个 nop ;
综上分析可知:按默认的指令来的话,OR1200 “复位->清R0,R1(SP) ->跳到SDRAM里面的复位向量地址0x100处取指令,如果SDRAM中没有初始化数据,即为0x0,对应l.j 0 指令。
atlys bootrom.v 的指令,这是uboot启动的引导,应该有SPI flash 的初始代码,之后会引导PC跳转到flash中的相关代码去等:
0 : wb_dat_o <= 32'h18000000; 1 : wb_dat_o <= 32'h18200000; 2 : wb_dat_o <= 32'h1880b000; 3 : wb_dat_o <= 32'ha8400051; 4 : wb_dat_o <= 32'hd8041000; 5 : wb_dat_o <= 32'hd8040004; 6 : wb_dat_o <= 32'ha8c00001; 7 : wb_dat_o <= 32'hd8043004; 8 : wb_dat_o <= 32'h0400001d; 9 : wb_dat_o <= 32'ha8600003; 10 : wb_dat_o <= 32'h0400001b; 11 : wb_dat_o <= 32'ha860001c; 12 : wb_dat_o <= 32'h04000019; 13 : wb_dat_o <= 32'ha8600000; 14 : wb_dat_o <= 32'h04000017; 15 : wb_dat_o <= 32'ha8600000; 16 : wb_dat_o <= 32'h18c00000; 17 : wb_dat_o <= 32'h18e0ffff; 18 : wb_dat_o <= 32'h04000013; 19 : wb_dat_o <= 32'he1013000; 20 : wb_dat_o <= 32'hd8081800; 21 : wb_dat_o <= 32'h9cc60001; 22 : wb_dat_o <= 32'hbc060004; 23 : wb_dat_o <= 32'h10000007; 24 : wb_dat_o <= 32'he4063800; 25 : wb_dat_o <= 32'h0ffffff9; 26 : wb_dat_o <= 32'h15000000; 27 : wb_dat_o <= 32'ha8210100; 28 : wb_dat_o <= 32'h44000800; 29 : wb_dat_o <= 32'hd8040004; 30 : wb_dat_o <= 32'h84e10000; 31 : wb_dat_o <= 32'hb9470050; 32 : wb_dat_o <= 32'hbc4a0200; 33 : wb_dat_o <= 32'h13ffffdf; 34 : wb_dat_o <= 32'h15000000; 35 : wb_dat_o <= 32'h03ffffef; 36 : wb_dat_o <= 32'h15000000; 37 : wb_dat_o <= 32'hd8041802; 38 : wb_dat_o <= 32'ha8600001; 39 : wb_dat_o <= 32'ha4630001; 40 : wb_dat_o <= 32'hbc030001; 41 : wb_dat_o <= 32'h13fffffe; 42 : wb_dat_o <= 32'h8c640001; 43 : wb_dat_o <= 32'h44004800; 44 : wb_dat_o <= 32'h8c640002;
以上的指令码是由 bootrom.S 文件(openrisc\trunk\orpsocv2\sw\bootrom\bootrom.S)和对应的板子 board.h (openrisc\trunk\orpsocv2\boards\xilinx\atlys\sw\board\include\board.h) 生成的。
BOOTROM_SPI_FLASH 部分
#include "board.h" #ifdef BOOTROM_SPI_FLASH /* Assembly program to go into the boot ROM */ /* For use with simple_spi SPI master core and standard SPI flash interface-compatible parts (ST M25P16 for example.)*/ /* Currently just loads a program from SPI flash into RAM */ /* Assuming address at RAM_LOAD_BASE gets clobbered, we need a byte writable address somewhere!*/ #define SPI_BASE SPI0_BASE /* simple_spi driver */ #define SPI_SPCR 0x0 #define SPI_SPSR 0x1 #define SPI_SPDR 0x2 #define SPI_SPER 0x3 #define SPI_SPSS 0x4 #define SPI_SPCR_XFER_GO 0x51 #define SPI_SPSS_INIT 0x1 #define SPI_SPSR_RX_CHECK 0x01 /* Check bit 0 is cleared, fifo !empty*/ #define RAM_LOAD_BASE SDRAM_BASE #define RESET_ADDR 0x100
boot_init: l.movhi r0, 0 l.movhi r1, RAM_LOAD_BASE l.movhi r4, hi(SPI_BASE)
r0 = 0;
r1 = RAM_LOAD_BASE = SDRAM_BASE=0 ;; 其实这里是把 SDRAM 的Wishbone从机地址存在 r1 中
r4 = hi(SPI_BASE) << 16 = hi(SPI0_BASE) << 16 = 0xb000_0000 ;; 这里是把SPI0设备的Wishbone从机地址存在 r4 中
SPI_INIT 部分
spi_init: l.ori r2, r0, SPI_SPCR_XFER_GO /* Setup SPCR with enable bit set */ l.sb SPI_SPCR(r4), r2 l.sb SPI_SPSS(r4), r0 /* Clear SPI slave selects */ l.ori r6, r0, SPI_SPSS_INIT l.sb SPI_SPSS(r4), r6 /* Set appropriate slave select */ l.jal spi_xfer l.ori r3, r0, 0x3 /* READ command opcode for SPI device*/ l.jal spi_xfer #ifdef BOOTROM_ADDR_BYTE2 l.ori r3, r0, BOOTROM_ADDR_BYTE2 /* Use addr if defined. MSB first */ #else l.or r3, r0, r0 #endif l.jal spi_xfer #ifdef BOOTROM_ADDR_BYTE1 l.ori r3, r0, BOOTROM_ADDR_BYTE1 #else l.or r3, r0, r0 #endif l.jal spi_xfer #ifdef BOOTROM_ADDR_BYTE0 l.ori r3, r0, BOOTROM_ADDR_BYTE0 #else l.or r3, r0, r0 #endif l.movhi r6, 0 l.movhi r7, 0xffff
r2 = r0| SPI_SPCR_XFER_GO = 0x51
l.sb SPI_SPCR(r4), r2 ;; EA = exts(0x0) + r4 = 0xb000_0000 , (EA)[7:0] = r2[7:0] = 0x51 这里访问的是SPI_SPCR(寄存体偏移0x0)寄存器,写入的数据为0x51。
l.sb SPI_SPSS(r4), r0 /* Clear SPI slave selects */ SPI_SPSS(寄存体偏移0x4) = r0 = 0。 SPSS应该是片选信号,且高为选中
l.ori r6, r0, SPI_SPSS_INIT ;; r6 = r0|0x1=0x1
l.sb SPI_SPSS(r4), r6 /* Set appropriate slave select */ SPI_SPSS=0X1 使片选有效
l.jal spi_xfer /*跳转到 spi_xfer代码块,LR=DelayInsnAddr+4,延时槽指令即跳转后的下一条,LF(R9)用于函数返回*/
l.ori r3, r0, 0x3 /* READ command opcode for SPI device*/ 传递参数到 spi_xfer 的跳转,0x3是SPI设备的读命令操作码
说明:一般SPI设备都是 写命令+数据操作(读写),这里先写入了一个 读数据的命令字
//接下来的这部分应该是 写入读数据的起始地址
//编译控制宏在头文件中均有定义
// Address bootloader should start from in FLASH
// Offset 0x1c0000
#define BOOTROM_ADDR_BYTE2 0x1c
#define BOOTROM_ADDR_BYTE1 0x00
#define BOOTROM_ADDR_BYTE0 0x00
//所以接下来的指令为分别调用了 三次 spi_xfer ,将宏定义的地址传递进去
l.jal spi_xfer
#ifdef BOOTROM_ADDR_BYTE2
l.ori r3, r0, BOOTROM_ADDR_BYTE2 /* Use addr if defined. MSB first */
#else
l.or r3, r0, r0
#endif
l.jal spi_xfer
#ifdef BOOTROM_ADDR_BYTE1
l.ori r3, r0, BOOTROM_ADDR_BYTE1
#else
l.or r3, r0, r0
#endif
l.jal spi_xfer
#ifdef BOOTROM_ADDR_BYTE0
l.ori r3, r0, BOOTROM_ADDR_BYTE0
#else
l.or r3, r0, r0
#endif
接下来分析下 spi_xfer 代码块的作用
spi_xfer: l.sb SPI_SPDR(r4), r3 /* Dummy write what's in r3 */ l.ori r3, r0, SPI_SPSR_RX_CHECK /* r3 = , ensure loop just once */ spi_xfer_poll: l.andi r3, r3, SPI_SPSR_RX_CHECK /* AND read fifo bit empty */ l.sfeqi r3, SPI_SPSR_RX_CHECK /* is bit set? ... */ l.bf spi_xfer_poll /* ... if so, rxfifo empty, keep polling */ l.lbz r3, SPI_SPSR(r4) /* Read SPSR */ l.jr r9 l.lbz r3, SPI_SPDR(r4) /* Get data byte */
spi_xfer:
l.sb SPI_SPDR(r4), r3 /* Dummy write what's in r3 */
l.ori r3, r0, SPI_SPSR_RX_CHECK /* r3 = , ensure loop just once */
// r4中存的是SPI0从设备的地址,再偏移地址SPI_SPDR,故使得SPI设备的寄存器SPDR=r3[7:0] (r3值由调用时的延时槽指令传入),这应该是数据寄存器 data regsisters
// r3 = r0|SPI_SPSR_RX_CHECK=SPI_SPSR_RX_CHECK=0x1 /* Check bit 0 is cleared, fifo !empty*/
spi_xfer_poll:
l.andi r3, r3, SPI_SPSR_RX_CHECK /* AND read fifo bit empty */
l.sfeqi r3, SPI_SPSR_RX_CHECK /* is bit set? ... */ SR[F]= (r3==0x1)?1:0
l.bf spi_xfer_poll /* ... if so, rxfifo empty, keep polling */
l.lbz r3, SPI_SPSR(r4) /* Read SPSR */ EA = (r4+SPI_SPSR(0x1) , r3[7:0]=(EA)[7:0],, r3[31:8]=0 即读取SPSR寄存器里面的低八位数值.
//循环检查 SPSR(可能为状态寄存器)里面的最低位,直到SPSR最低位为0,即表示 rxfifo 非空,则下面可以开始从SPDR读数据了,也就是写入读命令字(0x1)正常。
l.jr r9 ;; 函数返回,R9是链接寄存器(LR),里面保存着返回地址
l.lbz r3, SPI_SPDR(r4) /* Get data byte */ EA = (r4+SPI_SPDR(0x1) , r3[7:0]=(EA)[7:0],, r3[31:8]=0 即读取SPDR寄存器里面的低八位数值
综上可知:spi_xfer 函数其实是向 SPI设备写入一个字节的命令/数据,然后读取一个字节数据。都是借助 r3 来传递参数值的。
SPI COPY 部分:
l.movhi r6, 0 l.movhi r7, 0xffff copy: l.jal spi_xfer /* Read a byte into r3 */ l.add r8, r1, r6 /* Calculate store address */ l.sb 0(r8), r3 /* Write byte to memory */ l.addi r6, r6, 1 /* Increment counter */ l.sfeqi r6, 0x4 /* Is this the first word ?*/ l.bf store_sizeword /* put sizeword in the register */ l.sfeq r6, r7 /* Check if we've finished loading the words */ l.bnf copy /* Continue copying if not last word */ l.nop
r6 放的是 SDRAM 数据存放起始地址,r7放的是数据结束地址(这里只是一个初始化为32位里面最大数,实际数值在后面读取)。
copy:
l.jal spi_xfer // 读取一个字节放到 r3
r8 = r1 + r6 // r1 放的是 SDRAM的从机地址,加上SDRAM 数据访问地址
l.sb 0(r8), r3 // EA = r8+0x0 ,,,(EA)[7:0] = r3[7:0] 把 从SPI设备读取的一字节数据 r3[7:0] 写入 SDRAM
r6 = r6 + 1 // 这里是递增 1,访问是按字节访问的
l.sfeqi r6, 0x4 /* Is this the first word ?*/ SR[F]= (r6==0x4)?1:0 ,第一个WORD(字,4Byte)
l.bf store_sizeword /* put sizeword in the register */ 如果 SR[F]为1,则跳到 store_sizeword 代码块
l.sfeq r6, r7 /* Check if we've finished loading the words */ SR[F]= (r6==r7)?1:0
l.bnf copy /* Continue copying if not last word */ 如果 SR[F]为0,则跳到 copy 代码块
l.nop // 延时槽指
先分析下 store_sizeword 部分
store_sizeword: #ifdef SPI_RETRY_IF_INSANE_SIZEWORD l.lwz r7, 0(r1) /* Size word is in first word of SDRAM */ l.srli r10, r7, 16 /* Chop the sizeword we read in half */ l.sfgtui r10, 0x0200 /* It's unlikely we'll ever load > 32MB */ l.bf boot_init l.nop l.j copy l.nop #else l.j copy l.lwz r7, 0(r1) /* Size word is in first word of SDRAM */ #endif
board.h 中有定义 SPI_RETRY_IF_INSANE_SIZEWORD。
#ifdef SPI_RETRY_IF_INSANE_SIZEWORD
l.lwz r7, 0(r1) /* Size word is in first word of SDRAM */ EA=exts(0)+r1,,, r7 = (EA)[31:0],前面已经从SPI 设备(其实就是一个 FLASH)读取了开头的4个字节数据, 表示的是 数据量的大小
l.srli r10, r7, 16 /* Chop the sizeword we read in half */ r10 = r7>>16 (逻辑右移),只取高16位值
l.sfgtui r10, 0x0200 /* It's unlikely we'll ever load > 32MB */ SR[F] = (r10>0x0200)?1:0
l.bf boot_init //如果字节数大于 32MB(0x0200_0000 = 2^25 Byte),则重启。说明超出了允许的存储空间
l.nop //delayInsn
l.j copy //但是注意,这里的 结束地址 r7 已经更新为实际大小了
//如果数据量在 32MB 以内,检查Size word 通过,则继续跳到 copy ,继续将 FLASH 中的数据copy 到 SDRAM 中去
l.nop //delayInsn
#else
l.j copy //不作检查,直接跳到COPY继续拷贝数据到 SDRAM
l.lwz r7, 0(r1) /* Size word is in first word of SDRAM */ 存储实际的 sizeword 到 r7
#endif
跳转到SDRAM里面的复位向量:
goto_reset: l.ori r1, r1, RESET_ADDR l.jr r1 l.sb SPI_SPSS(r4), r0 /* Clear SPI slave selects */
由前面分析,r1高位存的是Wishbone从机地址对应SDRAM,再由得到 复位向量地址 0x100,经过 l.jr r1 跳到 SDRAM 的 0x100 位置处执行。延时槽指令是 关闭 SPI从机片选信号,因为前面数据已经 COPY 完毕了。
BOOTROM_GOTO_RESET 部分
#ifdef BOOTROM_GOTO_RESET /* Jump to reset vector in the SDRAM */ l.movhi r0, 0 l.movhi r4, SDRAM_BASE l.ori r4, r4, 0x100 l.jr r4 l.nop #endif
/* Jump to reset vector in the SDRAM */
l.movhi r0, 0 ;; r0=0
l.movhi r4, SDRAM_BASE ;; r4 = SDRAM_BASE<<16 ,, atlys 板子头文件定义是 0x0
l.ori r4, r4, 0x100 ;; r4 = r4|0x100 = (SDRAM_BASE<<16 | 0x0000_0100) =0x0000_0100 (atlys板子的)
l.jr r4 ;; PC = r4 ,让PC转到 SDRAM 里面的代码段去,
l.nop
注:PC地址高四位为wishbone的从机地址,对应SDRAM设备,复位初始时,在or1200_defines.v中定义的OR1200_BOOT_ADR=0xf000_0100),进入SDRAM_BASE 的代码段去。综上:由于使 PC跳到了 SDRAM里面的复位向量地址去执行,故为 BOOTROM_GOTO_RESET 。
BOOTROM_LOOP_AT_ZERO 部分
#ifdef BOOTROM_LOOP_AT_ZERO /* Don't load app via SPI, instead just put an infinite loop into bottom of memory and jump there. */ l.movhi r0, 0 l.movhi r4, SDRAM_BASE l.sw 0x0(r4), r0 l.movhi r5, hi(0x15000001) /* A l.nop 1 so sim exits if this enabled */ l.ori r5, r5, lo(0x15000001) l.sw 0x4(r4), r5 l.sw 0x8(r4), r5 l.sw 0xc(r4), r5 l.jr r4 l.nop #endif
l.movhi r0, 0 ;; r0 = 0
l.movhi r4, SDRAM_BASE ;; atlys 板子头文件定义是 0x0
l.sw 0x0(r4), r0 ;; EA = exts(0)+r4 ,, (SDRAM_BASE +0x0)[31:0] = r0
l.movhi r5, hi(0x15000001) /* A l.nop 1 so sim exits if this enabled */
l.ori r5, r5, lo(0x15000001) ;; r5 = 0x15000001 ,这就是 l.nop 1 的机器码
l.sw 0x4(r4), r5 ;; EA = exts(0x4)+r4 ,, (SDRAM_BASE +0x4)[31:0] = r5
l.sw 0x8(r4), r5 ;; EA = exts(0x8)+r4 ,, (EA)[31:0] = r5
l.sw 0xc(r4), r5 ;; EA = exts(0xc)+r4 ,, (EA)[31:0] = r5
l.jr r4 ;; l.jr r4 跳转到 SDRAM_BASE ,使得PC = 0x0000_0000,,跳到SDRAM_BASE后,由于RAM中起始0地址初始数据为0x0000_0000(即指令l.j 0)会执行一直在此处循环,类似 “ while(1); ” 。上面的语句用于在SDRAM_BASE后面插入几条 nop 延时槽指令
l.nop ;; 延时槽指令
综上:由于使得 PC跳到了 SDRAM里面的起始0地址部分,且在0地址出LOOP。故为 BOOTROM_LOOP_AT_ZERO
BOOTROM_LOOP_IN_ROM 部分
#ifdef BOOTROM_LOOP_IN_ROM /* Don't load app via SPI, instead just put an infinite loop into bottom of memory and jump there. */ l.movhi r0, 0 l.nop 0x1 l.j 0 l.nop l.nop #endif
l.movhi r0,0 ;; 清零 r0
l.nop 0x1 ;;
l.j 0 ;; PC = exts(0<<2)+本条指令的地址,故而一直在此处执行,相当于 while(1); 也没有改变最高4位wishbone从机地址的值,故一直在ROM中
l.nop ;; 跳转指令加nop
l.nop ;;
综合代码:由于这里没有跳转到 其他设备,只是在ROM里面LOOP。故为 BOOTROM_LOOP_IN_ROM 。
SPI 控制器的 RTL 代码在目录 \orpsocv2\rtl\verilog\simple_spi\ simple_spi.v 和 fifo4.v 文件
//module simple_spi_top( module simple_spi ( // renamed by Julius // 8bit WISHBONE bus slave interface clk_i, // clock rst_i, // reset (asynchronous active low) cyc_i, // cycle stb_i, // strobe adr_i, // address we_i, // write enable dat_i, // data input dat_o, // data output ack_o, // normal bus termination inta_o, // interrupt output sck_o, // serial clock output ss_o, //slave select mosi_o, // MasterOut SlaveIN miso_i // MasterIn SlaveOut );
在上面分析SPI_Init 部分有寄存器的定义和配置,具体要从RTL 代码来分析
由 RTL 定义 可知 寄存器的功能
由下面的 case 语句可知 寄存器的地址偏移
SPCR 寄存器的位定义
// decode Serial Peripheral Control Register wire spie = spcr[7]; // Interrupt enable bit wire spe = spcr[6]; // System Enable bit wire dwom = spcr[5]; // Port D Wired-OR Mode Bit wire mstr = spcr[4]; // Master Mode Select Bit wire cpol = spcr[3]; // Clock Polarity Bit wire cpha = spcr[2]; // Clock Phase Bit wire [1:0] spr = spcr[1:0]; // Clock Rate Select Bits
所以启动配置 SPCR = 0X51 ,,,系统使能、主机模式、时钟速率选择为 01 .
参考:
http://blog.csdn.net/column/details/openrisc.html?&page=4
http://blog.csdn.net/leishangwen/article/details/21713357
如何添加size_word ,SPI 中软件位置的偏移 OpenRisc-54-play with OpenRISC based atlys board