ZYNQ学习——将读秒数据通过串口发送
之前对ZYNQ不是很了解,仅有一些Verilog和8051单片机的基础,近日学习有所得,故将完成过程记录下来。所用板子为搭载xc7z20clg484的Zedboard,Vivado版本为2016。
本次工程为PS与PL的协同开发,读秒数据产生部分将使用PL实现,使用Zedboard板载的100MHzPL时钟接入以及EMIO按键作为复位信号输入;发送数据为ARM核通过AXI4总线读取数据,并通过板载UART发送,通过串口调试助手观察。
读秒数据产生
使用Verilog编写读秒数据产生模块的.v文件(已使用Modelsim仿真通过),并将其封装为IP核,以供之后BlockDesign工程使用。
代码
读秒数据产生(二进制)
module second_data_generator(
clk,
rst,
data_out,
);
input clk;
input rst;
output [7:0] data_out;
reg [31:0] div = 32'd0;
reg [7:0] cnt = 8'd0;
parameter factor = 32'd99999999; //clk = 12MHz, factor = 24'd11999999
//clk = 100MHz.factor = 24'd99999999
always @ ( posedge clk ) begin
if (div == factor)
div <= 32'd0;
else
div = div + 32'd1;
end
assign clk_1hz = ( div == factor ) ? 1'b1 : 1'b0;
always @ ( posedge clk_1hz ) begin
if ( rst )
cnt <= 8'd0;
else begin
if ( cnt == 8'd99 )
cnt <= 8'd0;
else
cnt <=cnt + 8'd1;
end
end
assign data_out = cnt;
endmodule
二进制转BCD
module bin_to_bcd(
clk,
data_in,
data_out
);
input clk;
input [7:0] data_in;
output reg [7:0] data_out = 8'b0;
reg [3:0] one = 4'b0;
reg [3:0] ten = 4'b0;
always @ ( posedge clk ) begin
one <= data_in % 8'd10;
ten <= data_in / 8'd10;
data_out <= { ten , one };
end
endmodule
读秒数据产生部分的顶层模块
module sd_tx (
clk,
rst,
txd
);
input clk;
input rst;
output [7:0] txd;
wire [7:0] data;
second_data_generator sdg (
.clk ( clk ),
.rst ( rst ),
.data_out ( data )
);
bin_to_bcd b2bcd (
.clk ( clk ),
.data_in ( data ),
.data_out ( txd )
);
endmodule
封装为IP核
1、首先点击Tool栏下的Create and package new IP。
2、选中红框所示选项,点击Next。
3、选择放置之前编写的.v文件所在地址。
4、给IP核命名,之后finish
5、之后得到如下界面
检查各项内容以后无需修改,点击封装IP。
封装AXI总线的外部设备IP核
1、打开之前封装IP的界面,这次选择创建AXI4外部设备。
2、给IP核命名
4、选择Lite AXI,作为slave,数据宽度,寄存器数量(最低4个,本次只用得到1个寄存器,用不到的不用管)。
5、点击编辑IP。
6、在自动创建打开的IP工程中对其中文件进行自定义。
首先打开下层模块
添加需要的端口,用作与读秒数据产生IP核的接口
因需要通过寄存器slv_reg0与PS交换数据,即将读秒数据输入该寄存器中让PS读取,所以屏蔽PS对该寄存器的写入操作,这样slv_reg0的数据只能有我们自定义的逻辑能改变
最后加入使用自定义的端口输入数据给slv_reg0赋值,至此对下层模块的操作结束。
接下来打开顶层文件
同样加入一个输入端口
加入对下层模块的例化端口,至此对IP核.v文件的修改完成。
7、打开封装IP核的界面,点击封装当前工程
Next之后点击Overwrite,完成IP核的创建。
8、之后打开工程所在文件夹的ip_repo文件夹,其中红框所示文件夹即为可使用的IP核。
9、在工程文件夹中新建my_ip文件夹,将IP核移动到其中,在vivado中打开工程设置
点击加号选择添加,之后即可在工程中搜索到。
创建并完成Blockdesign
1、点击创建Blockdesign文件
2、添加ZYNQ核
双击ZYNQ核以后,在MIO配置中打开UART1.
3、添加自定义的2个IP核,并将读秒数据的输出和输入端口连接
点击Run Block Automation和Run Connection Automation
在读秒数据产生IP核的clk核和rst端口上右键,点击 Make External
若觉得自动生成的布线太乱,可右键选择红框所示选项,将重新自动布线。
点击所示选项验证
4、在Source栏中右键bd文件,点击Generate Ouput Products
选择Global然后Generate
在刚才的右键菜单中点击Create HDL Wrapper,ok以后将生成该blockdesign设计的verilog代码。
5、点击综合
6、综合完成后点击实现(实现即是将综合后的网表加入物理约束和始终约束)
点击打开I/O port分配EMIO的引脚
其中clk分配的是PL的全局时钟GCLK,查原理图可知其引脚编号为Y9,电平标准为LVCMOS33;rst分配的是按键BTNU,引脚编号为T18,其电平标准为VADJ,需将板子的VADJ调节区1.8V处插上短路帽,因此最后选择LVCMOS18
保存为.xdc文件
注:vivado2016存在bug,直接保存后总是会报错,解决办法是再次保存为另一个文件,之后将第一个删除即可。
注:也可直接编写xdc文件,而不用通过图形界面进行分配。
7、点击生成比特流文件。
点叉结束
8、点击export-export hardware
勾选include bitstream,OK
点击export下方的launch SDK,即可将其导入到SDK中进行PS的设计。
PS的程序设计
1、新建application project
2、在建立的工程的src文件夹下新建一个C语言源文件
3、键入代码
#include <stdio.h>
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "xil_types.h"
#include "xil_printf.h"
int main(){
u8 txd=0;
txd = Xil_In32(XPAR_TX_AXI_V1_0_BASEADDR);
while(1){
txd = Xil_In32(XPAR_TX_AXI_V1_0_BASEADDR);
xil_printf("%x\r\n",txd);
sleep(1);
}
}
Xil_In32()函数为读寄存器中数据,地址可由xparameters.h文件中获得,其中地址为字节编址,而寄存器深度为32bit,即可利用地址自由获得各寄存器不同位置的数据。
xil_printf()可将数据通过UART发送(波特率为115200)。
sleep()函数可设置延迟,较为精确。
4、右键所建工程,选择debug as—debug configuration
双击该项
勾选
板子选择从JTAG启动并上电,点击apply、debug,点击yes。
点击该项将程序烧写到板子运行。
可在SDK中的SDK Terminal处添加端口观察读秒数据,也可使用其他串口调试助手进行观察。
现象