Search code examples
vhdlxilinx

How can i fill and display a matrix ? [VHDL]


I have a datain as an std_logic_vector and i want fill a matrix with datain's bits and then display it.

How can i fill and display the matrix ?

Here is my code:

    signal datain : std_logic_vector(39 downto 0) := "1111011101100110011001010110011001100110";

     for i1 in 1 to 5 loop 
     for j1 in 1 to 8 loop
     for j2 in datain'range loop
     mat1(i1,j1)<=datain(j2);
      end loop;
      end loop; 
      end loop;

      ------- display the matrix

        for i2 in 1 to 5 loop 
         for i3 in 1 to 8 loop  
          for i4 in dataout'range loop       
           dataout(i4) <= mat1(i2,i3);
          end loop;
        end loop;
       end loop;    

Thank you,


Solution

  • First we construct a Minimal, Complete and Verifiable example from your code snippets:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity abir is
    end entity;
    
    architecture foo of abir is
    
        type mat_type is array (1 to 5, 1 to 8) of std_logic;
        signal mat1: mat_type;
        signal datain : std_logic_vector(39 downto 0) := 
                    "1111011101100110011001010110011001100110";
        signal dataout: std_logic_vector (39 downto 0);  -- MISSING
    
        -- this function is predefined in VHDL -2008:
        function to_string (inp: std_logic_vector) return string is
            variable image_str: string (1 to inp'length);
            alias input_str:  std_logic_vector (1 to inp'length) is inp;
        begin
            for i in input_str'range loop
                image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
            end loop;
            return image_str;
        end function;
    begin
    
    INITIALIZE_MATRIX:
        process  -- (datain)
        begin
            for i1 in 1 to 5 loop 
                for j1 in 1 to 8 loop
                    for j2 in datain'range loop
                        mat1(i1,j1)<=datain(j2);
                    end loop;
                end loop; 
            end loop;
            wait;   -- Do only once, depends on the initial value of datain
        end process; -- the wait statement can be removed if you add sensitivity
    
       ------- display the matrix
    MATRIX_T0_DATAOUT:
        process (mat1)
        begin
            for i2 in 1 to 5 loop
                for i3 in 1 to 8 loop
                    for i4 in dataout'range loop
                        dataout(i4) <= mat1(i2,i3);
                    end loop;
                end loop;
            end loop;
        end process;
    
    DISPLAY_DATAOUT:
        process -- (dataout)
        begin               -- wait statements so only disply valid datout
            wait for 0 ns;  -- first delta cycle all 'U's (dataout uninitialized)
            wait for 0 ns;  -- second delta cycle all 'U's (mat1 uninitialized)
            report LF &
                   HT & "datain  = " & to_string(datain) & LF & 
                   HT & "dataout = " & to_string(dataout);
            wait on dataout;
        end process;
    end architecture;
    

    The function to_string is predefined in VHDL -2008, this MCVE should work with tools compliant earlier revisions of the VHDL standard.

    It's specific to demonstrating your code. It gives:

    ghdl -a abir.vhdl
    ghdl -e abir
    ghdl -r abir
    abir.vhdl:58:9:@0ms:(report note):
        datain  = 1111011101100110011001010110011001100110
        dataout = 0000000000000000000000000000000000000000
    

    So there's something wrong with your nested loops (you can also verify this with a waveform viewer to determine mat1 is indeed all '0's).

    So the cause of this is the very inner loops. With datain you assign each element of matrix mat1(i,j) N times where N is the length of datain (range of j2). With dataout you assign each indexed element of dataout (i4) every matrix element of mat(i2,i3).

    So is it possible to have three loops performing these assignments?

    Well, no.

    In the INITIALIZE_MATRIX process every (i,j) location of mat1 was overwritten with all the index values of datain. Only the last one took affect. This filled the matrix with all '0's.

    In the MATRIX_TO_DATAOUT process all the dataout indexes were 'scheduled' to have the last mat1(i2,i3) value of each i3 loop iteration, settling on the last loop iteration value of i2 and i3, a '0'.

    We can modify the two sets of loops to decrement j2 or i4 as variables directly (the ranges of datain and dataout are in in descending order):

    INITIALIZE_MATRIX:
        process  -- (datain)
            variable j2: natural range datain'range;
        begin
            j2 := datain'LEFT;  -- so the process can execute again.
            for i1 in 1 to 5 loop 
                for j1 in 1 to 8 loop
                    -- for j2 in datain'range loop
                        mat1(i1,j1) <= datain(j2);
                        if j2 /= datain'RIGHT then
                            j2 := j2 - 1;  -- datain has descending range
                        end if;
                    -- end loop;
                end loop; 
            end loop;
            wait;   -- Do only once, depends on the initial value of datain
        end process; -- the wait statement can be removed if you add sensitivity
    
       ------- display the matrix
    MATRIX_T0_DATAOUT:
        process (mat1)
            variable i4: natural range dataout'range;
        begin
            i4 := dataout'LEFT;  -- so the process can execute again
            for i2 in 1 to 5 loop
                for i3 in 1 to 8 loop
                    -- for i4 in dataout'range loop
                        dataout(i4) <= mat1(i2,i3);
                        if i4 /= dataout'RIGHT then
                            i4 := i4 - 1;  -- dataout has descending range
                        end if;
                    -- end loop;
                end loop;
            end loop;
        end process;
    

    And that gives us:

    abir.vhdl:68:9:@0ms:(report note):
        datain  = 1111011101100110011001010110011001100110
        dataout = 1111011101100110011001010110011001100110
    

    Where we find dataout matches datain. (A good thing.)

    So the issue was the three nested loops in each process were incorrect. We wanted to manage the pointers to the input and output arrays separately.

    We also manage the assignments to the variables j2 or i4 to prevent a bounds violation using if statements to prevent j2 or i4 being decremented when the variable assignment would be out of the value range of the variable. A bounds check failure on assignment would abort the simulation.

    Note that signal assignment results in a value being written to a projected output waveform (a queue). Signal updates don't occur before any pending process has run and suspended. There's only one value for any time in the projected output waveform. (including the current simulation time).

    These two modified processes could be used as the basis of conversion functions:

    architecture fum of abir is
    
        type mat_type is array (1 to 5, 1 to 8) of std_logic;
        signal mat1: mat_type;
        signal datain : std_logic_vector(39 downto 0) := 
                    "1111011101100110011001010110011001100110";
        signal dataout: std_logic_vector (39 downto 0);  -- MISSING
    
        -- this function is predefined in VHDL -2008:
        function to_string (inp: std_logic_vector) return string is
            variable image_str: string (1 to inp'length);
            alias input_str:  std_logic_vector (1 to inp'length) is inp;
        begin
            for i in input_str'range loop
                image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
            end loop;
            return image_str;
        end function;
    
        function to_matrix (inp: std_logic_vector) return  mat_type is
            alias input: std_logic_vector(0 to inp'length - 1) is inp; -- ascending
            variable mat: mat_type;
            variable inptr: natural range 0 to inp'length;
        begin
            assert input'length = mat'length(1) * mat'length(2)
            report LF & 
                "to_matrix call, input length (" &
                integer'image(inp'length) & ") " &
                "/= " & integer'image( mat'length(1) * mat'length(2))
            severity FAILURE;
            for i in mat'range(1) loop      -- first dimension
                for j in mat'range(2) loop  -- second dimension
                    mat(i,j) := input(inptr);
                    inptr := inptr + 1;  -- inptr range allows last increment
                end loop;
            end loop;
            return mat;
        end function;
    
        function to_std_logic_vector (mat: mat_type) return std_logic_vector is
            variable retval: 
                std_logic_vector(0 to mat'length(1) * mat'length(2) - 1);
            variable outptr: natural range 0 to retval'length;
        begin
            for i in mat'range(1) loop      -- first dimension
                for j in mat'range(2) loop  -- second dimension
                    retval(outptr) := mat(i,j);
                    outptr := outptr + 1; -- outptr range allows last increment
                end loop;
            end loop;
            return retval;
        end function;
    begin
    
    INITIALIZE_MATRIX:
        mat1 <= to_matrix(datain);
    
    MATRIX_T0_DATAOUT:
        dataout <= to_std_logic_vector(mat1);
    
    DISPLAY_DATAOUT:
        process -- (dataout)
        begin               -- wait statements so only disply valid datout
            wait for 0 ns;  -- first delta cycle all 'U's (dataout uninitialized)
            wait for 0 ns;  -- second delta cycle all 'U's (mat1 uninitialized)
            report LF &
                   HT & "datain  = " & to_string(datain) & LF & 
                   HT & "dataout = " & to_string(dataout);
            wait for 1 ns;
            wait on dataout;
        end process;
    end architecture;
    

    The two functions are dependent only on the matrix type declaration. You can change the mat_type declaration without having to modify declarations or the any of the sequence of statements found in the functions.

    The new architecture with the to_matrix[std_logic_vector return mat_type] and to_std_logic_vector[mat_type return std_logic_vector] function calls provides the same answer as the MCVE with the corrected process statements.