三段式序列检测器的实现

 

reference :  http://www.cnblogs.com/yulongchen/archive/2013/02/13/2911046.html

 第一次用verilog上机建模,深刻体会到看书所掌握不到的体会。有时候看书无法细心观察到的东西,在敲实验代码的时候,或许能够体现出来。现将第一次的体会记录如下。

还是先将verilog代码写下

 1 /************************************************************************
 2 模块作者:灰色的鱼
 3 编写时间:13:52 2013/2/13
 4 模块功能:对串行输入的数据流进行检测。只要发现10010序列,就立即输出高电平。
 5 *************************************************************************/
 6 module seqdet(x,z,clk,rst_n);
 7 input clk,rst_n;
 8 input x;
 9 output z;
10 
11 reg z;
12 reg [2:0] pstate,nstate;
13 
14 parameter s1=3'd0,
15           s2=3'd1,
16           s3=3'd2,
17           s4=3'd3,
18           s5=3'd4,
19           s6=3'd5;
20         //* 使用热独码会更好些。        

   //第一段: 现态与次态转换  时序电路
21 always @(posedge clk or negedge rst_n)
22 begin
23     if(!rst_n)
24         pstate<=s1;
25     else
26         pstate<=nstate;
27 end
28

   //第二段:各种状态间的跳变。 组合电路
29 always @(pstate or x)
30 begin
31     case(pstate)
32         s1:
33             if(x==1)
34                 nstate=s2;
35             else
36                 nstate=s1;
37         s2:
38             nstate=x?s2:s3;
39         s3:
40             nstate=x?s2:s4;
41         s4:
42             nstate=x?s5:s1;
43         s5:
44             nstate=x?s2:s6;
45         s6:
46             nstate=x?s2:s4;
47         default:
48             nstate=s1;
49         endcase
50 end
51 
   //第三段:确定最终的状态
52 always @(pstate or x or rst_n)
53 begin
54     if(!rst_n==1)
55         z=1'b0;
56     else if(pstate==s5 && x==0)
57                 z=1'b1;
58          else
59             z=1'b0;
60 end
61 
62 endmodule
 

然后是testbench的代码:

 
 1 `timescale 1 ns/ 1 ps
 2 module seqdet_vlg_tst();
 3 
 4 reg clk;
 5 reg rst_n;
 6 wire x;                                             
 7 wire z;
 8 
 9 reg[19:0] data;
10 
11 assign x=data[19];
12                           
13 seqdet i1 (
14   
15     .clk(clk),
16     .rst_n(rst_n),
17     .x(x),
18     .z(z)
19 );
20 
21 initial                                                
22 begin                                                  
23                         
24     clk=0;
25     rst_n=0;
26     #500 rst_n=1; 
27     data=20'b1100_1001_0000_1001_0100;                                                                                              
28     #(100*100) $stop;                     
29 end 
30                                                    
31 always                                                                                                                                        
32     #50 clk=~clk; 
33                                                                                                                                                    
34 always @(posedge clk)
35 begin
36     #2 data={data[18:0],data[19]};
37 end 
38                                                
39 endmodule
 

用Modelsim进行综合前仿真,波形如下:(波形从上到下依次是clk,rst_n,x,z),x用于序列输入,z为检测到"10010"序列之后的输出。

从第一次实验中得到了几点经验,当然在以后的学习之中还需好好注意:

  1. 写testbench时候,`timescale输入问题,t前面的符号是键盘左上角‘1’左边那个键。而3'd0中的符号是引号那个键。这在看书的时候,不细心发现,还不容易察觉。重点是作者也没强调。      (区分单引号与反引号)
  2. 软件问题。话了挺长时间弄软件(指的是Modelsim),加上本身对自己写的代码不是特别自信,所以到了后来才发现软件有问题。按照网络上各种方法医治,都无果。没有办法了,自豪使用免费版本的Modelsim(不过,学习的话真的够用了)。
  3. Verilog的相关语法:

    A. 关于阻塞赋值与非阻塞赋值的用法区别。

    B. assign语句赋值号左边只能是wire类型(只在wire与reg型之中做讨论)。而always快语句中,被赋值的只能是reg类型。

  4.在Quartus II中进行中文注释时,往往不能直接输入。最好的方法是先在记事本里将注释写好,然后copy到Quartus II里去。

  5.对数字的写法不要忘记是<位数>’<进制><数字>。

 

扩展1:

reference : http://blog.csdn.net/hyhop150/article/details/51252052    (博客中有CPU的部分,写个不错)

序列检测器用于检测一组或多组由二进制码组成的脉冲序列信号,当序列检测器连续收到一组串行二进制码后,如果这组码与检测器中的预设值相同则输出1,否则输出0

问题描述:

状态转换图:

在这里需要注意的是,Mealy状态机和Moore状态机不同,他的输出是由输入与当前状态共同决定的因此有:

实现代码如下:

在设计时值得注意的是,因为目标序列中都存在子序列“10"因此可以有效序列中肯定存在子序列”10“所以可以通过控制状态机的输入输出情况来进行序列检测

测试文件:

initial begin  
        // Initialize Inputs  
        clk = 0;        check = 0;  
        num = 1;        #20;        num = 1;        #20;  
        num = 1;        #20;        num = 1;        #20;  
        num = 0;        #20;        num = 1;        #20;  
        num = 1;        #20;        num = 0;        #20;  
        num = 1;        #20;        num = 1;        #20;  
        num = 0;        #20;        num = 1;        #20;  
        num = 1;        #20;        num = 1;        #20;  
        num = 1;        #20;        num = 1;        #20;  
        num = 0;        #20;        num = 1;        #20;          
    end  
      
   always #1 clk = ~clk;  
    always #10 check = ~check;  

 

仿真结果:
 

 

 

 

扩展2:

reference :   http://www.cnblogs.com/CodeWorkerLiMing/archive/2012/05/22/2513869.html

在数字电路中,FSM(有限状态机)的使用还是比较普遍的,下面举一个序列检测器。

verilog(Detector110.v)代码如下:

 
/*
finite state machine----FSM
implemente file
有限状态机的实例
2012/05/22
Iverilog + GTKWave in windows XP sp3
*/

`timescale 1ns/100ps

module Detector110(input a, clk, reset, output w);
    parameter [1:0] s0 = 2'b00,
                    s1 = 2'b01,
                    s2 = 2'b10,
                    s3 = 2'b11;
    reg [1:0] current;
    //两段式:
    always @ (posedge clk)
        begin
            if(reset)
                current = s0;
            else
                case (current)
                s0:    if(a) current <= s1;
                    else current <= s0;
                s1: if(a) current <= s2;
                    else current <= s0;
                s2: if(a) current <= s2;
                    else current <= s3;
                s3: if(a) current <= s1;
                    else current <= s0;
            endcase
        end
        
        assign w = (current == s3) ? 1 : 0;
endmodule
 

再写一个testbench文件test_tb.v:

/*
finite state machine----FSM
testbench file for Detector110.v
有限状态机的实例
2012/05/22
Iverilog + GTKWave in windows XP sp3
*/

`timescale 1ns/100ps

module test;
    reg aa, clk, rst;
    wire ww;
    Detector110 UUT(aa, clk, rst, ww);
    
    initial
        begin
            aa = 0;
            clk = 0;
            rst = 1;
        end
    
    initial
        repeat (44) #7 clk = ~clk;
    
    initial
        repeat (15) #23 aa = ~aa;
    
    initial
        begin
            #31 rst = 1;
            #23 rst = 0;
        end
    
    always @ (ww)
        if(ww == 1)
            $display("A 1 was detector on w at time = %t,",$time);
            
    initial
        begin            
            $dumpfile("test.vcd");
            $dumpvars;
        end
endmodule
 

写一个批处理文件go.bat:

ECHO OFF
ECHO *********************************
ECHO *        Batch file
ECHO *********************************
ECHO *
ECHO ON
iverilog -o test Detector110.v test_tb.v
vvp -n test -lxt2
cp test.vcd test.lxt
gtkwave test.lxt
 

 执行之后:

E:\lm\verilog\iverilog\MooreState>go.bat

E:\lm\verilog\iverilog\MooreState>ECHO OFF
*********************************
*        Batch file
*********************************
*

E:\lm\verilog\iverilog\MooreState>iverilog -o test Detector110.v test_tb.v

E:\lm\verilog\iverilog\MooreState>vvp -n test -lxt2
LXT2 info: dumpfile test.vcd opened for output.
A 1 was detector on w at time =                 1050,
A 1 was detector on w at time =                 1470,
A 1 was detector on w at time =                 1890,
A 1 was detector on w at time =                 2870,

E:\lm\verilog\iverilog\MooreState>cp test.vcd test.lxt

E:\lm\verilog\iverilog\MooreState>gtkwave test.lxt

之后启动了GTKWave,截图如下:

 哈哈!不错诶!

这里解释一下testbench里的

`timescale 1ns/100ps

这里表明仿真的时间单位为ns,而仿真的时间精度为100ps,即0.1ns,这意味着可以在程序中使用小数的时间值为0.1ns。

 

扩展3:

reference: http://home.eeworld.com.cn/my/space-uid-611861-blogid-255101.html

 

我学习verilog是参考的北航夏宇闻老师《verilog数字系统设计教程》。对其中第15章,例15.1的序列检测器的8个状态表示不能理解到位,自己结合之前学习的数字电路教程,写出了5个状态的状态机实现。现在就我设计的5个状态的状态机做一些说明。希望大家能告诉我夏老师教材中的这8个状态的设计原因以及你的想法。

讨论来自--较复杂时序逻辑实践第15章--夏宇闻

序列检测器10010

在时序逻辑的设计中,我初学常没有直接写出状态转移图的直觉,这次设计,用到了数字电路基础中,设计状态转移表,再去设计状态转移图。这样在设计中,只用到了5个状态,分别是IDLE, S1,S2,S3,S4这四个状态。其中,在化简时,将S5和S2化简成为一个状态S2。

其中各个状态

X:输入

Z:输出(当输入序列为10010时,输出为1,否则为0)

S0(IDLE):没有收到一个有效的数

S1:收到1个有效的数

S2:收到2个有效的数

S3:收到3个有效的数

S4:收到4个有效的数

S5:收到5个有效的数

根据要求,我设计一个mealy型的状态转移图,然后想根据状态转移表,设计有限状态机。

我看到S5和S2状态重叠,则化简状态转移图,将S5和S2合并为S2           

 

新图为

则状态转移图为

在设计有限状态机时候,特别注意抓三点:输入,输出,确立状态(当前状态,下一状态)

这里用独热码来分配状态

IDLE = 5'b00001,

S1  = 5'b00010,

S2  = 5'b00100,

S3  = 5'b01000,

S4  = 5'b10000;

代码如下

//------------------SEQDET.v----------------------------------------

module SEQDET(State,Z,X,Clk,Rst_n

    );

//-------------定义输入输出-----------------

input X,Clk,Rst_n;

output Z;

output [4:0] State;

//------------定义内部寄存器----------------

reg [4:0] State;

wire Z;

//-------------确定状态变量-----------------

parameter IDLE = 5'b00001,

 S1   = 5'b00010,

 S2   = 5'b00100,

          S3   = 5'b01000,

              S4   = 5'b10000;

//---------------实现功能-------------------

assign Z=((State==S4)&&(X==0));//如果第四位正确而且下一位输入变量X是0

always@(posedge Clk)

if(!Rst_n)

begin

State <= IDLE;

end

else

case(State)

IDLE:

if(X==1)

State <= S1;

else

State <= IDLE;

S1:

if(X==0)

State <= S2;

else

State <= S1;

S2:

if(X==0)

State <= S3;

else

State <= S1;

S3:

if(X==1)

State <= S4;

else

State <= IDLE;

S4:

if(X==0)

State <= S2;

else

State <= S1;

default:

State <= IDLE;

endcase

endmodule


 


//--------------------------------代码如下------------------------------------------------------------
`timescale 1ns / 1ps
module SEQDET(State,Z,X,Clk,Rst_n
    );
//-------------定义输入输出-----------------
input X,Clk,Rst_n;
output Z;
output [4:0] State;
//------------定义内部寄存器----------------
reg [4:0] State;
wire Z;
//-------------确定状态变量-----------------
parameter IDLE = 5'b00001,
 S1   = 5'b00010,
 S2   = 5'b00100,
          S3   = 5'b01000,
              S4   = 5'b10000;
//---------------实现功能-------------------

assign Z=((State==S4)&&(X==0));//如果第四位正确而且下一位输入变量X是0

always@(posedge Clk)
if(!Rst_n)
begin
State <= IDLE;
end
else
case(State)
IDLE:
if(X==1)
State <= S1;
else
State <= IDLE;
S1:
if(X==0)
State <= S2;
else
State <= S1;
S2:
if(X==0)
State <= S3;
else
State <= S1;
S3:
if(X==1)
State <= S4;
else
State <= IDLE;
S4:
if(X==0)
State <= S2;
else
State <= S1;
default:
State <= IDLE;
endcase

endmodule


 

//----------------------------------------------测试文件--------------------------------------------
`timescale 1ns / 1ps
`define CLKCYCLE 50
module TEST_SEQDET;

// Inputs

reg Clk;
reg Rst_n;
reg [23:0] data;

// Outputs
wire [4:0] State;
wire Z;
wire X;
assign X=data[23];
// Instantiate the Unit Under Test (UUT)
SEQDET UTEST_SEQDET (
.State(State), 
.Z(Z), 
.X(X), 
.Clk(Clk), 
.Rst_n(Rst_n)
);
always #`CLKCYCLE Clk=~Clk;
initial begin

Clk = 0;
Rst_n = 0;
#100;
#100 Rst_n = 1;
data= 20'b1100_1001_0000_1001_0100;//移位输出
#(`CLKCYCLE*1000)$stop;
end
   always@(posedge Clk)
#2 data={data[22:0],data[23]};
endmodule
 
//---------------------------------------仿真波形----------------------------------------------------

如图,观察波形,确实也实现了序列检测器的功能。
这里有些疑问:
教材的那八个状态是怎么观察分配出来的呢?尤其是F,G这两个状态,是怎么找出来的?
希望浏览过我贴子的FPGA爱好者能给予解答。
 
还有,因为喜欢电子,电子工程世界网站的页面规划还蛮有条有理的,我打算常驻这个论坛了。希望更多的电子爱好者能把一些平时遇到的问题还有经验总结下来。如果可以提供开源的项目代码那是更好了。希望咱们电子界能有好的学习气氛。
 
---------------------------------------------------此帖因为有论坛留言重新编辑---------------------------------------------------------
留言的主要问题是:序列检查器应该能检查出10010,但10010出现后应该有一个信号表明已经发现该序列,但当后面还有010出现时您设计的序列检查器能不能发现这也符合10010序列。虽然这两个相同序列有一部重合。即后面有效序列中的最前的10其实是属于上一个有效序列最后两个比特。您考虑了这个问题了吗?
 
答:我在之前的设计中注意到重叠序列的检测,在代码中用IDLE,S1-S4来避免了重叠序列不能检测出的问题。
 
鉴于ID为夏宇闻的用户在此帖下面留言我重新查看了我的这的这个讨论帖,并再次仿真了波形,用红色记号标出了序列检测器对于重叠序列可以正常检测的结果,现在将仿真结果再附图如下:

开始阶段

中间部分仿真

标记的是信号检测器的输出和输出,X为输入,Z为输出,经过代码设计和仿真观察,我的设计能够检查出重叠序列。

  • 3
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值