• (原创)基于FPGA的调光流水灯(Verilog,CPLD/FPGA)


    1.Abstract

        前几天做了一个呼吸灯,觉得确实挺有意思的;可惜的是只有一个灯管亮,板子上有四个灯,要是能让这些灯有序地亮起来,那应该更有趣味了!跟传统的一样,逻辑上做成一个流水灯的样式,这种带有PWM调光的吸引样式,真可谓是超级流水灯了。

        做这个是在已做好的呼吸灯的基础上进行添加功能的,整理好了也在随笔里边,这里就直接引用出来。

        基于Verilog的PWM呼吸灯:http://www.cnblogs.com/hechengfei/p/4106538.html

    2.Content

      2.1 总体设计

        PWM的原理在呼吸灯的那部分已经整理好了,这里就省去了原理分析一部分了,把做好的作为底层的一个小模块,主要关注顶层逻辑的设计了。

        要使灯顺序的流水起来,自然是一个时序逻辑,最佳的方法就是采用状态机,预先将各个状态分配好,然后让各个状态有序地切换,时序逻辑就清晰得被表达出来。板子上有4个灯,依次流水过去然后再回来,然后循环这样的逻辑;用图表示或许直观些。

    image FIG2.1 总体逻辑

        在途中,蓝色线表示向右流水,红色线表示向左流水。一次完整的流水执行下来,如绿色箭头指示;首先是LED0表演,完毕后,转至LED1表演,完毕后,再转至LED2表演,依次下去,直至回到LED0表演。

        根据上述的逻辑,将所有的逻辑状态分配出来;将总体逻辑平铺开,去掉多余的状态,就有8-2 = 6个状态(首尾的两个状态各重了一次)。分别用S0到S7八个状态表示(多余的两个状态直接转到S0),用状态表来说明一下。

    表2.1 总体状态转换表

    当前状态

    转换条件

    下一状态

    S0

    !RST 或者 !END0

    S0

    S0

    RST&END0

    S1

    S1

    !RST

    S0

    S1

    RST & (!END1)

    S1

    S1

    RST & END1

    S2

    S2

    !RST

    S0

    S2

    RST & (!END2)

    S2

    S2

    RST & END2

    S3

    S3

    !RST

    S0

    S3

    RST & (!END3)

    S3

    S3

    RST & END3

    S4

    S4

    !RST

    S0

    S4

    RST & (!END2)

    S4

    S4

    RST & END2

    S5

    S5

    !RST | END1

    S0

    S5

    RST & (!END1)

    S5

    S6

    *(Unconditional)

    S0

    S7

    *(Unconditional)

    S0

        用状态装换图表示可能更直观一些。

    image FIG2.2 总体状态转换图

        图中,绿色部分表示正常状态转换,红色部分表示控制信号强制转换。用状态转换图的方法虽然画得比较复杂,线比较多,但更容易理解和便于编写状态机。

        完成信号END的产生。在PWM呼吸灯中,它也是一个状态转换的实例,END信号的产生是由内部寄存器存储和操作的,用作子模块以后,最后的状态应该作为端口输出出来,以便顶层模块检测和顶层状态切换处理。顾只需要在原来的模块中添加一个END信号,在PWM一个周期输出完成以后,将此信号输出出来。

      2.2 顶层整合

        顶层的整合就是将所有模块连接起来,既可以采用Block块的方式,也可以用文本的方式。顶层块连接了控制模块和子状态控制块模块,各自逻辑描述如下。

        顶层块的描述:

    module breath(LED,CLK,RST);
    output [3:0] LED;
    
    input CLK;
    input RST;
    
    wire S_END0;
    wire S_END1;
    wire S_END2;
    wire S_END3;
    
    wire RST_LED0;
    wire RST_LED1;
    wire RST_LED2;
    wire RST_LED3;
    
    breath_state M0 (.RST_LED0(RST_LED0), .RST_LED1(RST_LED1), .RST_LED2(RST_LED2), .RST_LED3(RST_LED3),
                     .S_END0(S_END0), .S_END1(S_END1), .S_END2(S_END2), .S_END3(S_END3),
                     .CLK(CLK), .RST(RST));
    
    
    breath_led    M1 (.LED(LED[0]), .S_END(S_END0), .CLK(CLK), .RST(RST_LED0));
    breath_led    M2 (.LED(LED[1]), .S_END(S_END1), .CLK(CLK), .RST(RST_LED1));
    breath_led    M3 (.LED(LED[2]), .S_END(S_END2), .CLK(CLK), .RST(RST_LED2));
    breath_led    M4 (.LED(LED[3]), .S_END(S_END3), .CLK(CLK), .RST(RST_LED3));
    
    
    endmodule

        状态控制块的描述:

    module breath_state(RST_LED0, RST_LED1, RST_LED2, RST_LED3,
                        S_END0, S_END1, S_END2, S_END3,
                        CLK,RST);
    
    input S_END0;
    input S_END1;
    input S_END2;
    input S_END3;
    
    output reg RST_LED0 = 1'b0;
    output reg RST_LED1 = 1'b0;
    output reg RST_LED2 = 1'b0;
    output reg RST_LED3 = 1'b0;
    
    input CLK;
    input RST;
    
    parameter S0 = 3'd0,
              S1 = 3'd1,
              S2 = 3'd2,
              S3 = 3'd3,
              S4 = 3'd4,
              S5 = 3'd5,
              S6 = 3'd6,
              S7 = 3'd7;
    
    reg [2:0] state = 3'd0;
    
    always @(posedge CLK)
    begin
        if(!RST)
            begin
                state = S0;
            
                RST_LED0 = 1'b0; //熄灭所有灯
                RST_LED1 = 1'b0;
                RST_LED2 = 1'b0;
                RST_LED3 = 1'b0;
            end
        else
            begin
                case(state)
                S0:    begin
                        if(S_END0) begin state <= S1; end
                        else begin 
                                RST_LED0 <= 1'b1; // 仅LED0表演
                                RST_LED1 <= 1'b0;
                                RST_LED2 <= 1'b0;
                                RST_LED3 <= 1'b0;
                             end
                    end
                S1: begin
                        if(S_END1) begin state <= S2; end
                        else begin
                                RST_LED0 <= 1'b0;
                                RST_LED1 <= 1'b1;  // 仅LED1表演
                                RST_LED2 <= 1'b0;
                                RST_LED3 <= 1'b0;
                             end
                    end
                S2: begin
                        if(S_END2) begin state <= S3; end
                        else begin
                                RST_LED0 <= 1'b0;
                                RST_LED1 <= 1'b0;
                                RST_LED2 <= 1'b1; // 仅LED2表演
                                RST_LED3 <= 1'b0;
                             end
                    end
                S3: begin
                        if(S_END3) begin state <= S4; end
                        else begin
                                RST_LED0 <= 1'b0;
                                RST_LED1 <= 1'b0;
                                RST_LED2 <= 1'b0;
                                RST_LED3 <= 1'b1; // 仅LED3表演
                             end
                    end
                S4: begin
                        if(S_END2) begin state <= S5; end
                        else begin
                                RST_LED0 <= 1'b0;
                                RST_LED1 <= 1'b0;
                                RST_LED2 <= 1'b1; // 仅LED2表演
                                RST_LED3 <= 1'b0;
                             end
                    end
                S5: begin
                        if(S_END1) begin state <= S0; end
                        else begin
                                RST_LED0 <= 1'b0;
                                RST_LED1 <= 1'b1; // 仅LED1表演
                                RST_LED2 <= 1'b0;
                                RST_LED3 <= 1'b0;
                             end
                    end
                S6: begin
                        state <= S0; // 多余状态,无条件返回
                    end
                S7: begin
                        state <= S0; // 多余状态,无条件返回
                    end 
                endcase
            end
            
    
    end
    
    endmodule

      2.3 逻辑验证

        整理好各个模块以后,编译一下,看看生成出来的逻辑。

    image FIG2.3综合后的RTL视图网表

        顶层只做连接各个模块,那么在RTL视图中就是几个绿色的小方块儿表示了;排列的还是非常整齐的,对照连线,确定一下所有的线连接是正确的。再就是检查一下状态转换图。

    image FIG2.4 综合后的状态机视图

        对照视图下边的状态转换表,选中某根连线就可以在表格中直接高亮显示转换的初始状态,转换条件,下一状态等丰富信息。因为生成的状态机和预期的是一样的,所以就不再验证它的正确性了;配置好芯片引脚,将编程文件下载到实际电路板中,看看效果。

        因为博客中不能插入视频,所以将拍好的视频上传到专门的视频网站,生成一个链接,点击进去就可以观看了,也非常方便。

        实际效果:http://v.youku.com/v_show/id_XODMxOTY0MzQ0.html

    3.Conclusion

        对于时序逻辑,采用状态机的方法至关重要!逻辑的设计需要非常清晰,对于状态机的设计,可以先画出状态转换图,然后再进行编写文件;对于逻辑还不清晰的地方,需要认真在草稿纸上画画。

        关于编码的规范;对逻辑设计我也是刚刚起步,才学不到一年,更加关注的是逻辑综合后电路的正确性,所以没有对编码进行统一的规范;这方面我也会多多去学习,尽量将文件写的好一点,通用一点。

        写一点感受。逻辑设计和编写程序确实有很大的差别,也是各有所长吧;逻辑电路从大的范围来讲,分为组合逻辑和时序逻辑,有各自实现的方式,对于一个要求实现的逻辑,首先要做的就是把它完全分析清楚,将它们的波形都给画出来,然后再考虑哪部分使用组合逻辑,哪部分使用时序逻辑,最后就是将这些小块块连接起来做整合,设计采用的是自顶向下,而实现的方法可以是自下而上。相对逻辑设计,编程就显得不那么精准了,但是灵活性很高,特别适合做复杂的事情,而且总体看来它就是一个时序的,写好的指令是一步步往下走的,对硬件的理解要求不会太高,从而将注意力转移到如何实现上来。我觉得没有必要将它们严格的区分开来,也没有必要将它们混为一谈来对待,它们各自有优缺点,使用的时候只是转变一下逻辑而已。

    4.Reference

    [1] Verilog 数字系统设计教程(第二版) 夏宇闻

    [2] Verilog HDL 高级数字设计(第二版) Michael D.Ciletti

    5. Platform

    1).Quartus II Version 9.1 Build 222

    2).Microsoft Office Visio Professional 2003 SP3

    6.Attachment

    工程附录文件:http://i.cnblogs.com/Files.aspx

  • 相关阅读:
    关于git status
    JS的trim()方法
    js自定义方法名
    Autoit3 如何捕足控件
    AutoIt 脚本1
    Python2和Python3语法区别
    使用jmeter测试数据库性能
    selenium-Python之上传文件
    selenium-Python之鼠标事件
    selenium-Python之鼠标事件
  • 原文地址:https://www.cnblogs.com/hechengfei/p/4115087.html
Copyright © 2020-2023  润新知