Memories in VHDL

Digital IC design and vlsi notes


Memories in VHDL

source this playlist on VHDL design.

Registers

  • Registers are not suitable for large scale storage in asics and fpgas, instead we have to declare memories in a way that allows the synthesizer to know we are talking about memories instead of registers

Memories in FPGAs and ASICs

  • Two types of memories: roms and rams
  • the syntax allows the synthesizer to know realise we are talking about memories, what it does with memories depends on the platform
    • in FPGAs it usually implements it as block rams (specialized cells in fpgas that performs mass storage efficiently)
    • in asics srams and embedded memories usually implemented using a separate memory compiler and the synthesizer will often ignore the parts where we declare memories and leave them to the memory compiler, otherwise the synthesizer might implement it as a large register file
  • when the synthesizer see the initialization of the memory contents it knows we are declaring a rom as its contents exist at compilation time
  • in case of a large rom, we declare the contents of the rom usually using file io
  • we can remove the effect of the enable by hardwiring it when we instantiate the rom
entity rom_block is
    generic(
        address_bits : integer := 2;
        rom_word_width : integer := 4;
    );
    port(
        clk : in std_logic;
        en : in std_logic;
        address_bus : in std_logic_vector(address_bits-1 downto 0);
        data_out : out std_logic_vector(rom_word_width-1 downto 0)
    );
end entity;

architecture behavioral of rom_block is
    type rom_type is array ((2**address_bits)-1 down to 0) of std_logic_vector(rom_word_width-1 downto 0);
    signal rom_contents : rom_type := ("0000","0001","1111","1110");
    signal read_data : std_logic_vector(rom_word_width-1 downto 0);
begin
    read_data <= rom_contents(conv_integer(address_bus));
    process(clk)
    begin
        if clk'event and clk='1' then
            if en='1' then
                data_out <= read_data;
            end if;
        end if;
    end process;
end architecture;

Rams

entity ram_block is
    generic(
        address_bits : integer := 2;
        ram_word_width : integer := 4;
    );
    port(
        clk : in std_logic;
        en : in std_logic;
        we : in std_logic;
        address_bus : in std_logic_vector(address_bits-1 downto 0);
        data_in : in std_logic_vector(ram_word_width-1 downto 0);
        data_out : out std_logic_vector(ram_word_width-1 downto 0)

    );
end entity;

architecture behavioral of ram_block is
    type ram_type is array ((2**address_bits)-1 down to 0) of std_logic_vector(ram_word_width-1 downto 0);
    signal ram_memory : ram_type;
begin
    process(clk)
    begin
        if clk'event and clk='1' then
            if en='1' then
                if we='1' then
                    ram_memory(conv_integer(address_bus)) <= data_in;
                else
                    data_out <= ram_memory(conv_integer(address_bus));
                end if;
            end if;
        end if;
    end process;
end architecture;

what to do with data out bus when writing

  • read first the last value from the location to the output before writing to it
    --- read-first method, data_out takes the value of location from previous cycle
    if we='1' then
      ram_memory(conv_integer(address_bus)) <= data_in;
    end if;
    data_out <= ram_memory(conv_integer(address_bus));
    
  • latch the output ,read the last value of the data out port regardless of what was written on this cycle
    -- latched output, The data_out port preserves its last known value
    if we='1' then
      ram_memory(conv_integer(address_bus)) <= data_in;
    else
      data_out <= ram_memory(conv_integer(address_bus));
    end if;
    
  • write first, the value of data out port takes the new value ``` – Write first, the value of data_out port takes the new value if we=’1’ then ram_memory(conv_integer(address_bus)) <= data_in; data_out <= data_in else data_out <= ram_memory(conv_integer(address_bus)); end if;

## Multiport memory
- mutli port memory, multiple address buses 002,004
- shared_variable is a variable declared in the architecture part, and exists between multiple processes 
- we have mutliple processes a process for every independent bus, they share the memory contents, so we need to share the memory between them
- if you use a signal to declare the memory and writing to it by the two processes, you'll get an error because a signal cann't be written to by two processes, this will create a contention on the node and it's not allowed.
- the only way to create multiple processes writing to the same node is to use a shared variable.

entity ram_2p is generic( address_bits : integer := 2; ram_word_width : integer := 4; ); port( clk1 : in std_logic; en1 : in std_logic; we1 : in std_logic; address_bus1 : in std_logic_vector(address_bits-1 downto 0); data_in1 : in std_logic_vector(ram_word_width-1 downto 0); data_out1 : out std_logic_vector(ram_word_width-1 downto 0) clk2 : in std_logic; en2 : in std_logic; we2 : in std_logic; address_bus2 : in std_logic_vector(address_bits-1 downto 0); data_in2 : in std_logic_vector(ram_word_width-1 downto 0); data_out2 : out std_logic_vector(ram_word_width-1 downto 0)

); end entity;

architecture behavioral of ram_2p is type ram_type is array ((2**address_bits)-1 down to 0) of std_logic_vector(ram_word_width-1 downto 0); signal ram_memory : ram_type; begin process(clk1) begin if clk1’event and clk1=’1’ then if en1=’1’ then if we1=’1’ then ram_memory(conv_integer(address_bus1)) <= data_in1; else data_out1 <= ram_memory(conv_integer(address_bus1)); end if; end if; end if; end process;

process(clk2)
begin
    if clk2'event and clk2='1' then
        if en2='1' then
            if we2='1' then
                ram_memory(conv_integer(address_bus2)) <= data_in;
            else
                data_out2 <= ram_memory(conv_integer(address_bus2));
            end if;
        end if;
    end if;
end process;

end architecture; ```