草庐IT

AXI Quad SPI读写Flash做远程升级

同年纪_ 2024-01-09 原文

未经允许,本文禁止转载

目录

简介

AXI Quad SPI IP设置

寄存器说明

AXI Quad SPI支持的通用命令

读flash id

读flash 数据

擦除扇区

写flash 数据

注意事项


简介

        本文简要介绍xilinx 7系的AXI quad spi IP核的使用,主要用于读写boot用的flash(n25q128为例)做在线升级用。本文会略去很多细节,主要是因为我也没有搞得很懂,其次是很多细节可以在其他博客找到介绍。目前为止,我只尝试了使用axi lite接口配置寄存器,对flash读id,读数据,擦除扇区,写数据。后期会学习如何对flash进行分区管理,做升级备份以及针对不同flash加入quad的读写命令提高速率。

        串行flash通常指spi flash,有standard,dual,quad三种,flash的操作就是发送命令,发送地址(可选),写数据/读数据(可选)。各种模式间的区分主要在于传输数据在数据线上的分布。这里我描述不清楚,细节暂且略过。    

AXI Quad SPI IP设置

         手册pg153介绍了该ip的寄存器含义,在第五章节Example Programming Sequence介绍了几种flash操作方式的寄存器写顺序。IP配置中,XIP(eXecute In Place)即芯片内执行,指应用程序可以直接在flash闪存内运行,就是说提供一个memory map的操作接口让CPU直接访问地址,就像访问内存一样,而不是发送flash的cmd命令,相当于是flash里再集成了一个控制器,把读地址命令转换为各种读时序。注意XIP只能读flash。这里我用不上不勾选。勾选performance Mode就能有AXI4接口支持突发,目前也不需要。配置IP为quad模式,只有1个slave设备,设备类型是混合的,支持winbond,micron,spansion,macronix共有的命令。如果勾选Micron,就能支持micron的特殊命令,否则发送它的特殊命令,IPISR状态寄存器就会报command error。FIFO深度只有16和256两种选择。STARTUP原语勾选上后指SPI的clk就会从FPGA专用的CCLK引脚输出时钟。

         axi lite和spi的时钟频率在手册上有说明。spi_clk是操作flash clk的2倍,这个频率也要受到flash器件的约束。STARTUP_IO不用接,SPI_IO输出后用IOBUF引出到inout管脚即可,也可以自己写三态控制,spi_io0_t = 1时输出高阻。

 

寄存器说明

        寄存器说明在pg153的第二章节Register Space。主要寄存器如下。 

        该IP的操作原理就是,先配置SPI为master,配置相位/极性,复位fifo,禁止传输;再把命令和数据写到SPI_DTR寄存器里,再使能设备片选,使能传输,关闭片选,关闭传输;从SPI_DRR里读取出数据(可选)。此外可以配置中断,选择使能哪些中断,再打开全局中断使能,传输完后查询IPISR就知道当前传输有没有错误。

        40h:复位寄存器,写0xa复位整个IP,自动解复位。

        60h:控制寄存器,控制SPI的工作方式。

        64h:状态寄存器,查看fifo是否空满,用来判断是否传输结束。

        68h:发送fifo,往里面写数据,写满了会覆盖。所以不要写满。

        6ch:接收fifo,接收满了会自动丢弃后续数据。

        70h:片选,写0就表示0设备的cs拉低,某个设备片选结束后,写一个没有用的设备拉高cs。

        74h:发送fifo里有多少个数据。比如值5,表示里面有6个数据待发送。

        78h:接收fifo里有多少个数据。比如值5,表示里面有6个数据还没有读走。

AXI Quad SPI支持的通用命令

        查看flash手册可以发送,应该绝大多数flash都支持下命的这些命令。尤其是02/06/9f/d8,这也是我目前成功应用过的命令。

读flash id

        读flash id和普通读操作没有区别,先配置SPI,禁止发送,就是发送读cmd(9f),再写几个dummy(假的,没有意义的数据)用于交换数据出来,使能片选,使能发送,关闭片选,禁止发送,读数据。对应的手册描述如下所述。

具体代码如下所示。dummy的个数是可以自己控制的,要读多少数据就写几个dummy数据进去。

  REG_W(pstDev, 0x80040, 0xa);       //Software Reset Register
  REG_W(pstDev, 0x80028, 0x00003fff);//使能所有中断
  REG_W(pstDev, 0x8001c, 0x80000000);//打开全局中断使能
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x0000009f);//cmd = 9f,读flash id
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  REG_W(pstDev, 0x80068, 0x00000000);//dummy
  printf("Reg[0x%04X] : 0x%08X\n", 0x80074, REG_R(pstDev, 0x80074));
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe
  printf("Reg[0x%04X] : 0x%08X\n", 0x80078, REG_R(pstDev, 0x80078));
  for(i=0;i<11;i++){
    printf("data = 0x%08X\n", REG_R(pstDev, 0x8006c));
  }
  printf("Reg[0x%04X] : 0x%08X\n", 0x80020, REG_R(pstDev, 0x80020));
  REG_W(pstDev, 0x80020, REG_R(pstDev, 0x80020));//clear

读flash 数据

        手册描述和上面读id一致,只不过cmd = 03。并不是每次读需要复位0x40和设置中断。读数据时默认03命令后面需要3byte的addr,如果需要4byte的地址,命令根据flash不同会是不同的cmd,但一定不会是03。先发送高位地址。要注意,读回来的数据是4 + dummy 个数据。也就是说只要写一个数据到DTR里,就会有一个数据接收到写入DRR。所以读回来的数据会是FF FF FF FF xx xx......(真正数据)。要注意DRR fifo只有256深度,不要读太多数据。

  REG_W(pstDev, 0x80040, 0xa);       //Software Reset Register
  REG_W(pstDev, 0x80028, 0x00003fff);//使能所有中断
  REG_W(pstDev, 0x8001c, 0x80000000);//打开全局中断使能
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x00000003);//cmd = 03,读flash data
  //write addr
  REG_W(pstDev, 0x80068, (nOffset>>16)&0xff);
  REG_W(pstDev, 0x80068, (nOffset>>8)&0xff);
  REG_W(pstDev, 0x80068, (nOffset&0xff));
  for(i=0;i<nCount;i++){
     REG_W(pstDev, 0x80068, 0);//dummy
  }
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe
  for(i=0;i<nCount+4;i++){
    printf("data = 0x%08X\n", REG_R(pstDev, 0x8006c));
  }
  printf("Reg[0x%04X] : 0x%08X\n", 0x80020, REG_R(pstDev, 0x80020));
  REG_W(pstDev, 0x80020, REG_R(pstDev, 0x80020));//clear

擦除扇区

        每个flash都有自己的扇区,页写参数,d8命令是擦除1个扇区,但扇区的大小是不一样大的。例如n25q128手册上描述:

         可以看到,总共有16777216个字节,256个扇区(每个扇区64KB)有65536页(每页256字节)。所以执行一次擦除命令会擦除64KB的数据,把数据都写成0xFF,写数据只能把bit写成0。要注意的是,每一个擦除命令和写数据命令前都要有一个写使能命令(06)。擦除命令后面跟地址,就会擦除地址所在的地址对齐64KB。

 测试代码为:

  REG_W(pstDev, 0x80040, 0xa);       //Software Reset Register
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x00000006);//cmd = 06,写使能
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x000000d8);//cmd = d8,擦除扇区。
  //write addr
  REG_W(pstDev, 0x80068, (nOffset>>16)&0xff);
  REG_W(pstDev, 0x80068, (nOffset>>8)&0xff);
  REG_W(pstDev, 0x80068, (nOffset&0xff));
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe

写flash 数据

        和擦除扇区是类似的,写使能,写命令,写地址,写数据。

      要注意写FIFO也只有256字节深度,虽然页写是256字节。页写的意思,写命令后面最多跟这么多个数据,多余的数据就重复写入了。所以可以从0开始写128个,再从128写128个字节。

测试代码如下:

  REG_W(pstDev, 0x80040, 0xa);       //Software Reset Register
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x00000006);//cmd = 06,写使能
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe
  REG_W(pstDev, 0x80060, 0x000001e6);//复位tx rx fifo,
  REG_W(pstDev, 0x80060, 0x00000186);//解复位fifo
  REG_W(pstDev, 0x80068, 0x00000002);//cmd = 02 页写,256字节每页
  //write addr
  REG_W(pstDev, 0x80068, (nOffset>>16)&0xff);
  REG_W(pstDev, 0x80068, (nOffset>>8)&0xff);
  REG_W(pstDev, 0x80068, (nOffset&0xff));
  for(i=0;i<128;i++){
     REG_W(pstDev, 0x80068, i);//固定写入递增的数据
  }
  REG_W(pstDev, 0x80070, 0x00000000);//选择0通道cs
  REG_W(pstDev, 0x80060, 0x00000086);//使能master,开始发数据
  REG_W(pstDev, 0x80070, 0x00000001);//选择0通道cs拉高
  REG_W(pstDev, 0x80060, 0x00000186);//禁止master spe

 远程升级

        对于mcs,bin,bit,hex文件的区别,可以查看ug470 7 Series FPGAsConfiguration.      

         简单说就是bit文件,bit没有反序(每个字节的bit反序),是二进制文件。bin文件没有bit反序,二进制文件。MCS bit反序了,是ASCII文件,带有地址和校验。对于我们升级来说,bin文件就可以了。FPGA升级时从0开始读数据从到同步头aa 99 55 66,就表示一个有效的配置文件开始了。如下所示。

ffff ffff ffff ffff ffff ffff ffff ffff
ffff ffff ffff ffff ffff ffff ffff ffff
0000 00bb 1122 0044 ffff ffff ffff ffff
aa99 5566 2000 0000 3003 e001 0000 026b
3000 8001 0000 0012 2000 0000 3002 2001
0000 0000 3002 0001 0000 0000 3000 8001
0000 0000 2000 0000 3000 8001 0000 0007

        所以在线升级的简单设计就是拿到bin文件后,先根据bin文件大小擦除扇区,擦除每个扇区是是需要时间的,在手册里也有说明,擦除命令之间留出间隔即可,再从0开始直接写bin文件就可以了,写完后再读出校验。

        后续会研究如何做备份,普通升级写镜像时只写user image区域,当启动时发现user image启动失败,会自动跳转factory image,保证有出厂镜像里有在线升级的代码,防止一次升级失败导致必须返厂使用JTAG的问题。

注意事项

         如果使用JTAG to AXI Master,可以用JTAG去发送axi lite读写命令。

        使用方法:

                1.在bd中添加jtag to axi master ip,连接axi lite端口,配置address;

                2.编译工程,下载。

                3.先建立axi lite读写操作,再在vivado tcl console里执行,建立一次就可以了。

create_hw_axi_txn reset_qspi         [get_hw_axis hw_axi_1] -address 0x00000040 -data 0x0000000a -type write -force
create_hw_axi_txn rd_txn_lite_read_60 [get_hw_axis hw_axi_1] -address 00000060 -type read -force
run_hw_axi rd_txn_lite_read_74

        我的测试是把xdma的bypas接口接到了SPI IP,访问寄存器时,会发现读写数据都在变化,增加操作寄存器之间的延时,增加一定时间,例如10us即可。

        目前已验证A7上使用pcie xdma烧写bin文件到n25q128 flash,新bin能正常加载。驱动源码支持按文件大小擦除,逐页写入,支持读出校验。有需要可通过CSDN私信我,有偿提供技术支持,详解文章中未提及的部分编码细节。

        本文是本人原创,禁止任何形式的转载。

有关AXI Quad SPI读写Flash做远程升级的更多相关文章

  1. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  4. ruby - 在不使用 RVM 的情况下在 Mac 上卸载和升级 Ruby - 2

    我最近决定从我的系统中卸载RVM。在thispage提出的一些论点说服我:实际上,我的决定是,我根本不想担心Ruby的多个版本。我只想使用1.9.2-p290版本而不用担心其他任何事情。但是,当我在我的Mac上运行ruby--version时,它告诉我我的版本是1.8.7。我四处寻找如何简单地从我的Mac上卸载这个Ruby,但奇怪的是我没有找到任何东西。似乎唯一想卸载Ruby的人运行linux,而使用Mac的每个人都推荐RVM。如何从我的Mac上卸载Ruby1.8.7?我想升级到1.9.2-p290版本,并且我希望我的系统上只有一个版本。 最佳答案

  5. ruby-on-rails - 在 Flash 警报 Rails 3 中显示错误消息 - 2

    如果我在模型中设置验证消息validates:name,:presence=>{:message=>'Thenamecantbeblank.'}我如何让该消息显示在闪光警报中,这是我迄今为止尝试过的方法defcreate@message=Message.new(params[:message])if@message.valid?ContactMailer.send_mail(@message).deliverredirect_to(root_path,:notice=>"Thanksforyourmessage,Iwillbeintouchsoon")elseflash[:error]

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. ruby-on-rails - 从 Rails 2.3 升级到 Rails 4.0 - 2

    我们有一个目前在Rails2.3.12版和Ruby1.8.7版上运行的应用程序。我们想将我们的应用程序更新到Rails4.0和Ruby2.1.0。我们有大约200个模型和150个Controller。我想知道升级过程需要多大的努力。您还可以提供升级可以遵循的步骤。我们应该先升级Ruby然后再升级Rails还是相反? 最佳答案 您想要实现的目标将是史诗般的努力。我无法为您提供分步说明,因为不可能在一个答案中涵盖所有情况。我建议不要同时升级Ruby和Rails,而是分步升级。升级本身的复杂性是巨大的,但只要您的应用程序具有合理的测试覆盖

  8. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

  9. ruby-on-rails - Rails 4.1.0.beta1 升级失败 - 2

    我最近尝试安装rails4.1.0.beta1,但是railss导致以下错误。[RVM]/gems/ruby-2.0.0-p247/gems/activesupport-4.1.0.beta1/lib/active_support/core_ext/module/aliasing.rb:32:in`alias_method':undefinedmethod`graft'forclass`ActiveRecord::Associations::JoinDependency'(NameError)[RVM]/gems/ruby-2.0.0-p247/gems/activesupport-4

  10. jquery - Rails 如何创建 Jquery flash 消息而不是默认的 Rails 消息 - 2

    我想在页面顶部创建自定义Jquery消息而不是标准RailsFlash消息。我想在我的投票底部附近创建一条即时消息。我的Controller:defvote_up@post=Post.find(params[:id])current_user.up_vote(@post)flash[:message]='Thanksforvoting!'redirect_to(root_path,:notice=>'Takforditindlæg,deternuonline!')rescueMakeVoteable::Exceptions::AlreadyVotedErrorflash[:error]

随机推荐