Jump to content

    

"Схемотехнические трюки для ПЛИСоводов"

да я тоже пользуюсь 2-пробельной табуляцией. именно поэтому так широко размазано и получилось. но думаю это переживабельно.

 

очередь с рукопожатием на обоих портах. может быть сконфигурирована на использование block RAM так и на обычной логике (в зависимости от установленного атрибута).

для указателей на концы очереди использована тривиальная схема кодирования (бинарный код) так что не советую использовать в самолётах, спутниках и в районе экватора. можно только для поиграться.

есть две ноги для заглядывания в будущее (может быть полезны для поддержания непрерывного потока данных интерфейсных КА).

 

замеры не делал, потому как там мало управляющей логики.

 

описание:

parameterizable queue with a trivial pointer encodings

can be implementer eather with on-chip Block RAM or distributed memory, subject to synthesis attribute setting of the stack memory

when implemeted with a RAM block adopt the first-word-falls-through policy for zero latency immediate pop

provides forward read and write grant signals for smooth operation of FSMs controlling the data-flows on both ports of the channel(queue)

блин, други, я чичас чуть не поседел!

 

ВНИМАНИЕ! выложенное фифо содержит ошибку!!!

 

ошибка в неправильном расчёте ширины счётчиков при помощи стандартной функции $clog2()

раньше два параметра рассчитывались с помощью самописной функции

localparam ptr_width=number_of_bits_for_value_representation(stack_depth-1);

localparam cnt_width=number_of_bits_for_value_representation(stack_depth);

вот эта функция

function int number_of_bits_for_value_representation(input int value);
  int temp;
  temp    =    2;
  number_of_bits_for_value_representation    =    1;
  while (temp<=value)
    begin
      number_of_bits_for_value_representation = number_of_bits_for_value_representation+1; 
      temp  = temp*2;
    end
endfunction

 

перед отправкой на форум я решил выпендриться и заменил расчёт этих параметров на

localparam ptr_width=$clog2(stack_depth-1);

localparam cnt_width=$clog2(stack_depth);

и не проверив работу в симуляторе и не подумав о том, что результат будет уже другой - ширина для cnt_width на один бит меньше, чем положено

 

представляете мои ощущения, когда я в четверг запустил систему, которая была полностью отлажена и которую я не трогал пол года.

2 дня сидел разбирался почему система перестала корректно работать.

 

в общем звиняйте

итого. чтобы правильно работало с $clog2 нужно написать вот так

localparam ptr_width=$clog2(stack_depth);

localparam cnt_width=$clog2(stack_depth+1);

 

попрошу модератора, чтобы исправил исходный пост.

 

файл с изменениями:

Ring_Buffer_gnt_forward_fwft_ram_based.txt

Share this post


Link to post
Share on other sites
Что-то все про тему забыли... :)

у меня нет времени, работы тьма тьмущая :wacko:

Share this post


Link to post
Share on other sites

В схемах автоматики часто применяются интеграторы,здесь пример параметризуемого знакового интегратора с насыщением.Схема очень простая,пояснений не требуется.

src.rar

Share this post


Link to post
Share on other sites

Да-а-а-а-а,не удалось раскрутить общественность.А жаль,умирает тема...

Share this post


Link to post
Share on other sites

В качестве подпитки темы сюда можно выложить Synthesis Style Guide для разных компиляторов. У меня есть Precision® Synthesis Style Guide и HDL Coding Styles от Altera.

В принципе эти документы доступны всем, но кто их читает ;)

Share this post


Link to post
Share on other sites
Да-а-а-а-а,не удалось раскрутить общественность.А жаль,умирает тема...

у мну например цейтнот последние полгода. скоро разберусь уже с делами и буду вывешивать - есть достаточно разных мелких полезностей. так что никто не забыт и ничто не забыто. просто временно отложено.

Share this post


Link to post
Share on other sites
у мну например цейтнот последние полгода. скоро разберусь уже с делами и буду вывешивать - есть достаточно разных мелких полезностей. так что никто не забыт и ничто не забыто. просто временно отложено.

Вот,вот, именно "мелкие полезности" и представляют наибольший интерес,особенно для молодёжи.Я после праздников тоже выложу ещё один блок(вычисление модуля без квадратов и корней).

Share this post


Link to post
Share on other sites

Вот еще вариант двухклокового FIFO, на VHDL.

library ieee;
library ieee;
use IEEE.std_logic_1164.all, 
IEEE.std_logic_unsigned.all,
IEEE.std_logic_arith.all;

entity dc_fifo is
GENERIC
(
 WIDTHA : integer := 8;
 WIDTH  : integer := 8
);
PORT
(
 rst   : in  std_logic;

 wr_clk: in  std_logic;							 -- write clock
 wr_ena: in  std_logic;							 -- write clock enable   
 wr_dta: in  std_logic_vector(WIDTH-1 downto 0);	-- write data

 rd_clk: in  std_logic;							 -- read clock 
 rd_ena: in  std_logic;							 -- read enable
 rd_dta: out std_logic_vector(WIDTH-1 downto 0);	-- read data

 rd_empty, rd_full, rd_half : out boolean;		  -- read side status
 wr_empty, wr_full, wr_half : out boolean		   -- write side status
);

end dc_fifo;


architecture behavior of dc_fifo is

 constant N : integer := (2**WIDTHA);
 constant ADR_MAX : integer := (2**WIDTHA)-1;

 subtype ADDRESS is integer range 0 to ADR_MAX;
 subtype DATA	is std_logic_vector(WIDTH-1 downto 0);
 type FIFO_RAM is array (0 to ADR_MAX) of DATA;

 -- binary representation of addersses
 signal wr_b_adr   : ADDRESS;
 signal rd_b_adr   : ADDRESS;
 -- gray code representation of addresses
 signal wr_g_adr   : ADDRESS;
 signal rd_g_adr   : ADDRESS;
 -- next write signal
 signal wr_add	 : integer range 0 to 1;
 signal wr_next	: ADDRESS;

 signal ram		: FIFO_RAM;
 attribute syn_ramstyle : string;
 attribute syn_ramstyle of ram : signal is "no_rw_check";


 function bin2gray( a : ADDRESS ) return ADDRESS is
variable u, v : std_logic_vector( WIDTHA-1 downto 0 );
variable b : ADDRESS;
 begin
v := conv_std_logic_vector( a, WIDTHA );
u := v xor ('0' & v(WIDTHA-1 downto 1));
b := ADDRESS(conv_integer('0' & u));	
return b;
 end;


 function gray2bin( a : ADDRESS ) return ADDRESS is
variable u, v : std_logic_vector( WIDTHA-1 downto 0 );
variable b : ADDRESS;
variable x : std_logic;
 begin
v := conv_std_logic_vector( a, WIDTHA );

for i in u'range loop
  x := '0';
  for j in v'range loop
	if (j>=i) then
	  x := x xor v(j);
	end if;
  end loop;
  u(i) := x;
end loop;

b := ADDRESS(conv_integer('0' & u));	
return b;
 end;

 function fifo_size( w, r : ADDRESS ) return ADDRESS is
constant N : integer := (2**WIDTHA);
variable b : ADDRESS;
 begin
b := (N + w - r) mod N;
return b; 
 end;


begin

 wr_add  <= 1 when wr_ena='1' else 0;
 wr_next <= ( wr_b_adr + wr_add ) mod N;

 WRITE_PART:process(rst,wr_clk)
 begin

if rst='0' then
  wr_b_adr <= 0;
  wr_g_adr <= bin2gray( 0 );
  wr_empty <= true;
  wr_full <= false;
  wr_half <= false;
elsif rising_edge(wr_clk) then

  -- fix values. Needed for better inferring RAM block
  wr_b_adr  <= wr_next;
  wr_g_adr  <= bin2gray( wr_next );

  wr_empty <= fifo_size( wr_b_adr, gray2bin(rd_g_adr) ) < 2;
  wr_full  <= fifo_size( wr_b_adr, gray2bin(rd_g_adr) ) >= N-4;
  wr_half  <= fifo_size( wr_b_adr, gray2bin(rd_g_adr) ) >= N/2;

end if;
 end process;

 RAM_STORE:process(wr_clk)
 begin
-- perform write, no reset dependency
if rising_edge(wr_clk) then
  if wr_ena='1' then
	ram(wr_b_adr) <= wr_dta;
  end if;
end if;
 end process;


 READ_PART:process(rst,rd_clk)
 begin

if rst='0' then
  rd_b_adr   <= 0;
  rd_g_adr   <= bin2gray( 0 );
  rd_empty   <= true;
  rd_full	<= false;
  rd_half	<= false;
elsif rising_edge(rd_clk) then
  if rd_ena='1' then
	rd_b_adr	<= (rd_b_adr+1) mod N;
	rd_g_adr	<= bin2gray( (rd_b_adr+1) mod N );
  end if;
  -- fifo status on the reader part
  rd_empty <= fifo_size( gray2bin(wr_g_adr), rd_b_adr ) <= conv_integer(rd_ena);
  rd_full  <= fifo_size( gray2bin(wr_g_adr), rd_b_adr ) >= N-2;
  rd_half  <= fifo_size( gray2bin(wr_g_adr), rd_b_adr ) >= N/2;
end if;
 end process;

 RAM_PEEK:process(rd_clk)
 begin
-- read register for dual-port RAM inferring
if rising_edge(rd_clk) then
  if rd_ena='1' then
	rd_dta <= ram(rd_b_adr);
  end if;
end if;
 end process;


end behavior;

 

Где-то еще развлекался с преобразованием ширины шины на входе и выходе. Идея такая же как у базового FIFO, только запись или чтение добавляют к одному из счетчиков не 1, а 2^n. Например, Шина PCI имеет разрядность 32 бита, а кор заточен под 8. Можно делать регистры перепаковки снаружи обычного FIFO, а можно просто память читать другой ширины.

Share this post


Link to post
Share on other sites
Вот еще вариант двухклокового FIFO, на VHDL.

красиво написано, но напрягают несколько моментов :

1. полное отсутствие синхронизаторов указателей wr_clk/rd_clk

2. сомнительная производительность цепей вида fifo_size( gray2bin(wr_g_adr), rd_b_adr ) <= conv_integer(rd_ena)

Share this post


Link to post
Share on other sites
красиво написано, но напрягают несколько моментов

1. не понял, о чем речь :)

2. вообще производительность получалась вполне приличная, во всяком случае она примерно соответствовала производительности встроенной памяти, на которой и строилось FIFO. Генерацию статусов можно при желании и поменять, увеличив задержки их формирования и подняв частотку. Например,

fifo_size( gray2bin(wr_g_adr), rd_b_adr ) <= conv_integer(rd_ena)

можно заменить на

wr_b_adr <= gray2bin(wr_g_adr);
rd_empty <= fifo_size( wr_b_adr, rd_b_adr ) <= conv_integer(rd_ena)

 

Я вообще в этом коде делал упор на то, чтобы он синтезировался (автогенерировался блок встроенной памяти) под подобные любые семейства для Альтеры и Xilinx (Spartan-Cyclone, Virtex-Stratix). Проблема в том, что возможности блока памяти, формально выводимого из описания на VHDL, заметно урезаны по сравнению с вставкой библиотечного блока памяти, возможности которого зависят от семейства и требуют правки кода при смене платформы.

 

А вообще, добавлю, что:

Преждевременная оптимизация — корень всех зол
Edited by Hoodwin

Share this post


Link to post
Share on other sites

Если частоты независимы, то при переводе массива битов из одного частотного домена в другой возможны сильные искажения, поскольку изменения отдельных битов могут зафиксироваться в новом домене в разных тактах. Ну, к примеру, в первом домене счетчик идет по порядку 01111B, 10000B, а во втором домене может быть 01111B, 10100B, 10000B. То есть, задержка бита 2 такова, что он опаздывает на один такт, но не всегда, а лишь иногда, при неблагоприятных фазах двух частот.

 

А код Грея замечателен тем, что последовательные значения счетчика отличаются изменением только одного бита. Поэтому при переводе величины в коде Грея на новую частоту фазовые сдвиги тактовых сигналов не могут привести к ошибкам, возможно либо новое, либо старое значение величины после возврата к обычному бинарному представлению.

 

В итоге такое фифо может работать при любых соотношениях частот. Для ФИФО, работающего на одной частоте или на кратных частотах, код Грея не нужен.

Share this post


Link to post
Share on other sites

Итак господа, давайте попробуем достичь результата.

Выкладывать алгоритмы работы различных очередей (устройств со сложным поведением) мне кажется смысла особого нет. Есть принципиальные вещи в способах их построения, и одной из таких вещей является компонент перехода данных из одного частотного домена в другой. Давайте попробуем формализовать эту процедуру и зафиксировать результат в виде кода на VERILOG.

 

Постановка задачи:

- реализовать мост для данных (разрядность 1 бит) из одного частотного домена в другой.

- фазы частот разные, частоты отличаются в разы (например 125 МГц и 20 МГц)

- время перехода Тpd - минимальное.

 

-------

-----------------+ +---------------------------------- импульс в домене 125МГц (один такт)

 

<-----------> Тpd

--------------------------

-------------------------------+ +-------- импульс в домене 20МГц (один такт)

Шапка модуля:

 

module xdomen (

input clk1,

input din,

 

input clk2,

output dout

);

 

<здесь находится коД....>

// предлагаем свои решения....

 

endmodule

Share this post


Link to post
Share on other sites
Постановка задачи:

- реализовать мост для данных (разрядность 1 бит) из одного частотного домена в другой.

- фазы частот разные, частоты отличаются в разы (например 125 МГц и 20 МГц)

- время перехода Тpd - минимальное.

toggle_synchronizer.v

pulse_synchronizer.v

Share this post


Link to post
Share on other sites

не нашел куда написать, а новой темы.......

где-то спрашивал, но ответа не было.

в общем, есть для примера код:

DFFE ff0 (.d(Din[0]), .ena(ena), .clk(clk_GN), .q(iDin[0]));
DFFE ff1 (.d(Din[1]), .ena(ena), .clk(clk_GN), .q(iDin[1]));
DFFE ff2 (.d(Din[2]), .ena(ena), .clk(clk_GN), .q(iDin[2]));
DFFE ff3 (.d(Din[3]), .ena(ena), .clk(clk_GN), .q(iDin[3]));

На AHDL можно написать:

ff[3..0]: dffe;
ff.(d, ena, clk, q) = (Din[3..0], ena, clk_GN, iDin[3..0]);

На SV никак в одну строчку не укоротить?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now