文章目录
- 一、介绍
- 二、代码编写
- 三、引脚分配
- 四、仿真分析
- 五、添加 ILA IP
- 六、板上验证
一、介绍
本文介绍的是在ZYNQ 7020黑金开发板上实现PL端流水灯的例子,开发板上PL端的LED灯总共有4个,在原理图中找到 PL LED 如下图所示,通过看图可知,给 LED 置低电平时灯才亮。
这里预想的实验结果是:在1秒钟内,4个LED各亮0.25秒,看起来就像流水灯一样。
LED:4个,赋值为0时即点亮。
时钟:50MHz。
复位:低电平有效,按键设为开发板上的 PL KEY1。
计数:电平每变化一次就加1,加到49999999后重新置为0。
二、代码编写
工程的创建这里不再过多介绍,之前的文章已经介绍过了。
下面的代码就是在计数到0,0.25,0.5,0.75秒这些时刻时,点亮对应的1个LED灯。
`timescale 1ns / 1ps
module led(
input sys_clk,
input rst_n,
output reg[3:0] led
);
reg[31:0] timer_counter;
always@(posedge sys_clk or negedge rst_n)
begin
if(!rst_n)
begin
led <= 4'b1111; //置0时灯才亮
timer_counter <= 32'd0;
end
else if(timer_counter == 32'd0)
begin
led <= 4'b1110;
timer_counter <= timer_counter +32'd1;
end
else if(timer_counter == 32'd12_500_000)
begin
led <= 4'b1101;
timer_counter <= timer_counter +32'd1;
end
else if(timer_counter == 32'd25_000_000)
begin
led <= 4'b1011;
timer_counter <= timer_counter +32'd1;
end
else if(timer_counter == 32'd37_500_000)
begin
led <= 4'b0111;
timer_counter <= timer_counter +32'd1;
end
else if(timer_counter == 32'd49_999_999)
begin
timer_counter <= 32'd0;
end
else
begin
led <= led;
timer_counter <= timer_counter +32'd1;
end
end
endmodule
下面代码实现的功能与上面代码一致,其中引入了case语句。
`timescale 1ns / 1ps
module led(
input sys_clk,
input rst_n,
output reg[3:0] led
);
reg[31:0] timer_counter;
always@(posedge sys_clk or negedge rst_n)
begin
if(!rst_n)
begin
led <= 4'b1111;
timer_counter <= 32'd0;
end
else if(timer_counter == 32'd49_999_999)
// else if(timer_counter == 32'd19) //用于测试
begin
timer_counter <= 32'd0;
end
else
begin
case(timer_counter)
32'd0 : led <= 4'b1110;
32'd12_500_000 : led <= 4'b1101;
32'd25_000_000 : led <= 4'b1011;
32'd37_500_000 : led <= 4'b0111;
// 32'd0 : led <= 4'b1110; //用于测试
// 32'd5 : led <= 4'b1101;
// 32'd10 : led <= 4'b1011;
// 32'd15 : led <= 4'b0111;
endcase
timer_counter <= timer_counter + 32'd1;
end
end
endmodule
代码编写完成后系统会自动保存,注意看右侧边栏处是绿色还是红色,是绿色就说明代码没有错误,把鼠标放在绿色框上面就会显示下面的信息。
如果是红色就说明代码中有错误,代码出错的地方也会有红色的波浪线,如下图所示。
顺便提一下,这个Vivado默认的代码字体太小了,在菜单栏点击Tools——>Settings打开设置窗口,找到Text Editor——>Fonts and Colors,在右侧就可以设置代码的字体大小了。
三、引脚分配
在左侧 RTL ANALYSIS 下点击 Open Elaborated Design 查看原理图,如下图所示。
如果在底部没有 I/O Ports 可以点击顶部菜单栏 Window 下的 I/O Ports 选项调出。
然后就要参考原理图给这些端口分配引脚。
复位是低电平有效的,将其和开发板上PL的一个按键进行绑定,如下图所示,选择开发板上的第四个实体键 PL KEY1(前三个分别是复位和两个PS端的按键),对应的原理图引脚是KEY1。
开发板上的PL端实体按键如下图所示。
四个 LED 和按键 KEY1 对应的引脚如下图所示。
时钟选择的是 50MHz,在原理图中找到相应的名称,如下图。
然后找到该名称对应的引脚是U18,如下图所示。
其中顶部的VCC3V3代表3.3V,因此将其VCC设置为LVCMOS33*,如下图所示就分配完成了。
命名并保存该文件,该文件是后缀为xdc的约束文件,打开后如下图所示。
后续也可以通过编辑该文件实现引脚的分配或者修改。
接下来运行综合,完成后打开约束向导面板,设置系统时钟为50MHz。
这时候约束文件中也会多一行代码,如下图所示。
四、仿真分析
在下载比特流文件到开发板之前,之所以先在Vivado软件中仿真,是因为生成比特流的过程太慢了,如果你每改动一次就在板子上验证一下实在是太费时间了,所以提前在仿真中调试好代码,有把握后到开发板上验证即可。
上面提供的代码中,用于测试的代码可以将计数量缩小,这样在仿真中就可以缩短仿真时间,这对整体的功能实现没有任何影响,不过要在生成比特流文件时记得将代码改过来。
在 Simulation Settings下可以设置仿真的运行时长,这里根据需要设置。
新建一个用于仿真的源文件,如下图。
完成后在文件中写入下面的仿真测试代码。
`timescale 1ns / 1ps
module sim_led();
reg sys_clk;
reg rst_n;
wire [3:0] led;
initial
begin
sys_clk = 1;
rst_n = 0 ;
#100; //延迟100ns
rst_n = 1;
end
//Create clock
always #10 sys_clk = ~ sys_clk; //时钟频率为50MHZ,周期为20ns,因此每延迟10ns,时钟翻转一次
// Instantiate the Unit Under Test (UUT) 例化待测设计
led uut_led(
.sys_clk (sys_clk),
.rst_n (rst_n),
.led (led)
);
endmodule
先用Vivado自带的仿真工具进行简单的行为仿真,得到的结果如下图所示。仿真这部分的操作如果不太熟悉可以参考文章:Vivado中Simulator仿真软件的使用。
通过上述仿真,其结果与代码中预设的一致,那下面就可以到开发板上验证了。如果仿真结果与自己的预想不一致,就要修改代码,重新仿真,直到结果与自己的预期相符,然后再到开发板上验证。
五、添加 ILA IP
ILA(Integrated Logic Analyzer),即集成逻辑分析仪,这里添加两个探针,分别设置为4位和32位,对应代码中的 led 和 timer_counter。这一部分的详细介绍可以参考文章:使用Vivado软件进行硬件调试。
生成后打开ila_0文件,复制84-90行的内容,注意有多个探针时的位数设置,在代码中不要搞错。
在led.v文件的endmodule前添加下面的代码。
ILA 就添加完成了。
六、板上验证
接下来点击生成比特流(Generate Bitstream),这个时间会比较长,完成后弹出下面对话框,选择Open Hardware Manager。
连接好开发板的JTAG接口,给开发板上电,然后下载比特流文件到开发板。
添加ILA后Debug probes file这里也会有文件。
下载进度条满了之后就说明下载成功了,PL端流水灯的演示如下动图所示。
使用集成逻辑分析仪查看,给其设置一个触发值25000000,可以看到,从25000001开始,LED的值发生了变化,如下图所示。
当然,在12500000和37500000处,LED的值也会发生变化。通过仿真、开发板上LED的动图以及ILA分析,其结果是按照代码中设置的那样输出的。
以上就是 ZYNQ——PL端流水灯的实现的全部内容了!
参考资料:
ZYNQ 开发平台 FPGA 教程 AX7020