--
-- Copyright (c) 1999-2000 University of California, Riverside.
-- Permission to copy is granted provided that this header remains
-- intact.  This software is provided with no warranties.
--
-- Version : 1.0
--

-------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;

-------------------------------------------------------------------------------

entity DCTC is

    generic(STAT_REG_ADDR : INTEGER := 65527;
            CMND_REG_ADDR : INTEGER := 65528;
            DATAI_REG_ADDR : INTEGER := 65530;
            DATAO_REG_ADDR : INTEGER := 65529);
        
    port(rst      : in  STD_LOGIC;
         clk      : in  STD_LOGIC;
         addr     : in  UNSIGNED (15 downto 0);
         in_data  : in  UNSIGNED (7 downto 0);
         out_data : out UNSIGNED (7 downto 0);
         rd       : in  STD_LOGIC;
         wr       : in  STD_LOGIC);
end DCTC;

-------------------------------------------------------------------------------

architecture BHV of DCTC is

    type S_TYPE is array (0 to 7) of SIGNED (23 downto 0);
      
    type COS_ROW_TYPE is array (0 to 7) of SIGNED (6 downto 0);
    type COS_BLOCK_TYPE is array (0 to 7) of COS_ROW_TYPE;

    type ROW_TYPE is array (0 to 7) of SIGNED (15 downto 0);
    type BLOCK_TYPE is array (0 to 7) of ROW_TYPE;
    
    type STATE_TYPE1 is (ST1_IDLE, ST1_WAIT, ST1_INCREMENT);
    type STATE_TYPE2 is (ST2_IDLE, ST2_COMPUTE, ST2_FINISH);
  
    constant CD_8  : UNSIGNED (7 downto 0)  := "--------";
    constant CD_16 : SIGNED   (15 downto 0) := "----------------";
    constant CD_24 : SIGNED   (23 downto 0) := "------------------------";
    constant C0_2  : SIGNED   ( 1 downto 0) := "00";
    constant C0_6  : SIGNED   ( 5 downto 0) := "000000";
    constant C0_7  : SIGNED   ( 6 downto 0) := "0000000";
    constant C0_8  : SIGNED ( 7 downto 0) := "00000000";
    constant C0_13 : SIGNED   (12 downto 0) := "0000000000000";
    constant C0_17 : SIGNED   (16 downto 0) := "00000000000000000";
    constant C0_18 : SIGNED   (17 downto 0) := "000000000000000000";
    constant C0_24 : SIGNED   (23 downto 0) := "000000000000000000000000";
    constant C0_46 : SIGNED   (45 downto 0) := "0000000000000000000000000000000000000000000000";
    constant CZ_8  : UNSIGNED ( 7 downto 0) := "ZZZZZZZZ";
    constant C1_8  : UNSIGNED ( 7 downto 0) := "00000001";

    constant ONE_OVER_SQRT_TWO : SIGNED (  5 downto 0 ) :=        "000101";  -- 0,6
    constant SIXTYFOUR         : SIGNED ( 12 downto 0 ) := "1000000000000";  -- 7,6 
    constant SIXTEEN           : SIGNED ( 10 downto 0 ) :=   "10000000000";  -- 5,6 
    
    constant COS_TABLE : COS_BLOCK_TYPE := (                                 -- 1,6 

        ("1000000", "0111110", "0111011", "0110101",
         "0101101", "0100011", "0011000", "0001100"),
        
        ("1000000", "0110101", "0011000", "1110100",
         "1010011", "1000010", "1000101", "1011101"),
        
        ("1000000", "0100011", "1101000", "1000010",
         "1010011", "0001100", "0111011", "0110101"),
        
        ("1000000", "0001100", "1000101", "1011101",
         "0101101", "0110101", "1101000", "1000010"),
        
        ("1000000", "1110100", "1000101", "0100011",
         "0101101", "1001011", "1101000", "0111110"),
        
        ("1000000", "1011101", "1101000", "0111110",
         "1010011", "1110100", "0111011", "1001011"),
        
        ("1000000", "1001011", "0011000", "0001100",
         "1010011", "0111110", "1000101", "0100011"),
        
        ("1000000", "1000010", "0111011", "1001011",
         "0101101", "1011101", "0011000", "1110100")
    );

    signal row      : INTEGER range 0 to 7;
    signal col      : INTEGER range 0 to 7;
    signal x        : INTEGER range 0 to 8;
    signal s        : S_TYPE;
    signal msb      : std_logic;
    signal go       : std_logic;
    signal done     : std_logic;
    signal code_out : SIGNED (15 downto 0);
    signal busy     : STD_LOGIC;
    signal block_i  : BLOCK_TYPE;
    signal block_o  : BLOCK_TYPE;
    signal state1   : STATE_TYPE1;
    signal state2   : STATE_TYPE2;
begin

    process(rst, clk)
      variable v8  : SIGNED (7 downto 0);
      variable v10 : SIGNED (9 downto 0);
      variable v16 : SIGNED (15 downto 0);
    begin
    
        if( rst = '1' ) then
      
            row <= 0;
            col <= 0;
            msb <= '0';
            go <= '0';
            busy <= '0';
    
            for i in 0 to 7 loop

                for j in 0 to 7 loop

                    block_i(i)(j) <= CD_16;
                    block_o(i)(j) <= CD_16;
                end loop;
            end loop;
          
            state1 <= ST1_IDLE;
            out_data <= CZ_8;
                        
        elsif( clk'event and clk = '1' ) then

            out_data <= CZ_8;

            -- bus i/o
            if( wr = '1' and conv_integer(addr) = CMND_REG_ADDR ) then

                busy <= '1';
            end if;
            if( wr = '1' and conv_integer(addr) = DATAI_REG_ADDR ) then

                v10 := (C0_2 & SIGNED(in_data));
                v16 := (v10 & C0_6);
                block_i(row)(col) <= v16;
                
                if( col = 7 ) then
                    col <= 0;
                    if( row /= 7 ) then
                          
                        row <= row + 1;
                    end if;
                else
                    col <= col + 1;
                end if;
            end if;
            if( rd = '1' and conv_integer(addr) = STAT_REG_ADDR ) then

                out_data <= UNSIGNED(C0_7 & busy);
            end if;
            if( rd = '1' and conv_integer(addr) = DATAO_REG_ADDR ) then

                if ( msb = '0' ) then

                    v16 := block_o(row)(col);
                    v8 := v16(15 downto 8);                    
                    out_data <= UNSIGNED(v8);
                    msb <= '1';
                else
                   
                    v16 := block_o(row)(col);
                    v8 := v16(7 downto 0);                    
                    out_data <= UNSIGNED(v8);
                    
                    if( col = 7 ) then
                        col <= 0;
                        if( row /= 7 ) then
                          
                          row <= row + 1;
                        end if;
                    else
                        col <= col + 1;
                    end if;
                    msb <= '0';
                end if;
            end if;

            -- compute
            case state1 is
                when ST1_IDLE =>
                    if( busy = '1' ) then

                        row <= 0;
                        col <= 0;
                        go <= '1';
                        state1 <= ST1_WAIT;
                    end if;

                when ST1_WAIT =>
                    go <= '0';
                    if( done = '1' ) then

                        block_o(row)(col) <= code_out;
                        col <= col + 1;
                        state1 <= ST1_INCREMENT;
                    end if;

                when ST1_INCREMENT =>
                    state1 <= ST1_WAIT;
                    go <= '1';
                    if ( col = 7 ) then
                        col <= 0;
                        if( row = 7 ) then

                            row <= 0;
                            busy <= '0';
                            go <= '0';
                            state1 <= ST1_IDLE;
                        else

                            row <= row + 1;
                        end if;
                    end if;                    
            end case;
        end if;
    end process;

    process(rst, clk)
        variable v24_1, v24_2 : SIGNED ( 23 downto 0 );
        variable v48 : SIGNED (47 downto 0);
    begin
    
        if( rst = '1' ) then

            x <= 0;

            for i in 0 to 7 loop

                s(i) <= CD_24;
            end loop;
            
            done <= '0';
            code_out <= CD_16;
            state2 <= ST2_IDLE;
            
        elsif( clk'event and clk = '1' ) then

            case state2 is
                when ST2_IDLE =>
                    if( go = '1' ) then

                        done <= '0';
                        x <= 0;
                        state2 <= ST2_COMPUTE;
                    end if;

                when ST2_COMPUTE =>
                    if( x = 8 ) then

                        x <= 0;
                        state2 <= ST2_FINISH;
                    else

                        v24_1 := C0_24;
                        for i in 0 to 7 loop

                            -- COS_TABLE(i)(col) * block_i(x)(i)
                            v24_2 := C0_17 & COS_TABLE(i)(col);
                            v48 := v24_2 * ( C0_8 & block_i(x)(i));
                            v24_2 := C0_6 & v48(23 downto 6);

                            -- v24_1 += v24_2;
                            v24_1 := v24_1 + v24_2;
                        end loop;
                        s(x) <= v24_1;
                        x <= x + 1;
                    end if;

                when ST2_FINISH =>

                    -- v24_1 = s(0) + s(1) . . . + s(7)
                    v24_1 := C0_24;
                    for i in 0 to 7 loop
                        v24_1 := v24_1 + s(i);
                    end loop;

                    v24_2 := C0_13 & SIXTEEN;
                    
                    if ( row = 0 ) then

                      -- v24_2 *= ONE_OVER_SQRT_TWO
                      v48 := v24_2 * (C0_18 & ONE_OVER_SQRT_TWO);
                      v24_2 := C0_6 & v48(23 downto 6);
                    end if;

                    if ( col = 0 ) then 

                      -- v24_2 *= ONE_OVER_SQRT_TWO
                      v48 := v24_2 * (C0_18 & ONE_OVER_SQRT_TWO);
                      v24_2 := C0_6 & v48(23 downto 6);
                    end if;

                    -- v24_1 *= v24_2
                    v48 := v24_1 * v24_2;
                    v24_1 := C0_6 & v48(23 downto 6);

                    -- convert from fixed to decimal
                    code_out <= v24_1(22 downto 7);

                    -- done
                    done <= '1';
                    state2 <= ST2_IDLE;
            end case;
        end if;
    end process;
end BHV;








