Генератор белого шума. Много шума из ничего. Verilog.
Генератор белого шума. Так случается иногда с каждым. Место утонченного и тщательно структурированного порядка занимает беспросветный хаос. Причем чем плотнее распределение мощности в спектре тем лучше. Не буду долго ходить вокруг да около объясняя почему это Вам просто жизненно необходимо, приведу пример кода. Большого и очень большого.
Но объяснить кое-что все же придется. Экспертное сообщество считает что лучшим алгоритмом для реализации белого шума есть алгоритм генератора Мерсенна-Твистера. Но это если у Вас очень большая микросхема FPGA, с 2-мя ручками для переноски и есть настоящий друг который поможет ее переносить. Значит Вам очень сильно необходим Генератор белого шума.
Итак, код в студию:
noise_generator.v
module noise_generator (
input wire clk,
input wire clk_en,
input wire rst,
output reg [15:0] noise_out
);
// Параметры генератора Mersenne Twister
reg [31:0] mt [0:623];
integer i;
integer index;
reg [31:0] y;
reg [31:0] seed;
initial begin
seed = 32'h12345678; // начальное значение семени
mt[0] = seed;
for (i = 1; i < 624; i = i + 1) begin
mt[i] = 32'h6c078965 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i;
end
index = 624;
end
always @(posedge clk or posedge rst) begin
if (rst) begin
index <= 624;
end else if (clk_en) begin
if (index >= 624) begin
for (i = 0; i < 624; i = i + 1) begin
y = (mt[i] & 32'h80000000) + (mt[(i + 1) % 624] & 32'h7fffffff);
mt[i] = mt[(i + 397) % 624] ^ (y >> 1);
if (y % 2 != 0) begin
mt[i] = mt[i] ^ 32'h9908b0df;
end
end
index = 0;
end
y = mt[index];
y = y ^ (y >> 11);
y = y ^ ((y << 7) & 32'h9d2c5680);
y = y ^ ((y << 15) & 32'hefc60000);
y = y ^ (y >> 18);
// Преобразование в дополнительный код
noise_out <= y[15:0];
index = index + 1;
end
end
endmodule
tb_noise_generator.v
`timescale 1ns/1ps
module tb_noise_generator;
reg clk;
reg clk_en;
reg rst;
wire [15:0] noise_out;
integer file;
noise_generator uut (
.clk(clk),
.clk_en(clk_en),
.rst(rst),
.noise_out(noise_out)
);
initial begin
$dumpfile("noise_generator.vcd"); // Имя VCD файла
$dumpvars(0, tb_noise_generator); // Имя модуля тестбенча
clk = 0;
clk_en = 1;
rst = 1;
file = $fopen("noise_output.bin", "wb");
if (file == 0) begin
$display("Error opening file!");
$finish;
end
#10 rst = 0;
#1000000 $stop;
end
always #5 clk = ~clk;
always @(posedge clk) begin
if (!rst) begin
$fwrite(file, "%c%c", noise_out[7:0], noise_out[15:8]);
end
end
initial begin
#1000000 $fclose(file);
end
endmodule
Компиляция и симуляция генератора белого шума
Компиляция Verilog файлов с помощью iverilog
:
iverilog -o noise_sim tb_noise_generator.v noise_generator.v
Запуск симуляции:
vvp noise_sim
Визуализация генератора белого шума с помощью Python
Создайте файл plot_spectrogram.py
со следующим содержимым:
import numpy as np
import matplotlib.pyplot as plt
from vcdvcd import VCDVCD
# Открытие VCD файла
vcd_path = 'noise_generator.vcd'
vcd = VCDVCD(vcd_path)
# Находим правильный путь к сигналу
signal_path = 'tb_noise_generator.uut.noise_out[15:0]'
if signal_path not in vcd.signals:
raise ValueError(f'Signal {signal_path} not found in VCD file.')
signal_data = vcd[signal_path]
# Извлекаем временные метки и значения
times = []
values = []
for tv in signal_data.tv:
# Игнорируем неопределенные значения 'x'
if tv[1] != 'x':
times.append(tv[0])
values.append(int(tv[1], 2))
# Преобразование значений к signed 16-bit integer
values = np.array(values, dtype=np.int32)
values = np.where(values > 32767, values - 65536, values)
# Убедимся, что данные не пусты
if len(values) == 0:
raise ValueError('No data found in the specified signal.')
# Построение спектрограммы
plt.figure()
plt.specgram(values, Fs=1e6)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [s]')
plt.title('Spectrogram of Noise Signal from VCD')
plt.colorbar(label='Intensity [dB]')
# Чтение бинарного файла
bin_file_path = 'noise_output.bin'
with open(bin_file_path, 'rb') as f:
bin_data = f.read()
# Преобразование бинарных данных в массив numpy
bin_values = np.frombuffer(bin_data, dtype=np.int16)
# Убедимся, что данные не пусты
if len(bin_values) == 0:
raise ValueError('No data found in the binary file.')
# Построение спектрограммы для данных из бинарного файла
plt.figure()
plt.specgram(bin_values, Fs=1e6)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [s]')
plt.title('Spectrogram of Noise Signal from Binary File')
plt.colorbar(label='Intensity [dB]')
plt.show()
Запустите скрипт Python для визуализации спектрограммы генератора белого шума:
python3 plot_spectrogram.py
Будут ли ошибки при компиляции в Вашем случае? Скорее всего да. Дорогу осилит идущий. Продолжение следует, и следующий раз будет младший брат этого генератора белого шума, занимающий мало места и весьма неплохо живущий.
Чтобы создать генератор шума высокого качества, который поместится в не более чем 1000 логических ячеек FPGA и использует алгоритм, аналогичный Mersenne Twister, можно рассмотреть более простые, но все же эффективные методы, такие как комбинированные линейные конгруэнтные генераторы (Combined Linear Congruential Generators, CLCG). Этот метод требует меньше ресурсов, но обеспечивает достаточно качественный случайный шум. Но это конечно тоже не решение вопроса судя по результату.
Генерация гауссовского шума с помощью алгоритма, который можно реализовать на FPGA с использованием ограниченного количества логических ячеек, может быть выполнена с помощью метода Бокса-Мюллера (Box-Muller). Этот метод позволяет генерировать гауссовский шум из двух независимых равномерно распределенных случайных чисел.
Для реализации на Verilog с учетом ограниченного количества логических ячеек, нам нужно использовать эффективные способы генерации случайных чисел и вычисления логарифмических и тригонометрических функций. Возможно применить генератор случайных чисел LFSR и метод Бокса-Мюллера для генерации гауссовского шума.
Генератор белого шума на 16 LFSR :
При частоте дискретизации 38 Мгц или 19 мгц спектр шума должен быть линеен по уровню 3 Дб до частоты 2,5 Мгц
Для получения шума с требуемыми характеристиками спектра, необходимо применить несколько улучшений в генераторе шума. Основные шаги включают увеличение количества LFSR и добавление фильтра для улучшения спектральных характеристик. Цель — получить шум, который будет линейным по уровню до частоты 2,5 МГц при частоте дискретизации 38 МГц или 19 МГц.
noise_generator.v
module noise_generator (
input wire clk,
input wire clk_en,
input wire rst,
output reg [15:0] noise_out
);
// Параметры LFSR
reg [31:0] lfsr [0:15];
wire [31:0] lfsr_feedback [0:15];
integer i;
// Преобразование значений к signed 16-bit integer в двухкомплементарный код
wire signed [15:0] noise_twos_complement;
reg signed [31:0] sum;
reg signed [31:0] filtered_noise;
assign noise_twos_complement = sum[15:0];
generate
genvar j;
for (j = 0; j < 16; j = j + 1) begin : lfsr_gen
assign lfsr_feedback[j] = (lfsr[j] >> 1) ^ (-(lfsr[j] & 1) & 32'h80000057);
end
endgenerate
always @(posedge clk or posedge rst) begin
if (rst) begin
lfsr[0] <= 32'h12345678;
lfsr[1] <= 32'h87654321;
lfsr[2] <= 32'h56781234;
lfsr[3] <= 32'h43218765;
lfsr[4] <= 32'habcdef01;
lfsr[5] <= 32'hfedcba98;
lfsr[6] <= 32'h0f1e2d3c;
lfsr[7] <= 32'h4b5a6978;
lfsr[8] <= 32'h78912345;
lfsr[9] <= 32'h98765432;
lfsr[10] <= 32'h23456789;
lfsr[11] <= 32'h34567890;
lfsr[12] <= 32'h45678901;
lfsr[13] <= 32'h56789012;
lfsr[14] <= 32'h67890123;
lfsr[15] <= 32'h78901234;
sum <= 0;
filtered_noise <= 0;
end else if (clk_en) begin
for (i = 0; i < 16; i = i + 1) begin
lfsr[i] <= lfsr_feedback[i];
end
// Суммирование значений для генерации Gaussian шума
sum <= lfsr[0][15:0] + lfsr[1][15:0] + lfsr[2][15:0] + lfsr[3][15:0] +
lfsr[4][15:0] + lfsr[5][15:0] + lfsr[6][15:0] + lfsr[7][15:0] +
lfsr[8][15:0] + lfsr[9][15:0] + lfsr[10][15:0] + lfsr[11][15:0] +
lfsr[12][15:0] + lfsr[13][15:0] + lfsr[14][15:0] + lfsr[15][15:0];
// Корректирующий фильтр для улучшения спектра шума
filtered_noise <= (filtered_noise + sum) >> 1;
noise_out <= filtered_noise[15:0];
end
end
endmodule
Этот код включает простейший корректирующий фильтр (цифровой фильтр первого порядка), который помогает сгладить спектр шума и уменьшить завал нижних частот.
tb_noise_generator.v
module tb_noise_generator;
reg clk;
reg clk_en;
reg rst;
wire [15:0] noise_out;
noise_generator uut (
.clk(clk),
.clk_en(clk_en),
.rst(rst),
.noise_out(noise_out)
);
// Генерация тактового сигнала
initial begin
clk = 0;
forever begin
#5 clk = ~clk; // 100 MHz clock
end
end
integer file;
initial begin
// Открытие файла для бинарного вывода
file = $fopen("noise_output.bin", "wb");
// Инициализация входов
clk_en = 1;
rst = 1;
#10;
rst = 0;
// Запуск симуляции на заданное время
#1000000; // 1ms
// Запись шума в файл
forever begin
@(posedge clk);
if (clk_en && !rst) begin
// Запись данных в файл в формате 16-bit little-endian
$fwrite(file, "%c%c", noise_out[7:0], noise_out[15:8]);
end
end
// Закрытие файла
$fclose(file);
$stop;
end
// Генерация VCD файла
initial begin
$dumpfile("noise_generator.vcd");
$dumpvars(0, tb_noise_generator);
end
endmodule
Оценка количества логических ячеек
Используйте Yosys для оценки количества логических ячеек:
yosys -p "synth_altera -top noise_generator"
После этого вы можете использовать следующий командный файл для оценки:
read_verilog noise_generator.v
synth_altera -top noise_generator
stat
Генератор белого шума. Симуляция и тестирование
Запустите симуляцию, чтобы убедиться, что спектр шума соответствует требованиям. Если завал нижних частот остается, можно дополнительно подкорректировать фильтр или параметры LFSR.