------------------------------------------------------------------------------
--  Copyright (C) 2011, Kenichi Kurimoto
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; if not, write to the Free Software
--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
-----------------------------------------------------------------------------
-- Entity:      idct2
-- File:        idct2.vhd
-- Author:      Kenichi Kurimoto 
-- Description: 2nd IDCT calculation for jpeg decode
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library grlib;
use grlib.stdlib.all;

entity idct2 is
    port ( rst : in  std_ulogic;
           clk : in  std_ulogic;
           ready1 : out std_logic;
           strobe1 : in std_logic;
           coeffin : in  std_logic_vector (15 downto 0);
           outdata : out  std_logic_vector (7 downto 0);
           ready2 : in std_logic;
           strobe2 : out std_logic;
           startgen : in std_logic
           );
end idct2;

architecture rtl of idct2 is

function mysigned_mul(a,b : std_logic_vector) return std_logic_vector is
variable z : std_logic_vector(a'length + b'length -1 downto 0);
begin
    z := std_logic_vector(signed(a) * signed(b));
    return(z);
end;


function mysigned_add(a,b : std_logic_vector) return std_logic_vector is
variable ex_a : std_logic_vector(a'length downto 0);
variable ex_b : std_logic_vector(b'length downto 0);
variable z1 : std_logic_vector(a'length downto 0);
variable z2 : std_logic_vector(b'length downto 0);
begin
    ex_a := a(a'left) & a;
    ex_b := b(b'left) & b;
    if( a'length > b'length)then
       z1 := std_logic_vector(signed(ex_a) + signed(ex_b));
       return(z1);
    else
       z2 := std_logic_vector(signed(ex_a) + signed(ex_b));
       return(z2);
    end if;
end;

function round1(indata : std_logic_vector(32 downto 0)) return std_logic_vector is
variable judge :std_logic;
variable z : std_logic_vector(14 downto 0);
begin
	judge := indata(17);
	if (judge = '0') then
		z := indata(32 downto 18);
	else 
		z := indata(32 downto 18) + 1;
	end if;
	return(z);
end;

function round2(indata : std_logic_vector(32 downto 0); pol : std_logic) return std_logic_vector is
variable judge : std_logic;
variable tmpdata : std_logic_vector(32 downto 0);
variable z : std_logic_vector(14 downto 0);
begin
	if (pol = '1') then
		tmpdata := not indata + 1 ;
	else
		tmpdata := indata;
	end if;
	judge := tmpdata(17);
	if (judge = '1') then
		z := tmpdata(32 downto 18) + 1;
	else
		z := tmpdata(32 downto 18);
	end if;
	return(z);
end;

constant S_ZERO : SIGNED := "0000000000";
constant S_TWOFIVEFIVE : SIGNED := "0011111111";

function round3(indata : std_logic_vector(17 downto 0)) return std_logic_vector is
variable judge : std_logic;
variable z : std_logic_vector(12 downto 0);
variable value : integer;
begin
	judge := indata(4);
	value := 0;
	if (judge = '0') then
		z := indata(17 downto 5);
	else
		z := indata(17 downto 5) + 1;
	end if;
  if notx(z) then	value := to_integer(signed(z)); end if;
   if (value < -128) then
       return("10000000");
   end if;
   if(value > 127) then
       return("01111111");
   end if;	
	return(z(7 downto 0));
end;

			

subtype coeff23 is std_logic_vector(16 downto 0);
type coeff_array1 is array(0 to 31) of coeff23;
constant coeff_rom : coeff_array1 :=

("01011010100000101","01011010100000101","01011010100000101","01011010100000101",
 "01111101100010101","01101010011011011","01000111000111010","00011000111110001",
 "01110110010000011","00110000111111000","11001111000001001","10001001101111101",
 "01101010011011011","11100111000001111","10000010011101100","10111000111000111",
 "01011010100000101","10100101011111100","10100101011111100","01011010100000101",
 "01000111000111010","10000010011101100","00011000111110001","01101010011011011",
 "00110000111111000","10001001101111101","01110110010000011","11001111000001001",
 "00011000111110001","10111000111000111","01101010011011011","10000010011101100");


type tablereg_type is array (0 to 3) of std_logic_vector(16 downto 0);
type accumulator_type is array (0 to 7) of std_logic_vector(17 downto 0);
type resultreg_type is array (0 to 7) of std_logic_vector(7 downto 0);


type d_reg is record
   inreg : std_logic_vector(15 downto 0);
	accumulator : accumulator_type;
	result_reg : resultreg_type;
end record;

type c_reg is record
	counter : std_logic_vector(6 downto 0);
end record;

type all_reg is record
	data_reg : d_reg;
	control_reg : c_reg;
end record;
	
type node1_array is array (0 to 3) of std_logic_vector(16 downto 0);
type node2_array is array (0 to 3) of std_logic_vector(32 downto 0);
type node3_array is array (0 to 7) of std_logic_vector(14 downto 0);
type node4_array is array (0 to 7) of std_logic_vector(17 downto 0);
type node5_array is array (0 to 7) of std_logic_vector(18 downto 0);
type node6_array is array (0 to 7) of std_logic_vector(7 downto 0);
	
signal r, rin : all_reg;
--signal sig_node1_0 : std_logic_vector(16 downto 0);
--signal sig_node2_0 : std_logic_vector(32 downto 0);
--signal sig_node3_0 : std_logic_vector(14 downto 0);
--signal sig_node4_0 : std_logic_vector(17 downto 0);
--signal sig_node5_0 : std_logic_vector(18 downto 0);
--signal sig_node6_0 : std_logic_vector(7 downto 0);
--signal sig_node1_3 : std_logic_vector(16 downto 0);
--signal sig_node2_3 : std_logic_vector(32 downto 0);
--signal sig_node3_4 : std_logic_vector(14 downto 0);
--signal sig_node4_4 : std_logic_vector(17 downto 0);
--signal sig_node5_4 : std_logic_vector(18 downto 0);
--signal sig_node6_4 : std_logic_vector(7 downto 0);


begin

comb : process(r, rst, strobe1, ready2, coeffin, startgen)
	variable v : all_reg;
	variable node1 : node1_array;
	variable node2 : node2_array;
	variable node3 : node3_array;
	variable node4 : node4_array;
  variable node5 : node5_array;
	variable node6 : node6_array;
	variable node7 : std_logic_vector(9 downto 0);
	variable node8 : std_logic_vector(9 downto 0);
	variable pol : std_logic;

	variable count_num : integer;
	variable vstrobe2 : std_logic;
	variable vready1 : std_logic;
begin
	
	v := r;
	vstrobe2 := '0';
	count_num := to_integer(unsigned(r.control_reg.counter));
	
	v.data_reg.inreg := coeffin;
	
	case count_num is
	when 2 | 10 | 18 | 26 | 34 | 42 | 50 | 58 =>
		node1(0) := coeff_rom(4);
		node1(1) := coeff_rom(5);
		node1(2) := coeff_rom(6);
		node1(3) := coeff_rom(7);
	when 3 | 11 | 19 | 27 | 35 | 43 | 51 | 59 =>
		node1(0) := coeff_rom(8);
		node1(1) := coeff_rom(9);
		node1(2) := coeff_rom(10);
		node1(3) := coeff_rom(11);
	when 4 | 12 | 20 | 28 | 36 | 44 | 52 | 60 =>
		node1(0) := coeff_rom(12);
		node1(1) := coeff_rom(13);
		node1(2) := coeff_rom(14);
		node1(3) := coeff_rom(15);
	when 5 | 13 | 21 | 29 | 37 | 45 | 53 | 61 =>
		node1(0) := coeff_rom(16);
		node1(1) := coeff_rom(17);
		node1(2) := coeff_rom(18);
		node1(3) := coeff_rom(19);
	when 6 | 14 | 22 | 30 | 38 | 46 | 54 | 62 =>
		node1(0) := coeff_rom(20);
		node1(1) := coeff_rom(21);
		node1(2) := coeff_rom(22);
		node1(3) := coeff_rom(23);
	when 7 | 15 | 23 | 31 | 39 | 47 | 55 | 63 =>
		node1(0) := coeff_rom(24);
		node1(1) := coeff_rom(25);
		node1(2) := coeff_rom(26);
		node1(3) := coeff_rom(27);
	when 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 =>
		node1(0) := coeff_rom(28);
		node1(1) := coeff_rom(29);
		node1(2) := coeff_rom(30);
		node1(3) := coeff_rom(31);		
	when others =>
		node1(0) := coeff_rom(0);
		node1(1) := coeff_rom(1);
		node1(2) := coeff_rom(2);
		node1(3) := coeff_rom(3);
	end case;
	
	for i in 0 to 3 loop
	   node2(i) := mysigned_mul(node1(i), r.data_reg.inreg);
	   node3(i) := round1(node2(i));
	end loop;
	
	
--	when 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | ..... | 65 
-- when 2   4   6   8   10   12   14   16   18   20   22           64  

	if((count_num mod 2) = 0 and (count_num >= 2) and (count_num <= 64))then
		pol := '1';
	else
		pol := '0';
	end if;
			
	node3(4) := round2(node2(3), pol);
	node3(5) := round2(node2(2), pol);
	node3(6) := round2(node2(1), pol);
	node3(7) := round2(node2(0), pol);
	
	if((count_num = 1) or (count_num = 9) or (count_num = 17) or (count_num = 25) or (count_num = 33) or (count_num = 41) or (count_num = 49) or (count_num = 57)) then
		for i in 0 to 7 loop
		   node4(i) := (others => '0');
		end loop;
	else
	   for i in 0 to 7 loop
	      node4(i) := r.data_reg.accumulator(i);
	   end loop;
	end if;

   for i in 0 to 7 loop
      node5(i) := mysigned_add(node3(i), node4(i));
      v.data_reg.accumulator(i) := node5(i)(17 downto 0);
      node6(i) := round3(r.data_reg.accumulator(i));
   end loop;
	
	if((count_num = 9) or (count_num = 17) or (count_num = 25) or (count_num = 33) or (count_num = 41) or (count_num = 49) or (count_num = 57) or (count_num = 65)) then
		for i in 0 to 7 loop
		   v.data_reg.result_reg(i) := node6(i);
		end loop;
	else
	   for i in 0 to 6 loop
	      v.data_reg.result_reg(i) := r.data_reg.result_reg(i+1);
	   end loop;
	   v.data_reg.result_reg(7) := (others => '0');
	end if;

	node7 := mysigned_add(r.data_reg.result_reg(0),"010000000");

   if((count_num >= 10) and (count_num <= 73))then
      vstrobe2 := '1';
   end if; 

-- controller part
   if ((count_num = 0 and strobe1 = '1') or count_num /= 0) then
       v.control_reg.counter := std_logic_vector(to_unsigned(count_num + 1,7));
       if(count_num = 73)then
           v.control_reg.counter := (others => '0');
       end if;
   end if;
   vready1 := '0';
   if(ready2 = '1' and count_num <= 63)then
       vready1 := '1';
   end if;
   
-- reset part
   if rst = '0' or startgen = '1' then
       v.data_reg.inreg := (others => '0');
       for i in 0 to 7 loop
          v.data_reg.accumulator(i) := (others => '0');
          v.data_reg.result_reg(i) := (others => '0');
       end loop;
       v.control_reg.counter := (others => '0');
   end if; 
         
-- signal
	outdata <= node7(7 downto 0);
	strobe2 <= vstrobe2;
	ready1 <= vready1;
	rin <= v;

-- debug
--   sig_node1_0 <= node1(0);
--   sig_node2_0 <= node2(0);
--   sig_node3_0 <= node3(0);
--   sig_node4_0 <= node4(0);
--   sig_node5_0 <= node5(0);
--   sig_node6_0 <= node6(0);
--   sig_node1_3 <= node1(3);
--   sig_node2_3 <= node2(3);
--   sig_node3_4 <= node3(4);
--   sig_node4_4 <= node4(4);
--   sig_node5_4 <= node5(4);
--   sig_node6_4 <= node6(4);
end process;

-- registers
reg : process(clk)
begin
	if rising_edge(clk) then
		r <= rin;
	end if;
end process;

end rtl;

