ARM+FPGA开发:基于AXI总线的GPIO IP创建

fpga+arm是zynq的特点,那么pl部分怎么和arm通信呢,依靠的就是axi总线。这个实验是创建一个基于axi总线的gpio ip,利用pl的资源来扩充gpio资源。通过这个实验迅速入门开发基于总线的系统。
使用的板子是zc702。
axi总线初识:
axi (advanced extensible interface),由arm公司提出的一种总线协议。总线是一组传输通道, 是各种逻辑器件构成的传输数据的通道, 一般由数据线、地址线、 控制线构成。 xilinx从6系列的 fpga 开始对 axi 总线提供支持, 此时 axi 已经发展到了 axi4 这个版本, vivado里都是基于aix4的 ip。
zynq支持三种axi总线,拥有三种axi接口,用的都是axi协议:
axi4:(for high-performance memory-mapped requirements)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输。
axi4-lite:(for simple, low-throughput memory-mapped communication)是一个轻量级的地址映射单次传输接口, 占用很少的逻辑单元。
axi4-stream:(for high-speed streaming data)面向高速流数据传输,去掉了地址项,允许无限制的数据突发传输。
数据在总线上是遵守协议定的规则来传输的,axi信号传输先是传地址,然后检测ready+valid,都为高电平时开始传数据,当主机发送最后一个数据时last信号拉高,通知从机传输结束。
在介绍读写如何进行前先介绍握手协议:
ready,valid握手通信机制,主机产生 vlaid 信号来指明何时数据或控制信息有效。从机产生 ready 信号来指明已经准备好接受数据或控制信息。传输发生在 valid和 ready 信号同时为高的时候。(还有一个last信号表示什么时候传到最后一个数据了)
读时序:地址线上发来地址,地址准备和地址有效都高时,开始发送要读的数据,读准备和读有效都高时数据被读取到,发最后一个数据时读last信号拉高。
写时序:地址线上发来地址,地址准备和地址有效都高时,开始发送要写的数据,写准备和写有效都高时数据写入,发最后一个数据时写last信号拉高。写数据多了一个反馈信号,反馈给主机,主机接收到这个信号,就知道写成功了。
这个协议可以暂时不去理清,知道大致信号关系,后面会通过观察波形进一步加深印象,这次实验的重点是学习通过编程操作寄存器完成读写!
第一步,创建axi总线ip
新建一个工程,tools-->create and pacakge ip-->选择create axi4 peripheral
创建完以后(起个易理解的名字,放到能找到的路径下),有三项需要设置:接口类型,数据类型和寄存器数量
我们按默认这是就好,记住这里的设置:选择axi_lite总线,数据位宽是32位,也就是4字节,寄存器4个,实际我们用到的只有一个,但这里最低要求4个,没关系,多出的不用就是,待会我们就要通过操作寄存器完成对数据的读写。
然后选择edit ip,
打开ip的工程后,先打开这个文件:
这个就是基于axi_lite总线协议的模块,可以看到我们设置的数据位宽和寄存器数量:
axi总线向寄存器写数据:
axi总线下读寄存器的数据:
接下来我们添加一个信号,将寄存器绑定到用户输出,用这个输出控制led灯,这样可以通过观察led的亮灭看有没有写入成功。
然后打开顶层文件:
将添加的信号加上去:
保存,tools-->create and package ip:
overwrite原来的文件。
在ip自己创建的工程文件夹里,打包好的ip就是这个文件夹,可以将其拷贝放到任意地方:
至此,基于axi_lite总线的ip就完成了。可以将这个文件夹拷到你之前建的工程目录下,我是放在myip文件夹下。
第二步,使用基于axi总线的ip
将我们自定义的ip添加到库里:
create block design,命名为gpio_axi_led,
添加zynq核,双击修改ddr信号,其他默认设置:
添加我们自己创建的ip,然后点击自动连接:
会自动出现互联模块和复位模块,互联模块主要是起管理主从设备的作用:
本来我们还应该添加逻辑分析仪观察axi总线的各信号波形,但是为了先上手体验怎么开发基于axi的系统,我们先略过,放在下一个实验中。
再点击run block automatiom:
将led信号也输出出来,右击gpio_led,make external。
右击空白处,选择regenerate layout,美化一下排版:
这样我们的系统就搭建成功了,下面就是一些例行操作:
检验一下我们的设计:
保存一下我们的设计:
右键bd文件,复位一下系统,reset output products:
右键bd文件,geberate output products,
右键bd文件,create hdl wrapper。
然后就是添加管脚约束,把gpio_led信号连接到led灯上:
zc702的管教约束如下:
#gpio pmod1
set_property package_pin e15 [get_ports {gpio_led[7]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[7]}]
set_property package_pin d15 [get_ports {gpio_led[6]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[6]}]
set_property package_pin w17 [get_ports {gpio_led[5]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[5]}]
set_property package_pin w5 [get_ports {gpio_led[4]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[4]}]
#gpio pmod2
set_property package_pin v7 [get_ports {gpio_led[3]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[3]}]
set_property package_pin w10 [get_ports {gpio_led[2]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[2]}]
set_property package_pin p18 [get_ports {gpio_led[1]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[1]}]
set_property package_pin p17 [get_ports {gpio_led[0]}]
set_property iostandard lvcmos25 [get_ports {gpio_led[0]}]
添加完先综合一下,看看连线有没有错误。综合完成生成比特流文件。
至此,大功告成,下面就到了本实现的重点,进入sdk写代码来读写寄存器!
将硬件系统信息和bit文件导入sdk:
然后lanch sdk,新建一个空的工程:
在src文件下建一个c文件:
c大家都知道,用到什么函数要将这个函数所在的文件添加到头文件,这类先把头文件添加进去:
#include
#include xparameters.h
#include xil_io.h
#include sleep.h
#include xil_types.h
xinlin提供的读函数是xil_out32((baseaddr) + (u32)(regoffset)),写函数是xil_out32((baseaddr) + (u32)(regoffset), (u32)(data)),读写都是相对于master而言的,读当然是in,写当然是out了。
前面我们提到了,读写是对我们定义的寄存器操作,我们这里8个led灯,只要用到寄存器0的低8位就可以了。既然要操作寄存器,肯定要知道寄存器的地址,所有设备的地址都放在bsp文件下的include文件里的xparameters.h文件里,并且以宏定义,方便调用:
例如我们的自定义ip在这里,gpio_zhu,第一个是基地址,第二个是最高地址,:
寄存器0所在地址就是基地址,偏移量为0,因为我们定义的位宽是32位,4个字节,寄存器1所在地址就是基地址+4,依次类推。
这里我们让8个led灯依次闪烁,1秒移动一次,并读取寄存器的数据打印到串口:
#include
#include xparameters.h
#include xil_io.h
#include sleep.h
#include xil_types.h
int main(){
u8 i=0;
u8 ledvalue=0;
xil_out32(xpar_gpio_zhu_v1_0_0_baseaddr+0*4,0x00);
while(1){
for(i=0;i xil_out32(xpar_gpio_zhu_v1_0_0_baseaddr+0*4,1 ledvalue=xil_in32(xpar_gpio_zhu_v1_0_0_baseaddr+0*4);
xil_printf(ledvalue=%x/r/n,ledvalue); //打印到串口
sleep(1); //1s移动一次
}
i=0;
}
}
板子上电,连接好,以debug方式运行:
下载好后,打开串口:
点击开始运行:
led开始依次闪烁了!,并且在串口看到打印出的数据:
至此,实验成功,开启了我们arm+fpga开发之路!以后可以尝试开发更复杂的系统。


无线能量传输,线圈作为主要构成
简易触摸开关
配线架种类_配线架安装线序图解
浪潮基于RISC-V的linux系统移植方法
NV040D——扫地机语音芯片,实现语音提醒功能!
ARM+FPGA开发:基于AXI总线的GPIO IP创建
深入浅出Prompt Learning要旨及常用方法
Vishay新款中压厚膜片式电阻为系统节省空间并减少元器件用量
新型光纤技术在5G技术中首次应用
水晶光电联合肖特设立子公司 升级一体化供应链能力
华盛昌三合一热像仪万用绝缘表原理分析
电子芯闻早报:2015年各领域技术开启刷新模式
粉尘分离超声波振动筛电源发生器设计
喜报 | 瑞萨电子荣获海尔智家“战略合作伙伴奖”
如何理解HLS Block-level输入输出信号之间的时序关系
雷军又来耍猴,本月发布的小米6价格上升,将供不应求,小米这次又要掉粉了
半导体行业如何轻资产投入破局转型升级
背投显示应用的形式有哪些?
5G蕴含万亿机遇:诺基亚能否能抓住机遇逆袭华为
高通明年上半年推5G手机,四重因素驱动AI发展