书接上文:https://blog.csdn.net/moon9999/article/details/96614319
目前我们已经进化到0.2形态了,简单来说verilog代码层面已经没有我们不会的了(哈哈( ̄▽ ̄)"),如果还有不会的那就是自己的问题哈。那么接下来我们需要建立模块思维。
简单来讲,一个芯片是由若干多层级大大小小的模块构成,每个模块根据输入给出信号输出完成自己的功能,模块间通过互连和调用进行配合最终实现整体的功能。说句实在话,模块划分是个技术活,我觉得只有到架构师层次还能拍下来,划分不好不仅会影响芯片功能实现,还可能会严重拖延项目进度,还有可能几个设计人员打起来。。。
当然作为刚入门的渣渣,我们不用考虑这么多,假设别人就是把一个功能点以模块的形式给了我们,接下来我们开工。
给芯片做个万年历记录下芯片工作了多少年了,责任重大,以后查询一款芯片什么时候开始工作的就靠你这个功能了!
输出四个信号分别是是秒分时日,当输入使能信号有效是进行计时,否则包持原有时间计数,当脉冲信号clr有效时,清空所有计数,当清空信号和使能信号同时为1时,清空计数重新计数。
信号
类型
位宽
描述
clk
input
1
时钟,经过外部分频处理为1s一拍即时钟频率为1Hz
rst_n
input
1
复位信号,低电平有效,要求异步复位
en
input
1
使能信号,信号为高电平时万年历工作,否则各项计数包持原值不变,同步维持信号
clr
input
1
清零信号,信号拉高时清零所有计数,同步单拍脉冲信号
sec
output
8
输出万年历 秒
min
output
8
输出万年历 分
hour
output
8
输出万年历 时
day
output
8
输出万年历 日
简单看下我们的输入。输入信号很简单,时钟复位和使能信号,使能有效时万年历工作,使能无效时万年历包持原值,当脉冲信号clr有效时,清空所有计数,当清空信号和使能信号同时为1时,重新计数;由于没有异步信号,代码中无需涉及异步处理。
输出信号分别是万年历的秒分时日,看起来这个芯片使用周期很难超过256天啊;sec显然应该时是0-59循环计数,min是0-59循环计数,hour是0-23,day就一直计满再翻转了。
有些人的思维是自定而下型,先由功能搭出大概的框架,一步步向下划分模块;有些人的思维是自下而上型,先写一个个基础功能模块,再把这个模块通过互连和调用组合在一起
完成顶层功能。我也不知道这两种思路哪一种是更好的思路,如果我选我选择看心情。
对于这个需求呢,我们不妨就先来写一个小模块,名字就叫计数到某一固定值就自动反转的模块。
经过一番努力,这是我最后完成的小模块。实现的思路很简单,模块实现计数器功能,传参为计满翻转值,en信号有效时进行计数功能,clr信号有效清空计数值,clr信号优先级更高。代码非常简单,甚至不需要做成模块。
module u_count #( parameter CNT_MAX = 256 ) ( input clk , input rst_n , input en , input clr , output reg [7:0] cnt ); always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt <= 8'b0; end else if(clr)begin cnt <= 8'b0; end else if(en)begin if(cnt == CNT_MAX -1)begin cnt <= 8'b0; end else begin cnt <= cnt + 8'b1; end end else begin cnt <= cnt; end end endmodule代码里有几点说明一下:
a.信号位宽不定义,那么就取默认值位宽为1,不是自动适配的(差点被气心肌梗);
b.输入输出可以像我这样定义,可以采用如下两种形式,不过我认为一定性定义好是最简单有效的;
c.parameter可以定义在传参列表中,也可以在代码中定义,之后的传参形式是没有区别的;
d.位宽定义由大往小写;
e.为什么传参CNT_MAX默认值为256呢,因为256-1是8bit计数器能够达到的最大值,也就是说在不传参的场景下,该技术器模块实现的就是计满翻转的普适性功能;
ps.另外的两种输入输出定义方式:
module u_count #( parameter CNT_MAX = 256 ) ( input clk , input rst_n , input en , input clr , output cnt ); //parameter CNT_MAX = 256; reg [7:0] cnt; endmodule module u_count #( parameter CNT_MAX = 256 ) ( clk , rst_n , en , clr , cnt ); input clk ; input rst_n ; input en ; input clr ; output cnt ; //parameter CNT_MAX = 256; reg [7:0] cnt ; endmodule有了底层模块,我们就可以搭建更大的功能更上层的模块了。显然底层模块是不满足当前定义的输入输出接口需求的,不过没关系,我们先做个顶层把刚刚的count模块例化进去。
module time_top( input clk , input rst_n , input en , input clr , output reg [7:0] sec , output reg [7:0] min , output reg [7:0] hour , output reg [7:0] day ); u_count #( .CNT_MAX(60) ) U_SEC( .clk (clk ) , .rst_n (rst_n ) , .en (en) , .clr (clr) , .cnt (sec) ); endmodule好的例化模块完成,现在我们的顶层模块已经可以实现sec信号的输出了,那么我们来考虑下如何实现min信号输出。
min信号的实现原理比较简单,只要在sec计数到59的时候自己加1就可以了,跟我们表的时针是一毛一样的。那么我们可以选择在u_count里面加一段always块,当然也可以选择在顶层互连完成。
如何互连呢?其实就可以理解为min的使能条件改变了,sec的使能条件是en信号,en有效时候就一直加一直加,而min的是能条件呢显然变成当en有效且sec==59。遵循这个思路,我们直接在顶层例化一份min模块就可以了。
module time_top( input clk , input rst_n , input en , input clr , output reg [7:0] sec , output reg [7:0] min , output reg [7:0] hour , output reg [7:0] day ); wire min_en; assign min_en = en & (sec==8'd59); u_count #( .CNT_MAX(60) ) U_SEC( .clk (clk ) , .rst_n (rst_n ) , .en (en) , .clr (clr) , .cnt (sec) ); u_count #( .CNT_MAX(60) ) U_MIN( .clk (clk ) , .rst_n (rst_n ) , .en (min_en) , .clr (clr) , .cnt (min) ); endmodule遵循这这个思路,我们就可以把hour和day信号都做出来了,同样的操作再例化两份就ok了,那么下面时我最后完成的代码,不过我把parameter单独在外面定义了一份,这是为了便于之后的修改与调整可以集中在一处,不必总是到下面例化处去修改。
module time_top( input clk , input rst_n , input en , input clr , output reg [7:0] sec , output reg [7:0] min , output reg [7:0] hour , output reg [7:0] day ); parameter SEC_MAX = 60; parameter MIN_MAX = 60; parameter HOU_MAX = 24; wire min_en; wire hou_en; wire day_en; assign min_en = en & (sec ==SEC_MAX-1); assign hou_en = en & (min ==MIN_MAX-1); assign day_en = en & (hour==HOU_MAX-1); u_count #( .CNT_MAX(SEC_MAX) ) U_SEC( .clk (clk ) , .rst_n (rst_n ) , .en (en) , .clr (clr) , .cnt (sec) ); u_count #( .CNT_MAX(MIN_MAX) ) U_MIN( .clk (clk ) , .rst_n (rst_n ) , .en (min_en) , .clr (clr) , .cnt (min) ); u_count #( .CNT_MAX(HOU_MAX) ) U_HOUR( .clk (clk ) , .rst_n (rst_n ) , .en (hou_en) , .clr (clr) , .cnt (hour) ); u_count U_DAY( .clk (clk ) , .rst_n (rst_n ) , .en (day_en) , .clr (clr) , .cnt (day) ); endmoduleps.这段代码没有经过综合和仿真,如果哪里有些语法问题标点问题也是很有可能的,真的写出错了请告知( ̄▽ ̄)"