DocsTech
/
ЦОС
/

~ cd высокочастотный цифровой фильтр в последовательной форме на плис

Задача

Необходимо реализовать цифровой фильтр со следующими свойствами:

  1. Низкая чувствительность при высоких порядках фильтра
  2. Сохранить быстродействие
  3. Максимальное отклонения n% от максимальной амплитуды
  4. Максимальная высота гармоники m% от от амплитуды
  5. Максимальная точность при округлении

Решение

Под все выше сказанные параметры подходит эллиптический фильтр в последовательной формы.

Цифровые фильтры, реализованные в последовательной(каскадной) форме уменьшают нежелательные эффекты:

  1. Разделение полюсов и нулей: В последовательной форме сложный фильтр разбивается на каскад нескольких более простых фильтров малого порядка. При таком подходе амплитуды коэффициентов становятся меньше, что снижает влияние ошибок округления. При реализации фильтра в последовательной форме, коэффициенты каждого блока могут быть более простыми и лучше масштабированы. Это уменьшает требование к точности вычислений и, следовательно, уменьшает ошибки округления.
  2. Численная стабильность: Более простые блоки малого порядка, из которых строится каскад, имеют лучшее соотношение чисел при представлении в фиксированной точке. Это улучшает численную стабильность вычислений и снижает вероятность накопления ошибок округления. В последовательной форме ошибки округления влияют на выход одного блока, прежде чем перейти к следующему блоку.

Максимальные отклонения от максимальной амплитуды и высоту гармоник можно регулировать с помощью эллиптического фильтра.

MATLAB

Чтобы создать MATLAB код для расчета коэффициентов 9-го порядка эллиптического фильтра с учетом максимального отклонения от амплитуды и подавления гармоник в процентах от амплитуды, необходимо преобразовать эти процентные значения в децибелы, которые используются в функции ellip.

Для выполнения расчета частоты необходимо нормализовать относительно частоты Найквиста. Ниже приведен пример кода для расчета коэффициентов эллиптического фильтра:
...
Копировать
% Порядок фильтра
N = 9;

% Максимальное отклонение от амплитуды в полосе пропускания в процентах
n = 1; % например, 1%
Rp = -20*log10(1 - n/100); % преобразование процента в децибелы

% Максимальное подавление в полосе задерживания в процентах
m = 0.1; % например, 0.1%
Rs = -20*log10(m/100); % преобразование процента в децибелы

% Частота среза
Fs = 10; % частота дискретизации
Fc = 2; % частота среза

% Нормализованная частота среза (относительно частоты Найквиста)
Wp = Fc/(Fs/2);

% Расчет коэффициентов
[b, a] = ellip(N, Rp, Rs, Wp);

% Отображение результатов
fprintf('Коэффициенты числителя (b):\n');
disp(b);
fprintf('Коэффициенты знаменателя (a):\n');
disp(a);

Пояснения:

  1. N = 9: Порядок фильтра.
  2. n: Максимальное отклонение от амплитуды в процентах. Преобразуется в децибелы для использования в Rp.
  3. m: Максимальное подавление в полосе задерживания в процентах. Преобразуется в децибелы для использования в Rs.
  4. Fs: Частота дискретизации.
  5. Fc: Частота среза.
  6. Wp = Fc/(Fs/2): Нормализованная частота среза. Частота среза нормализуется относительно частоты Найквиста (которая равна половине частоты дискретизации).

Этот код преобразует процентные значения отклонений и подавлений в децибелы, нормализует частоту среза и вычисляет коэффициенты эллиптического фильтра с использованием функции ellip.

Код для ПЛИС

У меня получился 3-х составной фильтр в последовательной форме. Ниже приведу пояснения для реализации на высоких частотах.

Параметры Модуля верхнего уровня:

  1. DW — битовая ширина линии данных
  2. CW — битовая ширина коэффициентов
  3. CL — общее кол-во коэффициентов
  4. OW — битовая ширина выходной линии
  5. LREGS — дополнительные биты против потери
  6. C{i}P — количество коэффициентов в i составной части
  7. C{i}PK — кол-во b коэффициентов в i составной части
  8. C{i}PK{j} — коэффициент в i составной части j порядковым номером
Необязательно задавать коэффициенты через параметры! Можно объявить массив для i составной части и через начальный процедурный блок присвоить им значения:
...
Копировать
initial begin
	$readmemh("config1patch.hex", coef1p);
	$readmemh("config2patch.hex", coef2p);
	$readmemh("config3patch.hex", coef3p);
end

Обход по составным частям переделать через блок generate и for.

Сигналы Модуля верхнего уровня:

  1. o_result — результирующий сигнал
  2. i_arst — сброс всех регистров
  3. i_clk — тактовый сигнал
  4. i_data_wr — сигнал записи в регистры
  5. i_data — информационный сигнал

Параметры модуля firacc:

  1. DW — битовая ширина линии данных
  2. CW — битовая ширина коэффициентов
  3. SUBREGS — дополнительные биты против потери
  4. KICH — Наличие коэффициентов b составной части
  5. BICH — Наличие коэффициентов a составной части
  6. FIRST — Первое объявление составной части

Сигналы модуля firacc:

  1. o_acc — рассчитанная сумма
  2. o_datak — передача линии z^(-n + 1) в коэф b
  3. o_datab — передача линии в z^(-n + 1) в коэф a
  4. i_datak — предыдущая линия z^(-n — 1) в коэф b
  5. i_datab — предыдущая линия z^(-n — 1) в коэф a
  6. i_data_wr — сигнал записи
  7. i_data_rst — сигнал общего сброса
  8. i_clk — тактовый сигнал
  9. i_koefk — коэф b
  10. i_koefb — коэф a
  11. i_patch_acc — прошлая сумма
Транспонированная форма цифрового фильтра
Рисунок 1. RTL Каноническая форма составной часть фильтра в последовательной форме

Код написан на языке Verilog:

...
Копировать
`default_nettype	none
module ffir #(
	parameter 							DW = 8, CW = 8, CL = 9
) ( o_result, i_arst, i_clk, i_data_wr, i_data);

	localparam 							OW = DW + CW + $clog2(CL);
	localparam							LREGS = OW - (DW + CW);
	localparam 							C1P = 6;
	localparam 							C1PK = 3;
	localparam 							C2P = 3;
	localparam 							C2PK = 1;
	
	localparam							C1PK0 = 63;
	localparam							C1PK1 = 36;
	localparam							C1PK2 = 24;
	localparam							C1PK3 = 36;
	localparam							C1PK4 = 82;
	localparam							C1PK5 = 94;
	localparam							C2PK0 = 3;
	localparam							C2PK1 = 73;
	localparam							C2PK2 = 86;
	
	output wire signed	[(OW - 1): 0]	o_result;
	
	input wire							i_arst, i_clk, i_data_wr;
	input wire signed 	[(DW - 1) : 0]	i_data;
	
	wire signed			[(OW): 0] 		o_acc_f1 [(C1P - C1PK + 1): 0];
	wire signed			[(DW): 0] 		o_data_fk1 [(C1P - C1PK + 1): 0];
	wire signed			[(DW): 0] 		o_data_fb1 [(C1P - C1PK + 1): 0];
	
	wire signed			[(OW): 0] 		o_acc_f2 [(C2P - C2PK + 1): 0];
	wire signed			[(OW): 0] 		o_data_fk2 [(C2P - C2PK + 1): 0];
	wire signed			[(OW): 0] 		o_data_fb2 [(C2P - C2PK + 1): 0];
	
	assign o_acc_f1[0] = 0;
	assign o_data_fk1[0] = i_data;
	assign o_data_fb1[0] = 0;
	
	assign o_acc_f2[0] = 0;
	assign o_data_fk2[0] = o_acc_f1[(C1P - C1PK)];
	assign o_data_fb2[0] = 0;
	
	assign o_result = o_acc_f2[(C2P - C2PK)];
	
	firacc #(
		.DW(DW), .CW(CW), .SUBREGS(LREGS), .KICH(1), .BICH(1), .FIRST(1), .OW(OW)
	) accpath1_1 (
		.o_acc(o_acc_f1[1]), .o_datak(o_data_fk1[1]), .o_datab(o_data_fb1[1]),
		.i_datak(o_data_fk1[0]), .i_datab(o_data_fb1[0]), .i_data_wr(i_data_wr), .i_data_rst(i_arst), .i_clk(i_clk),
		.i_koefk(C1PK0), .i_koefb(C1PK3), .i_patch_acc(o_acc_f1[0])
	);
	firacc #(
		.DW(DW), .CW(CW), .SUBREGS(LREGS), .KICH(1), .BICH(1), .FIRST(0), .OW(OW)
	) accpath1_2 (
		.o_acc(o_acc_f1[2]), .o_datak(o_data_fk1[2]), .o_datab(o_data_fb1[2]),
		.i_datak(o_data_fk1[1]), .i_datab(o_data_fb1[1]), .i_data_wr(i_data_wr), .i_data_rst(i_arst), .i_clk(i_clk),
		.i_koefk(C1PK1), .i_koefb(C1PK4), .i_patch_acc(o_acc_f1[1])
	);
	firacc #(
		.DW(DW), .CW(CW), .SUBREGS(LREGS), .KICH(1), .BICH(1), .FIRST(0), .OW(OW)
	) accpath1_3 (
		.o_acc(o_acc_f1[3]), .o_datak(o_data_fk1[3]), .o_datab(o_data_fb1[3]),
		.i_datak(o_data_fk1[2]), .i_datab(o_data_fb1[2]), .i_data_wr(i_data_wr), .i_data_rst(i_arst), .i_clk(i_clk),
		.i_koefk(C1PK2), .i_koefb(C1PK5), .i_patch_acc(o_acc_f1[2])
	);
	
	
	firacc #(
		.DW(OW), .CW(CW), .SUBREGS(0), .KICH(1), .BICH(1), .FIRST(1), .OW(OW)
	) accpath2_1 (
		.o_acc(o_acc_f2[1]), .o_datak(o_data_fk2[1]), .o_datab(o_data_fb2[1]),
		.i_datak(o_data_fk2[0]), .i_datab(o_data_fb2[0]), .i_data_wr(i_data_wr), .i_data_rst(i_arst), .i_clk(i_clk),
		.i_koefk(C2PK0), .i_koefb(C2PK1), .i_patch_acc(o_acc_f2[0])
	);
	firacc #(
		.DW(OW), .CW(CW), .SUBREGS(0), .KICH(0), .BICH(1), .FIRST(0), .OW(OW)
	) accpath2_2 (
		.o_acc(o_acc_f2[2]), .o_datak(o_data_fk2[2]), .o_datab(o_data_fb2[2]),
		.i_datak(o_data_fk2[1]), .i_datab(o_data_fb2[1]), .i_data_wr(i_data_wr), .i_data_rst(i_arst), .i_clk(i_clk),
		.i_koefk(0), .i_koefb(C2PK2), .i_patch_acc(o_acc_f2[1])
	);
	
endmodule

module firacc #(
	parameter DW = 8, CW = 8, SUBREGS = 5, KICH = 1, BICH = 1, FIRST = 0, OW = MW + SUBREGS
) ( o_acc, o_datak, o_datab, i_datak, i_datab, i_clk, i_data_wr, i_data_rst, i_koefk, i_koefb, i_patch_acc);

	localparam 							MW = DW + CW;

	output reg signed	[(OW - 1): 0]	o_acc;
	output reg signed	[(DW - 1): 0]	o_datak;
	output reg signed	[(DW - 1): 0]	o_datab;
	
	input wire signed	[(CW - 1): 0]	i_koefk;
	input wire signed	[(CW - 1): 0]	i_koefb;
	input wire signed	[(DW - 1): 0]	i_datak;
	input wire signed	[(DW - 1): 0]	i_datab;
	input wire 							i_data_wr, i_data_rst, i_clk;
	input wire signed	[(OW - 1): 0]	i_patch_acc;
	
	wire signed 		[(MW - 1): 0]	m_data;
	wire signed			[(OW - 1): 0]	accum;
	
	reg signed 			[(OW - 1): 0]	delay_trig1;
	reg signed 			[(DW - 1): 0]	delay_trig2;
	
	initial begin
		delay_trig1 <= 0;
		delay_trig2 <= 0;
		o_acc <= 0;
	end
	
	// data kich
	generate
		if (KICH == 1) begin
			always @(posedge i_clk, posedge i_data_rst)
				if (i_data_rst) begin
					delay_trig1 <= 0;
					o_datak <= 0;
				end else begin
					if (i_data_wr) begin
						delay_trig1 <= i_datak;
						o_datak <= delay_trig1;
					end
				end
		end
	endgenerate
			
	//data bich
	generate
		if (BICH == 1) begin
			always @(posedge i_clk, posedge i_data_rst)
				if (i_data_rst) begin
					delay_trig2 <= 0;
					o_datab <= 0;
				end else begin
					if (i_data_wr) begin
						if (FIRST)
							delay_trig2 <= accum;
						else
							delay_trig2 <= i_datab;
						o_datab <= delay_trig2;
					end
				end
		end
	endgenerate
	
	always @(posedge i_clk, posedge i_data_rst)
			if (i_data_rst)
				o_acc <= 0;
			else if (i_data_wr)
				o_acc <= accum;
	
	generate
		if (FIRST == 1)
			assign m_data = i_koefk * i_datak;
		else
			if (KICH == 1) begin
				if (BICH == 1)
					assign m_data = i_koefk * i_datak + i_koefb * i_datab;
				else
					assign m_data = i_koefk * i_datak;
			end else
				assign m_data = i_koefb * i_datab;
	endgenerate
	
	assign accum = i_patch_acc + { {(OW - MW){m_data[(MW-1)]}}, m_data };
	
endmodule 
Результат компиляции последовательного фильтра
Рисунок 1. Результат компиляции фильтра.
Главная
Курсы
Вебинары
Шифрование AES: Что такое, как работает и почему это лучший выбор для безопасности данных
Введение в FSM (Конечные Автоматы) в FPGA Verilog
Latency в ЦОС: как её минимизировать
8 наиболее используемых Контрольных Сумм
Помехоустойчивое кодирование: описание, типы, применение
Высокочастотный цифровой фильтр в последовательной форме на ПЛИС
Амплитудная модуляция(AM): Что это такое?
Балансный Фазовый Детектор на Verilog
Фазовый детектор: описание, схемы и принцип работы
БИХ (Рекурсивный) Фильтр: Что это такое?
КИХ фильтр: описание и примеры на ПЛИС, stm32 и esp32
Фазовый накопитель: применение в ПЛИС и STM32 и ESP32
Цифровая Обработка Сигналов: введение
Закрыть