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;
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) #define SW (*( volatile unsigned int * )0x41200000) 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算法…(准备好删除重开了),事情好多…明天写篇近况理一下…