两种方式实现序列检测:三段式状态机、移位寄存器+比较器(含testbench激励代码)

问题引入:实现串行序列10110的检测,输出为高则表示检测到。

方法1:状态机实现


拓展:

一段式、二段式、三段式状态机;

参考链接:verilog状态机: 一段式,两段式,三段式_miaomiaofine_新浪博客 (sina.com.cn)

总结:状态机应包含三个部分:①状态转移部分(现态、次态转换顺序)、②判断状态转移条件(不同输入对状态转换的影响)、③输出状态(状态或输入对输出的影响)

一段式:一个always块——①、②、③都放在一起;

二段式:两个always块——一个always块放时序部分:①,另一个always块放组合部分:②、③;

注意:②中的组合部分的case敏感信号为current state;因为②、③在一起,二段式的状态机会有毛刺,需要拍频(加一个触发器)以消除毛刺;

三段式:三个always块——分别放①、②、③;

注意:三段式的输出通过触发器产生,不会有毛刺;③中的输出状态的case敏感信号为next_state,更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。

Moore型、Mealy型状态机的比较

Moore型:输出仅有当前状态有关,所以输出和状态写在一起。用作序列检测时会比Mealy型多一个状态;

Mealy型:输出不仅与当前状态有关,还与输入有关;


源代码:

module sequence_detect(
	//input
	clk,
	rst_n,
	d_in,
	
	//output
	d_out
	);
	
input clk;
input rst_n;
input d_in;
output reg d_out;

parameter IDLE = 3'd0;
parameter A = 3'd1;
parameter B = 3'd2;
parameter C = 3'd3;
parameter D = 3'd4;
parameter E = 3'd5;

reg [2:0] c_state,n_state;

//1、状态寄存器:描述对应当前状态的状态寄存器,非阻塞赋值
always@(posedge clk, negedge rst_n)
	begin
		if(!rst_n)
			c_state <= IDLE;
		else
			c_state <= n_state;
	end

//2、	描述下一状态的状态寄存器,阻塞赋值
always@(*)
	begin
		case(c_state)
			IDLE:
				if(d_in)	n_state = A;
				else n_state = IDLE;
			A:
				if(!d_in) n_state = B;
				else n_state = A;
			B:
				if(d_in) n_state = C;
				else n_state = A;
			C:
				if(d_in) n_state = D;
				else n_state = B;
			D:
				if(!d_in) n_state = E;
				else n_state = A;
			E:
				if(d_in) n_state = A;
				else n_state = IDLE;
			default:
				n_state = IDLE;
		endcase
	end

//3、描述输出,非阻塞赋值
always @(posedge clk, negedge rst_n)
	begin
		if(!rst_n)
			d_out <= 1'b0;
		else
			case(n_state)//对次态敏感
				IDLE:
					d_out <= 'b0;
				A:
					d_out <= 'b0;
				B:
					d_out <= 'b0;
				C:
					d_out <= 'b0;
				D:
					d_out <= 'b0;
				E:
					d_out <= 'b1;
				default:
					d_out <= 'b0;
			endcase
	end
	
endmodule

Testbench:

`timescale 1ns / 10ps
module sequence_detect_tb;
reg clk;
reg rst_n;
reg [17:0] data;
wire x_in;
wire z_out;
/***********************************************************/
initial
begin
    clk = 1'b0;
    rst_n = 1'b0;
    #100;
    rst_n = 1'b1;
end
/***********************************************************/
always #10 clk = ~clk;
/***********************************************************/
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        data <= 18'b11_0110_0100_0010_1101;
    else
        data <= {data[16:0],data[17]};
end
/***********************************************************/
assign x_in = data[17];
/***********************************************************/
sequence_detect sequence_detect_inst(
                                        //input
                                        .clk(clk),
                                        .rst_n(rst_n),
                                        .d_in(x_in),

                                        //ouput
                                        .d_out(z_out)
                                     );
/***********************************************************/
endmodule

仿真结果:


方法2:移位寄存器+比较器

先上电路图

实现思路:通过移位寄存器将输入的一位二进制数进行移位,再移位寄存器的输出值q与比较器中的检测序列进行比较,若相同则输出高电平

源代码:

//不同状态机检测序列10110
module seq_det_com(
	input clk,
	input rst_n,
	input din,
	output wire dout,
	output reg [4:0]q
);

	assign dout = (q == 5'b10110)?1'b1:1'b0;
	always @(posedge clk,negedge rst_n)
		begin
			if(!rst_n)
				q<=5'b0;
			else
				q<={q[3:0],din};
		end

endmodule

testbench:

`timescale 1ns/1ns
module seq_det_com_tb;
	
reg clk;
reg rst_n;
wire din;
reg [17:0] data;
wire dout;
/***********************************************************/
initial
begin
    clk = 1'b0;
    rst_n = 1'b0;
    #10;
    rst_n = 1'b1;
end
/***********************************************************/
always #10 clk = ~clk;
/***********************************************************/
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        data <= 18'b11_0110_0100_0010_1101;//待检测序列
    else
        data <= {data[16:0],data[17]};//并行转成串行,当然也可以串行输入
end
/***********************************************************/
assign din = data[17];
/***********************************************************/
seq_det_com my_seq_det_com(
                                        //input
                                        .clk(clk),
                                        .rst_n(rst_n),
                                        .din(din),

                                        //ouput
                                        .dout(dout)
                                     );
/***********************************************************/

	
endmodule

仿真结果:

由上面的仿真结果可知,输出dout相对din晚了一个时钟周期,因为由于移位寄存器的赋值是在always块中,故而相对实际延迟了一个clk.


总结:

  • 跟用状态机实现的区别在于,使用移位寄存器需要存储所有的码字,因此如果序列长度为N,则该方法需要消耗的寄存器就是N个。而使用状态机实现时,每个状态代表部分码字,如果使用十进制编码,则只需要使用log2(N)个寄存器即可编码所有状态,从寄存器资源的角度来看FSM实现起来代价较小
  • 此外,寄存器版本每来一个码元都要比较所有码字,因此需要消耗N个比较器,而FSM的的状态寄存器每一位在状态转移时都需要不同的译码逻辑,如果状态转移比较简单,组合逻辑可能会比移位寄存器少,状态转移复杂的化就不好说了。
  • 当然,移位寄存器的版本编码更加简洁明了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cheeky_man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值