PS与PL协同设计实现GPIO
凉凉不冷 Lv4

FPGA学习.PART3

1. 概述

ZYNQ7的逻辑部分PL与处理器部分PS协同工作,才能体现其强大,

在本实例中将FPGA当作一个PS处理器的外设,通过寄存器地址映射到PS的寻址空间,

在处理器中使用C程序访问这些寄存器,来实现软件和逻辑结合的协同设计效果

2. 实验流程

2.1 新建Block Design

放入ZYNQ7处理器,并导入Zedboard配置,方法见Part2.

方法一:手动连接

本次实验中PS部分仅用到 UART1、FCLK_CLK0(配置为100M)、FCLK_RESET0、M_AXI_GP0_ACLK端口,

检采完毕后如下图所示:

ZYNQ7.png

之后搜索(快捷键Ctrl + I)复位模块(reset),如下图所示:

然后是interconnect模块,其功能是将多路的AXI接口转化为多路的AXI接口(多对多,也可以是一对多):

继续添加GPIO模块:

按照下图进行连线:

双击GPIO_1进行如下配置(设置为8位),此为LED寄存器

同理配置GPIO_0(输入8位)

选择GPIO的输出端口(右侧),右键进行拓展(Make External,快捷键Ctrl + T),此时已经布局完毕,右键选择 “Regenerate Layout” 重新进行布局

方法二:自动连接

Vivado具有自动连接并放置模块的功能,我们可以只调出两个GPIO模块并拓展其输出,点击工作区上方的 “Run Connection Automation”,选择全部模块点击“OK”,便会自动帮我们添加AXI总线(互联)以及复位模块等.

2.2 修改引脚约束

方法一:修改TOP使其与XDC一致

完成互联后,右键工程(位于Design Sources)选择“Create HDL Wrapper”,因为LED自动生成的引脚会造成 XDC文件(约束文件)与TOP 文件不一致,所以此时选择 “Copy generated wrapper to allow user edits” 以方便我们进行编辑;

生成Verilog文件后,我们可以将代码中的端口声明 led_tri_o 换为 LED,将 sw_tri_i 换为 SW(输入输出声明处保持一致)

在声明wire类型处,添加

1
2
assign LED = led_tri_o;
assign SW = sw_tri_i;

或是直接更改网络名称(就是代码最下面的那一块),最终代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module design_1_wrapper
(DDR_addr,
DDR_ba,
DDR_cas_n,
DDR_ck_n,
DDR_ck_p,
DDR_cke,
DDR_cs_n,
DDR_dm,
DDR_dq,
DDR_dqs_n,
DDR_dqs_p,
DDR_odt,
DDR_ras_n,
DDR_reset_n,
DDR_we_n,
FIXED_IO_ddr_vrn,
FIXED_IO_ddr_vrp,
FIXED_IO_mio,
FIXED_IO_ps_clk,
FIXED_IO_ps_porb,
FIXED_IO_ps_srstb,
LED,
SW);
inout [14:0]DDR_addr;
inout [2:0]DDR_ba;
inout DDR_cas_n;
inout DDR_ck_n;
inout DDR_ck_p;
inout DDR_cke;
inout DDR_cs_n;
inout [3:0]DDR_dm;
inout [31:0]DDR_dq;
inout [3:0]DDR_dqs_n;
inout [3:0]DDR_dqs_p;
inout DDR_odt;
inout DDR_ras_n;
inout DDR_reset_n;
inout DDR_we_n;
inout FIXED_IO_ddr_vrn;
inout FIXED_IO_ddr_vrp;
inout [53:0]FIXED_IO_mio;
inout FIXED_IO_ps_clk;
inout FIXED_IO_ps_porb;
inout FIXED_IO_ps_srstb;
output [7:0]LED;
input [7:0]SW;

wire [14:0]DDR_addr;
wire [2:0]DDR_ba;
wire DDR_cas_n;
wire DDR_ck_n;
wire DDR_ck_p;
wire DDR_cke;
wire DDR_cs_n;
wire [3:0]DDR_dm;
wire [31:0]DDR_dq;
wire [3:0]DDR_dqs_n;
wire [3:0]DDR_dqs_p;
wire DDR_odt;
wire DDR_ras_n;
wire DDR_reset_n;
wire DDR_we_n;
wire FIXED_IO_ddr_vrn;
wire FIXED_IO_ddr_vrp;
wire [53:0]FIXED_IO_mio;
wire FIXED_IO_ps_clk;
wire FIXED_IO_ps_porb;
wire FIXED_IO_ps_srstb;
/*
wire [7:0]led_tri_o;
wire [7:0]sw_tri_i;

assign LED = led_tri_o;
assign SW = sw_tri_i;
*/
design_1 design_1_i
(.DDR_addr(DDR_addr),
.DDR_ba(DDR_ba),
.DDR_cas_n(DDR_cas_n),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cke(DDR_cke),
.DDR_cs_n(DDR_cs_n),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_reset_n(DDR_reset_n),
.DDR_we_n(DDR_we_n),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
.led_tri_o(LED),
.sw_tri_i(SW));
endmodule

修改完毕后即可保存生成比特流文件,但若这个顶层是自动生成的,则每次生成HDL Warpper的时候,都会被覆盖掉(若选择不覆盖则修改的内容无法更新),所以存在较大缺陷,这种情况下的约束文件内容为:(添加方式见Part1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
set_property PACKAGE_PIN T22  [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[0]}]

set_property PACKAGE_PIN T21 [get_ports {LED[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[1]}]
set_property PACKAGE_PIN U22 [get_ports {LED[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[2]}]
set_property PACKAGE_PIN U21 [get_ports {LED[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[3]}]
set_property PACKAGE_PIN V22 [get_ports {LED[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[4]}]
set_property PACKAGE_PIN W22 [get_ports {LED[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[5]}]
set_property PACKAGE_PIN U19 [get_ports {LED[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[6]}]
set_property PACKAGE_PIN U14 [get_ports {LED[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[7]}]


set_property PACKAGE_PIN F22 [get_ports {SW[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[0]}]
set_property PACKAGE_PIN G22 [get_ports {SW[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[1]}]
set_property PACKAGE_PIN H22 [get_ports {SW[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[2]}]
set_property PACKAGE_PIN F21 [get_ports {SW[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[3]}]
set_property PACKAGE_PIN H19 [get_ports {SW[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[4]}]
set_property PACKAGE_PIN H18 [get_ports {SW[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[5]}]
set_property PACKAGE_PIN H17 [get_ports {SW[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[6]}]
set_property PACKAGE_PIN M15 [get_ports {SW[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[7]}]

方法二:修改XDC使其与TOP一致

完成互联后,右键工程(位于Design Sources)选择“Create HDL Wrapper”,选择下方的 “Let Vivado manage wrapper and auto-update”,这样选择以后修改HDL Warpper的操作是无效的,

我们可以将约束文件中的 LED 全部替换为 led_tri_o,约束文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
set_property PACKAGE_PIN T22  [get_ports {led_tri_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[0]}]

set_property PACKAGE_PIN T21 [get_ports {led_tri_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[1]}]
set_property PACKAGE_PIN U22 [get_ports {led_tri_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[2]}]
set_property PACKAGE_PIN U21 [get_ports {led_tri_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[3]}]
set_property PACKAGE_PIN V22 [get_ports {led_tri_o[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[4]}]
set_property PACKAGE_PIN W22 [get_ports {led_tri_o[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[5]}]
set_property PACKAGE_PIN U19 [get_ports {led_tri_o[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[6]}]
set_property PACKAGE_PIN U14 [get_ports {led_tri_o[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[7]}]


set_property PACKAGE_PIN F22 [get_ports {sw_tri_i[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[0]}]
set_property PACKAGE_PIN G22 [get_ports {sw_tri_i[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[1]}]
set_property PACKAGE_PIN H22 [get_ports {sw_tri_i[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[2]}]
set_property PACKAGE_PIN F21 [get_ports {sw_tri_i[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[3]}]
set_property PACKAGE_PIN H19 [get_ports {sw_tri_i[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[4]}]
set_property PACKAGE_PIN H18 [get_ports {sw_tri_i[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[5]}]
set_property PACKAGE_PIN H17 [get_ports {sw_tri_i[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[6]}]
set_property PACKAGE_PIN M15 [get_ports {sw_tri_i[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[7]}]

SW全部替换为 sw_tri_i即可得到正确的比特流文件,如果报错的话,可以在“Sources Design” 右键选择 “Generate output products”

2.3 地址映射

FPGA要控制外设需要通过寄存器,寄存器需要映射入FPGA的寻址地址,若在“Address Editor”中没有自动关联,可以右键选中后点击“Assign Address”.

完成后返回“Sources Design” 右键选择 “Generate output products”,然后生成比特流文件,

生成完毕后不需要进行操作,可以选择 “View Reports”

2.4 SDK开发

“Export Hardware” 后 “Launch SDK”,

可以从图中看出,LED与SW的地址与我们映射的地址是保持一致的.

我们需要新建一个 “Application Project”,选项为 “Perihperal Tests” 以进行外设测试,

如下图所示:

点击左侧工作区中项目的 “src -> trestperiph.c” 对c文件进行编辑,

仅需要保留GpioInput与GpioOutput相关代码,

进入Gpio的函数中可以看到,输入状态时要在寄存器中全部置0,

GPIO的框图设计如下:

可以看出其三态门的架构,当寄存器中全部置1时,三态门为输入模式,而当寄存器被清空全部为0时,三态门为输出模式(看数据手册再结合实验收获满满),LED的基地址存储在一个查找表的结构体中,在xparameters.h文件中进行了定义:

1
#define XPAR_LED_BASEADDR 0x41210000

然后再将其写入GpioOutput中.

我们需要进行的操作是,写LED灯,读拨码开关.

寄存器地址如图所示:

由于已经在框图中设置了led为输出,sw为输出,所以不需要再配置GPIO_TRI的方向,

所以我们只需要将读到的拨码开关的寄存器写入LED的寄存器中,

通过指针来读取地址:(volatile意思是防止指针被优化)

1
( volatile unsigned int * )0x4121000

又要从地址中取数所以取其指针:(SW同理)

1
( * ( volatile unsigned int * )0x4121000)

所以可以将main函数进行修改:(注意地址为八位不要少写0,0少写了也会读不出来)

1
2
3
4
5
6
7
8
9
10
#define LED (*( volatile unsigned int * )0x41210000) //volatile为防止优化,
#define SW (*( volatile unsigned int * )0x41200000) //volatile为防止优化
int main()
{
while(1)
{
LED = SW;
}
return 0;
}

2.5 烧录

在保存后(会自动进行编译),

在终端页面添加串口后可以保存文件并准备烧录(Program FPGA),操作见Part2.

由于此次时PS与PL相互协作完成,所以在Program时需要选择比特流文件的地址,如下图

下载完毕后即可进行调试,右键工程 “ Run as -> Launch on Hardware(GDB)”

2.6 运行结果

运行结果是符合预期的,就是拍视频的时候一只手不太方便有点点遮挡视线…不过现象还是比较明显的.

折腾了一天终于搞出来了…接下来几天搞电赛培训了,还有卡了我好几天的Dijkstra算法…(准备好删除重开了),事情好多…明天写篇近况理一下…

 Comments