uninferred due to asynchronous read logic

S

Shannon

I'm really having a bad day and I hope you guys can help me out AGAIN.

I used altera's own template to instantiate a dual-port RAM. Pretty
straight forward really. I've used the single port ram before with no
problems. THIS time however when I compile it gives me some info:

Info: RAM logic "DPRAM:WIN_RAM|ram" uninferred due to asynchronous
read logic

Hmm...ok so I go check the template. Yeah the read stuff is inside a
clocked process. Hmm maybe what I'm driving the RAM_we signal with is
asynchronous to the RAM clock. Nope. Looked at that and it is inside
a clocked process that is clocked by the same clock as the RAM. Then
I looked at the RTL viewer and sure enough the RAM_we signal is driven
by a D-flip flop that is clocked by the right clock. So what am I
missing?

Shannon

(I'm working on a sanitized version of the code that I can post here
but I figured maybe I was missing somethnig obvious and should post
first.)
 
S

Shannon

I'm really having a bad day and I hope you guys can help me out AGAIN.

I used altera's own template to instantiate a dual-port RAM.  Pretty
straight forward really.  I've used the single port ram before with no
problems.  THIS time however when I compile it gives me some info:

Info: RAM logic "DPRAM:WIN_RAM|ram" uninferred due to asynchronous
read logic

Hmm...ok so I go check the template.  Yeah the read stuff is inside a
clocked process.  Hmm maybe what I'm driving the RAM_we signal with is
asynchronous to the RAM clock.  Nope.  Looked at that and it is inside
a clocked process that is clocked by the same clock as the RAM.  Then
I looked at the RTL viewer and sure enough the RAM_we signal is driven
by a D-flip flop that is clocked by the right clock.  So what am I
missing?

Shannon

(I'm working on a sanitized version of the code that I can post here
but I figured maybe I was missing somethnig obvious and should post
first.)

Ok, I thought I was on to something. The OTHER read signal was stuck
asserted all the time. This is actually what I want but nevermind
that. So what I did was made it synchronous just to see if the
problem went away. Nope. But when I look closer at the RTL viewer it
shows a "sync RAM" block that is being clocked by a single clock and
whose write enable / read enable signals are fed from d-FFs that are
clocked by the same clock. So, to me, it looks like the right thing.
However, it's clearly using FF's to do it and NOT the on board RAM.

Any ideas?
 
M

Mike Treseler

Shannon said:
Any ideas?

Dual port rams by X and A have an inference problem with
simultaneous writes to the same address. It always requires
some extra logic to arbitrate and synch things up. The easiest fix is
something like this:

ram_access : process (clk) is
begin
if rising_edge(clk) then
if we = '1' then
mem(to_integer(wr_adr)) <= (data_i);
end if;
data_q <= mem(to_integer(read_adr));
end if;
end process ram_access;

-- Mike Treseler
 
S

Shannon

Dual port rams by X and A have an inference problem with
simultaneous writes to the same address. It always requires
some extra logic to arbitrate and synch things up. The easiest fix is
something like this:

    ram_access : process (clk) is
    begin
       if rising_edge(clk) then
          if we = '1' then
             mem(to_integer(wr_adr)) <= (data_i);
          end if;
          data_q <= mem(to_integer(read_adr));
       end if;
    end process ram_access;

  -- Mike Treseler

The template that 'A' provides is quite close to what you are
suggesting:

-- Port A
process(clk)
begin
if(rising_edge(clk)) then
if(we_a = '1') then
ram(addr_a) <= data_a;
-- Read-during-write on the same port returns NEW data
q_a <= data_a;
else
-- Read-during-write on the mixed port returns OLD data
q_a <= ram(addr_a);
end if;
end if;
end process;

They add the 'q_a <= data_a;' bit but otherwise it seems to be the
same as what you suggest. Other than you have separate write and read
address signals. Is that the part you are suggesting I add to their
template?

Actually as I'm typing this I'm reading their comments and becoming
even more confused about what they intended. If there is only one
"we_a" signal to control both reads AND writes, how is it POSSIBLE to
ever have a "Read-during-write" event?
 
K

KJ

You're not following the template is my idea. Although I've also had
problems with memory when it added unnecessary pass-thru logic because
it had mucked with the read address logic in some unusual fashion.
The work around there was to add the syn_preserve and syn_keep
attributes to the read address signal. After that it didn't add any
pass thru logic and implemented what it should have. That's not quite
the same thing as what you're having trouble with but thought I'd
share the info.
The template that 'A' provides is quite close to what you are
suggesting:

-- Port A
process(clk)
begin
  if(rising_edge(clk)) then
    if(we_a = '1') then
      ram(addr_a) <= data_a;
      -- Read-during-write on the same port returns NEW data
      q_a <= data_a;
    else
      -- Read-during-write on the mixed port returns OLD data
      q_a <= ram(addr_a);
    end if;
  end if;
end process;
You might want to check where you got your template from. The
following is copy/pasted from the Quartus 2 manual from the heading
'Implementing Inferred RAM (VHDL). As you can see it matches Mike's
template, (and my template too for what it's worth).

-- Start of text from Quartus 2 manual
The example below shows ram_infer.vhd, a VHDL Design File that
implements a 32 x 32-bit single-clock RAM with separate read and write
addresses:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY ram_infer IS
PORT
(
clock: IN std_logic;
data: IN std_logic_vector (31 DOWNTO 0);
write_address: IN integer RANGE 0 to 31;
read_address: IN integer RANGE 0 to 31;
we: IN std_logic;
q: OUT std_logic_vector (31 DOWNTO 0)
);
END ram_infer;
ARCHITECTURE rtl OF ram_infer IS
TYPE mem IS ARRAY(0 TO 31) OF std_logic_vector(31 DOWNTO 0);
SIGNAL ram_block : mem;
BEGIN
PROCESS (clock)
BEGIN
IF (clock'event AND clock = '1') THEN
IF (we = '1') THEN
ram_block(write_address) <= data;
END IF;
q <= ram_block(read_address);
END IF;
END PROCESS;
END rtl;
-- End of text from Quartus 2 manual

Actually as I'm typing this I'm reading their comments and becoming
even more confused about what they intended.  If there is only one
"we_a" signal to control both reads AND writes, how is it POSSIBLE to
ever have a "Read-during-write" event?

Reads are always occurring, you don't really have an explicit 'read'
command to the memory. You're always able to read whether you're
reading or not. The issue has to do with if you're reading from the
same address that you're actively writing to (i.e. write_address =
read_address and we='1'). In that situation the data from the newly
written memory location won't show up until the clock cycle after the
write completes.

Kevin Jennings
 
K

KJ

Slight correction to previous post. What I said was...

You're always able to read whether you're reading or not.



What I meant to say was...

You're always able to read whether you're writing or not.

KJ
 
S

Shannon

You're not following the template is my idea.  Although I've also had
problems with memory when it added unnecessary pass-thru logic because
it had mucked with the read address logic in some unusual fashion.
The work around there was to add the syn_preserve and syn_keep
attributes to the read address signal.  After that it didn't add any
pass thru logic and implemented what it should have.  That's not quite
the same thing as what you're having trouble with but thought I'd
share the info.









You might want to check where you got your template from.  The
following is copy/pasted from the Quartus 2 manual from the heading
'Implementing Inferred RAM (VHDL).  As you can see it matches Mike's
template, (and my template too for what it's worth).

-- Start of text from Quartus 2 manual
The example below shows ram_infer.vhd, a VHDL Design File that
implements a 32 x 32-bit single-clock RAM with separate read and write
addresses:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY ram_infer IS
   PORT
   (
      clock: IN   std_logic;
      data:  IN   std_logic_vector (31 DOWNTO 0);
      write_address:  IN   integer RANGE 0 to 31;
      read_address:   IN   integer RANGE 0 to 31;
      we:    IN   std_logic;
      q:     OUT  std_logic_vector (31 DOWNTO 0)
   );
END ram_infer;
ARCHITECTURE rtl OF ram_infer IS
   TYPE mem IS ARRAY(0 TO 31) OF std_logic_vector(31 DOWNTO 0);
   SIGNAL ram_block : mem;
BEGIN
   PROCESS (clock)
   BEGIN
      IF (clock'event AND clock = '1') THEN
         IF (we = '1') THEN
            ram_block(write_address) <= data;
         END IF;
         q <= ram_block(read_address);
      END IF;
   END PROCESS;
END rtl;
-- End of text from Quartus 2 manual




Reads are always occurring, you don't really have an explicit 'read'
command to the memory.  You're always able to read whether you're
reading or not.  The issue has to do with if you're reading from the
same address that you're actively writing to (i.e. write_address =
read_address and we='1').  In that situation the data from the newly
written memory location won't show up until the clock cycle after the
write completes.

Kevin Jennings- Hide quoted text -

- Show quoted text -

I think I'm quickly getting in over my head. Thanks for the response
Kevin. I'm going to have to ponder what you're trying to tell me.

I got my template from within Quartus II itself. It has a handy pull-
down for "insert template". I'll paste it here:

-- Quartus II VHDL Template
-- True Dual-Port RAM with single clock

library ieee;
use ieee.std_logic_1164.all;

entity true_dual_port_ram_single_clock is

generic
(
DATA_WIDTH : natural := 8;
ADDR_WIDTH : natural := 6
);

port
(
clk : in std_logic;
addr_a : in natural range 0 to 2**ADDR_WIDTH - 1;
addr_b : in natural range 0 to 2**ADDR_WIDTH - 1;
data_a : in std_logic_vector((DATA_WIDTH-1) downto 0);
data_b : in std_logic_vector((DATA_WIDTH-1) downto 0);
we_a : in std_logic := '1';
we_b : in std_logic := '1';
q_a : out std_logic_vector((DATA_WIDTH -1) downto 0);
q_b : out std_logic_vector((DATA_WIDTH -1) downto 0)
);

end true_dual_port_ram_single_clock;

architecture rtl of true_dual_port_ram_single_clock is

-- Build a 2-D array type for the RAM
subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
type memory_t is array(addr_a'high downto 0) of word_t;

-- Declare the RAM signal.
signal ram : memory_t;

begin

-- Port A
process(clk)
begin
if(rising_edge(clk)) then
if(we_a = '1') then
ram(addr_a) <= data_a;

-- Read-during-write on the same port returns NEW data
q_a <= data_a;
else
-- Read-during-write on the mixed port returns OLD data
q_a <= ram(addr_a);
end if;
end if;
end process;

-- Port B
process(clk)
begin
if(rising_edge(clk)) then
if(we_b = '1') then
ram(addr_b) <= data_b;

-- Read-during-write on the same port returns NEW data
q_b <= data_b;
else
-- Read-during-write on the mixed port returns OLD data
q_b <= ram(addr_b);
end if;
end if;
end process;

end rtl;


I do notice both you and Mike seem to be talking about something other
than a dual-port RAM (in my simple mind). Both of your examples SEEM
to me to be single-port rams with separate read and write address
ports. Maybe I am just not understanding what I'm saying when I say
dual-port RAM. I hope the cut and paste above from Quartus gives a
hint of what I was trying to accomplish. Basically I have one module
(a microP interface) that is reading OR writing whenever it wants.
Then I have another module that is reading from the same RAM pretty
much all the time. Ok, not ALL the time but it goes through bursts
where it is incrementing the address and reading every clock cycle.

I'll slowly read through your post and see if I can come up with
relevent questions. I'm sensing I have two problems. One, I don't
understand what a dual-port RAM is. And two I'm not understanding
what the info "RAM logic "DPRAM:WIN_RAM|ram" uninferred due to
asynchronous read logic" means.

Shannon
 
M

Mike Treseler

Shannon said:
If there is only one
"we_a" signal to control both reads AND writes, how is it POSSIBLE to
ever have a "Read-during-write" event?

By having two data buses.
 
S

Shannon

I think I'm quickly getting in over my head.  Thanks for the response
Kevin.  I'm going to have to ponder what you're trying to tell me.

I got my template from within Quartus II itself.  It has a handy pull-
down for "insert template".  I'll paste it here:

-- Quartus II VHDL Template
-- True Dual-Port RAM with single clock

library ieee;
use ieee.std_logic_1164.all;

entity true_dual_port_ram_single_clock is

        generic
        (
                DATA_WIDTH : natural := 8;
                ADDR_WIDTH : natural := 6
        );

        port
        (
                clk             : in std_logic;
                addr_a  : in natural range 0 to 2**ADDR_WIDTH - 1;
                addr_b  : in natural range 0 to 2**ADDR_WIDTH - 1;
                data_a  : in std_logic_vector((DATA_WIDTH-1) downto 0);
                data_b  : in std_logic_vector((DATA_WIDTH-1) downto 0);
                we_a    : in std_logic := '1';
                we_b    : in std_logic := '1';
                q_a             : out std_logic_vector((DATA_WIDTH -1) downto 0);
                q_b             : out std_logic_vector((DATA_WIDTH -1) downto 0)
        );

end true_dual_port_ram_single_clock;

architecture rtl of true_dual_port_ram_single_clock is

        -- Build a 2-D array type for the RAM
        subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
        type memory_t is array(addr_a'high downto 0) of word_t;

        -- Declare the RAM signal.
        signal ram : memory_t;

begin

        -- Port A
        process(clk)
        begin
        if(rising_edge(clk)) then
                if(we_a = '1') then
                        ram(addr_a) <= data_a;

                        -- Read-during-write on the same port returns NEW data
                        q_a <= data_a;
                else
                        -- Read-during-write on the mixed port returns OLD data
                        q_a <= ram(addr_a);
                end if;
        end if;
        end process;

        -- Port B
        process(clk)
        begin
        if(rising_edge(clk)) then
                if(we_b = '1') then
                        ram(addr_b) <= data_b;

                        -- Read-during-write on the same port returns NEW data
                        q_b <= data_b;
                else
                        -- Read-during-write on the mixed port returns OLD data
                        q_b <= ram(addr_b);
                end if;
        end if;
        end process;

end rtl;

I do notice both you and Mike seem to be talking about something other
than a dual-port RAM (in my simple mind).  Both of your examples SEEM
to me to be single-port rams with separate read and write address
ports.  Maybe I am just not understanding what I'm saying when I say
dual-port RAM.  I hope the cut and paste above from Quartus gives a
hint of what I was trying to accomplish.  Basically I have one module
(a microP interface) that is reading OR writing whenever it wants.
Then I have another module that is reading from the same RAM pretty
much all the time.  Ok, not ALL the time but it goes through bursts
where it is incrementing the address and reading every clock cycle.

I'll slowly read through your post and see if I can come up with
relevent questions.  I'm sensing I have two problems.  One, I don't
understand what a dual-port RAM is.  And two I'm not understanding
what the info "RAM logic "DPRAM:WIN_RAM|ram" uninferred due to
asynchronous read logic" means.

Shannon- Hide quoted text -

- Show quoted text -

First question. Is this "INFO" refering to something about the
template that I'm using or is it refering to my use of the DPRAM? In
other words, is Quartus complaining that something about the structure
of the RAM template is telling it to infer logic over RAM? OR.... Is
Quartus complaining that somewhere in MY code I'm performing an
asynchronous read of the RAM so it will have to synthesize the RAM
with logic to be able to deal with MY asynchronous behavior?

Shannon
 
D

Dave

I think I'm quickly getting in over my head. Thanks for the response
Kevin. I'm going to have to ponder what you're trying to tell me.

I got my template from within Quartus II itself. It has a handy pull-
down for "insert template". I'll paste it here:

-- Quartus II VHDL Template
-- True Dual-Port RAM with single clock

library ieee;
use ieee.std_logic_1164.all;

entity true_dual_port_ram_single_clock is

generic
(
DATA_WIDTH : natural := 8;
ADDR_WIDTH : natural := 6
);

port
(
clk : in std_logic;
addr_a : in natural range 0 to 2**ADDR_WIDTH - 1;
addr_b : in natural range 0 to 2**ADDR_WIDTH - 1;
data_a : in std_logic_vector((DATA_WIDTH-1) downto 0);
data_b : in std_logic_vector((DATA_WIDTH-1) downto 0);
we_a : in std_logic := '1';
we_b : in std_logic := '1';
q_a : out std_logic_vector((DATA_WIDTH -1) downto 0);
q_b : out std_logic_vector((DATA_WIDTH -1) downto 0)
);

end true_dual_port_ram_single_clock;

architecture rtl of true_dual_port_ram_single_clock is

-- Build a 2-D array type for the RAM
subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
type memory_t is array(addr_a'high downto 0) of word_t;

-- Declare the RAM signal.
signal ram : memory_t;

begin

-- Port A
process(clk)
begin
if(rising_edge(clk)) then
if(we_a = '1') then
ram(addr_a) <= data_a;

-- Read-during-write on the same port returns NEW data
q_a <= data_a;
else
-- Read-during-write on the mixed port returns OLD data
q_a <= ram(addr_a);
end if;
end if;
end process;

-- Port B
process(clk)
begin
if(rising_edge(clk)) then
if(we_b = '1') then
ram(addr_b) <= data_b;

-- Read-during-write on the same port returns NEW data
q_b <= data_b;
else
-- Read-during-write on the mixed port returns OLD data
q_b <= ram(addr_b);
end if;
end if;
end process;

end rtl;

I do notice both you and Mike seem to be talking about something other
than a dual-port RAM (in my simple mind). Both of your examples SEEM
to me to be single-port rams with separate read and write address
ports. Maybe I am just not understanding what I'm saying when I say
dual-port RAM. I hope the cut and paste above from Quartus gives a
hint of what I was trying to accomplish. Basically I have one module
(a microP interface) that is reading OR writing whenever it wants.
Then I have another module that is reading from the same RAM pretty
much all the time. Ok, not ALL the time but it goes through bursts
where it is incrementing the address and reading every clock cycle.

I'll slowly read through your post and see if I can come up with
relevent questions. I'm sensing I have two problems. One, I don't
understand what a dual-port RAM is. And two I'm not understanding
what the info "RAM logic "DPRAM:WIN_RAM|ram" uninferred due to
asynchronous read logic" means.

Shannon

It might matter what particular chip you're targetting. I remember
some of the older Altera chips didn't have true dual-port RAM, but
instead had separate read and write ports. Could it be that the chip
you're targetting doesn't have a true dual-port RAM? What are you
targetting?

Dave
 
M

Mike Treseler

Shannon said:
I do notice both you and Mike seem to be talking about something other
than a dual-port RAM (in my simple mind). Both of your examples SEEM
to me to be single-port rams with separate read and write address
ports.

Exactly. As far as I know this is the only case
that can be inferred reliably across brands.
Maybe I am just not understanding what I'm saying when I say
dual-port RAM. I hope the cut and paste above from Quartus gives a
hint of what I was trying to accomplish. Basically I have one module
(a microP interface) that is reading OR writing whenever it wants.
Then I have another module that is reading from the same RAM pretty
much all the time.

You have only one writer.
I would have the microP write to two block rams and read one.
If that won't do, Instance a true dual port.

-- Mike Treseler
 
S

Shannon

Dave asked: What are you targetting?

Cyclone. As far as I can tell from the data sheet it indicates that
it is capable of true dual-port RAM

If that won't do, Instance a true dual port.

            -- Mike Treseler

Isn't that what I did?
 
S

Shannon

Ok! Now I'm getting somewhere (at least in my mind...for some reason I
have this picture of a clown on a tiny tricycle peddling furiously but
just going in a little circle)

If you take the template I posted and try to synthesize it all by
itself in Quartus II targeting a Cyclone, you will get the same
warning as I've posted above. So, to me, this means the warning has
nothing to do with MY use of the DPRAM elsewhere in my code. The
problem is entirely inside Quartus's own template! Ok, now with your
help I think we can make this thing infer real RAM. Any ideas? It
seems to think the read is asynchronous but in my view the read
behavior is inside a clocked process and the IF .. THEN is
incomplete. This means it will make a register out of the read port
so that it can hold it's value outside the process. That process is
clocked so I can't see how it thinks reads are asynchronous.

Shannon
 
S

Shannon

Ok! Now I'm getting somewhere (at least in my mind...for some reason I
have this picture of a clown on a tiny tricycle peddling furiously but
just going in a little circle)

If you take the template I posted and try to synthesize it all by
itself in Quartus II targeting a Cyclone, you will get the same
warning as I've posted above.  So, to me, this means the warning has
nothing to do with MY use of the DPRAM elsewhere in my code.  The
problem is entirely inside Quartus's own template!  Ok, now with your
help I think we can make this thing infer real RAM.  Any ideas?  It
seems to think the read is asynchronous but in my view the read
behavior is inside a clocked process and the IF .. THEN is
incomplete.  This means it will make a register out of the read port
so that it can hold it's value outside the process.  That process is
clocked so I can't see how it thinks reads are asynchronous.

Shannon

ooopss I had commented out the 'q_a <= data_a;' lines and it didn't
work. but when I put them back it correctly infers an altsynchRAM.
However, when I put this back into my project the error returns. I
guess that means there MUST be something wrong with my code. It must
be making it create an asynchronous read....grrrrrrr

I'm lost.
Shannon
 
M

Mike Treseler

Shannon said:
Ok! Now I'm getting somewhere (at least in my mind...for some reason I
have this picture of a clown on a tiny tricycle peddling furiously but
just going in a little circle)

Ok, now with your
help I think we can make this thing infer real RAM. Any ideas?

I assume you mean 'real' dual-port ram.
Let me know if you work it out.
I am done pushing on that rope.

As long as the vendors use asych logic to arbitrate writes,
the template, if it existed, would not be synchronous.

I'll stick with building blocks of
one read and one write port.
I have yet to run out of block ram.

-- Mike Treseler
 
S

Shannon

I assume you mean 'real' dual-port ram.
Let me know if you work it out.
I am done pushing on that rope.

As long as the vendors use asych logic to arbitrate writes,
the template, if it existed, would not be synchronous.

I'll stick with building blocks of
one read and one write port.
I have yet to run out of block ram.

        -- Mike Treseler

Thanks Mike. I appreciate all your help. I'll keep pushing on the
rope for a little while longer. I'm one of those block heads that
wants to understand WHY he can't do what he wants to do. I saw it
infer a true dual-port when the template is by itself. To me, that
means it is POSSIBLE to infer a true dual port RAM. I just have to
figure out why my surrounding code makes it UNinfer.

If anyone else has some tips or hints I'd be happy to hear 'em!

Shannon
 
D

Dave

ooopss I had commented out the 'q_a <= data_a;' lines and it didn't
work. but when I put them back it correctly infers an altsynchRAM.
However, when I put this back into my project the error returns. I
guess that means there MUST be something wrong with my code. It must
be making it create an asynchronous read....grrrrrrr

I'm lost.
Shannon

If it works when it's in its own entity, then maybe you should try
leaving the DPRAM that way, and instantiating it as a component is the
rest of your code. This might help Quartus see things more clearly.
Memory inferring is tricky, and I've found that sometimes the tools
need things a certain way just because, even though you're following
all the rules.

Dave
 
S

Shannon

If it works when it's in its own entity, then maybe you should try
leaving the DPRAM that way, and instantiating it as a component is the
rest of your code. This might help Quartus see things more clearly.
Memory inferring is tricky, and I've found that sometimes the tools
need things a certain way just because, even though you're following
all the rules.

Dave- Hide quoted text -

- Show quoted text -

Excellent idea. I am doing a direct instantiation right now but if I
understand you correctly, I could somehow complile it into a library
or something and then stick it into my code thereby not letting
Quartus re-synthesize the RAM part. Ok, it's back to the books to see
how I can do all that!

Shannon
 
D

Dave

Excellent idea. I am doing a direct instantiation right now but if I
understand you correctly, I could somehow complile it into a library
or something and then stick it into my code thereby not letting
Quartus re-synthesize the RAM part. Ok, it's back to the books to see
how I can do all that!

Shannon

That's a bit more than I meant... I was just meaning to instantiate
your RAM code, like you would in any hierarchical design. Nothing
more. No precompiling anything or any other weirdness.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top