精华内容
下载资源
问答
  • 树莓派BootLoader

    万次阅读 2013-05-18 12:04:39
    made by Rk 本文由浙江大学《嵌入式系统》课程提供强力支持。 感谢翁恺老师 @翁恺BA5AG .../**********************...David Welch的GitHub的 bootloader05给出了一个非常简单的RPi bootloader,他的代码链接在内存

    made by Rk

    本文由浙江大学《嵌入式系统》课程提供强力支持。

    感谢翁恺老师 @翁恺BA5AG

    /*************************************************************/


    实验内容:

    David Welch的GitHub的 bootloader05给出了一个非常简单的RPi bootloader,他的代码链接在内存的0x00020000位置,一直在监听串口是 否有XMODEM协议的文件下载,如果有就开始接收数据,并复制到0x00008000位置,传输完成后跳转到 0x00008000去执行。
    TA写了一个Python脚本,按照下面的命令调用脚本可以下载并执行用户程序


    python xmodem-loader.py -p com3 -baud 115200 output.bin
    你的任务是修改bootloader和python脚本实现如下功能:


    调用命令 python xmodem-loader.py -p com3 -baud 115200 启动脚本并且与板卡建立串口连接,之后可以发送下面的命令。
    load *.bin 下载程序*.bin
    go 执行已下载的程序
    peek addr 以一个字为单位读取内存中addr位置的数据(addr是4字节对齐,十六进行的形式,长度为8,例如 0x00008000),并以十六进制的形式输出
    poke addr data 以一个字为单位修改内存中addr位置的数据为data(addr是4字节对齐,十六进行的形式,长 度为8, data也是十六进行的形式,长度为8)
    verify *.bin 验证已下载的程序和*.bin是否完全相同。

    背景

    在国外大神的README中介绍了相关背景:

    This repo serves as a collection of low level examples.  No operating
    system, embedded or low level embedded or deeply embedded or bare metal,
    whatever your term is for this.
    介绍了教程来源

    I am in no way shape or form associated with the raspberry pi organization
    nor broadcom.  I just happen to own one (some) and am sharing my
    experiences.  The raspberry pi is about education, and I feel low
    level education is just as important as Python programming.
    介绍了作者对于树莓派的想法

    From what we know so far there is a gpu on chip which:

    1) boots off of an on chip rom of some sort
    2) reads the sd card and looks for additional gpu specific boot files
    bootcode.bin and start.elf in the root dir of the first partition
    (fat32 formatted, loader.bin no longer used/required)
    3) in the same dir it looks for config.txt which you can do things like
    change the arm speed from the default 700MHz, change the address where
    to load kernel.img, and many others
    4) it reads kernel.img the arm boot binary file and copies it to memory
    5) releases reset on the arm such that it runs from the address where
    the kernel.img data was written

    树莓派通过板上的GPU加载启动信息,

    1、读取SD卡第一个分区根目录里面的bootcode二进制文件以及start.elf。

    2、在同一个目录下加载系统配置文件,比如配置CPU主频等,然后切换到读取kernel.img的地址。

    3、读取kelnel后加载ARM启动二进制文件然后拷贝到内存,发送重置信号,使得树莓派可以从kernel.img的写入数据区开始加载命令继续执行。


    The memory is split between the GPU and the ARM, I believe the default
    is to split the memory in half.  And there are ways to change that
    split (to give the ARM more).  Not going to worry about that here.


    内存被分为GPU和ARM使用两部分,目测对半分。


    From the ARMs perspective the kernel.img file is loaded, by default,
    to address 0x8000.  (there are ways to change that, not going to worry
    about that right now).
    从ARM的角度来看,kernel镜像从地址0x8000开始加载。

    Hardware and programming information:


    You will want to go here
    http://elinux.org/RPi_Hardware
    And get the datasheet for the part
    http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
    (might be an old link, find the one on the wiki page)
    And the schematic for the board
    http://www.raspberrypi.org/wp-content/uploads/2012/04/Raspberry-Pi-Schematics-R1.0.pdf
    (might be an old link, find the one on the wiki page)


    Early in the BCM2835 document you see a memory map.  I am going to
    operate based on the middle map, this is how the ARM comes up.  The
    left side is the system which we dont have direct access to in that
    form, the gpu probably, not the ARM.  The ARM comes up with a memory
    space that is basically 0x40000000 bytes in size as it mentions in
    the middle chart.  The bottom of this picture shows total system
    sdram (memory) and somewhere between zero and the top of ram is a
    split between sdram for the ARM on the bottom and a chunk of that
    for the VC SDRAM, basically memory for the gpu and memory shared
    between the ARM and GPU to allow the ARM to ask the GPU to draw stuff
    on the video screen.  256MBytes is 0x10000000, and 512MBytes is
    0x20000000.
     Some models of raspberry pi have 256MB, newer models have
    512MB total ram which is split between the GPU and the ARM.  Assume
    the ARM gets at least half of this.  Peripherals (uart, gpio, etc)
    are mapped into arm address space at 0x20000000.
     When you see
    0x7Exxxxxx in the manual replace that with 0x20xxxxxx as your ARM
    physical address.  Experimentally I have seen the memory repeats every
    0x40000000, read 0x40008000 and you see the data from 0x8000.  I
    wouldnt rely on this, just an observation (likely ignoring the upper

    address bits in the memory controller).

    第一次编译运行

    在Mac系统上的编译一开始遇到一些问题,需要修改loader文件为以下内容:
    MEMORY
    {
        ram : ORIGIN = 0x8000, LENGTH = 0x1000000
    }
    
    SECTIONS
    {
        .text : { *(.text*) *(.rodata.str1.4) *(.rodata) } > ram
        .bss : { *(.bss*) } > ram
    }

    修改bootloader05.c文件,原理是:
    将原有的3个控制状态和128个数据读写状态修改为7个控制状态以及128个数据读写状态。
    其中新增的状态有:
    1、GO状态,跳转到Base Address读取并且执行代码;
    2、Verify状态,根据error address的不同进行检查,error address初始化为0,一旦在verify过程中检查到已读到的数据与新的bin文件不一致,记录当前错误地址,并且回到状态0;
    3、Peek状态,根据后面指令输入的address,通过GET(address)函数(定义在vector.s中)得到该内存地址上存的值,打印输出;
    4、Poke状态,根据指令后面的地址以及新的数据,通过PUT(address,data value)函数(定义在vector.s中)修改内存地址,并且打印输出修改地址和修改后的值(通过GET得到)
    5、LOAD状态,也就是加载二进制文件。

    源代码中检查block块有两次检验,个人感觉第二次可以去掉,于是尝试了一下,事实证明也是可以的。
    代码以及注释如下:

    //-------------------------------------------------------------------------
    //-------------------------------------------------------------------------
    
    // The raspberry pi firmware at the time this was written defaults
    // loading at address 0x8000.  Although this bootloader could easily
    // load at 0x0000, it loads at 0x8000 so that the same binaries built
    // for the SD card work with this bootloader.  Change the ARMBASE
    // below to use a different location.
    
    #define ARMBASE 0x8000
    #define true 1
    
    #define LOAD    0x00
    #define GO      0x01
    #define PEEK    0x02
    #define POKE    0x03
    #define VERIFY  0x04
    
    extern void PUT32 ( unsigned int, unsigned int );
    extern void PUT16 ( unsigned int, unsigned int );
    extern void PUT8 ( unsigned int, unsigned int );
    extern unsigned int GET32 ( unsigned int );
    extern unsigned int GET8 ( unsigned int );
    extern unsigned int GETPC ( void );
    extern void BRANCHTO ( unsigned int );
    extern void dummy ( unsigned int );
    
    extern void uart_init ( void );
    extern unsigned int uart_lcr ( void );
    extern void uart_flush ( void );
    extern void uart_send ( unsigned int );
    extern unsigned int uart_recv ( void );
    extern void hexstring ( unsigned int );
    extern void hexstrings ( unsigned int );
    extern void timer_init ( void );
    extern unsigned int timer_tick ( void );
    
    extern void timer_init ( void );
    extern unsigned int timer_tick ( void );
    
    void print_pi(char* s) {
        int i = 0;
        while(s[i] != '\0') {
            uart_send(s[i++]);
        }
        uart_send(0x0D);    //send carriage return
        uart_send(0x0A);    //send new line
    }
    
    //------------------------------------------------------------------------
    unsigned char xstring[256];
    //------------------------------------------------------------------------
    int notmain ( void ) {
        unsigned int ra;
        //unsigned int rb;
        unsigned int rx;
        unsigned int addr;
        unsigned int block;
        unsigned int state;
    
        unsigned int crc;
        unsigned int error_addr;
    
        uart_init();            //init serial
        hexstring(0x12345678);  //translate to hex value and send
        hexstring(GETPC());
        hexstring(ARMBASE);
        print_pi("This is Raspberry Pi!");
        uart_send(0x04);
        timer_init();
    
    //SOH 0x01
    //ACK 0x06
    //NAK 0x15
    //EOT 0x04
    
    //block numbers start with 1
    
    //132 byte packet
    //starts with SOH
    //block number byte
    //255-block number
    //128 bytes of data
    //checksum byte (whole packet)
    //a single EOT instead of SOH when done, send an ACK on it too
        
        addr=ARMBASE;	//base RAM address
        error_addr = 0; //record the address of error
        block=1;        //the 
        state=0;		//initial state
        crc=0;          //check sum
        rx=timer_tick();
    
        while(true)
        {
            ra=timer_tick();
            if((ra-rx)>=4000000)
            {
                uart_send(0x15);		//send long time no-response signal
                rx+=4000000;
            }
    
            if((uart_lcr()&0x01)==0)
            {
                continue;               //test if input string begin with 0x01
            }
    
            xstring[state]=uart_recv();
            rx=timer_tick();
    
            switch(state)
            {
                case 0:     //initial state, decide which action to take
                {
                    if(xstring[state]==0x01)
                    {
                        crc=xstring[state];
                        state++;
                    }
                    else if (xstring[state] == 0x04)    //End Of transmission
                    {                                   //decide the action
                        uart_send(0x06);
                        if (xstring[1] == LOAD)
                        {
                            print_pi("This is LOAD command!");
                            uart_send(0x04);
                            uart_flush();
                        }
                        else if (xstring[1] == VERIFY)
                        {
                            if (error_addr == 0)
                            {
                                print_pi("Verify successful!");
                                uart_send(0x04);
                            }
                            else
                            {
                                print_pi("Verify error");
                                print_pi("Error Adress:");
                                hexstring(error_addr);
                                print_pi("Error Value:");
                                hexstring(GET32(error_addr));
                                uart_send(0x04);
                            }
                            uart_flush();
                        }
                        addr = ARMBASE;
                        error_addr = 0;
                        block = 1;
                        state = 0;
                        crc = 0;
                    }
                    else
                    {
                        state=0;
                        uart_send(0x15);
                        print_pi("Init Error!");
                        uart_send(0x04);
                        uart_flush();
                    }
                    break;
                }
                case 1:                             //decide the action
                {
                    if (xstring[1] > VERIFY)
                    {
                        state = 0;
                        uart_send(0x15);
                        print_pi("Command error!");
                        uart_send(0x04);
                        uart_flush();
                    }
                    else if (xstring[1] == GO)
                    {
                        state = 0;
                        uart_send(0x06);
                        print_pi("Branch to the base address!");
                        uart_send(0x04);
                        uart_flush();
                        BRANCHTO(ARMBASE);
                    }
                    else if (xstring[1] == PEEK)
                    {
                        state = 133;
                    }
                    else if (xstring[1] == POKE)
                    {
                        state = 133;
                    }
                    else
                    {
                        state++;
                    }
                    break;
                }
                case 2:
                {
                    if(xstring[state] == block)//if the data has the right block number
                    {
                        crc += xstring[state];
                        state++;
                    }
                    else
                    {
                        state = 0;
                        uart_send(0x15);
                        print_pi("Data block error!");
                        uart_send(0x04);
                        uart_flush();
                    }
                    break;
                }
                
                case 132:   //receive and verify progress
                {
                    crc &= 0xFF;
                    if(xstring[state]==crc)
                    {
                        if (xstring[1] == LOAD)
                        {
                            for(ra=0; ra<128; ra++)
                            {
                                PUT8(addr++,xstring[ra+4]);
                            }
                            uart_send(0x06);
                        }
                        else
                        {
                            //Verify progress
                            for (ra=0; ra<128; ra++,addr++)
                            {
                                if (xstring[ra + 4] != (GET8(addr) & 0xff))
                                {
                                    error_addr = addr;  //get the error address
                                    break;
                                }
                            }
                            uart_send(0x06);
                        }
                        block=(block+1) & 0xFF; //if the data flow has not stopped
                    }
                    else
                    {
                        uart_send(0x15);
                        print_pi("Check sum error!");
                        uart_send(0x04);
                        uart_flush();
                    }
                    state=0;
                    break;
                }
                case 136:
                {
                    if (xstring[1] == PEEK)
                    {
                        unsigned int peek_addr = 0;
                        for (ra = 0; ra < 4; ra++)
                        {   //generate the address
                            peek_addr = peek_addr << 8 | xstring[ra + 133];
                        }
                        uart_send(0x06);
                        print_pi("Peek command value:");
                        hexstring(GET32(peek_addr));
                        uart_send(0x04);
                        uart_flush();
                        state = 0;
                    }
                    else
                    {
                        state++;
                    }
                    break;
                }
                case 140:
                {
                    if (xstring[1] == POKE)
                    {
                        unsigned int poke_addr = 0x00000000;
                        unsigned int poke_data = 0;
                        for (ra = 0; ra < 4; ra++)
                        {
                            poke_addr = poke_addr << 8 | xstring[ra + 133];
                            poke_data = poke_data << 8 | xstring[ra + 137];
                        }
                        uart_send(0x06);
                        print_pi("Poke command:");
                        PUT32(poke_addr, poke_data);
                        print_pi("Poke address:");
                        hexstring(poke_addr);           //get the Poke address
                        print_pi("Poke value:");
                        hexstring(GET32(poke_addr));   //get the value after edit action
                        uart_send(0x04);
                        uart_flush();
                        state = 0;
                    }
                    else
                    {
                        state = 0;
                    }
                    break;
                }
                default:
                {
                    crc+=xstring[state];
                    state++;
                    break;
                }
            }
        }
        return(0);
    }
    
    //-------------------------------------------------------------------------
    //-------------------------------------------------------------------------
    
    
    //-------------------------------------------------------------------------
    //
    // Copyright (c) 2012 David Welch dwelch@dwelch.com
    //
    // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    //
    // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    //
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    //
    //-------------------------------------------------------------------------
    

    在vector.s中增加GET8:
    .globl GET8
    GET8:
        ldrb r0,[r0]
        bx lr

    修改py文件对串口数据进行操作:
    import sys, getopt
    import serial
    import time
    
    def open(aport='/dev/tty.usbserial', abaudrate=115200) :#此处修改为本机串口地址,可以参见博文 http://blog.csdn.net/rk2900/article/details/8632713
      return serial.Serial(
          port=aport,
          baudrate=abaudrate,     # baudrate
          bytesize=8,             # number of databits
          parity=serial.PARITY_NONE,
          stopbits=1,
          xonxoff=0,              # enable software flow control
          rtscts=0,               # disable RTS/CTS flow control
          timeout=None               # set a timeout value, None for waiting forever
      )
    
    def printLog(sp):
      temp = sp.read()
      while ord(temp) != 0x04:
          write(temp)
          temp = sp.read()
    
    if __name__ == "__main__":
    
      # Import Psyco if available
      try:
          import psyco
          psyco.full()
          print "Using Psyco..."
      except ImportError:
          pass
    
      conf = {
          'port': '/dev/tty.usbserial',#此处修改为本机串口地址
          'baud': 115200,
      }
    
      try:
          opts, args = getopt.getopt(sys.argv[1:], "hqVewvrp:b:a:l:")
      except getopt.GetoptError, err:
          print str(err)
          sys.exit(2)
    
      for o, a in opts:
          if o == '-p':
              conf['port'] = a
          elif o == '-b':
              conf['baud'] = eval(a)
          else:
              assert False, "option error!"
    
      sp = open(conf['port'], conf['baud'])
    
    # print args[0]
    # print conf['port']
    # print conf['baud']
    
      write=sys.stdout.write
    
      isLoaded = False
    
      while True:
          print ''
          cmd = raw_input('>> ').split(' ');
          sp.flushInput()
    
          if cmd[0] == 'go':
              if not isLoaded:
                  confirm = raw_input("No file loaded, sure to go? [Y/N]")
                  if confirm == '' or confirm[0] == 'N' or confirm[0] == 'n':
                      continue
    
              success = False
              while success == False:
                  sp.write(chr(0x01))
                  sp.write(chr(0x01))
                  sp.flush()
    
                  temp=sp.read()
    
                  if ord(temp)==0x06:
                      success = True
                  else:
                      print ord(temp)
    
                      printLog(sp)
    
          elif cmd[0] == 'peek':
              if len(cmd) < 2:
                  print "Incorrect command, should be 'peek addr'"
                  continue
    
              addr = int(cmd[1], 16) & 0xffffffff
    
              success = False
              while success == False:
                  sp.write(chr(0x01))
                  sp.write(chr(0x02))
    
                  for i in range(0,4):
                      sp.write(chr(addr >> 24 & 0xff))
                      addr = addr << 8
    
                  sp.flush()
    
                  temp=sp.read()
    
                  if ord(temp)==0x06:
                      success = True
                  else:
                      print ord(temp)
    
                      printLog(sp)
    
          elif cmd[0] == 'poke':
              if len(cmd) < 3:
                  print "Incorrect command, should be 'poke addr data'"
                  continue
    
              addr = int(cmd[1], 16) & 0xffffffff
              data = int(cmd[2], 16) & 0xffffffff
    
              success = False
              while success == False:
                  sp.write(chr(0x01))
                  sp.write(chr(0x03))
    
                  for i in range(0,4):
                      sp.write(chr(addr >> 24 & 0xff))
                      addr = addr << 8
                  for i in range(0,4):
                      sp.write(chr(data >> 24 & 0xff))
                      data = data << 8
    
                  sp.flush()
    
                  temp=sp.read()
    
                  if ord(temp)==0x06:
                      success = True
                  else:
                      print ord(temp)
    
                      printLog(sp)
    
          elif cmd[0] == 'load' or cmd[0] == 'verify':
              if len(cmd) < 2:
                  print "Please input the filename"
                  continue
    
              try:
                  data = map(lambda c: ord(c), file(cmd[1],"rb").read())
              except:
                  print "File path error"
                  continue
    
              temp = sp.read()
              buf = ""
    
              dataLength = len(data)
              blockNum = (dataLength-1)/128+1
              print "The size of the image is ",dataLength,"!"
              print "Total block number is ",blockNum,"!"
              print "Download start,",blockNum,"block(s) in total!"
    
              for i in range(1,blockNum+1):
                  success = False
                  while success == False:
                      sp.write(chr(0x01))
                      if cmd[0] == 'load':
                          sp.write(chr(0x00))
                      else:
                          sp.write(chr(0x04))
                      sp.write(chr(i&0xFF))
                      sp.write(chr(0xFF-i&0xFF))
                      crc = 0x01+0xFF
    
                      for j in range(0,128):
                          if len(data)>(i-1)*128+j:
                              sp.write(chr(data[(i-1)*128+j]))
                              crc += data[(i-1)*128+j]
                          else:
                              sp.write(chr(0xff))
                              crc += 0xff
    
                      crc &= 0xff
                      sp.write(chr(crc))
                      sp.flush()
    
                      temp=sp.read()
                      sp.flushInput()
    
                      if ord(temp)==0x06:
                          success = True
                          print "Block",i,"has finished!"
                      else:
                          print ord(temp)
                          print "Error,send again!"
    
                          printLog(sp)
    
              sp.write(chr(0x04))
              sp.flush()
              temp=sp.read()
    
              if ord(temp)==0x06:
                  if (cmd[0] == 'load'):
                      isLoaded = True
                  print "Download has finished!\n"
          elif cmd[0] == 'q':
              sys.exit(0)
          else:
              print "Invalid command!"
    
    
          printLog(sp)
    
    
      # while True:
      #   write(sp.read())
    
      sp.close()

    实验结果

    LOAD指令:


    输入GO指令,此处我加载的是外国人搞的blinker二进制文件,效果就是树莓派上ACT的小灯闪烁。
    效果如图:


    进行PEEK与POKE操作
    此处POKE将修改后的内存值打印出来是需要通过GET操作得到后来该内存地址处的值,而非打印POKE操作输入的参数。


    POKE操作之前进行Verify:



    POKE操作之后进行verify:



    参考资料:

    百科关于ASCII码:http://baike.baidu.com/view/15482.htm

    华官的博客:http://blog.csdn.net/logicworldzju/article/details/8923596

    达达的博客:http://www.neohe.tk/raspberrypi-diy-bootloader/

    外国人的GitHub:https://github.com/dwelch67/raspberrypi

    展开全文
  • 树莓派实验五: bootloader

    千次阅读 2013-06-23 14:15:46
     熟悉树莓派bootloader 二、实验内容  David Welch的GitHub的bootloader05给出了一个非常简单的RPi bootloader,他的代码链接在内存的0x00020000位置,一直在监听串口是否有XMODEM协议的文件下载,如果有就...

    一、实验目的

           熟悉树莓派的bootloader

    二、实验内容

        David Welch的GitHub的bootloader05给出了一个非常简单的RPi bootloader,他的代码链接在内存的0x00020000位置,一直在监听串口是否有XMODEM协议的文件下载,如果有就开始接收数据,并复制到0x00008000位置,传输完成后跳转到 0x00008000去执行。
    TA写了一个Python脚本,按照下面的命令调用脚本可以下载并执行用户程序

    python xmodem-loader.py -p com3 -baud 115200 output.bin

    你的任务是修改bootloader和python脚本实现如下功能:

    1. 调用命令 python xmodem-loader.py -p com3 -baud 115200 启动脚本并且与板卡建立串口连接,之后可以发送下面的命令。
    2. load *.bin 下载程序*.bin
    3. go 执行已下载的程序
    4. peek addr 以一个字为单位读取内存中addr位置的数据(addr是4字节对齐,十六进行的形式,长度为8,例如 0x00008000),并以十六进制的形式输出
    5. poke addr data 以一个字为单位修改内存中addr位置的数据为data(addr是4字节对齐,十六进行的形式,长 度为8, data也是十六进行的形式,长度为8)
    6. verify *.bin 验证已下载的程序和*.bin是否完全相同。

     

    三、主要仪器设备

           1. 树莓派板

           2. 移动电源

           3. PC

     

    四、操作方法和实验步骤

    1. 下载bootloader05代码

           具体的下载地址为:

           https://github.com/dwelch67/raspberrypi

             或直接在linux命令行输入

           git clone https://github.com/dwelch67/raspberrypi.git

            

             如下图分别为win7和ubuntu下截图:

     

     

     

    2、 Windows下运行py脚本

          

           首先win7下需要安装有python2.7.3,并配置好path。

           如果不能import serial,则使用easy_install工具安装serial。


           将TA给的脚本接口改为com3(本机连接的串口为com3)


     

     

     

    3. 运行已有程序,看绿灯闪烁

    ①将.img文件通过sd卡拷贝到树莓派上

    ②将blinker.bin和xmodem-loader_o.py放在同一目录下

    ③运行脚本文件,同时插入树莓派电源

     

     

    运行界面如上图所示,拍得树莓派板绿灯闪烁,如下图

     

    说明基本配置可用

     

     

     

     

     

    4、设计状态图

           bootloader实际就是一个状态机控制数据传输的过程,我的设计如下

     

     

     

           为了实现这个设计,我们将bootloader05.c文件改为附录程序1所示。

           我们将py脚本文件改为附录程序2所示。

           其余不变。

          

     

    5、 测试正确性

     

    修改makefile中第一行为 ARMGNU?= arm-linux-gnueabihf

    在ubuntu下make重新编译,生成新的img和bin文件。

    按照3中操作,运行程序。

    1. 测试load和go命令


     

    2. 测试peek

     

    3. 测试poke

     

    4. 测试verify

     

    可见实验成功


           这次实验除了细节很繁琐之外,最难的就是需要在写程序前设计一个状态图,根据这个状态图再进行具体代码实现,这是最好的保证bootloader运行正确的方法。经过这次实验,我学到了很多。

    展开全文
  • kernel.img开头就是FE DF 07 EA,这应该就跳转指令,由于大小端的原因(其实我并不知道具体意思...)实际加载到cpu是EA 07 DF FE,对比bootloader05.list即可分析出来。 然后查表,armv7架构arm指令里跳转指令是b...

    代码来源:https://github.com/dwelch67/raspberrypi/tree/master/bootloader05

    前要:po学渣,想造车轮,于是开始了不归路。

    树莓派启动流程(照搬github主原文):

    1. boots off of an on chip rom of some sort
    2. reads the sd card and looks for additional gpu specific boot filesbootcode.bin and start.elf in the root dir of the first partition(fat32 formatted, loader.bin no longer used/required)
    3. in the same dir it looks for config.txt which you can do things likechange the arm speed from the default 700MHz, change the address whereto load kernel.img, and many others
    4. it reads kernel.img the arm boot binary file and copies it to memory
    5. releases reset on the arm such that it runs from the address where the kernel.img data was written.

    所以kernel.img会被gpu加载到0x8000的地方,我们要写的代码于是就从这里开始。

    file:vectors.s
    
    .globl _start
    _start:
        b skip
    
    .space 0x200000-0x8004,0  ;这里将0x8004到0x200000的数据统统填为0
    
    skip:
        mov sp,#0x08000000
        bl notmain
    ....

    为什么填0x8004,原因是0x8000地址开始是要写一个跳转指令,跳转到skip这边来。

    所以,原理一下子就明晰了,写代码然后做成kernel.img给gpu加载到内存0x8000的地方并让cpu去执行。

    剩下的就是如何组织代码结构的问题了。

    github主的方法是,cpu加载他的代码,然后他的代码配置uart,并等待数据传输进来,然后从0x8000这里开始堆放代码数据,最后执行跳转到0x8000执行您传输的数据。

    file:periph.c
    .......
    #define AUX_ENABLES     0x20215004
    #define AUX_MU_IO_REG   0x20215040
    #define AUX_MU_IER_REG  0x20215044
    #define AUX_MU_IIR_REG  0x20215048
    #define AUX_MU_LCR_REG  0x2021504C
    #define AUX_MU_MCR_REG  0x20215050
    #define AUX_MU_LSR_REG  0x20215054
    #define AUX_MU_MSR_REG  0x20215058
    #define AUX_MU_SCRATCH  0x2021505C
    #define AUX_MU_CNTL_REG 0x20215060
    #define AUX_MU_STAT_REG 0x20215064
    #define AUX_MU_BAUD_REG 0x20215068
    .......
    

    github主说uart地址在0x20215000这里开始,树莓派的datasheet却写的是0x7E20 1000(arm pl011)和0x7E21 5000(mini uart)

    当然,kernel.img我也没具体运行过,懒。既然mmu也没启用,肯定是github主写错了,要不就是我错了(已解决,看下面的更新)。

    然后github主弄了个判断xmodem传输协议(看下面的更新解释)的代码来判断数据传输状态

    file:bootloader05.c
    //SOH 0x01
    //ACK 0x06
    //NAK 0x15
    //EOT 0x04
    .......
    if(state==0)
            {
                if(xstring[state]==0x04)
                {
                    uart_send(0x06);
                    for(ra=0;ra<30;ra++) hexstring(ra);
                    hexstring(0x11111111);
                    hexstring(0x22222222);
                    hexstring(0x33333333);
                    uart_flush();
                    BRANCHTO(ARMBASE);
                    break;
                }
            }
    ........

    当接受的数据块头标识着EOT(我猜全称是end of transmit)时,就完成数据写入,并反馈,然后跳转执行您发送的数据。

    由于我要造轮子,所以研究到这还不够。于是打开了bootloader05.list,并打开kernel.img和vectors.o继续研究。

    kernel.img与vectors.o前面一部份相同,原因是kernel.img是vectors.o与其他文件一同链接成的(elf文件格式)然后生成的纯代码块(elf应该是linux下一种软件执行标准)。

    kernel.img开头就是FE DF 07 EA,这应该就跳转指令,由于大小端的原因(其实我并不知道具体意思...)实际加载到cpu是EA 07 DF FE,对比bootloader05.list即可分析出来。

    然后查表,armv7架构arm指令里跳转指令是b,由于有几种处理器都是32位的,所以有个cond这个值用来判断处理器(这句话我瞎编的,我也不知道是干嘛的)。

    但后边的1010确实是A,所以应该就是这个指令。07 DF FE 是imm24,是有符号的值,用来表示当前运行地址指针(不知道是什么指针,先不研究)要偏移的量。

    参考过的文章:http://blog.csdn.net/logicworldzju/article/details/8923596

    更新:

    前面的地址问题,我弄明白了。一共有三种地址

    1. ARM virtual addresses (standard Linux kernel only)(这个地址是开启了mmu后的地址)
    2. ARM physical addresses(cpu访问的地址,实际访问时的地址)
    3. Bus addresses(总线地址,arm总线amba连接外围设备,然后芯片厂商将总线上的设备地址转换给cpu能用的地址去访问)

    所以,由于没有开启mmu,实际的设备地址在cpu看来应该是从0x7E00 0000偏移到了0x2000 000。

    偏移量为0x5E00 0000,所以mini uart的地址是0x7E21 5000 - 0x5E00 0000 = 0x2021 5000。

    关于XModem协议

    XModem协议介绍:
    XModem是一种在串口通信中广泛使用的异步文件传输协议,分为XModem和1k-XModem协议两种,前者使用128字节的数据块,后者使用1024字节即1k字节的数据块。

    这里说的异步文件传输,不知道这个异步到底是个什么异步。看了下这个文章,估计是指每块数据块之间不严格限定时间间隔。

    转载于:https://my.oschina.net/wangyangdahai/blog/729783

    展开全文
  • 树莓派3/A20 Bootloader加载过程分析

    万次阅读 2014-03-15 10:26:46
    作为一名树莓派3的玩家,可能会将会感兴趣于这个板子的启动过程,就嵌入式linux而言,整个加电——启动bootloader——启动内核——加载rootfs流程对于新手会感到非常的模糊,而不知如何下手。本篇内容尽可能详细的...
    

         作为一名树莓派3的玩家,可能会将会感兴趣于这个板子的启动过程,就嵌入式linux而言,整个加电——启动bootloader——启动内核——加载rootfs流程对于新手会感到非常的模糊,而不知如何下手。本篇内容尽可能详细的描述了利用superboard从加电到启动linux内核的整个操作过程,为进一步学习如何构建一个可运行的linux系统打下了基础。后面,将会在此基础上继续介绍如何进一步挂载跟文件系统,启动到shell甚至GUI图形界面,从而构建一个完整、可用的linux系统。
        由于全志A10和A20在加载Bootloader过程方面基本一致,下面仅以A20叙述,但同时也适用于A10,另现在市面上一般所说的SD卡即为Micro SD Card,也就是TF卡,为区别于一般传统的SD卡,本文一般使用TF卡描述,但同于平时所说的SD卡。
         A20的启动过程大概可分为5步:
         Boot ROM,SPL,Uboot,Kernel,RootFileSystem。

         本文关注的是镜像的加载和启动过程,分析Boot ROM→SPL→Uboot→Kernel的启动流程
         系统上电后,ARM处理器在复位时从地址0x000000开始执行指令,把板上ROM或Flash映射到这一地址。A20将启动设备选择程序固化在CPU内部的一个32KB ROM中,默认的启动时序为SD Card0,NAND FLASH,SD Card2,SPI NOR FLASH。另外通过外部的一个启动选择引脚可以使其跳转到USB启动模式。通常情况下,启动选择引脚状态连接50K内部上拉电阻。在上电后,执行存储在Boot ROM中的启动代码,将自动检测启动选择引脚状态。只有当该引脚状态为低电平时才选择USB启动模式。在选择启动设备后将加载并执行bootloader程序,CPU通过拷贝或映射bootloader程序到内存,然后执行bootloader的第一条指令。通过阅读官方的uboot烧写方法,发现A20通过uboot引导系统之前先载入了uboot SPL。
           什么是SPL?通过查阅uboot的官网资料得知,SPL是一个迷你版的uboot,全拼为Second Program Loader。适用于SOC的内部SRAM<64K的情况,用它来加载完整的uboot程序到SDRAM,并通过完整uboot加载内核来启动系统。其中SRAM一般指CPU内部的L1/L2或外部的L2高速缓存,这里即为Boot ROM,而SDRAM一般指内存。

        SPL程序流程如下:
    1. 初始化ARM处理器
    2. 初始化串口控制台
    3. 配置时钟和最基础的分频
    4. 初始化SDRAM
    5. 配置引脚多路复用功能
    6. 启动设备初始化(即上面选择的启动设备)
    7. 加载完整的uboot程序并转交控制权

    如下图

     

        搞清楚了上面的概念,可以知道superboard出厂已经烧写了NandFlash中的程序,即在启动时使用的是
    NandFlash。现在根据全志A20上述步骤,我们尝试用SD Card0(即树莓派3上卡槽中的TF卡)来启动
    系统。
         感谢:itviewer

         树莓派3 请看这里:http://blog.csdn.net/ok138ok/article/details/20941801

        

    
    展开全文
  • 树莓派启动

    2018-05-23 16:53:49
    树莓派开发板没有配置板载FLASH,因为它支持SD卡启动,所有我们需要下载相应镜像,并将其烧写在SD上,启动系统即可(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统)树莓派由于其开源特性,支持非常多...
  • 树莓派学习

    2017-02-06 22:56:22
    想搞搞树莓派买了一块...但是今天找bootloader的时候发现不太对头。查了一下发现了一下 树莓派大致的启动步骤看到这一段 At power-up, the CPU is offline, and a small RISC core on the GPU is responsible for bo
  • 树莓派简介

    2016-07-07 22:14:20
    树莓派开发板没有配置板载 FLASH,因为它支持 SD 卡启动,所有我们需要下载相应镜像,并将其烧写在 SD 上,启动系统即可 (这个镜像里包含了我们通常所说的 bootloader、kernel、文件系统) 树莓派由于其开源特性,...
  • 1、树莓派启动方式及支持的系统...(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型(指的文件系统): Raspbian、Arch Linux ARM、Debian Squeeze、
  • 树莓派2构建的u-boot二进制文件在树莓派3b上不起作用,这就是为什么树莓派2和树莓派3b有单独的镜像。但是,Raspberry Pi有自己的内置bootloader。这可以在对系统启动分区上的config.txt文件进行一些更改时使用。 ...
  • 树莓派升级内核

    2020-05-09 10:57:46
    树莓派升级内核 升级内核直接执行: sudo apt-get install --reinstall raspberrypi-bootloader raspberrypi-kernel 后面就是: sudo apt-get update & sudo apt-get upgrade
  • 树莓派上手使用

    2017-08-05 00:52:48
    1、树莓派启动方式及支持的系统: ...(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型(指的文件系统): Raspbian、Arch Linux ARM、Debian Squeeze、
  • 树莓派内核更新

    2020-09-18 10:28:02
    树莓派等芯片的启动过程 51、stm32(裸机)c直接控制底层寄存器实现相关业务。...启动过程: 电源-》Bootloader(引导操作系统启动)-》Linux内核-》文件系统(根据功能来组织文件夹、带访问权限)-》KTV点歌机、ATM机
  • 一.树莓派启动方式及支持...(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型: Raspbian、Arch Linux ARM、Debian Squeeze、Firefox OS、Gentoo Linux...
  • 树莓派 Linux备忘

    2016-04-23 18:10:00
    //更新树莓派 sudo apt-mark hold raspberrypi-bootloader sudo apt-get update sudo apt-get upgrade //配置 raspi-config //安装git sudo apt-get install git-core //安装wiringPi git clone ...
  • 树莓派开发板没有配置板载FLASH,因为它支持SD卡启动,所有我们需要下载相应镜像,并将其烧写在SD上,启动系统即可(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统)。 树莓派由于其开源特性,支持非常...
  • 树莓派3B+移植uboot

    2021-05-10 17:21:17
    一、前言 最近入手得一块树莓派3B+开发板,试着移植Uboot到其中作启动内核,练练手。...树莓派上电后,SoC 中的 bootloader 首先被执行,其作用是挂载 SD 卡上的 FAT32 分区,从而加载下一阶段的 bootl
  • 树莓派Linux内核编译

    千次阅读 2018-10-30 17:29:31
    firmware:树莓派的交叉编译好的二进制内核、模块、库、bootloader。 linux:内核源码,可以在branches下进行版本的选择。 tools:编译内核和其他源码所需的工具——交叉编译器等。 以上三个文件最为重要,下面的工程...
  • 1.2 点亮你的树莓派

    2020-05-01 15:27:25
    树莓派系统镜像的下载和烧写 一....(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型: Raspbian、Arch Linux ARM、Debian Squeez...
  • 《用树莓派做点什么》QQ群:879921733 叨逼叨 作为Android系统工程师,不管是Linux平台的各种常用命令和工具,还是从驱动层到应用层的系统架构,以及当你按下Power Key开机时bootloader到Laucher桌面起来整个流程...
  • 嵌入式设备带操作系统的启动过程扫盲 1.树莓派等芯片带操作系统的启动过程: C51,STM32: 启动过程:C语言直接操控底层寄存器实现相关业务...启动过程:电源----Bootloader(引导操作系统启动)----linux内核----文件系统
  • 树莓派Pi4B系统镜像的下载和烧写

    千次阅读 多人点赞 2020-07-19 21:21:08
    树莓派Pi4B系统镜像的下载和烧写 ...(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型: Raspbian、Arch Linux ARM、Debian Squeeze、Fi
  • 树莓派入门(二)

    千次阅读 2015-11-22 23:32:01
    1、树莓派启动方式及支持的系统...(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统) 树莓派由于其开源特性,支持非常多的系统类型(指的文件系统): Raspbian、Arch Linux ARM、Debian Squeeze、
  • 2.1树莓派系统的安装树莓派开发板没有配置板载FLASH,因为它支持SD卡启动,所有我们需要下载相应镜像,并将其烧写在SD上,启动系统即可(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统)。 树莓派...
  • 树莓派移植U-boot

    2018-05-13 19:31:00
    本文是本人将Uboot 移植到树莓派上的过程, 其中由于树莓派的特殊性, 参考https://www.embbnux.com/2014/04/21/raspberry_bootloader_startup/, 因此并不能算真正意义上的移植, 但是对于Uboot的学习还是非常有帮助的....
  • 树莓派3B+的uboot启动内核

    千次阅读 2020-04-25 22:22:56
      入手一个树莓派Raspberry 3B+后准备拿来当学习嵌入式Linux及深度学习边缘计算的板子,从BootLoader到Linux内核再到深度学习推理框架。本文主要是让树莓派启动Linux内核后开启一个命令行窗口,旨在了解系统启动...
  • 树莓派开发板没有配置板载FLASH,因为它支持SD卡启动,所有我们需要下载相应镜像,并将其烧写在SD上,启动系统即可(这个镜像里包含了我们通常所说的bootloader、kernel、文件系统)。 树莓派由于其开源特性,支持非常...
  • Linux 交叉编译树莓派

    2021-04-07 17:24:24
    平台运行至少需要两样东西:BootLoader(启动引导代码)以及操作系统核心 宿主机(host):编辑和编译程序的平台,一般基于x86的pc机,通常也成为主机 目标机(target):用户开发的系统,通常都是非x86平台.host编译得到的可...
  • 1.获取所需源码 1)下载地址: ... 上面列出了树莓派所有的开源软件: linux:内核源码 ...tools:编译内核和其他源码所需的工具——交叉...firmware:树莓派的交叉编译好的二进制内核、模块、库、bootloader document...

空空如也

空空如也

1 2 3 4 5
收藏数 84
精华内容 33
关键字:

树莓派bootloader