1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第二十二章IP封装与接口定义实验
在前面的实验中,我们通过调用各种功能的IP核,可以快速地搭建Block Design。除了调用Vivado IP库中的IP核,我们还可以创建或封装自己的IP核。在本章我们将学习如何把HDL实现的功能模块封装成IP核,以及如何定义新的接口类型。
本章包括以下几个部分:
2222.1简介
22.2实验任务
22.3硬件设计
22.4程序设计
22.5下载验证
22.1简介
Vivado开发工具集成了大量Xilinx官方IP核以及第三方厂商提供的IP核,如图 22.1.1所示。这些IP核可以实现不同类型的功能,通过调用IP核可以大大加速我们的设计流程。我们也可以创建或封装自己的IP核,并将之添加到Vivado的IP库中,以方便模块的重复调用,或者在团队开发过程中进行IP的共用。
图 22.1.1 Vivado IP核目录
在搭建Block Design的过程中,我们通过连线将各IP核相同类型的接口连接起来。IP核通过接口与外部模块进行信号传递,在Vivado的IP库中包含各种不同类型的接口定义,如图 22.1.2所示:
图 22.1.2 Vivado 接口目录
IP核的接口(Interfaces)由一个或者多个端口(Ports)组成,不同类型的信号需要通过不同类型的接口进行传递。需要注意的是,这里的“信号”可以由一根信号线传递,如时钟信号,此时时钟接口仅包含一个端口。有的信号需要通过多根信号线进行传递,比如视频信号,它包含像素数据、行同步信号和场同步信号等,那么视频接口就由多个端口组成,各个端口分别由独立的信号线连接。
例如,图 22.1.3是AXI GPIO IP核的模块框图,从图中可以看出该IP核有四个接口。其中由两个红色箭头指示的加号表示这两个接口由多个端口组成,另外两个接口则只包含一个端口。
图 22.1.3 AXI GPIO IP核
我们点击上图中红色箭头所指示的加号可以将该接口展开,如下图所示:
图 22.1.4 接口与端口
从图 22.1.4中可以看出,S_AXI接口共包含17个端口,端口名均以s_axi_开头。GPIO接口共包含3个端口,端口名均以gpio_io_开头。
在创建自定义IP核的过程中,为了方便IP核之间的信号连接,我们可以把模块中同类性质或者属于同一组信号的多个端口定义成一个接口。如果在Vivado的IP库中找不到所需要的接口类型,我们也可以重新创建一个新的接口定义。
22.2实验任务
本章的实验任务是将“HDMI彩条显示实验”中所实现的RGB2DVI模块封装成一个IP核。
22.3硬件设计
本次实验中的硬件设计部分与“HDMI彩条显示实验”完全相同,请大家参考《MPSOC之FPGA开发指南》中相应的章节。
22.4程序设计
(1)创建IP目录
首先我们在Vivado工程目录下新建一个文件夹DVI_TX,这个就是我们要封装的IP所在的位置。这个目录可以与大家平时创建Vivado工程的路径一致,也可以选择其他路径,但是要求路径名只能由英文字母、数字和下划线组成,不能包含中文、空格或者特殊字符。
接下来,在DVI_TX文件夹中另外新建两个文件夹:ip和if。其中ip用于存放封装的IP核,if用于存放我们定义的接口(if是interface的缩写)。创建完成后如下图所示:
图 22.4.1 IP核与接口文件目录
然后在ip文件夹中新建一个名为src的文件夹,并将HDMI彩条显示实验中RGB2DVI模块所对应的4个源文件拷贝到src文件夹中,拷贝完成后如下图所示:
图 22.4.2 IP封装使用的模块源文件
为了方便大家能够快速地找到这几个源文件,在这里我们也把这几个源文件所在的位置截图如下:
图 22.4.3 HDMI彩条显示实验源文件
(2)创建Vivado工程
在文件夹目录和源文件准备好之后,接下来我们需要创建一个Vivado工程,用于管理IP核。
首先打开Vivado软件,软件启动后在Tasks一栏选择“Manage IP”,然后点击“New IP Location”,如下图所示:
图 22.4.4 新建IP路径
在弹出的对话框中点击“Next”,如下图所示:
图 22.4.5 创建自定义 IP路径向导
在IP设置界面选择器件类型,并指定工程所在目录,然后点击“Finish”,如下图所示:
图 22.4.6 管理IP工程设置
需要注意的是,在图 22.4.6中,IP location一栏中的路径是Vivado工程所在的路径,与我们要封装的IP核所在的路径(即前面我们新建的DVI_TX文件夹)无关。
在上图中点击“Finish”之后,Vivado会新建一个名为“Manage IP”的工程,如下图所示:
图 22.4.7 Manage IP工程
(3)创建和封装IP
接下来,我们将在当前工程中创建和封装新的IP核。
在菜单栏中点击“Tools”,然后在下拉列表中选择“Create and Package New IP”,如下图所示:
图 22.4.8 创建和封装IP
在弹出的对话框中点击“Next”,如下图所示:
图 22.4.9 创建和封装IP
然后在下一个界面中选择“Package a specified directory”,然后点击“Next”,如下图所示:
图 22.4.10 选择封装一个指定的目录
在下图所示界面中,指定要封装的IP核源文件所在的目录,即我们前面所创建的DVI_TX文件夹中的ip目录,源文件位于该目录下的src文件夹内。然后点击Next,如下图所示:
图 22.4.11 选择源文件所在目录
在下一个界面中提示将会重新打开一个工程,我们将在新建的工程中对IP核进行编辑。工程的名称和路径已经自动添加,我们直接点击Next,如下图所示:
图 22.4.12 IP封装工程名
最后点击Finish,如下图所示:
图 22.4.13 新IP创建
在点击Finish之后,Vivado会重新打开一个工程用于编辑IP核,如下图所示:
图 22.4.14 编辑IP工程
在图 22.4.14中我们可以在Sources栏看到需要进行封装的IP核源文件。需要注意的是,箭头指示的模块serializer_10_to_1前有品字形的标识,表明该模块被识别成顶层模块。但是顶层模块实际上是dvi_transmitter_top模块,因此我们需要在dvi_transmitter_top上右击,然后选择“Set as top”,将其设置成顶层模块,如下图所示:
图 22.4.15 设置顶层模块
在修改顶层模块之后,接下来我们要对IP核进行设置和编辑。
(4)设置IP信息
在主界面Package IP页中,点击左侧Packaging Steps一栏中的“Identification”。然后修改右侧箭头所指示的选项,括厂商(Vendor)、IP核名称(Name)、IP核显示名称(Display name)以及对IP核的描述(Description)等信息。修改后的IP核信息如下图所示:
图 22.4.16 IP核信息
从图 22.4.16下方红色方框所标识的内容可以看出,IP核的根目录位于DVI_TX/ip文件夹中。
(5)设置IP兼容性
然后点击左侧Packaging Steps一栏中的“Compatibility”,在右侧设置IP核兼容性,如下图所示:
图 22.4.17 IP核兼容性
IP核兼容性是指我们封装的IP核可用于哪些型号的器件。由于我们封装的RGB2DVI模块在设计时用到了器件的原语,其功能与器件底层的硬件结构有一定的关联,因此并不适用于Xilinx所有型号的器件。此处我们在IP核兼容性中只保留zynqplus系列。然后将其他所有型号的器件选中后,点击图 22.4.17中箭头所指示的减号将其删除。
在删除多余的器件型号后,IP核兼容性设置如下图所示:
图 22.4.18 IP核兼容性设置
(6)设置文件组
然后点击左侧Packaging Steps一栏中的“File Groups”,如下图所示:
图 22.4.19 IP核文件组
在文件组(File Groups)中列出了IP核所使用到的源文件。由于我们前面修改了顶层模块,因此在图 22.4.19中我们需要点击箭头所指示的位置,将改动更新到文件组向导界面。
(7)设置变量
接下来点击左侧Packaging Steps一栏中的“Customization Parameters”如下图所示:
图 22.4.20 IP核定制变量
这里我们同样需要点击图 22.4.20中红色箭头所指示的位置,将改动更新到定制变量(Customization Parameters)向导界面中。在点击之后会弹出对话框提示出现错误,如下图所示:
图 22.4.21 错误提示
这个错误是由于工具最开始错误地识别了顶层模块,这个错误我们将会稍后解决,在这里我们直接点击OK。
由于在RGB2DVI模块中没有定义parameter变量,因此在Customization Parameters页面工具没有识别到任何变量。
(8)设置端口和接口
接下来点击左侧Packaging Steps一栏中的“Ports and Interfaces”,如下图所示:
图 22.4.22 IP核端口和接口
我们将在图 22.4.22所示的界面设置IP核的端口和接口。在该界面中已经列出了顶层模块所有的输入和输出端口,同时工具自动识别出了reset_n等接口。但是工具自动识别出来的接口并不完全正确,需要我们根据各端口的定义重新进行修改。
在这里我们重新给出顶层模块的输入和输出接口定义,代码如下所示:
1 module dvi_transmitter_top(
2 input pclk, // pixel_clk
3 input pixel_clk_5x, // pixel_clk的5倍时钟
4 input pixel_clk_2_5x, // pixel_clk的2.5倍时钟
5 input reset_n, // reset
6
7 input [23:0] video_din, // RGB888 video in
8 input video_hsync, // hsync data
9 input video_vsync, // vsync data
10 input video_de, // data enable
11
12 output tmds_clk_p, // TMDS 时钟通道
13 output tmds_clk_n,
14 output [2:0] tmds_data_p, // TMDS 数据通道
15 output [2:0] tmds_data_n,
16 output tmds_oen // TMDS 输出使能
17 );
我们将代码中的接口与图 22.4.22中的接口进行对比发现,图 22.4.22中箭头所指示的两个接口信号在代码中并不存在。这同样是由于工具最开始将serializer_10_to_1模块识别成了顶层模块,而箭头所指示的两个接口信号正是serializer_10_to_1模块中的接口,因此我们需要先将这两个多余的接口从IP中移除。
选中多余的两个接口reset和paralell_clk,然后右击,选择“Remove Interface”,如下图所示:
图 22.4.23 移除多余接口
在移除这两个接口之后,图 22.4.23红色圆圈所指示的两个错误也随之消失。如下图所示:
图 22.4.24 错误提示消失
另外工具错误地将tmds_clk_n和tmds_clk_p这两个端口识别成了两个时钟接口,因此我们需要将这两个接口移除,如下图所示:
图 22.4.25 移除错误接口
移除错误的时钟接口后,IP核端口与接口界面如下图所示:
图 22.4.26 IP核端口与接口
如图 22.4.26中的红色方框所示,我们将该IP核的输入输出端口分成了四种接口类型:复位接口、时钟接口、视频接口和TMDS接口。其中复位接口已经由工具识别到复位信号reset_n后自动创建,而其他三个类型的接口需要我们手动添加。除此之外,由于TMDS连接中仅包含三个数据通道和一个时钟通道(详见HDMI彩条显示实验),因此tmds_oen信号没有划分到TMDS接口中,而是作为IP核独立的端口。
接下来我们首先添加时钟接口,由于每个时钟接口只包含一路时钟信号,因此我们需要为pclk、pclk_x5和pclk_x2_5各添加一个时钟接口。
(9)添加时钟接口
在pclk上右击,然后选择“Add Bus Interface”,如下图所示:
图 22.4.27 添加总线接口
在弹出的对话框中选择General页面。首先要指定添加的接口类型,默认的接口类型是aximm_rtl,如下图所示:
图 22.4.28 添加接口
点击图 22.4.28中箭头所指示的按钮,然后在弹出的接口选择界面中点击放大镜图标进行搜索,如下图所示:
图 22.4.29 选择接口类型
由于我们需要添加时钟接口,因此在图 22.4.29的搜索栏中输入“clock”进行搜索。然后在下面列出的搜索结果中选择clock_rtl,在其右侧可以看到对该接口的描述“Xilinx时钟信号接口”,最后点击“OK”。
在返回General界面后,输入该时钟接口的名称为pclk,并设置模式为slave。如下图所示:
图 22.4.30 指定接口名称与模式
当模式设置为slave时,表示该时钟接口是一个输入接口;如果设置为master,则表示它是一个输出接口。在RGB2DVI模块中,两个时钟信号均为输入信号,因此需要设置为slave。
接下来我们进入Port Mapping页面,将接口的逻辑端口与IP的物理端口映射起来,如下图所示:
图 22.4.31 IP端口映射
在图 22.4.31中,左侧的Interface’s Logical Ports指的是我们添加的接口由哪些端口组成,比如时钟接口就只有一个名为“CLK”的时钟端口。而在右侧的IP’s Physical Ports一栏中列出了IP核顶层模块所有的输入输出端口。我们需要在Port Mapping页面中将二者映射起来,也就是为我们模块的输入输出端口指定信号类型。
如图 22.4.31中的两个圆圈所示,在左侧点击选中逻辑端口CLK,然后在右侧选中物理端口pclk,最后点击箭头所指示的按钮“Map Ports”即可完成pclk的端口映射。映射之后的端口会在下方Mapped Ports Summary栏中列出。
最后点击右下角的OK,即可完成时钟接口的添加,添加完成后如下图所示:
图 22.4.32 添加pclk时钟接口
图 22.4.32中箭头所指示的是我们为IP核手动添加的时钟接口,并将其命名为pclk。该时钟接口只包含一个端口,即顶层模块的输入端口pclk。
IP核顶层模块有三个输入时钟,因此我们需要为pclk_x5和pclk_x2_5分别添加一个时钟接口。该过程与添加pclk时钟接口的步骤是相同的,只需要在接口命名时分别修改为pclk_x5和pclk_x2_5。添加完成之后如下图所示:
图 22.4.33 添加pclk_x5和pclk_x2_5时钟接口
(10)添加视频接口
在添加了三个时钟接口之后,接下来我们要为图 22.4.33中红色方框标注的四个端口添加一个视频接口。在其中任一端口上右击,然后选择“Add Bus Interface”。
在弹出的对话框中选择General页面,然后点击“Interface Definition”右侧的按钮选择接口类型。
接下来,在接口选择界面搜索“video”,然后选择“vid_io_rtl”视频接口,如下图所示:
图 22.4.34 选择视频接口
在图 22.4.34中点击右下角的“OK”返回General界面,然后输入接口名称“Video_In”。另外,将该视频接口的模式设置为“Slave”,即作为输入接口,如下图所示:
图 22.4.35 设置视频接口
在图 22.4.35中“Description”一栏输入对该视频接口的描述RGB888 Video,表明输入的视频数据格式为RGB888。
接下来在Port Mapping界面对端口进行映射,如下图所示:
图 22.4.36 视频端口映射
从图 22.4.36中可以看出,我们所添加的视频接口共包含7个端口,但是我们只用到了其中四个:DATA是视频像素数据,ACTIVE_VIDEO是视频有效信号,HSYNC和VSYNC分别是视频的行同步和场同步信号。
我们按照图中箭头所指示的连接方式将上述四个端口与IP核的视频端口一一映射起来,映射完成后的端口在下方Mapped Ports Summary栏中列出。
最后点击右下角的OK,完成视频接口的添加。添加完成后IP核接口页面如下图所示:
图 22.4.37 添加Video_In视频接口
到这里我们已经成功添加了时钟接口和视频接口,接下来我们要为图 22.4.37中圆圈所标注的四个端口添加一个TMDS接口。但是在Vivado的IP库中并没有这个TMDS的接口定义,因此我们需要先定义一个TMDS接口。
(11)创建TMDS接口定义
在当前工程中的菜单栏点击“Tools”,然后选择“Create Interface Definition”,如下图所示:
图 22.4.38 创建接口定义
在弹出的对话框中选择“Create new interface definition”,然后分别输入厂商(Vendor)、接口名(Name)、路径(Location)等信息,如下图所示
图 22.4.39 设置接口定义
在图 22.4.39中将接口名称设置为“TMDS”,路径设置为IP核路径中的if文件夹,然后点击OK。
接下来工具会自动打开TMDS接口的配置界面,里面已经包含了前面设置的接口信息,如下图所示:
图 22.4.40 添加TMDS接口端口
在图 22.4.40中的Description一栏,我们可以添加对该接口的描述——“TMDS Link Interface”,即TMDS连接接口。TMDS接口一共包含四个端口,分别是:tmds_clk_p、tmds_clk_n、tmds_data_p和tmds_data_n,其中tmds_data_p和tmds_data_n的位宽为3位。
接下来我们需要为该接口添加端口,在上图中Ports一栏点击箭头所指示的加号,会弹出如下图所示的对话框:
图 22.4.41 设置端口
如图 22.4.41所示,我们添加的第一个端口名为“tmds_clk_p”,并将其默认值(Default Value)设置为0。接下来我们还需要针对TMDS接口的Master和Slave两种模式,分别设置该端口的属性。首先在这两种模式下该端口的位宽(Width)都是1位,另外在Master模式下该端口作为输出信号(out),在Slave模式下该端口作为输入信号(in)。
接下来在端口类型(Type)一栏,勾选“Clock”一项,表明该端口传输的是时钟信号。最后点击右下角的OK。
端口属性设置完成后TMDS接口定义界面如下图所示:
图 22.4.42 添加tmds_clk_p端口
然后我们还需要按照同样的方式添加tmds_clk_n端口,在这里就不再赘述了。
接下来我们添加tmds_data_p端口,tmds_data_p端口的设置界面如下图所示:
图 22.4.43 设置tmds_data_p端口
需要注意的是图 22.4.43中圆圈标注的部分,与tmds_clk_p端口不同,tmds_data_p端口的位宽(Width)为3,类型(Type)为数据(Data)。然后我们需要按照同样的方式tmds_data_n端口。
所有的端口添加完成后TMDS接口定义的界面如下图所示:
图 22.4.44 TMDS接口定义
到这里我们的TMDS接口就创建完成了,点击图 22.4.44中右下角的Save保存。
保存后我们可以到最开始创建的DVI/if文件夹下查看所生成的接口定义文件,如下图所示:
图 22.4.45 TMDS接口定义文件
在创建TMDS接口定义之后,我们在IP核中添加该接口。
(12)添加TMDS接口
在图 22.4.37中圆圈所标注出的端口上右击,然后选择“Add Bus Interface”,然后在弹出的接口选择界面搜索“TMDS”,如下图所示:
图 22.4.46 添加TMDS接口
在图 22.4.46中可以看到,TMDS_rtl接口所在的库为User,即用户自定义的接口。这个接口原本在Vivado的接口库中是不存在的,在我们创建其接口定义之后才能够搜索到。点击选中该接口后点击右下角的OK。
在返回General页面后,在该界面对添加的TMDS接口进行设置,如下图所示:
图 22.4.47 设置TMDS接口
图 22.4.47中,我们设置接口名为“TMDS”,然后设置模式为“master”,即该接口在IP核中作为输出接口。
设置完成后,我们到Port Mapping界面中对接口的端口进行映射,如下图所示:
图 22.4.48 TMDS接口端口映射
按照图 22.4.48中箭头所指示的连接方式,将左侧TMDS接口的逻辑端口与IP核的物理端口映射起来。映射后的端口会在下方Mapped Ports Summary一栏中列出。映射完成后点击图中右下角的OK。
至此,我们要封装的IP核所有的接口均已添加完成,如图 22.4.49所示。IP核共包括六个接口:Video_In、TMDS、reset_n、pclk、pclk_x5和pclk_x2_5。其中reset_n接口是工具在识别到模块的复位端口后自动添加的接口,而TMDS接口是我们自定义的接口。
另外,图中箭头所指示的tmds_oen端口将作为独立的端口存在,我们没有为它再创建新的接口定义。也就是说,在IP核中,并不是所有的端口都要以接口的形式存在。我们为IP核添加或者创建接口是为了更方便地连接IP核之间的端口信号。
图 22.4.49 IP核接口
(13)编辑复位接口
除了删除或者添加接口,我们还可以对IP核的接口重新进行编辑或者修改。在复位接口reset_n上右击,然后选择“Edit Interface”,如下图所示:
图 22.4.50 编辑接口
然后在弹出的窗口中进入“Parameters”页面,在该页面中可以为接口添加变量。如下图所示:
图 22.4.51 添加变量
在图 22.4.51左侧选择“POLARITY”变量,然后点击图中圆圈所标注的箭头,将该变量添加到右侧IP核的变量列表中。添加完成后如下图所示:
图 22.4.52 设置变量
在图 22.4.52中,我们需要在Value一栏设置POLARITY变量的值为ACTIVE_LOW。变量POLARITY指的是复位接口的极性,而我们将它的值设置为“ACTIVE_LOW”则表明该复位接口是低电平有效。这一设置需要与IP核程序设计中复位接口的极性保持一致。最后点击右下角的OK完成对复位接口的编辑。
到这里我们对IP核端口和接口的设置就完成了,其中时钟和复位接口会被折叠到“Clock and Reset Signals”分组中,如下图所示:
图 22.4.53 完成端口和接口设置
(14)查看IP核GUI界面
另外在我们封装的IP核中不涉及存储映射和地址空间等,因此可以直接跳过图 22.4.53中箭头所指示的Addressing and Memory设置界面,直接进入下一个Customization GUI界面。如下图所示:
图 22.4.54 IP核图形用户界面
在图 22.4.54中我们可以看到IP核封装后的图形用户界面,共包含五个接口和一个端口(tmds_oen)。
(15)封装IP核
最后我们点击左侧“Packaging Steps”一栏中的“Review and Package”,如下图所示:
图 22.4.55 封装IP
在图 22.4.55中的Summary一栏我们可以看到IP核的名称、描述,以及根目录等信息。确认无误后,点击下方的“Package IP”按钮完成IP封装。
IP封装完成后,会弹出一个对话框提示名为“DVI_Transmitter”的IP核封装成功,如下图所示:
图 22.4.56 关闭编辑IP工程
在对话框中给出了最终封装的IP所在的路径,并提示是否关闭当前工程,我们直接点击Yes。
接下来我们到IP核所在的路径中查看封装之后的IP核文件,如下图所示:
图 22.4.57 封装之后的IP
然后我们双击打开图 22.4.57中的xgui文件夹,如下图所示:
图 22.4.58 xgui文件夹
可以看到xgui文件夹中有两个文件,其中DVI_Transmitter_v1_0.tcl与我们封装的IP核名称一致。而另外一个名为serializer_10_to_1_v1_0.tcl的文件是由于工具最开始将serializer_10_to_1模块错误地识别成了顶层模块而生成的,在这里我们将图 22.4.58中箭头所指示的文件删除。
到这里我们的IP封装和接口定义就完成了,如下图所示:
图22.4.59 封装完成的IP
22.5下载验证
为了验证我们封装的IP核功能是否正常,我们可以将DVI_TX文件夹拷贝到《SD卡读BMP图片HDMI显示实验》的工程目录中,然后打开工程,将Block Design中的DVI_Transmitter模块替换成大家自己封装的IP。在重新编译之后,参考《SD卡读BMP图片HDMI显示实验》下载验证部分的操作方式进行下载。如果HDMI显示器上能够正常显示图像,即说明我们本次实验成功地完成了DVI_Transmitter IP核的封装和TMDS接口的定义。