一、实验目的:
通过自定义AXI 接口 IP连接pl和ps侧然后通过ps侧操作axi寄存器来改变pl侧呼吸灯的频率。
二、实验架构:
三、实验流程
1、先创建两个文件夹,一个是管理我们自定义IP的文件夹,一个是存储待会儿用到我们自定义ip的工程
2、点击manage ip->new ip localtion
3、next->finish。如图暂时不选择器件,可以在配置好ip后添加器件
4、如图
5、创建AXI接口的 IP,为什么不选上面的三个呢,因为上面的几个是对我们已有的ip进行操作,而最下面是创建IP选项。
6、修改工程命,因为我这里的ip和我的呼吸灯工程有关,因此命名bre_led。点击next
7、AXI 有三种接口,由于我们这里的数据量比较小,因此我们选择AXI_lite接口。由于我们的要创建的工程是PS端控制PL端的呼吸灯工程,因此将PL侧的这个创建的IP定义为从机:slave。AXI数据位宽为32位,最少四个AXI接口数据寄存器。
8、next->默认点击确定。
9、右键自定义IP后点击编辑IP,进入编辑ip的界面,这个界面可以添加PL端控制led呼吸灯的代码。
10、添加呼吸灯的代码(.v)如下图将我们的代码复制到如下目录,然后导入进编辑ip项目的工程里面。
module breath_led(
input sys_clk , //系统时钟 50MHz
input sys_rst_n , //系统复位,低电平有效
input sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭
input set_en , //设置呼吸灯频率设置使能信号
input [9:0] set_freq_step , //设置呼吸灯频率变化步长
output led //LED灯
);
//parameter define
parameter START_FREQ_STEP = 10'd1; //设置频率步长初始值
parameter CNT_2US_MAX = 7'd100;
parameter CNT_2MS_MAX = 10'd1000;
parameter CNT_2S_MAX = 10'd1000;
//reg define
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s;
reg inc_dec_flag; //亮度递增/递减 0:递增 1:递减
reg [9:0] freq_step ; //呼吸灯频率间隔步长
reg led_t ;
//*****************************************************
//** main code
//*****************************************************
assign led = led_t & sw_ctrl;
//设置频率间隔,频率步长值在1-10之间
always @(posedge sys_clk)
if(!sys_rst_n)
freq_step <= START_FREQ_STEP;
else if(set_en) begin
if(set_freq_step == 0)
freq_step <= 10'd1;
else if(set_freq_step >= 10'd1000)
freq_step <= 10'd10;
else
freq_step <= set_freq_step;
end
//cnt_2us:计数2us
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2us <= 7'b0;
else if(cnt_2us == (CNT_2US_MAX - 7'b1 ))
cnt_2us <= 7'b0;
else
cnt_2us <= cnt_2us + 7'b1;
end
//cnt_2ms:计数2ms
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2ms <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2ms <= 10'b0;
else if(cnt_2us == CNT_2US_MAX - 7'b1)
cnt_2ms <= cnt_2ms + 10'b1;
else
cnt_2ms <= cnt_2ms;
end
//cnt_2s:计数2s
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2s <= 10'b0;
else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= cnt_2s + freq_step;
else
cnt_2s <= cnt_2s;
end
//inc_dec_flag为低电平,led灯由暗变亮,inc_dec_flag为高电平,led灯由亮变暗
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
inc_dec_flag <= 1'b0;
else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms ==( CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
inc_dec_flag <= ~inc_dec_flag;
else
inc_dec_flag <= inc_dec_flag;
end
//led:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led_t <= 1'b0;
else if((inc_dec_flag == 1'b1 && cnt_2ms >= cnt_2s) || (inc_dec_flag == 1'b0 && cnt_2ms <= cnt_2s))
led_t <= 1'b1;
else
led_t<= 1'b0;
end
endmodule
11、修改代码
在低层添加我们的用户逻辑,即添加我们呼吸灯例化的工程,当然因为这里使用到了一个输出端口,因此我门需要逐级添加led端口。以下是需要修改的地方。
// Add user logic here
breath_led(
.sys_clk (S_AXI_ACLK), //系统时钟 50MHz
.sys_rst_n (S_AXI_ARESETN), //系统复位,低电平有效
.sw_ctrl (slv_reg0[0]), //呼吸灯开关控制信号 1:亮 0:灭
.set_en (slv_reg1[31]), //设置呼吸灯频率设置使能信号
.set_freq_step (slv_reg1[9:0]), //设置呼吸灯频率变化步长
.led (led) //LED灯
);
// User logic ends
12、综合一下看看有没有语法错误
13、点击这里的merge........来刷新配置
14、点击cus后点击上面的提示信息刷新配置。
15、点击提示信息
16、添加zynq型号
17、修改我们添加的参数,记住这里的long类型的务必要将默认值换成相应的类型。比如我这里默认值为100,最下面一行。
18、点击Re-package ip,建立我们自定义的IP。
19、新建一个空工程
20、添加我们自定义的ip核进入工程,设置->IP->仓库->➕->自定义ip管理目录->ip_repo->选中IP->点击ok
可以看见已经添加进来了。
21、创建block design,并添加ps 侧的ip和我们刚刚创建的PS侧的呼吸灯的ip。按照以前的修改zynq ip的配置。
22、修改bre_led ip的配置。但这里留给外部的可修改的参数也就一个(步长)因此在配置参数的时候我们已经设置默认值了(100),所以我们不修改。
23、依次点击两个提示信息默认点击ok即可。然后刷新。到这里我们就完成我们block的设计了。接下来就是在SDK里面写代码操作寄存器即可。
24、右键bd文件生成ip设计文件。然后生成verilog顶层文件。都默认点击ok即可。
25、点击综合一下,然后配置引脚。保存生成约束文件。
26、一定要生成bit流
27、然后就是导出sdk设计文件,一定要勾选我们刚刚生成的bit流因为,这里用到了PL端的led。
28、打开SDK
29、新建项目->新建源文件(main.c)->复制以下代码进源文件。注意:这里的bre_led头文件和几个定义要和你生成的示例文件保持一致。因为如果每个人命名不一样生成的文件也不一样。导致报错。这几个定义和我们需要的函数都在如下第二张图片里面。注意:这几个文件是sdk根据你项目命名自动生成的。如果项目命名不一样里面的头文件和一些宏定义也是有去别的。根据自己的头文件和示例文件修改一下即可。
#include "stdio.h"
#include "xparameters.h"
#include "bre_led.h"
#include "xil_io.h"
#include "sleep.h"
#define BRE_IP_BASEADDR XPAR_BRE_LED_0_S0_AXI_BASEADDR
#define BRE_IP_REG0 BRE_LED_S0_AXI_SLV_REG0_OFFSET
#define BRE_IP_REG1 BRE_LED_S0_AXI_SLV_REG1_OFFSET
int main()
{
int freq_change=0;
printf("custom ip test start\n");
while(1)
{
if(freq_change==0)
{
BRE_LED_mWriteReg(BRE_IP_BASEADDR,BRE_IP_REG1,0x800000ef);
freq_change=1;
}
else
{
BRE_LED_mWriteReg(BRE_IP_BASEADDR,BRE_IP_REG1,0x8000002f);
freq_change=0;
}
print("led open\n");
BRE_LED_mWriteReg(BRE_IP_BASEADDR,BRE_IP_REG0,1);
sleep(6);
print("led close\n");
BRE_LED_mWriteReg(BRE_IP_BASEADDR,BRE_IP_REG0,0);
sleep(2);
}
return 0;
}
30、连接串口(绿色➕)、右键项目->run as->run config......。因为这里用到了PL端外设因此我们需要配置bit文件。如果不需要用到pl端外设,就直接luanch on hardware 下载ps端程序即可。
31、双击GDB->勾选system reast 和program fpga 。勾选这两个可以帮助我们下载fpga pl侧程序。就不需要我们在vivade单独下载了。
32、点击run下载程序,观察结果。我这里是正确的。就不放视频演示了。