从一个记忆游戏来说明Verilog——5、顶层模块的书写

内容回顾

到这里我们已经实现了所有的子模块,这篇博客简单的对我们的小游戏进行了分析,现在我们按照自顶向下的方式,搭出来主模块的内容。

主模块

首先是输入输出:
输入:5个按键开关、8个拨码开关,一个时钟信号
输出:两组led灯的内容&使能端

因为是拨码开关,我们需要对输入的信号进行消抖处理,消抖模块在这里

主要内容

  1. 在生成随机数的模块中,我们需要有一个种子的输入(可以考虑成C的伪随机数种子),他需要在程序开始就不断自增,在我们需要使用的时候传入随机数模块;
  2. 我们生成的随机数需要输出,读入的地址需要输出,读入的数据需要输出,那么我们就有三个模块对输出显示模块进行控制,但是还不能是一组变量,所以我采用的方式是创建了三组中间变量,通过按键开关的使能信号决定哪一组数据应当被传入显示模块;
  3. 我们需要在最后显示地址-正确的随机数,所以我们需要存储用户输入的地址和每一组随机数;在判定数据正确与否的模块中,我们传出了两个使能信号,这些都需要在主模块中利用中间变量进行传输。
  4. 在整个的程序中,我们采用了一个类似于状态机的东西(因为本身就是通过不断的改变状态来实现指定的功能),我们给出了一些使能端,每一个使能端对应着一个状态(没有给出具体的状态编码而已),
    而输入信号是一闪而过的,我们需要的是一个长时间的信号来保证能在指定的时间内调用这个模块。

代码

`timescale 1ns / 1ps
module ex6_top(
    input s0_in,//开始生成
    input s1_in,//输入地址
    input s2_in,//开始匹配内容
    input s3_in,//读入一位八进制数
    input s4_in,//复位
    input [7:0]sw,
    input clk,
    output [7:0]leda,
    output [7:0]ledb,
    output [3:0]dn0,
    output [3:0]dn1,
    //8个led灯,当匹配成功有流水灯效果
    output [7:0]cele,		
    //8个led灯,3-7是匹配模块中判断有多少个数匹配成功,0-2是译码模块判断计数器的,
    //debug时采用的,没什么具体用处
    output [7:0]how_many	
    );
    //线网型除颤过了的信号
    wire s0;
    wire s1;
    wire s2;
    wire s3;
    wire s4;
    //种子
    reg [14:0]seed = 15'b010_0110_0011_1100;
    //输出显示模块的信号源*3
    wire [3:0]number1_t;
    wire [3:0]number2_t;
    wire [3:0]number3_t;
    wire [3:0]number4_t;
    wire [3:0]number5_t;
    wire [3:0]number6_t;
    wire [3:0]number7_t;
    wire [3:0]number8_t;
    
    wire [3:0]number1_p;
    wire [3:0]number2_p;
    wire [3:0]number3_p;
    wire [3:0]number4_p;
    wire [3:0]number5_p;
    wire [3:0]number6_p;
    wire [3:0]number7_p;
    wire [3:0]number8_p;
    
    wire [3:0]number1_n;
    wire [3:0]number2_n;
    wire [3:0]number3_n;
    wire [3:0]number4_n;
    wire [3:0]number5_n;
    wire [3:0]number6_n;
    wire [3:0]number7_n;
    wire [3:0]number8_n;
    //最终送入输出显示模块的信号
    reg [3:0]number1_r;
    reg [3:0]number2_r;
    reg [3:0]number3_r;
    reg [3:0]number4_r;
    reg [3:0]number5_r;
    reg [3:0]number6_r;
    reg [3:0]number7_r;
    reg [3:0]number8_r;
    //中间变量
    wire [14:0]random_number1;	//5个随机数
    wire [14:0]random_number2;
    wire [14:0]random_number3;
    wire [14:0]random_number4;
    wire [14:0]random_number5;
    wire [3:0]place;    		//输入的地址
    wire if_finish;     		//五个随机数是否生成完成
    wire flash;         		//控制输入错误的闪烁
    wire if_right;      		//判断数字对不对
    //状态机部分的使能信号
    reg s_zero= 1'b0;
    reg s_one = 1'b0;
    reg s_two = 1'b0;
    reg finish = 1'b0;  		//随机数产生是否完成的使能端,状态机部分
    reg judge  = 1'b1;			//辅助finish使能信号部分,保证状态正常转换
    
    reg [14:0]right_input= 15'b111_111_111_111_111;//地址对应的内容 
    
    chuchan cc_0(s0_in,clk,s0);
    chuchan cc_1(s1_in,clk,s1);
    chuchan cc_2(s2_in,clk,s2);
    chuchan cc_3(s3_in,clk,s3);
    chuchan cc_4(s4_in,clk,s4);
    
    always@(posedge clk)    //种子处理
        seed <= seed + 1;
    //将各种信号统一修改成类似状态机的状态的使能,以支持模块
    always@(posedge clk)
    begin
        if(s0)			//开始生成随机数
        begin
            s_zero <= 1'b1;
        end
        else if(if_finish && judge)
        begin
            finish <= 1'b1;
            judge <= 1'b0;
        end
        else if(s1)		//开始输入地址
        begin
            s_zero <= 1'b0;
            finish <= 1'b0;
            s_one <= 1'b1;
            case(place)
            4'b0000: right_input <= random_number1;
            4'b0001: right_input <= random_number2;
            4'b0010: right_input <= random_number3;
            4'b0011: right_input <= random_number4;
            4'b0100: right_input <= random_number5;
            endcase
        end
        else if(s2)		//开始匹配
        begin
            s_one <= 1'b0;
            s_two <= 1'b1;
        end
        else if(s4)		//重置
        begin
            s_zero<= 1'b0;
            s_one <= 1'b0;
            s_two <= 1'b0;
            finish<= 1'b0;
            judge <= 1'b1;
        end
        else;
    end
    //给输出显示模块传入状态对应的数据
    always@(*)
    begin
        if(finish)					//输出随机数的时间
        begin
            number1_r = number1_t;
            number2_r = number2_t;
            number3_r = number3_t;
            number4_r = number4_t;
            number5_r = number5_t;
            number6_r = number6_t;
            number7_r = number7_t;
            number8_r = number8_t;
        end
        else if(s_one)				//输出地址的时间
        begin
            number1_r = number1_p;
            number2_r = number2_p;
            number3_r = number3_p;
            number4_r = number4_p;
            number5_r = number5_p;
            number6_r = number6_p;
            number7_r = number7_p;
            number8_r = number8_p;
        end
        else if(s_two)				//输出匹配情况的时间
        begin
            number1_r = number1_n;
            number2_r = number2_n;
            number3_r = number3_n;
            number4_r = number4_n;
            number5_r = number5_n;
            number6_r = number6_n;
            number7_r = number7_n;
            number8_r = number8_n;
        end
        else;
    end
    //功能调用
    celebrate cel(if_right,clk,cele);
    light li_all(clk,s_one,s_two,flash,if_right,finish,number1_r,number2_r,number3_r,number4_r,number5_r,number6_r,number7_r,number8_r,leda,ledb,dn0,dn1);
    random rand(s0,clk,seed,s4,random_number1,random_number2,random_number3,random_number4,random_number5,if_finish);
    translate translation(random_number1,random_number2,random_number3,random_number4,random_number5,clk,s4,finish,number4_t,number5_t,number6_t,number7_t,number8_t,how_many[2:0]);
    in_place ip(s1,sw,place,number1_p,number2_p,number3_p,number4_p,number5_p,number6_p,number7_p,number8_p);
    in_number in(sw[2:0],s3,s_two,s4,clk,right_input,place[2:0],how_many[7:3],number1_n,number2_n,number3_n,number4_n,number5_n,number6_n,number7_n,number8_n,flash,if_right);
endmodule

细节

首先是除颤模块调用,还是之前的博客代码,直接拿过来了就是,这部分不做阐述。

cele模块是我们在匹配成功给出的一个流水灯效果,之前的流水灯博客在这里,这个基本上是相同的。
不过这次的效果是从两侧开始向中间逐渐变亮。

`timescale 1ns / 1ps

module celebrate(
    input right,
    input clk,
    output [7:0]cele_o
    );
    
    wire clk_o;
    reg [2:0]counter = 3'b111;
    cele_divider cele_div(clk,clk_o);//4HZ的分频模块
    
    reg [7:0]cele = 8'b0000_0000;
    assign cele_o = cele;

    always@(posedge clk_o)
    begin
        if(right)
        begin
            case(counter)
            3'b000:
            begin
                cele[0] <= 1'b1;
                cele[7] <= 1'b1;
            end
            3'b001:
            begin
                cele[1] <= 1'b1;
                cele[6] <= 1'b1;
            end
            3'b010:
            begin
                cele[2] <= 1'b1;
                cele[5] <= 1'b1;
            end
            3'b011:
            begin
                cele[3] <= 1'b1;
                cele[4] <= 1'b1;
            end
            endcase
            if(counter != 3'b011)
                counter <= counter+1'b1;
            else;
        end
        else
        begin
            cele = 8'b0000_0000;
            counter = 3'b111;
        end
    end
    
endmodule

分频在流水灯的博客中,都是之前的东西了。

结语

到这里我们的整个记忆游戏项目就结束了,虽然不是很大,但是我们也从中学到了很多。
首先是对于一个项目,如何自顶向下的分析,如何处理好不同模块之间的关系,需要传递什么,需要接受什么;模块之间的并行关系如何形成一个个状态(类似状态机)
还有我们确实很难做到一次将整体的架构搭好,所以我们需要对照着我们的要求,不断将顶层模块和子模块进行修改,这些不可避免,但是如果开始想的比较好,还是会剩下来很多时间的。

项目不大,但是还算有趣,学到了很多东西,尽管Verilog结束了一段时间,但是还是留下了很深的印象。

上一篇:FPGA设计之折叠思路


下一篇:Verilog中同步复位和异步复位比较