当前位置:   article > 正文

基于伪随机数LFSR和Box Muller变换的可综合的高斯白噪声FPGA生成器,AD/DA回环输出采集HDMI显示(已通过matlab、仿真和板级验证)_fpga产生白噪声模块

fpga产生白噪声模块

硬件平台

基于XLINX公司生产的AX7035开发板,具有HDMI输出输出,可以满足在没有示波器条件下输入输出回环测试。项目中仅使用了ROM ip核用来存储查找表计算根号、对数、cos、sin,可以移植到其他任意开发中,但HDMI输出波形可能无法观测到,只能通过示波器显示。

设计内容

设计内容主要分为两部分:高斯分布序列产生和HDMI显示。该项目侧重点是高斯白噪声产生,我主要介绍LFSR序列发生器和Box Muller转换设计思路。

LFSR伪随机数生成

该模块产生32位均匀分布序列,循环周期是2^64 = 1.8*10^19 。

利用64位斐波那契型LFSR,反馈多项式为 x^64 + x^63 + x^61 + x^60 + 1,可通过调用函数改变初始种子seed参数以产生不同的随机序列。

同时修改参数中的MIN和MAX大小设置输出随机序列的范围,理论上可以设置为-2,147,483,648——+2,147,483,648,也就是±2^31。

  1. module LFSR_fibonacci #(
  2. parameter seed = 64'd18446744070000000000,
  3. parameter MIN = 1,
  4. parameter MAX = 100
  5. )(
  6. input wire clk,
  7. input wire rst_n,
  8. input wire enable,
  9. output wire [31:0] rand_out
  10. );
  11. wire signed [63:0] rand64;
  12. wire signed [31:0] rand_out1;
  13. wire signed [31:0] rand_out2;
  14. reg [63:0] lfsr_reg;
  15. always @(posedge clk or posedge rst_n) begin
  16. if (!rst_n) begin
  17. lfsr_reg <= seed; // 初始值可以根据需要设置为其他值
  18. end else if (enable) begin
  19. // LFSR 反馈多项式为 x^64 + x^63 + x^61 + x^60 + 1
  20. lfsr_reg <= {lfsr_reg[62:0], lfsr_reg[63] ^ lfsr_reg[60] ^ lfsr_reg[61] ^ lfsr_reg[63]};
  21. end
  22. end
  23. assign rand64 = lfsr_reg;
  24. assign rand_out1 = rand64[47:16] + {rand64[15:0],rand64[63:48]};
  25. assign rand_out2 = rand64[31:0] + rand64[63:32];
  26. assign rand_out = (rand_out1 + rand_out2) % (MAX - MIN+1'b1) + MIN;
  27. endmodule

注意,我将LFSR设置为64位,而输出是经过64位随机序列变换得到,因此产生的随机序列周期是64位线性移位寄存器产生的伪随机数周期,但输出位数为32位。如果有对于资源要求比较高可以减小线性移位寄存器位数,通过查找响应位数特征多项式(不同位数有不同的特征多项式,这决定了产生随机序列的周期,使用特征多项式可以达到最大周期2……N-1)。

对于输出变换的解释,由LFSR直接产生的随机序列在一段时间内小数特别多,不符合我们对于均匀随机序列的要求,具体可编写测试文件观测到。因此,我对输出64位随机序列进行变换,这里我用两个rand_out1和rand_out2相加的结果取模,rand_out1和rand_out2分别是rand64取不同位数得到。当然,这里的方法不唯一,感兴趣的可以自行设计。

同时,需要注意的是,在输出级采用MAX和MIN对输出范围做了限制,输出的随机序列最大最小值均是有符号数!想要获得任意范围定点数可以自行另外设计输出逻辑。

更详细的LFSR设计原理和结构可以参照

https://cloud.tencent.com/developer/article/2287083?areaId=106001

Box-Muller转换产生高斯分布序列

Box-Muller transformation算法有两种形式,一种称之为基本形式:

其中U1,U2是均匀分布的随机数,Z1和Z2是满足高斯分布的随机数。可以看出,要得到高斯分布序列需要两个均匀分布序列,同时经过算数运算得到。

另一种表达形式是极坐标形式我没不使用,原因是极坐标形式Box-Muller转换虽然不涉及cos、sin运算,但引入了除法,这在FPGA设计中需要占用较多逻辑资源,我就使用基本形式对其变换。

利用使用ROM查找表,设计ln、根号、正余弦计算器。根据输入变量大小实现地址映射,考虑到设计精度等因素,对输入输出进行一定比例放大。对于cos只需要存储1/4周期的数据,其余数据均可通过地址映射得到这里不展开细讲。

  1. module cos(
  2. input clk,
  3. input [15:0] x_in,
  4. input enable,
  5. output [15:0] result_out
  6. );
  7. wire [9:0] addr_ROM;
  8. wire [13:0] ROM_out;
  9. cos_ROM cos_ROM_0(
  10. .clka (~clk),
  11. .ena (enable),
  12. .addra (addr_ROM),
  13. .douta (ROM_out)
  14. );
  15. wire [15:0] angle_3600;
  16. assign angle_3600 = (x_in[15] == 1'b1) ? ((~x_in[15:0]+1'b1) % 16'd3600) : (x_in[15:0] % 16'd3600);
  17. wire sign;
  18. wire [15:0] addr_angle;
  19. assign addr_angle = (angle_3600 <= 16'd900) ? (angle_3600 ) :
  20. (angle_3600 < 16'd1800) ? (16'd1800 - angle_3600) :
  21. (angle_3600 < 16'd2700) ? (angle_3600 - 16'd1800) :
  22. (angle_3600 < 16'd3600) ? (16'd3600 - angle_3600) : 16'd901;
  23. assign sign = (angle_3600 <= 16'd900) ? (1'b0 ) :
  24. (angle_3600 < 16'd1800) ? (1'b1 ) :
  25. (angle_3600 < 16'd2700) ? (1'b1 ) :
  26. (angle_3600 < 16'd3600) ? (1'b0 ) : 1'b0;
  27. assign addr_ROM = addr_angle[9:0];
  28. reg sign_delay0;
  29. always @(posedge clk) begin
  30. sign_delay0 <= sign;
  31. end
  32. assign result_out = sign_delay0 ? (~{2'b0,ROM_out} + 1'b1): {2'b0,ROM_out}; //
  33. endmodule

输入是角度制,同时需要将角度放大10倍,也就是3600是一个周期。输出16位有符号数,为是结果放大10000倍。

另外,是利用流水线的计算模式,ln和cos是第一级流水线,ln计算的结果乘以-2后再取根号,因此需要把cos计算的结果延迟一个节拍,再相乘输出。

Box Muller 转换流水线代码如下

  1. module Box_Muller_transform(
  2. input wire clk_data,
  3. input wire clk_cal,
  4. input wire enable,
  5. input wire signed [15:0] data_in_1,
  6. input wire signed [15:0] data_in_2,
  7. output reg signed [31:0] result_out_0,
  8. output reg signed [31:0] result_out_1
  9. );
  10. wire signed [15:0] cos_sin_in;
  11. wire signed [15:0] sqrt_in;
  12. wire signed [15:0] sqrt_out, ln_out, cos_out, sin_out;
  13. reg signed [15:0] cos_out_delay, sin_out_delay;
  14. reg signed [15:0] x_in_1, x_in_2;
  15. always @(posedge clk_data) begin
  16. if (enable) begin
  17. x_in_1 <= data_in_1;
  18. x_in_2 <= data_in_2;
  19. end
  20. end
  21. // instantiate sqrt module
  22. assign sqrt_in = ln_out * (-16'd2);
  23. sqrt sqrt_inst (
  24. .clk(clk_cal),
  25. .enable(enable),
  26. .x_in(sqrt_in),
  27. .result_out(sqrt_out)
  28. );
  29. // instantiate ln module
  30. ln ln_inst (
  31. .clk(clk_cal),
  32. .enable(enable),
  33. .x_in(x_in_1),
  34. .result_out(ln_out)
  35. );
  36. // instantiate cos module
  37. assign cos_sin_in = x_in_2 * 16'd36 / 16'd10;
  38. cos cos_inst (
  39. .clk(clk_cal),
  40. .enable(enable),
  41. .x_in(cos_sin_in),
  42. .result_out(cos_out)
  43. );
  44. sin sin_inst (
  45. .clk(clk_cal),
  46. .enable(enable),
  47. .x_in(cos_sin_in),
  48. .result_out(sin_out)
  49. );
  50. // delay cos result
  51. always @(posedge clk_cal) begin
  52. if (enable) begin
  53. cos_out_delay <= cos_out;
  54. sin_out_delay <= sin_out;
  55. end
  56. end
  57. // calculate final result using your formula
  58. always @(posedge clk_data) begin
  59. if (enable) begin
  60. result_out_0 <= cos_out_delay * sqrt_out;
  61. result_out_1 <= sin_out_delay * sqrt_out;
  62. end
  63. end
  64. endmodule

高斯序列产生

该模块例化了两个LFSR随机数生成模块(利用不同的种子)和一个Box Muller转换模块,将两个均匀分布序列转换为高斯分布序列。

同时,可以在模块参数中修改均值和方差,以达到产生任意分布正态分布的作用。默认为标准正态分布。

  1. /默认产生32位有符号数,结果除以1M为实际值
  2. module Guassian_generate #(
  3. parameter average = 32'd0, //均值
  4. parameter variance = 32'd0 //方差
  5. )(
  6. //input sys_clk,
  7. input clk_data,
  8. input clk_tra,
  9. input rst_n,
  10. input enable,
  11. debug
  12. //output wire [31:0] rand_out_1,
  13. //output wire [31:0] rand_out_2,
  14. output wire signed [31:0] rand_out1,
  15. output wire signed [31:0] rand_out2
  16. );
  17. wire [31:0] rand_out_1, rand_out_2;
  18. LFSR_fibonacci #(
  19. .seed(64'd18446744070000000000),
  20. .MIN(32'd1),
  21. .MAX(32'd1000)
  22. ) Uniform_Distribution_generator_0(
  23. .clk(clk_data),
  24. .rst_n(rst_n),
  25. .enable(1'b1),
  26. .rand_out(rand_out_1)
  27. );
  28. LFSR_fibonacci #(
  29. .seed(64'd14742147901846518232),
  30. .MIN(32'd1),
  31. .MAX(32'd1000)
  32. ) Uniform_Distribution_generator_1(
  33. .clk(clk_data),
  34. .rst_n(rst_n),
  35. .enable(1'b1),
  36. .rand_out(rand_out_2)
  37. );
  38. //将均匀分布输出数据转化为16位输入
  39. wire [15:0] tra_data_in_1, tra_data_in_2;
  40. assign tra_data_in_1 = rand_out_1[15:0];
  41. assign tra_data_in_2 = rand_out_2[15:0];
  42. wire signed [31:0] Guassian_0;
  43. wire signed [31:0] Guassian_1;
  44. Box_Muller_transform Box_Muller_transform(
  45. .clk_data(clk_data),
  46. .clk_cal(clk_tra),
  47. .enable(1'b1),
  48. .data_in_1(tra_data_in_1),
  49. .data_in_2(tra_data_in_2),
  50. .result_out_0(Guassian_0),
  51. .result_out_1(Guassian_1)
  52. );
  53. assign rand_out1 = enable ? ((Guassian_0[31] == 1'b1) ? (~((~Guassian_0 + 1'b1) / variance ) + 1'b1) + average : Guassian_0 / variance + average) : 32'd0;
  54. assign rand_out2 = enable ? ((Guassian_1[31] == 1'b1) ? (~((~Guassian_1 + 1'b1) / variance ) + 1'b1) + average : Guassian_1 / variance + average) : 32'd0;
  55. endmodule

注意该模块默认产生32位有符号数,结果除以1000000为实际值。这里实际值是标准正态分布的结果,因为根据Box Muller转换,输入均匀分布的范围是0-1,我利用LFSR产生1-1000的随机数,相当与输入放大了1000倍。

仿真测试

测试LFSR伪随机数生成均匀分布序列

使用vivado内置仿真环境

matlab仿真

延长仿真时间生成10M个样本

LFSR仿真测试代码和MATLB验证代码同下正态高斯测试一样,只需稍加修改即可。

高斯序列产生测试Verilog代码

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/01/14 12:37:28
  7. // Design Name:
  8. // Module Name: Guassian_generate_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module Guassian_generate_tb;
  22. reg clk_data;
  23. reg clk_tra;
  24. reg rst_n;
  25. reg enable;
  26. wire signed [31:0] rand_out1;
  27. Guassian_generate#(
  28. .average(32'd0),
  29. .variance(32'd0)
  30. )Guassian_generate_inst (
  31. .clk_data(clk_data),
  32. .clk_tra(clk_tra),
  33. .rst_n(rst_n),
  34. .enable(enable),
  35. .rand_out1(rand_out1)
  36. );
  37. wire [7:0] rand;
  38. assign rand = rand_out1[7:0];
  39. // Clock generation
  40. initial begin
  41. clk_tra = 0;
  42. forever #3 clk_tra = ~clk_tra;
  43. end
  44. initial begin
  45. clk_data = 0;
  46. #7;
  47. forever #10 clk_data = ~clk_data;
  48. end
  49. integer Guss0;
  50. //integer Guss1;
  51. initial begin
  52. Guss0=$fopen("randGuss0.txt"); //打开所创建的文件
  53. // Guss1=$fopen("randGuss1.txt"); //打开所创建的文件
  54. // rand0=$fopen("rand0.txt"); //打开所创建的文件
  55. // rand1=$fopen("rand1.txt"); //打开所创建的文件
  56. end
  57. always @(negedge clk_data) begin
  58. if(enable) begin
  59. $fdisplay(Guss0,"%d",$signed(rand_out1)); //$fdisplay(dout_file,"%d",$signed(num)); //保存有符号数据
  60. // $fdisplay(Guss1,"%d",$signed(Z_standard1)); //$fdisplay(dout_file,"%d",$signed(num)); //保存有符号数据
  61. // $fdisplay(rand0,"%d",$signed(rand_out_1)); //$fdisplay(dout_file,"%d",$signed(num)); //保存有符号数据
  62. // $fdisplay(rand1,"%d",$signed(rand_out_2)); //$fdisplay(dout_file,"%d",$signed(num)); //保存有符号数据
  63. end
  64. end
  65. // 在仿真结束时关闭文件
  66. initial begin
  67. #10000000; // 确保仿真足够长的时间
  68. $fclose(Guss0);
  69. // $fclose(Guss1);
  70. // $fclose(rand0);
  71. // $fclose(rand1);
  72. $stop;
  73. end
  74. // Testbench stimulus
  75. initial begin
  76. rst_n = 0;
  77. enable = 0;
  78. #100
  79. rst_n =1;
  80. #100
  81. enable = 1;
  82. // Finish simulation
  83. #10000000;
  84. end
  85. endmodule

将测试文件生成的随机数存储在randGuss0.txt文件中,再通过matlab读取进行分析。

可以看出产生的随机序列为一个标准的正态分布!

matlab测试代码如下。注:这里没有使用绝对路径查看txt文件,'randGuss0.txt'使用相对路径保存的文件再sim目录下,请自行查找。

  1. %生成rand64向量
  2. vivado_data_Guss0 = importdata('randGuss0.txt'); %read files
  3. % 统计数据分布
  4. minValue = min(vivado_data_Guss0);
  5. maxValue = max(vivado_data_Guss0);
  6. meanValue = mean(vivado_data_Guss0);
  7. medianValue = median(vivado_data_Guss0);
  8. stdDeviation = std(vivado_data_Guss0);
  9. % 显示统计结果
  10. fprintf('最小值:%f\n', minValue);
  11. fprintf('最大值:%f\n', maxValue);
  12. fprintf('平均值:%f\n', meanValue);
  13. fprintf('中位数:%f\n', medianValue);
  14. fprintf('标准差:%f\n', stdDeviation);
  15. % 绘制直方图
  16. figure(1)
  17. histogram(vivado_data_Guss0, 100, 'Normalization', 'probability');
  18. title('guss1数据分布直方图');
  19. xlabel('数值');
  20. ylabel('频率');

DC输出高斯分布序列AD采集,HDMI显示

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/673015
推荐阅读
相关标签
  

闽ICP备14008679号