Lionet 0 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба Приветствую! Возникла задача сделать на ПЛИС приёмник данных по интерфейсу, временная диаграмма работы которого схематически выглядит так: RDY - выход (для ПЛИС), показывает внешнему устройству, что можно начинать передачу данных. Начавшуюся передачу тормозить уже нельзя, к слову, но объём каждой посылки фиксирован и тут всё норм. С этим сигналом сложностей нет - он синхронизирован с логикой в ПЛИС. CLK - внешний клок, определяющий как частоту следования данных, так и битовую синхронизацию - первый фронт после выставления RDY - нулевой бит и т.д. Как видно, этот сигнал тактирования присутствует не постоянно, а только на время передачи, что, как оказалось, доставляет массу проблем. Din0...Din3 - линии передачи данных. Их может быть 1 или 4. Тут всё просто. Как видно, интерфейс DDR - с передачей данных по обоим фронтам тактового сигнала. Это не представляется проблемой, т.к. получить data_rise и data_fall несложно, но это увеличивает число параллельных сигналов, подлежащих передаче между доменами тактирования в ПЛИС. Впрочем, Xilinx-овский примитив IDDR применить не удалось - он конвееризирован и при любой конфигурации требует несколько дополнительных тактов (которых у меня нет) для вывода последних полученных бит. После обдумывания проблемы, я вспомнил, что это мне напоминает - частый на форумах вопрос о реализации SPI slave - там тоже клок внешний и присутствует только при передаче данных. Но в том случае большинство рекомендаций сводится к стробированию входных сигналов внутренним тактовым через стандартную схему с регистрами. Однако, даже там получается шина как минимум из двух сигналов SPI_CLK и SPI_MOSI, а шину так синхронизировать, насколько я знаю, не рекомендуют. А главный вопрос - скорость передачи. В случае SPI - внешняя тактовая МГц до 10 при клоке ПЛИС 50-100 МГц. А у меня интерефейс работает с частотой 100-500 МГц. Понятно, что после десериализации частота следования данных упадёт как минимум в 8 раз (если распараллеливать в 8 бит по фронту + 8 бит по спаду), но синхронизировать шинные сигналы через асинхронные регистры нельзя, нужно FIFO. А вот как раз FIFO у Xilinx работает только при постоянно присутствующем (free running) тактировании с обеих сторон. Засада... Пока что попробовал сделать приём данных в принципе, только по внешнему клоку. Сыро, но работает. Но чтобы всё это интегрировать в полную подсистему, с AXI-stream инфраструктурой, нужно корректно передать данные во внутренний тактовый домен - иначе хитрая Вивада пошлёт лесом, да и работать всё будет глючно. Пока что правильного рещения не нашёл. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Nick_K 0 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба Вам нужно всего 2 регистра сдвига: один будет работать по прямому входящему клоку, второй по инверсному. Плюс пара триггеров для логики RDY и счётчик полученных байт - защёлкивать входные десериализированные данные в следующий блок для дальнейшей обработки. Схема проще некуда. НО! Это только начало Какой чип, какой тип интерфейса и т.д.? Там с констрейнами/плейсментом будет геморроя дофигища. Я на дев борде Artix'а с горем по-полам распихал 3 последовательных триггера на 400MHz... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Lionet 0 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба Думаю, можно оставить в стороне все частоты входного тактирования выше 100 МГц - бОльшая скорость в моей задаче не требуется, а меньше не позволяет микросхема-передатчик. Собственно, на такой частоте у меня сейчас работает "сырой" тест приёмника данных на ZedBoard-e (Zynq-7020) - выводит полученные данные на светодиоды :) Регистр причём даже один - загружается по обоим фронтам, хотя это, возможно, и не совсем корректно. Проблема в том, что вся эта схема работает в тактовом домене входного сигнала (тот самый CLK, который то есть, то нет). И чтобы корректно передать их в основной тактовый домен ПЛИС,нужно какое-то хитрое FIFO, которое умеет работать с непостоянным клоком с одной стороны. IP core FIFO от Xilinx не умеет - даже в даташите написано. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
nice_vladi 1 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба 100-500 МГц - довольно большой диапазон. Предложенный выше вариант (два сдвиговых регистра + счётчик) точно заработает на 100 МГц. А вот на 500 - уже не точно. Так что, первым дело, стоит определиться с частотой интерфейса и от этого отталкиваться. 2 minutes ago, Lionet said: Думаю, можно оставить в стороне все частоты входного тактирования выше 100 МГц - бОльшая скорость в моей задаче не требуется, а меньше не позволяет микросхема-передатчик. Собственно, на такой частоте у меня сейчас работает "сырой" тест приёмника данных на ZedBoard-e (Zynq-7020) - выводит полученные данные на светодиоды :) Регистр причём даже один - загружается по обоим фронтам, хотя это, возможно, и не совсем корректно. Проблема в том, что вся эта схема работает в тактовом домене входного сигнала (тот самый CLK, который то есть, то нет). И чтобы корректно передать их в основной тактовый домен ПЛИС,нужно какое-то хитрое FIFO, которое умеет работать с непостоянным клоком с одной стороны. IP core FIFO от Xilinx не умеет - даже в даташите написано. Можно без фифо. Перекладываете строб RDY на внутреннюю тактовую, заводите по нему счётчик и, допустим, на 10й такт (те когда ВСЕ пришедшие биты ТОЧНО легли в сдвиговые регистры) перекладываете эти регистры на внутренний клок. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Lionet 0 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба 3 minutes ago, nice_vladi said: Можно без фифо. Перекладываете строю RDY на внутреннюю тактовую, заводите по нему счётчик и, допустим, на 10й такт (те когда ВСЕ пришедшие биты ТОЧНО легли в сдвиговые регистры) перекладываете эти регистры на внутренни клок. Как вариант. Но всё равно остаётся ощущение, что что-то делаю не так. К слову, тот же счётчик входных бит надо бы периодически обнулять - при сбросе или перед приёмом очередного байта (пусть он и сам по переполнению обнуляется, но так дополнительная защита от сбоев). А для этого сигнал от внутреннего клока ПЛИС надо затащить в домен внешнего CLK. а его опять нет до передачи данных. Снова засада... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
iosifk 3 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба 2 часа назад, Lionet сказал: К слову, тот же счётчик входных бит надо бы периодически обнулять - при сбросе или перед приёмом очередного байта (пусть он и сам по переполнению обнуляется, но так дополнительная защита от сбоев). А для этого сигнал от внутреннего клока ПЛИС надо затащить в домен внешнего CLK. а его опять нет до передачи данных. Снова засада... Судя по картинке сигнал RDY может быть признаком 1-го бита.. И он может обнулять счетчик принятых битов... А дальше нужен не счетчик, а сдвиговый регистр, который и отсчитает принятые биты. В этот регистр задвигайте 1, и когда она появится в последней позиции - это и будет означать конец приема.. И там, если после приема есть пауза, то бит сдвигового регистра будет стоять все время паузы... Но можно сделать регистр длиннее на 1 бит и отлавливать лишние сбойные клоки... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Lionet 0 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба 16 minutes ago, iosifk said: Судя по картинке сигнал RDY может быть признаком 1-го бита.. И он может обнулять счетчик принятых битов... Тут есть нюанс: RDY - это сигнал, формируемый во внутреннем тактовом домене ПЛИС (например, когда процессор проинициализировал DMA для приёма данных). Соответственно, он находится не в тактовом домене входного сигнала CLK (в котором работает счетчик входных бит). И чтобы его туда пробросить, нужно, чтобы этот CLK работал. А он появляется, когда уже пошла передача, соответственно что-то обнулять поздно. Т.е. логически я идею, конечно, понял и сам пытался её реализовать. Но всё оказалось не так просто Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 31 октября, 2019 Опубликовано 31 октября, 2019 · Жалоба Приветствую! 1 hour ago, Lionet said: Т.е. логически я идею, конечно, понял и сам пытался её реализовать. Но всё оказалось не так просто Тогда уж выкладывает все как есть на входе и что хотите получать на выходе - а так только угадывать приходится. Самый простой вариант cdc тут это elastic-buffer - пишете последним фронтом в память (distributed) увеличивая счетчик записи. Сам счетчик передаете на сторону чтения через обычную цепочку cdc (тут только clk чтения нужно), а там уж логика чтения как у обычного FIFO. Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 1 ноября, 2019 Опубликовано 1 ноября, 2019 · Жалоба On 10/31/2019 at 8:33 PM, Lionet said: RDY - выход (для ПЛИС), показывает внешнему устройству, что можно начинать передачу данных. Начавшуюся передачу тормозить уже нельзя, к слову, но объём каждой посылки фиксирован и тут всё норм. С этим сигналом сложностей нет - он синхронизирован с логикой в ПЛИС. On 11/1/2019 at 2:10 AM, Lionet said: Тут есть нюанс: RDY - это сигнал, формируемый во внутреннем тактовом домене ПЛИС (например, когда процессор проинициализировал DMA для приёма данных). Соответственно, он находится не в тактовом домене входного сигнала CLK (в котором работает счетчик входных бит). И чтобы его туда пробросить, нужно, чтобы этот CLK работал. А он появляется, когда уже пошла передача, соответственно что-то обнулять поздно. в чем проблема использовать RDY асинхронно? Аналогично тому, как делаются синхронизаторы сброса. Управляете уровнем, RDY держите всю посылку ну или сформируйте из него короткий импульс сброса. RDY появляется гарантированно до начала посылки, ну а дальше тривиально, собираете ваши слова и кладете их асинхронно в регистры или в память на SLICEM или в квазиасинхронную RAMB ЗЫ. В свое время, от Xilinx гуру (что, то мне подсказывает что от Ken Chapmen) была статья, с примерами асинхронно/синхронных переходов, еще под спартан 2. Работало же) Но сходу не нагуглилось Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
iosifk 3 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба В 31.10.2019 в 22:10, Lionet сказал: Тут есть нюанс: RDY - это сигнал, формируемый во внутреннем тактовом домене ПЛИС (например, когда процессор проинициализировал DMA для приёма данных). Соответственно, он находится не в тактовом домене входного сигнала CLK (в котором работает счетчик входных бит). И чтобы его туда пробросить, нужно, чтобы этот CLK работал. А он появляется, когда уже пошла передача, соответственно что-то обнулять поздно. Я предлагаю сделать все на стороне внутреннего клока. Для этого в FIFO делаем теги, т.к. еще один дополнительный бит, кроме бита данных. И туда записываем сигнал RDY . И уже на внутренней стороне по наличию этого сигнала определяем начало посылки, делаем счетчики и пр. Ну а данные пишем "как есть" и на другой стороне их преобразовываем. Получится наверное два FIFO, один для положительного фронта, другой - для отрицательного. Теги можно сделать и большей разрядности и писать туда служебную информацию. У меня есть ранняя статья о том, как я делал МАС. И там это более подробно расписано... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба 2 hours ago, iosifk said: И туда записываем сигнал RDY . И уже на внутренней стороне по наличию этого сигнала определяем начало посылки, делаем счетчики и пр. Ну а данные пишем "как есть" и на другой стороне их преобразовываем. на частоте как у ТС не получится так записать весь поток, а потом разгрести Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба Приветствую! 2 hours ago, des00 said: на частоте как у ТС не получится так записать весь поток, а потом разгрести Так и есть - да и не нужно это. Тут классический elastic buffer. Разве что клок не постоянный. Отсюда вытекает что все надо сделать на последнем фронте. Spoiler `timescale 1ns/1ps `default_nettype none module data_din #(parameter DIN_WH = 4, DIN_N = 4, ADDR_WH = 4 ) ( input wire clk , input wire rst , input wire ready , // device pins output logic dev_rdy , input wire dev_clk , input wire [ DIN_WH-1:0] dev_din , // write to fifo output wire ram_wr_clk , output logic [ADDR_WH-1:0] ram_wr_addr, output logic ram_wr_ena , output logic [DIN_N*2-1:0][DIN_WH:0] ram_wr_data, output logic ram_wr_done ); logic [DIN_N:1] cnt_f; logic rdy_f; assign dev_rdy = ready && ~rdy_f; always @(negedge dev_clk or negedge ready) begin if (~ready) begin rdy_f <= '0; cnt_f <= '0; end else begin rdy_f <= 1'b1; // ~cnt_f[DIN_N-1] cnt_f <= (cnt_f<<1) | ~rdy_f; end end // logic [ADDR_WH-1:0] addr_wr ; logic [ DIN_N-1:0][DIN_WH-1:0] din_r ; logic [ DIN_N-1:0][DIN_WH-1:0] din_f ; logic [ DIN_N-1:0][DIN_WH-1:0] din_f_nx; assign din_f_nx = (din_r<<DIN_WH) | dev_din; always @(posedge dev_clk) begin din_r <= din_f_nx; end always @(negedge dev_clk) begin din_f <= (din_f<<DIN_WH) | dev_din; end // assign ram_wr_clk = ~dev_clk; assign ram_wr_ena = cnt_f[DIN_N-1]; assign ram_wr_done = cnt_f[DIN_N]; gray_count #(.WH(ADDR_WH), .ASYNC_RST(1)) i_addr_wr (.clk(ram_wr_clk), .rst(rst), .ena(ram_wr_ena), .dou(ram_wr_addr)); always_comb begin for (int ii=0; ii<DIN_N; ++ii) begin ram_wr_data[ii*2+0] = din_r[ii]; ram_wr_data[ii*2+1] = din_f_nx[ii]; end end endmodule `default_nettype wire Основной гемор будет с констрейнами. Так как есть 3 критический пути от dev_din к регистрам din_r, din_f и ram_wr_data. Ну и возможно задержка снятия dev_rdy после первого отрицательного клока. Но это уже на целевой FPGA надо смотреть. Если нужно будет выжать последний 5 MНz можно разные трюки применить кои не всегда по феншую синхронного дизайна делаются. Успехов! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба 1 hour ago, RobFPGA said: Тут классический elastic buffer. Разве что клок не постоянный. асинхронно-синхронный переход как он есть))) @SM был бы, он бы лютой асинхры из азиков привел)))) ЗЫ. а вы точно код тот привели? ~rdy_f всегда в нуле, стробы записи не поднимутся никогда) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба Приветствую! 17 minutes ago, des00 said: ЗЫ. а вы точно код тот привели? ~rdy_f всегда в нуле, стробы записи не поднимутся никогда) Это вы про это ? rdy_f <= 1'b1; // ~cnt_f[DIN_N-1] cnt_f <= (cnt_f<<1) | ~rdy_f; Предполагается что управление ready поднимается на все время передачи, rdy_f установится в 1 первом false edge, а по cnt_f при этом побежит 1. Я набросал это по быстрому и на симе не проверял - может и накосячил где. Если нужно принимать несколько посылок то можно сразу закольцевать ( закоменнтированй кусок). Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 2 ноября, 2019 Опубликовано 2 ноября, 2019 · Жалоба 3 minutes ago, RobFPGA said: Предполагается что управление ready поднимается на все время передачи, rdy_f установится в 1 первом false edge, а по cnt_f при этом побежит 1. Я набросал это по быстрому и на симе не проверял - может и накосячил где. логику я понял, все по класике, но вот единица не побежит) главное чтобы ТС понял что и как править) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться