I'm having trouble generating normal random variables as part of a bigger project and need some help. First of all, yes, I am aware of AWGN methods, Box-Muller etc and other better random number generators and yes I have chosen a pretty ineffective RNG solution but this is my first big verilog project and I'm fine doing it the slow way because I'm trying to learn so please don't just respond by telling me all the different ways I should be going about this... Unless I'm doing something very wrong! Now, the problem.
The idea is by using a LFSR we can generate uniform pseudo-random variables and then via the central limit theorem if we sample the mean of many independent uniform variables we'll get a normal distribution.
I've read a similar VHDL solution that seemed relatively successful so I believe the method should work (not the most accurate though) - VHDL method
I believe my error is in failing to collect the mean of all 32 LFSR outputs each clock cycle as I've checked the waveforms of my LFSR module and that alone seems to be working fine but when I check the outputs in python I'm still getting a uniform distribution... not a normal one.
Below is my module to accumulate the uniform pseudo-random variables from 32 LFSRs each clock cycle (with different seeds - "randomly" generated in python haha), and then take the average them (divide by 32).
`timescale 1ns / 1ps
module CLTuniformToNormal(output [31:0] o_Mean, input i_Clock, input i_Reset);
reg [36:0] r_Accumulator = 0;
wire [31:0] o_LFSR_Out[31:0];
parameter c_SEED0 = 32'h2b5f712d; parameter c_SEED1 = 32'h1834a06b; parameter c_SEED2 = 32'h9c82b0a3; parameter c_SEED3 = 32'hf33e3930;
parameter c_SEED4 = 32'hf5cc2528; parameter c_SEED5 = 32'h6b21a119; parameter c_SEED6 = 32'hf874c4ea; parameter c_SEED7 = 32'h8b5d0748;
parameter c_SEED8 = 32'hdf4c754a; parameter c_SEED9 = 32'hc1cfb4b8; parameter c_SEED10 = 32'h2097bf95; parameter c_SEED11 = 32'h9bc45a55;
parameter c_SEED12 = 32'hb49c15c4; parameter c_SEED13 = 32'h0a7b9340; parameter c_SEED14 = 32'h7f658627; parameter c_SEED15 = 32'hee57af5a;
parameter c_SEED16 = 32'hd8b427bb; parameter c_SEED17 = 32'he793836a; parameter c_SEED18 = 32'hf4e3e39f; parameter c_SEED19 = 32'h2987b38b;
parameter c_SEED20 = 32'h173ac4ce; parameter c_SEED21 = 32'hdb53d09e; parameter c_SEED22 = 32'h5af5fd7f; parameter c_SEED23 = 32'h1352a672;
parameter c_SEED24 = 32'h2ab03e34; parameter c_SEED25 = 32'hdd03bb9b; parameter c_SEED26 = 32'h6b72eaca; parameter c_SEED27 = 32'hb5e22b2e;
parameter c_SEED28 = 32'hfa232aea; parameter c_SEED29 = 32'h067db0f8; parameter c_SEED30 = 32'hed09559a; parameter c_SEED31 = 32'hfacf5eaa;
// Instantiate 32 LFSR modules
genvar i;
generate
for (i = 0; i < 32; i = i + 1) begin : INST_LFSR
LFSR #(.c_SEED((i==0) ? c_SEED0 : (i==1) ? c_SEED1 : (i==2) ? c_SEED2 : (i==3) ? c_SEED3 : (i==4) ? c_SEED4 :
(i==5) ? c_SEED5 : (i==6) ? c_SEED6 : (i==7) ? c_SEED7 : (i==8) ? c_SEED8 :
(i==9) ? c_SEED9 : (i==10) ? c_SEED10 : (i==11) ? c_SEED11 : (i==12) ? c_SEED12 :
(i==13) ? c_SEED13 : (i==14) ? c_SEED14 : (i==15) ? c_SEED15 : (i==16) ? c_SEED16 :
(i==17) ? c_SEED17 : (i==18) ? c_SEED18 : (i==19) ? c_SEED19 : (i==20) ? c_SEED20 :
(i==21) ? c_SEED21 : (i==22) ? c_SEED22 : (i==23) ? c_SEED23 : (i==24) ? c_SEED24 :
(i==25) ? c_SEED25 : (i==26) ? c_SEED26 : (i==27) ? c_SEED27 : (i==28) ? c_SEED28 :
(i==29) ? c_SEED29 : (i==30) ? c_SEED30 : c_SEED31))
LFSR_inst (
.o_LFSR_Out(o_LFSR_Out[i]),
.i_Clock(i_Clock),
.i_Reset(r_Reset)
);
end
endgenerate
always @(posedge i_Clock or posedge i_Reset) begin
if (i_Reset) begin
r_Accumulator <= 0;
end else begin
// Accumulate outputs
r_Accumulator <= r_Accumulator + o_LFSR_Out[0] + o_LFSR_Out[1] + o_LFSR_Out[2] + o_LFSR_Out[3] + o_LFSR_Out[4] + o_LFSR_Out[5] +
o_LFSR_Out[6] + o_LFSR_Out[7] + o_LFSR_Out[8] + o_LFSR_Out[9] + o_LFSR_Out[10] +
o_LFSR_Out[11] + o_LFSR_Out[12] + o_LFSR_Out[13] + o_LFSR_Out[14] + o_LFSR_Out[15] +
o_LFSR_Out[16] + o_LFSR_Out[17] + o_LFSR_Out[18] + o_LFSR_Out[19] + o_LFSR_Out[20] +
o_LFSR_Out[21] + o_LFSR_Out[22] + o_LFSR_Out[23] + o_LFSR_Out[24] + o_LFSR_Out[25] +
o_LFSR_Out[26] + o_LFSR_Out[27] + o_LFSR_Out[28] + o_LFSR_Out[29] + o_LFSR_Out[30] +
o_LFSR_Out[31];
end
end
// Calculate mean
assign o_Mean = (r_Accumulator >> 5);// - 2**31; // Divide by 32 and shift mean to 0
endmodule
My testbench writes the o_Mean each clock cycle to a text file which I then plot in a histogram in python. Hope someone can see where I'm going wrong. Thanks.
Ah, I've realised I need to not add r_Accumulator to itself each clock cycle.
Sorry, simple solution but have been scratching my head on this for a few hours.