这是HDLBits的电路->时序逻辑->计数器问题的压轴大题,如果没有从头开始刷起,直接刷12-hour clock对我这种初学者来说是非常吃力的。从这可以看出HDLBits网站出题人的用心良苦,他想告诉我们硬件初学者的是对于复杂的问题不要害怕,复杂的问题是由一个一个简单的小问题组成的,要有将复杂大问题(module)分解成一个一个简单小问题(sub-module)的能力。
对计数器这一章的8个问题从头到尾捋一遍,便于我们理解模块化设计思想。
Build a 4-bit binary counter that counts from 0 through 15, inclusive, with a period of 16. The reset input is synchronous, and should reset the counter to 0.重点是从0计数到15,并且是同步复位。
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
always@(posedge clk) begin
if(reset)
q<=0;
else
q<=q+1;
end
endmodule
Build a decade counter that counts from 0 through 9, inclusive, with a period of 10. The reset input is synchronous, and should reset the counter to 0.与上一个问题不同的是计数器是从0计数到9,是人们熟悉的10进制计数法。
module top_module(
input clk,
input reset,
output reg [3:0] q);
always @(posedge clk)
if (reset || q == 9) // Count to 10 requires rolling over 9->0 instead of the more natural 15->0
q <= 0;
else
q <= q+1;
endmodule
Make a decade counter that counts 1 through 10, inclusive. The reset input is synchronous, and should reset the counter to 1.与上一个不同的是从1计数到10,更符合常理的计数。
module top_module (
input clk,
input reset,
output [3:0] q);
always@(posedge clk) begin
if(reset | q == 10)
q<=1;
else
q<=q+1;
end
endmodule
Build a decade counter that counts from 0 through 9, inclusive, with a period of 10. The reset input is synchronous, and should reset the counter to 0. We want to be able to pause the counter rather than always incrementing every clock cycle, so the slowena input indicates when the counter should increment.个人任务这个例题是这几个例题中最重要的,也充分展现了使能信号的妙用,学校里教的使我们感觉使能信号是一个很鸡肋的信号,这个例题以及后面的例题会告诉我们使能信号是整个计数时钟的连接枢纽,是整个时钟系统最重要的信号。使用使能信号可以改变计数的快慢,这点与时针分针秒针的计数快慢相联系。因为我们不可能去生成不同频率的时钟信号来实现计数的快慢——这样就不是同步电路了。使能信号就是控制信号,而控制信号才是电路的核心信号。
module top_module (
input clk,
input slowena,
input reset,
output [3:0] q);
always@(posedge clk) begin
if(reset)
q<=0;
else if(slowena)
if(q == 9) q<=0;
else
q<=q+1;
else q<=q;
end
endmodule
The c_enable, c_load, and c_d outputs are the signals that go to the internal counter's enable, load, and d inputs, respectively. Their purpose is to allow these signals to be checked for correctness.该例题为ece241题,主要引入模块化设计思想和三个check信号:使能、装载(包含reset的功能)、和提示一轮计数完成的信号。我在实现Count Clock的时候并没有使用load和d去验证计数的正确性,这是可以改进的一个点。
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
); //
assign c_enable = enable;
assign c_load = (reset|(Q==4'd12&enable))?1'b1:1'b0;
assign c_d = c_load?4'd1:Q;
count4 the_counter (clk, c_enable, c_load, c_d, Q);
endmodule
module count4(
input clk,
input enable,
input load,
input [3:0] d,
output reg [3:0] Q
);
将1000HZ通过三个级联的十计数器生成1HZ的信号,这个例题其实是很关键的,很类似Count Clock例题。完成该例题的关键就在于级联的enable信号的设置。
module top_module (
input clk,
input reset,
output OneHertz,
output [2:0] c_enable
); //
reg [3:0] Q0,Q1,Q2;
assign c_enable[0] = 1'b1;
assign c_enable[1] = Q0 == 4'd9;
assign c_enable[2] = Q1 == 4'd9 && Q0 == 4'd9;
assign OneHertz = Q2 == 4'd9 && Q1 == 4'd9 && Q0 == 4'd9;
bcdcount counter0 (clk, reset, c_enable[0],Q0);
bcdcount counter1 (clk, reset, c_enable[1],Q1);
bcdcount counter2 (clk, reset, c_enable[2],Q2);
endmodule
Build a 4-digit BCD (binary-coded decimal) counter. Each decimal digit is encoded using 4 bits: q[3:0] is the ones digit, q[7:4] is the tens digit, etc. For digits [3:1], also output an enable signal indicating when each of the upper three digits should be incremented.该例题主要展示如何用十进制显示数字,为Count Clock铺垫。主要还是enable信号发挥了关键性的作用。
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:1] ena,
output [15:0] q);
assign ena[1] = q[3:0] == 9;
assign ena[2] = q[3:0] == 9 && q[7:4] == 9;
assign ena[3] = q[3:0] == 9 && q[7:4] == 9 && q[11:8] == 9;
decade_counters d1(clk,1,reset,q[3:0]);
decade_counters d2(clk,ena[1],reset,q[7:4]);
decade_counters d3(clk,ena[2],reset,q[11:8]);
decade_counters d4(clk,ena[3],reset,q[15:12]);
endmodule
module decade_counters (
input clk,
input slowena,
input reset,
output [3:0] q);
always@(posedge clk) begin
if(reset)
q<=0;
else if(slowena)
if(q == 9) q<=0;
else
q<=q+1;
else q<=q;
end
endmodule
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.
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
wire ena_m,ena_h;
reg pm_reg = 0;
assign pm = pm_reg;
assign ena_m = (ss[3:0] == 4'd9 && ss[7:4] == 4'd5);
assign ena_h = (mm[3:0] == 4'd9 && mm[7:4] == 4'd5) && (ss[3:0] == 4'd9 && ss[7:4] == 4'd5);
always@(posedge clk) begin
if((hh[3:0] == 1 && hh[7:4] == 1)&&(mm[3:0] == 9 && mm[7:4] == 5) && (ss[3:0] == 9 && ss[7:4] == 5))
pm_reg = ~pm_reg;
else
pm_reg = pm_reg;
end
ms_counters sc(clk,ena,reset,ss);
ms_counters mc(clk,ena_m,reset,mm);
cnt_counters_h hc(clk,ena_h,reset,hh);
endmodule
module ms_counters (
input clk,
input ena,
input reset,
output [7:0] q);
wire ena_g;
assign ena_g = q[3:0] == 4'd9 & ena;
cnt_counters d1(clk,ena,reset,4'd9,q[3:0]);
cnt_counters d2(clk,ena_g,reset,4'd5,q[7:4]);
endmodule
module cnt_counters (
input clk,
input ena,
input reset,
input [3:0] cnt,
output [3:0] q);
reg [3:0] q_reg;
assign q = q_reg;
always@(posedge clk) begin
if(reset)
q_reg<=4'd0;
else if(ena)
if(q_reg == cnt) q_reg<=4'd0;
else
q_reg<=q_reg+1;
else q_reg<=q_reg;
end
endmodule
module cnt_counters_h (
input clk,
input ena,
input reset,
output [7:0] q_out);
reg [7:0] q;
assign q_out = q;
always@(posedge clk) begin
if(reset) begin
q[3:0]<=4'd2;
q[7:4]<=4'd1;
end
else if(ena) begin
if(q[3:0] == 4'd2 && q[7:4] == 4'd1) begin
q[3:0]<=4'd1;
q[7:4]<=4'd0;
end
else if(q[3:0] == 4'd9 && q[7:4] == 4'd0) begin
q[3:0]<=4'd0;
q[7:4]<=4'd1;
end
else begin
q[3:0]<=q[3:0]+4'd1;
q[7:4]<=q[7:4];
end
end
else begin
q[3:0]<=q[3:0];
q[7:4]<=q[7:4];
end
end
endmodule
注意以上代码的39行,一开始是assign ena_g = q[3:0] == 4'd9;会导致如下的波形错误:
test bench代码:
`timescale 1ns / 1ns
module top_tb;
parameter PERIOD = 10;
// top_module Inputs
reg clk = 0 ;
reg reset = 1 ;
reg ena = 1 ;
// top_module Outputs
wire pm ;
wire [7:0] hh ;
wire [7:0] mm ;
wire [7:0] ss ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) reset = 0;
#(PERIOD*4) reset = 1;
#(PERIOD*2) reset = 0;
end
top_module u_top_module (
.clk ( clk ),
.reset ( reset ),
.ena ( ena ),
.pm ( pm ),
.hh ( hh [7:0] ),
.mm ( mm [7:0] ),
.ss ( ss [7:0] )
);
// initial
// begin
// $finish;
// end
endmodule
模块化为子模块单个解决的思想方法。