天天看点

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

位计数电路概念:所谓位计数电路,就是对一个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能更好的理解实现电路所需的基本块(寄存器、移位寄存器、存储器、计数器、多路选择器、译码器)

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

图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如下所示。

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

图2 数据路径

 描述完dataoath之后,下一步就是要使用verilog描述datapath。这就是verilog为什么称为硬件描述语言,因为其本质使用来描述硬件电路,而不是凭空写代码。在实用硬件描述语言进行描述之前,还需要确定ASM状态机中的每个状态的控制信号。

四、控制信号

根据图2数据路径图可知,控制移位寄存器的输入信号有load、en、data_in[n-1:0],输出信号有flag、data_bit0。计数器的输入信号有en_a,输出信号有count[

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

-1:0]。接下来只需要在图1的算法状态机中将这些信号正确赋值即可。

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

图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仿真波形如下:

读书笔记:数字逻辑基础与verilog设计之数字系统设计流程01----------位计数电路设计

图4 行为仿真波形

 从波形图中可以看出,输入数据是data_in[3:0]='b1111;计数结果count在检测完成信号done拉高时给出正确的计数结果4。

总结:本设计整体可以实现位计数的基本功能,但是从仿真中可以看出一个问题,当done信号拉低时,count计数器并没有清零,不能为下一次序列检测做准备。

解决方法:只需要在S0状态增加计数器清零控制信号即可。

继续阅读