您的位置:首页 > 运维架构 > Linux

-09-EMIO Slice 自定义IP设计【Xilinx-Petalinux学习】

2017-03-14 18:37 423 查看
前一阵与一个朋友一起测试了petalinux中的GPIO驱动先关的代码。

一开始使用的AXI_GPIO这个IP核,输出的驱动还好,但是输入要加入中断等功能,就有点麻烦,而且配置来配置去的总是出问题,所以以后再来试这个吧。

因此决定以后再petalinux中使用GPIO功能时,都直接使用PS内的MIO和EMIO来实现。这样两者的驱动程序不仅相同,EMIO还能够自定义管脚位置,并且还不需要AXI_GPIO这个IP核需要消耗掉的逻辑资源。最重要的是,没有写驱动程序,而是直接使用了内核中的gpio-keys, leds-gpio驱动实现了我需要的功能 。

EMIO Slice设计

在block design中,一开始做的时候,引脚连接如下图:



GPIO_0及是PS的EMIO输入输出管脚,这里面可能包含了按键、LED等不同功能的GPIO,但我们没有办法,必须在约束文件中对他们进行区分,可视性不太好。所以想着做一个IP,实现EMIO各个位的引脚对应不同功能的区分。最终的IP GUI如下图:



下面来进行设计。

S1. verilog源码生成

我用Python脚本做了一个脚本,可以根据需要对EMIO的分支个数、每个分支的位宽进行配置,可以用来生成verilog源代码文件。脚本如下:

#! usr/bin/python
#coding=utf-8
import time
import os
import sys
out_width_max = 64
out_width_unuse = 0
out_width = 0
out_arg = ""

src_dir = "src"

def min_system_emio_slice_verilog_generate():
File = open("./" + src_dir + "/min_system_emio_slice.v", "w")
#
File.write("// Company:\n")
File.write("// Engineer: vacajk\n")
File.write("// \n")
File.write("// Create Tool: emio slice python script\n")
File.write("// Create Date: " + time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(time.time())) + "\n")
File.write("// Design Name: \n")
File.write("// Module Name: emin_system_mio_slice\n")
File.write("// Tool Versions: vivado 2015.4\n")
File.write("\n")
#
File.write("`timescale 1ns / 1ps\n")
File.write("\n")
#
File.write("module min_system_emio_slice (\n\t")
File.write("Din_i, Din_o, Din_t,\n\t")
for Index in range(len(out_arg)):
string_out = "Out" + str(Index)
File.write(string_out + "_i," + string_out + "_o," + string_out + "_t")
if Index < (len(out_arg) - 1):
File.write(",\n\t")
else:
File.write("\n\t")
File.write(");\n")
File.write("\n")
#
File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 Din TRI_I\" *)\n")
File.write("output wire [" + str(out_width-1)  +":0] Din_i;\n")
File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 Din TRI_O\" *)\n")
File.write("input wire [" + str(out_width-1)  +":0] Din_o;\n")
File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 Din TRI_T\" *)\n")
File.write("input wire [" + str(out_width-1)  +":0] Din_t;\n")
#
out_base = 0
for Index in range(len(out_arg)):
string_out = "Out" + str(Index)
string_out_base_low = str(out_base)
string_out_base_high = str(out_base + out_arg[Index] - 1)

File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 " + string_out + " TRI_I\" *)\n")
File.write("input wire [" + string_out_base_high + ":" + string_out_base_low + "] " + string_out + "_i;\n")
File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 " + string_out + " TRI_O\" *)\n")
File.write("output wire [" + string_out_base_high + ":" + string_out_base_low + "] " + string_out + "_o;\n")
File.write("(* X_INTERFACE_INFO = \"xilinx.com:interface:gpio:1.0 " + string_out + " TRI_T\" *)\n")
File.write("output wire [" + string_out_base_high + ":" + string_out_base_low + "] " + string_out + "_t;\n")
F
4000
ile.write("\n")
#
File.write("emio_slice #(\n\t")
File.write(".NUM_PORTS(" + str(len(out_arg)) +"),\n\t")
File.write(".DIN_WIDTH(" + str(out_width) +"),\n\t")
for Index in range(out_width_max):
if Index < len(out_arg):
File.write(".OUT" + str(Index) + "_WIDTH(" + str(out_arg[Index]) + ")")
else:
File.write(".OUT" + str(Index) + "_WIDTH(1)")
if Index < (out_width_max - 1):
File.write(",\n\t")
else:
File.write("\n\t")
#
File.write(") emio_slice_inst (\n\t")
File.write(".Din_i(Din_i), .Din_o(Din_o), .Din_t(Din_t),\n\t")
for Index in range(out_width_max):
str_i = "Out" + str(Index) + "_i"
str_o = "Out" + str(Index) + "_o"
str_t = "Out" + str(Index) + "_t"
if Index < len(out_arg):
File.write("." + str_i + "(" + str_i + "), ")
File.write("." + str_o + "(" + str_o + "), ")
File.write("." + str_t + "(" + str_t + ")")
else:
File.write("." + str_i + "(1'b0), ")
File.write("." + str_o + "(), ")
File.write("." + str_t + "()")
if Index < (out_width_max - 1):
File.write(",")
if Index < len(out_arg):
File.write("\n\t")
else:
File.write(" ")
if ((Index - len(out_arg)) % 4) == 3:
File.write("\n\t" )

else:
File.write("\n\t" )
File.write(");\n")
File.write("\n")
File.write("endmodule\n")
File.write("\n")

def emio_slice_verilog_generate(max_num_ports = 64):
File = open("./" + src_dir + "/emio_slice.v", "w")
#
File.write("// Company:\n")
File.write("// Engineer: vacajk\n")
File.write("// \n")
File.write("// Create Tool: emio slice python script\n")
File.write("// Create Date: " + time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(time.time())) + "\n")
File.write("// Design Name: \n")
File.write("// Module Name: emio_slice\n")
File.write("// Tool Versions: vivado 2015.4\n")
File.write("\n")
#
File.write("`timescale 1ns / 1ps\n")
File.write("\n")
#
File.write("module emio_slice (\n\t")
File.write("Din_i, Din_o, Din_t,\n\t")
for Index in range(max_num_ports):
File.write("Out" + str(Index) + "_i, ")
File.write("Out" + str(Index) + "_o, ")
if Index < (max_num_ports - 1):
File.write("Out" + str(Index) + "_t, ")
if (Index % 4) == 3:
File.write("\n\t" )
else:
File.write("Out" + str(Index) + "_t")
File.write("\n\t" )
File.write(");\n")
File.write("\n")
#
File.write("parameter NUM_PORTS = 2;\n")
File.write("parameter DIN_WIDTH = 2;\n")
File.write("output [DIN_WIDTH-1:0] Din_i;\n")
File.write("input [DIN_WIDTH-1:0] Din_o, Din_t;\n")
File.write("\n")
#
for Index in range(max_num_ports):
File.write("parameter OUT" + str(Index) + "_WIDTH = 1;\n")
File.write("\n")
#
for Index in range(max_num_ports):
if (Index % 10) == 0:
File.write("//" + str(Index) + "~" + str(Index+9) + "\n")
File.write("input [OUT" + str(Index) + "_WIDTH-1:0] Out" + str(Index) + "_i;\n")
File.write("output [OUT" + str(Index) + "_WIDTH-1:0] Out" + str(Index) + "_o, Out" + str(Index) + "_t;\n")
File.write("\n" )

#
string_line0 = ""
string_line1 = ""
string_line2 = ""
pre_string_line0 = ""
pre_string_line1 = ""
pre_string_line2 = ""
for Index in range(max_num_ports):
if (Index % 10) == 0:
File.write("//" + str(Index) + "~" + str(Index+9) + "\n")
string_line0 = "Out" + str(Index) + "_i" + pre_string_line0
string_line1 = "Out" + str(Index) + "_o" + pre_string_line1
string_line2 = "Out" + str(Index) + "_t" + pre_string_line2
pre_string_line0 = ", " + string_line0
pre_string_line1 = ", " + string_line1
pre_string_line2 = ", " + string_line2

File.write("generate if (NUM_PORTS == " + str(Index+1) + ")\n")
File.write("begin : S_NUM_" + str(Index+1) + "\n")
File.write("\tassign Din_i = {" + string_line0 + "};\n")
File.write("\tassign {" + string_line1 + "} = Din_o;\n")
File.write("\tassign {" + string_line2 + "} = Din_t;\n")
File.write("end\n")
File.write("endgenerate\n")
File.write("\n" )

File.write("endmodule\n")
File.write("\n")

File.close()

def make_src_dir(dir_name = "src"):
src_dir = dir_name
if(os.path.isdir(dir_name) == False):
os.mkdir(dir_name)

if __name__ == "__main__":
out_arg = sys.argv
del out_arg[0]
out_arg = map(int, out_arg)
out_width = sum(out_arg)
out_width_unuse = out_width_max - out_width
make_src_dir()
emio_slice_verilog_generate()
min_system_emio_slice_verilog_generate()
print "emio_slice verilog sources generated done!!!"


针对ZedBoard开发板来看,它有5个btn,8个sw,8个led,所以我们在命令行运行python脚本,并附加“5 8 8”的参数:



这样在子目录src文件夹下就能得到后面需要使用到的两个verilog源文件:



S2. 自定义IP生成

首先,以ZedBoard为平台新建vivado工程,这里就不描述了,然后导入刚才生成的两个源文件emio_slice.v和min_system_emio_slice.v,可以进行综合验证其是否有错误。

接下来就是一系列的生成自定义IP的操作。

创建和打包IP:



打包当前工程:



指定IP的生成目录:



接着就会弹出一个窗口,进行详细的IP配置。

设置IP名称、版本号等信息:



兼容所有Zynq平台:



<
bd17
strong>不知道为何vivado识别不到我在verilog里面的宏描述,导致必须手动配置接口总线,以后再来研究[/b]

进入Ports and Interfaces界面,如下图,我们需要把每组的GPIO信号进行分组。



点击上图的+号按钮,弹出下面的窗口。把Interface选择为gpio,然后手动指定管脚对应的_i,_o,_t。需要注意Din是gpio的从接口,所以mode需要选择mirroredMaster,而Out*则选择Master就好。同时也可根据信号的不同进行命名,我这里将Din定义为PS_EMIO,Out0定义为BTN,Out1定义为SW,Out2定义为LED。





全部配置完后如下图:



检查GUI是否正确:



点击Package IP生成IP:



这样,EMIO Slice IP就设计完成了,在指定的目录下将生成若干IP相关的文件:



S3. 在block design中加入EMIO Slice IP

首先在建立好的zynq工程中加入上面IP所在的目录。



然后在PS中配置EMIO的位宽,这里为:5+8+8=21



添加我们的min_sys_emio_slice自定义IP并连接到PS,最终效果如下图所示。



总结

虽然说这样子弄了半天才仅仅是把EMIO的信号更加形象的在block design中表现出来,有用没用我谈不上,但对于我这种使用GUI进行连线并伴有严重强迫症的人来说还是有点用的。

现在的问题就是在verilog源码中下面的定义,不知为什么vivado工具识别不了,必须要我手动去定义gpio bus,略显麻烦。

(* X_INTERFACE_INFO = "xilinx.com:interface:gpio:1.0 Din TRI_I" *)
output wire [20:0] Din_i;
(* X_INTERFACE_INFO = "xilinx.com:interface:gpio:1.0 Din TRI_O" *)
input wire [20:0] Din_o;
(* X_INTERFACE_INFO = "xilinx.com:interface:gpio:1.0 Din TRI_T" *)
input wire [20:0] Din_t;


下一次,生成新的hdf和bit文件,并编译出petalinux,试一试这个IP使用起来是否正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: