从一个记忆游戏来说明Verilog——2、15位随机数生成(基于LFSR)

前言

上一篇博客我们讲述了游戏的基本原理和具体的模块分工,虽然我们是说是按照自顶向下设计方式,但我们在分析好之后还是应该从底层模块开始,衔接好中间变量并确定我们需要的内容。
这篇博客开始就要详细的说明每一个模块的功能了。

按照游戏顺序来,我们应该先说随机数;从实现的难度上来说,我们也应该先声明一下随机数模块的问题。

随机数? 伪随机数?

学过C的都知道C的随机数虽然是叫做随机数,但是实际上完全就是通过一个种子进行不断的计算实现的伪随机数,或者我们添加一个时间种子

在上一篇博客中我们提到了两种实现随机数的方式:
实现方法参考:取计数器的输出或使用线性反馈移位寄存器(LFSR)生成伪随机数);
这两种方法的本质上和C的时间种子有异曲同工之妙,接下来我们将进行阐述。

我们的随机数原理是这样的,首先主模块从一开始就定义一个15位的种子(因为我们是5个八进制数,也就是一共十五位的二进制数),在程序开始就让其进行自增,当我们按下s0,也就是随机数模块的使能信号有效,我们读入这个种子然后将其处理。(如果是处理,就属于LFSR的方式,不处理就是第一种的直接读取计数器的值,这里我们还是处理一下)

LFSR

线性反馈移位寄存器(Linear Feedback Shift Register),说明在这里。
说白了其实就是通过将一组数据不断按照周期进行循环移位,然后将其中几位进行异或的操作,打乱了种子的内容,看着像随机数而已。

这是通过LFSR输出一个15位二进制数的模块,输入为时钟信号和种子,以及使能端。

`timescale 1ns / 1ps
//一次生成一个五位八进制随机数
module RanGen(
    input clk,       
    input en,       
    input [14:0]seed,     
    output [14:0]rand_num  
);

reg [14:0]num = 15'b00000_00000_00000;	//对输出赋值
assign rand_num = num;

reg if_in1 = 1'b0;						//判断是否输入
reg if_in2 = 1'b0;

always@(posedge en)
begin
    if_in1 <= ~if_in1;
end

always@(posedge clk)
begin       
    if(if_in1 != if_in2)               	//模块开始,读入种子
    begin
        num <= seed;
        if_in2 <= if_in1;
    end
    else                                //LFSR
    begin
        num[0]  <= num[14];
        num[1]  <= num[0];
        num[2]  <= num[1];
        num[3]  <= num[2];
        num[4]  <= num[3]^num[14];
        num[5]  <= num[4]^num[14];
        num[6]  <= num[5]^num[14];
        num[7]  <= num[6];
        num[9]  <= num[7];
        num[8]  <= num[8]^num[14];
        num[10] <= num[9];
        num[11] <= num[10];
        num[12] <= num[11]^num[14];
        num[13] <= num[12];
        num[14] <= num[13];
    end        
end
endmodule

细节部分:

  1. 首先我们使用了一个reg寄存器为输入的wire型赋值,这是因为我们无法直接对输出进行初始化,所以需要使用另外一个变量进行处理。
  2. 一组的if_in使能端,这是因为我们需要在en使能信号上升沿进行处理,但是如果直接将posedge加在clk时钟信号中(使用if(en)的方式),我们知道实际按键的时间是很长的,我们的时钟信号又很快,导致我们在上层模块中取到的随机数就会相同(这几个数都是种子,因为赋值语句还没结束,num一直等于种子)
  3. 循环异或的在上面,这里个人感觉就只要打乱顺序就行了,应该没什么具体的要求。

接着说我们的游戏——模块调用

然后我是采用一个模块来调用这个子模块,通过时钟信号计时的方式每次读取一个随机数,一共五次。
(就五个周期的时间,所以上面的种子输入才使用了上升沿+一组使能信号来触发)
代码:

`timescale 1ns / 1ps
//生成随机数
module random(
    input en,
    input clk,
    input [14:0]seed,
    input s_four,
    output reg [14:0]random_number0,
    output reg [14:0]random_number1,
    output reg [14:0]random_number2,
    output reg [14:0]random_number3,
    output reg [14:0]random_number4
    );
    
    wire [14:0]ret;					//模块调用返回值
    reg [14:0]seed_in;				//输入种子
    reg [2:0]counter = 3'b000;		//计数器(因为非阻塞赋值,所以case需要从001开始)

    reg if_in1 = 1'b0;				//一组输入使能
    reg if_in2 = 1'b0;
    
    RanGen r(clk,en,seed_in,ret);	//LFSR模块调用
    
    always@(posedge en)				//开始输入
    begin
        seed_in <= seed;
        if_in1 <= ~if_in1;
    end
    
    always@(posedge clk or posedge s_four)			//异步清零
    begin
    if(s_four)
        counter <= 3'b000;
    else if(if_in2 != if_in1)
    begin
        case(counter)
        4'b0001:random_number0 <= ret;
        4'b0010:random_number1 <= ret;
        4'b0011:random_number2 <= ret;
        4'b0100:random_number3 <= ret;
        4'b0101:random_number4 <= ret;
        endcase
        if(counter != 3'b100)
            counter <= counter+1;
        else
            if_in2 <= if_in1;
    end
    else;
    end
endmodule

分析

  • 首先我们没有上来就对counter == 3’b000开始取值,是因为要错开刚开始的输入(会导致随机数为0),但我们这样的取法第一位一定是种子。(可以通过从2开始,将上限改变的方式,错开第一个,但是因为种子本身有也有随机性,并没有必要)
    (这里的截图是我们经过了放缩得到的,因为原来的计数器数字过大,仿真时间太长)
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值