Getting XST (Xilinx' synthesis tool) to infer RAM or ROM that is dual-port requires some tricks.

For some reason, the two ports must be described by separate processes. Furthermore, an unusual VHDL construct, a shared variable, is needed 1.

Below is a listing of my parameterized module for dual-port RAM. It will successfully infer dual-port RAM, as desired, with XST. Remove the write enable-signals and write logic to get ROM instead of RAM. Specify width and depth with width and highAddr (highAddr is one less than desired depth) generics.

The code

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity genRAM is
  generic(
    width     : integer;
    highAddr  : integer -- highest address (= size-1)
  );
  port(
    -- Two sets of ports (A and B), each set having ports Adress, Data in,
    -- Data out and Write enable:
    Aaddr     : in  integer range 0 to highAddr        := 0;
    ADI       : in  std_logic_vector(width-1 downto 0) := (others => '0');
    ADO       : out std_logic_vector(width-1 downto 0) := (others => '0');
    AWE       : in  std_logic                          := '0';
    Baddr     : in  integer range 0 to highAddr        := 0;
    BDI       : in  std_logic_vector(width-1 downto 0) := (others => '0');
    BDO       : out std_logic_vector(width-1 downto 0) := (others => '0');
    BWE       : in  std_logic                          := '0';
    clk       : in  std_logic
  );
end genRAM;

architecture arch of genRAM is
  subtype TmemWord is bit_vector(width-1 downto 0);
  type    Tmem     is array(0 to highAddr) of TmemWord;
  shared variable memory: Tmem;

  process(clk) is
  begin
    if (rising_edge(clk)) then
      ADO <= To_StdLogicVector(memory(Aaddr));
      if (AWE = '1') then
        memory(Aaddr) := To_bitvector(std_logic_vector(ADI));
      end if;
    end if;
  end process;

  process(clk) is
  begin
    if (rising_edge(clk)) then    
      BDO <= To_StdLogicVector(memory(Baddr));
      if (BWE = '1') then
        memory(Baddr) := To_bitvector(std_logic_vector(BDI));
      end if;
    end if;
  end process;
end arch;

Read-first and write-first

The code above implements read-first behavior. That means that if address 0x00 contains 0xcafe and you write 0xbeef to 0x00, the cycle after the write will display 0xcafe on the data-out port ("data is read to output port before being written to memory").

If you desire write-first behaviour, change order of the reading and writing for both processes, below is how it would be for port A:

-- excerpt for write-first behaviour:
if (AWE = '1') then
  memory(Aaddr) := To_bitvector(std_logic_vector(ADI));
end if;
ADO <= To_StdLogicVector(memory(Aaddr));

In the above case, data-out would display 0xbeef one cycle after the write ("data is written to memory before reading memory contents to output port").

XST synthesis output

When identifying dual-port RAM, Synthesis should ouput something similar to the following in the synthesis report:

Synthesizing (advanced) Unit <genRAM>.
INFO:Xst:3040 - The RAM <memory> will be implemented as a BLOCK RAM,
absorbing the following register(s): <ADO> <BDO>
    -----------------------------------------------------------------------
    | ram_type           | Block                               |          |
    -----------------------------------------------------------------------
    | Port A                                                              |
    |     aspect ratio   | 128-word x 10-bit                   |          |
    |     mode           | write-first                         |          |
    |     clkA           | connected to signal <clk>           | rise     |
    |     weA            | connected to signal <AWE>           | high     |
    |     addrA          | connected to signal <Aaddr>         |          |
    |     diA            | connected to signal <ADI>           |          |
    |     doA            | connected to signal <ADO>           |          |
    -----------------------------------------------------------------------
    | optimization       | speed                               |          |
    -----------------------------------------------------------------------
    | Port B                                                              |
    |     aspect ratio   | 128-word x 10-bit                   |          |
    |     mode           | write-first                         |          |
    |     clkB           | connected to signal <clk>           | rise     |
    |     weB            | connected to signal <BWE>           | high     |
    |     addrB          | connected to signal <Baddr>         |          |
    |     diB            | connected to signal <BDI>           |          |
    |     doB            | connected to signal <BDO>           |          |
    -----------------------------------------------------------------------
    | optimization       | speed                               |          |
    -----------------------------------------------------------------------
Unit <genRAM> synthesized (advanced).

Notes:

  1. The variable must be accessible through two different processes, and hence must be shared. A signal wouldn't have worked; to implement write-first behaviour in a convenient way, a variable must be used, since the data written must be accessible on the next line of code.