--- Sample Code for LSU EE 4702-1 Spring 2001
---
--- Population Modules and Testbench
---

 -- Note: synthesizablity not tested.

 --- Utility functions.

library ieee;
use ieee.std_logic_1164.all;

package mystuff is
  function "srl" (a:std_logic_vector; n:natural) return std_logic_vector;
  function "srl" (a:integer; n:natural) return integer;
end mystuff;

package body mystuff is
  function "srl" (a:std_logic_vector; n:natural) return std_logic_vector is
    variable b: std_logic_vector ( a'range );
  begin
    b := ( others => '0' );
    b( b'high - n downto b'low ) := a( a'high downto a'low + n );
    return b;
  end;
  function "srl" (a:integer; n:natural) return integer is
    variable p:integer;
  begin
    if a >= 0 then
      return a / 2 ** n;
    else
      p:= (-a) / 2 ** n;
      if p > 0 then p:= p - 1; end if;
      return integer'high / 2 ** (n-1) - p;
    end if;
  end;
end mystuff;


 --- Combinational Population Counter

library ieee;
use ieee.std_logic_1164.all;
library Std_DevelopersKit;
use Std_DevelopersKit.Std_Regpak.all;
library work;
use work.mystuff.all;

entity popsc is
  port( pop:out std_logic_vector (3 downto 0);
        a: in std_logic_vector (7 downto 0));
end entity popsc;


architecture form1 of popsc is

begin
  process(a)
    variable pop_work: std_logic_vector ( pop'range );
    variable a_work: std_logic_vector ( a'range );

  begin

    pop_work := ( others => '0' );
    a_work := a;

    for i in a'range loop
      pop_work := pop_work + a_work(0);
      a_work := a_work srl 1;
    end loop;

    pop <= pop_work;

  end process;
    
end form1;


 --- Sequential Population Counters


library ieee;
use ieee.std_logic_1164.all;
library Std_DevelopersKit;
use Std_DevelopersKit.Std_Regpak.all;
library work;
use work.mystuff.all;

entity pops is
  port( pop:out std_logic_vector (3 downto 0);
        rdy:out std_logic;
        a: in std_logic_vector (7 downto 0);
        clk:in std_logic);
end entity pops;


architecture form3 of pops is

begin
  process
    variable pop_work: std_logic_vector ( pop'range );
    variable a_work, a_copy: std_logic_vector ( a'range ) := ( others => '0' );
  begin

    wait until rising_edge(clk);

    if a_copy /= a  then 
      wait until rising_edge(clk);

      rdy    <= '0';
      pop_work := to_stdlogicvector(0,pop'length);
      a_work := a;
      a_copy := a;

      wait until rising_edge(clk);

      while a_work /= 0 loop
        wait until rising_edge(clk);
        pop_work := pop_work + a_work(0);
        a_work := a_work srl 1;
      end loop;

      pop <= pop_work;

      wait until rising_edge(clk);

    end if;

    if a_copy = a then rdy <= '1'; else rdy <= '0'; end if;

  end process;

end form3;


library ieee;
use ieee.std_logic_1164.all;
library Std_DevelopersKit;
use Std_DevelopersKit.Std_Regpak.all;
use Std_DevelopersKit.Std_IOpak.all;
library work;
use work.mystuff.all;

entity poptb is end;

architecture a of poptb is

  signal clk:std_logic:= '0';
  signal rdy:std_logic;
  signal pout,pout2:std_logic_vector (3 downto 0);
  signal num:std_logic_vector (7 downto 0) := ( others => '0' );

   function pop(a : integer) return integer is
     variable p: integer := 0;
     variable b: integer := a;
   begin
     while b /= 0 loop
       p := p + b mod 2;
       b := b srl 1;
     end loop;
     return p;
   end;

begin

  pop1:entity work.pops(form3) port map(pout,rdy,num,clk);
  pop2:entity work.popsc(form1) port map(pout2,num);

  clk <= not clk after 2 ns;
  
  process
    variable check: integer := 0;

  begin
    num <= to_stdlogicvector(1,num'length);
    wait until rdy = '0'; wait until rdy = '1'; 
    
    for i in 0 to 255 loop

      num <= to_stdlogicvector(i,num'length);

      if num /= i then wait until rdy = '0'; end if;
      wait until rdy = '1';
      
      assert '0' & pout = pop(i)
        report "Seq wrong value i " & to_string(i) & " pop: " & to_string(pout)
        severity failure;

      assert '0' & pout2 = pop(i)
        report "Cmb wrong value i " & to_string(i) & " pop: " & to_string(pout2)
        severity failure;

      check := check + to_integer( '0' & pout );

    end loop;

    assert check = 256 * 4  
      report "Incorrect check.";

    assert false
      report "Done with tests."
      severity failure;
    
  end process;

end a;