【正点原子FPGA连载】第二十章AXI4接口之DDR读写实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第二十章AXI4接口之DDR读写实验
Xilinx从Spartan-6和Virtex-6系列开始使用AXI协议来连接IP核。在ZYNQ MPSOC器件中,Xilinx在IP核中继续使用AXI协议。本章我们对AXI协议作一个简单介绍,并在Vivado中实现一个AXI4接口的IP核,用于对MPSOC PS端的DDR4进行读写测试。
本章包括以下几个部分:
2020.1简介
20.2实验任务
20.3硬件设计
20.4软件设计
20.5下载验证
20.1简介
MPSOC将高性能ARM Cotex-A系列处理器与高性能FPGA在单芯片内紧密结合,为设计带来了如减小体积和功耗、降低设计风险,增加设计灵活性等诸多优点。在将不同工艺特征的处理器与FPGA融合在一个芯片上之后,片内处理器与FPGA之间的互联通路就成了MPSOC芯片设计的重中之重。如果Cotex-A53与FPGA之间的数据交互成为瓶颈,那么处理器与FPGA结合的性能优势就不能发挥出来。
我们在前面的实验中介绍了一些MPSOC PS与PL交互所使用的接口,比如《EMIO按键控制LED实验》中的EMIO,以及《AXI GPIO按键控制LED实验》中的AXI4-Lite接口等。其中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、写响应
下面是使用读地址和读数据通道实现读传输过程的示意图:

图 20.1.1 读传输过程示意图
从图 20.1.1中可以看到,在一个读传输过程中,主机首先在读地址通道给出读地址和控制信号,然后从机由读数据通道返回读出的数据。另外我们需要注意的是,这是一次突发读操作,主机只给出一个地址,从该地址连续突发读出四个数据。
写传输过程如图 20.1.2所示,它用到了写地址、写数据和写响应三个通道。主机在写地址通道给出写地址和控制信号,然后在写数据通道连续突发写四个数据。从机在接收数据之后,在写响应通道给出响应信号。

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

图 20.1.3 VALID和READY握手机制
在图 20.1.3中,ACLK为时钟信号,在AXI协议中,所有的输入信号都是在ACLK的上升沿采样,所有的输出信号必须在ACLK的上升沿之后才能改变。在T1之后,源端将VALID拉高,表明INFORMATION信号线上传输的是有效的地址、数据或者控制信息。目的端在T2之后将READY拉高,表明它已经准备好接收数据,此时源端必须保持INFORMATION数据稳定不变,直到T3时刻进行数据传输。
需要注意的是,源端不允许等目的端的READY信号拉高之后,才将VALID信号置为有效状态。而且,一旦VALID拉高,源端必须保持其处于有效状态,直至成功握手(在时钟上升沿检测到VALID和READY同时为有效状态)。
AXI协议的五个通道都有各自的VALID/READY握手信号对,每个通道握手信号对的名称如下图所示:

图 20.1.4 各通道握手信号名称
到这里,我们已经简单介绍了AXI4协议的读写过程,以及握手协议。关于如何实现AXI4通信协议,以及如何在设计中使用该协议进行通信,我们将在硬件设计部分进行讲解。
20.2实验任务
本章的实验任务是通过自定义一个AXI4接口的IP核,通过AXI_HP接口对PS端DDR4进行读写测试。
20.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:

图 20.3.1 系统框图
在图 20.3.1中,DDR Test是我们自定义的IP核,具有AXI4 Master端口,该端口通过AXI Smartconnect模块,最终连接到PS端的Slave AXI_HP端口。输入的按键控制DDR Test模块对PS的DDR4进行读写测试,并在读写测试结束后,通过两个PL LED灯分别指示读写完成和读写错误。需要说明的是,DDR Test模块通过输入信号的上升沿作为对DDR4读写测试的触发信号,需要对输入的按键信号进行消抖,否则会导致读写错误。
首先创建Vivado工程,工程名为“axi4_ddr_rw”,然后创建Block Design设计(design_1.bd)并添加Zynq UltraScale+ MPSOC模块。接下来按照《“Hello World”实验》中的步骤2-7、2-8分别配置PS的UART和DDR控制器。需要特别注意的是,我们在《“Hello World”实验》的步骤2-10中,移除了PS中与PL端交互的接口信号,在我们本次实验中要保留时钟“PL Fabric Clocks”和复位“Fabric Reset Enable”信号。
由于本次实验用到了PS端的HP接口,因此在Zynq UltraScale+ MPSOC模块的配置界面左侧点击“PS-PL Configuration”标签,然后依次展开右侧界面中PS-PL Interfaces-> Slave Interface->AXI HP,勾选“AXI HP0 FPD”,如下图所示:

图 20.3.2 PS-PL接口配置
最后点击右下角的“OK”,本次实验Zynq UltraScale+ MPSOC模块就配置完成了。
Zynq UltraScale+ MPSOC模块配置完成后其接口如下图所示:

图 20.3.3 Zynq UltraScale+ MPSOC模块接口
在图 20.3.3中,S_AXI_HP0_FPD是PS端的AXI高性能接口,它是一个从接口,连接到PS内的存储器互联,用于PL访问PS内的存储设备,包括OCM和DDR。
在本次实验中,PS内的数据通路如图 20.3.4所示。我们在PL内自定义的DDR Test IP核作为主设备,通过PS Slave AXI_HP0接口,与DDR控制器进行通信,最终对DDR4存储器进行读写操作。

图 20.3.4 PS内的数据通路
接下来我们要在Block Design中添加PL端的自定义IP核。
在添加该IP之前,我们需要先自定义一个带有AXI4 Master端口的IP核,并将其添加到工程的IP库中。我们在《自定义IP核-呼吸灯实验》中介绍了如何定义一个带有AXI-Lite Slave接口的IP核,在本次实验中定义IP的方法与之相同,只是这次我们要选择AXI4 Master接口。
在菜单栏中点击“Tools”,然后在下拉列表中选择“Create and Package New IP”,如下图所示:

图 20.3.5 创建IP
在弹出的对话框中直接点击“Next”,如下图所示:

图 20.3.6 创建和封装IP
在下图所示的界面中选择“Create a new AXI4 peripheral”,点击“Next”:
接下来设置IP核的名称为“axi4_rw_test”,并将IP的路径修改为当前工程路径下的“ip_repo”文件夹,最后点击“Next”,如下图所示:

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

图 20.3.8 自定义IP核接口配置
图 20.3.8中对IP核接口的配置与《自定义IP核——呼吸灯实验》不同,在这里我们新建的IP核作为主机(Master),除此之外我们使用的接口类型变成了AXI-Full,而不再是AXI-Lite。
设置完成后点击上图中右下角的“Next”。
接下来在图 20.3.9中选择“Edit IP”,最后点击“Finish”,如下图所示:

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

图 20.3.10 Edit IP工程
AXI4接口共有五个独立的通道,每个通道又有少则几个,多则十几个信号,如果让我们自己来实现这样一个接口还是比较复杂的。不过大家不用担心,我们在图 20.3.8中创建AXI4接口的IP时,Vivado提供的IP封装工具已经自动帮我们实现了这样一个接口,并提供了一个示例程序。
在图 20.3.10中,箭头所指示的文件实现了AXI4协议下的读写测试模块,我们甚至都不用对代码作任何修改,即可实现对DDR的读写测试功能。虽然该模块的代码看上去比较长(900多行),但是大多是一些注释,非常详尽。大家可以通过阅读代码及注释,来学习AXI4协议主机的实现方式。
在这里我们只贴出部分代码:
735 //implement master command interface state machine
736
737 always @ ( posedge M_AXI_ACLK)
738 begin
739 if (M_AXI_ARESETN == 1'b0 )
740 begin
741 // reset condition
742 // All the signals are assigned default values under reset condition
743 mst_exec_state <= IDLE;
744 start_single_burst_write <= 1'b0;
745 start_single_burst_read <= 1'b0;
746 compare_done <= 1'b0;
747 ERROR <= 1'b0;
748 end
749 else
750 begin
751
752 // state transition
753 case (mst_exec_state)
754
755 IDLE:
756 // This state is responsible to wait for user defined C_M_START_COUNT
757 // number of clock cycles.
758 if ( init_txn_pulse == 1'b1)
759 begin
760 mst_exec_state <= INIT_WRITE;
761 ERROR <= 1'b0;
762 compare_done <= 1'b0;
763 end
764 else
765 begin
766 mst_exec_state <= IDLE;
767 end
768
769 INIT_WRITE:
770 // This state is responsible to issue start_single_write pulse to
771 // initiate a write transaction. Write transactions will be
772 // issued until burst_write_active signal is asserted.
773 // write controller
774 if (writes_done)
775 begin
776 mst_exec_state <= INIT_READ;
777 end
778 else
779 begin
780 mst_exec_state <= INIT_WRITE;
781
782 if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)
783 begin
784 start_single_burst_write <= 1'b1;
785 end
786 else
787 begin
788 start_single_burst_write <= 1'b0; //Negate to generate a pulse
789 end
790 end
791
792 INIT_READ:
793 // This state is responsible to issue start_single_read pulse to
794 // initiate a read transaction. Read transactions will be
795 // issued until burst_read_active signal is asserted.
796 // read controller
797 if (reads_done)
798 begin
799 mst_exec_state <= INIT_COMPARE;
800 end
801 else
802 begin
803 mst_exec_state <= INIT_READ;
804
805 if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read)
806 begin
807 start_single_burst_read <= 1'b1;
808 end
809 else
810 begin
811 start_single_burst_read <= 1'b0; //Negate to generate a pulse
812 end
813 end
814
815 INIT_COMPARE:
816 // This state is responsible to issue the state of comparison
817 // of written data with the read data. If no error flags are set,
818 // compare_done signal will be asseted to indicate success.
819 //if (~error_reg)
820 begin
821 ERROR <= error_reg;
822 mst_exec_state <= IDLE;
823 compare_done <= 1'b1;
824 end
825 default :
826 begin
827 mst_exec_state <= IDLE;
828 end
829 endcase
830 end
831 end //MASTER_EXECUTION_PROC
上面的代码实现了一个状态机,其状态转换图如下所示:

图 20.3.11 状态转换图
系统复位后,状态机处于初始状态,在该状态下等待外部输入的启动传输脉冲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状态,等待下一次读写操作的启动信号。
我们在查看了图 20.3.10中箭头所指示的IP核源码后,不需要再对IP作任何修改,直接关闭名为edit_AXI4_RW_TEST_v1_0的工程。最终我们创建的IP核将通过AXI4 Master端口向Slave端指定的4K存储空间中连续写入1024个数据,写入的数值从1累加到1024,每个数据占32bit。
IP核创建完成后,我们在工程目录下的ip_repo文件夹中可以找到IP相关的文件,如下图所示:

图 20.3.12 IP核相关的文件
需要注意的是,在图 20.3.12中我们只需要保留红色方框中的文件夹即可,其余文件及文件夹是用于对IP进行编辑的工程文件,我们可以直接删除。
回到axi4_ddr_rw工程界面,在左侧“Flow Navigator”一栏点击“IP Catalog”,然后在右侧的IP目录中可以看到我们前面所创建的IP核——AXI4_RW_TEST,该IP已经自动添加到了当前工程的IP库中。

图 20.3.13 IP目录
接下来在Diagram窗口中给设计添加自定义的IP核AXI4_RW_TEST,添加完成后如下图所示:

图 20.3.14 添加AXI4_RW_TEST IP核
在图 20.3.14中,M_AXI是AXI-Full类型的主机接口,我们将通过这个接口对PS端的DDR4进行读写操作。AXI4_RW_TEST IP核在检测到m_axi_init_axi_txn端口的上升沿后会启动读写过程,并将读出的数据与写入的数据作比较,比较完成后m_axi_txn_done输出高电平。另外,在比较完成后,m_axi_error信号会指示整个过程是否出错。如果在读写过程中出错,或者在比较的过程中发现读出的数据与写入的数据不一致,那么m_axi_error将会拉高。
添加完自定义IP核之后,双击该IP核对其进行配置,如下图所示:

图 20.3.15 配置AXI4_RW_TEST IP核
在图 20.3.15中,我们将变量C M AXI TARGET SLAVE BASE ADDR的值修改为0x1000_0000,它位于DDR4存储器的地址空间,是AXI4_RW_TEST IP核进行读写操作的起始地址。我们将该地址之前的存储空间预留下来,用于运行PS中的软件程序。
接下来,我们还要添加Utility Vector Logic IP核。然后将其配置成非门,位宽为1,作为反向器使用,如下图所示:

图 20.3.16 Utility Vector Logic IP核设置
这是因为我们需要使用PL端的按键来作为AXI4_RW_TEST IP核的启动信号,MPSOC开发板上的按键在按下的时候为低电平,因此我们通过添加一个反向器,将其修改为按下时输出高电平。
接下来为按键添加消抖模块,用于消除按键的抖动。添加的方法是先将《MPSOC之FPGA开发指南》中“按键控制蜂鸣器”里的按键消抖模块(key_debounce.v)添加至工程,然后再添加至框图设计中即可。下面开始具体操作,我们在工程目录.\axi4_ddr_rw\axi4_ddr_rw.srcs\sources_1下创建一个文件夹new,将key_debounce.v拷贝至new文件夹下,如下图所示:

图 20.3.17 key_debounce路径
接下来将key_debounce.v模块添加至工程中,右击Design Sources下的design_1(design_1.bd),选择“Add Sources…”,如下图所示。

图 20.3.18 添加源文件
在弹出的页面中选择第二个“Add or create design sources”,点击“NEXT”。
然后点击页面中的“Add Files”,在弹出的页面中选择“key_debounce.v”,如下图所示。

图 20.3.19 添加“key_debounce.v”
最后点击“Finish”完成代码的添加,如下图所示。

图 20.3.20 点击“Finish”
此时在Design Sources界面下,可以看到刚刚添加的文件,如下图所示。

图 20.3.21 添加完成界面
接下来在Diagram界面空白处右击,选择“Add Module…”,如下图所示。

图 20.3.22 点击“Add Module”
此时会弹出工程中添加的.v文件,将key_debounce.v添加进来,如下图所示。

图 20.3.23 添加“key_debounce.v”至BD中
需要说明的是,如果上图的界面中,没有出现按键消抖模块,可能是软件界面还没有更新刚刚添加的设计文件,可以稍作等待后再添加,添加完成后如下图所示。

图 20.3.24 消抖模块成功添加至BD界面中
IP核添加完成后,使用工具的自动连接功能,对设计进行连线,连线后如下图所示:

图 20.3.25 自动连接
以上是使用Vivado软件的自动连线,接下来还需要手动进行消抖模块、反向器、AXI4_RW_TEST IP核之间的连接。连接完成后,在Diagram窗口空白处右击,然后选择“Regenerate Layout”对设计进行重新布局,布局后的界面如下图所示:

图 20.3.26 重新布局后的设计界面
从上图中可以看到,在执行了自动连接之后,工具自动添加了两个IP核,分别是AXI智能互联(AXI Smartconnect)和处理器系统复位(Processor System Reseet)。接下来需要将消抖模块key端口引出,用于连接开发板PL端按键,并重命名为key_int,命名方法参考AXI GPIO按键LED实验;还需要将AXI4读写测试模块的m_axi_txn_done和m_axi_error两个端口引出,用于连接PL端LED灯,并分别重命名为compare_done和error_flag,如下图所示:

图20.3.27 引出管脚
图20.3.27中橙色的连线标注出了本设计中AXI4总线的连接,其中AXI4_RW_TEST IP核的M_AXI作为主机接口,Zynq UltraScale+ MPSOC的S_AXI_HP0_FPD为从机接口,中间经过了AXI Smartconnect。AXI Smartconnect的功能与AXI Interconnect IP核类似,都是用于将AXI存储器映射的主器件连接到存储器映射的从器件。
到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“design_1.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
在左侧Flow Navigator导航栏中找到RTL ANALYSIS,点击该选项中的“Open Elaborated Design”,在弹出的窗口中点击“OK”。或者在菜单栏中点击 Layout,在下拉列表中选择I/O Planning以打开I/O Ports窗口。我们将在 I/O Ports 窗口中对key_init等接口进行管脚分配,如下图所示:

图20.3.28 管脚分配
在图20.3.28中,我们将key_init分配到了AD11引脚上,该引脚最终与MPSOC开发板上的按键PL_KEY0相连接;compare_done和error_flag分别连接到开发板上的LED:PL_LED0和PL_LED1。管脚分配完成后按快捷键Ctrl+S保存管脚约束,并在弹出的保存约束窗口中输入文件名“axi4_ddr_rw”。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。将导出的“design_1_wrapper.xsa”文件放到vitis文件夹,然后在菜单栏选择File > Launch Vitis,启动VITIS软件。
20.4软件设计
在VITIS软件中新建一个空的应用工程,应用工程名为“axi4_ddr_rw”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码:
1 #include "stdio.h"
2 #include "xil_cache.h"
3 #include "xil_printf.h"
4 #include "xil_io.h"
5
6 int main()
7 {
8 int i;
9 char c;
10
11 Xil_DCacheDisable();
12 printf("AXI4 PL DDR TEST!\n\r");
13
14 while(1)
15 {
16 scanf("%c",&c);
17 if(c == 'c'){
18 printf("start\r\n");
19 for(i=0;i<4096;i=i+4)
20 {
21 printf("%d is %d\n",i,(int)(Xil_In32(0x10000000+i)));
22 }
23 }
24 }
25 return 0;
26 }
可以看出,我们的软件程序特别简单。在代码的第14行至24行,通过一个while(1)死循环,连续判断用户输入的字符。当输入字符“c”时,程序通过一个for循环开始从地址0x1000_0000读取DDR存储器中的数据,读取的存储空间大小为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中真正的数据。
20.5下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB_UART(开发板PS PORT)接口与电脑连接,用于串口通信。最后连接开发板的电源,给开发板上电。
打开Vitis Terminal终端,设置并连接串口。然后下载本次实验的程序,下载完成后,在下方的Vitis Terminal中可以看到应用程序打印的信息“AXI4 PL DDR TEST!”。
然后在Vitis Terminal窗口中输入字符“c”,然后按回车键发送。程序会打印从DDR中读出的数据,如下图所示:

图 20.5.1 第一次从DDR4中读出的数据
如图 20.5.1所示,串口打印出了DDR存储空间中从地址0x1000_0000开始的1024个数据,每个数据占4个字节。从图中可以看出,开发板上电后,在没对该地址空间进行写操作之前,DDR中的数据是随机的。
我们按下开发板上的按键PL_KEY1然后释放,该动作会启动AXI4_RW_TEST IP核对DDR的读写操作。然后开发板上的PL_LED1会点亮,表示读写操作完成。如下图所示:

图 20.5.2 MPSOC开发板实物图
在图 20.5.2中,如果旁边的PL_LED2也点亮了,这表明在对DDR进行读写操作过程中出现了错误。但是这个错误指示灯点亮的原因并不唯一,通过分析AXI4_RW_TEST IP核的源码可以看出,在AXI4通信过程中写响应出错、读响应出错、以及读出与写入的数据不一致均会导致错误指示灯点亮。
笔者最早在实现本次实验功能的时候,旁边的PL_LED2偶尔也会亮,是由于没有对按键进行消抖处理所致,添加按键消抖模块后,没有再出现读写错误的情况。
在Vitis Terminal窗口中重新输入字符“c”并发送,程序会再次打印从DDR中读出的数据,如下图所示:

图 20.5.3 第二次从DDR4中读出的数据
从图 20.5.3中可以看出,PS端软件从DDR中指定的4KB存储空间中读出的数据依次为1到1024,与AXI4_RW_TEST IP核写入的数据一致,说明本次实验在MPSOC开发板上面下载验证成功。
相关文章:
【正点原子FPGA连载】第二十章AXI4接口之DDR读写实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第二十章AXI4接口…...
超出认知的数据压缩 用1-bit数据来表示32-bit的梯度 语音识别分布式机器学习 梯度压缩 论文精读
说明 介绍1−bit1-bit1−bit论文内容。 原文链接:1-bit stochastic gradient descent and its application to data-parallel distributed training of speech DNNs | Semantic Scholar ABS 实验证明在分布式机器学习的过程中能够通过将同步所传递的梯度进行量化…...
深度剖析指针(上)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是指针噢,在学习C语言的过程中,指针算是一个比较重要的内容,当然,难度也是比较大的,那么现在就让小雅兰来带大家进入指针的世界吧 字符指针 数组指针…...
学习 Python 之 Pygame 开发魂斗罗(六)
学习 Python 之 Pygame 开发魂斗罗(六)继续编写魂斗罗1. 创建碰撞类2. 给地图添加碰撞体3. 让人物可以掉下去4. 实现人物向下跳跃5. 完整的代码继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗(五)中,我…...
LeetCode题解:1238. 循环码排列,归纳法,详细注释
原题链接: https://leetcode.cn/problems/circular-permutation-in-binary-representation/ 前置条件: 在解题之前,请先一定要阅读89.格雷编码的题解格雷编码可以满足题目的条件“p[i] 和 p[i1] 的二进制表示形式只有一位不同”,…...
全新后门文件Nev-3.exe分析
一、 样本发现: 蜜罐 二、 内容简介: 通过公司的蜜罐告警发现一个Nev-3.exe可执行文件文件,对该样本文件进行分析发现,该可执行程序执行后会从远程服务器http://194.146.84.2:4395/下载一个名为“3”的压缩包,解压后…...
线性回归系数解释
线性回归系数解释线性回归系数1、R2R^2R2(R方,R-Square)2、Adj−R2Adj-R^2Adj−R2(调整后的 R 方)3、标准误差4、FFF 值5、FFF 显著度6、置信区间7、PPP 值线性回归系数 回归模型得到后会有多个系数,这些系…...
22.2.27打卡 Codeforces Round #852 (Div. 2) A~D
A Yet Another Promotion 题面翻译 题目描述 共 ttt 组数据,每组数据中,你需要买 nnn 公斤苹果,第一天单价为 aaa ,但每买 mmm 公斤赠送一公斤;第二天单价为 bbb 。求最小花费。 输入输出格式 第一行一个正整数 …...
如何查看Spring Boot各版本的变化
目录 1.版本 2.基础特性和使用 3.新增特性和Bug修复 1.版本 打开Spring官网,点进Spring Boot项目我们会发现在不同版本后面会跟着不同的标签: 这些标签对应不同的版本,其意思如下: GA正式版本,通常意味着该版本已…...
程序员是否要加入创业公司?
我从1月份入职到2月份离职,历时一个半月。短暂的体验了一段创业生活,更准确的说是一段“待在”创业团队的生活,因为我发现创业本身跟我关系不大。一个半月的就业经历,对任何人来说都不是一个好选择,当然也不是我所期望…...
2023软件测试工程师全新技术栈,吃透这些,起薪就是25k~
相信每个准备软件测试面试的同学,不管你是大学刚毕业,满心憧憬着进入公司实习、非计算机行业转行软件测试、自学测试就业还是培训后就业,都会面临着众多的疑问和不解,那就是该怎么走出着第一步,今天本文一次性告诉你&a…...
【ChatGPT情商大考验】ChatGPT教我谈恋爱
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...
C++类内存结构模型
内存分区 内存全局数据区,代码区,栈区,堆区。 定义一个类 类的成员函数被放在代码区 类的静态成员变量被放在全局数据区(不占用类的存储空间) 非静态成员在类的实例内,实例在栈区或者堆区 虚函数指针&…...
HTML#4超链接标签,列表标签,表格标签和布局标签
一. 超链接标签介绍<a> 定义超链接,用于连接到另一个资源herf: 指定访问资源的URLtarget: 指定打开资源的方式代码<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>超链接标签</title> <…...
本科课程【数字图像处理】实验汇总
文章目录 实验1 - 腐蚀与膨胀实验2 - 图像增强实验3 - 图像的几何变换实验4 - 图像的蒙纱效果实验5 - 空洞填充实验6 - 取阈值的邻域平均算法实验7 - 图像的平移与伸缩变换实验1 - 腐蚀与膨胀 实验目的 分析掌握腐蚀与膨胀的基本原理,编写腐蚀与膨胀的算法,并掌握开闭运算的规…...
nginx安装lua、jwt模块,通过lua验证jwt实现蓝绿发布样例
文章目录前言一、基础组件下载二、组件安装1.luajit安装2.lua-nginx-module安装3.lua-resty-core安装4.lua-resty-lrucache安装5.ngx_devel_kit安装6.nginx加载lua模块7.lua-cjson安装8.lua-resty-string安装9.lua-resty-jwt安装10.lua-resty-hmac安装三、验证jwt中属性实现蓝绿…...
【redis的几种数据结构及在Java里的应用案例】
Redis是一款高性能的key-value存储系统,支持多种数据结构,包括字符串、列表、哈希表、集合和有序集合等。下面是Redis的几种数据结构及在Java中的应用案例: string 字符串(String) 字符串是Redis中最基本的数据类型,用于存储字符…...
【mybatis】 01- mybatis快速入门
数据库创建(注意:最好先创建好数据库设置utf8再进行表创建) create database mybatis; use mybatis;drop table if exists tb_user;create table tb_user(id int primary key auto_increment,username varchar(20),password varchar(20),gender char(1),addr varch…...
【C语言每日一题】杨氏矩阵(源码以及改进源码)
【C语言每日一题】—— 杨氏矩阵😎😎😎 目录 💡前言🌞: 💛杨氏矩阵题目💛 💪 解题思路的分享💪 😊题目源码的分享😊 Ǵ…...
JavaScript 面向对象【快速掌握知识点】
目录 类和对象 属性和方法 继承 多态 封装 类和对象 类是用于定义对象的模板或蓝图;它包含对象的属性和方法,我们可以使用class关键字来定义类。 class Person {constructor(name, age) {this.name name;this.age age;}sayHello() {console.log(H…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
