高斯随机数发生器(verilog实现)

高斯随机数发生器(verilog实现)


一、理论部分

​ 经查阅网络资料与文献,首先通过细胞自动机(cellular automata)获得均匀分布的随机变量,然后通过Box-Muller变换将均匀分布的随机变量变换为服从高斯分布的随机变量。

(一)细胞自动机

​ 细胞自动机是一个细说起来十分复杂的概念,但是可以在我们的应用场景中可以得到简化。先放表达式
s i t + 1 = f i ( s i − 1 t , s i t , s i + 1 t ) s_i^{t+1}=f_i(s_{i-1}^{t},s_{i}^{t},s_{i+1}^{t}) sit+1=fi(si1t,sit,si+1t)
​ 式中 s i t + 1 s_i^{t+1} sit+1表示在t+1时刻第i个细胞的状态,它取决于t时刻第i-1个、第i个、第i+1个细胞的状态为自变量的某一种函数映射关系( f i f_i fi)。

​ 为了方便起见,将基本细胞自动机的邻域状态配置{ s i t − 1 s_i^{t-1} sit1, s i t s_i^{t} sit, s i t + 1 s_i^{t+1} sit+1}的映射 { f 7 f_7 f7=(111), f 6 f_6 f6=(110),… f 1 f_1 f1=(001), f 0 f_0 f0=(000)} 的组合 I f I_f If= ∑ i = 0 7 f i 2 i \sum_{i=0}^{7}f_i2^i i=07fi2i 称为基本细胞自动机的规则号。

​ 例如{ f 7 f 6 f 5 f 4 f 3 f 2 f 1 f 0 = 10010110 f_7f_6f_5f_4f_3f_2f_1f_0=10010110 f7f6f5f4f3f2f1f0=10010110} 即 150 = 2 1 + 2 2 + 2 4 + 2 7 150=2^1+2^2+2^4+2^7 150=21+22+24+27 对应的规则号是150。对于规则150,上述表达式可以写为
s i t + 1 = s i − 1 t + s i t + s i + 1 t s_i^{t+1}=s_{i-1}^{t}+s_{i}^{t}+s_{i+1}^{t} sit+1=si1t+sit+si+1t
如下图

请添加图片描述

​ 再如{ f 7 f 6 f 5 f 4 f 3 f 2 f 1 f 0 = 01011010 f_7f_6f_5f_4f_3f_2f_1f_0=01011010 f7f6f5f4f3f2f1f0=01011010} 即 90 = 2 1 + 2 3 + 2 4 + 2 6 90=2^1+2^3+2^4+2^6 90=21+23+24+26 对应的规则号是90。对于规则90,上述表达式可以写为
s i t + 1 = s i − 1 t + s i + 1 t s_i^{t+1}=s_{i-1}^{t}+s_{i+1}^{t} sit+1=si1t+si+1t
如下图

请添加图片描述

在这里贴上一些规则号与状态表达式的映射关系

请添加图片描述

(二)Box-Muller变换

​ Box-Muller变换是通过服从均匀分布 的随机变量,来构建服从高斯分布 的随机变量的一种方法。设 U1U2为服从[0,1]上均匀分布的随机变量,若 X、**Y **满足
X = c o s ( 2 π U 1 ) − 2 l n U 2 Y = s i n ( 2 π U 1 ) − 2 l n U 2 X=cos(2\pi U_1 )\sqrt{-2lnU_2}\\ Y=sin(2\pi U_1 )\sqrt{-2lnU_2} X=cos(2πU1)2lnU2 Y=sin(2πU1)2lnU2
XY 服从均值为0,方差为1的高斯分布。

​ 以下是数学推导,若以实现功能为目的可自行跳过。

​ 假定 X、Y 服从均值为0,方差为1的高斯分布,且相互独立。令p(X)和p(Y)分别为其密度函数,则
p ( X ) = 1 2 π e − X 2 2 p ( Y ) = 1 2 π e − Y 2 2 p(X)= \frac{1}{\sqrt{2\pi}}e^{-\frac{X^2}{2}}\\ p(Y)= \frac{1}{\sqrt{2\pi}}e^{-\frac{Y^2}{2}} p(X)=2π 1e2X2p(Y)=2π 1e2Y2
由于X、Y相互独立,因此它们的联合概率密度满足
p ( X , Y ) = 1 2 π e − X 2 + Y 2 2 p(X,Y)=\frac{1}{\sqrt{2\pi}}e^{-\frac{X^2+Y^2}{2}} p(X,Y)=2π 1e2X2+Y2
将X、Y作坐标变换,使
X = R cos ⁡ θ Y = R sin ⁡ θ X=R\cos{\theta}\\ Y=R\sin{\theta} X=RcosθY=Rsinθ

∫ − ∞ ∞ ∫ − ∞ ∞ 1 2 π e − X 2 + Y 2 2 d X d Y = ∫ − ∞ ∞ ∫ − ∞ ∞ 1 2 π e − R 2 2 R d θ d R = 1 \int_{-\infty}^{\infty}\int_{-\infty}^{\infty}\frac{1}{2\pi}e^{-\frac{X^2+Y^2}{2}}dXdY=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}\frac{1}{2\pi}e^{-\frac{R^2}{2}}Rd\theta dR=1 2π1e2X2+Y2dXdY=2π1e2R2RdθdR=1
由此可得R与θ的分布函数
P R ( R ≤ r ) = ∫ 0 2 π ∫ 0 r 1 2 π e − R 2 2 R d θ d R = 1 − e − r 2 2 P θ ( θ ≤ ϕ ) = ∫ 0 ϕ ∫ 0 ∞ 1 2 π e − R 2 2 R d θ d R = ϕ 2 π P_R(R\leq r)=\int_{0}^{2\pi}\int_0^{r}\frac{1}{2\pi}e^{-\frac{R^2}{2}}Rd\theta dR=1-e^{-\frac{r^2}{2}}\\ P_\theta(\theta\leq \phi)=\int_{0}^{\phi}\int_0^{\infty}\frac{1}{2\pi}e^{-\frac{R^2}{2}}Rd\theta dR=\frac{\phi}{2\pi} PR(Rr)=02π0r2π1e2R2RdθdR=1e2r2Pθ(θϕ)=0ϕ02π1e2R2RdθdR=2πϕ
显然,θ服从[0,2π]上的均匀分布。令
F R ( r ) = 1 − e r 2 2 F_R(r)=1-e^{\frac{r^2}{2}} FR(r)=1e2r2
则其反函数
R = F R − 1 = − 2 l n ( 1 − z ) R=F^{-1}_{R}=\sqrt{-2ln(1-z)} R=FR1=2ln(1z)
当z服从[0,1]上均匀分布时,R的分布函数为 F R ( r ) F_R(r) FR(r) 。因此可以选择两个服从[0,1]上均匀分布的随机变量U1、U2,使得
U 1 = θ 2 π , U 2 = 1 − z U_1 = \frac{\theta}{2\pi}, U_2 = 1-z U1=2πθ,U2=1z

θ = 2 π U 1 , R = − 2 l n U 2 \theta = 2\pi U_1,R=\sqrt{-2lnU_2} θ=2πU1,R=2lnU2
将其带入
X = R cos ⁡ θ , Y = R sin ⁡ θ X=R\cos{\theta}, Y=R\sin{\theta} X=Rcosθ,Y=Rsinθ
得到最终表达式
X = c o s ( 2 π U 1 ) − 2 l n U 2 Y = s i n ( 2 π U 1 ) − 2 l n U 2 X=cos(2\pi U_1 )\sqrt{-2lnU_2}\\ Y=sin(2\pi U_1 )\sqrt{-2lnU_2} X=cos(2πU1)2lnU2 Y=sin(2πU1)2lnU2

二、实践部分
(一)一维细胞自动机的实现

对于单个细胞

  always @(*)
  begin
    case({head,tail})
      2'b10:self_next = ctrl ? self^right:right;
      2'b01:self_next = ctrl ? left^self : left;
      2'b00:self_next = ctrl ? left^self^right:left^right;
      default:self_next=ctrl ? left^self^right:left^right;
    endcase
  end

head,tail用于判断该细胞是否处于队头或队尾。对头则无左侧细胞,队尾则无右侧细胞。

self_next 是该细胞下一时刻的值。

self、left、right 为本细胞、左侧细胞、右侧细胞的当前值。

ctrl用于判断规则号,这里只用到了规则150和规则90,分别以1和0表示。

当ctrl==1时,self_next =left ^ self ^ right,即规则150。

当ctrl==0时,self_next =left ^ right,即规则90。

对于N位的一维细胞自动机,只需要将上述单个细胞例化N次,并给出细胞的初始值和不同位置的规则号。

例如,对于32位自动细胞机,可以给出

parameter INIT_VEC = 32'b0100_1000_0001_0010_0100_1000_0001_0010,//初始值
parameter RULE_VEC = 32'b0000_1100_0100_0111_0000_1100_0000_0110,//规则号ctrl

通过使用generate语句简化例化过程。

generate
 genvar i;
  for(i=0; i<N; i=i+1) begin
   if (i==0) begin
    SingleCell#(.init(INIT_VEC[i]),.head(1'b0),.tail(1'b0)) 
    inst_SingleCell(    
           .clk_i   (clk_i),  
           .ctrl    (RULE_VEC[i]),
           .left    (1'b0),
           .right   (uni_r[1]),
           .self    (uni_r[i]),
           .out     (uni_next[i])
       );
   end
   ......//省略部分内容减少篇幅
endgenerate

一般的语法流程不再赘述,只展示关键部分。

为了检验生成的数据是否为服从均匀分布的随机变量,我们在对本模块仿真时导出数据为txt文本,并在matlab上展示出来。

仿真代码

`timescale 1ns / 1ps
module tb_CellAutomata();
localparam  INIT_VEC = 32'b0100_1000_0001_0010_0100_1000_0001_0010;
localparam  RULE_VEC = 32'b0000_1100_0100_0111_0000_1100_0000_0110;
localparam  N = 32;
localparam period = 10;
reg clk_i = 0;
reg rst_n_i = 1;
wire [N-1:0] uni_out;
CellularAutomata 
#(
  .INIT_VEC(INIT_VEC ),
  .RULE_VEC(RULE_VEC ),
  .N (N )
)
CellularAutomata_dut (
  .clk_i (clk_i ),
  .rst_n_i (rst_n_i ),
  .uni_out  (uni_out )
);
initial begin
  begin
    #(period*2) rst_n_i = 1'b0;
    #(period*10) rst_n_i = 1'b1;
    #(period*201_000);
    $writememb("new_data.txt",mem);
    $finish;
  end
end
parameter M = 200_000;
reg [N-1:0] mem [M:1];
integer index = 1;
initial begin//采集数据
    begin
        #(period*100);
        forever begin
            #(period);
            mem[index] = uni_out;
            index = (index >= M) ? 1 : index + 1;
        end
    end
  end
always
  #(period/2)  clk_i = !clk_i ;
endmodule

matlab部分

clear all;
clc;
fid = fopen("D:\MatlabProject\MatlabData\new_data.txt");
data = textscan(fid,"%b");
fclose(fid);
data = cell2mat(data);
data = double(data)/double(max(data));
h = histogram(data,1024,"Binlimits",[0,1]);

可以看出虽然有些参差,但大致符合[0,1]上的服从均匀分布的随机变量。

请添加图片描述

(二)Box-Muller变换的实现

X = c o s ( 2 π U 1 ) − 2 l n U 2 Y = s i n ( 2 π U 1 ) − 2 l n U 2 X=cos(2\pi U_1 )\sqrt{-2lnU_2}\\ Y=sin(2\pi U_1 )\sqrt{-2lnU_2} X=cos(2πU1)2lnU2 Y=sin(2πU1)2lnU2

本次选择了简易的方法近似实现函数。大致思路是,首先用matlab生成 s i n x sinx sinx c o s x cosx cosx − 2 l n x \sqrt{-2lnx} 2lnx 函数自变量和因变量的映射,然后使IP核生成ROM并将其映射关系存放在ROM中,最后通过IP核生成乘法器将两部分相乘。

s i n sin sin函数为例,给出matlab代码

clc;clear;                %储存单元地址线
depth=2^10;                 %存储单元;
widths=10;                    %数据宽度;
index = linspace(0,pi*2,depth);              
sin_value = sin(index); 
y1 = sin_value/max(sin_value);
sin_value = round(y1 * (depth/2 -1));  %扩大正弦幅度值    
plot(sin_value);
number = [0:depth];
fid=fopen('fsin.coe','w+');
fprintf(fid,'memory_initialization_radix=10;\n');
fprintf(fid,'memory_initialization_vector=\n');
for i = 1 : depth - 1  
    fprintf(fid, '%d,\n', sin_value(i));
end
fprintf(fid, '%d;', sin_value(depth));
fclose(fid);

然后是IP核的使用。以Vivado2017.4为例。

第一步,点击IP Catalog,找到Block Memory Generator
请添加图片描述

第二步,点击Block Memory Generator后进入Customize IP界面进行配置。

Basic界面中Memory Type使用 Single Port ROM即可。

请添加图片描述

Port A Options中Port A Width决定了输出位宽,Port A Depth决定了输入地址的宽度。Enable Port Type 可以改成 Always Enabled,图个方便。

请添加图片描述

Other Options 界面中需要勾选 Load Init File 以将Matlab生成的.coe

文件导入。当文件格式、文件位置、文件中数据宽度不符合配置要求时都会标红。
请添加图片描述

​ Summary界面中可以看到一些相关信息,不再赘述。注意,当文件需要的IP核较多时,应当将IP核更改为条例清晰的命名以方便后续工作。生成完IP核后,使用时直接例化就ok了。乘法器IP核只需要找到 Multilplier核并配置即可,没有太大难度,不再演示。

按照有效果即可的原则(其实是电脑跑不动分析),我将原本计划的32位改为了20位输出。

module top_module(
    input clk_i,
    input reset,
    output wire [19:0]gaus1,
    output wire [19:0]gaus2
    );
    wire [31:0]uni_out;                  
    wire [9:0]addr1 = uni_out[9:0];
    wire [9:0]addr2 = uni_out[19:10];
    wire [9:0]value_sin;
    wire [9:0]value_cos;
    wire [9:0]value_y;
    CellularAutomata #(
      .INIT_VEC(32'b0100_1000_0001_0010_0100_1000_0001_0010 ),
      .RULE_VEC(32'b0000_1100_0100_0111_0000_1100_0000_0110 ),
      .N (32 ))
    CellularAutomata_dut (
      .clk_i (clk_i ),
      .reset (reset ),
      .uni_out  ( uni_out)
    );
    new 
    new_dut (
      .clk_i (clk_i ),
      .reset (reset ),
      .addr1 (addr1  ),
      .addr2 (addr2 ),
      .value_sin (value_sin ),
      .value_cos (value_cos ),
      .value_y  ( value_y)
    ); 
    Mult_Box_Muller inst1 (
      .CLK(clk_i),  // input wire CLK
      .A(value_sin),      // input wire [9 : 0] A
      .B(value_y),      // input wire [9 : 0] B
      .P(gaus1)      // output wire [19 : 0] P
    );
    Mult_Box_Muller inst2 (
      .CLK(clk_i),  // input wire CLK
      .A(value_cos),      // input wire [9 : 0] A
      .B(value_y),      // input wire [9 : 0] B
      .P(gaus2)      // output wire [19 : 0] P
    );
endmodule
				
(三)结果的仿真与检验

第一次仿真我得到了20位的输出,想要用Matlab读入数据时,发现我只会用Matlab读入16、32位这种整位的有符号数据,没有读入20位有符号数据的方法(没详细学过Matlab,别骂了别骂了),然后本着能用就用的原则,我把20位数据后加了12个0,凑到了32位。

仿真文件

`timescale 1ns / 1ps
module tb_top();
localparam period = 10;
reg clk_i = 0;
reg reset = 0;
wire [19:0] gaus1;
wire [19:0] gaus2;
reg [31:0]buffer1;
reg [31:0]buffer2;
always@(*)begin
  buffer1={gaus1,{12{1'b0}}};
  buffer2={gaus2,{12{1'b0}}};
end
parameter M = 65536;
reg [31:0] mem1 [M:1];
reg [31:0] mem2 [M:1];
integer index = 1;
top_module top_module_dut (
  .clk_i (clk_i ),
  .reset (reset ),
  .gaus1 (gaus1 ),
  .gaus2 (gaus2 )
);
integer k;
initial begin
for(k=1;k<(M+1);k=k+1) begin mem1[k] = 31'd0;mem2[k] = 31'd0; end
end
initial begin
  begin
    #(period*2)    reset = 1'b1;
    #(period*10)   reset = 1'b0;
    #(period*(M+1000));
    $writememb("gaus1.txt",mem1);
    $writememb("gaus2.txt",mem2);
    #(period*300);
    $finish;
  end
end
initial begin//采集数据
    begin
        #(period*12);
        forever begin
            #(period);
            mem1 [index] = buffer1;
            mem2 [index] = buffer2;
            index = (index >= M) ? 1 : index + 1;
        end
    end
end
always
  #(period/2)  clk_i = ! clk_i ;
endmodule

写的比较稍微有亿点点乱。将生成的数据用Matlab展示一下。

clear all;
clc;
fid = fopen("D:\MatlabProject\MatlabData\gaus1.txt");
data = textscan(fid,"%bs32");
fclose(fid);
data = cell2mat(data);
disp(data);
data = double(data)/pow2(32);
h = histogram(data, 1024,'BinLimits',[-1,1]);
%[h,p]=lillietest(data);
%normplot(data);

请添加图片描述

好像有点像高斯分布,为了验证,我查到了两种检测方法。没错,就是我注释了的那两句。

[h,p]=lillietest(data);

请添加图片描述

normplot(data);

请添加图片描述

头尾较为稀疏且偏离较大,大概是因为原本数据只有20位数,强行加的12个0导致边缘稀疏。

三、一些感想

现在是2022年大年初四,立春,北京冬奥会开幕式。本来想写一下总结的,但是快到凌晨,所以不想了。新年快乐呀!

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值