序列检测器
功能描述:
序列检测器就是将一个指定序列从数字码流中识别出来。本例中将设计一个“10010”序列的检测器。设X为数字码流的输入,Z为检测出标记输出,高电平表示发现指定的序列10010.考虑码流为110010010000100101....则,如表有:
用FSM实现
module seqdet ( input wire x, input wire clk, input wire rst, output wire z ); reg [2:0] state; localparam IDLE = 3'd0, A = 3'd1, B = 3'd2, C = 3'd3, D = 3'd4, E = 3'd5, F = 3'd6, G = 3'd7; assign z = (state == D && x==0)?1'b1:1'b0;//状态为D时又收到输入0,表明10010已经收到,输出为1 always @ (posedge clk,negedge rst) if(!rst) begin state <= IDLE; end else casex(state) IDLE: if(x==1) state <= A; //状态A记住第一位正确高电平1来过 else state <= IDLE; A: if(x==0) state <= B; //状态B记住第二位正确低电平0来过 else state <= A; B: if(x==0) state <= C; //状态C记住第三位正确低电平0来过 else state <= F; //输入高电平,不符合要求,F记住只有1位对过 C: if(x==1) state <= D; //状态D记住第四位正确高电平1来过 else state <= G; //输入低电平,不符合要求,G记住没有1为曾经对过 D: if(x==0) state <= E; //状态E记住第五位正确低电平0来过 else state <= A; E: if(x==0) state <= C; //用状态记住100正确来过 else state <= A; F: if(x==1) state <= A; else state <= B; G: if(x==1) state <= F; else state <= B; default: state <=IDLE; endcase endmodule
测试文件:
`timescale 1ns/1ns module seqdet_tb; localparam T =20; reg clk,rst; reg [23:0] data; wire z,x; assign x = data[23]; initial begin clk =0; rst =1; #2 rst =0; #30 rst =1; data =20'b1100_1001_0000_1001_0100; #(T*1000) $stop; end always #T clk = ~clk; always @ (posedge clk) #2 data = {data[22:0],data[23]}; seqdet U1 ( .x(x), .z(z), .clk(clk), .rst(rst) ); endmodule
用移位寄存器实现检测
module seqdet ( input wire x, input wire clk, input wire rst, output wire z, output reg [4:0] q ); //reg [4:0] q; assign z = (q == 5'b10010) ? 1'b1:1'b0; always @ (posedge clk,negedge rst) if(!rst) q <= 5'd0; else q <= {q[3:0],x}; endmodule
RTL级视图:
测试文件:
`timescale 1ns/1ns module seqdet_tb; localparam T =20; reg clk,rst; reg [23:0] data; wire z,x; wire [4:0] q; assign x = data[23]; initial begin clk =0; rst =1; #2 rst =0; #30 rst =1; data =20'b1100_1001_0000_1001_0100; #(T*1000) $stop; end always #T clk = ~clk; always @ (posedge clk) #2 data = {data[22:0],data[23]}; seqdet U1 ( .x(x), .z(z), .clk(clk), .q(q), .rst(rst) ); endmodule
仿真结果:
NOTE:
由于移位寄存器的赋值是在always块中,故而相对实际延迟了一个clk.
由上面的方针结果可知,输出z相对x晚了一个时钟周期,因为由于移位寄存器的赋值是在always块中,故而相对实际延迟了一个clk.
代码修改如下:
module seqdet ( input wire x, input wire clk, input wire rst, output wire z, output reg [4:0] q ); wire [4:0] q_next; assign q_next ={q[3:0],x}; assign z = (q_next== 5'b10010) ? 1'b1:1'b0; always @ (posedge clk,negedge rst) if(!rst) q <= 5'd0; else q <= q_next; endmodule
状态机修改:
module seqdet ( input wire x, input wire clk, input wire rst_n, output wire z ); //用verilog设计一个 10010 序列的检测器 reg [4:0] cs,ns; localparam [4:0] IDLE =5'd0, A =5'd1, B =5'd2, C =5'd3, D =5'd4, E =5'd5; //状态转移 always @ (posedge clk,negedge rst_n) if(!rst_n) cs <= IDLE; else cs <= ns; //组合逻辑,产生下一状态译码逻辑 always @ (*) begin ns = 5'bx; case(cs) IDLE: begin if(x==1) ns = A; else ns = IDLE; end A: begin if(x==0) ns = B; else ns = A; end B: begin if(x==0) ns = C; else ns = A; end C: begin if(x==1) ns = D; else ns = IDLE; end D: begin if(x==0) ns = E; else ns = A; end E: begin if(x==0) ns = C; else ns = A; end default: ns = IDLE; endcase end //状态寄存输出 /* always @ (posedge clk,negedge rst_n) begin if(!rst_n) z <= 1'b0; else begin case(ns) IDLE,A,B,C,D: z <= 1'b0; E : z <= 1'b1; default: z <= 1'b0; endcase end end */ //状态为D时又收到输入0,表明10010已经收到,输出为1 //assign z = (cs == D && x==0)?1'b1:1'b0; assign z = (ns==E); endmodule
路漫漫其修远兮,吾将上下而求索