|
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 - все равно будут проблемы, и в конце концов проковыряешся больше, что собственно и доказывают посты в этой ветке. Нету там никаких особых тонкостей. Только заглянуть пару раз в мануалы, что никогда не помешает. А времени потратил до смешного немного. Даже удивительно, отчего это зайлинкс сам не сделал эту работу, например как альтера. Но опять же - на вкус и цвет...
|
|
|
|
|
Aug 23 2006, 09:08
|

Частый гость
 
Группа: Свой
Сообщений: 192
Регистрация: 23-11-05
Из: г. Москва
Пользователь №: 11 307

|
У меня Spartan2 а не 3, но думаю это непринципиально. Мне нужно написать двухпортовую RAM с разными клоками на концах с одной стороны только запись с we а с другой только чтение. на сайте Xilinx есть примеры описания НО мне нужна память с разной разрядностью шин на разных портах, там такого нет. Есть вариант использовать готовую память типа RAMB4_Sm_Sn или, как говорилось выше, использовать COREGEN, но хочется иметь возможность изменять под себя (выбирать фронты и пр.) ну и по-возможности не использовать черных ящиков. Очевидно, что написать самому возможно, но пока не получается. Вероятно кто-то уже возился с этим. Пишу на VHDL, но Verilog тоже прочитаю.
Сообщение отредактировал qwqw - Aug 23 2006, 09:10
|
|
|
|
|
Aug 23 2006, 19:42
|

Знающий
   
Группа: Свой
Сообщений: 541
Регистрация: 11-04-05
Из: Москва
Пользователь №: 4 045

|
Пытался делать такую штуку в ISE 4.2. Были проблемы с синтезом (надо было писать и читать по обоим портам). Судя по тому, что приводится в Language templates, ситуация мало изменилась, хотя попробовать и не мешает: Код process (<clockA>) begin if (<clockA>'event and <clockA> = '1') then if (<enableA> = '1') then if (<write_enableA> = '1') then <ram_name>(conv_integer(<addressA>)) <= <input_dataA>; end if; <ram_outputA> <= <ram_name>(conv_integer(<addressA>)); end if; end if; end process;
process (<clockB>) begin if (<clockB>'event and <clockB> = '1') then if (<enableB> = '1') then <ram_outputB> <= <ram_name>(conv_integer(<addressB>)); end if; end if; end process; Единственный выход в данном случае - использовать библиотечные элементы. По поводу разной разрядности на портах - попробуйте все это описать на поведенческом уровне следующим образом - массив (который память) используйте с минимальной используемой разрядностью, а при описании чтения и записи по другому порту используйте несколько индексов массива. Например так: Код <ram_outputB> <= <ram_name>(conv_integer(<addressB>*2 + 1)) & <ram_name>(conv_integer(<addressB>*2)); Это в случае, если разрядность различается в 2 раза. Посмотрите, что Вам синтезатор на это скажет. Если схавает - ваша взяла, если нет - ничего не попишешь. Кстати, тут возможна и другая проблема - в библиотечных элементах может не найтись памяти с нужной комбинацией разрядности портов и/или размером (например мне в Спартане 2е понадобилась память объемом 4КБайт и разрядностью 8 и 32). Тут может оказаться удобным объединение выходов нескольких BRAM при помощи OR или XOR и использовании входов сброса выходных регистров BRAM. Понятно как - на те BRAM, что не используем подаем сигнал сброса, после OR или XOR получаем то, что нужно. Правда, это может оказаться слишком медленно.
Сообщение отредактировал BSV - Aug 23 2006, 19:43
--------------------
Дурак, занимающий высокий пост, подобен человеку на вершине горы - все ему кажется маленьким, а всем остальным кажется маленьким он сам. /Законы Мерфи/
|
|
|
|
|
Aug 24 2006, 08:44
|

Частый гость
 
Группа: Свой
Сообщений: 192
Регистрация: 23-11-05
Из: г. Москва
Пользователь №: 11 307

|
разрядность портов: A (запись) = 8 B(чтение) = 32 глубина от 1024 байт и выше ваш вариант (и смежные с ним) я уже пробовал примерно так: Код -------------- A side ------------------------- my_RAM_A_side:process(clkA) begin if clkA'event and clkA = '1' then --- if WEA='1' then ram_block(conv_integer(AddrA))<=DIA;end if; --- end if; -- clk end process my_RAM_A_side; ----------------------------------------------- -------------- B side ------------------------- my_RAM_B_side:process(clkB) begin if clkB'event and clkB = '1' then --- --read_addr<=4*AddrB;--conv_integer(AddrB); read_addr0<=4*AddrB; read_addr1<=4*AddrB+1; read_addr2<=4*AddrB+2; read_addr3<=4*AddrB+3; --- end if; -- clk end process my_RAM_B_side; ---- ---- --DOB <= ram_block(read_addr)&ram_block(read_addr+1)&ram_block(read_addr+2)&ram_block(read_addr+3); DOB( 7 downto 0)<= ram_block(read_addr0); DOB(15 downto 8)<= ram_block(read_addr1); DOB(23 downto 16)<= ram_block(read_addr2); DOB(31 downto 24)<= ram_block(read_addr3); ----------------------------------------------- но увы не получается я могу использовать 2 закаскадированых RAMB4_S4_S16, но хочется все же самому закодить
|
|
|
|
|
Aug 24 2006, 11:40
|

Знающий
   
Группа: Свой
Сообщений: 541
Регистрация: 11-04-05
Из: Москва
Пользователь №: 4 045

|
Попробовал - таки да, не хавает, говорит, что вместо памяти триггеров навставляет. Ну и шут с ним. Для вас есть вариант - объединить 4 синтезированных блока xxx_S8_S8 способом, который я описал выше, хотя, вам же по 8-разрядному порту не надо читать - так что это и не нужно. С двумя блоками фокус не пройдет - выравнивание поедет (PORTB(x) /= PORTA(x*4+3) & PORTA(x*4+2) & PORTA(x*4+1) & PORTA(x*4)).
--------------------
Дурак, занимающий высокий пост, подобен человеку на вершине горы - все ему кажется маленьким, а всем остальным кажется маленьким он сам. /Законы Мерфи/
|
|
|
|
|
Aug 24 2006, 14:05
|

Частый гость
 
Группа: Свой
Сообщений: 192
Регистрация: 23-11-05
Из: г. Москва
Пользователь №: 11 307

|
насчет ошибки я не согласен, если посмотрите в примерах там сделано аналогично. Да и раньше я делал такую память(только с одинаковой разрядностью) и все благополучно паковалось в BRAM. Цитата Для вас есть вариант - объединить 4 синтезированных блока xxx_S8_S8 способом, который я описал выше, хотя, вам же по 8-разрядному порту не надо читать - так что это и не нужно. С двумя блоками фокус не пройдет - выравнивание поедет (PORTB(x) /= PORTA(x*4+3) & PORTA(x*4+2) & PORTA(x*4+1) & PORTA(x*4)). я уже сделал, как говорил выше: каскадировал 2 блока RAMB4_S4_S16 и как раз получается 8/32 и глубина 1024. Соответственно 1-й RAMB4_S4_S16 - на вход младшая тетрада на 2-й - старшая и выход разобран соответствующим образом. Все работает и в BRAM упаковалось. Однако камень предкновения во фронтах. Если не получится с положительными сделать, придется тратить GBUF с CLKDLL (брать противофазный клок), а с ними тоже напряг.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|