赞
踩
首先得承认,我并不是主动拥抱顶层文件这套思路的,原因很简单,能用就行干嘛费劲搞那么多东西。起初知识点亮一个LED灯,整一个半加器的简单模拟,也确实根本用不上。后边工程有一定的负责度,例如设计数字时钟,LCD1602驱动设计等等,这个时候我就发现了层次化设计的一个便捷之处,在于他们方便复用
,只需要定义好一个功能Module,可以在仿真 – 下板之间无缝衔接,增加了自己开发的效率,减少不必要的注释,感兴趣可以接着往下看。
另外自己上述两个工程博客连接:
一图胜千言,引用野火开发板他们家的资料(文末备注)里说明复用
的思想在哪个地方。举个例子,比如我现在定义了一个橙色的模块,我现在第一层(绿色)的需求是设计一个数字时钟,那么第二层拆分开就是定时1秒(蓝色),定时1分钟(橙色)、定时1小时(红色)。接着蓝色的定时1秒,我再通过其他小模块继续拆分。那么大家有没有想过一个问题,都是同样的计数/计时,秒钟/分钟/时钟不都是一样的吗,只不过一个是定时1秒,一个是60秒,一个是3600秒。是的,我们完全可以统一写一个定时模块(比如第三层蓝色功能模块1_1),改变定时的值(1/6/3600)即可。
也就是说,层次化的设计思想,其实就是拆分需求,减少重复代码统一集成一个公用的模块,这就达到了Module复用
的功能。说起来非常高端,我们通过一个简单例子来说明下如何使用吧,时间才是检验真理的唯一标准。
首先要申明一点,目前入门FPGA的阶段属于非常非常,所有工程代码、工程逻辑都可能存在错误,希望各位同学老师能够指出,同时也参考了大量的互联网上优质内容,在这里一并感谢,基于这个原因也决定将核心逻辑进行共享。请勿做一切商业用途。接下来会以我在数字时钟那篇的博客,讲一讲大概的逻辑,具体实操大家可以移步数字时钟的那篇博客。
这里是引用
带大家看三个文件,第一个是功能模块长什么样;第二个是理解顶层文件Top.v长什么样,到底是怎么复用的;第三个是了解testbench文件是如何设计的,和顶层文件有什么区别。
clock1.v文件:
//数字时钟模块 module clock1( sec, min, hour, rst, sys_clk_p, sys_clk_n, clk_out1, led_out1, led_out2 ); input rst; input sys_clk_p; // Differential input clock 200Mhz input sys_clk_n; // Differential input clock 200Mhz output clk_out1; output [7:0] sec,min; output [7:0] hour; output led_out1; output led_out2; reg [7:0] sec=0; reg [7:0] min=0; reg [7:0] hour=0; reg [32:0] cnt; reg clk_div; reg led_out1; reg led_out2; //分频部分 50MHz - 1Hz always @ ( posedge clk_out1 or posedge rst)begin 篇幅原因略去代码 end // 秒钟部分 always @ ( posedge clk_div or posedge rst)begin 篇幅原因略去代码 end //分钟部分 always @ ( posedge clk_div or posedge rst)begin 篇幅原因略去代码 end // 时钟部分 always @ ( posedge clk_div or posedge rst) 篇幅原因略去代码 end endmodule
这个模块该有的功能全都有,篇幅原因省去的代码其实都基本一个样。需要关注到的是这个module里边详细定义了sec
、min
、hour
这三个变量,并且都是output
类型。因为我们是对顶层设计的举例子,代码详细功能不在这里讲解。接下来一并看剩下两个文件。
Top.v文件
module TOP( input rst, input sys_clk_p, input sys_clk_n, output clk_out1, output led_out1, output led_out2 ); //***********差分时钟50MHz ***************************************** wire sys_clk_p; wire sys_clk_n; wire rst; wire clk_out1; //***********数字时钟**************************************** wire led_out1; wire led_out2; //时钟模块初始化 clock1 clock( .led_out1(led_out1), .led_out2(led_out2), .rst(rst), .sys_clk_p(sys_clk_p ), .sys_clk_n(sys_clk_n ), .clk_out1(clk_out1 ) ); //PLL分频的代码 endmodule
Testbench文件
`timescale 1ns / 1ns module clock1_tb(); //***********??????***************************************** reg sys_clk_p; wire sys_clk_n; reg rst; wire clk_out1; //***********??????***************************************** wire [7:0] sec; wire [7:0] min; wire [7:0] hour; wire led_out1; wire led_out2; //初始化系统时钟 initial begin sys_clk_p = 1'b0; 略去代码 end //数字时钟初始化 clock1 clock( .led_out1(led_out1), .led_out2(led_out2), .sec(sec), .min(min), .hour(hour), .rst(rst), .sys_clk_p(sys_clk_p ), .sys_clk_n(sys_clk_n ), .clk_out1(clk_out1 ) ); //PLL时钟分频 endmodule
可以看两个文件夹,代码基本都是一模一样的,定义好端口(input/output/wire/reg等),初始化clock模块如何结束。
这里重点关注第一点,我们这里并不需要把sec
、min
、hour
这三个寄存器变量输出(需要制定引脚),所以我们在Top.v文件里边不声明他的输入输出类型,但实际下板却并不影响clock功能的实现,因为模块的功能实现与否,和你是否声明并无关系。你申明了,他就展示出来,不声明他就不展示出来,但功能一直都在实现。
这样写的一个好处就是,我们只需要写好一个module文件,在仿真测试,以及下板验证的时候都能够很快、很简单地实现,而不用大量复制代码,在两头切换。
补充
在testbench文件我们对他进行声明了,当然了,因为我们仿真的时候需要这个变量来看看,初步确认他的功能是好的,实际下板我们并不需要把秒、分、时都输出出来(设想一个寄存器变量占8位,3个就是24位,并没有这么多引脚给你占用)。当然如果你有意为之,你也可以如下设置这三个变量,那么你便需要设置相关引脚并进行下一步。
此时的Top.v文件
module TOP( output sec, output min, output hour, ………… 同上 ); wire [7:0] sec; wire [7:0] min; wire [7:0] hour; //***********差分时钟50MHz ***************************************** 同上 //***********数字时钟**************************************** 同上 //时钟模块初始化 同上 //PLL分频的代码 同上 endmodule
不声明,但功能实现时候,引脚的截图:
不声明,功能也实现时候,引脚的截图:
Testbench也可以理解为一种顶层文件
);以上。
如果你觉得这篇文章对你有帮助,请为我点个赞谢谢
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。