megafunction

A

Amit

Guest
Hello,

I'm learning to use megafunction "cdfifo" and as its documentation
says there is an output as wrusedw[] which allows an external device
(writer) to know the number of words that are currently in the FIFO.
Now, my question is how should I take care of indexing?

I have never used it before and thinking if I write into the FIFO in a
loop how should I handle the indxing? Or it would be handled by the
megafunction itself?!

Thanks.
 
On Apr 16, 8:58 am, Amit <amit.ko...@gmail.com> wrote:
Hello,

I'm learning to use megafunction "cdfifo" and as its documentation
says there is an output as wrusedw[] which allows an external device
(writer) to know the number of words that are currently in the FIFO.
Now, my question is how should I take care of indexing?

I have never used it before and thinking if I write into the FIFO in a
loop how should I handle the indxing? Or it would be handled by the
megafunction itself?!

Thanks.

Do you mean "dcfifo"? the altera dual clock fifo?
If you only have 1 clock throughout your design, then Id use the
"scfifo" instead (single clock). the DC fifo is a very reliable way of
passing data accross clock domains, and is alot safer than double
registering. The SC fifo is very useful for data storage in a
pipelined design (as is the DC fifo if you have a pipeline accross
clock domains).

The whole point of a FIFO is that you dont have to do any addressing.
FIFO stands for "First In First Out". The wrusedw signal will tell you
how full the FIFO is, so you have some idea of how many more words you
can input without reading any values out. These "how full" signals
often lag behind the real value of how full the fifo is, especially in
a dual clocked fifo, so are not fully reliable. Often its just best to
use the "wrfull" signal instead.

Usually, the fifo control in done by means of empty/full or enable/
ready type signals on both the read and write side. No addressing
needed. The internal counters do all that for you.

One point to note with the Altera DC fifo. I have had problems with
this when the Read and write clocks are very close to one another ( <
1% difference). The read counter would randomly reset itself back to 0
while the write counter would stay where it should be.
 
Tricky wrote:

If you only have 1 clock throughout your design, then Id use the
"scfifo" instead (single clock).
Yes. I also prefer synchronous (one clock) fifos.

the DC fifo is a very reliable way of
passing data across clock domains, and is a lot safer than double
registering.
I don't agree.

The LPM_FIFO_DC (dual-clock)
has asynchronous priority resolution.
WRCLOCK has 'priority' only as long as the two
rising edges stay away from each other.


The whole point of a FIFO is that you dont have to do any addressing.
Exactly.

One point to note with the Altera DC fifo. I have had problems with
this when the Read and write clocks are very close to one another (
1% difference). The read counter would randomly reset itself back to 0
while the write counter would stay where it should be.
....or just about anything else can happen.

-- Mike Treseler
 
On Apr 16, 1:56 am, Tricky <Trickyh...@gmail.com> wrote:
On Apr 16, 8:58 am, Amit <amit.ko...@gmail.com> wrote:

Hello,

I'm learning to use megafunction "cdfifo" and as its documentation
says there is an output as wrusedw[] which allows an external device
(writer) to know the number of words that are currently in the FIFO.
Now, my question is how should I take care of indexing?

I have never used it before and thinking if I write into the FIFO in a
loop how should I handle the indxing? Or it would be handled by the
megafunction itself?!

Thanks.

Do you mean "dcfifo"? the altera dual clock fifo?
If you only have 1 clock throughout your design, then Id use the
"scfifo" instead (single clock). the DC fifo is a very reliable way of
passing data accross clock domains, and is alot safer than double
registering. The SC fifo is very useful for data storage in a
pipelined design (as is the DC fifo if you have a pipeline accross
clock domains).

The whole point of a FIFO is that you dont have to do any addressing.
FIFO stands for "First In First Out". The wrusedw signal will tell you
how full the FIFO is, so you have some idea of how many more words you
can input without reading any values out. These "how full" signals
often lag behind the real value of how full the fifo is, especially in
a dual clocked fifo, so are not fully reliable. Often its just best to
use the "wrfull" signal instead.

Usually, the fifo control in done by means of empty/full or enable/
ready type signals on both the read and write side. No addressing
needed. The internal counters do all that for you.

One point to note with the Altera DC fifo. I have had problems with
this when the Read and write clocks are very close to one another (
1% difference). The read counter would randomly reset itself back to 0
while the write counter would stay where it should be.


Thanks for help.
 
KJ wrote:

Meaning that lpm_fifo_dc will (or could) misfunction in what way? Ignore a
write? Something else?
Depends on constraints. It's an asynch race:

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
use work.LPM_COMPONENTS.all;

entity LPM_FIFO_DC is
generic (LPM_WIDTH : positive ;
LPM_WIDTHU : positive := 1;
LPM_NUMWORDS : positive;
LPM_SHOWAHEAD : string := "OFF";
LPM_TYPE : string := "LPM_FIFO_DC";
LPM_HINT : string := "UNUSED");
port (DATA : in std_logic_vector(LPM_WIDTH-1 downto 0);
WRCLOCK : in std_logic;
RDCLOCK : in std_logic;
WRREQ : in std_logic;
RDREQ : in std_logic;
ACLR : in std_logic := '0';
Q : out std_logic_vector(LPM_WIDTH-1 downto 0);
WRUSEDW : out std_logic_vector(LPM_WIDTHU-1 downto 0);
RDUSEDW : out std_logic_vector(LPM_WIDTHU-1 downto 0);
WRFULL : out std_logic;
RDFULL : out std_logic;
WREMPTY : out std_logic;
RDEMPTY : out std_logic);
end LPM_FIFO_DC;

architecture LPM_SYN of lpm_fifo_dc is

type lpm_memory is array (lpm_numwords-1 downto 0) of
std_logic_vector(lpm_width-1 downto 0);

signal i_q : std_logic_vector(lpm_width-1 downto 0) := (OTHERS => '0');
signal i_read_and_write : std_logic := '0';
signal i_rdptr, i_wrptr : integer := 0;
signal i_rdempty, i_wrempty : std_logic := '1';
signal i_rdfull, i_wrfull : std_logic := '0';
signal i_rdusedw, i_wrusedw : integer := 0;

constant ZEROS : std_logic_vector(lpm_width-1 downto 0) := (OTHERS =>
'0');
constant RDPTR_DP : integer := 5;
constant WRPTR_DP : integer := 5;
constant RDUSEDW_DP : integer := 1;
constant WRUSEDW_DP : integer := 1;
constant FULL_RISEEARLY : integer := 2;

type t_rdptrpipe is array (0 to RDPTR_DP) of integer;
type t_wrptrpipe is array (0 to WRPTR_DP) of integer;
type t_rdusedwpipe is array (0 to RDUSEDW_DP) of integer;
type t_wrusedwpipe is array (0 to WRUSEDW_DP) of integer;

begin

process (rdclock, wrclock, aclr, i_read_and_write)
variable mem_data : lpm_memory := (OTHERS => ZEROS);
variable pipe_wrptr : t_rdptrpipe := (OTHERS => 0);
variable pipe_rdptr : t_wrptrpipe := (OTHERS => 0);
variable pipe_rdusedw : t_rdusedwpipe := (OTHERS => 0);
variable pipe_wrusedw : t_wrusedwpipe := (OTHERS => 0);

begin
if (ACLR = '1') then

----- CLEAR ALL VARIABLES -----
i_q <= ZEROS;
i_read_and_write <= '0';
i_rdptr <= 0;
i_wrptr <= 0;
i_rdempty <= '1';
i_wrempty <= '1';
i_rdfull <= '0';
i_wrfull <= '0';
-- i_rdusedw and i_wrusedw are cleard in the pipelines.
if (lpm_showahead = "ON") then
i_q <= mem_data(0);
end if;

elsif (wrclock'event and wrclock = '1') then

----- SET FLAGS -----
if (i_wrusedw >= lpm_numwords-1-FULL_RISEEARLY) then
i_wrfull <= '1';
else
i_wrfull <= '0';
end if;

if (i_wrusedw <= 0 and i_rdptr = i_wrptr) then
i_wrempty <= '1';
end if;

if (wrreq = '1' and i_wrfull = '0' and not wrreq'event) then

----- WRITE DATA -----
mem_data(i_wrptr) := data;

----- SET FLAGS -----
i_wrempty <= '0';

----- INC WRPTR -----
if (i_wrptr >= lpm_numwords-1) then
i_wrptr <= 0;
else
i_wrptr <= i_wrptr + 1;
end if;

end if;

if (rdclock'event and rdclock = '1') then
i_read_and_write <= '1';
end if;

elsif ((rdclock'event and rdclock = '1') or
i_read_and_write = '1') then
i_read_and_write <= '0';

----- SET FLAGS -----
if (i_rdusedw >= lpm_numwords-1-FULL_RISEEARLY) then
i_rdfull <= '1';
else
i_rdfull <= '0';
end if;

if (i_rdptr = i_wrptr) then
i_rdempty <= '1';
elsif (i_rdempty = '1' and i_rdusedw > 0) then
i_rdempty <= '0';
end if;

----- Q SHOWAHEAD -----
if (lpm_showahead = "ON" and i_rdptr /= i_wrptr) then
i_q <= mem_data(i_rdptr);
end if;

if (rdreq = '1' and i_rdempty = '0' and not rdreq'event) then


----- READ DATA -----
i_q <= mem_data(i_rdptr);
if (lpm_showahead = "ON") then
if (i_rdptr = i_wrptr) then
i_q <= ZEROS;
else
if (i_rdptr >= lpm_numwords-1) then
i_q <= mem_data(0);
else
i_q <= mem_data(i_rdptr+1);
end if;
end if;
end if;

----- SET FLAGS -----
if ((i_rdptr = lpm_numwords-1 and i_wrptr = 0) or
i_rdptr+1 = i_wrptr) then
i_rdempty <= '1';
end if;

----- INC RDPTR -----
if (i_rdptr >= lpm_numwords-1) then
i_rdptr <= 0;
else
i_rdptr <= i_rdptr + 1;
end if;
end if;

end if;

----- DELAY WRPTR FOR RDUSEDW -----
pipe_wrptr(RDPTR_DP) := i_wrptr;
if (RDPTR_DP > 0) then
if (ACLR = '1') then
for i in 0 to RDPTR_DP loop
pipe_wrptr(i) := 0;
end loop;
elsif (rdclock'event and rdclock = '1') then
pipe_wrptr(0 to RDPTR_DP-1) := pipe_wrptr(1 to RDPTR_DP);
end if;
end if;
if (pipe_wrptr(0) >= i_rdptr) then
pipe_rdusedw(RDUSEDW_DP) := pipe_wrptr(0) - i_rdptr;
else
pipe_rdusedw(RDUSEDW_DP) :=
lpm_numwords + pipe_wrptr(0) - i_rdptr;
end if;

----- DELAY RDPTR FOR WRUSEDW -----
pipe_rdptr(WRPTR_DP) := i_rdptr;
if (WRPTR_DP > 0) then
if (ACLR = '1') then
for i in 0 to WRPTR_DP loop
pipe_rdptr(i) := 0;
end loop;
elsif (wrclock'event and wrclock = '1') then
pipe_rdptr(0 to WRPTR_DP-1) := pipe_rdptr(1 to WRPTR_DP);
end if;
end if;
if (i_wrptr >= pipe_rdptr(0)) then
pipe_wrusedw(WRUSEDW_DP) := i_wrptr - pipe_rdptr(0);
else
pipe_wrusedw(WRUSEDW_DP) := lpm_numwords + i_wrptr - pipe_rdptr(0);
end if;

----- DELAY RDUSEDW -----
if (RDUSEDW_DP > 0) then
if (ACLR = '1') then
for i in 0 to RDUSEDW_DP loop
pipe_rdusedw(i) := 0;
end loop;
elsif (rdclock'event and rdclock = '1') then
pipe_rdusedw(0 to RDUSEDW_DP-1) :=
pipe_rdusedw(1 to RDUSEDW_DP);
end if;
end if;
i_rdusedw <= pipe_rdusedw(0);

----- DELAY WRUSEDW -----
if (WRUSEDW_DP > 0) then
if (ACLR = '1') then
for i in 0 to WRUSEDW_DP loop
pipe_wrusedw(i) := 0;
end loop;
elsif (wrclock'event and wrclock = '1') then
pipe_wrusedw(0 to WRUSEDW_DP-1) :=
pipe_wrusedw(1 to WRUSEDW_DP);
end if;
end if;
i_wrusedw <= pipe_wrusedw(0);

end process;

----- SET OUTPUTS ------
q <= i_q;
rdempty <= i_rdempty;
wrempty <= i_wrempty;
rdfull <= i_rdfull;
wrfull <= i_wrfull;
rdusedw <= conv_std_logic_vector(i_rdusedw, lpm_widthu);
wrusedw <= conv_std_logic_vector(i_wrusedw, lpm_widthu);

end LPM_SYN;
 
jens wrote:
the DC fifo is a very reliable way of
passing data accross clock domains, and is alot safer than double
registering.

Note that double-registering any bus larger than 1 bit is useless for
crossing clock domains, so being safer than that is not very
difficult. That could make an interesting interview question! :)
Good point.

I meant to object to "a very reliable way"
not to agree with that alternative.

-- Mike Treseler
 
the DC fifo is a very reliable way of
passing data accross clock domains, and is alot safer than double
registering.
Note that double-registering any bus larger than 1 bit is useless for
crossing clock domains, so being safer than that is not very
difficult. That could make an interesting interview question! :)

Often its just best to use the "wrfull" signal instead.
In many applications, a FIFO full signal means it's already too late.
The fill count is often required for read and write waterlines, so
both sides know when they should be reading and writing data.
 
On Apr 16, 6:23 pm, Mike Treseler <mike_trese...@comcast.net> wrote:
KJ wrote:
Meaning that lpm_fifo_dc will (or could) misfunction in what way?  Ignore a
write?  Something else?

Depends on constraints. It's an asynch race:
What is the source for the code that you posted? It is structured
quite a bit differently then what Quartus has for lpm_fifo_dc.

I'm assuming also that the dicey part in the code that you're
referring to is that the rising edge of rdclock is checked only if the
rising edge of wrclock has not occurred but it appears that even
within the section that gets triggered on the rising edge of wrclock
that there is an attempt to look for the rising edge of rdclock as
well (see snipped out code below).

   begin
      if (ACLR = '1') then
snip
      elsif (wrclock'event and wrclock = '1') then
snip
         if (rdclock'event and rdclock = '1') then
            i_read_and_write <= '1';
         end if;

      elsif ((rdclock'event and rdclock = '1') or
snip
      end if;
I agree that the code that you posted for lpm_fifo_dc might present
some challenges to synthesis (hence the reason for asking where you
got it from since it differs substantially from what Altera supplies)
but it's also quite possible that synthesis could properly unravel the
embedded rising edge checks into separate clocked logic. Without
looking at what your posted code ends up being synthesized as I
wouldn't hazard to guess, but would also not want to trust that code
either.

The code that Altera publishes for lpm_fifo_dc is a simple wrapper for
another component called lpm_fifo_dc_async. The code for
lpm_fifo_dc_async instantiates a slew of components called
lpm_fifo_dc_dffpipe and lpm_fifo_dc_fefifo and then has a process that
is of the form shown below which does not have the dicey arrangement
of checking for simulaneous rdclock/wrclock that your code has,
although the placement of the "if aclr = '1' then" at the top rather
than the bottom of the process doesn't look too good to me. The file
that I'm referring to is 220model.vhd and is labelled as "Quartus II
3.0 Build 245 10/09/2003".

Lastly, for what it's worth, I've used lpm_fifo_dc for several years
and the only issue that I ran across is that simultaneous reads and
writes occasionally produced incorrect status on the 'wrfull' output
(and indeed all of the 'wrusedw' outputs also). The scenario there
was the fifo was nowhere near full (~1/4 full or so) and a
simultaneous read/write occurred and for one clock cycle all of the
'wrusedw' and 'wrfull' outputs went to 1, on the next clock they went
back to the correct state.

if (aclr'event and (aclr = '1')) then
<snip>
end if; -- aclr event

if (wrclk'event) then
if ((aclr = '1') and (not
(IS_FAMILY_STRATIX(intended_device_family) or ... then
<snip>
elsif ((wrclk = '1') and (NOW > 0 ns))
then
<snip>
end if;
<snip>
end if;
end if; -- wrclk event

if (rdclk'event) then
if ((aclr = '1') and (not
(IS_FAMILY_STRATIX(intended_device_family) or ... then
<snip>
elsif ((rdclk = '1') and (i_rden = '1') and (NOW > 0 ns)) then
if ((aclr = '0') and (i_rdptr < max_widthu_minus_one)) ...
then
<snip>
end if;
end if;
end if; -- rdclk event

Kevin Jennings
 
KJ wrote:

What is the source for the code that you posted? It is structured
quite a bit differently then what Quartus has for lpm_fifo_dc.
There used to be a site called edif.org that held the
lpm sources in the public domain.

I agree that the code that you posted for lpm_fifo_dc might present
some challenges to synthesis (hence the reason for asking where you
got it from since it differs substantially from what Altera supplies)
but it's also quite possible that synthesis could properly unravel the
embedded rising edge checks into separate clocked logic.
It's possible, but Jen's posting seems to confirm that
it is a good idea to constrain or arbitrate the time
between clocks.

Without looking at what your posted code ends up being synthesized as I
wouldn't hazard to guess, but would also not want to trust that code
either.
A d-flop is the only asynch element I am at all comfortable with ;)

-- Mike
 

Welcome to EDABoard.com

Sponsor

Back
Top