インテル® Quartus® Prime プロ・エディションのユーザーガイド: デザインの推奨事項

ID 683082
日付 8/03/2023
Public
ドキュメント目次

1.4.1.8. トゥルー・デュアルポート同期RAM

このセクションのコード例では、Verilog HDLおよびVHDLのコードでトゥルー・デュアルポート同期RAMを推論しています。 合成ツールによっては、これらのタイプのメモリーのサポートが異なる場合があります。

インテルFPGAの同期メモリーブロックには2つの独立したアドレスポートがあり、2つの一意のアドレスでの同時操作が可能です。読み出し操作と書き込み操作では、同じアドレスを共有している場合に、同じポートを共有することができます。

インテル® Quartus® Prime開発ソフトウェアは、Verilog HDLおよびVHDLのトゥルー・デュアルポートRAMを推論します。それには次の特徴があります。

  • 同じクロックサイクルでの独立した読み出しまたは書き込み操作の任意の組み合わせ
  • 最大2つの一意のポートアドレス
  • 1クロックサイクルにおいて、1つまたは2つの一意のアドレスで次の内容を実行することができます。
    • 2つの読み出しと1つの書き込み
    • 2つの書き込みと1つの読み出し
    • 2つの書き込みと2つの読み出し

同期RAMブロックのアーキテクチャーでは、2つのポート間に優先順位はありません。したがって、両方のポートで同じ位置に同時に書き込みを行う場合のデバイス・アーキテクチャーでの結果は不確定です。デザインを専用のハードウェア・メモリー・ブロックに実装する場合は、HDLコードでメモリーブロックへの書き込みの優先順位を暗示していないことを確認する必要があります。例えば、両方のポートが同じプロセスブロックで定義されている場合、コードは順番に合成およびシミュレーションされます。よって、2つのポート間には優先順位ができます。コードで優先順位を暗示している場合、ロジックはデバイスのRAMブロックに実装することができず、通常のロジックセルに実装されます。また、RAMブロックのRead-During-Write動作を考慮し、デバイスのRAMアーキテクチャーに直接マッピングできるようにする必要があります。

読み出し操作と書き込み操作が同じポートで同じアドレスに対して行われると、読み出し操作は次のように動作します。

  • Read new data - インテル® Arria® 10 および インテル® Stratix® 10 デバイスでこの動作をサポートしています。
  • Read old data - サポートされていません。

読み出し操作と書き込み操作が異なるポートで同じアドレスに対して行われると (混合ポートとも呼ばれる)、読み出し操作は次のように動作します。

  • Read new data - インテル® Quartus® Prime プロ・エディションの合成では、バイパスロジックを同期メモリーブロックの周囲に作成することで、このモードをサポートします。
  • Read old data - インテル® Arria® 10および インテル® Cyclone® 10デバイスでこの動作をサポートしています。
  • Read don’t care - 同期メモリーブロックは、シンプル・デュアルポート・モードでこの動作をサポートします。

Verilog HDLシングルクロック・コード例は、 インテル® Arria® 10同期メモリーブロックに直接マッピングされます。読み出し操作と書き込み操作が同じポートで同じアドレスに対して行われると、メモリーに書き込まれる新しいデータが読み出されます。読み出し操作と書き込み操作が異なるポートで同じアドレスに対して行われると、メモリーの古いデータが読み出されます。両方のポートで同じ位置に対して同時に書き込みを行う場合の動作は不確定です。

同じ動作を記述するこのデザインのデュアルクロック・バージョンを生成すると、ターゲットデバイスの推論メモリーは、未定義の混合ポートRead-During-Write動作を示します。これは、クロック間の関係に依存しているために起こります。

Verilog HDLトゥルー・デュアルポートRAM (シングルクロック使用)

/ Quartus Prime Verilog Template
// True Dual Port RAM with single clock
//
// Read-during-write behavior is undefined for mixed ports 
// and "new data" on the same port

module true_dual_port_ram_single_clock
#(parameter DATA_WIDTH=8, parameter 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];

	// Port A 
	always @ (posedge clk)
	begin
		if (we_a) 
		begin
			ram[addr_a] = data_a;
		end
		q_a <= ram[addr_a];
	end 

	// Port B 
	always @ (posedge clk)
	begin
		if (we_b) 
		begin
			ram[addr_b] = data_b;
		end
		q_b <= ram[addr_b];
	end

endmodule

VHDL読み出しステートメント例

-- 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;

VHDLシングルクロック・コード例は、インテルFPGA同期メモリーに直接マッピングされます。読み出し操作と書き込み操作が同じポートで同じアドレスに対して行われると、メモリーに書き込まれる新しいデータが読み出されます。読み出し操作と書き込み操作が異なるポートで同じアドレスに対して行われると、 インテル® Arria® 10および インテル® Cyclone® 10デバイスの場合は古いデータになり インテル® Stratix® 10デバイスの場合は未定義です。両方のポートで同じ位置に対して同時に書き込みを行う場合の動作は不確定です。

同じ動作を記述するこのデザインのデュアルクロック・バージョンを生成すると、ターゲットデバイスのメモリーは、未定義の混合ポートRead-During-Write動作を示します。これは、クロック間の関係に依存しているために起こります。

VHDLトゥルー・デュアルポートRAM (シングルクロック使用)

-- Quartus Prime VHDL Template
-- True Dual-Port RAM with single clock
--
-- Read-during-write behavior is undefined for mixed ports 
-- and "new data" on the same port

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 
	shared variable ram : memory_t;

begin

	-- 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;

end rtl;