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