[Verilog]带使能端的级联BCD码计数器 - 以时钟计数器为例

问题描述

//模块声明
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
  • 级联计数器基础
  1. 每满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
  1. 每满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与分秒逻辑不同之处在于:

  1. hh的低四位不是0-9的循环, 在hh == 12时, 接收进位就要归零了, 可以通过hout提前判断缩小边界.
  2. 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值