问题描述
//模块声明
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
前置知识:
- BCD码: 将十进制数的每一位(0~9)按序, 用4位2进制数表示
D e c i m a l = [ 3 : 0 ] B i n a r y ( 78 ) 10 = ( 0111 , 1000 ) B C D Decimal = [3:0]\ Binary \\(78)_{10} = (0111, 1000)_{BCD} Decimal=[3:0] Binary(78)10=(0111,1000)BCD - 级联计数器基础
- 每满
16
进一位的基础2-digits hex_cnt
module hex_cnt(
input clk;
input reset;//同步复位
input enable;//计数使能, 优先级低于reset
output [7:0] hex;//十六进制输出
);
always @(posedge clk)begin
if(reset) hex <= 8'h00;
else hex <= hex + enable;
end
endmodule
- 每满
10
进位的200-dights bcd_cnt
对于不满足二进制进位的级联计数器, 不能使用+
的行为描述来简易实现, 需要对进位进行判断:
module bcd_cnt(
input clk;
input reset;
input enable;
output cout;
output [799:0] bcd;//200数位, 800比特位
);
//纯组合逻辑部分, 过程块使用阻塞赋值
reg [200:0] cin;
always @(*)begin
cin[0] = enable;
for (int i=0; i<200; i=i+1)begin
cin[i+1] = cin[i] && (bcd[4*i +: 4]==4'h9);
//此位已满, 且有进位, 才可向下一级进位
end
end
assign cout = cin[200];
//时序逻辑部分, 使用非阻塞赋值
always @(posedge clk) begin
if(reset) bcd <= 800'b0;
else begin
for(int i=0; i<200; i=i+1)begin
bcd[4*i +: 4] <= cin[i+1] ? 4'h0 : bcd[4*i +: 4]+cin[i];
//若要向下一级进位, 则清零, 否则加本级进位
end
end
end
endmodule
[处理过程]
首先实现纯组合逻辑部分, 也就是时, 分, 秒
的进位逻辑, 其中秒的进位就是ena
, 所以无需额外实现
wire min, hin, hout;
assign min = ena&&(ss==8'h59);
assign hin = min&&(mm==8'h59);
assign hout = hin&&(hh==8'h12);//小时数在12且本位接受进位后, 就要归1了
接着来到时序逻辑, 涉及到时钟的部分. 一个可行的规范是: 在每个always块中, 仅对一个变量赋值
首先实现ss
的进位逻辑, 由基础部分我们知道bcd
码应当按照 十进制数/4bits 分析
always @(posedge clk)begin
if(reset) ss <= 8'h0;
else begin
ss[3:0] <= (ss[3:0]==4'h9)&&ena ? 4'h0 : ss[3:0]+ena;
ss[7:4] <= min ? 4'h0 : ss[7:4]+((ss[3:0]==4'h9)&&ena);//注意二元运算符的左结合性, 这里需要括号!
end
end
mm
同理易得
always @(posedge clk) begin
if (reset) mm <= 8'h0;
else begin
mm[3:0] <= (mm[3:0]==4'h9)&&min ? 4'h0 : mm[3:0]+min;
mm[7:4] <= hin ? 4'h0 : mm[7:4]+((mm[3:0]==4'h9)&&min);
end
end
hh
与分秒逻辑不同之处在于:
hh
的低四位不是0-9
的循环, 在hh == 12
时, 接收进位就要归零了, 可以通过hout
提前判断缩小边界.hh
的高四位在进位后归一, 分析知:高四位仅0-1
变化, 可用异或实现取反逻辑.
always @(posedge clk) begin
if (reset) hh <= 8'h12;
else begin
if(hout) hh <= 8'h01;//注意hh产生进位是归1, 原题要求无0时
else begin
hh[3:0] <= (hh[3:0]==4'h9)&&hin ? 4'h0 : hh[3:0]+hin;
/*hh的高四位hh[4 +: 4]中, 仅从0-1之间变换,高三位恒为0, 使用异或对最低位取反.
取反逻辑为: 产生进位hout: 1->0; 接收hh[3:0]进位 (hh[3:0]==4'h9)&&hin : 0->1,
而hout是接收低四位进位的一种已被包括的情况, 故也即接收低四位进位 */
hh[4] <= hh[4] ^ ((hh[3:0]==4'h9)&&hin);
end
end
end
pm
也易实现, 只需在hh -> 12
时翻转即可
always @(posedge clk)begin
if (reset) pm <= 0;
else pm <= pm ^ ((hh=8'h11)&&hin);//pm在hh变为12时的情况下取反
end
[写在最后]
合: 不要使用 d
来表示bcd码, bcd码的每四位都是独立的, 如果8'h12
写成8'd12
, 会被编译器误认为8h0c
module cnt_clock(
input clk,
input reset,
input ena,
output reg pm,
output reg [7:0] hh,
output reg [7:0] mm,
output reg [7:0] ss);
wire min, hin, hout;
assign min = ena&&(ss==8'h59);
assign hin = min&&(mm==8'h59);
assign hout = hin&&(hh==8'h12);
always @(posedge clk)begin
if(reset) ss <= 8'h0;
else begin
ss[3:0] <= (ss[3:0]==4'h9)&&ena ? 4'h0 : ss[3:0]+ena;
ss[7:4] <= min ? 4'h0 : ss[7:4]+((ss[3:0]==4'h9)&&ena);
end
end
always @(posedge clk) begin
if (reset) mm <= 8'h0;
else begin
mm[3:0] <= (mm[3:0]==4'h9)&&min ? 4'h0 : mm[3:0]+min;
mm[7:4] <= hin ? 4'h0 : mm[7:4]+((mm[3:0]==4'h9)&&min);
end
end
always @(posedge clk) begin
if (reset) begin
hh <= 8'h12;
end
else begin
if(hout) hh <= 8'h01;
else begin
hh[3:0] <= (hh[3:0]==4'h9)&&hin ? 4'h0 : hh[3:0]+hin;
hh[4] <= hh[4] ^ ((hh[3:0]==4'h9)&&hin);
end
end
end
always @(posedge clk)begin
if (reset) pm <= 0;
else pm <= pm ^ ((hh==8'h11)&&hin);
end
endmodule