|
Block RAM на VHDL в Spartan3, Could not implement Block RAM. Is the read address registered using th |
|
|
|
Jun 28 2006, 09:38
|

Участник

Группа: Участник
Сообщений: 65
Регистрация: 7-09-05
Из: г. Новосибирск
Пользователь №: 8 335

|
При описании памяти как массива производится чтение и запись по разным адресам, при этом чтение производится ассинхронно, а запись синхронно. При объявлении массива указываю атрибут: attribute syn_ramstyle : string; attribute syn_ramstyle of data_array_1 : signal is "block_ram";--"no_rw_check";
При этом Synplify Pro 8.4 в отчете выдает следующее сообщение: Could not implement Block RAM. Is the read address registered using the same clock as the RAM?
Код примерно следующий:
entity name is clock : in std_logic; din : in signed( 15 downto 0 ); write : in std_logic; dout : out signed( 15 downto 0 ); ... end name;
architecture name_body of name is
subtype data_type is signed( 15 downto 0 ); type data_array_type is array( 0 to 7 ) of data_type; signal data_array_1 : data_array_type := ( others => to_signed( 0, data_type'length ) ); signal data_array_2 : data_array_type := ...; attribute syn_ramstyle : string; attribute syn_ramstyle of data_array_1 : signal is "block_ram";--"no_rw_check"; attribute syn_ramstyle of data_array_2 : signal is "block_ram";--"no_rw_check";
signal rd1_addr, wr1_addr : unsigned( 2 downto 0 ) := to_unsigned( 0, 4 ); signal rd2_addr, wr2_addr : unsigned( 2 downto 0 ) := to_unsigned( 0, 4 ); signal A, C : data_type := to_signed( 0, data_type'length );
body
A <= data_array_1( to_integer( rd1_addr ) ); C <= data_array_2( to_integer( rd2_addr ) );
process( reset, clock ) begin if reset = '1' then A <= to_signed( 0, data_type'length ); C <= to_signed( 0, data_type'length ); rd1_addr <= ...; wr1_addr <= ...; rd2_addr <= ...; wr2_addr <= ...; ... elsif rising_edge( clock ) then dout <= A * C; if write = '1' then data_array_1( to_integer( wr1_addr ) ) <= din; end if; rd1_addr <= rd1_addr + 1; wr1_addr <= wr1_addr + 1; rd2_addr <= rd2_addr + 1; end if; end process;
end name_body;
Как нужно описывать RAM в виде массива, чтобы Synplify сделал из него BlockRAM?
|
|
|
|
|
Jun 28 2006, 09:43
|
Местный
  
Группа: Свой
Сообщений: 265
Регистрация: 15-03-05
Из: Москва
Пользователь №: 3 367

|
Цитата(Vadim_nsk @ Jun 28 2006, 13:38)  При описании памяти как массива производится чтение и запись по разным адресам, при этом чтение производится ассинхронно, а запись синхронно. При объявлении массива указываю атрибут: attribute syn_ramstyle : string; attribute syn_ramstyle of data_array_1 : signal is "block_ram";--"no_rw_check";
При этом Synplify Pro 8.4 в отчете выдает следующее сообщение: Could not implement Block RAM. Is the read address registered using the same clock as the RAM?
Код примерно следующий:
entity name is clock : in std_logic; din : in signed( 15 downto 0 ); write : in std_logic; dout : out signed( 15 downto 0 ); ... end name;
architecture name_body of name is
subtype data_type is signed( 15 downto 0 ); type data_array_type is array( 0 to 7 ) of data_type; signal data_array_1 : data_array_type := ( others => to_signed( 0, data_type'length ) ); signal data_array_2 : data_array_type := ...; attribute syn_ramstyle : string; attribute syn_ramstyle of data_array_1 : signal is "block_ram";--"no_rw_check"; attribute syn_ramstyle of data_array_2 : signal is "block_ram";--"no_rw_check";
signal rd1_addr, wr1_addr : unsigned( 2 downto 0 ) := to_unsigned( 0, 4 ); signal rd2_addr, wr2_addr : unsigned( 2 downto 0 ) := to_unsigned( 0, 4 ); signal A, C : data_type := to_signed( 0, data_type'length );
body
A <= data_array_1( to_integer( rd1_addr ) ); C <= data_array_2( to_integer( rd2_addr ) );
process( reset, clock ) begin if reset = '1' then A <= to_signed( 0, data_type'length ); C <= to_signed( 0, data_type'length ); rd1_addr <= ...; wr1_addr <= ...; rd2_addr <= ...; wr2_addr <= ...; ... elsif rising_edge( clock ) then dout <= A * C; if write = '1' then data_array_1( to_integer( wr1_addr ) ) <= din; end if; rd1_addr <= rd1_addr + 1; wr1_addr <= wr1_addr + 1; rd2_addr <= rd2_addr + 1; end if; end process;
end name_body;
Как нужно описывать RAM в виде массива, чтобы Synplify сделал из него BlockRAM? Канал чтения должен быть также синхронным. Не поленитесь, посмотрите в документацию на Synplify, там все достаточно подробно расписано
|
|
|
|
|
Jun 28 2006, 11:47
|

Участник

Группа: Участник
Сообщений: 65
Регистрация: 7-09-05
Из: г. Новосибирск
Пользователь №: 8 335

|
Цитата(oval @ Jun 28 2006, 16:43)  Канал чтения должен быть также синхронным. Не поленитесь, посмотрите в документацию на Synplify, там все достаточно подробно расписано  Да в том то и дело, что не поленился... Может я конечно чего-то недопонимаю... Пример из файла reference.pdf, стр. 10-84 (документация Synplify): "Two-write Port RAM Example" WRITE_RAM : process (clk) begin if rising_edge(clk) then if (wren_a = '1') then mem(to_integer(unsigned(addr_a))) <= data_a; end if; if (wren_b='1') then mem(to_integer(unsigned(addr_b))) <= data_b; end if; addr_a_reg <= addr_a; addr_b_reg <= addr_b; end if; end process WRITE_RAM; q_a <= mem(to_integer(unsigned(addr_a_reg))); q_b <= mem(to_integer(unsigned(addr_b_reg))); Как видно, чтение ассинхронное. Чтение внутрь процесса вносил (под клок), результат тот же... Что же касается сброса, то он относится не к памяти, а к всему вычислительному блоку (надо же мне както инициализировать сигналы). Готовые RAM блоки, рекомендованые под архитектуру спартана использовать можно, но их тяжеловато подстраивать под конкретный размер массива данных. Я пытался сделать компонент в общем виде под задаваемую разрядность и объем данных.
|
|
|
|
|
Jun 28 2006, 12:09
|

Гуру
     
Группа: Модераторы
Сообщений: 2 095
Регистрация: 27-08-04
Из: Россия, СПб
Пользователь №: 553

|
Возможно, что дело еще в: Код rd1_addr <= rd1_addr + 1; wr1_addr <= wr1_addr + 1; rd2_addr <= rd2_addr + 1; Добавьте дополнительные регистры в код :rd1_addr_br,wr1_addr_br,rd2_addr_br и делайте примерно так: Код ... --synthesis translate_off rd1_addr <= ...; wr1_addr <= ...; rd2_addr <= ...; wr2_addr <= ...; --synthesis translate_on ... data_array_1( to_integer( wr1_addr_br ) ) <= din; ... wr1_addr_br<=wr1_addr+1; wr1_addr<=wr1_addr+1; ...
|
|
|
|
|
Jun 28 2006, 12:16
|
Местный
  
Группа: Свой
Сообщений: 265
Регистрация: 15-03-05
Из: Москва
Пользователь №: 3 367

|
Цитата(Vadim_nsk @ Jun 28 2006, 15:47)  Цитата(oval @ Jun 28 2006, 16:43)  Канал чтения должен быть также синхронным. Не поленитесь, посмотрите в документацию на Synplify, там все достаточно подробно расписано  Да в том то и дело, что не поленился... Может я конечно чего-то недопонимаю... Пример из файла reference.pdf, стр. 10-84 (документация Synplify): "Two-write Port RAM Example" WRITE_RAM : process (clk) begin if rising_edge(clk) then if (wren_a = '1') then mem(to_integer(unsigned(addr_a))) <= data_a; end if; if (wren_b='1') then mem(to_integer(unsigned(addr_b))) <= data_b; end if; addr_a_reg <= addr_a; addr_b_reg <= addr_b; end if; end process WRITE_RAM; q_a <= mem(to_integer(unsigned(addr_a_reg))); q_b <= mem(to_integer(unsigned(addr_b_reg))); Как видно, чтение ассинхронное. Чтение внутрь процесса вносил (под клок), результат тот же... Обратите внимание, что адрес чтения защелкивается. Посмотрите в документе Synplicity FPGA Synthesis раздел Design Optimization -> Inferring RAMs. Цитата Что же касается сброса, то он относится не к памяти, а к всему вычислительному блоку (надо же мне както инициализировать сигналы). Разнесите по разным процессам память и остальную логику.
|
|
|
|
|
Jun 28 2006, 13:11
|
Местный
  
Группа: Свой
Сообщений: 244
Регистрация: 2-10-04
Из: Мухосранска
Пользователь №: 763

|
Цитата Почему-то никто ни слова не произнес про COREGEN. Потому что не удобно.Сегодня я хочу память в 2К слов.Завтра дадут ценное указание и память станет 4К слов.А послезавтра разрядность увеличить придётся. По мне править код намного проще и удобней, чем занового генерить ядра.
|
|
|
|
|
Jun 30 2006, 09:32
|

Местный
  
Группа: Свой
Сообщений: 449
Регистрация: 28-10-04
Из: Украина
Пользователь №: 1 002

|
Цитата(maior @ Jun 29 2006, 18:26)  Hо ведь есть еще стандартные примитивы памяти (блочной и распределенной), на базе которых можно сделать универсальные параметризиремые (через generic) бибиотечные элементы. Я так и делал для виртексов 2 и 4 (один элемент годился для обоих и позволял строить память почти любой конфигурации!). Для спартанов - тоже не должно быть проблем. И не заморачивался ни с coregen, ни с behaviorial, которое по-разному и неустойчиво интерпретируется разными синтезаторами в разных условиях и для разных (даже слегка разных!) чипов. Фактически, вы руками доделали некоторую часть работы. Которую запросто мог за вас сделать Кореген. Более того, вам пришлось разбираться с тонкостями использования тех примитивов - причем обременительными тонкостями, типа зануления старших адресов если используется не вся память и построение дешифраторов, если требуется бОльшая. И зачем?! Когда есть кореген, одним движением пальца делающий все это. Насчет переносимости, разных синтезаторов и проч. Сдается мне, что проблему сильно преувеличивают. Не знаю, кто как, но я только один раз кардинально менял окружение. С Леонардо Спектрум для Atmel FPSLIC прыгнул на Spartan2 и XST (ISE). Разумеется, блоки памяти потребовали кардинальной переделки. Которая заключалась всего лишь в генерации оных в Корегене. Затем перешел на Спартан 3, так ничего и не поменялось. Сгенерировал то же самое в Корегене. А теперь пожалуйста ответьте, как часто вам, уважаемые коллеги, пришлось переходить (в ходе разработки ОДНОГО изделия) на кардинально другие FPGA и кардинально другие синтезаторы? Ото ж... А на рихтовку своих исходников с целью сделать их универсальными вы потратите очень много времени с непредсказуемым результатом. (речь идет об аппаратно-специфических вещах, память, DDR регистры и проч.). Моя идеология проста: делаем на plain VHDL все, что можно. По возможности без атрибутов и прочих бубенцов. Все специфическое включаем в блэк-боксы, их генерируем в целевой среде. Констрейны и прочие весьма зависимые прибамбасы (распиновка, типы выводов и тд) делаем исключительно в целевой среде. И не паримся. При переходе на что-то другое у нас остается везде компилируемый чистый VHDL, блэк-боксы создаем по-новой - тут уж никуда не деться. А констрейны, пинауты и иже с ними в новой среде все равно придется вбивать по-новой. Каламбурчик.
--------------------
Умею молчать на 37 языках...
|
|
|
|
|
Jun 30 2006, 12:58
|
Частый гость
 
Группа: Свой
Сообщений: 177
Регистрация: 21-10-04
Пользователь №: 948

|
Цитата(Gorby @ Jun 30 2006, 13:32)  ... Фактически, вы руками доделали некоторую часть работы. Которую запросто мог за вас сделать Кореген....
... Более того, вам пришлось разбираться с тонкостями использования тех примитивов - причем обременительными тонкостями,....
.. А на рихтовку своих исходников с целью сделать их универсальными вы потратите очень много времени с непредсказуемым результатом. (речь идет об аппаратно-специфических вещах, память, DDR регистры и проч.)... ОДИН раз сделал - и больше уже не пришлось делать что-то еще.. Вот уже пару лет как пользую свою библиотеку памяти для разных проектов. Идея была создать для зайлинкса такой же набор LPM памяти какая есть у альтеры. У меня они даже совместимы по entity и generic, так-что можно прыгать с зайлинкса на альтеру без труда. Посмотрел бы я на вас с вашим corgen в такой ситуации. Coregen (для памяти) - в отстое уже давно. Он явно неудобен, о чем тут уже говорилось, не стоит об этом спорить. А с plane (behaviorial) HDL - все равно будут проблемы, и в конце концов проковыряешся больше, что собственно и доказывают посты в этой ветке. Нету там никаких особых тонкостей. Только заглянуть пару раз в мануалы, что никогда не помешает. А времени потратил до смешного немного. Даже удивительно, отчего это зайлинкс сам не сделал эту работу, например как альтера. Но опять же - на вкус и цвет...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|