Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
15 years ago

RAM uninferred due to asynchronous read logic

Hello,

I have a cache module which uses an inferred synchronous RAM block. When I synthesize the module by itself (as the project's top module) the synthesis works as intended and the RAM is inferred correctly.

When I instantiate the module from another module, the RAM inference fails with this error message:

--start of Quartus II error message

Info: RAM logic "mips_cache:cache|code_line_table" is uninferred due to asynchronous read logic

-- end of error message

This the process that should infer the RAM and fails:


...
code_line_memory:
process(clk)
begin
    if clk'event and clk='1' then
        if ps=code_refill_bram_1 or ps=code_refill_sram8_3 or ps=code_refill_sram_1 then
            code_line_table(conv_integer(code_word_addr_wr)) <= code_refill_data;
        end if;
    
        code_cache_rd <= code_line_table(conv_integer(code_word_addr));
    end if;
end process code_line_memory;
...
please note that the RAM output is registered.

It is identical to other process in the same module that does work, and it's identical to the VHDL RAM templates I've been using for years (as far as I can tell).

When I re-register the output signal (that is, load code_cache_rd on a register before feeding it to the rest of the circuit), then the error disappears but at the cost of an extra delay cycle that should not be necessary (since the RAM output is already registered).

I have tried a number of random changes, with an increasing level of desperation, and can't figure what's wrong.

I'm using Quartus-II 9.0 build 235.

Any help will be appreciated.

Thanks!

19 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Regarding the RAM inference problem, as we haven't seen the real code, it's only guessing. The requirements for RAM inference are basically simple, but depending on the coding style, it may be more difficult, to see if they are met. In case of doubt, I would use the Altera RAM templates as a starting point.

    I can't exclude of course, that there may be a Quartus problem, that prevents RAM inference in cases, that are expected to work. But I'm not aware of any, yet.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I have tried moving the RAM block to a separate module and I get the same result. Synthesizing the module standalone gives the expected result, synthesizing it in my design gives the same error as before.

    The separate module in its entirety is this:

    
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
    use work.mips_pkg.all;
    entity bram_2p is
        generic (
            BRAM_SIZE : integer         := 1024;
            BRAM_WIDTH : integer        := 16
        );
        port(
            clk             : in std_logic;
            reset           : in std_logic;
            rd_addr         : in std_logic_vector(9 downto 0);--in std_logic_vector(log2(BRAM_SIZE)-1 downto 0);
            rd_data         : out std_logic_vector(BRAM_WIDTH-1 downto 0);
            
            wr_addr         : in std_logic_vector(9 downto 0);
            wr_data         : in std_logic_vector(BRAM_WIDTH-1 downto 0);
            
            we              : in std_logic
        );
    end entity bram_2p;
    architecture inferred of bram_2p is
    type t_ram is array(BRAM_SIZE-1 downto 0) of std_logic_vector(BRAM_WIDTH-1 downto 0);
    signal ram :                t_ram;
    begin
    memory:
    process(clk)
    begin
        if clk'event and clk='1' then
            if we='1' then
                ram(conv_integer(wr_addr)) <= wr_data;
            end if;
        
            rd_data <= ram(conv_integer(rd_addr));
        end if;
    end process memory;
    end architecture inferred;
    
    I have tried it with and without generics, the code is a bit dirty as the result of a lot of aimless changes.

    The code is nearly identical to all the Altera templates I have been able to find, and identical to the code that I have been using with no problem in other projects.

    There definitely is something in my code that is confusing the synthesizer but I can't find what it is.

    I have a question for any Altera engineers thay may be reading this. Is there an app note or document that explains exactly how inference works? So I can guess under which circumstances it will fail, despite using the Altera template?

    I have tried enabling all the error message levels that I have been able to find in the IDE but the only error message is this:

    
    Info: Found 1 instances of uninferred RAM logic
        Info: RAM logic "mips_cache:cache|bram_2p:code_line_memory|ram" is uninferred due to asynchronous read logic
    

    There is no error message involving any of the ram interface signals.

    At this point I think we can call this behavior a bug, in fairness. All I want is to find some way to work around it that does not involve instantiating an architecture-dependent module.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I have discovered a fix:

    If I pass the RAM output through a layer of logic (I have tried an AND gate and a MUX, both implemented as a single LUT I think) then the synthesis works as expected. I guess I should have tried this a lot earlier...

    This is a fix I can happily live with so I guess the problem is solved. Yet, I would like to know about that documentation I mentioned in my previous post; knowing how the synthesis works would help me prevent this kind of trouble in the future.

    Thank you very much for your help. If you think of some other thing that you'd like me to try, tell me and I will. Or if any of you Altera guys reading this wants the project code to test this issue I can give it to you (with some mild embarrassment about its quality).
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Did you notice, that the Quartus template is using a different topology than your design? It's registering the read address rather than the output. This structure is corresponding to the actual hardware behaviour. Normally, Quartus is able to convert the output register you have defined in your code into an input register, but apparently this doesn't work in your full design. I guess, that the compiler pushes the register into the design part driven by the RAM before it starts to process the RAM part.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hello,

    As you suggested, I have just tried registering the read address instead of the output, but it does not help.

    I had seen that construct you mention suggested in the manual (example 10-4 in the handbook, section on recommended hdl coding styles), so this is one of the things that I tried previously. Yet, the template in Quartus-2 'insert template' menu (and in some other Altera manuals that I don't have on hand right now) matches the construct I have used. I will for the sake of consistency refactor the RAMs to use the recommended construct (it is compatible with ISE too) but as I said it does not help in this case.

    I think you are right in that some of the logic connected to the RAM output somehow gets mixed in with the RAM construct and upsets the 'pattern' recognized by the synthesis tool. Putting the extra layer of logic I mentioned in my previous post prevents that 'mixing'.

    If there is a synthesis option to prevent optimization across hierarchy boundaries, it might help. I haven't found it in the IDE or the manual (yet).

    Thank you very much for your help!
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hello again,

    In my original code (the code that failed) the uninferred RAM data output was connected straight to the address input of a different RAM block. Can this be the source of the trouble? The problem disappeared when I inserted a layer of logic between data output and address input.

    This is the only difference I can think of between this and many other designs in which I used this inferred RAM construct witl Quartus-2. I don't remember seeing this case mentioned in the manual but I'll have to check again more carefully...

    I will conduct an experiment and post the result ASAP.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    In my original code (the code that failed) the uninferred RAM data output was connected straight to the address input of a different RAM block. Can this be the source of the trouble?

    --- Quote End ---

    Yes, that's sounds plaubsible. I guess, it's a somewhat unusual connection, so it most likely hasn't been considered by Altera.

    The reported behaviour of not inferring a RAM although the conditions are apparently met seems like a bug to me. You should also tell to Altera support.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    In Quartus, from the Assignments menu, select "Design Partitions Window". Then from the Project Navigator window find all instances of your RAM and either right click to create a partition around it or click and drag it to the Design Partitions Window. If the code infers into memory when compiled at the top level, then I am pretty sure this will do the trick.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Yes, I have seen this exact circumstance - a register for the output of one RAM is the address register for another. QII synthesis makes an early decision of which register to pull in to each RAM to make it synchronous. Unfortunately, this is done independently for each RAM, so there's nothing to prevent the tool from choosing the same register for each RAM which then results in one RAM or the other not being inferred correctly. Does the second RAM have output registers in the RTL? I believe QII prefers to use the output register if available to make the RAM synchronous, so if you add the output register, it may stop the tool from trying to choose the read address register (which is the same as the output register already being chosen by the first RAM).