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

前一阵与一个朋友一起测试了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")
    File.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平台:
这里写图片描述
不知道为何vivado识别不到我在verilog里面的宏描述,导致必须手动配置接口总线,以后再来研究
进入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使用起来是否正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值