前言
最近开始研究FPGA,在实现音乐播放器的频谱显示功能上,需要使用随机数来控制字体颜色,奈何官方提供的IP核中没有随机数发生器,物理电路本身并没有随机数发生器,所以功能实现后,特意公开,方便大家使用。
原理
随机数发生器的核心是M序列发生器,但在M序列的基础上进行了改进,引入了时间种子,在M序列产生一个周期后,把当前随机值与时间种子进行异或后装载到随机数寄存器上,使得每次产生的序列都不一样,避免了周期性序列的问题。
下图是硬件RTL分析图
实现
具体实现代码详见附录。
模块可修改参数
- seed,开始种子,随机数模块的初始化输出值。
- feedback,反馈系数,根据M序列发生器的标准,最低位必须为1且必须最少两位参与反馈。
输入
- clk,系统时钟
- rst_n,复位信号
- new,新随机数产生信号
输出
- rand_code,模块产生的随机码
仿真
实际上的模块产生的是32位随机数,为了仿真研究方便,做了一个8位数的版本,以下的数据仿真分析全部采用8位数的版本。
仿真代码与数据分析代码详见附录。
模块时序
模块时序图如下图所示
- rst_n复位信号拉高,模块开始工作
- new新随机数信号每检测到一个上升沿,在clk的下一拍到来时更新随机数输出值
- 在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('分布散点图');