【逻辑电路设计】一种在物理逻辑电路中产生随机数的方法及其verilog仿真分析

前言

最近开始研究FPGA,在实现音乐播放器的频谱显示功能上,需要使用随机数来控制字体颜色,奈何官方提供的IP核中没有随机数发生器,物理电路本身并没有随机数发生器,所以功能实现后,特意公开,方便大家使用。

原理

随机数发生器的核心是M序列发生器,但在M序列的基础上进行了改进,引入了时间种子,在M序列产生一个周期后,把当前随机值与时间种子进行异或后装载到随机数寄存器上,使得每次产生的序列都不一样,避免了周期性序列的问题。
下图是硬件RTL分析图
RTL分析图

实现

具体实现代码详见附录。

模块可修改参数

  1. seed,开始种子,随机数模块的初始化输出值。
  2. feedback,反馈系数,根据M序列发生器的标准,最低位必须为1且必须最少两位参与反馈。

输入

  1. clk,系统时钟
  2. rst_n,复位信号
  3. new,新随机数产生信号

输出

  1. rand_code,模块产生的随机码

仿真

实际上的模块产生的是32位随机数,为了仿真研究方便,做了一个8位数的版本,以下的数据仿真分析全部采用8位数的版本
仿真代码与数据分析代码详见附录。

模块时序

模块时序图如下图所示
模块时序图

  1. rst_n复位信号拉高,模块开始工作
  2. new新随机数信号每检测到一个上升沿,在clk的下一拍到来时更新随机数输出值
  3. 在new新随机数信号的下降沿处,外部模块即可获取到产生的随机数

结果分析

仿真时,把每次生成的随机数记录到了一个文件中,使用Matlab对生成的随机数数列进行分析。一共生成了8组,每组共255个随机数。
仿真使用的是8位二进制随机数生成器,开始种子0x12,反馈系数0x53。
仿真共生成了2040个随机数

自相关性

随机数列的自相关性如下图所示
自相关性分析
随机数列的自相关曲线只有一个峰值,最后几乎为0,说明模型产生的数列在一定程度上可以视为随机数列。

频数分布

数列的频数分布如下图所示
数列分布频数直方图
可见在数值比较均匀的分布在允许值上,计算可以得到数列的数学期望是126.9,这个比较接近理想的255/2=127.5,说明本模型所生成的数列分布相当接近平均分布。

分布散点图

为了更直观地观察产生的数组,根据组与组之间以时间种子重装载时刻作为分割,把输出数组分成8组,每255为一组,绘制散点图如下图所示
分布散点图
散点图中“每一层”为一组数据,Y轴为数列序号,Z轴为数列值。

附录

实现代码

32位版

rand_core.v

module rand_core(
    input               clk,        //系统时钟信号
    input               rst_n,      //复位信号,低电平有效
    input               new,        //新随机数产生信号,上升沿有效
    
    output reg  [31:0] rand_code
    );
    
parameter seed = 32'h3ecd12;  //开始种子
parameter feedback = 32'h280c97;  //反馈系数

reg [31:0] time_seed;
reg        reset_seed_d;
reg        new_d;
reg [31:0] new_cnt;

//计算新的位
/* 注^号在verilog中可作为单目运算符使用,表示位异或 */
wire feedback_item = ^(feedback & rand_code);

//new信号次数计数器
//当new了2^31-1次后,产生种子重装载信号
wire reset_seed; //种子重装信号,低电平有效
assign reset_seed = (new_cnt == ~(32'd0)) ? 1'b0:1'b1;
always @(posedge clk or negedge rst_n) begin
     if(!rst_n)
        new_cnt <= 32'd0;
     else if(new_d & (~new))
        new_cnt <= new_cnt + 1'b1;
     else if(new_cnt == ~(32'd0))
        new_cnt <= 32'd0;
end

//信号缓存器更新块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        new_d <= 1'b0;
    else
        new_d <= new;
end

//clk计数器实现种子更新
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        time_seed <= 32'd0;
    else
        time_seed <= time_seed + 1'b1;
end

//M序列模块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        rand_code <= seed;
    else if(!reset_seed)
        rand_code <= rand_code ^ time_seed;
    else if(~new_d & new)
        rand_code <= {feedback_item,rand_code[31:1]};
    //防止进入全0死锁状态
    else if(rand_code == 32'd0)
        rand_code <= seed;
end
    
endmodule

8位版

rand_core_u8.v

module rand_core_u8(
    input               clk,        //系统时钟信号
    input               rst_n,      //复位信号,低电平有效
    input               new,        //新随机数产生信号,上升沿有效
    
    output reg  [7:0] rand_code
    );
    
parameter seed = 8'h12;  //开始种子
parameter feedback = 8'h53;  //反馈系数

reg [7:0] time_seed;
reg        reset_seed_d;
reg        new_d;
reg [7:0] new_cnt;

//计算新的位
/* 注^号在verilog中可作为单目运算符使用,表示位异或 */
wire feedback_item = ^(feedback & rand_code);

//new信号次数计数器
//当new了2^7-1次后,产生种子重装载信号
wire reset_seed; //种子重装信号,低电平有效
assign reset_seed = (new_cnt == ~(8'd0)) ? 1'b0:1'b1;
always @(posedge clk or negedge rst_n) begin
     if(!rst_n)
        new_cnt <= 8'd0;
     else if(new_d & (~new))
        new_cnt <= new_cnt + 1'b1;
     else if(new_cnt == ~(8'd0))
        new_cnt <= 8'd0;
end

//信号缓存器更新块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        new_d <= 1'b0;
    else
        new_d <= new;
end

//clk计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        time_seed <= 7'd0;
    else
        time_seed <= time_seed + 1'b1;
end

//M序列模块
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        rand_code <= seed;
    else if(!reset_seed)
        rand_code <= rand_code ^ time_seed;
    else if(~new_d & new)
        rand_code <= {feedback_item,rand_code[7:1]};
    //防止进入全0死锁状态
    else if(rand_code == 8'd0)
        rand_code <= seed;
end
    
endmodule

仿真代码

rand_core_sim.v

`timescale 1ns / 1ps

module rand_core_sim();

reg clk;
reg rst_n;
reg new;

wire [31:0] rand_code;

rand_core u_rand_core(
    .clk            (clk),      
    .rst_n          (rst_n),    
    .new            (new),      
                      
    .rand_code      (rand_code)
);

integer i = 0,fp;

initial begin
    //初始化文件
    fp = $fopen("data_out.txt","w");
    clk = 1'b0;
    rst_n = 1'b0;
    new = 1'b1;
    #200;
    rst_n = 1'b1;
    while(i != 255*8)begin
        new = 1'b1;
        i = i + 1;
        #20;
        new = 1'b0;
        //保存值方便分析
        $fwrite(fp, "%d ", rand_code);
        #20;
    end
    $fclose(fp);
    $stop;
end

always #10 clk <= ~clk;

endmodule

分析代码

main.m

close all
%读取数据
fp = fopen("F:\zynq_fpga_prj\rand_gen\rand_gen.sim\sim_1\behav\xsim\data_out.txt","r");
data = fscanf(fp,"%d ");
fclose(fp);

%计算数学期望与标准差
fprintf('平均值:%f,标准差:%f\n',mean(data),std(data));

%计算自相关性
corr = autocorr(data);
figure;
plot(0:length(corr)-1,corr);
title('数列自相关性');

%分析分布频数直方图
figure;
hist(data);
title('数列频数直方图');

%绘制立体散点图
figure;
foo = reshape(data,255,8);
foo = foo';
[X,Y] = meshgrid(0:254, 0:7);
scatter3(X(:),Y(:),foo(:));
title('分布散点图');
  • 10
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值