Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Передача данных UART => SDRAM память
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
Flip-fl0p
Добрый вечер уважаемые форумчане. Хочу попросить вашего совета по правильной организации передачи данных между UART и SDRAM контроллером.
Хочу записать изображение в SDRAM память, и вывести его на LVDS матрицу.
Изображение представляет собой набор пикселей, где каждый пиксель состоит из 3 байтов данных. Каждый байт передает данные о базовой составляющей цвета (RGB). Передача изображения из компьютера в SDRAM память будет через UART (9600/8-N-1).
Мой UART приемник работает по классической схеме: через частоту в 16 раз больше бодовой и выборкой трех значений с середины битового интервала, с последующим мажорированием. После принятия байта приемником он поступает на модуль, который после принятия трех байт, отбрасывает ненужные мне разряды цвета, и объединяет их в одно слово, являющееся цветом пикселя, которое будет записываться в SDRAM память.
Таким образом каждые 3 байта я должен передавать данные SDRAM контроллеру (который может работать на частоте до 133 МГц) для их записи в память.
Соответственно мы в итоге должны передать данные между 2 клоковыми доменами UART => SDRAM контроллер.
У меня есть 2 варианта по организации передачи данных: использовать протокол Handshake (рукопожатие) или FIFO буфер. Первый вариант кажется предпочтительным, поскольку применение FIFO буфера неоправданно из-за того, что скорость записи данных в него будет несопоставимо ниже, чем я могу этот буфер читать, и буфер будет всегда пустой. Можно накопить данные в буфере, и периодически его считывать по мере заполнения. Но не вижу рациональных причин так делать. Вот и хотелось бы услышать Ваше мнение, как правильнее организовать передачу данных. Естественно UART приемник и SDRAM контроллер реализованы в ПЛИС.
iosifk
Цитата(Flip-fl0p @ Nov 6 2016, 19:14) *
Таким образом каждые 3 байта я должен передавать данные SDRAM контроллеру (который может работать на частоте до 133 МГц) для их записи в память.

Все красиво, кроме одного... Откуда Вы узнаете, где в потоке данных будет 1-й байт, а где 2-й и т.д.
Достаточно одного сбоя и канал перестанет работать правильно...
Flip-fl0p
Цитата(iosifk @ Nov 6 2016, 22:10) *
Все красиво, кроме одного... Откуда Вы узнаете, где в потоке данных будет 1-й байт, а где 2-й и т.д.
Достаточно одного сбоя и канал перестанет работать правильно...

Поскольку у меня приемник UART самописный - то я добавил сигнал валидности.
Когда байт принят - формируется короткий импульс, что данные приняты и их можно использовать. Считая эти импульсы я и делаю вывод о том, какой номер байта у меня принят. Вот модуль которым я это делаю. Я правда в железе его не отлаживал, поскольку макетная плата на работе. Но симуляцию вроде прошел успешно.
CODE
--=============================== PIXEL_CREATING =========================================
-- Данный модуль предназначен для приема 3 байт данных по UART и пребразования их
-- В одно слово, содержащее данные о цвете пикселя.

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY PIXEL_CREATING IS
GENERIC
(
COLOR_DEPTH : INTEGER := 12 --COLOR_DEPTH/3 Для одного цвета
);
PORT
(
CLK : IN STD_LOGIC; -- Частота UART ресивера
UART_DATA : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- Данные, выдавемые UART
DATA_VALID : IN STD_LOGIC; -- Сигнал подтверждения валидных данных рессивером
ASY_RESET : IN STD_LOGIC; -- Асинхронный сброс Синхронизирован в топ модуле.
PIXEL_COLOR : OUT STD_LOGIC_VECTOR(COLOR_DEPTH-1 DOWNTO 0); -- Выходной сигнал который является цветом пикселя
PIXEL_VALID : OUT STD_LOGIC
);
END ENTITY;

ARCHITECTURE PIXEL_CREATING_arc OF PIXEL_CREATING IS
SIGNAL BYTE_CNT : INTEGER RANGE 0 TO 2 := 0; -- Счетчик принятых байт
SIGNAL RED_COLOR_REG : STD_LOGIC_VECTOR(COLOR_DEPTH/3-1 DOWNTO 0) := (OTHERS => '0'); -- Регистр куда записывается красная составляющая пикселя
SIGNAL GREEN_COLOR_REG : STD_LOGIC_VECTOR(COLOR_DEPTH/3-1 DOWNTO 0) := (OTHERS => '0'); -- Регистр куда записывается зеленая составляющая пикселя
SIGNAL BLUE_COLOR_REG : STD_LOGIC_VECTOR(COLOR_DEPTH/3-1 DOWNTO 0) := (OTHERS => '0'); -- Регистр куда записывается синяя составляющая пикселя
SIGNAL LOAD_DATA : STD_LOGIC := '0'; -- Сигнал разрешения объедениения данных
BEGIN
BYTE_COUNTER : PROCESS
(
CLK,
ASY_RESET
)
BEGIN
IF (ASY_RESET = '1') THEN
BYTE_CNT <= 0 ;
LOAD_DATA <= '0';
PIXEL_VALID <= '0';
RED_COLOR_REG <= (OTHERS => '0');
GREEN_COLOR_REG <= (OTHERS => '0');
BLUE_COLOR_REG <= (OTHERS => '0');
PIXEL_COLOR <= (OTHERS => '0');
ELSIF (CLK'EVENT AND CLK = '1') THEN
LOAD_DATA <= '0'; -- Постоянно держим сигнал разрешения в нуле
PIXEL_VALID <= '0'; -- Постоянно держим сигнал валидности пикселя в нуле

IF (DATA_VALID = '1') THEN -- Как только поступил сигнал валидности данных
CASE BYTE_CNT IS -- Начинаем анализировать состояние счётчика

WHEN 0 => -- Есть сигнал валидности и счтчик равен 0
RED_COLOR_REG <= UART_DATA(COLOR_DEPTH/3-1 DOWNTO 0); -- Значит это были данные о красном цвете пикселя
BYTE_CNT <= BYTE_CNT + 1; -- Инкрементируем счетчик на 1

WHEN 1 => -- Есть сигнал валидности и счтчик равен 1
GREEN_COLOR_REG <= UART_DATA(COLOR_DEPTH/3-1 DOWNTO 0); -- Значит это были данные о зеленом цвете пикселя
BYTE_CNT <= BYTE_CNT + 1; -- Инкрементируем счетчик на 1

WHEN 2 => -- Есть сигнал валидности и счтчик равен 2
BLUE_COLOR_REG <= UART_DATA(COLOR_DEPTH/3-1 DOWNTO 0); -- Значит это были данные о синем цвете пикселя
LOAD_DATA <= '1'; -- Разрешам следующим тактом "собрать пиксель"
BYTE_CNT <= 0 ; -- Обнулим счетчик

WHEN OTHERS => BYTE_CNT <= 0 ; -- Обнуляем счётчик если он вышел за диапазон счета
END CASE;
END IF;

IF (LOAD_DATA = '1') THEN -- Как только наступило разрешение сбора пикселя
PIXEL_VALID <= '1'; -- Значит следующим тактом у нас будет валидный пиксель
PIXEL_COLOR <= BLUE_COLOR_REG&GREEN_COLOR_REG&RED_COLOR_REG; -- Соберем его
END IF;

END IF;
END PROCESS;
END ARCHITECTURE;

Я тоже думал про то, что если будет сбой, то счетчик байтов может сбиться. Но к сожалению у меня нет ответа на вопрос, что будет при сбое. Остаётся надежда только на то, что сбоя не будет, поскольку кроме UART у меня нет других способов связи между макетной платой и ПК. Связь осуществляется через микросхему FTDI FT2232H. В системе у меня 2 виртуальных COM порта, один из которых применяется для конфигурации ПЛИС, а через другой буду передавать данные. Надежность данного решения под вопросом. Но приходиться работать с тем, что имею. Как вариант найти какой-нибудь из протоколов на основе UART и передавать информацию посредством этого протокола. Но хочется обойтись "малой кровью"
ViKo
Сделать счетчик таймаута приема. Если он сработал, сбрасывать счетчик, он же адрес, байтов.
Передавать сплошным потоком, не допуская больших разрывов. Впрочем, можно выбрать нужный тамаут.
Александр77
Цитата(Flip-fl0p @ Nov 7 2016, 01:29) *
Я тоже думал про то, что если будет сбой, то счетчик байтов может сбиться. Но к сожалению у меня нет ответа на вопрос, что будет при сбое. Остаётся надежда только на то, что сбоя не будет, поскольку кроме UART у меня нет других способов связи между макетной платой и ПК...
...Как вариант найти какой-нибудь из протоколов на основе UART и передавать информацию посредством этого протокола. Но хочется обойтись "малой кровью"

Сами спросили, сами ответили и засомневались.
Раз есть вероятность сбоя - значит нужно поверх UART'а слепить протокол в котором будут присутствовать преамбула (начало передачи), некоторые служебные признаки, сами данные и результат для контрольной проверки.
Судя по тому, что данные значительные, наверное правильно их разбить. Для Вашего случая вполне возможно будет достаточно передавать протоколы с содержанием информации по отдельной строке или некоторому набору пикселей (например квадрату 16х16).
Flip-fl0p
Цитата(Александр77 @ Nov 7 2016, 07:16) *
Сами спросили, сами ответили и засомневались.
Раз есть вероятность сбоя - значит нужно поверх UART'а слепить протокол в котором будут присутствовать преамбула (начало передачи), некоторые служебные признаки, сами данные и результат для контрольной проверки.
Судя по тому, что данные значительные, наверное правильно их разбить. Для Вашего случая вполне возможно будет достаточно передавать протоколы с содержанием информации по отдельной строке или некоторому набору пикселей (например квадрату 16х16).

Без причины сбоить не должно. Данные передаю в плату простой командой, набранной в командной строке:
Код
mode com5 baud=9600 parity=n data=8
copy D:\bit /b com5
pause

Попробую через FIFO буфер передавать данные (буду его применять только потому, что примитив FIFO буфера у Altera есть готовый, и мне меньше работы). А дальше будет видно по изображению на матрице как передались данные. На стороне ПЛИС я могу делать всё что смогу, и реализовать почти любой протокол. Лишь бы хватило ресурсов в ПЛИС. А вот со стороны ПК передавать данные по UART через какой-либо протокол... У меня пока недостаточно знаний в области программирования, чтобы сделать такое. Вот и хочу обойтись "малой кровью".... Очень похожий проект на такой-же макетной плате заработал нормально. https://marsohod.org/projects/marsohod2/214-c3fb2.
Bad0512
Вижу 2 проблемы в вашем дизайне :
1. Вы не придумали как передавать адрес пиксела в SDR памяти. Поэтому при изменении хотя бы одного пиксела вам придется переписывать всю картинку, и ещё надо придумать как всё-таки сбрасывать адресный счетчик.
2. Передавать данные картинки по UART со скоростью 9600 - это оооооочень медленно. Посчитайте сколько времени займёт перезапись полной картинки (это вы ещё накладные расходы на протокол не учитывали).
Flip-fl0p
Цитата(Bad0512 @ Nov 7 2016, 08:13) *
Вижу 2 проблемы в вашем дизайне :
1. Вы не придумали как передавать адрес пиксела в SDR памяти. Поэтому при изменении хотя бы одного пиксела вам придется переписывать всю картинку, и ещё надо придумать как всё-таки сбрасывать адресный счетчик.
2. Передавать данные картинки по UART со скоростью 9600 - это оооооочень медленно. Посчитайте сколько времени займёт перезапись полной картинки (это вы ещё накладные расходы на протокол не учитывали).

С адресом вроде проблем нет. Первый пиксель пишется в ячейку матрицы с адресом (0,0) Второй пиксель по адресу (0,1). Я заполняю подряд ячейки памяти. Разрешение матрицы 800х480. Соответственно использую 384000 ячеек памяти. При чтении памяти каждому пикселю всегда строго соответствует своя ячейка памяти. Написав простенький автомат, который генерирует полосы я в этом убедился, поскольку изображение статично. Так-же если в память ничего не писать а просто её читать, так-же получается статичная картинка в виде "цветного матраса". Поскольку я передаю не видеопоток, а картинку, то пикселя изменяться и не должны.
Данные будут писаться примерно 2 минуты.
ViKo
Что мешает ускорить скорость передачи до 250 кбит/с?
tvcam
Я могу ошибиться, но не две минуты, 9600/8 = 1200 байт в секунду, 800*480*3 = 1152000 всего байт,
1152000/1200=960 секунд или 960/60=16 минут + накладные
Bad0512
Цитата(Flip-fl0p @ Nov 7 2016, 12:24) *
С адресом вроде проблем нет. Первый пиксель пишется в ячейку матрицы с адресом (0,0) Второй пиксель по адресу (0,1). Я заполняю подряд ячейки памяти. Разрешение матрицы 800х480. Соответственно использую 384000 ячеек памяти. При чтении памяти каждому пикселю всегда строго соответствует своя ячейка памяти. Написав простенький автомат, который генерирует полосы я в этом убедился, поскольку изображение статично. Так-же если в память ничего не писать а просто её читать, так-же получается статичная картинка в виде "цветного матраса". Поскольку я передаю не видеопоток, а картинку, то пикселя изменяться и не должны.
Данные будут писаться примерно 2 минуты.

Ну хорошо, вот вы записали всю вашу картинку целиком. В результате указатель памяти будет указывать на место в памяти сразу за последним пикселом. Что вы будете делать дальше? Сбрасывать всю ПЛИС ради установки одного счетчика? Ну то есть по записи картинки жизнь вашей системы закончилась?
По скорости записи : при скорости UARTа 9600 реальная скорость передачи данных с учетом старт и стоп битов будет 9600/10*8 = 7680 бит в секунду или 960 байт в секунду. При размере картинки 800*480*3 байта = 1152000 байт время записи составит 20 минут! И мы тут ещё не учитываем накладные. Не очень хорошая скорость для системы отображения - один кадр в 20 минут. sad.gif
Flip-fl0p
Цитата(Bad0512 @ Nov 7 2016, 09:59) *
Ну хорошо, вот вы записали всю вашу картинку целиком. В результате указатель памяти будет указывать на место в памяти сразу за последним пикселом. Что вы будете делать дальше? Сбрасывать всю ПЛИС ради установки одного счетчика? Ну то есть по записи картинки жизнь вашей системы закончилась?
По скорости записи : при скорости UARTа 9600 реальная скорость передачи данных с учетом старт и стоп битов будет 9600/10*8 = 7680 бит в секунду или 960 байт в секунду. При размере картинки 800*480*3 байта = 1152000 байт время записи составит 20 минут! И мы тут ещё не учитываем накладные. Не очень хорошая скорость для системы отображения - один кадр в 20 минут. sad.gif

У меня есть автомат который управляет записью и чтением контроллера SDRAM. После того, как запись будет закончена, автомат перейдет в состояние чтения, и начнет читать данные начиная с адреса (0,0). Как только прочитал все ячейки с данными, он опять начинает читать. Таким образом после записи, автомат начинает циклически читать данные по кругу. Данные с памяти постоянно отправляются на асинхронный FIFO буфер, через который уже и выводятся на матрицу. А вот со скоростью UART беда, действительно 20 минут получается... Буду брать большую частоту. Думаю надо работать с частотой 115 200 (8-N-1).
А про какой указатель памяти Вы имели ввиду ?
iosifk
Цитата(Flip-fl0p @ Nov 7 2016, 08:06) *
Без причины сбоить не должно. Данные передаю в плату простой командой, набранной в командной строке:
Код
mode com5 baud=9600 parity=n data=8
copy D:\bit /b com5
pause

Уверяю Вас, сбоит всегда подло и без причины...
И если одна посылка хотя бы сбойнет, то все следующее за ней пойдет криво. И у Вас не будет возможности понять что и где произошло. Только сбросить все и перезагрузиться...
И тайм-ауты не помогут. Это же винды на хосте, а они без причины могут запустить проверку почты или что-то еще...
Bad0512
Цитата(Flip-fl0p @ Nov 7 2016, 14:29) *
У меня есть автомат который управляет записью и чтением контроллера SDRAM. После того, как запись будет закончена, автомат перейдет в состояние чтения, и начнет читать данные начиная с адреса (0,0). Как только прочитал все ячейки с данными, он опять начинает читать. Таким образом после записи, автомат начинает циклически читать данные по кругу. Данные с памяти постоянно отправляются на асинхронный FIFO буфер, через который уже и выводятся на матрицу. А вот со скоростью UART беда, действительно 20 минут получается... Буду брать большую частоту. Думаю надо работать с частотой 115 200 (8-N-1).
А про какой указатель памяти Вы имели ввиду ?

Мне почему-то показалось что у вас порты на запись и на чтение SDRAM разные и работают независимо.Под указателем имелся ввиду счетчик адреса на запись. Ну а если у вас порт один, то следующим усовершенствованием в вашей системе будет независимая схема (один порт - только на запись, один - только на чтение). Дальше можно ещё добавлять портов, с помощью арбитра разделять по времени доступ к SDRAM и так далее. В идеале все порты могут жить в разных клоковых доменах, тем более производительности SDRAM хватает, в общем многие вещи тут можно улучшить.
warrior-2001
Реализуйте что-то типа Манчестера и будет вам счастье. Накладные ресурсы - не большие. CRC - можно и не считать, хотя желательно. Поднять и на ПК и на ПЛИС - не сложно.
iosifk
Цитата(Flip-fl0p @ Nov 7 2016, 08:06) *
На стороне ПЛИС я могу делать всё что смогу, и реализовать почти любой протокол. Лишь бы хватило ресурсов в ПЛИС. А вот со стороны ПК передавать данные по UART через какой-либо протокол... У меня пока недостаточно знаний в области программирования, чтобы сделать такое.

Я могу Вам по скайпу перебросить ксайлинсовский пример... Как будете на связи, вызовите меня...
называется: wave_gen_vhd_v6.zip
Flip-fl0p
Реализовал я передачу данных между UART => ПЛИС следующим образом:
Для увеличения скорости передачи данных я выбрал скорость UART 230400 (8-N-1). Думаю можно и больше, но не тестировал.
Данные с uart приемника подаются в модуль PIXEL_CREATING, который принимает 3 байта, отбрасывает ненужные мене значения байтов и объединяет их в цвет пикселя, который мне нужно записать в память.
Когда модуль PIXEL_CREATING "собрал" пиксель и готов его записывать в память, формируется одиночный сигнал PIXEL_VALID (равный одному периоду синхрочастоты), который разрешает запись данных в FIFO буфер.
В FIFO буфере у меня настроен сигнал rdempty, синхронизированный(внутри FIFO буфера)с частотой работы SDRAM контроллера, которой я этот буфер читаю. Через 22 такта, (почему именно 22 такта я не знаю, есть зависимость от количества стадий синхронизации, настраиваемых в mega wizzard. В datasheet на эту мегафункцию, я не нашел данных про это. Буду благодарен, если услышу пояснения почему так.) после записи, буфер выдаст сигнал rdempty равный лог.0, который говорит о том, что в буфере есть данные. Этот сигнал я подаю на детектор заднего фронта. Детектированным фронтом READ_UART_FIFO_SIG я читаю данные из FIFO буфера. Этот же фронт- сигнал о том, что можно записывать данные в контроллер SDRAM. Таким образом я реализовал передачу данных между клоковомыи доменами. То, что я теряю 22 такта - меня не смущает, поскольку частоты приёма по UART и частоты записи в память очень сильно отличаются, и за время приёма одного байта по UART пройдет гораздо больше времени чем 22 такта...
Данные передаются нормально, сбоев нет. Изображение передано в матрицу без искажений.
Кому интересно, проект приёмника UART c модулем PIXEL_CREATING выложил внизу. Буду рад всем недочётам, обнаруженным в проекте.




Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.