牛客Verilog题目(4)——输入序列连续的序列检测(理解非阻塞和阻塞)

1.题目

该题出自牛客27题
牛客Verilog题目(4)——输入序列连续的序列检测(理解非阻塞和阻塞)_第1张图片
最一开始的程序:(在vivado仿真程序,为了方便观看,将中间变量也作为输出)

`timescale 1ns/1ns
module test2(
	input clk,
	input rst_n,
	input data,
	output reg match,
	output reg not_match,
	output reg [5:0] adata,
	output reg [2:0] num
	);

	always@(posedge clk or negedge rst_n) begin
	   if(!rst_n) begin
	       adata <= 6'b0; 
	       end
	    else begin
	       adata <= {adata[4:0],data}; 
	       end
	    end
	    // counter 6
	always@(posedge clk or negedge rst_n) begin
            if(!rst_n) begin
                num <= 3'b0; 
            end
            else if(num == 5 ) begin
                num <= 3'b0; 
            end
            else begin
                num <= num + 1; 
            end
        end
	// match test
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			match <= 0;
			not_match <= 0;
		end
		else if(num == 5 && adata == 6'b011100) begin
			match <= 1;
			not_match <= 0;
		end
		else if(num == 5 && adata != 6'b011100) begin
			match <= 0;
			not_match <= 1;
		end
		else begin
			match <= 0;
			not_match <= 0;
		end
	end
endmodule

激励函数为:(我习惯写成计数器的形式,而不是一个个值写入initial中)
其中初始值为100的目的也就是第一个值为000,这样的序列顺序就对上了。

`timescale 1ns / 1ns
module testbench(

    );
    reg clk=0,rst_n;
    reg a;
    reg Q2,Q1;
    wire match;
    wire not_match;
    wire [5:0] adata;
    wire [2:0] num;
    //wire [5:0] adata;
    always #5 clk = ~clk;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            a <= 0;
            Q1 <= 0;
            Q2 <= 1;          
            end
        else begin
            Q2 <= Q1;
            Q1 <= a;
            a <= ~Q2;
            end
            end
     initial begin
        rst_n=0;
        #1 rst_n = 1;
        #150 $finish;
        end  
            
    test2 dut(
        .clk(clk),
        .rst_n(rst_n),
        .data(a),
        .match(match),
        .not_match(not_match),
        .num(num),
        .adata(adata)
    );
endmodule

时序图为:
牛客Verilog题目(4)——输入序列连续的序列检测(理解非阻塞和阻塞)_第2张图片
很明显是不对的,因为判定时刻理应是正确时刻的下一个时刻,因此需要判定当下时刻。
所以将上一时刻判定序列(上面的代码)

		else if(num == 5 && adata == 6'b011100) begin
			match <= 1;
			not_match <= 0;
		end
		else if(num == 5 && adata != 6'b011100) begin
			match <= 0;
			not_match <= 1;
		end

改为当下时刻判定:

		else if(num == 5 && {adata[4:0],data} == 6'b011100) begin
			match <= 1;
			not_match <= 0;
		end
		else if(num == 5 && {adata[4:0],data} != 6'b011100) begin
			match <= 0;
			not_match <= 1;
		end

还有一种改发,就是让adata立马等于{adata[4:0],data}。这样下面就不需要改,将

	always@(posedge clk or negedge rst_n) begin
	   if(!rst_n) begin
	       adata <= 6'b0; 
	       end
	    else begin
	       adata <= {adata[4:0],data}; 
	       end
	    end

中的非阻塞赋值变成阻塞赋值:

	always@(posedge clk or negedge rst_n) begin
	   if(!rst_n) begin
	       adata = 6'b0; 
	       end
	    else begin
	       adata = {adata[4:0],data}; 
	       end
	    end

但是不建议这么使用:虽然牛客也可以成功通过测试例,但是不建议时序电路用阻塞赋值,因为容易产生竞争关系。
所以综上所述,可以这里一下阻塞赋值和非阻塞赋值:
非阻塞赋值操作右端操作数使用的是上一个时钟周期的旧值
而阻塞赋值右端操作数会立马赋值给当下值,而非阻塞会在下一个时序再用到这个值。这个思想有的像i++和++i的区别。
就比如:

always @ ( posedge clk ) begin
    a = b ;
end
 
always @ ( posedge clk ) begin
    b = a ;
end

会产生竞争。
但是:
always @ ( posedge clk ) begin
a <= b ;
end

always @ ( posedge clk ) begin
b <=a ;
end
就相当于a^(n+1) = b ^n
b^(n+1) = a ^n

你可能感兴趣的:(fpga开发)