Перейти к содержанию
    

Фильтр скользящее среднее

Доброго времени суток!

Нужна помощь в оптимизации кода на Verilog, среда разработки Quartus II 13.0.1 , плисина Циклон 3.

Вход/выход 16-битный, клок 120МГц

Размер окна фильтра на 16 точек

 

module MovAverageFilter(clk,datain,dataout);

input  clk;
input  [15:0] datain;
output reg [15:0] dataout;


reg [15:0] FIFO [0:15];
integer i;

initial begin
	for(i = 15; i >= 0; i=i-1) FIFO[i] = 16'd0;
	end


always@(posedge clk) begin

	dataout <= (FIFO[0]+FIFO[1]+FIFO[2]+FIFO[3]+FIFO[4]+FIFO[5]+FIFO[6]+FIFO[7]+FIFO[8]+FIFO[9]+ 		FIFO[10]+FIFO[11]+FIFO[12]+FIFO[13]+FIFO[14]+FIFO[15])>>4;	

	end

	
always@(negedge clk) begin
      
	for(i = 15; i > 0; i=i-1) FIFO[i] <= FIFO[i-1]; 	
	FIFO[0] <= datain;	
	end

endmodule

 

Есть желание увеличить размер окна с 16 до 32 или даже до 64 но тогда нужно будет больше сумматоров и я беспокоюсь за обеспечение синхронности схемы. Может есть более изящный вариант брать сумму точек в окне и делить на их кол-во?

 

Изменено пользователем Halfback

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

2 hours ago, Halfback said:

Есть желание увеличить размер окна с 16 до 32 или даже до 64 но тогда нужно будет больше сумматоров и я беспокоюсь за обеспечение синхронности схемы. Может есть более изящный вариант брать сумму точек в окне и делить на их кол-во?

Для  скользящего  среднего при котором отсчеты приходят по одному  достаточно  одного сложения и одного вычитания  для окна любой длинны. :yes3:

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неправильный алгоримт: каждый раз используется уйма вычислений. А ведь достаточно завести кольцевой буфер! В текущую ячейку помещаем новое значение и инкрементируем номер, на последнем номере переводим счетчик в нуль. "Автоматически" новое значение всегда будет в конце буфера.

Да и еще ускорить можно: завести отдельную переменную для общей суммы. В этом случае перед тем, как закинуть новое значение в кольцевой буфер, сначала вычитаем из суммы старое, затем запихиваем в буфер новое и добавляем его к сумме. Экономим очень прилично!

Со скользящей медианой чуть сложней, но там тоже на кольцевом буфере все завязано (а для построения qsort используется массив указателей).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 часов назад, RobFPGA сказал:

Для  скользящего  среднего при котором отсчеты приходят по одному  достаточно  одного сложения и одного вычитания  для окна любой длинны. :yes3:

Наверное я правильно понял что вот это

dataout <= (FIFO[0]+FIFO[1]+FIFO[2]+FIFO[3]+FIFO[4]+FIFO[5]+FIFO[6]+FIFO[7]+FIFO[8]+FIFO[9]+FIFO[10]+FIFO[11]+FIFO[12]+FIFO[13]+FIFO[14]+FIFO[15])>>4;

надо заменить на вот это

dataout <= (dataout + FIFO[0]-FIFO[15])>>4;

Другой вопрос что в Си-подобном языке dataout справа в скобках будет расценено как предыдущее значение а слева - как текущее значение. Так ли это будет в верилоге? (Я всё равно в железе проверю)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

по заданию требуется возможность генерации модуля с разным размером окна и разрешены размеры окна по степеням двойки, поэтому в модуле можно в качестве параметра задавать степень (FIFO_LEN_LOG) и на основе ее рассчитывать размер окна (FIFO_LEN)

    parameter FIFO_LEN_LOG = 4;
    localparam FIFO_LEN = (1 << FIFO_LEN_LOG);

обьявление, инициализация и работа внутренней памяти (FIFO) останется прежней, но с учетом новых параметров

    reg [15:0] FIFO [0:FIFO_LEN-1];
    integer i;

    initial
    begin
        for(i = FIFO_LEN-1; i >= 0; i=i-1)
        begin
            FIFO[i] = 16'd0;
        end
    end
    
    always @(posedge clk)
    begin

        for(i = FIFO_LEN-1; i > 0; i=i-1)
        begin
            FIFO[i] <= FIFO[i-1];
        end
        
        FIFO[0] <= datain;
        
    end

как было замечено выше в обсуждении темы, для вычисления результата суммирования (fifo_sum) с использованием предыдущего результата рассчета количество сложений может быть уменьшено

    reg [FIFO_LEN_LOG+15:0] fifo_sum = 0;
    
    always @(posedge clk)
    begin
        fifo_sum <= fifo_sum + datain - FIFO[FIFO_LEN-1];    
    end

деление результата суммирования также можно записать с учетом параметров

    assign dataout = fifo_sum >> FIFO_LEN_LOG;

весь модуль у меня получился таким

Spoiler



module MovAverageFilter
#(
    parameter FIFO_LEN_LOG = 4//,
)
(
    input  clk,
    input  [15:0] datain,
    output [15:0] dataout//,
);
    localparam FIFO_LEN = (1 << FIFO_LEN_LOG);
    
    reg [15:0] FIFO [0:FIFO_LEN-1];
    integer i;

    initial
    begin
        for(i = FIFO_LEN-1; i >= 0; i=i-1)
        begin
            FIFO[i] = 16'd0;
        end
    end
    
    always @(posedge clk)
    begin

        for(i = FIFO_LEN-1; i > 0; i=i-1)
        begin
            FIFO[i] <= FIFO[i-1];
        end
        
        FIFO[0] <= datain;
        
    end
    
    reg [FIFO_LEN_LOG+15:0] fifo_sum = 0;
    
    always @(posedge clk)
    begin
        fifo_sum <= fifo_sum + datain - FIFO[FIFO_LEN-1];	
    end
    
    assign dataout = fifo_sum >> FIFO_LEN_LOG;
    
endmodule


 

модуль проверялся вот таким тестбенчем

Spoiler

`timescale 1 ns / 1 ps

module MovAverageFilter_tb();

reg clk = 0;

always #(10.0/2)
begin
    clk = ~clk;
end

reg [15:0] datain;


MovAverageFilter_16 MovAverageFilter_16_i
(
	.clk(clk),
	.datain(datain)//,
);

MovAverageFilter 
#(
    .FIFO_LEN_LOG(4)//,
)
MovAverageFilter_i_4
(
	.clk(clk),
	.datain(datain)//,
);

MovAverageFilter 
#(
    .FIFO_LEN_LOG(6)//,
)
MovAverageFilter_i_6
(
	.clk(clk),
	.datain(datain)//,
);

real signal;
real signal_noise;


initial
begin
    datain = 0;
    signal = 0;
    signal_noise = 0;
end

always @(posedge clk)
begin
    
    //
    signal = (1-1e-5)*signal + (5e-8)*$random();
    signal_noise = signal + (1e-6)*$random() + 2.0**15;
    signal_noise = signal_noise + (1e-6)*$random();
    
    //
    datain = signal_noise;
    
    if (signal_noise>=(2.0**16-1))
    begin
        datain = (2.0**16-1);
    end
    if (signal_noise<=(0))
    begin
        datain = (0);
    end
    
end

endmodule

тут

(signal) - низкочастотный сигнал
(signal_noise) - зашумленный низкочастотный сигнал

(MovAverageFilter_16_i.dataout) - выход оригинального блока
(MovAverageFilter_i_4.dataout) - выход параметризованного варианта с размером окна как у оригинального
(MovAverageFilter_i_6.dataout) - выход параметризованного варианта с большим размером окна

(MovAverageFilter_16) - оригинальный блок для сравнения результатов

Spoiler


module MovAverageFilter_16
(
    input  clk,
    input  [15:0] datain,
    output [15:0] dataout//,
);
    
    reg [15:0] FIFO [0:15];
    integer i;

    initial
    begin
        for(i = 15; i >= 0; i=i-1)
        begin
            FIFO[i] = 16'd0;
        end
    end
    
    always @(posedge clk)
    begin

        for(i = 15; i > 0; i=i-1)
        begin
            FIFO[i] <= FIFO[i-1];
        end
        
        FIFO[0] <= datain;
        
    end
    
    reg [19:0] fifo_sum = 0;
    
    always @(posedge clk)
    begin
        fifo_sum <=
        (
            +FIFO[ 0]+FIFO[ 1]+FIFO[ 2]+FIFO[ 3]
            +FIFO[ 4]+FIFO[ 5]+FIFO[ 6]+FIFO[ 7]
            +FIFO[ 8]+FIFO[ 9]+FIFO[10]+FIFO[11]
            +FIFO[12]+FIFO[13]+FIFO[14]+FIFO[15]
        );	
    end
    
    assign dataout = fifo_sum >> 4;
    
endmodule

 

 

результаты моделирования

res_00.thumb.png.17e00d3146d97c30fb58654536ff16d6.png

 

сигнал с выхода параметризованного варианта с размером окна как у оригинального повторяет сигнал с выхода оригинального блока, сигнал с выхода параметризованного варианта с большим размером окна отсеивает больше шума

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

stealthisname

спасибо большое!!!

есть еще идея оптимизировать в части 

for(i = FIFO_LEN-1; i > 0; i=i-1)
        begin
            FIFO[i] <= FIFO[i-1];
        end

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Оптимизировал. Проверил, работает вроде также.

Скрытый текст

module MovAverageFilter
#(
    parameter FIFO_LEN_LOG = 4//,
)
(
    input  clk,
    input  [15:0] datain,
    output [15:0] dataout//,
);
    localparam FIFO_LEN = (1 << FIFO_LEN_LOG);
    
    reg [15:0] FIFO [0:FIFO_LEN-1];
     reg [FIFO_LEN_LOG+15:0] fifo_sum = 0;
    integer i;
     
     reg [FIFO_LEN_LOG-1:0] k = 0;

    initial
    begin
        for(i = FIFO_LEN-1; i >= 0; i=i-1)  FIFO = 16'd0;
    end

    
     always @(posedge clk)
        begin        
        fifo_sum <= fifo_sum + datain - FIFO[k];        
        end
        
    always @(negedge clk)
        begin        
        FIFO[k] <= datain;
        k<=k+1;        
        end
    
    assign dataout = fifo_sum >> FIFO_LEN_LOG;
    
endmodule

 

P.S. Бегающий указатель "к" специально не обнуляю, в железе это само собой происходит, т.е. переполнения нет.

Изменено пользователем Halfback

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Halfback сказал:

Оптимизировал. Проверил, работает вроде также.

  Скрыть содержимое

    always @(negedge clk)

 

        begin        
        FIFO[k] <= datain;
        k<=k+1;        
        end
 

 

 

Не надо использовать задний фронт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 hours ago, Halfback said:

dataout <= (FIFO[0]+FIFO[1]+FIFO[2]+FIFO[3]...

Неужели таким способом частота 120 МГц была достижима? При 16 битных то семплах, на какой интересно элементной базе и как оно структурно вообще могло такой сумматор построить. Никогда не задумывался я, а что, там N каскадов сумматоров организуется синтезатором? У меня была бы изначально идея с кольцевым буфером, не знаю даже как иначе это эффективно могло быть сделано.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, AVR сказал:

Неужели таким способом частота 120 МГц была достижима?

окно на 16 при таком способе худо-бедно но работает, на 32 окно пробовал - не работает.

3 часа назад, andrew_b сказал:

Не надо использовать задний фронт.

почему?

Изменено пользователем Halfback

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

16 minutes ago, Halfback said:

окно на 16 при таком способе худо-бедно но работает, на 32 окно пробовал - не работает.

юзайте

https://drive.google.com/drive/folders/1EQpuRnWBgQJ1AEHo5MNp98cgfFQFPJDS?usp=sharing

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, Halfback сказал:

почему?

Не. Давайте так: вы используете оба фрона тактового сигнала, вот вы и скажите, зачем вы это делаете.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

21 hours ago, Halfback said:

почему?

Потому что Вы:

а) уменьшаете допустимое время сетап от rising_edge флопов до falling_edge (для этих путей требования по времянке х2)

б) то же самое для FF от falling_edge до следующих rising_edge

в) проект становится сложным в понимании

г) в этом нет острой необходимости

д) скважность клока может быть не 50% (по техническим причинам PLL) тогда у Вас пункт а) и б) будут гулять + джиттер

е) это не красиво тем более что г)

ж) тут можно подставить ещё с 10-ток пунктов...

Если хочется быстродействия - поднимите частоту и не занимайтесь ерундой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Nick_K

трудно поспорить с перечисленными Вами пунктами. Пока мне не очень понятно насколько корректно будет работать описание внутри posegde и negedge если их засунуть в один posegde. Надо проверять, т.к. вычисление должно происходить за один такт. Я с плисиной и verilog еще сравнительно мало работаю.

Изменено пользователем Halfback

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...