dma 对文件读写的影响_【正点原子FPGA连载】第九章AXI4接口之DDR读写--摘自达芬奇之Microblaze 开发指南...

1)实验平台:正点原子达芬奇FPGA开发板

2) 摘自【正点原子】达芬奇之Microblaze 开发指南

3)购买链接:https://detail.tmall.com/item.htm?id=624335496505

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html

5) 正点原子官方B站:https://space.bilibili.com/394620890

6)对正点原子FPGA感兴趣的同学可以加群讨论:876744900

第九章AXI4接口之DDR读写实验

Xilinx从Spartan-6和Virtex-6系列开始使用AXI协议来连接IP核。在7系列器件中,Xilinx在IP核中继续使用AXI协议。本章我们对AXI协议作一个简单介绍,并在Vivado中实现一个AXI4接口的IP核,用于对DDR3进行读写测试。
本章包括以下几个部分:
99.1简介
9.2实验任务
9.3硬件设计
9.4软件设计
9.5下载验证

9.1简介

我们在前面的实验中介绍了一些带AXI接口的IP核的使用,比如《AXI GPIO控制LED实验》中的AXI4 GPIO IP核和《按键中断实验》中的AXI中断控制器等,这些IP核都带有AXI4接口。其中AXI4-Lite接口属于AXI4总线协议,接下来我们将对该协议作一个更具体的介绍。

AXI的英文全称是Advanced eXtensible Interface,即高级可扩展接口,它是ARM公司所提出的AMBA(Advanced Microcontroller Bus Architecture)协议的一部分。在介绍AXI协议之前,我们首先要对通信协议有一个基本的概念。

简单来说,通信协议就是指双方进行信息传递所遵循的规则和约定。其实在我们的生活当中,比如在打电话的时候,就遵循着一些基本的“通信协议”。为了更形象的说明这一概念,我们首先来看一个通话记录:

《少林寺的通话记录》

老方丈:下午张三丰和灭绝师太要来参观,你去机场接一下,我把他俩手机号给你 //主机发送控制信号

小和尚:好的,稍等,我找张纸记一下。

老方丈:嗯。 //主机等待

小和尚:我准备好了,您说吧。 //从机返回Ready信号

老方丈:张三丰的是“123-321-34567”,灭绝师太的是“123-456-56789”。 //主机突发传输数据

小和尚:记下来了。 //从机返回响应信号

上面的通话记录是一次完整的通信过程,传输的信息是两个手机号。我们把“老方丈”当成主机,“小和尚”当成从机,那么这一通信过程由主机发起,最终向从机写入两组数据(手机号)。我们需要注意的是整个过程中二者的协调配合:为了确保数据传输无误,主机需要等从机准备好之后才能发送数据;另外从机在接收数据完成后,会发送响应信号,表示传输完成。

然后再来看另外一个通话记录:

《武当山的通话记录》

张三丰:下午我要去趟少林寺,你把方丈的手机号找给我 //主机发送控制信号

小道士:找到了,188-666…… //从机返回有效数据

张三丰:等一下,我找支笔。好了,你说吧 //主机发送Ready信号

小道士:188-666-66666,念完了 //从机返回有效数据,以及响应信号

张三丰:好的。

在武当山的通话记录中,张三丰是主机,小道士是从机。通信过程同样是由主机发起,向从机请求数据。主机准备好之后发送Ready信号,接下来从机开始发送数据。从机在数据发送完成后给出响应信号,表明本次传输结束。

对比上述两个通话记录可以发现,少林寺的通话是一次主机向从机写数据的过程,而武当山的通话则是主机向从机读数据的过程。在通信过程中,主从之间会进行协调,只有等接收方准备好之后,才能开始数据传输,这种机制我们称之为“握手”。

在打电话的时候,通话双方能够理解彼此的语言,进而从中筛选有效信息。而在数字电路中,通信双方就没有那么智能了,主设备和从设备需要按照约定好的数据传输方式来发送和接收数据。AXI协议就是描述了主设备和从设备之间的数据传输方式,在该协议中,主设备和从设备之间通过握手信号建立连接。

AXI协议是一种高性能、高带宽、低延迟的片内总线,具有如下特点:

1、总线的地址/控制和数据通道是分离的;

2、支持不对齐的数据传输;

3、支持突发传输,突发传输过程中只需要首地址;

4、具有分离的读/写数据通道;

5、支持显著传输访问和乱序访问;

6、更加容易进行时序收敛。

在数字电路中只能传输二进制数0和1,因此可能需要一组信号才能高效地传输信息,这一组信号就组成了接口。AXI4协议支持以下三种类型的接口:

1、AXI4:高性能存储映射接口。

2、AXI4-Lite:简化版的AXI4接口,用于较少数据量的存储映射通信。

3、AXI4-Stream:用于高速数据流传输,非存储映射接口。

在这里我们首先解释一下存储映射(Meamory Map)这一概念。如果一个协议是存储映射的,那么主机所发出的会话(无论读或写)就会标明一个地址。这个地址对应于系统存储空间中的一个地址,表明是针对该存储空间的读写操作。

AXI4协议支持突发传输,主要用于处理器访问存储器等需要指定地址的高速数据传输场景。AXI-Lite为外设提供单个数据传输,主要用于访问一些低速外设中的寄存器。而AXI-Stream接口则像FIFO一样,数据传输时不需要地址,在主从设备之间直接连续读写数据,主要用于如视频、高速AD、PCIe、DMA接口等需要高速数据传输的场合。

在本章我们重点介绍AXI4接口,它由五个独立的通道构成:

1、读地址

2、读数据

3、写地址

4、写数据

5、写响应

下面是使用读地址和读数据通道实现读传输过程的示意图:

9ff8a6b8d49c8b33af3a32322dcc823e.png

图 9.1.1 读传输过程示意图

从上图中可以看到,在一个读传输过程中,主机首先在读地址通道给出读地址和控制信号,然后从机由读数据通道返回读出的数据。另外我们需要注意的是,这是一次突发读操作,主机只给出一个地址,从该地址连续突发读出四个数据。

写传输过程如图 9.1.2所示,它用到了写地址、写数据和写响应三个通道。主机在写地址通道给出写地址和控制信号,然后在写数据通道连续突发写四个数据。从机在接收数据之后,在写响应通道给出响应信号。

c7fc0d4ed0b70020f2fac039ffbd9f14.png

图 9.1.2 写传输过程示意图

AXI总线中的每个通道都包含了一组信息信号,还有一个VALID和一个READY信号。VALID信号由源端(source)产生,表示当前地址或者数据线上的信息是有效的;而READY信号由目的端(destination)产生,则表示已经准备好接收地址、数据以及控制信息。VALID和READY信号提供了AXI总线中的握手机制,如下图所示:

89e0dcd900a1a1af960db986bb7381a3.png

图 9.1.3 VALID和READY握手机制

在图 9.1.3中,ACLK为时钟信号,在AXI协议中,所有的输入信号都在是ACLK的上升沿采样,所有的输出信号必须在ACLK的上升沿之后才能改变。在T1之后,源端将VALID拉高,表明INFORMATION信号线上传输的是有效的地址、数据或者控制信息。目的端在T2之后将READY拉高,表明它已经准备好接收数据,此时源端必须保持INFORMATION数据稳定不变,直到T3时刻进行数据传输。

需要注意的是,源端不允许等目的端的READY信号拉高之后,才将VALID信号置为有效状态。而且,一旦VALID拉高,源端必须保持其处于有效状态,直至成功握手(在时钟上升沿检测到VALID和READY同时为有效状态)。

AXI协议的五个通道都有各自的VALID/READY握手信号对,每个通道握手信号对的名称如下图所示:

fc5110419fa589b9d585ba9ea5d14e26.png

图 9.1.4 各通道握手信号名称

到这里,我们已经简单介绍了AXI4协议的读写过程,以及握手协议。关于如何实现AXI4通信协议,以及如何在设计中使用该协议进行通信,我们将硬件设计部分进行讲解。

9.2实验任务

本章的实验任务是通过自定义一个AXI4接口的IP核,通过AXI接口对DDR3进行读写测试。

9.3硬件设计

根据实验任务我们可以画出本次实验的系统框图,如下图所示:

8aec893c245cd5babd327296634e7ebc.png

图 9.3.1 系统框图

在上图中,DDR3_TEST是我们自定义的IP核,具有AXI4 Master端口,该端口通过AXI Interconnect模块,最终连接MIG IP核。AXI UART IP核打印从DDR3中读出的数据。DDR3_TEST IP核在检测到按键按下后会启动读写过程,并将读出的数据与写入的数据作比较,比较完成后点亮LED1。另外,如果在读写过程中出错,或者在比较的过程中发现读出的数据与写入的数据不一致,LED2会点亮。

在进行硬件设计之前,我们需要先自定义一个带有AXI4 Master端口的IP核,并将其添加到工程的IP库中。我们在《自定义IP核-呼吸灯实验》中介绍了如何定义一个带有AXI-Lite Slave接口的IP核,在本次实验中定义IP的方法与之相同,只是这次我们要选择AXI4 Master接口。本次实验我们将在《Hello World》实验的基础上进行。

打开《Hello World》实验的工程,我们先打开《Hello World》实验的Vivado工程,打开后依次点击菜单栏的“File-> Project->Save As...”,将工程名改为“axi4_ddr_rw”。

在菜单栏中点击“Tools”,然后在下拉列表中选择“Create and Package New IP”,如图 9.3.2所示:

431f56988cb54d97e87efbf8e0164a7b.png

图 9.3.2 创建IP

在弹出的对话框中直接点击“Next”,如图 9.3.3所示:

268ccd0bc0abf57b6bc202daae7f561f.png

图 9.3.3 创建和封装IP

在弹出的界面中选择“Create a new AXI4 peripheral”,点击“Next”。

接下来设置IP核的名称为“DDR3_TEST”,并将IP的路径修改为当前工程路径下的“ip_repo”文件夹,最后点击“Next”,如图 9.3.4所示:

0185ddb7ed99d9a4879909a3747c9c7c.png

图 9.3.4 设置IP名称和路径

在“Add Interfaces”界面中修改接口名称为“M_AXI”,选择接口类型为“Full”,接口模式为“Master”,数据位宽为“32”。如图 9.3.5所示:

38b5ba456ea5c15e6376cf77d8cd0795.png

图 9.3.5 自定义IP核接口配置

上图中对IP核接口的配置与《自定义IP核—呼吸灯实验》不同,在这里我们新建的IP核作为主机(Master),除此之外我们使用的接口类型变成了AXI-Full,而不再是AXI-Lite。

设置完成后点击上图中右下角的“Next”。

接下来选择“Edit IP”,最后点击“Finish”。如图 9.3.6所示:

df5d01181870bd358f5542bbdcbb9e57.png

图 9.3.6 选择Edit IP

在上图中点击“Finish”后会自动打开一个新的Vivado工程,工程名为“edit_DDR3_TEST_v1_0”,如图 9.3.7所示。我们可以在这个工程中对创建的IP核——DDR3_TEST进行编辑。

721f689de7066d1b444087b59ec2f214.png

图 9.3.7 Edit IP工程

AXI4接口共有五个独立的通道,每个通道又有少则几个,多则十几个信号,如果让我们自己来实现这样一个接口还是比较复杂的。不过大家不用担心,我们在上图中创建AXI4接口的IP时,Vivado提供的IP封装工具已经自动帮我们实现了这样一个接口,并提供了一个示例程序。

DDR3_TEST_v1_0文件实现了AXI4协议下的读写测试模块,我们甚至都不用对代码作任何修改,即可实现对DDR的读写测试功能。虽然该模块的代码看上去比较长(900多行),但是大多是一些注释,非常详尽。大家可以通过阅读代码及注释,来学习AXI4协议主机的实现方式。

在这里我们只贴出部分代码:

  1. 735 //implement master command interface state machine
  2. 736
  3. 737 always @ ( posedge M_AXI_ACLK)
  4. 738 begin
  5. 739 if (M_AXI_ARESETN == 1'b0 )
  6. 740 begin
  7. 741 // reset condition
  8. 742 // All the signals are assigned default values under reset condition
  9. 743 mst_exec_state <= IDLE;
  10. 744 start_single_burst_write <= 1'b0;
  11. 745 start_single_burst_read <= 1'b0;
  12. 746 compare_done <= 1'b0;
  13. 747 ERROR <= 1'b0;
  14. 748 end
  15. 749 else
  16. 750 begin
  17. 751
  18. 752 // state transition
  19. 753 case (mst_exec_state)
  20. 754
  21. 755 IDLE:
  22. 756 // This state is responsible to wait for user defined C_M_START_COUNT
  23. 757 // number of clock cycles.
  24. 758 if ( init_txn_pulse == 1'b1)
  25. 759 begin
  26. 760 mst_exec_state <= INIT_WRITE;
  27. 761 ERROR <= 1'b0;
  28. 762 compare_done <= 1'b0;
  29. 763 end
  30. 764 else
  31. 765 begin
  32. 766 mst_exec_state <= IDLE;
  33. 767 end
  34. 768
  35. 769 INIT_WRITE:
  36. 770 // This state is responsible to issue start_single_write pulse to
  37. 771 // initiate a write transaction. Write transactions will be
  38. 772 // issued until burst_write_active signal is asserted.
  39. 773 // write controller
  40. 774 if (writes_done)
  41. 775 begin
  42. 776 mst_exec_state <= INIT_READ;
  43. 777 end
  44. 778 else
  45. 779 begin
  46. 780 mst_exec_state <= INIT_WRITE;
  47. 781
  48. 782 if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)
  49. 783 begin
  50. 784 start_single_burst_write <= 1'b1;
  51. 785 end
  52. 786 else
  53. 787 begin
  54. 788 start_single_burst_write <= 1'b0; //Negate to generate a pulse
  55. 789 end
  56. 790 end
  57. 791
  58. 792 INIT_READ:
  59. 793 // This state is responsible to issue start_single_read pulse to
  60. 794 // initiate a read transaction. Read transactions will be
  61. 795 // issued until burst_read_active signal is asserted.
  62. 796 // read controller
  63. 797 if (reads_done)
  64. 798 begin
  65. 799 mst_exec_state <= INIT_COMPARE;
  66. 800 end
  67. 801 else
  68. 802 begin
  69. 803 mst_exec_state <= INIT_READ;
  70. 804
  71. 805 if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read)
  72. 806 begin
  73. 807 start_single_burst_read <= 1'b1;
  74. 808 end
  75. 809 else
  76. 810 begin
  77. 811 start_single_burst_read <= 1'b0; //Negate to generate a pulse
  78. 812 end
  79. 813 end
  80. 814
  81. 815 INIT_COMPARE:
  82. 816 // This state is responsible to issue the state of comparison
  83. 817 // of written data with the read data. If no error flags are set,
  84. 818 // compare_done signal will be asseted to indicate success.
  85. 819 //if (~error_reg)
  86. 820 begin
  87. 821 ERROR <= error_reg;
  88. 822 mst_exec_state <= IDLE;
  89. 823 compare_done <= 1'b1;
  90. 824 end
  91. 825 default :
  92. 826 begin
  93. 827 mst_exec_state <= IDLE;
  94. 828 end
  95. 829 endcase
  96. 830 end
  97. 831 end //MASTER_EXECUTION_PROC

上面的代码实现了一个状态机,其状态转换图如下所示:

d3a3b6946fd8f6476d57d3754913c76c.png

图 9.3.8 状态转换图

系统复位后,状态机处于初始状态,在该状态下等待外部输入的启动传输脉冲init_txn_pulse。一旦检测到init_txn_pulse为高电平,状态机跳转到INIT_WRITE状态。

在INIT_WRITE状态下,状态机拉高start_single_burst_write信号,来不断地启动AXI4 Master接口对Slave端大小为4KB的存储空间进行突发写操作。写操作完成后,write_done信号会拉高,状态机进入INIT_READ状态。

在INIT_READ状态下,状态机拉高start_single_burst_read信号,不断地启动AXI4 Master接口对Slave端同一存储空间进行突发读操作,同时将读出的数据与写入的数据进行对比。读操作完成后,read_done信号拉高,状态机进入INIT_COMPARE状态。

在INIT_COMPARE状态下,判断AXI4接口在读写过程中是否发生错误,并将错误状态赋值给ERROR信号,然后将compare_done信号拉高,表示一次读写测试完成。最后跳转到IDLE状态,等待下一次读写操作的启动信号。

我们在查看DDR3_TEST IP核源码后,不需要再对IP作任何修改,直接关闭名为edit_DDR3_TEST_v1_0的工程。最终我们创建的IP核将通过AXI4 Master端口向Slave端指定的4K存储空间中连续写入1024个数据,写入的数值从1累加到1024,每个数据占32bit。

IP核创建完成后,我们在工程目录下的ip_repo文件夹中可以找到IP相关的文件,如图 9.3.9所示:

0660acac5dd2930a16717d6cae48d699.png

图 9.3.9 IP核相关的文件

需要注意的是,在图 9.3.9中我们只需要保留红色方框中的文件夹即可,其余文件及文件夹是用于对IP进行编辑的工程文件,我们可以直接删除。

回到axi4_ddr_rw工程界面,在左侧“Flow Navigator”一栏点击“IP Catalog”,然后在右侧的IP目录中可以看到我们前面所创建的IP核——DDR3_TEST,该IP已经自动添加到了当前工程的IP库中。

2b0d35dfbfb4520dba633523dffb6bbe.png

图 9.3.10 IP目录

在前面的实验中,都是不带DDR的系统结构,本次实验将学习带DDR的MicroBlaze系统搭建过程。

点击“Open Block Design”,在Diagram窗口中进行硬件设计。

首先打开MicroBlaze IP核,点击该IP进行配置,这里启用存储于指令优化选项,如图 9.3.11所示:

0391e8d5e398c535ecdde78b9ca08ec9.png

图 9.3.11 MicroBlaze设置

继续点击“Next”至第3页Cache页,将地址设置如图 9.3.12所示:

e1f57fd79e2ddc4633001e7d8cace735.png

图 9.3.12 地址设置

上处地址值必须与IP INITRATOR->Adress Editor->microblaze_0->的mig_7series_0数据和指令的地址值保持一致,如图 9.3.13所示:

2ec22a0de3b0dfc09f3adea8d64d7a20.png

图 9.3.13 MicroBlaze地址设置参考值

MicroBlaze IP核设置完毕。

双击Clocking Wizard时钟IP核,在Output Clocks页面,选中clk_out1和clk_out2,时钟频率分别设置为100和200,复位设为低电平,如图 9.3.14所示:

397bfaf3deca846de3605d8e9cb8b0b4.png

图 9.3.14 设置输出时钟

clk_out1是给microblaze_0、uart、spi等模块提供时钟;clk_out2给mig_7series_0模块提供时钟。

接着添加MIG(Memory Interface Generalor) IP核,点击“+”按钮,输入“MIG”,双击Memory Interface Generator,添加IP核,如图 9.3.15所示:

dd947b2c652e8752e818512830f4e884.png

图 9.3.15 添加MIG IP核

双击添加的MIG IP,进入配置页面,我们在达芬奇开发板FPGA设计部分,已经对MIG的配置操作有过详细讲解,这里我们简略说明一下配置需主要的配置步骤。

在Controller Options目录,DDR芯片的运行时钟设为“400M”,DDR3芯片型号选择“MT41K128M16XX-15E”,数据位宽这里设为“16”。如图 9.3.16所示:

84fb18b270185b9a4f3871cb6665b24e.png

图 9.3.16 时钟及物理芯片配置

在Memory Options目录, Input Clock Period我们选择“200M”。如图 9.3.17所示:

a21bf62d7674b044205e2f9fbbc16f5d.png

图 9.3.17 储存器配置

在FPGA Options目录,按照图 9.3.18所示进行配置。

15738e7d19a9a60efcd9dd4c272b40fb.png

图 9.3.18 系统时钟配置

“Pin/Bank Selection Mode”我们选择第二项,后面我们会使用.xdc文件。

c2b5128b484c94fb149e12f97dfa5772.png

图 9.3.19 Pin/Bank Selection Mode选项卡

在Pin Selection目录我们选择第二个选项“Read XDC/UCF”直接导入管脚分配文件,如图 9.3.20所示。

1a1f9b23f74772d6e2b96dcb0e358a73.png

图 9.3.20 管脚分配导入选项卡

如下图所示,在工程目录下,我们已经为大家准备好了一个ddr3_rw_test.xdc文件,用户只要直接导入这个.xdc文件就可以完成ddr3的管脚分配。ddr3_rw_test.xdc文件的路径为G:/tis_pro/axi4_ddr_rw。

导入后我们点击“Validate”,此时会跳出对话框,表明已经验证通过,我们点击“OK”,此时“Next”变成可选,点击“Next”完成管脚分配。如图 9.3.21所示:

96fc6eb9897352b5ffd5b0d57d388113.png

图 9.3.21 分配验证管脚

后续操作就不一一说明了,MIG IP设置完成后,将sys_rst与sys_rst_n相连,clk_out2与sys_clk_i、clk_ref_i相连,ext_reset_in与sys_rst_n相连,如图 9.3.22所示:

bca68884eef2158786e4010b94fd7863.png

图 9.3.22 手动连线

连线完成后,点击“Run Connection Automation”,弹出提示页面后,选中所有信号,点击“OK”,如图 9.3.23所示:

49739f16fd81dd3dafae34e3992ff608.png

图 9.3.23 自动连线

我们再把自定义的IP添加进来,添加完成后如图 9.3.24所示:

307f84a2cfdd905979988ad1c8f457fa.png

图 9.3.24 添加DDR3_TEST IP核

在图 9.3.24中,M_AXI是AXI-Full类型的主机接口,我们将通过这个接口对DDR3进行读写操作。DDR3_TEST IP核在检测到m_axi_init_axi_txn端口的上升沿后会启动读写过程,并将读出的数据与写入的数据作比较,比较完成后m_axi_txn_done输出高电平。另外,在比较完成后,m_axi_error信号会指示整个过程是否出错。如果在读写过程中出错,或者在比较的过程中发现读出的数据与写入的数据不一致,那么m_axi_error将会拉高。

添加完自定义IP核之后,双击该IP核对其进行配置,如图 9.3.25所示:

335419f331b3188f730e5ba873f919fe.png

图 9.3.25 配置DDR_TEST IP核

在图 9.3.25中,我们将变量C M AXI TARGET SLAVE BASE ADDR的值修改为0x88000_0000(DDR3的基地址为0x80000000),它位于DDR3存储器的地址空间,是DDR3_TEST IP核进行读写操作的起始地址。

点击“Run Connection Automation”,连接DDR3_TEST IP核。

接下来,我们还要添加Utility Vector Logic IP核。然后将其配置成非门,位宽为1,作为反向器使用。这是因为我们需要使用按键来作为DDR_TEST IP核的启动信号,达芬奇开发板上的按键在按下的时候为低电平,因此我们通过添加一个反向器,将其修改为按下时输出高电平。

最后,添加一个AXI_Quad_SPI核,用来存储要固化的程序(若不需要固化,该IP核可不添加)。搜索“spi”并添加该IP核后如图 9.3.26:

9b37e4cc8c27de4fdcd4a9c4aaf681c4.png

图 9.3.26 AXI_Quad_SPI核

双击AXI_Quad_SPI核进行配置,Mode选择“Quad”,Slave Device选择“Micron”,FIFO Depth选择“256”,勾选“Enable STARTUP Primitive”。如图 9.3.27所示:

178459c58b1c98ff1046c6985410abfa.png

图 9.3.27 配置AXI_Quad_SPI核

点击“Run Connection Automation”,选中所有连线,点击“OK”,自动连接AXI_Quad_SPI核。将ext_spi_clk 与s_axi_aclk连接,管脚spi_rtl_0改名为“qspi_flash”。

a2c697ffaa17e1cc78d7c887d26d5c22.png

图 9.3.28 最终布局图

另手动添加上图中4个橙色的外部接口,其中“key_init”用于连接达芬奇开发板上的按键,“error_flag”和“compare_done”两个接口用于连接开发板上的LED。

到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。

接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。

添加约束文件:打开system_wrapper.xdc文件删除之前的约束文件,添加如下管脚约束:

create_clock -period 20.000 -name sys_clk [get_ports sys_clk]

  1. set_property PACKAGE_PIN R4 [get_ports sys_clk]
  2. set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
  3. set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]
  4. set_property IOSTANDARD LVCMOS33 [get_ports UART_rxd]
  5. set_property IOSTANDARD LVCMOS33 [get_ports UART_txd]
  6. set_property IOSTANDARD LVCMOS33 [get_ports {qspi_flash_ss_io[0]}]
  7. set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io0_io]
  8. set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io1_io]
  9. set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io2_io]
  10. set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io3_io]
  11. set_property IOSTANDARD LVCMOS33 [get_ports {key_init[0]}]
  12. set_property IOSTANDARD LVCMOS33 [get_ports compare_done]
  13. set_property IOSTANDARD LVCMOS33 [get_ports error_flag]
  14. set_property PACKAGE_PIN T19 [get_ports {qspi_flash_ss_io[0]}]
  15. set_property PACKAGE_PIN P22 [get_ports qspi_flash_io0_io]
  16. set_property PACKAGE_PIN R22 [get_ports qspi_flash_io1_io]
  17. set_property PACKAGE_PIN P21 [get_ports qspi_flash_io2_io]
  18. set_property PACKAGE_PIN R21 [get_ports qspi_flash_io3_io]
  19. set_property PACKAGE_PIN T1 [get_ports {key_init[0]}]
  20. set_property PACKAGE_PIN U5 [get_ports UART_rxd]
  21. set_property PACKAGE_PIN T6 [get_ports UART_txd]
  22. set_property PACKAGE_PIN U2 [get_ports sys_rst_n]
  23. set_property PACKAGE_PIN R2 [get_ports compare_done]
  24. set_property PACKAGE_PIN R3 [get_ports error_flag]
  25. set_property CFGBVS VCCO [current_design]
  26. set_property CONFIG_VOLTAGE 3.3 [current_design]
  27. set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
  28. set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
  29. set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
  30. set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

管脚分配完成后按快捷键“Ctrl+S”保存管脚约束。

最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bit(Bitstream)文件。

在生成Bitstream之后,在菜单栏中依次点击“File->Export->Export hardware”导出硬件,并在弹出的对话框中,勾选“Include bitstream”,在导出路径最后添加“/vitis”。如图 9.3.29所示:

66e9ef65cb9fca2cf9723519173be98d.png

图 9.3.29 设置硬件导出地址

然后在菜单栏依次点击“Tools->Launch Vitis”,启动vitis软件。

9.4软件设计

在Vitis软件中新建一个BSP工程和一个空的应用工程,应用工程名为“axi4_ddr_rw”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码如下所示:

  1. 1 #include <stdio.h>
  2. 2 #include "xil_cache.h"
  3. 3 #include "xil_printf.h"
  4. 4 #include "xil_io.h"
  5. 5
  6. 6 int main()
  7. 7 {
  8. 8 int i;
  9. 9 char c;
  10. 10
  11. 11 Xil_DCacheDisable();
  12. 12 print("AXI4 DDR TEST!nr");
  13. 13
  14. 14 while(1){
  15. 15 scanf("%c",&c);
  16. 16 if(c=='c'){
  17. 17 printf("startnr");
  18. 18 for(i=0;i<4096;i=i+4){
  19. 19 printf("%d is %dn",i,(int)Xil_In32(0x88000000+i));
  20. 20 }
  21. 21 }
  22. 22 }
  23. 23
  24. 24 return 0;
  25. 25 }

可以看出,我们的软件程序特别简单。在代码的第14行至22行,通过一个while(1)死循环,连续判断用户输入的字符。当输入字符“c”时,程序通过一个for循环开始从地址0x8800_0000读取DDR3存储器中的数据,读取的存储空间大小为4KB。需要注意的是,变量i每次累加4,这是因为我们调用了函数Xil_In32( )来读取内存数据,每次读取32bit。而内存地址是以字节(1字节==8bit)为单位的,那么操作完成后地址应该累加4。

可以看出,我们在软件中读取的内存地址与硬件设计过程中DDR_TEST IP核所写入的地址是一致的。我们将软件读出的数据通过串口打印出来,与DDR_TEST IP核写入的数据进行对比,即可验证我们通过AXI4接口对DDR进行的读写操作是否成功。

另外,在代码的第11行,我们通过调用函数Xil_DCacheDisable( )来关闭数据缓存(Data Cache),以避免从缓存中读取数据。这是因为在对同一地址进行读操作时,读出的有可能是Data Cache中缓存的数据,而不是DDR中真正的数据。

程序设计完成后,按快捷键Ctrl+S保存main.c文件,接着编译工程。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。

9.5下载验证和固化

下载验证

首先我们将下载器与达芬奇开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。

在Vitis软件中设置并连接串口。然后下载本次实验硬件设计过程中所生成的bit文件。最后下载软件程序,下载完成后,在右下方的Terminal中可以看到应用程序打印的信息“AXI4 DDR TEST!”。

cf37aecf9458f833bebe7d795afe8d01.png

图 9.5.1 程序下载完成

然后点击Terminal窗口空白处,Terminal窗口就会出现一个黑色的光标,此时键盘输入字符“c”。程序会打印从DDR3中读出的数据,如图 9.5.2所示:

a28e9ec8d6806af3d339a6c1ef72d813.png

图 9.5.2 第一次从DDR3中读出的数据

如图 9.5.2所示,串口打印出了DDR3存储空间中从地址0x8800_0000开始的1024个数据,每个数据占4个字节。从图中可以看出,开发板上电后,在没对该地址空间进行写操作之前,DDR3中的数据是随机的。

按下开发板上的按键KEY0然后释放,该动作会启动DDR3_TEST IP核对DDR3的读写操作。然后开发板上的LED0会点亮,表示读写操作完成。如图 9.5.3所示:

0775b727a64a716a999f3f3c93c25974.png

图 9.5.3 达芬奇开发板实物图

在图 9.5.3中,如果LED1也点亮了,这表明在对DDR3进行读写操作过程中出现了错误。但这个错误指示灯点亮的原因并不唯一,通过分析DDR3_TEST IP核的源码可以看出,在AXI4通信过程中写响应出错、读响应出错、以及读出与写入的数据不一致均会导致错误指示灯点亮。

因此,要判断我们自定义的IP核究竟有没有成功地向DDR3指定地址中写入数据,还是要通过查看该内存空间中的数据来判断。

在Terminal窗口中重新输入字符“c”并发送,程序会再次打印从DDR3中读出的数据,如下图所示:

28177c77afb64e2cfac8560a2ec487b7.png

图 9.5.4 第二次从DDR3中读出的数据

从图 9.5.4中可以看出,从DDR3中指定的4KB存储空间中读出的数据依次为1到1024,与DDR3_TEST IP核写入的数据一致,这时再按下KEY0然后释放,只有LED0亮。说明本次实验在达芬奇开发板上面下载验证成功。

程序固化

在《AXI GPIO控制LED实验》中,固化时,将硬件设计生成的bit流文件和软件应用程序合并成一个download.bit文件,然后再将download.bit文件写入QSPI Flash芯片中。Flash芯片内的数据在断电之后不会丢失的,再次上电后,QSPI Flash内的数据被写入BRAM中,程序就会再次执行。

BRAM为FPGA内部的资源,程序写入后无需引导即可运行,然而,有些程序是需要引导的,如本章实验DDR3读写程序,该程序占用空间资源较大,不宜在BRAM内运行,所以将该程序放在DDR RAM中运行。DDR RAM是FPGA的外部资源而非内部资源,所以程序想要在DDR RAM中运行程序就必须先进行引导,本章实验采用Bootloader引导程序对其进行引导。

Bootloader:简单地说,Bootloader就是在应用程序运行之前,运行的一段小程序。系统在上电或复位时通常都从地址0x00000000开始执行,这个地址安排的通常就是系统的Bootloader程序。通过这段小程序,我们可以初始化硬件设备,建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为调用应用程序准备好正确的环境。

本章实验中,先将硬件配置文件system_wrapper.bit及Bootloader引导程序文件bootloader.elf合并为download.bit文件,然后将download.bit文件和用户的应用程序axi4_ddr_rw.elf文件固化到QSPI Flash中。板子断电再次上电后进行加载,FPGA自动从QSPI Flash中读取download.bit文件,将硬件配置和引导程序信息配置到片内BRAM中。加载后进行引导,FPGA内部逻辑启动,运行Bootloader引导程序读取QSPI Flash中的用户应用程序,并写到外部DDR3的相应位置。引导完成后,Bootloader程序切换指针到用户应用程序开始运行的指定位置,在外部的DDR3中开始执行用户的应用程序。

接下来我们就开始采用Bootloader的引导方式进行程序的固化。

设置板级支持包信息

按图 9.5.5指示依次点击,最后点击“Modify BSP Setting”。

7ddf0cb58ca7da89bce18b7cc13d445c.png

图 9.5.5 设置板级支持包信息

选中“xilisf”,即Flash库(如果不选则无法固化QSPI Flash),如图 9.5.6所示:

9899deead03d2207c26df85fd0a60719.png

图 9.5.6 选中xilisf

点击左上角的“xilisf”,在serial_flash_family将原先的值“1”改为“5”(板载Flash芯片MT41K128M16XX-15E属于镁光,对应值为5)。如图 9.5.7所示:

3dce27a95ec2e1da41f490a5139a6cca.png

图 9.5.7 选择串行flash对应值

右键system_wraper,点击“Build Project”,编译工程。

建立BootLoader工程

新建工程,工程名设置为“bootloader”,点击“Next”。如图 9.5.8所示:

98953a41666e0d2be60b1368a5925c68.png

图 9.5.8 新建工程bootloader

选择当前的硬件平台,点击“Next”。如图 9.5.9所示:

a7382734c3d59d8c7ee4e2edc6d41e48.png

图 9.5.9 选择硬件平台

继续点击“Next”,选择模板“SERC SPI Bootloader”,点击“Finish”。如图 9.5.10所示:

4b5d11f6d1a42f028b228952eb629249.png

图 9.5.10 选择SERC SPI Bootloader

点击修改blconfig.h文件,将MicroBlaze软件文件在Flash中的加载点修改为“0x00800000”:Flash前面的8MB空间用来存储Bitstream文件,从地址0x00800000开始保存软件,如图 9.5.11所示。(这里设置加载点的值可以根据比特流文件的大小进行调整)。

c0980bc46a0404b5f3e2e90ab8258c5e.png

图 9.5.11 修改软件存储位置

右键bootloader_system,点击“Build Project”,编译工程,生成引导程序bootloader.elf文件。

生成download.bit文件

点击菜单栏“Xilinx”,选择“Program FPGA”,将硬件设计生成的bit文件和bootloader.elf文件合成为一个download.bit文件,用于烧录到QSPI Flash芯片中。如图 9.5.12所示:

19760e536724dbc5a74aa8491e6faf2e.png

图 9.5.12 Program FPGA

点击“microblaze_0”,选择编译生成的bootloader.elf文件,路径G:vitis_proaxi4_ddr_rwvitisbootloaderDebug。点击“Program”,生成download.bit文件。

05f04be6f33b538487a912ae61c07c85.png

图 9.5.13 加载bootloader.elf

download.bit文件路径为G:vitis_proaxi4_ddr_rwvitisbootloader_idebitstreamdownload.bit。

生成download.bit文件后,点击菜单栏Xilinx,选择“Program Flash”。如图 9.5.14所示:

b8ea1d8388506eea604a8a94ffd44ea2.png

图 9.5.14 Program Flash

在Project Type栏,选中Application。在Image File栏,点击“Browse…”,选择上一步生成的download.bit文件,选择器件“mt25ql128-spi_x1_x2_x4”,勾选“Verify after flash”,点击“Program”。如图 9.5.15所示:

fc3d0a7bad355a37f2b438cd29a1bbdd.png

图 9.5.15 烧录download.bit文件

烧录download.bit成功,现象如图 9.5.16所示:

ec2012c7c173fc0650c9ea9e688a6766.png

图 9.5.16 烧录download.bit成功

烧写应用程序

再次点击菜单栏“Xilinx”,选择“Program Flash”。

将用户的应用程序文件axi4_ddr_rw.elf烧写到地址“0x00800000”,elf路径G:vitis_proaxi4_ddr_rwvitisaxi4_ddr_rwDebug。选择器件“mt25ql128-spi_x1_x2_x4”,勾选“Convert ELF to bootloadable SREC format and program”和“Verify after flash”,点击“Program”。如图 9.5.17所示:

7fc066e17b9f6ca95dd7579a00400737.png

图 9.5.17 烧录axi4_ddr_rw.elf

烧录应用程序成功,现象如图 9.5.18所示:

bd010596c919501f4533c3aa6ac4a514.png

图 9.5.18 烧录应用程序成功

关掉电源,拔出JTAG连接线,重新上电,板子自动运行。

重新连接Terminal,等待引导完毕,引导过程耗费的时间与用户程序的大小成正比(优化技巧详见文章末尾)。程序引导完毕,如下图所示:

e8fa2145bdfd3c80319413025c5261e0.png

图 9.5.19 用户程序引导完成

按下按键,LED0亮;输入字符“c”,Terminal中会打印值1到1024;说明本实验的程序固化成功,如下图所示:

5604218f66a5ade81f7755244d5ca5ae.png

图 9.5.20 LED0亮

优化技巧:如果用户程序比较大,图 9.5.19中的引导时间会相应变长,我们可以使用“//”号屏蔽VERBOSE宏定义,省去不必要的打印信息,进而缩短引导时间。如下图所示:

e4e6b84af74432c72f388499cf5f5379.png

图 9.5.21 屏蔽宏

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值