现在的位置: 首页 > 综合 > 正文

OpenRisc-32-ORPSoC烧写外部spi flash

2014年02月10日 ⁄ 综合 ⁄ 共 26766字 ⁄ 字号 评论关闭

引言

经过前面的分析和介绍,我们对ORPSoC的启动过程(http://blog.csdn.net/rill_zhen/article/details/8855743)和

ORpSoC的debug子系统(http://blog.csdn.net/rill_zhen/article/details/9045837)有了初步的了解,

但是对于启动和debug前的工作还没有仔细分析过。比如FPGA是怎么config的,orpmon是怎么load的,是如何事先烧到外部的flash里的,等等。

本小节就解决这个问题。

1,系统的setup

1>回顾

根据前面的分析,我们知道了ORPSoC复位之后的操作,简单整理如下:

1》首先从0xf0000100(rom0的wishbone地址)这个地址取出rom0的内容(bootrom.S),并执行之。

2》bootrom.S中会通过地址为0xb000_0000(simple-spi的wishbone地址)的控制器从0xc0000(sizeword的地址)地址取出需要copy的内容的大小(sizeword)

3》根据sizeword的大小将0xc0004~0xc0004+sizeword的内容copy到SDRAM的0x0的位置。

4》copy完成后,产生一个reset中断,中断入口为0x100。

5》从SDRAM的0x100取指令,启动整个程序。

以上内容可以从

orpsocv2\boards\altera\ordb2a-ep4ce22\sw\board\include\board.h

orpsocv2\sw\bootrom\bootrom.S

中分析获得。

2>数据流向(data flow),从flash启动。

上面说了ORPSoC复位后的操作,但是在复位之前呢,如下图:一种颜色代表一条data path。

上面的机制可以通过schematic来进一步验证:

说明:

1》我们之前的实验都是通过jtag工具将svf文件,经FPGA本身的JTAG模块将ORPSoC配置到FPGA,然后通过or32-elf-gdb工具将可执行bin文件,经ORPSoC中的jtag_tap模块load到SDRAM中,再通过spr npc 0x100,设置启动地址,最终启动helloworld.HW ,orpmon和linux。最后通过picocom工具接受uart16550的内容,显示出来。

2》上面的过程,断电之后是不保存的,所以每次上电需要重新操作一遍才行。如果想上电自动执行,就需要将ORPSoC和需要执行的软件实现烧到外部的flash或者EEPROM或者SD card里面。

3》要想烧写外部的flash,首先需要将spi_fwd模块通过jtag工具将spoi_fwd.svf配置到FPGA里面,然后通关过spiflash-program工具将对应的rbf文件(ORPSoC的fpga bit文件),将orpmon.sizebin(前4字节是文件大小)/vmlinux.sizebin烧到0xc0000地址。这样上电之后,FPGA自动配置ORPSoC,然后or1200通过rom0中的bootrom.S经simple-spi模块和serialflashloader模块将flash中的软件copy到SDRAM,并运行之。

注:

1》spi_fwd和jtag_tap两个模块都是用户逻辑,并且两个模块对应的FPGA的引脚是一样的,两个模块对应的驱动工具分别是spiflash-program和or32-debug-proxy。ubuntu本地和官网svn有对应的c语言source code。感兴趣的可以下载下来看一下。

2》一般综合ORPSoC的工程后会自动生成rbf文件如果不能生成,需要手动生成,具体操作步骤,请参考后面的附录。

3>数据流向,从SD card启动。

上面分析了从外部flash启动的数据流向路径,要想从SDcard启动怎么办呢?步骤如下:

1》rbf文件仍然需要烧到外部的flash。这样上电之后FPGA本身的config模块才会自动load rbf文件内容来实现配置FPGA。

2》将软件事先通过PC烧到SDcard里面(起始地址要记住)。

3》修改bootrom.S代码,实现SD card controller的驱动,并将软件从SDcard copy到SDRAM。

2,program spi flash 

步骤如下:

1>配置spi_fwd模块到FPGA芯片

$ cd ~/program-spi-flash

$ jtag ./program_spi.jtag


2>擦除flash

 $ ./spiflash/spiflash-program -e

3>将ORPSoC的rbf文件烧到flash的0x0地址

$ ./spiflash/spiflash-program -p /home/openrisc/fpga_dev_board/ordb2a-ep4ce22/output_file.rbf

4>将booloader/vmlinux的sizebin文件烧到flash的0xc0000地址,具体烧什么要看你的板子的flash的大小而定(板子后面的U6和U7的型号),如果太小可以自己换一个大的(up to 8M bytes)。

$ ./spiflash/spiflash-program -a 0xc0000 -P /home/openrisc/program-spi-flash/vmlinux.sizebin

或者

$ ./spiflash/spiflash-program -a 0xc0000 -P /home/openrisc/program-spi-flash/orpmon.or32.sizebin

注:上面的2-4步可以通过一条命令实现:文件路径需要修改正确。

spiflash-program -e -p output_file.rbf -a 0xc0000 -P orpmon.or32.sizebin


5>重新插拔USB线

6>打开串口工具picocom


$ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2


7>应该能看到orpmon的输出信息,如下图(注:下图是我刚买回板子后用win7下的hyperterminal测试的截屏,不是这次实验的截屏。)




3,问题的解决

经过努力,终于可以完成烧写外部spi flash的工作了。上面的步骤没问题,问题出在工具上(spiflash-program),这个工具在ubuntu的镜像中针对的flash是64Mbit的,但是官网的板子出厂默认焊的是8Mbit的,所以就需要修改一下对应的spiflash-program.c,然后重新编译,然后重复上面的步骤即可。具体的代码修改,请参考附录。如果还没成功的话可以用‘-a addr  -l size -r read.data’参数将flash的addr地址的size大小的内容读到read.data文件,和打算烧录的文件进行对比,进一步查找问题。

下面是截屏:可以看到orpmon的输出信息。



4,小结

本小节介绍了ORPSoC的系统setup过程,并尝试烧写外部的flash。这样的话,每次上电就可以自动运行了。

附录A 生成rbf文件

如下图,根据sof文件生成rbf文件:用quartus_cpf工具生成。如果连sof文件都没有,可以用programmer工具根据svf文件生成sof文件。



附录B ORPSoC运行软件实验参考文档



===========================================================
Running Software on Hardware
===========================================================

This FPGA development board has been developed specifically 
to fit a OpenRISC processor design, with the smallest form-factor 
and lowest cost.

The board is called: ordb2a-ep4ce22 (OpenRisc Development Board 2A)

More detailed information can be found at the link below:

http://opencores.org/or1k/ordb2a-ep4ce22

===========================================================
Connecting ORSoC's OpenRISC-FPGA-development-board
===========================================================

1. Connect your ordb2a-board to your computer using the 
   USB connector located on the top-left corner on the board.

2. The FPGA is programmed with a pre-compiled OpenRISC
   processor SOC-design, with Ethernet, SDHC, UART and 
   SDRAM support. And with a small boot-loader (orpmon) that is 
   stored in the external SPI-flash. The boot-loader should 
   now be loaded and executing by the OpenRISC processor.

3. Open up a new terminal and type:
   $ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2

   Press "Enter" and you should now see a print-out in your 
   terminal-window showing: ORSoC devboard>

   You now have a command-prompt "ORSoC devboard>" controlling 
   the orpmon-software.
   By typing "help", all the diffent commands will be listed. 
   For example you can start a coremark-test (cpu benchmark test), 
   by typing "coremark 30"


===============================================================
Program ORSoC's OpenRISC-FPGA-development-board and run Linux
===============================================================

1. Connect your ordb2a-board to your computer using the 
   USB connector located on the top-left corner on the board.

2. Start a terminal and type:
   $ cd ~/fpga_dev_board/ordb2a-ep4ce22
   $ jtag ./program_fpga.jtag

   The file "program_fpga.jtag" defines what FPGA programming file 
   that should be used.

3. The FPGA is now programmed with a pre-compiled OpenRISC
   processor SOC-design, with the OpenRISC processor, Ethernet, 
   SDHC, UART and SDRAM support. An small boot-loader is stored 
   in the SPI-flash and should now be loaded and executed 
   by the OpenRISC processor.

4. Let's now try and download Linux to the SDRAM and then boot it
   on the OpenRISC SoC design. There are many ways that we can 
   boot Linux, we can use GDB, we can use orpmon and download it 
   using TFTP, or we can program the SPI-flash. We will using GDB
   in demo. Open up a new terminal tab (shift+ctrl+t) and type:
   $ /opt/or_debug_proxy/bin/or_debug_proxy -r 55555

   This starts a program (or_debug_proxy) that controls the 
   USB connection and communication between the GDB debugger and 
   the OpenRISC processor.
 
   You should see the follwoing print-out:

   "
   Connecting to OR1k via USB debug cable
   Initialising USB JTAG interface
   JTAG ID = a188a928
   Stalling OR1K CPU0
   Read      npc = 0001727c ppc = 00017278 r1 = 00031774
   Waiting for gdb connection on localhost:55555
   Press CTRL+c to exit.
   "

5. Open up a new terminal tab (shift+ctrl+t) and type:
   $ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2

   This UART connection will be our Linux-terminal when we boot-up 
   Linux on the OpenRISC SoC-design.


6. Open up a new terminal tab (shift+ctrl+t) and type:
   $ cd ~/soc-design/linux
   $ make ARCH=openrisc defconfig
   $ make ARCH=openrisc
   $ or32-elf-gdb

   The GDB-debugger is now started and you need to connect it to
   the or_debug_proxy program, by typing:
   (gdb) target remote :55555

   GDB is now connected to the OpenRISC processor and are now 
   waiting. Lets now download the Linux-image, by typing:
   (gdb) file ./vmlinux
   Answer "y" on the questions.

   (gdb) load

   The actual download of the Linux image is now in progress and 
   this takes some time, since the JTAG interface is not the 
   fastest one. The following load information should appear:

   "
   Loading section .text, size 0x22bd34 lma 0x0
   Loading section .rodata, size 0x49860 lma 0x22c000
   Loading section __param, size 0x1c0 lma 0x275860
   Loading section .data, size 0x15760 lma 0x276000
   Loading section __ex_table, size 0xa50 lma 0x28b760
   Loading section .head.text, size 0x4000 lma 0x28e000
   Loading section .init.text, size 0x12348 lma 0x292000
   Loading section .init.data, size 0x155e54 lma 0x2a4360
   Start address 0xc0000000, load size 4160160
   Transfer rate: 86 KB/sec, 4015 bytes/write.
   "

   Now we want to set the program-counter to start executing 
   from address 0x100, by typing:
   (gdb) spr npc 0x100

   Now let's boot up Linux on the FPGA development board, by typing:
   (gdb) c

   You should now see Linux booting in the picocom-terminal-window 
   that was opened earlier. And you should get a prompt where you 
   can play around with the Linux port that is running on the 
   OpenRISC processor system.

   For example, you can plug in your board into your Ethernet network 
   and get an IP address by typing:
   # ifup eth0

   Your network should now provide your board with an IP address (DHCP) 
   and you can test your connection by pinging a known IP address.


====================================================================
Program ORSoC's OpenRISC-FPGA-development-board with bare-metal SW
====================================================================

1. Connect your ordb2a-board to your computer using the 
   USB connector located on the top-left corner on the board.

2. Start a terminal and type:
   $ cd /home/openrisc
   $ jtag ./program_fpga.jtag

   The file "program_fpga.jtag" defines what FPGA programming file 
   that should be used.

3. The FPGA is now programmed with a pre-compiled OpenRISC
   processor SOC-design, with Ethernet, SDHC, UART and 
   SDRAM support. An small boot-loader is stored in the SPI-flash 
   and should now be loaded and executed by the OpenRISC processor.

4. Let's now try and download a bare-metal application (hello world) to 
   the board and run it. Open up a new terminal and type:
   $ /opt/or_debug_proxy/bin/or_debug_proxy -r 55555

   This starts a program (or_debug_proxy) that controls the 
   USB connection and communication between the GDB debugger and 
   the OpenRISC processor.
 
   You should see the following print-out:

   "
   Connecting to OR1k via USB debug cable
   Initialising USB JTAG interface
   JTAG ID = a188a928
   Stalling OR1K CPU0
   Read      npc = 0001727c ppc = 00017278 r1 = 00031774
   Waiting for gdb connection on localhost:55555
   Press CTRL+c to exit.
   "

5. Open up a new terminal and type:
   $ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2

   This UART connection will be a UART-terminal and will 
   receive the Helloworld application's printf text.


6. Open up a new terminal and type:
   $ cd ~/soc-design/helloworld-or1ksim
   $ or32-elf-gdb

   The GDB debugger is now started and you need to connect it to
   the or_debug_proxy program, by typing:
   (gdb) target remote :55555

   GDB is now connected to the OpenRISC processor and are now 
   waiting. Lets now download the Linux-image, by typing:
   (gdb) file ./helloworld_hw
   Answer "y" on the questions.

   (gdb) load

   The actual download of the Helloworld-image is now in progress and 
   this takes some time, since the JTAG interface is not the 
   fastest one. The following load-information should appear:

   "
   Loading section .vectors, size 0x2000 lma 0x0
   Loading section .init, size 0x28 lma 0x2000
   Loading section .text, size 0x494c lma 0x2028
   Loading section .fini, size 0x1c lma 0x6974
   Loading section .rodata, size 0x50 lma 0x6990
   Loading section .eh_frame, size 0x4 lma 0x69e0
   Loading section .ctors, size 0x8 lma 0x89e4
   Loading section .dtors, size 0x8 lma 0x89ec
   Loading section .jcr, size 0x4 lma 0x89f4
   Loading section .data, size 0x9b8 lma 0x89f8
   Start address 0x2028, load size 29616
   Transfer rate: 42 KB/sec, 1851 bytes/write.
   "

   Now we want to set the program-counter to start executing 
   from address 0x100, by typing:
   (gdb) spr npc 0x100

   Now lets start the Helloworld_hw application on the FPGA 
   development board, by typing:
   (gdb) c

   You should now see the follwoing printout in the 
   picocom terminal window that was opened earlier:

   "
   Hello world!!!!
   "

   Now you can start developing your own 
   bare-metal applications :-) 


===========================================================
Programming external SPI-flash 
===========================================================
The external SPI flash contains both the FPGA programming file 
and a bootloader (orpmon).
Below are information on how to program the SPI with these two files:


   1. Download an FPGA-programming file that just connects the 
      FTDI JTAG signals to the SPI flash IOs.
      $ cd ~/program-spi-flash
      $ jtag ./program_spi.jtag

   2. Erase the SPI flash before programming it:
      $ ./spiflash/spiflash-program -e


   3. Program the SPI flash with an FPGA programming file (rbf-format):
      $ ./spiflash/spiflash-program -p /home/openrisc/fpga_dev_board/ordb2a-ep4ce22/output_file.rbf

   4. Program the SPI-flash with OpenRISC SW application (select only one):

      4a. Program OpenRISC Linux (requires a large SPI flash):
          $ ./spiflash/spiflash-program -a 0xc0000 -P /home/openrisc/program-spi-flash/vmlinux.sizebin


      4b. Program OpenRISC bootloader (orpmon)
          $ ./spiflash/spiflash-program -a 0xc0000 -P /home/openrisc/program-spi-flash/orpmon.or32.sizebin


Unplug the board and connect it again to load the new flash contents.


Steps 2-4 can also be combined like so (directory paths omitted for brevity):
   spiflash-program -e -p output_file.rbf -a 0xc0000 -P orpmon.or32.sizebin

Sizebin files are produced from binary memory dumps using bin2binsizeword:
   ~/soc-design/orpsocv2/sw/utils/bin2binsizeword ~/soc-design/orpmon/orpmon.or32.bin ~/program-spi-flash/orpmon.or32.sizebin


Good luck and welcome to the OpenCores OpenRISC community ;-) 



Delivered by: Marcus.Erlandsson@orsoc.se, Yann.Vernier@orsoc.se
2011-12-15



附录C spiflash-program.c的修改:


修改后代码:

/* Compile with gcc -std=c99 `pkg-config --cflags --libs libftdi` */

#define _POSIX_C_SOURCE 2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ftdi.h>

void usage()
{
    fputs("Usage: spiflash-program [-s serial] [-a address] [-l length] [-d] [-e] [-[rRpP] filename] \n\
 serial selects an ORDB2A board to access\n\
 operations: d=detect, e=erase, r=read, p=program, lowercase for LSB first (use with RBF)\n\
 example: spiflash-program -e -p fpga.rbf -a 0xc0000 -P orpmon.or32.sizebin\n", stderr);
    exit(1);
}

void die(const char *s, struct ftdi_context *ctx)
{
    fprintf(stderr, "%s: %s\n", s, ftdi_get_error_string(ctx));
    exit(2);
}

// Actually const, but ftdi write is misdeclared.
unsigned char spi_select[3]={SET_BITS_LOW, 0x00, 0x0b},
                            spi_deselect[]={SET_BITS_LOW, 0x08, 0x0b}, // set CSn high
                                           //MPSSE_DO_WRITE, 0, 0, 0}, // clock out a byte to ensure CSn is high long enough
                                           init_mpsse0[]=
{
    TCK_DIVISOR, 99, 0,   // 60kHz - div=6e6/freq-1
    LOOPBACK_END
};

int spi_command(struct ftdi_context *ctx,
                const unsigned char *writecmd, int writecmdlen,
                const unsigned char *writedata, int writelen,
                unsigned char *readdata, int readlen,
                int lsbfirst)
{
    const int debug=1;
    unsigned char buf[sizeof spi_select+3+writecmdlen+3+writelen+
                      3+sizeof spi_deselect], *p=buf;
    int ret, len;

    //printf("LSB %s\n", lsbfirst?"first":"last");
    memcpy(p, spi_select, sizeof spi_select);
    p+=sizeof spi_select;

    if (writecmdlen)
    {
        *p++=MPSSE_DO_WRITE|MPSSE_WRITE_NEG;
        *p++=(writecmdlen-1)&0xff;
        *p++=((writecmdlen-1)>>8)&0xff;
        memcpy(p, writecmd, writecmdlen);
        p+=writecmdlen;
    }

    if (writelen)
    {
        *p++=MPSSE_DO_WRITE|MPSSE_WRITE_NEG | (lsbfirst?MPSSE_LSB:0);
        *p++=(writelen-1)&0xff;
        *p++=((writelen-1)>>8)&0xff;
        memcpy(p, writedata, writelen);
        p+=writelen;
    }

    if (readlen)
    {
        *p++=MPSSE_DO_READ | (lsbfirst?MPSSE_LSB:0);
        *p++=(readlen-1)&0xff;
        *p++=((readlen-1)>>8)&0xff;
    }

    memcpy(p, spi_deselect, sizeof spi_deselect);
    p+=sizeof spi_deselect;
    ret=ftdi_write_data(ctx, buf, p-buf);

    if (ret!=p-buf) return -1;
    if (debug)
    {
        printf("Wrote data:");

        for (ret=0; ret<p-buf; ret++)
            printf(" %02x", buf[ret]);

        printf("\n");
    }

    if (readlen)
    {
        len=0;

        while (len<readlen)
        {
            ret=ftdi_read_data(ctx, readdata+len, readlen-len);

            if (ret<0)
            {
                die("ftdi_read_data", ctx);
                return ret;
            }

            len+=ret;
        }

        ret=len;
    }

    if (debug)
        printf("spi_command returning %d\n", ret);

    return ret;
}

int waitonbusy(struct ftdi_context *ctx)
{
    unsigned char readstatus1=0x05, status1;
    int err;

    printf("Waiting for busy to clear");

    do
    {
        err=spi_command(ctx, &readstatus1, sizeof readstatus1, 0, 0,
                        &status1, sizeof status1, 0);
        if (err!=1)
            die("spi_command", ctx);

        putchar('.');
        fflush(stdout);
    }
    while (status1&0x01);

    putchar('\n');
    return err;
}

int writeenable(struct ftdi_context *ctx)
{
    unsigned char writeenable_cmd=0x06;
    return spi_command(ctx, &writeenable_cmd, sizeof writeenable_cmd, 0, 0, 0, 0, 0);
}

int detect(struct ftdi_context *ctx, long *memsize)
{
    int i;
    unsigned char idcmd=0x9F, idbuf[3];
    i=spi_command(ctx, &idcmd, sizeof idcmd, 0, 0, idbuf, sizeof(idbuf), 0);
    if (i!=sizeof(idbuf) )
        die("spi_command", ctx);

    /* TODO: detect capacity. Complete getopt-based parameter parsing. Update virtualbox image. */
    printf("Manufacturer %02x type %02x capacity %02x\n", idbuf[0], idbuf[1], idbuf[2]);
	
    if( idbuf[0]==0xEF ) /* W25Q Manufacturer’s ID ==> 0xEF */
    {
        printf("Winbond%s\n", (idbuf[1]==0x40 && idbuf[2]==0x17)? " W25Q64BV": "");
        if (idbuf[1]==0x40 && idbuf[2]==0x17)
            *memsize=8*1024*1024L;
    }
	else
	{
		printf("W25Q Manufacturer’s ID (0xEF) != %02X\n", idbuf[0] );
		// exit( 1 );
		*memsize=1*1024*1024L;
	}

    return 0;
}

struct ftdi_context *open_serial(const char *serial)
{
    struct ftdi_context *ctx;

    ctx=ftdi_new();

    if (!ctx)
    {
        fputs("Error initializing libftdi.\n", stderr);
        exit(2);
    }

    if (ftdi_set_interface(ctx, INTERFACE_B)<0)
        die("ftdi_set_interface", ctx);

    if (ftdi_usb_open_desc(ctx, 0x0403, 0x6011, NULL, serial)<0)
        die("ftdi_usb_open", ctx);

    if (ftdi_usb_reset(ctx)<0)
        die("ftdi_usb_reset", ctx);

    // if(ftdi_set_bitmode(ctx, 0x0000, BITMODE_RESET)<0)
    //   die("ftdi_set_bitmode", ctx);  // reset MPSSE
    if (ftdi_usb_purge_buffers(ctx)<0)
        die("ftdi_usb_purge_buffers", ctx);

    if (ftdi_write_data_set_chunksize(ctx, 0x10000)<0)
        die("ftdi_write_data_set_chunksize", ctx);

    if (ftdi_read_data_set_chunksize(ctx, 0x10000)<0)
        die("ftdi_read_data_set_chunksize", ctx);

    if (ftdi_set_event_char(ctx, 0, 0)<0)
        die("ftdi_set_event_char", ctx);

    if (ftdi_set_error_char(ctx, 0, 0)<0)
        die("ftdi_set_error_char", ctx);

    if (ftdi_set_latency_timer(ctx, 64)<0)
        die("ftdi_set_latency_timer", ctx);

    if (ftdi_set_bitmode(ctx, 0x0000, BITMODE_MPSSE)<0)
        die("ftdi_set_bitmode", ctx);

    if (ftdi_usb_reset(ctx)<0)
        die("ftdi_usb_reset", ctx);

    if (ftdi_usb_purge_buffers(ctx)<0)
        die("ftdi_usb_purge_buffers", ctx);

    if (ftdi_setflowctrl(ctx, SIO_RTS_CTS_HS)<0)
        die("ftdi_set_flowctrl", ctx);

    if (ftdi_write_data(ctx, init_mpsse0, sizeof init_mpsse0)!=sizeof init_mpsse0)
        die("ftdi_write_data", ctx);

    if (ftdi_usb_reset(ctx)<0)
        die("ftdi_usb_reset", ctx);

    if (ftdi_write_data(ctx, spi_deselect, sizeof spi_deselect)!=sizeof spi_deselect)
        die("ftdi_write_data", ctx);

    return ctx;
}

int main(int argc, char *argv[])
{
    int lsbfirst=0;
    // unsigned char chiperase=0x60; // or 0xc7
    unsigned char chiperase = 0xc7;
    enum {NONE, DETECT, ERASE, READ, WRITE} op=NONE;
    long address= 0;
	long size	= -1;
	long memsize= -1;
    char *p;
    const char *serial=NULL;
    int opt, err, didops=0;
    FILE *file=NULL;
    struct ftdi_context *ctx=NULL;

    /* -W is not usable due to POSIX.2 */
    const char *opts="der:R:p:P:a:l:s:";

    for( opt=getopt(argc, argv, opts); opt>=0; opt=getopt(argc, argv, opts) )
    {
        switch (opt)
        {
        case 'l':   /* length */
            size=strtol(optarg, &p, 0);
            if (*p!=0 || size<=0)
			{
                usage( );
			}
            printf("Size %#lx (%ld)\n", size, size);
            break;
			
        case 's':   /* serial */
            serial=optarg;
            break;
			
        case 'a':   /* address */
            address=strtol(optarg, &p, 0);
            if( *p!=0 || address<-1 )
            {
                fprintf(stderr, "Improper length? Got %ld, \"%s\" left.\n", address, p);
                usage( );
            }
            printf("Address %#lx (%ld)\n", address, address);
            break;
			
        case 'd':   /* detect */
            op=DETECT;
            break;
			
        case 'e':   /* erase */
            op=ERASE;
            break;
			
        case 'r':   /* read LSB first */
            lsbfirst=1;
            op=READ;
            break;
			
        case 'R':   /* read MSB first */
            lsbfirst=0;
            op=READ;
            break;
			
        case 'p':   /* write LSB first */
            lsbfirst=1;
            op=WRITE;
            break;
			
        case 'P':   /* write MSB first */
            lsbfirst=0;
            op=WRITE;
            break;
			
		default:	/* Unknown Param */
			usage( );
			break;
        }

        /* Continue parsing arguments if this was not a command */
        if( op==NONE )
            continue;

        if( !ctx )
        {
            ctx=open_serial(serial);
            if (!ctx)
			{
                die("Internal error opening FTDI port", ctx);
			}
        }

        err = detect(ctx, &memsize);
        if( err<0 )
		{
            die("detect", ctx);
		}

        if( op==ERASE && size<0 )
        {
            printf(" \tJF DBG : ERASE Enabling write\n");

            if (writeenable( ctx )<0)
                die("writeenable", ctx);

            printf("\tJF DBG : Sending chip erase\n");

            if (spi_command(ctx, &chiperase, sizeof chiperase, 0, 0, 0, 0, 0)<0)
                die("chiperase", ctx);

            waitonbusy( ctx );
            op=NONE;
			
			printf(" \tJF DBG : ERASE Operator Finish\n");
        }

        switch( op )
        {
        case DETECT:
            break; /* already detected */

        case READ:
        case WRITE:
            file=fopen(optarg, op==WRITE?"rb":"wb");
            if (!file)
            {
                perror(optarg);
                usage();
            }

            /* Try to detect size */
            if( size<0 )
            {
                size = memsize;

                if (op==WRITE)
                {
                    int i=fseek(file, 0, SEEK_END);

                    if (i==0)
                    {
                        size=ftell(file);
                        fseek(file, 0, SEEK_SET);
                    }
                    else
                    {
                        fprintf(stderr, "Warning: Could not detect file size, assuming %ld.\n", memsize);
                    }
                }
                else
                {
                    fprintf(stderr, "Warning: Length not given, assuming %ld.\n", memsize);
                }
            }

        /* fall through */
        case ERASE:
            /* Process data in sectors */
            while( size>0 )
            {
                // 0x100 is page size / largest write, 0x1000 is sector size / smallest erase
				// W25Q80BV: 256-byte per programmable page   => 0x100
				//           Uniform Sector Erase (4K-bytes)  => 0x1000
				// 			 Uniform Block Erase (32K and 64K-bytes)
				// 			 Program one to 256 bytes
                int pagesize= op==ERASE ? 0x1000 : 0x100-(address&0xff);
                unsigned char buf[pagesize+4];
                switch( op )
                {
                case WRITE:
                    buf[0]=0x02;
                    break;
					
                case READ:
                    buf[0]=0x03;
                    break;
					
                case ERASE:
                    buf[0]=0x20;
                    break;
                }

                buf[1]=(address>>16)&0xff;
                buf[2]=(address>>8)&0xff;
                buf[3]=(address)&0xff;

                if (op!=ERASE && pagesize>size)
                    pagesize=size;     // don't read/write more than asked for

                printf("%sing %d bytes at %#lx (%ld left)\n", 
				        op==WRITE ? "Writ":op==READ?"Read":"Eras", 
						pagesize, address, size);
                if( op==ERASE || op==WRITE )
                {
                    if( op==WRITE )
                    {
                        pagesize=fread(buf+4, 1, pagesize, file);
                        if (pagesize<0)
                        {
                            perror("Reading file");
                            return 1;
                        }
                        else if (pagesize==0)
                        {
                            printf("End of file, %ld bytes remaining.\n", size-pagesize);
                            return 0;
                        }
                    }

                    if (writeenable(ctx)<0)
                        die("writeenable", ctx);

                    if (spi_command(ctx, buf, 4, buf+4, op==WRITE? pagesize:0, 0, 0, lsbfirst)<0)
                        die("spi_command", ctx);

                    waitonbusy(ctx);
                }
                else
                {
                    err=spi_command(ctx, buf, 4, 0, 0, buf+4, pagesize, lsbfirst);

                    if (err<0)
                        die("spi_command read", ctx);

                    pagesize=fwrite(buf+4, 1, err, file);

                    if (pagesize<0)
                    {
                        perror("Writing file");
                        return 1;
                    }
                }

                size-=pagesize;
                address+=pagesize;
            }
        }

        didops++;
        op=NONE;
    }

    if (optind<argc || didops==0)
        usage();

    printf("%d operations done.\n", didops);

    return 0;
}

修改前代码:


/* Compile with gcc -std=c99 `pkg-config --cflags --libs libftdi` */

#define _POSIX_C_SOURCE 2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ftdi.h>

void usage() {
  fputs("Usage: spiflash-program [-s serial] [-a address] [-l length] [-d] [-e] [-[rRpP] filename] \n\
 serial selects an ORDB2A board to access\n\
 operations: d=detect, e=erase, r=read, p=program, lowercase for LSB first (use with RBF)\n\
 example: spiflash-program -e -p fpga.rbf -a 0xc0000 -P orpmon.or32.sizebin\n", stderr);
  exit(1);
}

void die(const char *s, struct ftdi_context *ctx) {
  fprintf(stderr, "%s: %s\n", s, ftdi_get_error_string(ctx));
  exit(2);
}

// Actually const, but ftdi write is misdeclared.
unsigned char spi_select[3]={SET_BITS_LOW, 0x00, 0x0b},
  spi_deselect[]={SET_BITS_LOW, 0x08, 0x0b}, // set CSn high
  //MPSSE_DO_WRITE, 0, 0, 0}, // clock out a byte to ensure CSn is high long enough
  init_mpsse0[]={
    TCK_DIVISOR, 99, 0,   // 60kHz - div=6e6/freq-1
    LOOPBACK_END};

int spi_command(struct ftdi_context *ctx, 
		const unsigned char *writecmd, int writecmdlen,
		const unsigned char *writedata, int writelen,
		unsigned char *readdata, int readlen,
		int lsbfirst) {
  const int debug=0;
  unsigned char buf[sizeof spi_select+3+writecmdlen+3+writelen+
		    3+sizeof spi_deselect], *p=buf;
  int ret, len;

  //printf("LSB %s\n", lsbfirst?"first":"last");
  memcpy(p, spi_select, sizeof spi_select);
  p+=sizeof spi_select;
  if (writecmdlen) {
    *p++=MPSSE_DO_WRITE|MPSSE_WRITE_NEG;
    *p++=(writecmdlen-1)&0xff;
    *p++=((writecmdlen-1)>>8)&0xff;
    memcpy(p, writecmd, writecmdlen);
    p+=writecmdlen;
  }
  if (writelen) {
    *p++=MPSSE_DO_WRITE|MPSSE_WRITE_NEG | (lsbfirst?MPSSE_LSB:0);
    *p++=(writelen-1)&0xff;
    *p++=((writelen-1)>>8)&0xff;
    memcpy(p, writedata, writelen);
    p+=writelen;
  }
  if (readlen) {
    *p++=MPSSE_DO_READ | (lsbfirst?MPSSE_LSB:0);
    *p++=(readlen-1)&0xff;
    *p++=((readlen-1)>>8)&0xff;
  }
  memcpy(p, spi_deselect, sizeof spi_deselect);
  p+=sizeof spi_deselect;
  ret=ftdi_write_data(ctx, buf, p-buf);
  if (ret!=p-buf) return -1;
  if (debug) {
    printf("Wrote data:");
    for (ret=0; ret<p-buf; ret++)
      printf(" %02x", buf[ret]);
    printf("\n");
  }
  if (readlen) {
    len=0;
    while (len<readlen) {
      ret=ftdi_read_data(ctx, readdata+len, readlen-len);
      if (ret<0) {
	die("ftdi_read_data", ctx);
	return ret;
      }
      len+=ret;
    }
    ret=len;
  }
  if (debug)
    printf("spi_command returning %d\n", ret);
  return ret;
}

int waitonbusy(struct ftdi_context *ctx) {
  unsigned char readstatus1=0x05, status1;
  int err;

  printf("Waiting for busy to clear");
  do {
    err=spi_command(ctx, &readstatus1, sizeof readstatus1, 0, 0,
		    &status1, sizeof status1, 0);
    if (err!=1)
      die("spi_command", ctx);
    putchar('.');
    fflush(stdout);
  } while (status1&0x01);
  putchar('\n');
  return err;
}

int writeenable(struct ftdi_context *ctx) {
  unsigned char writeenable_cmd=0x06;
  return spi_command(ctx, &writeenable_cmd, sizeof writeenable_cmd, 0, 0,
		     0, 0, 0);
}

int detect(struct ftdi_context *ctx, long *memsize) {
  unsigned char idcmd=0x9f, idbuf[3];
  int i;
  i=spi_command(ctx, &idcmd, sizeof idcmd, 0, 0, idbuf, sizeof idbuf, 0);
  if (i!=sizeof idbuf)
    die("spi_command", ctx);
  /* TODO: detect capacity. Complete getopt-based parameter parsing. Update virtualbox image. */
  printf("Manufacturer %02x type %02x capacity %02x\n",
	 idbuf[0], idbuf[1], idbuf[2]);
  if(idbuf[0]==0xef) {
    printf("Winbond%s\n",
	   (idbuf[1]==0x40 && idbuf[2]==0x17)?" W25Q64BV":
	   "");
    if (idbuf[1]==0x40 && idbuf[2]==0x17)
      *memsize=8*1024*1024L;
  }
  return 0;
}

struct ftdi_context *open_serial(const char *serial) {
    struct ftdi_context *ctx;

    ctx=ftdi_new();
    if (!ctx) {
      fputs("Error initializing libftdi.\n", stderr);
      exit(2);
    }
    if(ftdi_set_interface(ctx, INTERFACE_B)<0)
      die("ftdi_set_interface", ctx);

    if(ftdi_usb_open_desc(ctx, 0x0403, 0x6011, NULL, serial)<0)
      die("ftdi_usb_open", ctx);

    if(ftdi_usb_reset(ctx)<0)
      die("ftdi_usb_reset", ctx);
    //if(ftdi_set_bitmode(ctx, 0x0000, BITMODE_RESET)<0)
    //  die("ftdi_set_bitmode", ctx);  // reset MPSSE
    if(ftdi_usb_purge_buffers(ctx)<0)
      die("ftdi_usb_purge_buffers", ctx);
    if(ftdi_write_data_set_chunksize(ctx, 0x10000)<0)
      die("ftdi_write_data_set_chunksize", ctx);
    if(ftdi_read_data_set_chunksize(ctx, 0x10000)<0)
      die("ftdi_read_data_set_chunksize", ctx);
    if(ftdi_set_event_char(ctx, 0, 0)<0)
      die("ftdi_set_event_char", ctx);
    if(ftdi_set_error_char(ctx, 0, 0)<0)
      die("ftdi_set_error_char", ctx);
    if(ftdi_set_latency_timer(ctx, 64)<0)
      die("ftdi_set_latency_timer", ctx);
    if(ftdi_set_bitmode(ctx, 0x0000, BITMODE_MPSSE)<0)
      die("ftdi_set_bitmode", ctx);
    if(ftdi_usb_reset(ctx)<0)
      die("ftdi_usb_reset", ctx);
    if(ftdi_usb_purge_buffers(ctx)<0)
      die("ftdi_usb_purge_buffers", ctx);
    if(ftdi_setflowctrl(ctx, SIO_RTS_CTS_HS)<0)
      die("ftdi_set_flowctrl", ctx);
    if(ftdi_write_data(ctx, init_mpsse0, sizeof init_mpsse0)!=sizeof init_mpsse0)
      die("ftdi_write_data", ctx);
    if(ftdi_usb_reset(ctx)<0)
      die("ftdi_usb_reset", ctx);
    if(ftdi_write_data(ctx, spi_deselect, sizeof spi_deselect)!=sizeof spi_deselect)
      die("ftdi_write_data", ctx);
    return ctx;
}

int main(int argc, char *argv[]) {
  int lsbfirst=0;
  unsigned char chiperase=0x60; // or 0xc7
  enum {NONE, DETECT, ERASE, READ, WRITE} op=NONE;
  long address=0, size=-1, memsize=-1;
  char *p;
  const char *serial=NULL;
  int opt, err, didops=0;
  FILE *file=NULL;
  struct ftdi_context *ctx=NULL;

  /* -W is not usable due to POSIX.2 */
  const char *opts="der:R:p:P:a:l:s:";
  for (opt=getopt(argc, argv, opts); opt>=0;
       opt=getopt(argc, argv, opts)) {
    switch (opt) {
    case 'l':   /* length */
      size=strtol(optarg, &p, 0);
      if (*p!=0 || size<=0)
	usage();
      printf("Size %#lx (%ld)\n", size, size);
      break;
    case 's':   /* serial */
      serial=optarg;
      break;
    case 'a':   /* address */
      address=strtol(optarg, &p, 0);
      if (*p!=0 || address<-1) {
	fprintf(stderr, "Improper length? Got %ld, \"%s\" left.\n", address, p);
	usage();
      }
      printf("Address %#lx (%ld)\n", address, address);
      break;
    case 'd':   /* detect */
      op=DETECT;
      break;
    case 'e':   /* erase */
      op=ERASE;
      break;
    case 'r':   /* read LSB first */
      lsbfirst=1;
      op=READ;
      break;
    case 'R':   /* read MSB first */
      lsbfirst=0;
      op=READ;
      break;
    case 'p':   /* write LSB first */
      lsbfirst=1;
      op=WRITE;
      break;
    case 'P':   /* write MSB first */
      lsbfirst=0;
      op=WRITE;
      break;
    }

    /* Continue parsing arguments if this was not a command */
    if (op==NONE)
      continue;
    
    if (!ctx) {
      ctx=open_serial(serial);
      if (!ctx)
	die("Internal error opening FTDI port", ctx);
    }
    err=detect(ctx, &memsize);
    if (err<0)
      die("detect", ctx);

    if (op==ERASE && size<0) {
      printf("Enabling write\n");
      if (writeenable(ctx)<0)
	die("writeenable", ctx);
      printf("Sending chip erase\n");
      if (spi_command(ctx, &chiperase, sizeof chiperase, 0, 0, 0, 0, 0)<0)
	die("chiperase", ctx);
      waitonbusy(ctx);
      op=NONE;
    }

    switch (op) {
    case DETECT:
      break; /* already detected */
    case READ:
    case WRITE:
      file=fopen(optarg, op==WRITE?"rb":"wb");
      if(!file) {
	perror(optarg);
	usage();
      }
      /* Try to detect size */
      if (size<0) {
	size=memsize;
	if (op==WRITE) {
	  int i=fseek(file, 0, SEEK_END);
	  if (i==0) {
	    size=ftell(file);
	    fseek(file, 0, SEEK_SET);
	  } else {
	    fprintf(stderr, "Warning: Could not detect file size, assuming %ld.\n", memsize);
	  }
	} else {
	  fprintf(stderr, "Warning: Length not given, assuming %ld.\n", memsize);
	}
      }
      /* fall through */
    case ERASE:
      /* Process data in sectors */
      while (size>0) {
	// 0x100 is page size / largest write, 0x1000 is sector size / smallest erase
	int pagesize=op==ERASE?0x1000:0x100-(address&0xff);
	unsigned char buf[pagesize+4];
	switch (op) {
	case WRITE: buf[0]=0x02; break;
	case READ:  buf[0]=0x03; break;
	case ERASE: buf[0]=0x20; break;
	}
	buf[1]=(address>>16)&0xff;
	buf[2]=(address>>8)&0xff;
	buf[3]=(address)&0xff;
	if (op!=ERASE && pagesize>size)
	  pagesize=size;     // don't read/write more than asked for
	printf("%sing %d bytes at %#lx (%ld left)\n", op==WRITE?"Writ":op==READ?"Read":"Eras", pagesize, address, size);
	if (op==ERASE || op==WRITE) {
	  if (op==WRITE) {
	    pagesize=fread(buf+4, 1, pagesize, file);
	    if (pagesize<0) {
	      perror("Reading file");
	      return 1;
	    } else if (pagesize==0) {
	      printf("End of file, %ld bytes remaining.\n", size-pagesize);
	      return 0;
	    }
	  }
	  if (writeenable(ctx)<0)
	    die("writeenable", ctx);
	  if (spi_command(ctx, buf, 4, buf+4, op==WRITE?pagesize:0, 0, 0, lsbfirst)<0)
	    die("spi_command", ctx);
	  waitonbusy(ctx);
	} else {
	  err=spi_command(ctx, buf, 4, 0, 0, buf+4, pagesize, lsbfirst);
	  if (err<0)
	    die("spi_command read", ctx);
	  pagesize=fwrite(buf+4, 1, err, file);
	  if (pagesize<0) {
	    perror("Writing file");
	    return 1;
	  }
	}
	size-=pagesize;
	address+=pagesize;
      }
    }

    didops++;
    op=NONE;
  }

  if (optind<argc || didops==0)
    usage();

  printf("%d operations done.\n", didops);

  return 0;
}

抱歉!评论已关闭.