• orpsocv2 从ROM(bootrom)启动分析--以atlys板子的启动为例子


    1 复位后的启动地址

      1) 复位后,启动地址在or1200_defines.v最后宏定义,atlys板子的目录:orpsocv2oardsxilinxatlys tlverilogincludeor1200_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)在文件 orpsocv2oardsxilinxatlys tlverilogincludeorpsoc-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位地址线)

    在文件 orpsocv2oardsxilinxatlys tlverilogorpsoc_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 runkorpsocv2swootromootrom.S)和对应的板子 board.h (openrisc runkorpsocv2oardsxilinxatlysswoardincludeoard.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
    bootrom.S 宏定义块

     

    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    
    BOOTROM_GOTO_RESET

     /* 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
    BOOTROM_LOOP_AT_ZERO

    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 tlverilogsimple_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

    SVN : http://opencores.org/ocsvn/openrisc/openrisc

  • 相关阅读:
    linux tmux基本操作
    AJAX json集合传入Controller后台
    python 数据读取
    appium
    接口测试设计思路
    python 常用模块
    接口测试程序部分
    测试用例设计思路
    mock_待续
    网站收藏
  • 原文地址:https://www.cnblogs.com/hythink/p/6075534.html
Copyright © 2020-2023  润新知