zoukankan      html  css  js  c++  java
  • 【接口时序】2、Verilog实现流水灯及与C语言的对比

    一、 软件平台与硬件平台

      软件平台:

      1、操作系统:Windows-8.1

      2、开发套件:ISE14.7

      3、仿真工具:ModelSim-10.4-SE

      硬件平台:

      1、FPGA型号:XC6SLX45-2CSG324

    二、 原理介绍

      我的开发板上有4个LED灯,原理图如下:

      

      

      由原理图可知仅当FPGA的对应管脚输入低电平时LED才会亮,流水灯的效果可以轮流让四个对应管脚输出低电平来产生。

    三、 目标任务

      编写四个LED流水的Verilog代码并用ModelSim进行仿真,仿真通过以后下载到开发板进行测试,要求开发板上每个LED亮的时间为1s。

    四、 设计思路与Verilog代码编写

      由于每个LED亮的时间为1s,所以首先很自然想到产生一个1s的时钟用来驱动后续逻辑,有了这个1s的时钟以后,就可以在这个1s时钟的节拍下对LED的输出进行以移位操作来产生流水灯的效果。

       1、1s时钟的分频逻辑

       由于主时钟是50MHz,周期为20ns,所以可以利用50MHz主时钟驱动一个计数器,当计数器的值每次到达24999999时,消耗的时间为25000000*20ns=0.5s,这时把分频器的输出反转,并把计数值清0,这样分频器的输出就会每隔0.5s翻转一次,产生了一个1s的时钟。

      Verilog代码如下:

    //////////////////////////////////////////////////////////////////
    // 功能:产生1s的时钟
    //////////////////////////////////////////////////////////////////
    always @(posedge I_clk or negedge I_rst_n)
    begin
        if(!I_rst_n)
            begin
                R_cnt_ls        <= 32'd0 ; 
                R_clk_ls_reg    <= 1'b1  ;
            end 
        else if(R_cnt_ls == 32'd24_999_999)
            begin
                R_cnt_ls        <= 32'd0          ;
                R_clk_ls_reg    <= ~R_clk_ls_reg  ;  
            end
        else
            R_cnt_ls <= R_cnt_ls + 1'b1 ;          
    end
    
    assign W_clk_ls = R_clk_ls_reg ;

      2、移位逻辑

      有了1s的时钟信号以后,就在这个1s时钟信号的驱动下对输出的LED寄存器进行移位操作产生流水效果。

      Verilog代码如下:

    //////////////////////////////////////////////////////////////////
    // 功能:对输出寄存器进行移位产生流水效果
    //////////////////////////////////////////////////////////////////
    always @(posedge W_clk_ls or negedge I_rst_n)
    begin
        if(!I_rst_n) 
            R_led_out_reg <= 4'b0001 ; 
        else if(R_led_out_reg == 4'b1000)
            R_led_out_reg <= 4'b0001 ;
        else    
            R_led_out_reg <= R_led_out_reg << 1 ;             
    end
    
    assign O_led_out = ~R_led_out_reg ;

    五、 ModelSim仿真

      写好逻辑以后,为了确定时序是正确的,最好写一个测试文件对功能进行仿真,为了加快仿真速度,修改分频逻辑计数器的计数值为24,然后编写测试文件,测试文件中激励产生的Verilog代码如下:

    initial begin
        // Initialize Inputs
        I_clk = 0;
        I_rst_n = 0;
    
        // Wait 100 ns for global reset to finish
        #100;
        I_rst_n = 1;
        
        // Add stimulus here
    
    end
    
    always #10 I_clk = ~I_clk ;

      仿真的时序图如下图所示:

    可以看到时序完全正确,接下来就是绑定管脚,生成bit文件下载到开发板测试了。

    六、 进一步思考——C语言流水灯与Verilog流水灯区别

      看完网上《Verilog那些事》系列博文以后,作者提出了一种“仿顺序操作”方法,其实以前自己写代码的时候无形之中一直在用这种思想,但是一直没有提炼出来,看完作者的介绍以后才发现确实是有那个“仿顺序”的味道。详细的博文请参考博客园博主akuei2的系列博文。这里我在总结一遍,给以后留个印象。

      C语言实现流水灯的大致代码框架如下:

        while(1)

        {

          1、让第1个LED亮,其他的灭;

          2、延时1s

          3、让第2个LED亮,其他的灭

          4、延时1s

          5、让第3个LED亮,其他的灭;

          6、延时1s

          7、让第4个LED亮,其他的灭

          8、延时1s

          }

      在while(1)里面代码是一行一行的执行,最后一行执行完毕以后在回到第一行重新开始新一轮的执行。就这样产生了流水的效果。

      看到这里,有人应该突然明白了吧,这不正好就是Verilog中的一个状态机么。对应的Verilog代码也可以写出来了 

      always @(posedge I_clk)

      begin

             case(R_state)

                    第1个状态:让第1个LED亮,其他的灭,下一状态是第2个状态;

                    第2个状态:延时1s,下一状态是第3个状态;

                    第3个状态:让第2个LED亮,其他的灭,下一状态是第4个状态;

                    第4个状态:延时1s,下一状态是第5个状态;

                    第5个状态:让第3个LED亮,其他的灭,下一状态是第6个状态;

                    第6个状态:延时1s,下一状态是第7个状态;

                    第7个状态:让第4个LED亮,其他的灭,下一状态是第8个状态;

                    第8个状态:延时1s,下一状态是第1个状态;

                    default          : ;

             endcase

      end

      具体的代码如下:

    //////////////////////////////////////////////////////////////////
    // 功能:“仿顺序操作”
    //////////////////////////////////////////////////////////////////
    always @(posedge I_clk or negedge I_rst_n)
    begin
        if(!I_rst_n)
            begin
                R_state  <= 3'b000 ; 
                R_cnt_ls <= 32'd0  ;
            end
        else
            begin    
                case(R_state)
                    C_S0:
                        begin
                            R_led_out_reg <= 4'b0001 ;
                            R_state       <= C_S1    ;  
                        end
                    C_S1:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S2  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S2:
                        begin
                            R_led_out_reg <= 4'b0010 ;
                            R_state       <= C_S3    ;  
                        end
                    C_S3:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S4  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S4:
                        begin
                            R_led_out_reg <= 4'b0100 ;
                            R_state       <= C_S5    ;  
                        end
                    C_S5:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S6  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S6:
                        begin
                            R_led_out_reg <= 4'b1000 ;
                            R_state <= C_S7 ;  
                        end
                    C_S7:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S0  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end 
                    default: R_state <= 3'b000 ;                                                                               
                endcase 
            end                  
    end
    
    assign O_led_out = ~R_led_out_reg ;

      时序图如下图:

      时序图仍然正确,实现了流水灯的效果

    七、 总结

      1、所谓的“仿顺序操作”实际上就是一个状态机,通过状态的跳变实现“顺序执行”的效果。这种思想在后面写接口时序的时候还是挺管用的,今后可以多多琢磨琢磨。

      2、 C语言的while(1)和Verilog语言的always @(posedge I_clk)有类似的地方,只要CPU的时钟存在,它们就一直执行下去。书上都说C语言是一种串行语言,Verilog是一种并行语言,实际上这里也能有体会:C语言里只能有1个while(1)语句,进入while(1)以后CPU就出不来了,而Verilog中可以有多个always @(posedge I_clk)语句,并且每个always @(posedge I_clk)同时运行的,这就是两种语言最大的区别吧。

    八、 附录

      1、分频1s产生流水灯的完整代码

    module led_work_top
    (
        input           I_clk       ,
        input           I_rst_n     ,
        output  [3:0]   O_led_out
    );
    
    reg  [31:0]  R_cnt_ls      ;
    wire         W_clk_ls      ;
    reg          R_clk_ls_reg  ;
    reg  [3:0]   R_led_out_reg ;
    
    //////////////////////////////////////////////////////////////////
    // 功能:产生1s的时钟
    //////////////////////////////////////////////////////////////////
    always @(posedge I_clk or negedge I_rst_n)
    begin
        if(!I_rst_n)
            begin
                R_cnt_ls        <= 32'd0 ; 
                R_clk_ls_reg    <= 1'b1  ;
            end 
        else if(R_cnt_ls == 32'd24_999_999)
            begin
                R_cnt_ls        <= 32'd0          ;
                R_clk_ls_reg    <= ~R_clk_ls_reg  ;  
            end
        else
            R_cnt_ls <= R_cnt_ls + 1'b1 ;          
    end
    
    assign W_clk_ls = R_clk_ls_reg ;
    
    //////////////////////////////////////////////////////////////////
    // 功能:对输出寄存器进行移位产生流水效果
    //////////////////////////////////////////////////////////////////
    always @(posedge W_clk_ls or negedge I_rst_n)
    begin
        if(!I_rst_n) 
            R_led_out_reg <= 4'b0001 ; 
        else if(R_led_out_reg == 4'b1000)
            R_led_out_reg <= 4'b0001 ;
        else    
            R_led_out_reg <= R_led_out_reg << 1 ;             
    end
    
    assign O_led_out = ~R_led_out_reg ;
    
    endmodule

      2、 “仿顺序操作”产生流水灯完整代码

    module led_work_top
    (
        input           I_clk         ,
        input           I_rst_n       ,
        output  [3:0]   O_led_out     
    );                                
                                      
    reg  [31:0]  R_cnt_ls             ;
    reg  [3:0]   R_led_out_reg        ;
    reg  [2:0]   R_state              ;
    
    parameter    C_CNT_1S =   32'd49_999_999  ;          
    
    parameter    C_S0     =   3'b000  ,
                 C_S1     =   3'b001  ,
                 C_S2     =   3'b010  ,
                 C_S3     =   3'b011  ,
                 C_S4     =   3'b100  ,
                 C_S5     =   3'b101  ,
                 C_S6     =   3'b110  ,
                 C_S7     =   3'b111  ;
    
    //////////////////////////////////////////////////////////////////
    // 功能:仿顺序操作
    //////////////////////////////////////////////////////////////////
    always @(posedge I_clk or negedge I_rst_n)
    begin
        if(!I_rst_n)
            begin
                R_state  <= 3'b000 ; 
                R_cnt_ls <= 32'd0  ;
            end
        else
            begin    
                case(R_state)
                    C_S0:
                        begin
                            R_led_out_reg <= 4'b0001 ;
                            R_state       <= C_S1    ;  
                        end
                    C_S1:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S2  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S2:
                        begin
                            R_led_out_reg <= 4'b0010 ;
                            R_state       <= C_S3    ;  
                        end
                    C_S3:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S4  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S4:
                        begin
                            R_led_out_reg <= 4'b0100 ;
                            R_state       <= C_S5    ;  
                        end
                    C_S5:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S6  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end
                    C_S6:
                        begin
                            R_led_out_reg <= 4'b1000 ;
                            R_state <= C_S7 ;  
                        end
                    C_S7:
                        begin
                            if(R_cnt_ls == C_CNT_1S)
                                begin
                                    R_cnt_ls <= 32'd0 ;
                                    R_state  <= C_S0  ;
                                end
                            else
                                R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                        end 
                    default: R_state <= 3'b000 ;                                                                               
                endcase 
            end                  
    end
    
    assign O_led_out = ~R_led_out_reg ;
    
    endmodule

      3、测试记录文件完整代码

    module tb_led_work_top;
    
        // Inputs
        reg I_clk;
        reg I_rst_n;
    
        // Outputs
        wire [3:0] O_led_out;
    
        // Instantiate the Unit Under Test (UUT)
        led_work_top U_led_work_top (
            .I_clk(I_clk), 
            .I_rst_n(I_rst_n), 
            .O_led_out(O_led_out)
        );
    
        initial begin
            // Initialize Inputs
            I_clk = 0;
            I_rst_n = 0;
    
            // Wait 100 ns for global reset to finish
            #100;
            I_rst_n = 1;
            
            // Add stimulus here
    
        end
        
        always #5 I_clk = ~I_clk ;
          
    endmodule

    欢迎关注我的公众号:FPGA之禅

  • 相关阅读:
    1347: Last Digit (周期函数)
    1363: Count 101 (经典数位dp)
    1360: Good Serial Inc.(不知道是什么类型的题)
    C#winForm调用WebService的远程接口
    Web Service 的创建简单编码、发布和部署
    极致精简的webservice集成例子
    SVN使用教程总结
    C# int.Parse()与int.TryParse()
    C# 函数1 (函数的定义)
    C#中的委托和事件
  • 原文地址:https://www.cnblogs.com/liujinggang/p/9463589.html
Copyright © 2011-2022 走看看