Halfback 0 July 27, 2021 Posted July 27, 2021 (edited) · Report post Доброго времени суток! Нужна помощь в оптимизации кода на 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 [email protected](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 [email protected](negedge clk) begin for(i = 15; i > 0; i=i-1) FIFO[i] <= FIFO[i-1]; FIFO[0] <= datain; end endmodule Есть желание увеличить размер окна с 16 до 32 или даже до 64 но тогда нужно будет больше сумматоров и я беспокоюсь за обеспечение синхронности схемы. Может есть более изящный вариант брать сумму точек в окне и делить на их кол-во? Edited July 27, 2021 by Halfback Quote Share this post Link to post Share on other sites More sharing options...
Alex77 2 July 27, 2021 Posted July 27, 2021 · Report post ну так поменяйте алгоритм вычисления и будет вам счастье... Quote Share this post Link to post Share on other sites More sharing options...
RobFPGA 8 July 27, 2021 Posted July 27, 2021 · Report post Приветствую! 2 hours ago, Halfback said: Есть желание увеличить размер окна с 16 до 32 или даже до 64 но тогда нужно будет больше сумматоров и я беспокоюсь за обеспечение синхронности схемы. Может есть более изящный вариант брать сумму точек в окне и делить на их кол-во? Для скользящего среднего при котором отсчеты приходят по одному достаточно одного сложения и одного вычитания для окна любой длинны. Удачи! Rob. Quote Share this post Link to post Share on other sites More sharing options...
Eddy_Em 0 July 27, 2021 Posted July 27, 2021 · Report post Неправильный алгоримт: каждый раз используется уйма вычислений. А ведь достаточно завести кольцевой буфер! В текущую ячейку помещаем новое значение и инкрементируем номер, на последнем номере переводим счетчик в нуль. "Автоматически" новое значение всегда будет в конце буфера. Да и еще ускорить можно: завести отдельную переменную для общей суммы. В этом случае перед тем, как закинуть новое значение в кольцевой буфер, сначала вычитаем из суммы старое, затем запихиваем в буфер новое и добавляем его к сумме. Экономим очень прилично! Со скользящей медианой чуть сложней, но там тоже на кольцевом буфере все завязано (а для построения qsort используется массив указателей). Quote Share this post Link to post Share on other sites More sharing options...
Halfback 0 July 28, 2021 Posted July 28, 2021 · Report post 12 часов назад, RobFPGA сказал: Для скользящего среднего при котором отсчеты приходят по одному достаточно одного сложения и одного вычитания для окна любой длинны. Наверное я правильно понял что вот это 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 справа в скобках будет расценено как предыдущее значение а слева - как текущее значение. Так ли это будет в верилоге? (Я всё равно в железе проверю) Quote Share this post Link to post Share on other sites More sharing options...
stealthisname 7 July 28, 2021 Posted July 28, 2021 · Report post по заданию требуется возможность генерации модуля с разным размером окна и разрешены размеры окна по степеням двойки, поэтому в модуле можно в качестве параметра задавать степень (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 результаты моделирования сигнал с выхода параметризованного варианта с размером окна как у оригинального повторяет сигнал с выхода оригинального блока, сигнал с выхода параметризованного варианта с большим размером окна отсеивает больше шума Quote Share this post Link to post Share on other sites More sharing options...
Halfback 0 July 28, 2021 Posted July 28, 2021 · Report post stealthisname спасибо большое!!! есть еще идея оптимизировать в части for(i = FIFO_LEN-1; i > 0; i=i-1) begin FIFO[i] <= FIFO[i-1]; end Идея в том чтобы этого не делать сдвиг массива а организовать кольцевой буфер как посоветовал Eddy_Em Quote Share this post Link to post Share on other sites More sharing options...
Halfback 0 July 28, 2021 Posted July 28, 2021 (edited) · Report post Оптимизировал. Проверил, работает вроде также. Скрытый текст 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. Бегающий указатель "к" специально не обнуляю, в железе это само собой происходит, т.е. переполнения нет. Edited July 28, 2021 by Halfback Quote Share this post Link to post Share on other sites More sharing options...
andrew_b 7 July 28, 2021 Posted July 28, 2021 · Report post 1 час назад, Halfback сказал: Оптимизировал. Проверил, работает вроде также. Скрыть содержимое always @(negedge clk) begin FIFO[k] <= datain; k<=k+1; end Не надо использовать задний фронт. Quote Share this post Link to post Share on other sites More sharing options...
1891ВМ12Я 0 July 28, 2021 Posted July 28, 2021 · Report post 22 hours ago, Halfback said: dataout <= (FIFO[0]+FIFO[1]+FIFO[2]+FIFO[3]... Неужели таким способом частота 120 МГц была достижима? При 16 битных то семплах, на какой интересно элементной базе и как оно структурно вообще могло такой сумматор построить. Никогда не задумывался я, а что, там N каскадов сумматоров организуется синтезатором? У меня была бы изначально идея с кольцевым буфером, не знаю даже как иначе это эффективно могло быть сделано. Quote Share this post Link to post Share on other sites More sharing options...
Halfback 0 July 28, 2021 Posted July 28, 2021 (edited) · Report post 3 минуты назад, AVR сказал: Неужели таким способом частота 120 МГц была достижима? окно на 16 при таком способе худо-бедно но работает, на 32 окно пробовал - не работает. 3 часа назад, andrew_b сказал: Не надо использовать задний фронт. почему? Edited July 28, 2021 by Halfback Quote Share this post Link to post Share on other sites More sharing options...
quato_a 3 July 28, 2021 Posted July 28, 2021 · Report post 16 minutes ago, Halfback said: окно на 16 при таком способе худо-бедно но работает, на 32 окно пробовал - не работает. юзайте https://drive.google.com/drive/folders/1EQpuRnWBgQJ1AEHo5MNp98cgfFQFPJDS?usp=sharing Quote Share this post Link to post Share on other sites More sharing options...
andrew_b 7 July 28, 2021 Posted July 28, 2021 · Report post 4 часа назад, Halfback сказал: почему? Не. Давайте так: вы используете оба фрона тактового сигнала, вот вы и скажите, зачем вы это делаете. Quote Share this post Link to post Share on other sites More sharing options...
Nick_K 0 July 29, 2021 Posted July 29, 2021 · Report post 21 hours ago, Halfback said: почему? Потому что Вы: а) уменьшаете допустимое время сетап от rising_edge флопов до falling_edge (для этих путей требования по времянке х2) б) то же самое для FF от falling_edge до следующих rising_edge в) проект становится сложным в понимании г) в этом нет острой необходимости д) скважность клока может быть не 50% (по техническим причинам PLL) тогда у Вас пункт а) и б) будут гулять + джиттер е) это не красиво тем более что г) ж) тут можно подставить ещё с 10-ток пунктов... Если хочется быстродействия - поднимите частоту и не занимайтесь ерундой. Quote Share this post Link to post Share on other sites More sharing options...
Halfback 0 July 29, 2021 Posted July 29, 2021 (edited) · Report post Nick_K, трудно поспорить с перечисленными Вами пунктами. Пока мне не очень понятно насколько корректно будет работать описание внутри posegde и negedge если их засунуть в один posegde. Надо проверять, т.к. вычисление должно происходить за один такт. Я с плисиной и verilog еще сравнительно мало работаю. Edited July 29, 2021 by Halfback Quote Share this post Link to post Share on other sites More sharing options...