0
\$\begingroup\$

I have created a component for a simulated, serial EEPROM to use in a testbench. The logic for performing the SPI communications is straightforward. In addition, I have created a byte array, with preloaded, simulated, initial EEPROM values into it. Of course, within the EEPROM logic, I need to be able to read and write the memory array.

But here's the problem. Right now, the code reads from a fixed array "EEPROM_CONFIGURATION_DATA" as shown below.

shifter_out <= EEPROM_CONFIGURATION_DATA( to_integer(ee_address) );

While this works, I would like to be able to create multiple instances of the EEPROM component, and have each instance access a different memory array.

What I need is the equivalent of passing an array pointer to each EEPROM component instance so that it accesses its own memory array.

This is for simulation only, so I don't expect this to be synthesized.

Of course, I could always create unique copies of the EEPROM component, and each would access their own unique memory arrays (hard coded). But I was wondering if there were another solution.

EDIT - Additional information.

Here is the component definition. The intent is to pass t_512_byte_eeprom_array (type) into the component as an input.

   entity tb_25LC512_EEPROM is
   port ( 
    MEMORY_ARRAY_DATA       : in    t_512_byte_eeprom_array;
    configured              : in    std_ulogic;
   
    eeprom_cs_n             : in    std_ulogic;
    eeprom_din              : in    std_ulogic;
    eeprom_dout             : out   std_ulogic;
    eeprom_sclk             : in    std_ulogic;
    eeprom_hold_n           : in    std_ulogic;     -- unused
    
    --eeprom_wp_n           : in    std_ulogic;
    
    clock_320MHZ            : in    std_ulogic);
   end entity tb_25LC512_EEPROM;

Here is the definition of the type.

-----------------------------------------------------
-- Create two 512 byte arrays, unsigned type
-- 1. EEPROM_CONFIGURATION_DATA
-- 2. EEPROM_HUC_DATA
--
-- When we create instances of the tb_25LC512_EEPROM
-- components, use these signals as inputs for the
-- MEMORY_ARRAY_DATA.
--
-----------------------------------------------------
type t_512_byte_eeprom_array is array (0 to 511) of unsigned(7 downto 0);       -- fully constrained type
signal EEPROM_CONFIGURATION_DATA    : t_512_byte_eeprom_array;
signal EEPROM_HUC_DATA              : t_512_byte_eeprom_array;

Reading data from a file,and putting information into array.

procedure read_configuration_data(
signal      EEPROM_CONFIGURATION_DATA   : out   t_512_byte_eeprom_array
) is

    file        file_pointer        : text;
    variable    line_input_value    : line;
    variable    hex_value           : std_logic_vector(7 downto 0);
    variable    file_index          : integer;

    begin

        ----------------------
        -- Open the input file
        ----------------------

        file_open(file_pointer, "Configuration_Test_3.txt", READ_MODE);
                    
    
        for file_index in 0 to 511 loop
            -----------------------
            -- Read loop
            -----------------------
                    
            readline( file_pointer, line_input_value );                         
            hread( line_input_value, hex_value );                               
            EEPROM_CONFIGURATION_DATA(file_index)       <= unsigned(hex_value);
        end loop;
    
        file_close(file_pointer);

end procedure;

Create two instances of the component in the testbench, and map signals to the components. One of them brings in array EEPROM_CONFIGURATION_DATA and the other EEPROM_HUC_DATA.

    ----------------------------------------------
    -- Create an instance of the tb_25LC512 eeprom
    -- and map to Config EEPROM signals
    ----------------------------------------------
   tb_25LC512_EEPROM_CONFIG : tb_25LC512_EEPROM
   port map ( 
    MEMORY_ARRAY_DATA       => EEPROM_CONFIGURATION_DATA,
    configured              => eeprom_config_configured,
   
    eeprom_cs_n             => eeprom_cs_n,
    eeprom_din              => eeprom_si,
    eeprom_dout             => eeprom_so,
    eeprom_sclk             => eeprom_sck,
    eeprom_hold_n           => eeprom_hold_n,
    
    clock_320MHZ            => clock_320MHZ
    );
    
    
    
    ----------------------------------------------
    -- Create an instance of the tb_25LC512 eeprom
    -- and map to HUC EEPROM signals
    ----------------------------------------------
   tb_25LC512_EEPROM_HUC : tb_25LC512_EEPROM
   port map ( 
    MEMORY_ARRAY_DATA       => EEPROM_HUC_DATA,
    configured              => eeprom_huc_configured,
   
    eeprom_cs_n             => huc_eeprom_cs_n,
    eeprom_din              => huc_eeprom_si,
    eeprom_dout             => huc_eeprom_so,
    eeprom_sclk             => huc_eeprom_sck,
    eeprom_hold_n           => huc_eeprom_hold_n,
    
    clock_320MHZ            => clock_320MHZ
    );  

It appears as if the compiler doesn't like the array type that I created. Here is the error message.

** Error: C:/HDL_Simulations/complete_breu_simulation_6/tb_25LC512_EEPROM.vhdl(68): (vcom-1136) Unknown identifier "t_512_byte_eeprom_array".

I seem to be having trouble passing the array (pointer I assume) to the component when it's mapped. What am I doing wrong ?

============ MORE INFO ============

I've gotten around the compilation error. I created a package that has the type definition for the EEPROM memory.


-- Library Declarations

library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all;


-- Package Declaration.

package eeprom_memory_pkg is
type t_512_byte_eeprom_array is array (0 to 511) of unsigned(7 downto 0);       -- fully constrained type
end package eeprom_memory_pkg;

The package was compiled into the breu_lib, and then referenced in both the upper level testbench code and in the EEPROM component as well. This stopped the compilation error since both the upper level and lower level code was aware of the definition of the type.

library breu_lib;
use breu_lib.eeprom_memory_pkg.all;

The problem now seems to be that when the instance of the tb_25LC512 component is created and mapped, that MEMORY_ARRAY_DATA has nothing in it (viewing in Modelsim). EEPROM_CONFIGURATION_DATA was created in an upper level of the testbench, was and filled with data. However, mapping MEMORY_ARRAY_DATA to EEPROM_CONFIGURATION_DATA didn't seem to work. Of course, MEMORY_ARRAY_DATA is of the same type as EEPROM_CONFIGURATION_DATA (t_512_byte_eeprom_array). Does anyone have any suggestions ? Also, how is the array information passed from a higher level to the lower level component ? Is the entire array passed, or is it just a reference to the array ?

    ----------------------------------------------
    -- Create an instance of the tb_25LC512 eeprom
    -- and map to Config EEPROM signals
    ----------------------------------------------
   tb_25LC512_EEPROM_CONFIG : tb_25LC512_EEPROM
   port map ( 
    MEMORY_ARRAY_DATA       => EEPROM_CONFIGURATION_DATA,

================== LATEST INFO ======================

Here is what I have found. The reason why MEMORY_ARRAY_TYPE does not have any data in it is because EEPROM_CONFIGURATION_DATA does not have any data in it. The type for this data array come from a library (breu_lib.eeprom_memory_pkg.all) as shown above. So as a test, I created another array (EEPROM_CONFIGURATION_DATA_2) and assigned it a type identical to the one in the eeprom_memory_pkg. The only difference is that the type assigned to EEPROM_CONFIGURATION_DATA_2 was created locally, and the type for EEPROM_CONFIGURATION_DATA came from the package in the library. Manual assignment of the first 8 elements of both arrays were performed. The array that had type locally defined had data placed into it, and the array that had the type defined in the library package did not have data placed into it.

        --------------------------------------------------------------------------
        -- EEPROM_CONFIGURATION_DATA is of type t_512_byte_eeprom_array. This 
        -- array is 512 bytes of unsigned(7 downto 0) data. This type is defined
        -- in breu_lib.eeprom_memory_pkg.
        -- The package shows up within the breu_lib. It has been compiled,
        -- recompiled, updated, and refreshed. 
        -- Values aren't assigned to the array as expected, but no errors either.
        --------------------------------------------------------------------------
            
        EEPROM_CONFIGURATION_DATA(0) <= to_unsigned(0,8);
        EEPROM_CONFIGURATION_DATA(1) <= to_unsigned(1,8);   
        EEPROM_CONFIGURATION_DATA(2) <= to_unsigned(2,8);
        EEPROM_CONFIGURATION_DATA(3) <= to_unsigned(3,8);   
        EEPROM_CONFIGURATION_DATA(4) <= to_unsigned(4,8);
        EEPROM_CONFIGURATION_DATA(5) <= to_unsigned(5,8);   
        EEPROM_CONFIGURATION_DATA(6) <= to_unsigned(6,8);
        EEPROM_CONFIGURATION_DATA(7) <= to_unsigned(7,8);       

    -------------------------------------------------------------------------
    -- EEPROM_CONFIGURATION_DATA_2 is of type t_512_eeprom_array_2. This type
    -- is an identical array of 512 bytes of unsigned(7 downto 0) data like
    -- the one above. This type is defined above within this file.
    -- This works. Data is assigned as expected.
    -------------------------------------------------------------------------
    
    EEPROM_CONFIGURATION_DATA_2(0) <= to_unsigned(0,8);
    EEPROM_CONFIGURATION_DATA_2(1) <= to_unsigned(1,8); 
    EEPROM_CONFIGURATION_DATA_2(2) <= to_unsigned(2,8);
    EEPROM_CONFIGURATION_DATA_2(3) <= to_unsigned(3,8); 
    EEPROM_CONFIGURATION_DATA_2(4) <= to_unsigned(4,8);
    EEPROM_CONFIGURATION_DATA_2(5) <= to_unsigned(5,8); 
    EEPROM_CONFIGURATION_DATA_2(6) <= to_unsigned(6,8);
    EEPROM_CONFIGURATION_DATA_2(7) <= to_unsigned(7,8); 

Is there something wrong with the way that the type or package was defined ? Reference to the package in library is straightforward. Why wasn't data assigned to EEPROM_CONFIGURATION_DATA array ?

========== LATEST INFO =========

Problem may be found, but not a solution. If the port direction of MEMORY_ARRAY_DATA is changed from "inout" to "in", then the values show up in EEPROM_CONFIGURATION_DATA. If port direction is set to "inout", then "X" shows up in Modelsim view of EEPROM_CONFIGURATION_DATA (like a driver conflict).

How would one build a component to interface to a read/write memory array without "inout" port direction? The memory array itself doesn't care if you are reading or writing. Could one make two ports on the component, one for read and one for write, both accessing the same memory array? I will try and see.

   entity tb_25LC512_EEPROM is
   port ( 
    MEMORY_ARRAY_DATA       : in    t_512_byte_eeprom_array;        -- was inout
    configured              : in    std_ulogic;
   
    eeprom_cs_n             : in    std_ulogic;
    eeprom_si               : in    std_ulogic;
    eeprom_so               : out   std_ulogic;
    eeprom_sck              : in    std_ulogic;
    eeprom_hold_n           : in    std_ulogic;     -- unused
    
    --eeprom_wp_n           : in    std_ulogic;
    
    clock_320MHZ            : in    std_ulogic);
   end entity tb_25LC512_EEPROM;
\$\endgroup\$
6
  • \$\begingroup\$ Pass the array through an in port. The EEPROM behavior shouldn't define the data, the higher level component should. Each instance of the EEPROM has its own port map, and thus independent contents. \$\endgroup\$
    – Ben Voigt
    Feb 2 at 20:09
  • \$\begingroup\$ How big is your memory? How do you plan on initializing your memory? Do you use all of it during a simulation or only a portion of it? Memory can consume lots of your computer memory. \$\endgroup\$
    – Jim Lewis
    Feb 3 at 15:54
  • \$\begingroup\$ Have you checked out the memory models at free model foundry freemodelfoundry.com/fmf_VHDL_models.php \$\endgroup\$
    – Jim Lewis
    Feb 3 at 15:57
  • \$\begingroup\$ You can initialize your memory with either a generic - passing the constant you want or by reading a file. If you are reading a file, maybe you want to use the OSVVM memory model data structure as it can do the read of memory files for you (it reads the same format that Verilog reads - most formats can be converted to this). OSVVM is available at: github.com/OSVVM/OsvvmLibraries or osvvm.org/downloads \$\endgroup\$
    – Jim Lewis
    Feb 3 at 16:06
  • \$\begingroup\$ After "Latest info": Why do you want the port to be inout? \$\endgroup\$ Feb 5 at 8:51

1 Answer 1

1
\$\begingroup\$

You can do this by using arrays of arrays:

library ieee;
use ieee.std_logic_1164.all;
entity multi_dimensional_array is
end entity multi_dimensional_array;
architecture struct of multi_dimensional_array is
    type t_eeprom_mem   is array (natural range <>) of std_logic_vector(7 downto 0);
    type t_eeprom_array is array (natural range <>) of t_eeprom_mem(1 downto 0);

    signal eeprom_array : t_eeprom_array (2 downto 0) := (
    0 => (X"34", X"12"),
    1 => (X"78", X"56"),
    2 => (X"BC", X"9A")
    );
    signal data : std_logic_vector(7 downto 0);
begin
    data <= eeprom_array(0)(0),
            eeprom_array(0)(1) after 10 ns,
            eeprom_array(1)(0) after 20 ns,
            eeprom_array(1)(1) after 30 ns,
            eeprom_array(2)(0) after 40 ns,
            eeprom_array(2)(1) after 50 ns,
            eeprom_array(0)(0) after 60 ns;
    
end architecture;
\$\endgroup\$
1
  • \$\begingroup\$ Depending on the size of the memory, I would not recommend either std_logic_vector or the use of signals here. \$\endgroup\$
    – Jim Lewis
    Feb 3 at 15:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.