-------------------------------------------------------------------------------
--
-- LKR hardware decoder unit for the Leo-Kat-Russ image encoding format
--
-- Leo Gertsenshteyn
-- lpg2006@columbia.edu
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;


entity LKR_decoder is
  generic (
    AWIDTH : natural := 20);
  port (
    OPB_Clk        : in std_logic;
    Pixel_Clock    : in std_logic;
    xferAck    : in std_logic; 
    SRAM_data   : in std_logic_vector(15 downto 0);

    Go, Reset   : in std_logic;
    Base_addr   : in std_logic_vector(AWIDTH-1 downto 0);
    SRAM_addr   : out std_logic_vector(AWIDTH-1 downto 0);
    SRAM_CE     : out std_logic;
    
    -- Video signals
    VIDOUT_RED     : out std_logic_vector(9 downto 0);
    VIDOUT_GREEN   : out std_logic_vector(9 downto 0);
    VIDOUT_BLUE    : out std_logic_vector(9 downto 0));

end LKR_decoder;



architecture Behavioral of LKR_decoder is

  type color is array (0 to 2) of std_logic_vector(7 downto 0);
  type state is (init, start, reqRed, stoRed, reqGrn, stoGrn,
                 reqBlu, stoBlu, idling, decode_pix, load_pix);
  type color_table is array (0 to 15) of color;

  signal to_SRAM_addr: std_logic_vector(AWIDTH-1 downto 0);
  signal color_tbl : color_table;
  signal counting : integer := 0;
  signal index: integer := 0;
  signal cstate, nstate : state := idling;

  
  signal countdown: std_logic_vector(3 downto 0);
  signal SRAM_current: std_logic_vector(7 downto 0);
  signal cdwn_clr,cdwn_enable, cdwn_ld, cdwn_up: std_logic;
  signal d_addr_clr, d_addr_enable, d_addr_ld: std_logic;
  
  component  UD_COUNTER1
    generic (WIDTH: natural := 4);
    port ( CLK,CLEAR,LOAD,CE,UP : in std_logic;    -- INPUT and OUTPUT declaration
           DIN : in std_logic_vector(WIDTH-1 downto 0);
           Q : out std_logic_vector(WIDTH-1 downto 0));
  end component;


begin  -- Behavioral

d_addr : UD_COUNTER1 generic map(WIDTH => AWIDTH)
  port map (
    CLK=>OPB_Clk,
    CLEAR=>d_addr_clr,
    LOAD=>d_addr_ld,
    CE=>d_addr_enable,
    UP=>'1',
    DIN=> Base_addr,
    Q=> to_SRAM_addr
  );

down_ctr : UD_COUNTER1
  port map (
    CLK=>OPB_Clk,
    CLEAR=>cdwn_clr,
    LOAD=>cdwn_ld,
    CE=>cdwn_enable,
    UP=>cdwn_up,
    DIN=>(others=>'0'),
    Q=> countdown
  );

SRAM_addr <= to_S;


SYNC_PROC: process (OPB_Clk, Reset)
begin
  if (RESET='1') then
    CSTATE <= init;
  elsif (OPB_Clk'event and OPB_Clk = '1') then
    CSTATE <= NSTATE;
  end if;
end process;

COMB_PROC: process (CSTATE, Go, xferAck)
begin
  NSTATE<= init;
  cdwn_clr <= '0';
  d_addr_clr <= '0';
  cdwn_up <= '0'; -- this is not in any way helpful except when invalid state
  d_addr_ld <= '0';
  SRAM_CE <= '0';
  index <= CONV_INTEGER(to_SRAM_addr(7 downto 0));
  
  case CSTATE is

    --init state
    when init =>
      if Go='1' then
        NSTATE <= start;
      end if;

    --starting up state
    when start =>
      d_addr_ld<='1';
      d_addr_enable <='0';
      NSTATE <= reqRed;
      
    when reqRed =>
      SRAM_CE <= '1';
      if xferAck = '1' then
        NSTATE <= stoRed;
      end if;
    when stoRed =>
      color_tbl(index)(0) <= SRAM_data(15 downto 8) when to_SRAM_addr(0) = '0' else
                             SRAM_data(7 downto 0);
      d_addr_enable <= '1';
      NSTATE <= reqGrn;

    when reqGrn =>
      SRAM_CE <= '1';
      if xferAck = '1' then
        NSTATE <= stoGrn;
      end if;
    when stoGrn =>
      color_tbl(index)(1) <= SRAM_data(15 downto 8) when to_SRAM_addr(0) = '0' else
                             SRAM_data(7 downto 0);
      d_addr_enable <= '1';
      NSTATE <= reqBlu;

    when reqBlu =>
      SRAM_CE <= '1';
      if xferAck = '1' then
        NSTATE <= stoBlu;
      end if;
    when stoBlu =>
      color_tbl(index)(2) <= SRAM_data(15 downto 8) when to_SRAM_addr(0) = '0' else
                             SRAM_data(7 downto 0);
      d_addr_enable <= '1';
      if (to_SRAM_addr(7 downto 0) = "110000") then
        NSTATE <= idling;
      else
        NSTATE <= reqRed;
      end if;  

    when idling =>
      NSTATE <= load_pix;

    when load_pix =>
      SRAM_CE <= '1';
      NSTATE <= idling when Go = '0' else
            decode_pix when xferAck = '1' else
            load_pix; 

    when decode_pix =>
      SRAM_current <= SRAM_data(15 downto 8) when to_SRAM_addr(0) = '0' else
                      SRAM_data(7 downto 0);
      VIDOUT_RED <= "00" & color_tbl(CONV_INTEGER(SRAM_data(3 downto 0)))(0);
      VIDOUT_GREEN <= "00" & color_tbl(CONV_INTEGER(SRAM_data(3 downto 0)))(1);
      VIDOUT_BLUE <= "00" & color_tbl(CONV_INTEGER(SRAM_data(3 downto 0)))(2);
      NSTATE <= idling when Go = '0' else
            load_pix;
                    
  end case;
end process;
                

end Behavioral;

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

--******************************************************************************
--*                                                                            *
--*      Synchronous Binary 4bits Up-Down Counter without Carry/Borrow         *
--*                                                     Device : XC9536-PC44   *
--*                                                     Author : Seiichi Inoue *
--******************************************************************************

library ieee;                                    -- Library declaration
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity UD_COUNTER1 is
  generic( WIDTH : natural := 4);
  port ( CLK,CLEAR,LOAD,CE,UP : in std_logic;    -- INPUT and OUTPUT declaration
         DIN : in std_logic_vector(WIDTH-1 downto 0);
         Q : out std_logic_vector(WIDTH-1 downto 0));
end UD_COUNTER1;

architecture UD_COUNTER_ARCH of UD_COUNTER1 is
signal Q_IN : std_logic_vector(WIDTH-1 downto 0);      -- Internal counter signal
begin
  Q <= Q_IN;                                     -- Set output
  process( CLEAR, CLK, LOAD, CE, UP ) begin
    if CLEAR='1' then                            -- CLEAR = ON ?
       Q_IN <= (others => '0');                           -- Yes. Counter clear
    elsif CLK='1' and CLK'event then             -- Clock in ?
       if LOAD='1' then                          -- Yes. LOAD = ON ?
          Q_IN <= DIN;                           -- Set Input to Output
       else                                      -- LOAD = OFF
          if CE='1' then                         -- Count Enable ?
             if UP='1' then                      -- Yes. Up count ?
                Q_IN <= Q_IN + '1';              -- Yes. Count-up
             else                                -- Not count-up
                Q_IN <= Q_IN - '1';              -- Count-down
             end if;
          end if;                                -- Not CE = 1
       end if;
    end if;
  end process;
end UD_COUNTER_ARC;
