位计数电路概念:所谓位计数电路,就是对一个N位寄存器中1或者0的个数进行计数。
例如:要统计一个四位寄存器data_in[3:0]='b1111中1的个数,将该数据输入位计数电路,会输出结果4。
实现位计数电路步骤:
一、首先使用自然语言来描述实现的过程
1、要将data_in[3:0]每次向右移一位,取移位后的结果的最低位data_in[0];
2、判断data_in[0]是否为1,如果是1,计数器count加1,否则,保持不变。输出计数结果;
3、判断右移后的数据data_in[3:0]是否为0,如果为0,结束整个过程,反之循环步骤1~3。
这个描述完了之后,对位计数器的基本工作过程就已经明明白白。接下来要把上述语言转换为流程图,根据最近看《数字逻辑基础与verilog设计》所学知识,使用ASM来描述比用流程图描述要清晰一些,而且更容易画出实现电路的datapath,毕竟Verilog 是硬件描述语言,最高境界就是写代码之前先把datapath画出然后用verilog描述。因此,此处也使用ASM来进行描述上述实现过程。
二、ASM设计
ASM跟FSM的状态迁移图基本功能一致,都有状态转移以及状态转移条件。只不过ASM更详细一些,把在每个状态中所要实现的功能都尽量描述出来,而FSM只描述输入输出及状态转移条件。因此,使用ASM能更好的理解实现电路所需的基本块(寄存器、移位寄存器、存储器、计数器、多路选择器、译码器)
图1 算法状态机
- ASM如上图所示:首先在rst信号下进入状态S0,在S0对计数器count进行初始化,输入信号S用来控制这个位计数过程,当信号S=0时,在Load信号的控制下加载数据A,当S=1时,进入S1状态,在S1状态,对数据A进行右移,然后判断移位后的A是否为0,如果不等于0,判断A的最低位A[0]是否等于0,如果等于0,计数器保持不变,继续S1状态操作;如果A[0]=1,计数器count加1,然后继续S1状态。如果移位后的A=0,跳转到S2状态,计数完成信号Done拉高,然后判断S信号是否为0,如果为0,跳转到状态S0,否则保持在状态S2。
三、datapath设计
所谓datapath,即描述整个电路所需的基本块以及输入输出信号,基本块包括寄存器、移位寄存器、存储器、计数器、多路选择器、译码器等。本设计中,很明显只需要计数器和移位寄存器这两个数据块。对应的datapath如下所示。
图2 数据路径
描述完dataoath之后,下一步就是要使用verilog描述datapath。这就是verilog为什么称为硬件描述语言,因为其本质使用来描述硬件电路,而不是凭空写代码。在实用硬件描述语言进行描述之前,还需要确定ASM状态机中的每个状态的控制信号。
四、控制信号
根据图2数据路径图可知,控制移位寄存器的输入信号有load、en、data_in[n-1:0],输出信号有flag、data_bit0。计数器的输入信号有en_a,输出信号有count[
-1:0]。接下来只需要在图1的算法状态机中将这些信号正确赋值即可。
图3 控制信号
理清控制信号是如何产生以及如何发挥作用的之后,下一步就可以用verilog来描述上述datapath。
四、verilog描述datapath
顶层代码:
module Bit_count(input clk,
input rst,
input [3:0]data_in,
input start,状态机开始信号
input load,/移位寄存器加载信号
output reg[2:0]count,///计数结果
output reg done/计数完成标志信号
);
parameter [2:0] s0 = 'b001;
parameter [2:0] s1 = 'b010;
parameter [2:0] s2 = 'b100;
reg [2:0]next_state = 'd0;
reg [2:0]current_state = 'd0;
/
reg en_a;
wire flag;
reg en_b;
wire data_bit0;
次态向现态转移
always @(posedge clk or negedge rst)
if(!rst)
current_state <= s0;
else
current_state <= next_state;
/状态转移条件
always @(*) begin
case(current_state)
s0: begin
if(start)
next_state = s1;
else
next_state = s0;
end
s1: begin
if(flag)
next_state = s2;
else
next_state = s1;
end
s2: begin
if(!start)
next_state = s0;
else
next_state = s2;
end
default:
next_state = s0;
endcase
end
/输出结果
always@(*) begin
case(current_state)
s0: begin
en_a = 'd0;//计数器使能
en_b = 'd0;//移位寄存器使能
done = 'd0;
end
s1: begin
en_b = 'd1;
done = 'd0;
if(data_bit0)
en_a = 'd1;
else
en_a = 'd0;
end
s2: begin
done = 'd1;
en_a = 'd0;
en_b = 'd0;
end
default:begin
done = 'd0;
en_a = 'd0;
en_b = 'd0;
end
endcase
end
///计数器
always @(posedge clk or negedge rst)
if(!rst)
count <= 'd0;
else if(en_a)
count <= count + 'd1;
else
count <= count;
/移位寄存器
Right_shift Right_shift_4bit (
.clk(clk),
.data_in(data_in),
.load(load),
.en(en_b), //移位寄存器使能
.flag(flag),
.data_bit0(data_bit0)
);
endmodule
顶层中例化的移位寄存器代码:
module Right_shift(
input clk,
input [3:0]data_in,///待移位的数据
input load,///移位寄存器装载数据
input en,/移位寄存器的使能信号
output flag,/右移位完成标志信号
output reg data_bit0//右移位输出信号
);
reg [3:0]data = 'd0;
always @(posedge clk) begin
if(load) begin
data <= data_in;
end
else if(en) begin
data_bit0 <= data[0];
data <= {0,data[3:1]};
end
else begin
data_bit0 <= data_bit0;
data <= data;
end
end
assign flag = ~(|data);
endmodule
代码如上图表示,代码中控制信号都是由状态机产生,这就体现出ASM状态机的一个优点,使用算法状态机描述电路,可以使控制信号由状态机产生,使时序更加稳定。
五、行为仿真验证
首先建立仿真所需要的激励文件,代码如下所示:
module test;
// Inputs
reg clk;
reg rst;
reg [3:0] data_in;
reg start;
reg load;
// Outputs
wire [2:0] count;
wire done;
// Instantiate the Unit Under Test (UUT)
Bit_count uut (
.clk(clk),
.rst(rst),
.data_in(data_in),
.start(start),
.load(load),
.count(count),
.done(done)
);
initial begin
// Initialize Inputs
clk = 0;
rst = 'd0;
data_in = 0;
start = 0;
load = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
end
always #5 clk = ~clk;
reg[4:0]cnt = 'd0;
always @(posedge clk)
if(cnt=='d25)
cnt <= 'd25;
else
cnt <= cnt + 'd1;
生成复位信号
always @(posedge clk)
if(cnt <'d2)
rst <= 'd0;
else
rst <= 'd1;
生成data_in和load信号
always @(posedge clk)
if((cnt>='d2)&&(cnt<='d5)) begin
data_in <= 'b1111;
load <= 'd1;
end
else begin
data_in <= 'b0;
load <= 'd0;
end
/生成start信号
always@(posedge clk)
if((cnt>='d4)&&(cnt<='d20))
start <= 'd1;
else
start <= 'd0;
endmodule
运行Isim仿真波形如下:
图4 行为仿真波形
从波形图中可以看出,输入数据是data_in[3:0]='b1111;计数结果count在检测完成信号done拉高时给出正确的计数结果4。
总结:本设计整体可以实现位计数的基本功能,但是从仿真中可以看出一个问题,当done信号拉低时,count计数器并没有清零,不能为下一次序列检测做准备。
解决方法:只需要在S0状态增加计数器清零控制信号即可。