Intel® Quartus® Prime Standard Edition User Guide: Design Recommendations

ID 683323
Date 9/24/2018
Public
Document Table of Contents

2.4.1.8. True Dual-Port Synchronous RAM

The code examples in this section show Verilog HDL and VHDL code that infers true dual-port synchronous RAM. Different synthesis tools may differ in their support for these types of memories.

Intel FPGA synchronous memory blocks have two independent address ports, allowing for operations on two unique addresses simultaneously. A read operation and a write operation can share the same port if they share the same address.

The Intel® Quartus® Prime software infers true dual-port RAMs in Verilog HDL and VHDL, with the following characteristics:

  • Any combination of independent read or write operations in the same clock cycle.
  • At most two unique port addresses.
  • In one clock cycle, with one or two unique addresses, they can perform:
    • Two reads and one write
    • Two writes and one read
    • Two writes and two reads

In the synchronous RAM block architecture, there is no priority between the two ports. Therefore, if you write to the same location on both ports at the same time, the result is indeterminate in the device architecture. You must ensure your HDL code does not imply priority for writes to the memory block, if you want the design to be implemented in a dedicated hardware memory block. For example, if both ports are defined in the same process block, the code is synthesized and simulated sequentially so that there is a priority between the two ports. If your code does imply a priority, the logic cannot be implemented in the device RAM blocks and is implemented in regular logic cells. You must also consider the read-during-write behavior of the RAM block to ensure that it can be mapped directly to the device RAM architecture.

When a read and write operation occurs on the same port for the same address, the read operation may behave as follows:

  • Read new dataThis mode matches the behavior of synchronous memory blocks.
  • Read old dataThis mode is supported only in device families that support M144K and M9K memory blocks.

When a read and write operation occurs on different ports for the same address (also known as mixed port), the read operation may behave as follows:

  • Read new data Intel® Quartus® Prime Standard Edition integrated synthesis supports this mode by creating bypass logic around the synchronous memory block.
  • Read old dataSynchronous memory blocks support this behavior.
  • Read don’t care—Synchronous memory blocks support this behavior in simple dual-port mode.

The Verilog HDL single-clock code sample maps directly into synchronous Intel memory . When a read and write operation occurs on the same port for the same address, the new data being written to the memory is read. When a read and write operation occurs on different ports for the same address, the old data in the memory is read. Simultaneous writes to the same location on both ports results in indeterminate behavior.

If you generate a dual-clock version of this design describing the same behavior, the inferred memory in the target device presents undefined mixed port read-during-write behavior, because it depends on the relationship between the clocks.

Verilog HDL True Dual-Port RAM with Single Clock

module true_dual_port_ram_single_clock
#(parameter DATA_WIDTH = 8, ADDR_WIDTH = 6)
(
	input [(DATA_WIDTH-1):0] data_a, data_b,
	input [(ADDR_WIDTH-1):0] addr_a, addr_b,
	input we_a, we_b, clk,
	output reg [(DATA_WIDTH-1):0] q_a, q_b
);


	// Declare the RAM variable
	reg [DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0];

	always @ (posedge clk)
	begin // Port a
		if (we_a)
		begin
			ram[addr_a] <= data_a;
			q_a <= data_a;
		end
		else
			q_a <= ram[addr_a];
	end
	always @ (posedge clk)
	begin // Port b
		if (we_b)
		begin
			ram[addr_b] <= data_b;
			q_b <= data_b;
		end
		else
			q_b <= ram[addr_b];
	end
endmodule

VHDL Read Statement Example

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

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

The VHDL single-clock code sample maps directly into Intel FPGA synchronous memory. When a read and write operation occurs on the same port for the same address, the new data writing to the memory is read. When a read and write operation occurs on different ports for the same address, the behavior is undefined. Simultaneous write operations to the same location on both ports results in indeterminate behavior.

If you generate a dual-clock version of this design describing the same behavior, the memory in the target device presents undefined mixed port read-during-write behavior because it depends on the relationship between the clocks.

VHDL 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((2**ADDR_WIDTH - 1) downto 0) of word_t;
	-- Declare the RAM signal.
	signal ram : memory_t;

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

	process(clk)
	begin
		if(rising_edge(clk)) then -- Port B
			if(we_b = '1') then
				ram(addr_b) <= data_b;
				-- Read-during-write on same port returns NEW data
				q_b <= data_b;
			else
	          -- Read-during-write on mixed port returns OLD data
				q_b <= ram(addr_b);
			end if;
		end if;
	end process;
end rtl;
The port behavior inferred in the Intel® Quartus® Prime software for the above example is:
PORT_A_READ_DURING_WRITE_MODE = "new_data_no_nbe_read"
PORT_B_READ_DURING_WRITE_MODE = "new_data_no_nbe_read"
MIXED_PORT_FEED_THROUGH_MODE = "old"