题目:Create a set of counters suitable for use as a 12-hour clock (with am/pm indicator). Your counters are clocked by a fast-running clk, with a pulse on ena whenever your clock should increment (i.e., once per second).
reset resets the clock to 12:00 AM. pm is 0 for AM and 1 for PM. hh, mm, and ss are two BCD (Binary-Coded Decimal) digits each for hours (01-12), minutes (00-59), and seconds (00-59). Reset has higher priority than enable, and can occur even when not enabled.
The following timing diagram shows the rollover behaviour from 11:59:59 AM to 12:00:00 PM and the synchronous reset and enable behaviour.
写一下在做这道题的时候出现的一些问题和debug的过程。
问题1:计时器部分是很简单的多位计数器,只需要把握好进位和重置的条件就可以,分钟进位要受到秒针的影响,在59秒进位,在9分59秒进位;时钟进位要受到秒针和分钟影响,59分59秒进位,9时59分59秒进位,清零也是一样的条件。
问题2:时钟部分需要注意,测试用例默认reset后为12:00:00,所以初始化的时钟个位和十位分别为2和1,并且个位存在达到9进位清零的情况,也存在和十位一起达到12后归1的情况,要分开讨论。
问题3:pm跳变是其中的一个难点,pm不能响应时钟变化(不能always块加posedge clk也不能加*),也不能在reset后12:00:00显示为1(此时是午夜0时)。在这个地方真是卡了很久!最后采用了计数方式,也就是计数12点出现的次数,这里为了同步时钟,改成了11:59:59的计数,计数达到偶数次时,则将pm置为1。
(代码里看起来很像是偶数次置0?reset后为1,在零点后遇到第一个12点,变为2,此时判断问句结果为0,应该置0?但实际上,非阻塞赋值同时进行,hh_count的+1操作和取余操作是同时的,所以会置1)。
问题4:其实是ena的使用,因为ena使能实际在每一级都有体现,控制每一级进位和清零的,除了上一级数值还有ena的允许。
最终代码如下:(csdn为什么没有verilog的代码解析器真是令人失望)
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
reg [3:0] ss_one,ss_ten,mm_one,mm_ten,hh_one,hh_ten;
reg [5:0] in;
reg [3:0] hh_count;
always@(posedge clk)begin
if(reset||(ena&&ss_one==9))begin
ss_one<=0;end
else if(ena)begin
ss_one<=ss_one+1;end
else begin
ss_one<=ss_one;end
end
assign in[0]=(ena&&ss_one==9)?1:0;
always@(posedge clk)begin
if(reset||(ena&&ss_one==9&&ss_ten===5))begin
ss_ten<=0;end
else if(in[0])begin
ss_ten<=ss_ten+1;end
else begin
ss_ten<=ss_ten;end
end //second complete
assign in[1]=(ena&&ss_one==9&&ss_ten==5)?1:0;
always@(posedge clk)begin
if(reset||(ena&&mm_one==9&&ss_one==9&&ss_ten==5))begin
mm_one<=0;end
else if(in[1])begin
mm_one<=mm_one+1;end
else begin
mm_one<=mm_one;end
end
assign in[2]=(ena&&mm_one==9&&ss_one==9&&ss_ten==5)?1:0;
always@(posedge clk)begin
if(reset||(ena&&mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5))begin
mm_ten<=0;end
else if(in[2])begin
mm_ten<=mm_ten+1;end
else begin
mm_ten<=mm_ten;end
end
assign in[3]=(ena&&mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5)?1:0;
always@(posedge clk)begin
if(reset)begin
hh_one<=8'd2;end
else if(mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5&&hh_one==9)begin
hh_one<=0;end
else if(mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5&&hh_one==2&&hh_ten==1)begin
hh_one<=1;end
else if(in[3])begin
hh_one<=hh_one+1;end
else begin
hh_one<=hh_one;end
end
assign in[4]=(ena&&mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5&&hh_one==9)?1:0;
always@(posedge clk)begin
if(reset)begin
hh_ten<=8'd1;end
else if(mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5&&hh_one==2&&hh_ten==1)begin
hh_ten<=0;end
else if(in[4])begin
hh_ten<=hh_ten+1;end
else begin
hh_ten<=hh_ten;end
end
assign in[5]=(ena&&mm_one==9&&ss_one==9&&ss_ten==5&&mm_ten===5&&hh_one==1&&hh_ten==1)?1:0;
//判断11:59:59
always@(posedge clk)begin
if(reset)begin
hh_count<=1;end
else if(in[5])begin
hh_count<=hh_count+1;pm<=(hh_count%2)?1:0;end //偶数次pm置为1(非阻塞赋值同时进行)
else begin
hh_count<=hh_count;
end
end
assign hh={hh_ten,hh_one};
assign mm={mm_ten,mm_one};
assign ss={ss_ten,ss_one};
endmodule
收获颇多!