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

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

2.4.2. Inferring ROM Functions from HDL Code

Synthesis tools infer ROMs when a CASE statement exists in which a value is set to a constant for every choice in the CASE statement.

Because small ROMs typically achieve the best performance when they are implemented using the registers in regular logic, each ROM function must meet a minimum size requirement for inference and placement in memory.

Note: If you use Intel® Quartus® Prime Standard Edition integrated synthesis , you can direct the Intel® Quartus® Prime software to infer ROM blocks for all sizes with the Allow Any ROM Size for Recognition option in the Advanced Analysis & Synthesis Settings dialog box.

Some synthesis tools provide options to control the implementation of inferred ROM blocks for Intel FPGA devices with synchronous memory blocks. For example, Intel® Quartus® Prime Standard Edition integrated synthesis provides the romstyle synthesis attribute to specify the type of memory block or to specify the use of regular logic instead of a dedicated memory block.

For device architectures with synchronous RAM blocks, such as the Arria®  series, Cyclone®  series, or Stratix®  series devices, to infer a ROM block, synthesis must use registers for either the address or the output. When your design uses output registers, synthesis implements registers from the input registers of the RAM block without affecting the functionality of the ROM. If you register the address, the power-up state of the inferred ROM can be different from the HDL design. In this scenario, Intel® Quartus® Prime synthesis issues a warning.

The following ROM examples map directly to the Intel FPGA memory architecture.

Verilog HDL Synchronous ROM

module sync_rom (clock, address, data_out);
	input clock;
	input [7:0] address;
	output reg [5:0] data_out;
	reg [5:0] data_out;

	always @ (posedge clock)
	begin
		case (address)
			8'b00000000: data_out = 6'b101111;
			8'b00000001: data_out = 6'b110110;
			...
			8'b11111110: data_out = 6'b000001;
			8'b11111111: data_out = 6'b101010;
		endcase
	end
endmodule

VHDL Synchronous ROM

LIBRARY ieee;
USE ieee.std_logic_1164.all;

ENTITY sync_rom IS
	PORT (
		clock: IN STD_LOGIC;
		address: IN STD_LOGIC_VECTOR(7 downto 0);
		data_out: OUT STD_LOGIC_VECTOR(5 downto 0)
	);
END sync_rom;

ARCHITECTURE rtl OF sync_rom IS
BEGIN
PROCESS (clock)
	BEGIN
	IF rising_edge (clock) THEN
		CASE address IS
			WHEN "00000000" => data_out <= "101111";
			WHEN "00000001" => data_out <= "110110";
			...
			WHEN "11111110" => data_out <= "000001";
			WHEN "11111111" => data_out <= "101010";
			WHEN OTHERS     => data_out <= "101111";
		END CASE;
	END IF;
	END PROCESS;
END rtl;

Verilog HDL Dual-Port Synchronous ROM Using readmemb

module dual_port_rom
#(parameter data_width=8, parameter addr_width=8)
(
	input [(addr_width-1):0] addr_a, addr_b,
	input clk, 
	output reg [(data_width-1):0] q_a, q_b
);
	reg [data_width-1:0] rom[2**addr_width-1:0];

	initial // Read the memory contents in the file
			 //dual_port_rom_init.txt. 
	begin
		$readmemb("dual_port_rom_init.txt", rom);
	end

	always @ (posedge clk)
	begin
		q_a <= rom[addr_a];
		q_b <= rom[addr_b];
	end
endmodule

VHDL Dual-Port Synchronous ROM Using Initialization Function

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity dual_port_rom is
	generic (
		DATA_WIDTH : natural := 8;
		ADDR_WIDTH : natural := 8
	);
	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;
		q_a     : out std_logic_vector((DATA_WIDTH -1) downto 0);
		q_b     : out std_logic_vector((DATA_WIDTH -1) downto 0)
	);
end entity;

architecture rtl of dual_port_rom is
	-- Build a 2-D array type for the ROM
	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;

	function init_rom
		return memory_t is 
		variable tmp : memory_t := (others => (others => '0'));
	begin 
		for addr_pos in 0 to 2**ADDR_WIDTH - 1 loop 
			-- Initialize each address with the address itself
			tmp(addr_pos) := std_logic_vector(to_unsigned(addr_pos, DATA_WIDTH));
		end loop;
		return tmp;
	end init_rom;	 

	-- Declare the ROM signal and specify a default initialization value.
	signal rom : memory_t := init_rom;
begin
	process(clk)
	begin
	if (rising_edge(clk)) then
		q_a <= rom(addr_a);
		q_b <= rom(addr_b);
	end if;
	end process;
end rtl;