--  Files containing most (if not all) features of vhdl08.
--  Like a comment.

--  TODO: at specifications.

context ctxt_ieee is
  library ieee;
  use ieee.std_logic_1164.all;
end ctxt_ieee;

context work.ctxt_ieee;

package pkg is
  --  TODO: file param,
  procedure clear (v : out std_logic_vector);

  type my_enum is
    (lit_a, lit_b, lit_c, 'e');
  subtype my_enum_lit is my_enum range lit_b to 'e';
  pure function "+" (v : my_enum) return my_enum;

  type my_short is range -2**15 to 2**15 - 1;
  type DISTANCE is range 0 to 1E16 units
    -- primary unit:
    angstrom;
    -- metric lengths:
    nm = 10 angstrom;
    um = 1000 nm;
    mm = 1000 um;
    cm = 10 mm;       -- Large unit.
  end units;

  type my_float is range 0.0 to 1.0e20;

  type my_array1d is array (my_short range <>) of boolean;
  subtype my_array1d10 is my_array1d (1 to 10);

  type my_array2d is array (natural range <>, natural range <>) of boolean;
  subtype my_array2d_8x8 is my_array2d (1 to 8, 1 to 8);

  type d2c_type is array (0 to 9) of character;
  type chess_type is array (1 to 8, 1 to 8) of std_logic_vector;

  attribute user_attr : boolean;
  attribute user_attr of clear [std_logic_vector]: procedure is True;

  type cell;
  type cell_acc is access cell;
  type cell is record
    chain : cell_acc;
    val : natural;
    b0, b1 : bit;
  end record;

  procedure prepend (variable ch : inout cell_acc; val : natural);

  type text_file is file of string;

  function is_eof (file t : text_file; fake : boolean) return boolean;

  alias iseof is is_eof [text_file, boolean return boolean];

  type sharedcounter is protected
     procedure increment (n : natural);
     procedure decrement (constant n : natural);
     impure function get return natural;
  end protected;

  type my_urec is record
    l : std_ulogic;
    adr : std_ulogic_vector;
    dat : std_ulogic_vector;
  end record;

  subtype my_urec8 is my_urec (adr (1 downto 0), dat (7 downto 0));
  subtype my_slv is (resolved) std_ulogic_vector;
end pkg;

package body pkg is
  pure function "+" (v : my_enum) return my_enum is
  begin
    return v;
  end "+";

  procedure clear (v : out std_logic_vector) is
  begin
    v := (v'range => '0');
  end clear;

  procedure prepend (variable ch : inout cell_acc; val : natural)
  is
    variable res : cell_acc;
    variable len : natural;
  begin
    --  Check if already in the list.
    res := ch;
    while res /= null loop
      if res.val = val then
       return;
      end if;
      res := res.all.chain;
      null;
    end loop;

    len := 0;
    res := ch;
    L1: loop
       exit L1 when res = null;
       len := len + 1;
       res := res.chain;
       next when res.val = val;
    end loop;

    res := new cell'(chain => ch, val => val, b0 | b1 => '0');
    ch := res;
  end prepend;

  function is_eof (file t : text_file; fake : boolean) return boolean is
  begin
    s: if fake then
      return false;
    else
      return endfile (t);
    end if s;
  end is_eof;

  procedure check_is_eof parameter (filename : string)
  is
    file f : text_file open read_mode is filename;
    file f2, f3 : text_file;
  begin
    null;
  end check_is_eof;

  type sharedcounter is protected body
    variable val : natural := 0;
    procedure increment (n : natural) is
    begin
      val := val + n;
    end increment;

    procedure decrement (constant n : natural) is
    begin
      val := val - n;
    end procedure decrement;

    impure function get return natural is
    begin
      return val;
    end function get;
  end protected body;
end pkg;

package genpkg is
  generic (val : natural := 5;
           function plus (l, r : integer) return integer);
  procedure add (l : inout integer);
end genpkg;

package body genpkg is
  procedure add (l : inout integer) is
  begin
    l := plus (l, val);
  end add;
end genpkg;

package genpkg2 is
  generic (v : natural;
           type t1;
           package subgenpkg is new work.genpkg generic map (<>));
end genpkg2;

package my_adder_pkg is new work.genpkg generic map (val => open, plus => "+");

library ieee, work;
use ieee.std_logic_1164.all;

entity reg is
  generic (width : natural);
  port (clk : std_logic;
        signal rst_n : std_logic;
        d : in std_logic_vector (width - 1 downto 0);
        q : out std_logic_vector (width - 1 downto 0));
  subtype bus_type is std_logic_vector (width - 1 downto 0);
begin
  ass1: postponed assert width < 128 report "large width" severity warning;
end reg;

library ieee;
use work.pkg.sharedcounter, ieee.std_logic_1164.all;

architecture behav of reg
is
  shared variable counter : sharedcounter;
begin
  process (clk, rst_n)
  begin
    if rising_edge(clk) then
      if rst_n = '0' then
        q <= (others => '0');
        counter.increment (1);
      else
        q <= d;
      end if;
    end if;
  end process;
end behav;

configuration reg_conf1 of reg is
  for behav
  end for;
end reg_conf1;

library ieee, work;
use ieee.std_logic_1164.all;

entity check_zero is
  port (i0 : in std_logic);
end check_zero;

architecture behav of check_zero is
begin
  assert (i0 = '0');
end behav;

entity reg_tb is
  generic (conf : natural := 2);
end reg_tb;

library ieee;
use ieee.std_logic_1164.all;
use work.pkg.all;

architecture behav of reg_tb is
  component reg is
    generic (width : natural);
    port (clk : std_logic;
          rst_n : std_logic;
          d : in std_logic_vector (width - 1 downto 0);
          q : out std_logic_vector (width - 1 downto 0));
  end component reg;

  component check_zero is
    port (i0 : in std_logic);
  end component check_zero;

  subtype data_type is std_logic_vector (31 downto 0);

  function get_vector (n : natural) return data_type is
  begin
    case n is
      when 0 =>
        return x"0000_0000";
      when 1 =>
        return x"0000_0001";
      when 2 =>
        return x"1111_1111";
      when 3 | 4 =>
        return data_type'(x"3333_4444");
      when 5 to 7 =>
        return (0 to 5 => '1', 6 | 7 => '0', others => '1');
      when 8 =>
        return ('0', '1', '0', others => '0');
      when others =>
        return x"ffff_ffff";
    end case;
  end get_vector;

  signal clk : std_logic bus;
  signal rst_n : std_logic := '0';
  signal din, dout : data_type;

  signal s1 : std_logic;

  signal si : integer;
  signal si2 : integer;

  alias my_clk : std_logic is clk;

  group syn is (subtype, signal <>);
  group sig_syn : syn (data_type, clk, rst_n);

  type data_array_type is array (natural range <>) of data_type;

  constant zero : natural := 0;

  procedure disp_msg (msg : string) is
  begin
    if msg'left (1) /= 1 then
      report "strange start" severity note;
    elsif msg'length > 20 then
      report "long message";
    else
      report msg
        severity note;
    end if;
  end disp_msg;

  for cmpz0 : check_zero use entity work.check_zero port map (i0 => i0);
  for cmpz1 : check_zero use open;
  for others : check_zero use entity work.check_zero;
begin
  process
  begin
    clk <= '0', '1' after 10 ns;
    wait for 20 ns;
  end process;

  rst_n <= '0', '1' after 25 ns;

  si2 <= 1 when rst_n = '0' else
         2 when rst_n = '1';

  cmpz0 : check_zero port map (i0 => din (0));
  cmpz1 : check_zero port map (i0 => din (1));
  cmpz2 : check_zero port map (i0 => din (2));

  disp_msg ("start of design");

  process (all)
  begin
    s1 <= not rst_n;
    assert s1'driving and s1'driving_value /= '0';
  end process;

  si <= integer'(1) when clk = '0' else 2;

  assert si'event or si'active or si'last_value < 3;
  assert si'last_active < 10 ns and si'last_event < 10 ns;
  assert si'transaction = '0';

  postponed process is
  begin
    disp_msg (msg => "test is starting """ & reg_tb'simple_name & '"');

    for i in 1 to 10 loop
      din <= get_vector(i);
      wait on my_clk until rising_edge(my_clk);
    end loop;

    wait;
  end process;

  compute: process
    subtype byte_idx is natural range 0 to 7;
    variable v : integer;
    variable b1, b2, b3 : boolean;
    variable bv1, bv2 : bit_vector (byte_idx);
    variable d : distance;
  begin
    b2 := true;
    b1 := (b2 and b3) or b1;
    b3 := (b1 xor b2) nand b3;
    b2 := (b1 nor b2) xnor b3;

    assert byte_idx'left = 0 and byte_idx'low = 0;
    assert byte_idx'right = 7 and byte_idx'high = 7;
    assert byte_idx'ascending;

    assert boolean'pos(b1) = 1;

    bv1 := bv2 sll v;
    bv2 := (bv1 rol v) and 8x"f0";
    bv1 := not(bv2 sra (v rem 3));

    v := -2;
    v := ((3 * v) / 4) ** 2;
    v := (v + 4) - 1;
    v := natural (v mod 128);
    b1 := v >= 3;
    b2 := v /= 4;
    b3 := b2 or (v = 5);

    d := 1.5 cm when v >= 0 else mm;

    report "v = " & integer'image (v) severity note;
    wait;
  end process compute;

  cmp_reg : reg
    generic map (width => 32)
    port map (clk => clk,
      rst_n => rst_n,
      d => din,
      q => dout);

  blk1: block (clk)
    signal dout2 : data_type register;
    disconnect dout2 : data_type after 1 ns;

    signal dout3 : data_type;
    signal dout4 : data_type;

    for cmpz1_0, cmpz1_1 : check_zero use entity work.check_zero;
  begin
    assert dout (7 downto 0) = din (7 downto 0);
    assert dout'right = 0;

    cmpz1_0 : check_zero port map (i0 => din (0));
    cmpz1_1 : check_zero port map (i0 => din (1));

    dout2 <= guarded din;

    with dout(0) select
      dout4 <= din when '0',
              (others => '1') when others;

    g1: for i in 0 to 40 generate
       g2: if i <= data_type'left generate
         cmp: entity work.reg
            generic map (width => 1)
            port map (clk => clk,
                       rst_n => rst_n,
                       d(0) => din (i),
                       q(0) => dout3 (i));
      end generate g2;
    end generate g1;
  end block;

  blk2: block is
    generic (w : natural);
    generic map (w => 1);
    port (di : std_logic_vector (w - 1 downto 0);
          do : out std_logic_vector (w - 1 downto 0));
    port map (di => din (0 downto 0),
              do => dout (0 downto 0));

    for all : check_zero use entity work.check_zero;
  begin
    cmpz1_0 : check_zero port map (i0 => din (0));

    g4: case conf generate
      when g4_1: 1 | 2 =>
        begin
          cmp : configuration work.reg_conf1
            generic map (width => 1)
            port map (clk => clk,
                      rst_n => std_logic (rst_n),
                      d => di,
                      q => do);
        end g4_1;
      when others =>
    end generate g4;
  end block blk2;
end behav;

configuration cfg of reg_tb is
  for behav
    --  component configuration.
    for cmp_reg : reg
      use entity work.reg (behav);
    end for;
    for blk1
      for g1(1)
      end for;
      for g1(2 to 3)
        for g2
        end for;
      end for;
    end for;
  end for;
end cfg;
