srf55 2 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба Добрый день! Написал модуль кольцевого буфера, планирую использовать для временного хранения отсчетов, захваченных с АЦП. Модуль служит для того, чтобы сохранять предыдущие N-отсчетов с АЦП. Суть работы модуля такая: 1) Данные пишутся в буфер непрерывно, по кругу 2) при возникновении разового импульса на входе catch все данные выдаются на выход (интерфейс AXI-Stream) 3) В момент выдачи данных, запрещается запись в буфер Вроде хорошо работает на частотах до 250 МГц (Virtex-6). Но хотелось бы оптимизировать его, чтобы он работать на чуть большей частоте (300МГц). Может кто-нибудь подскажет, как. Еще хотелось бы сделать модуль более параметризируемым. Чтобы можно было выбирать тип используемой памяти (блочная или распределенная) Код модуля: `timescale 1ns/1ps module ring_buffer #( parameter DATA_WIDTH = 8, parameter BUFFER_DEPTH = 32768, parameter USE_BLOCK_RAM = 1 ) ( input wire clk, // Тактовый сигнал input wire reset, // Сигнал сброса input wire [DATA_WIDTH-1:0] data_in, // Входные данные Native интерфейса input wire catch, // Флаг начала выдачи данных output wire [DATA_WIDTH-1:0] data_out, // Выходные данные output wire valid_out, // Сигнал валидности выходных данных output wire last_out // Сигнал последнего отсчета выходных данных ); reg [DATA_WIDTH-1:0] buffer [0:BUFFER_DEPTH-1]; // Кольцевой буфер предыдущих отсчетов reg [$clog2(BUFFER_DEPTH)-1:0] read_ptr = 0; // Указатель на чтение reg [$clog2(BUFFER_DEPTH)-1:0] write_ptr = 0; // Указатель на запись reg transfer_active = 1'b0; // Флаг активной передачи данных always @(posedge clk or posedge reset) begin if (reset) begin // Сброс в начальное состояние при срабатывании сигнала сброса //buffer <= 0; read_ptr <= 0; write_ptr <= 0; transfer_active <= 0; end else begin // Запись в буфер независимо от флага if (!transfer_active) buffer[write_ptr] <= data_in; if (!transfer_active) write_ptr <= write_ptr + 1; // Установка флага активной передачи данных if (catch) begin transfer_active <= 1; end else if (last_out) begin transfer_active <= 0; end if (transfer_active) begin read_ptr <= read_ptr + 1; end else begin if (catch) read_ptr <= write_ptr + 1; end end end // Выходные данные assign data_out = buffer[read_ptr]; assign valid_out = transfer_active; assign last_out = (read_ptr == write_ptr - 1) ? 1'b1 : 1'b0; // Сигнал последнего отсчета при чтении последнего отсчета из буфера endmodule Тестбенч: `timescale 1ns/1ps module ring_tb (); logic clk = '0; // Тактовый сигнал logic reset = '0; // Сигнал сброса logic [7:0] data_in = '0; // Входные данные Native интерфейса logic catch = '0; // Флаг начала выдачи данных logic [7:0] data_out; // Выходные данные logic valid_out; // Сигнал валидности выходных данных logic last_out; // Сигнал последнего отсчета выходных данных ring #( .DATA_WIDTH (8), .BUFFER_DEPTH (32768), .USE_BLOCK_RAM (1) ) dut ( .clk (clk), // Тактовый сигнал .reset (reset), // Сигнал сброса .data_in (data_in), // Входные данные Native интерфейса .catch (catch), // Флаг начала выдачи данных .data_out (data_out), // Выходные данные .valid_out (valid_out), // Сигнал валидности выходных данных .last_out (last_out) // Сигнал последнего отсчета выходных данных ); initial begin clk = 0; forever clk = #5 ~clk; end initial begin reset = 1; #100; reset = 0; #100; end always_ff @(posedge clk) begin : proc_data_in data_in <= data_in + 1'b1; end initial begin catch = '0; #500000; catch = '1; #10; catch = '0; #500000; catch = '1; #10; catch = '0; end endmodule Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба 28 минут назад, srf55 сказал: 3) В момент выдачи данных, запрещается запись в буфер Я не специалист в ПЛИС, но... почему так? Зачем запрещать запись? 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
srf55 2 21 ноября, 2023 Опубликовано 21 ноября, 2023 (изменено) · Жалоба 34 минуты назад, Arlleex сказал: Я не специалист в ПЛИС, но... почему так? Зачем запрещать запись? Сейчас подумал, что можно обеспечить, чтобы адрес write_ptr был на 1 больше, чем адрес read_ptr. Тогда никаких коллизий не будет происходить. Спасибо! Только тогда придется сделать еще один счетчик(считать до значения глубины FIFO), т.к не получится останавливать выдачу данных на условии: (read_ptr == write_ptr - 1) Изменено 21 ноября, 2023 пользователем srf55 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Maverick_ 15 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба я в свое время написал аналогичный модуль library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity delay_line is generic( W : integer := 8; -- data width L : integer := 1200); -- delay length, shall be > 3 port( i_clk : in std_logic; i_sync_reset : in std_logic; i_data : in std_logic_vector(W-1 downto 0); o_data : out std_logic_vector(W-1 downto 0)); end delay_line; architecture rtl of delay_line is type t_ram is array (L-2 downto 0) of std_logic_vector(W-1 downto 0); signal m_ram : t_ram; signal r_addr_wr : integer range 0 to L-2; signal r_addr_rd : integer range 0 to L-2; signal r_enable_read : std_logic; begin p_write : process (i_clk) begin if rising_edge(i_clk) then if(i_sync_reset='1') then r_addr_wr <= 0; r_enable_read <= '0'; else m_ram(r_addr_wr) <= i_data; if(r_addr_wr<L-2) then r_addr_wr <= r_addr_wr + 1; else r_addr_wr <= 0; r_enable_read <= '1'; -- enable reading section end if; end if; end if; end process p_write; p_read : process (i_clk) begin if rising_edge(i_clk) then if(i_sync_reset='1') then r_addr_rd <= 0; else if(r_enable_read='1') then o_data <= m_ram(r_addr_rd) ; -- additional delay if(r_addr_rd<L-2) then r_addr_rd <= r_addr_rd + 1; else r_addr_rd <= 0; end if; end if; end if; end if; end process p_read; end rtl; тоже по кольцу пишет, но может обеспечить задержку... PS все предложенное варианты реализации фифо... Вам предложу рассмотреть реализацию счетчика на базе конвеера из более мелких счетчиков, должно повысить тактовую частоту... 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_4afc_ 25 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба 1 hour ago, Maverick_ said: Вам предложу рассмотреть реализацию счетчика на базе конвеера из более мелких счетчиков, должно повысить тактовую частоту... Xilinx быстрые счётчики реализует на DSP48. 2 hours ago, srf55 said: Вроде хорошо работает на частотах до 250 МГц (Virtex-6). Но хотелось бы оптимизировать его, чтобы он работать на чуть большей частоте (300МГц). Может кто-нибудь подскажет, как. Выделить каждый регистр в отдельный always\модуль и посмотреть максимально возможную частоту его работы. 2 hours ago, srf55 said: Еще хотелось бы сделать модуль более параметризируемым. Чтобы можно было выбирать тип используемой памяти (блочная или распределенная) На распределённой скорость будет ниже, но если хочется - попробуйте generate. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
srf55 2 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба 1 час назад, Maverick_ сказал: Вам предложу рассмотреть реализацию счетчика на базе конвеера из более мелких счетчиков, должно повысить тактовую частоту... Спасибо! Основываясь на мысли Arlleex сделал ring.v (прекратил запрещать запись во время выдачи данных) Добавил дополнительные регистры (ring_pipe.v). Но счетчики, на более мелкие (как посоветовал Maverick_) пока что дробить не стал. Т.к Fmax возрос до 370 МГц и этого хватает. Потом ради интереса может займусь. сделал ring_tb.sv - для проверки того, что модули работают идентично Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 33 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба Прежде чем беспокоить DSP или зазря переписывать always есть смысл посмотреть логи и понять что же является узким место в плане времянки. Из приведенного куска кода вопрос может вызывать только assign last_out = (read_ptr == write_ptr - 1) ? 1'b1 : 1'b0; Так как сравнении идет с не с регистром, а с разностью - и это может влиять на времянку. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrew_b 16 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба 2 hours ago, srf55 said: интерфейс AXI-Stream Разве? Где сигнал ready? А вот когда он появится, тогда 1 hour ago, srf55 said: обеспечить, чтобы адрес write_ptr был на 1 больше, чем адрес read_ptr не получится. 2 hours ago, srf55 said: Чтобы можно было выбирать тип используемой памяти (блочная или распределенная) Через строковый параметр parameter RAM_TYPE = "block" ... localparam L_RAM_TYPE = (RAM_TYPE == "block") ? "block" : "distributed"; (* RAM_STYLE = L_RAM_TYPE *) reg [DATA_WIDTH-1:0] buffer [0:BUFFER_DEPTH-1]; // Кольцевой буфер предыдущих отсчетов 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Maverick_ 15 21 ноября, 2023 Опубликовано 21 ноября, 2023 · Жалоба 34 minutes ago, _4afc_ said: Xilinx быстрые счётчики реализует на DSP48. DSP48 тратить на счетчики на мой взгляд дорогое удовольствие - можно такое делать когда других вариантов нет Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Bad0512 2 22 ноября, 2023 Опубликовано 22 ноября, 2023 · Жалоба 20 hours ago, Maverick_ said: DSP48 тратить на счетчики на мой взгляд дорогое удовольствие - можно такое делать когда других вариантов нет Как-то давным-давно исследовал вопрос "так какой счетчик всё-таки шустрее считает, обычный или на DSP ?". Использовал в качестве базовой архитектуры какой-то из Спартанов, не помню точно какой. Так вот в итоге получилось что приvерно до 32 бит обычный счётчик уделывает по времянке DSP-шный. Дальше побеждает DSP. В общем, fast carry logic - великая сила, а ведь были времена когда её в ПЛИС не было! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
srf55 2 22 ноября, 2023 Опубликовано 22 ноября, 2023 · Жалоба Спасибо всем за помощь! Вот что получилось: ring_buffer.v ring_buffer_pipe.v ring_buffer_tb.sv Исправил ошибку, last выставлялся несвоевременно, на такт раньше, чем нужно. В 21.11.2023 в 15:28, andrew_b сказал: В 21.11.2023 в 12:27, srf55 сказал: Чтобы можно было выбирать тип используемой памяти (блочная или распределенная) Через строковый параметр Добавил возможность выбирать тип используемой памяти (параметр USE_BLOCK_RAM). В 21.11.2023 в 15:26, RobFPGA сказал: Из приведенного куска кода вопрос может вызывать только assign last_out = (read_ptr == write_ptr - 1) ? 1'b1 : 1'b0; Так как сравнении идет с не с регистром, а с разностью - и это может влиять на времянку. Исправил, теперь в этом месте происходит сравнение с константой 😀 В 21.11.2023 в 15:28, andrew_b сказал: В 21.11.2023 в 12:27, srf55 сказал: интерфейс AXI-Stream Разве? Где сигнал ready? А вот когда он появится, тогда Смотрел стандарт AMBA AXI-Stream и не нашел, что ready (TREADY) - это обязательный сигнал (но мог упустить, и где-то об этом говорится). В этом конкретном случае, на мой взгляд, сигнал TREADY не нужен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrew_b 16 22 ноября, 2023 Опубликовано 22 ноября, 2023 · Жалоба 37 minutes ago, srf55 said: В этом конкретном случае, на мой взгляд, сигнал TREADY не нужен. Вам виднее. Но если вы будете сопрягать свой модуль с каким-то сторонним, у которого выход ready есть? На ваш вход ready всегда можно подать постоянную единицу, так что от него хуже не будет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dde29 0 15 марта Опубликовано 15 марта · Жалоба Может пригодится еще: Вот пример конвейерезированного счетчика 24 бит, который разбит на два 12-битных: reg [11:0] cnt_low = 12'd0; reg [11:0] cnt_high = 12'd0; reg cnt_low_done = 1'b0; wire cnt_low_done_buf; assign cnt_low_done_buf = (cnt_low == 12'hFFE) ? 1'b1 : 1'b0; always @ (posedge clk) begin if ((cnt_high == MAX_VALUE[23:12])&(cnt_low == MAX_VALUE[11:0])) begin cnt_high <= 12'd0; cnt_low <= 12'd0; cnt_low_done <= 1'b0; end else begin cnt_low <= cnt_low + 1'b1; cnt_low_done <= cnt_low_done_buf; if (cnt_low_done) cnt_high <= cnt_high + 1'b1; end end Если вдруг оказывается, что самое "узкое" по времянке место в проекте это именно большеразрядный счетчик, то данное решение в виде конвейера - поможет однозначно... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 58 16 марта Опубликовано 16 марта · Жалоба 22 часа назад, dde29 сказал: Если вдруг оказывается, что самое "узкое" по времянке место в проекте это именно большеразрядный счетчик, то данное решение в виде конвейера - поможет однозначно... Как оно поможет? Чем это лучше: logic cnt = 0; always_ff @(posedge clk) begin cnt <= cnt + 1; end ? Ваш вариант будет работать точно не быстрее. В лучшем случае он сведётся к приведённому. Скоростные многоразрядные счётчики делаются совсем не так. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dde29 0 16 марта Опубликовано 16 марта · Жалоба On 3/16/2024 at 10:45 AM, dxp said: Как оно поможет? Чем это лучше: logic cnt = 0; always_ff @(posedge clk) begin cnt <= cnt + 1; end ? Ваш вариант будет работать точно не быстрее. В лучшем случае он сведётся к приведённому. Скоростные многоразрядные счётчики делаются совсем не так. Может тогда подскажете как? Но лично мне то, что я привел - помогало увеличить максимальную частоту работы счётчиков, как на Альтере, так и на Ксилинксе... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться