MiZ702学习笔记13——ZYNQ通过AXI-Lite与PL交互

在《MiZ702学习笔记7——尝试自制带总线IP》,我曾提到了AXI4-Lite的简单用法,驱动了下流水灯,只涉及到了写总线。今天,我想利用之前的VGA模块,将AXI4-Lite的读写都应用上。这篇文章主要是思想的介绍,以及AXI4-Lite读的方法。一些细节请先阅读《MiZ702学习笔记7——尝试自制带总线IP》。

具体思路为如下框图所示:

所以这次,我们需要两条AXI4-Lite总线,一条负责给VGA模块提供RGB数据(写),一条读取VGA模块提供的扫描的坐标信息(读)。

点击下图中的加号,就能为我们额外的添加一条AXI4-Lite总线:

因此在我们的IP工程里可以看到两套总线框架:

IP打包好之后是这个样子:

我们将S00这条总线作为写数据用,将S01作为读数据总线。当然一条总线是既可以读也可以写的,但是VGA这个工程决定了读和写必须是同行进行的不可复用。

其他的就和之前讲到一样了,接下来AXI4-Lite的读和写。PL为了和PS通讯,是需要一些逻辑去支持的。而这些逻辑在我们生成AXI4-Lite IP的时候,vivado就帮我们自动生成了。我们要做的部分很少,只需要增改一些内容即可,就如《MiZ702学习笔记7——尝试自制带总线IP》提到的那样。

这个工程中,对于总线的写而言就是给VGA模块送去RGB数据,“写”我们只需要增加自己的逻辑即可:

reg [11:0]rlcd_rgb;

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1‘b0 )

begin

rlcd_rgb <= 12‘d0;

end

else

begin

rlcd_rgb <= S_AXI_WDATA[11:0];

end

end

assign lcd_rgb = rlcd_rgb;

对于总线的读而言,我们需要将框架进行稍微的改动:

//写总线测试修改!!!!!!!!!

wire[31:0]wlcd_xy;// = {10‘d0,lcd_xy};

assign wlcd_xy = {10‘d0,lcd_xy};

assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;

always @(*)

begin

// Address decoding for reading registers

case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

2‘h0 : reg_data_out <= wlcd_xy;//slv_reg0;

2‘h1 : reg_data_out <= slv_reg1;

2‘h2 : reg_data_out <= slv_reg2;

2‘h3 : reg_data_out <= slv_reg3;

default : reg_data_out <= 0;

endcase

end

这里其实,也就改了一个地方,本来是reg_data_out <= slv_reg0;被我改成了reg_data_out <= wlcd_xy;这样就能通过总线读取wlcd_xy的值了。

在回到这张框架图:

到这了,我们的AXI IP已经具备了和ZYNQ以及VGA交互的能力,它就是PS和PL之间的桥梁,现在我们只差一个VGA IP,而这个IP就是个普通IP,没错就是《MiZ702学习笔记12——封装一个普通的VGA IP》讲的那样,我们将程序稍加修改,在进行IP打包,就能得到。

最后,我们分别添加这几个IP,进行连线就完成了硬件的搭建。其实我提一些细节。

我们配置下ZNYQ的输出时钟:

其中FCLK0一般添加zynq核时是默认引出的,这里主要是给AXI4-Lite IP提供时钟,大小我设置是250M,再高就提示出错了。

FCLK1将其设置为25M,主要给VGA IP提供时钟,应为VGA扫描是25M。

硬件平台准备好之后,就可以开始SDK的编程了。首先,我们得根据地址访问到AXI4-Lite IP,通过xparameter.h这个头文件可以得知S00这条总线的地址:

/* Definitions for driver MYVGAIP */

#define XPAR_MYVGAIP_NUM_INSTANCES 1

/* Definitions for peripheral MYVGAIP_0 */

#define XPAR_MYVGAIP_0_DEVICE_ID 0

#define XPAR_MYVGAIP_0_S00_AXI_BASEADDR 0x43C00000

#define XPAR_MYVGAIP_0_S00_AXI_HIGHADDR 0x43C0FFFF

但是头文件并未看到S01这条总线的地址,不过可以猜想,S00总线宽带是32位的,且首地址是0x43C00000,那么如果地址是连续的总线S01的首地址应该就是0x43C1ffff。

通过查看system.hdf可以确定以上推测是正确的:

于是定义如下:

#define RGB_AXI_BASEADDR 0x43C00000

#define LCDXY_AXI_BASEADDR 0x43C10000

首先进行彩条的测试,先通过Xil_In32,这个函数读总线,得到VGA模块输出的坐标值。其中低11位是Y坐标的值,高11位是X坐标的值:

temp = Xil_In32(LCDXY_AXI_BASEADDR);

lcd_ypos = temp & 0x7ff;

lcd_xpos = (temp>>11) & 0x7ff;

就可以测试横向彩条:

//横向彩条

if (lcd_ypos >= 0 && lcd_ypos < (V_DISP/8)*1)

Xil_Out32(RGB_AXI_BASEADDR, RED);

else if(lcd_ypos >= (V_DISP/8)*1 && lcd_ypos < (V_DISP/8)*2)

Xil_Out32(RGB_AXI_BASEADDR, GREEN);

else if(lcd_ypos >= (V_DISP/8)*2 && lcd_ypos < (V_DISP/8)*3)

Xil_Out32(RGB_AXI_BASEADDR, BLUE);

else if(lcd_ypos >= (V_DISP/8)*3 && lcd_ypos < (V_DISP/8)*4)

Xil_Out32(RGB_AXI_BASEADDR, WHITE);

else if(lcd_ypos >= (V_DISP/8)*4 && lcd_ypos < (V_DISP/8)*5)

Xil_Out32(RGB_AXI_BASEADDR, BLACK);

else if(lcd_ypos >= (V_DISP/8)*5 && lcd_ypos < (V_DISP/8)*6)

Xil_Out32(RGB_AXI_BASEADDR, YELLOW);

else if(lcd_ypos >= (V_DISP/8)*6 && lcd_ypos < (V_DISP/8)*7)

Xil_Out32(RGB_AXI_BASEADDR, CYAN);

else if(lcd_ypos >= (V_DISP/8)*7 && lcd_ypos < (V_DISP/8)*8)

Xil_Out32(RGB_AXI_BASEADDR, ROYAL);

结果和想象中一样完美~~~

接下来测试,总线彩条:

//竖向扫描有点问题,

if (lcd_xpos >= 0 && lcd_xpos < (H_DISP/8)*1)

Xil_Out32(RGB_AXI_BASEADDR, RED);

else if(lcd_xpos >= (H_DISP/8)*1 && lcd_xpos < (H_DISP/8)*2)

Xil_Out32(RGB_AXI_BASEADDR, GREEN);

else if(lcd_xpos >= (H_DISP/8)*2 && lcd_xpos < (H_DISP/8)*3)

Xil_Out32(RGB_AXI_BASEADDR, BLUE);

else if(lcd_xpos >= (H_DISP/8)*3 && lcd_xpos < (H_DISP/8)*4)

Xil_Out32(RGB_AXI_BASEADDR, WHITE);

else if(lcd_xpos >= (H_DISP/8)*4 && lcd_xpos < (H_DISP/8)*5)

Xil_Out32(RGB_AXI_BASEADDR, BLACK);

else if(lcd_xpos >= (H_DISP/8)*5 && lcd_xpos < (H_DISP/8)*6)

Xil_Out32(RGB_AXI_BASEADDR, YELLOW);

else if(lcd_xpos >= (H_DISP/8)*6 && lcd_xpos < (H_DISP/8)*7)

Xil_Out32(RGB_AXI_BASEADDR, CYAN);

else

Xil_Out32(RGB_AXI_BASEADDR, ROYAL);

结果就不想想象中那么完美了:

我们发现彩条的边缘有锯齿,这是为什么呢?原因是横向彩条的扫描依据是lcd_ypos的值,纵向彩条的扫描依据是lcd_xpos。而VGA扫描是水平扫描,这意味着X的更新频率是Y更新频率的N倍。

也就是说AXI4-Lite的读写速度跟上了lcd_ypos的变化,却没能完全跟上lcd_xpos的值。

有的读者可能要发问了,之前给AXI4-Lite IP提供了250M的时钟吗,而VGA扫描才25M,怎么可能速度上跟不上?

这只能说AXI4-Lite本身,并不适合做大量传输数据这件事。AXI4-Lite只适合做一些简单的IO控制,配置寄存器用。

还记得在选择总线类型的时候吗?

我们选择的是Lite,其实还有其他类型可选,如:AXI4,以及AXIStream类型。这些就是我们接下来的任务了。

最近,路飞开4档了,实在太帅了,我最终的目的本来是想将他显示在VGA上的,奈何一开始选错了总线类型,只能将就着看了:

总结:

虽然最终的结果不算成功,但是过程还是能学到不少东西,这里例子可以很好的体现ZYNQ的优势:

1、利用PL部分驱动VGA,充分的发挥FPGA的优势。为PS分担的不小的任务。

2、利用PS完成数据的提供,数据方便存储,且更加多变,正好弥补了FPGA这方面的缺点。

接下来的教程里,就开始探究AXI4以及AXIStream,必将完善次项目,就到这里吧。东莞今天居然下雪了,不说了拿电吹风取暖去了。明天回家了,祝我一路顺风吧~~

工程文件,待会上传~~~

时间: 2024-08-08 09:37:33

MiZ702学习笔记13——ZYNQ通过AXI-Lite与PL交互的相关文章

MiZ702学习笔记12&mdash;&mdash;封装一个普通的VGA IP

还记得<MiZ702学习笔记(番外篇)--纯PL VGA驱动>这篇文章中,用verilog写了一个VGA驱动.我们今天要介绍的就是将这个工程打包成一个普通的IP,目的是为后面的一篇文章做个铺垫. 打包成一个普通的IP的目的,可以直接将这个IP粘贴到Block文件中.(和用文本实例化是一个意思).应为我们调用zynq的核的时候一般是用Block的形式,为了zynq和我们的VGA模块更方便的组织起来,就需要这种IP打包方式. 为什么是强调是普通的IP,这个主意是区分带AXI接口的IP,这个在后面介

HTML&CSS基础学习笔记13—无序列表

无序列表 有时我们的工作繁忙,杂事很多,怕忘记,就会把事情一件件列出来,防止忘记. 它们的排列顺序对于我们来说并不重要,可以随意调换,我们将它称为无序列表,HTML里用<ul>标签来表示无序列表,列表里的项目则用<li>标签来表示: 1 2 3 4 5 <ul>     <li></li>     <li></li>     ... </ul> 看一段实例代码: 对于的浏览器显示结果是这样的: 更多内容学习,请

python 学习笔记 13 -- 常用的时间模块之time

Python 没有包含对应日期和时间的内置类型,不过提供了3个相应的模块,可以采用多种表示管理日期和时间值: *    time 模块由底层C库提供与时间相关的函数.它包含一些函数用于获取时钟时间和处理器的运行时间,还提供了基本解析和字符串格式化工具 *    datetime 模块为日期.时间以及日期时间值提供一个更高层接口.datetime 中的类支持算术.比较和时区配置. *    calendar 模块可以创建周.月和年的格式化表示.它还可以用来计算重复事件.给定日期是星期几,以及其他基

Swift学习笔记(13)--属性 (Properties)

普通属性用var和let即可,本文不做详述 1.延迟存储属性 延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性.在属性声明前使用@lazy来标示一个延迟存储属性. class DataImporter { /* DataImporter 是一个将外部文件中的数据导入的类. 这个类的初始化会消耗不少时间. */ var fileName = "data.txt" // 这是提供数据导入功能 } class DataManager { @lazy var importer = D

springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定

springmvc学习笔记(13)-springmvc注解开发之集合类型參数绑定 springmvc学习笔记13-springmvc注解开发之集合类型參数绑定 数组绑定 需求 表现层实现 list绑定 需求 表现层实现 map绑定 本文主要介绍注解开发的集合类型參数绑定,包含数组绑定,list绑定以及map绑定 数组绑定 需求 商品批量删除,用户在页面选择多个商品.批量删除. 表现层实现 关键:将页面选择(多选)的商品id,传到controller方法的形參,方法形參使用数组接收页面请求的多个商

Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法

Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法 这篇笔记将介绍如何使用Ext.Net GridPanel 中使用Sorter. 默认情况下,Ext.Net GridPanel中的列都具有排序功能,效果如下: 如果要禁用列排序,需要在列模型中添加一个属性Sortable="false" 客户端排序 排序是对Store的操作.如果我们要在一个Store中加入排序,可以使用下面的配置: <Sorters> <ext:DataSorter P

mybatis学习笔记(13)-查询缓存之二级缓存

mybatis学习笔记(13)-查询缓存之二级缓存 mybatis学习笔记13-查询缓存之二级缓存 二级缓存原理 开启二级缓存 调用pojo类实现序列化接口 测试方法 useCache配置 刷新缓存就是清空缓存 应用场景和局限性 本文主要讲mybatis的二级缓存,二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的. 二级缓存原理 首先开启mybatis的二级缓存. sqlS

python基础教程_学习笔记13:标准库:一些最爱——sys

标准库:一些最爱 sys sys这个模块让你能够访问与python解释器联系紧密的变量和函数. sys模块中一些重要的函数和变量 函数/变量 描述 argv 命令行参数,包括脚本名称 exit([arg]) 退出当前程序,可选参数为给定的返回值或者错误信息 modules 映射模块名字到载入模块的字典 path 查找模块所在目录的目录名列表 platform 类似sunos5或者win32的平台标识符 stdin 标准输入流--一个类文件对象 stdout 标准输出流--一个类文件对象 stde

springmvc学习笔记(13)-springmvc注解开发之集合类型参数绑定

springmvc学习笔记(13)-springmvc注解开发之集合类型参数绑定 springmvc学习笔记13-springmvc注解开发之集合类型参数绑定 数组绑定 需求 表现层实现 list绑定 需求 表现层实现 map绑定 本文主要介绍注解开发的集合类型参数绑定,包括数组绑定,list绑定以及map绑定 数组绑定 需求 商品批量删除,用户在页面选择多个商品,批量删除. 表现层实现 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商