1

This is my first time posting, so I will try to be as more specific as I can.

In the part of the code that I am going to post, I am trying to implement in a generic way the code in the "Case"-expression that I have commented out. This code, is part of a bigger implementation and I have only kept the signals that are currently in use in this code.

So the thing is that I want my outputs "kin" and "din" to be generic and in accordance to what comes as input in the address bus "bus_a", the correct words (2-byte long) of the registers "kin_2" and "din_2" should be filled with the value of the "bus_di" input at that time.

For my example here, I am going to use the original lengths for "kin" and "din", which are 128-bit each. So for 128-bit length, N=16 (16*8-bit = 128bit) and K=8.

library IEEE;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_1164.ALL;

entity stck_if is
    generic (N: integer:=16; K: integer:=8);

    port (  -------------- Clock and Reset
        clk: in std_logic;
        rst: in std_logic;


        bus_a: in std_logic_vector (15 downto 0); -- Address
        bus_di: in std_logic_vector (15 downto 0); --Data In

        kin: out std_logic_vector (8*N-1 downto 0);  
        din: out std_logic_vector (8*N-1 downto 0));    

end stck_if;

architecture stck_if_arch of stck_if is

        signal kin_2: std_logic_vector (8*N-1 downto 0);
        signal din_2: std_logic_vector (8*N-1 downto 0);
        signal encdec_2: std_logic;

        signal trig_wr: std_logic;

        begin

            proc1: process(clk,rst)
                variable int_add: integer:=0;
                variable add: std_logic_vector (15 downto 0);
                variable bit_add: std_logic_vector (15 downto 0);
                begin
                    if rst='1' then
                        encdec_2<='0';  
                        kin_2<=(others =>'0');
                        din_2<=(others =>'0');
                    elsif (clk'event and clk='1') then
                        if (trig_wr = '1') then

                            if (bus_a = "0000000000001100") then
                                    encdec_2 <= bus_di(0);
                            end if;

                            for i in 0 to K-1 loop
                                bit_add:="0000000100000000";
                                int_add:= 2*i;
                                add:=std_logic_vector(to_unsigned(int_add, 16));
                                bit_add:= std_logic_vector(unsigned(bit_add) + unsigned(add));
                                if (bus_a = bit_add) then
                                        kin_2((8*(N-int_add)-1) downto 8*(N-int_add)) <= bus_di;
                                end if;                             
                            end loop;


                            for i in 0 to K-1 loop
                                bit_add:="0000000101000000";
                                int_add:= 2*i;
                                add:=std_logic_vector(to_unsigned(int_add, 16));
                                bit_add:= std_logic_vector(unsigned(bit_add) + unsigned(add));
                                if (bus_a = bit_add) then
                                        din_2((8*(N-int_add)-1) downto 8*(N-int_add)) <= bus_di;
                                end if;                             
                            end loop;   



                            --case bus_a is
                            --  when "0000000000001100"=> encdec_2 <= bus_di(0);    --bus_a = 000C hex
                            --  when "0000000100000000"=> kin_2(127 downto 112) <= bus_di;  --bus_a = 0100 hex
                            --  when "0000000100000010"=> kin_2(111 downto 96) <= bus_di;   --bus_a = 0102 hex
                            --  when "0000000100000100"=> kin_2(95 downto 80) <= bus_di;    --bus_a = 0104 hex
                            --  when "0000000100000110"=> kin_2(79 downto 64) <= bus_di;    --bus_a = 0106 hex                                                                          
                            --  when "0000000100001000"=> kin_2(63 downto 48) <= bus_di;    --bus_a = 0108 hex
                            --  when "0000000100001010"=> kin_2(47 downto 32) <= bus_di;    --bus_a = 010A hex
                            --  when "0000000100001100"=> kin_2(31 downto 16) <= bus_di;    --bus_a = 010C hex
                            --  when "0000000100001110"=> kin_2(15 downto 0) <= bus_di; --bus_a = 010E hex

                            --  when "0000000101000000"=> din_2(127 downto 112) <= bus_di;  --bus_a = 0140 hex
                            --  when "0000000101000010"=> din_2(111 downto 96) <= bus_di;   --bus_a = 0142 hex
                            --  when "0000000101000100"=> din_2(95 downto 80) <= bus_di;    --bus_a = 0144 hex
                            --  when "0000000101000110"=> din_2(79 downto 64) <= bus_di;    --bus_a = 0146 hex                                                                          
                            --  when "0000000101001000"=> din_2(63 downto 48) <= bus_di;    --bus_a = 0148 hex
                            --  when "0000000101001010"=> din_2(47 downto 32) <= bus_di;    --bus_a = 014A hex
                            --  when "0000000101001100"=> din_2(31 downto 16) <= bus_di;    --bus_a = 014C hex
                            --  when "0000000101001110"=> din_2(15 downto 0) <= bus_di; --bus_a = 014E hex
                            --  when others => null;                    
                            --end case;
                        end if;
                    end if;
            end process;



            kin <= kin_2;
            din <= din_2;

end stck_if_arch;

I am using ModelSim PE Student Edition 10.4a. In order to simulate the operation of the implementation, I use the following code at the command line of ModelSim:

restart -f

//clock period 20ns
force -freeze sim:/stck_if/clk 1 0, 0 {10 ns} -r 20


//Activate reset
force -freeze sim:/stck_if/rst 1 0
run

//activate wr
force -freeze sim:/stck_if/trig_wr 1 0

force -freeze sim:/stck_if/rst 0 0
run

force -freeze sim:/stck_if/bus_a 16'h0100 0
force -freeze sim:/stck_if/bus_di 16'h1111 0
run
run


force -freeze sim:/stck_if/bus_a 16'h0102 0
run
run

force -freeze sim:/stck_if/bus_a 16'h0104 0
run
run

force -freeze sim:/stck_if/bus_a 16'h0106 0
run
run

force -freeze sim:/stck_if/bus_a 16'h0108 0
run
run

etc.

Although the code is compiling without giving any error, during simulation ModelSim gives the following error:

  • Cannot continue because of fatal error. HDL call sequence: Stopped at C:/Modeltech_pe_edu_10.4a/examples/stack.vhd 53 ForLoop loop

From what I understand, the problem is in the use of "For Loop" and from what I assume, it must be that VHDL cannot translate the following expression:

kin_2((8*(N-int_add)-1) downto 8*(N-int_add)) <= bus_di;

into an actual circuit.

Am I right with my hypothesis? Any ideas of how to overcome this problem, or any suggestions on where I should look/read to find my answers, would be really helpfull!

N. Mos.
  • 66
  • 1
  • 9
  • Note that the range `n-1 downto n` is a null range : used as a subrange within a vector it describes a vector with no elements. But that alone isn't a fatal error. Any messages about width mismatches in there? –  Jul 11 '16 at 20:09
  • @BrianDrummond You were right about the range! I was checking again at the start of the error messages and the first error that comes up (in blue color?!) says: **Fatal: (vsim-3420) Array lengths do not match. Left is 0 (127 downto 128 (null array)). Right is 16 (15 downto 0).** I fixed it by adding a "+2" at the second "int_add". Now the command is the following: **kin_2((8*(N-int_add)-1) downto 8*(N-(int_add+2))) <= bus_di;** I must have been really sleepy. Now the code works like a charm. Any optimizations or advices would be welcome! Thank you! – N. Mos. Jul 11 '16 at 22:50

2 Answers2

0

The following version should be functionally equivalent to yours, shorter, and -in my humble opinion- easier to read. However, I don't know what the synthesis result will be. I find it difficult to imagine which the inferred primitives will be in single-process descriptions with variables.

architecture stck_if_arch of stck_if is
  signal kin_2: std_logic_vector (8*N-1 downto 0);
  signal din_2: std_logic_vector (8*N-1 downto 0);
  signal encdec_2: std_logic;
  signal trig_wr: std_logic;  
begin
  proc1: process(clk,rst)
    variable high: unsigned(9 downto 0);
    variable bid: integer range 0 to N-1;
    variable left, right: integer range 0 to 8*N-1;
  begin
    if rst='1' then
      encdec_2<='0';  
      kin_2<=(others =>'0');
      din_2<=(others =>'0');
    elsif (clk'event and clk='1') then
      if (trig_wr = '1') then

        high := unsigned(bus_a(15 downto 6));
        bid := to_integer(unsigned(bus_a(5 downto 1)));
        left := 16*(8-bid)-1;
        right := 16*(7-bid);

        if bus_a="0000000000001100" then
          encdec_2 <= bus_di(0);
        elsif bus_a(0)='0' then
          if high=4 then
            kin_2(left downto right) <= bus_di;
          elsif high=5 then
            din_2(left downto right) <= bus_di;
          end if;
        end if;

      end if;
    end if;
  end process;

 kin <= kin_2;
 din <= din_2;

end stck_if_arch;
0

I had the same idea U.Martinez-Corral had with a bit of a twist:

architecture foo of stck_if is
    type N_array is array (0 to K - 1) of std_logic_vector(N - 1 downto 0);
    signal kin_2: N_array;
    signal din_2: N_array;
    signal encdec_2: std_logic;
    signal trig_wr: std_logic := '1'; -- ;  -- FOR TESTING without FORCE
begin

proc1: 
    process(clk,rst)
        variable high: unsigned(9 downto 0);
        variable Kidx: integer range 0 to K-1;
    begin
        if rst = '1' then
            encdec_2 <= '0';  
            kin_2 <= (others => (others => '0'));
            din_2 <= (others => (others => '0'));
        elsif rising_edge(clk) then
            if trig_wr = '1' then
                high := unsigned(bus_a (15 downto 6));
                Kidx := to_integer(unsigned(bus_a(3 downto 1)));
                if bus_a = "0000000000001100" then
                    encdec_2 <= bus_di(0);
                elsif bus_a(0) = '0' then
                    if high = 4 then
                        kin_2(kidx) <= bus_di;
                    elsif high = 5 then
                        din_2(kidx) <= bus_di;
                    end if;
                end if;
            end if;
        end if;
    end process;

UNION:
    if K = 8 generate
        kin <= kin_2(0) & kin_2(1) & kin_2(2) & kin_2(3) & 
               kin_2(4) & kin_2(5) & kin_2(6) & kin_2(7);
        din <= din_2(0) & din_2(1) & din_2(2) & din_2(3) & 
               din_2(4) & din_2(5) & din_2(6) & din_2(7);
    end generate;  -- GENERATE statement for every usable K value

end architecture foo;

This would require a generate statement for each useful value of K due to the lack of unions in VHDL. It's the ugly part of this.

Otherwise, the idea here is to not use bit (element index) ranges.

And a testbench for those of us who are not Modelsim license holders/users:

library ieee;
use ieee.std_logic_1164.all;

entity stck_if_tb is
end entity;

architecture foo of stck_if_tb is
    constant K:     integer := 8;
    constant N:     integer := 16;
    signal clk:     std_logic := '0';
    signal rst:     std_logic;

    signal bus_a:   std_logic_vector (15 downto 0);
    signal bus_di:  std_logic_vector (15 downto 0);

    signal kin:     std_logic_vector (8 * N - 1 downto 0);  
    signal din:     std_logic_vector (8 * N - 1 downto 0);
begin
DUT:
    entity work.stck_if 
        generic map (
            N => N,
            K => K
        )
        port map (
            clk => clk,
            rst => rst,
            bus_a => bus_a,
            bus_di => bus_di,
            kin => kin,
            din => din
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        if now > 240 ns then
            wait;
        end if;
    end process;
STIMULI:
    process
    begin
        wait for 20 ns;
        rst <= '1';
        wait for 20 ns;
        rst <= '0';
        bus_a <= x"0100";
        bus_di <= x"1111";
        wait for 20 ns;
        wait for 20 ns;
        bus_a <= x"0102";
        wait for 20 ns;
        wait for 20 ns;
        bus_a <= x"0104";
        wait for 20 ns;
        wait for 20 ns;
        bus_a <= x"0106";
        wait for 20 ns;
        wait for 20 ns;
        bus_a <= x"0108";
        wait for 20 ns;
        wait for 20 ns;
        wait;
    end process;
end architecture;

And that gives:

stck_if_tb_foo.png

Note that I the added trig_wr default value when not using a force.

Addendum

U. Martinez-Corral commented, demonstrating again we are generally bad at doing index arithmetic while suggesting the use of a for generate statement. (IEEE Std 1076-2008, 11.8 Generate statements)

This demonstrates the method with the loop bounds corrected to match the commented out case statement in the original question:

UNION: 
    for x in K - 1 downto 0 generate 
        kin(N * (x + 1) - 1 downto N * x) <= kin_2(K - 1 - x); 
        din(N * (x + 1) - 1 downto N * x) <= din_2(K - 1 - x); 
    end generate;

It generates the same waveform with the testbench.

  • I think that you could use a for-generate as the `union` you mention: `UNION: for x in K downto 0 generate kin(N*(x+1)-1 downto N*x) <= kin_2(x); din(N*(x+1)-1 downto N*x) <= din_2(x); end generate;`. – U.Martinez-Corral Jul 12 '16 at 16:16
  • 1
    The idea was to avoid slice arithmetic but a generate statement with a for scheme would be elaboration time calculation and appears to work except for one slight problem K downto 0 is 9 slices of din and kin (K - 1 downto 0?), it's also wrong endian (see the commented case in the OP's original, should be <= kin_2(K - 1 - x)? A quick check shows that works). (We are generally weak applying this sort of abstraction, perhaps we need a right hand side construct to shorten and generalize set solutions.) –  Jul 12 '16 at 20:09