------------------------------------------------------------------------------
--  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:      huff
-- File:        huff.vhd
-- Author:      Kenichi Kurimoto 
-- Description: AMBA slave interface and huffman decoder for jpeg decode
------------------------------------------------------------------------------

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

library grlib;
use grlib.amba.all;
use grlib.stdlib.all;
use grlib.devices.all;

library techmap;
use techmap.gencomp.all;

library kuri;
use kuri.mjpeg.all;

entity huff  is
   generic (
      memtech : integer := DEFMEMTECH;
      shindex : integer := 0;
      haddr  : integer := 0;
      hmask  : integer := 16#fff#;
      hirq   : integer := 0;      
      pindex : integer := 0;
      paddr  : integer := 0;
      pmask  : integer := 16#fff#;
      mhindex : integer := 0;
      chprot : integer := 3);      
   port (
      rst   : in std_ulogic;
      clk   : in std_ulogic;
      ahbsi : in ahb_slv_in_type;
      ahbso : out ahb_slv_out_type;
      apbi  : in apb_slv_in_type;
      apbo  : out apb_slv_out_type;
      kready : in std_logic;
      kstrobe : out std_logic;
      kdata   : out std_logic_vector(11 downto 0);
      kaddress : out std_logic_vector(5 downto 0);
      jpg_setting : out jpg_set_type;
      error : in std_logic_vector(2 downto 0);
      startgen : out std_logic;
      kstrobeq : out std_logic;
      kdataq : out std_logic_vector(7 downto 0);
      kaddq : out std_logic_vector(7 downto 0);
      krddataq : in std_logic_vector(7 downto 0);
      krdq : out std_logic      
   );
end;

architecture rtl of huff is

constant shconfig : ahb_config_type := (
 0 => ahb_device_reg( VENDOR_CONTRIB, CONTRIB_CORE1, 0, 0, hirq),
 4 => ahb_membar(haddr, '0', '0', hmask),
 others => zero32);
  
constant pconfig : apb_config_type := (
 0 => ahb_device_reg( VENDOR_CONTRIB, CONTRIB_CORE1, 0, 0, 0),
 1 => apb_iobar(paddr, pmask));
 
constant fdepth : integer := 512;

constant const_4b4 : std_logic_vector(3 downto 0) := "0100";
constant const_4b5 : std_logic_vector(3 downto 0) := "0101";
constant const_4b6 : std_logic_vector(3 downto 0) := "0110";
constant const_6b8 : std_logic_vector(5 downto 0) := "001000";
constant const_u6b24 : UNSIGNED(5 downto 0) := "011000";

function sign_ex(data, bitnum : std_logic_vector) return std_logic_vector is
   variable outb : std_logic_vector(11 downto 0);
   variable minusjudge : std_logic;
   variable tmp : integer;
begin
   minusjudge := '0';
   if(bitnum = "0001")then
       if(data(0) = '0')then
           outb := "111111111111";
       else
           outb := "000000000001";
       end if;
   elsif(bitnum = "0010")then
       if(data(1) = '0')then
           tmp := to_integer(signed('1' & data(1 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(1 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "0011")then
       if(data(2) = '0')then
           tmp := to_integer(signed('1' & data(2 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(2 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;       
   elsif(bitnum = "0100")then
       if(data(3) = '0')then
           tmp := to_integer(signed('1' & data(3 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(3 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;       
   elsif(bitnum = "0101")then
       if(data(4) = '0')then
           tmp := to_integer(signed('1' & data(4 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(4 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;        
   elsif(bitnum = "0110")then
       if(data(5) = '0')then
           tmp := to_integer(signed('1' & data(5 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(5 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "0111")then
       if(data(6) = '0')then
           tmp := to_integer(signed('1' & data(6 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(6 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "1000")then
       if(data(7) = '0')then
           tmp := to_integer(signed('1' & data(7 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(7 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "1001")then
       if(data(8) = '0')then
           tmp := to_integer(signed('1' & data(8 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(8 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "1010")then
       if(data(9) = '0')then
           tmp := to_integer(signed('1' & data(9 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(9 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   elsif(bitnum = "1011")then
       if(data(10) = '0')then
           tmp := to_integer(signed('1' & data(10 downto 0))) + 1;
           outb := std_logic_vector(to_signed(tmp, 12));
       else
           tmp := to_integer(signed('0' & data(10 downto 0)));
           outb := std_logic_vector(to_signed(tmp, 12));
       end if;
   else
       outb := (others => '0');
   end if;
   
   return(outb);
end;

type fstate_type is (memwait, bytefetch, ffmemwait, ffcheck, markermode);
type dstate_type is (symreq, symcheck, valout, symng, symokvalng, serialwait, serialcheck, serialfinish, standby);

type ahbs_reg is record
   getscan : std_logic;
   rdscan : std_logic;
   getq : std_logic;
   rdq : std_logic;
   getcache : std_logic;
   rdacache : std_logic;
   rddcache : std_logic;
   getmax : std_logic;
   rdmax : std_logic;
   getoffset : std_logic;
   rdoffset : std_logic;
   getval : std_logic;
   rdval : std_logic;
   hreadyff : std_logic;
   hselff : std_logic;
   haddkeep : std_logic_vector(15 downto 0);
end record;

type apbs_reg is record
   sampf : std_logic;
   xmcumax   : std_logic_vector(5 downto 0);
   ymcumax   : std_logic_vector(4 downto 0);
   incaddy   : std_logic_vector(15 downto 0);
   incaddmcux : std_logic_vector(15 downto 0);
   incaddmcuy : std_logic_vector(10 downto 0);
   fbstartadd : std_logic_vector(31 downto 0);
   through_bit : std_logic;
   hardonly : std_logic;
end record;

type control_reg is record
   fetch_state : fstate_type;
   dec_state : dstate_type;
   preg : apbs_reg;
   hreg : ahbs_reg;
   fifo_rp : std_logic_vector(8 downto 0);
   fifo_wp : std_logic_vector(8 downto 0);
   fetch_reg : std_logic_vector(31 downto 0);
   marker_reg : std_logic_vector(7 downto 0);
   valuebit : std_logic_vector(5 downto 0);
   byteselect : std_logic_vector(1 downto 0);
   reqbit_keep : std_logic_vector(3 downto 0);
   valbit_keep : std_logic_vector(3 downto 0);
   dcac : std_logic;
   serial_counter : std_logic_vector(4 downto 0);
   idcounter : std_logic_vector(3 downto 0); 
   memaddcnt : std_logic_vector(5 downto 0);
   lastdc0 : std_logic_vector(11 downto 0);
   lastdc1 : std_logic_vector(11 downto 0);
   lastdc2 : std_logic_vector(11 downto 0);
   byte3keep : std_logic_vector(23 downto 0);
   cntdown : std_logic;
   capture : std_logic_vector(1 downto 0);
   skipcnt : std_logic_vector(15 downto 0);
end record;

signal r, rin : control_reg;
signal read_en_fifo, write_en_fifo : std_logic;
signal read_pointer_fifo : std_logic_vector(8 downto 0);
signal write_pointer_fifo : std_logic_vector(8 downto 0);
signal data_out_fifo : std_logic_vector(31 downto 0);
signal data_in_fifo : std_logic_vector(31 downto 0);

signal dccacheadd : std_logic_vector(9 downto 0);
signal dccachedin : std_logic_vector(7 downto 0);
signal dccachedout : std_logic_vector(7 downto 0);
signal dccacheen,dccachewr : std_logic;
signal accacheadd : std_logic_vector(9 downto 0);
signal accachedin : std_logic_vector(11 downto 0);
signal accachedout : std_logic_vector(11 downto 0);
signal accacheen,accachewr : std_logic;
signal sermaxadd : std_logic_vector(6 downto 0);
signal sermaxdin : std_logic_vector(16 downto 0);
signal sermaxdout : std_logic_vector(16 downto 0);
signal sermaxen,sermaxwr : std_logic;
signal seroffadd : std_logic_vector(6 downto 0);
signal seroffdin : std_logic_vector(16 downto 0);
signal seroffdout : std_logic_vector(16 downto 0);
signal seroffen,seroffwr : std_logic;
signal servaladd : std_logic_vector(9 downto 0);
signal servaldin : std_logic_vector(7 downto 0);
signal servaldout : std_logic_vector(7 downto 0);
signal servalen,servalwr : std_logic;

signal debug_vmemaddcnt : std_logic_vector(5 downto 0);

begin
   ramscan : syncram_2p generic map(tech => memtech, abits => 9, dbits => 32,sepclk => 0)
                port map( clk, read_en_fifo, read_pointer_fifo, data_out_fifo,
                    clk, write_en_fifo, write_pointer_fifo, data_in_fifo);
   huffdccache : syncram generic map(tech => memtech, abits => 10, dbits => 8)
                port map( clk, dccacheadd, dccachedin, dccachedout, dccacheen, dccachewr);
   huffaccache : syncram generic map(tech => memtech, abits => 10, dbits => 12)
                port map( clk, accacheadd, accachedin, accachedout, accacheen, accachewr);
   serialmax   : syncram generic map(tech => memtech, abits => 7, dbits => 17)
                port map( clk, sermaxadd, sermaxdin, sermaxdout, sermaxen, sermaxwr);
   serialoffset : syncram generic map(tech => memtech, abits => 7, dbits => 17)
                port map( clk, seroffadd, seroffdin, seroffdout, seroffen, seroffwr);
   serialval : syncram generic map(tech => memtech, abits => 10, dbits => 8)
                port map( clk, servaladd, servaldin, servaldout, servalen, servalwr);


comb_fetch : process(r, rst, ahbsi, apbi, data_out_fifo, dccachedout, accachedout, sermaxdout, seroffdout, servaldout, kready, error, krddataq)
   variable v : control_reg;
   variable virq : std_logic_vector(NAHBIRQ-1 downto 0);
   variable vsready : std_logic;
   variable write_point : integer;
   variable read_point : integer;
   variable num_ele : integer;
   variable apbwrite : std_logic;
   variable vprdata : std_logic_vector(31 downto 0);
   variable vhrdata : std_logic_vector(31 downto 0);
   variable vwriting :std_logic;
   variable vreading : std_logic;
   variable vdccacheadd : std_logic_vector(9 downto 0);
   variable vdccachewr : std_logic;
   variable vaccacheadd : std_logic_vector(9 downto 0);
   variable vaccachewr : std_logic;
   variable vsermaxadd : std_logic_vector(6 downto 0);
   variable vsermaxwr : std_logic;
   variable vseroffadd : std_logic_vector(6 downto 0);
   variable vseroffwr : std_logic;
   variable vservaladd : std_logic_vector(9 downto 0);
   variable vservalwr : std_logic;   
   
   variable vbytedata : std_logic_vector(7 downto 0);
   variable vinsertdata : std_logic_vector(7 downto 0);
   variable vbyte0, vbyte1, vbyte2, vbyte3 : std_logic_vector(7 downto 0);
   variable vfetching : std_logic;
   
   variable vcache_symbit : std_logic_vector(4 downto 0);
   variable vcache_runlength : std_logic_vector(3 downto 0);
   variable vcache_valbit : std_logic_vector(3 downto 0);
   
   variable vint_plusv : integer;
   variable vint_minusv : integer;   
   
   variable vserial_symbit : std_logic_vector(4 downto 0);
   variable vserial_runlength : std_logic_vector(3 downto 0);
   variable vserial_valbit : std_logic_vector(3 downto 0);
   variable vserial_tmpin : std_logic_vector(16 downto 0);
   variable vserial_mask : std_logic_vector(16 downto 0);
   variable vserial_judge : std_logic;
   variable vserial_tmpadd : std_logic_vector(16 downto 0);
   
   variable vintshift : integer;
   variable vshiftnum : std_logic_vector(4 downto 0);
   variable vint_valuebit : integer;
   variable vint_valbkp : integer;
   variable vint_sercnt : integer;
   variable vshiftout : std_logic_vector(15 downto 0);
   variable vtmpshiftout : std_logic_vector(31 downto 0);
   variable vid : std_logic;
   variable vcompid : std_logic_vector(1 downto 0);
   variable vkstrobe : std_logic;
   variable vkdata    : std_logic_vector(11 downto 0);
   variable vint_csymbit : integer;
   variable vint_reqbitkp : integer;
   variable vint_cvalbit : integer;
   variable vint_sersym : integer;
   variable vint_serval : integer;
   
   variable vkaddq : std_logic_vector(7 downto 0);
   variable vkrdq : std_logic;
   variable vgetbyte : std_logic;
   variable vstartgen : std_logic;
   begin

   v := r;
   virq := (others => '0');
   vdccachewr := '0'; vdccacheadd := (others => '0');
   vaccachewr := '0'; vaccacheadd := (others => '0');
   vsermaxwr := '0';  vsermaxadd := (others => '0');
   vseroffwr := '0';  vseroffadd := (others => '0');   
   vservalwr := '0';  vservaladd := (others => '0');  
   vkaddq := (others => '0'); vkrdq := '0';
   vserial_judge := '0';
   vkstrobe := '0'; vstartgen := '0';
      
-- apb controle part
   apbwrite := apbi.psel(pindex) and apbi.pwrite and apbi.penable;
   vprdata := (others => '0');
   case apbi.paddr(5 downto 2) is
   when "0000" =>
      if apbwrite = '1' then
         v.preg.fbstartadd := apbi.pwdata(31 downto 0);
      end if;
      vprdata := r.preg.fbstartadd(31 downto 0);
   when "0001" =>
      if apbwrite = '1' then
         v.preg.sampf := apbi.pwdata(22);
         v.preg.ymcumax := apbi.pwdata(21 downto 17);
         v.preg.xmcumax := apbi.pwdata(16 downto 11);
         v.preg.incaddmcuy := apbi.pwdata(10 downto 0);
      end if;
      vprdata := "000000000" & r.preg.sampf & r.preg.ymcumax & r.preg.xmcumax & r.preg.incaddmcuy;
   when "0010" =>
      if apbwrite = '1' then
          v.preg.incaddy := apbi.pwdata(31 downto 16);
          v.preg.incaddmcux := apbi.pwdata(15 downto 0);
      end if;
      vprdata := r.preg.incaddy & r.preg.incaddmcux;
   when "0011" =>
      if apbwrite = '1' then
          if apbi.pwdata(31) = '1' then
              vstartgen := '1'; 
          end if;
          v.preg.through_bit := apbi.pwdata(15);
          v.preg.hardonly := apbi.pwdata(14);
          v.marker_reg := apbi.pwdata(23 downto 16);
      end if;
      vprdata := "00000000" & r.marker_reg & r.preg.through_bit & r.preg.hardonly &"00000000000000" ;
   when others =>
   end case;  

if(r.hreg.getcache = '1' or  r.hreg.hreadyff = '0')then
   if (r.hreg.haddkeep(15) = '1') then
      vdccachewr := '1';
      vdccacheadd := r.hreg.haddkeep(11 downto 2);
   else
      vaccachewr := '1';
      vaccacheadd := r.hreg.haddkeep(11 downto 2);
   end if;
else
   vdccacheadd := ahbsi.haddr(11 downto 2);
   vaccacheadd := ahbsi.haddr(11 downto 2);   
end if;    
if(r.hreg.getq = '1' or r.hreg.hreadyff = '0')then
   vkaddq := r.hreg.haddkeep(9 downto 2);
else
   vkaddq := ahbsi.haddr(9 downto 2);
   vkrdq := '1';
end if;
if(r.hreg.getmax = '1' or r.hreg.hreadyff = '0')then
   vsermaxwr := '1';
   vsermaxadd := r.hreg.haddkeep(8 downto 2);
else
   vsermaxadd := ahbsi.haddr(8 downto 2);
end if;
if(r.hreg.getoffset = '1' or r.hreg.hreadyff = '0')then
   vseroffwr := '1';
   vseroffadd := r.hreg.haddkeep(8 downto 2);
else
   vseroffadd := ahbsi.haddr(8 downto 2);
end if;
if(r.hreg.getval = '1' or r.hreg.hreadyff = '0')then
   vservalwr := '1';
   vservaladd := r.hreg.haddkeep(11 downto 2);
else
   vservaladd := ahbsi.haddr(11 downto 2);
end if;
 
if(ahbsi.hready = '1' ) then
    v.hreg.getscan := '0';
    v.hreg.rdscan := '0';
    v.hreg.getq := '0';
    v.hreg.rdq := '0';
    v.hreg.getcache := '0';
    v.hreg.rdacache := '0';
    v.hreg.rddcache := '0';
    v.hreg.getmax := '0';
    v.hreg.rdmax := '0';
    v.hreg.getoffset := '0';
    v.hreg.rdoffset := '0';
    v.hreg.getval := '0';  
    v.hreg.rdval := '0'; 
    
    v.hreg.hselff := ahbsi.hsel(shindex) and ahbsi.htrans(1);
    vwriting := ahbsi.hwrite and v.hreg.hselff;
    vreading := (not ahbsi.hwrite) and v.hreg.hselff; 
    if(ahbsi.haddr(19 downto 8) = "000000000000")then
        if(vwriting = '1')then
           v.hreg.getscan := '1';
        elsif(vreading = '1')then
           v.hreg.rdscan := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 12) = "1100" )then
        if(vwriting = '1')then
           v.hreg.getq := '1';
        elsif(vreading = '1')then
           v.hreg.rdq := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 12) = "0100" or ahbsi.haddr(15 downto 12) = "0101")then
        if(vwriting = '1')then
           v.hreg.getcache := '1';
        elsif(vreading = '1')then
           v.hreg.rdacache := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 12) = "1000" or ahbsi.haddr(15 downto 12) = "1001")then
        if(vwriting = '1')then
           v.hreg.getcache := '1';
        elsif(vreading = '1')then
           v.hreg.rddcache := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 10) = "000001")then
        if(vwriting = '1')then
           v.hreg.getmax := '1';
        elsif(vreading = '1')then
           v.hreg.rdmax := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 10) = "000010")then
        if(vwriting = '1')then
           v.hreg.getoffset := '1';
        elsif(vreading = '1')then
           v.hreg.rdoffset := '1';
        end if;
    end if;
    if(ahbsi.haddr(15 downto 13) = "001")then
        if(vwriting = '1')then
           v.hreg.getval := '1';
        elsif(vreading = '1')then
           v.hreg.rdval := '1';
        end if;
    end if;
    v.hreg.haddkeep := ahbsi.haddr(15 downto 0);
end if;

if( v.hreg.getscan = '1' or v.hreg.getq = '1' or v.hreg.getcache = '1' 
     or v.hreg.getmax = '1' or v.hreg.getoffset = '1' or v.hreg.getval = '1')then
     v.hreg.hreadyff := not(v.hreg.hselff and not ahbsi.hwrite);
     v.hreg.getscan := v.hreg.getscan and v.hreg.hreadyff;
     v.hreg.getq := v.hreg.getq and v.hreg.hreadyff;
     v.hreg.getcache := v.hreg.getcache and v.hreg.hreadyff;
     v.hreg.getmax := v.hreg.getmax and v.hreg.hreadyff;
     v.hreg.getoffset := v.hreg.getoffset and v.hreg.hreadyff;
     v.hreg.getval := v.hreg.getval and v.hreg.hreadyff;
end if;

-- FIFO # of element calculation
    write_point := to_integer(unsigned(r.fifo_wp));
    read_point := to_integer(unsigned(r.fifo_rp));
    if (write_point >= read_point) then
        num_ele := write_point - read_point;
    else
        num_ele := fdepth - read_point + write_point;
    end if;
    if num_ele > fdepth/2 then
        vsready := '0';
    else 
        vsready := '1';
    end if;   
   
    vhrdata := vsready & "0000000000000000000000000000000";
    if(r.hreg.rdscan = '1')then
        vhrdata := data_out_fifo;
    elsif(r.hreg.rdq ='1')then
        vhrdata := "000000000000000000000000" &krddataq;
    elsif(r.hreg.rdacache = '1')then
        vhrdata := "00000000000000000000" & accachedout;
    elsif(r.hreg.rddcache = '1')then
        vhrdata := "000000000000000000000000" & dccachedout;
    elsif(r.hreg.rdmax = '1')then
        vhrdata := "000000000000000" & sermaxdout;
    elsif(r.hreg.rdoffset = '1')then
        vhrdata := "000000000000000" & seroffdout;
    elsif(r.hreg.rdval = '1')then
        vhrdata := "000000000000000000000000" & servaldout;
    end if;

--FIFO writing
    if r.hreg.getscan = '1' then
        write_point := write_point + 1;
        if write_point = fdepth then
            write_point := 0;
        end if;
    end if;
    v.fifo_wp :=  std_logic_vector(to_unsigned(write_point,9)); 
   
--FIFO reading
    if((r.fetch_state = bytefetch and r.byteselect = "00" and num_ele >= 1 and unsigned(r.valuebit)<= const_u6b24)
         or (r.fetch_state = ffcheck and r.byteselect = "00" and num_ele >= 1 and unsigned(r.valuebit)<= const_u6b24)) then
       read_point := read_point + 1;
       if read_point = fdepth then
           read_point := 0;
       end if;
       v.byte3keep(23 downto 0) := data_out_fifo(23 downto 0); 
    end if;
    v.fifo_rp :=  std_logic_vector(to_unsigned(read_point,9));

-- byte select from FIFO output
    if(r.byteselect = "00") then
        vbytedata := data_out_fifo(31 downto 24);
    elsif(r.byteselect = "01") then
        vbytedata := r.byte3keep(23 downto 16);
    elsif(r.byteselect = "10") then
        vbytedata := r.byte3keep(15 downto 8);
    else
        vbytedata := r.byte3keep(7 downto 0);
    end if;
   
    vgetbyte := '0';
    if((r.fetch_state = bytefetch and unsigned(r.valuebit) <= const_u6b24 )
      or (r.fetch_state = ffcheck and unsigned(r.valuebit) <= const_u6b24))then
        v.byteselect := v.byteselect + 1;
        vgetbyte := '1';
    end if;

--data FF 
    if(r.fetch_state = ffcheck) then
        vinsertdata := "11111111";
    else
        vinsertdata := vbytedata;
    end if;

-- byte fetching to 32bit fetch_register
    if(   (r.fetch_state = bytefetch and vbytedata  /= "11111111" and unsigned(r.valuebit) <= const_u6b24 and r.preg.through_bit = '0' ) 
       or (r.fetch_state = ffcheck and vbytedata = "00000000" and unsigned(r.valuebit) <= const_u6b24 and r.preg.through_bit = '0')) then
        vfetching := '1';
    else
        vfetching := '0';
    end if;

    if(vfetching = '1') then
        vbyte0 := vinsertdata;
        vbyte1 := r.fetch_reg(7 downto 0);
        vbyte2 := r.fetch_reg(15 downto 8);
        vbyte3 := r.fetch_reg(23 downto 16);
    else
        vbyte0 := r.fetch_reg(7 downto 0);
        vbyte1 := r.fetch_reg(15 downto 8);
        vbyte2 := r.fetch_reg(23 downto 16);
        vbyte3 := r.fetch_reg(31 downto 24);
    end if;
    v.fetch_reg := vbyte3 & vbyte2 & vbyte1 & vbyte0;

-- Marker register
    if(r.fetch_state = ffcheck and vbytedata /= "00000000" and r.preg.through_bit = '0') then
        v.marker_reg := vbytedata;
    end if;
    if(r.marker_reg /= "00000000")then
        virq(hirq) := '1';
    end if;

-- Through bit & skip counter calculation
-- This part is for "motion"-JPEG".
-- It's not beautiful implementation, but.....
    if(r.fetch_state = ffcheck and r.preg.through_bit = '1' and  vbytedata = "11011010")then
        v.cntdown := '1';
        v.capture := "10";
    end if;  
    if(r.capture = "10")then
        v.skipcnt(15 downto 8) := vbytedata;
    end if;
    if(r.capture = "01")then
        v.skipcnt(7 downto 0) := vbytedata;
    end if;
    if(r.cntdown = '1' and vgetbyte = '1')then
        if(r.capture = "10")then
            v.capture := "01";
        end if; 
        if(r.capture = "01")then
            v.capture := "00";
        end if;
        if(r.capture = "00")then
            v.skipcnt := r.skipcnt - 1;
        end if;
        if(r.skipcnt = "0000000000000011")then
            v.preg.through_bit := '0';
            v.cntdown := '0';
            v.skipcnt := (others => '0');
        end if;
    end if;
    
-- State machine transition (fetch part)  
    case r.fetch_state is
    when  memwait =>
        if (num_ele /= 0 and unsigned(r.valuebit) <= const_u6b24) then
            v.fetch_state := bytefetch;
        end if;
    when bytefetch =>
        if(r.byteselect = "11" and unsigned(r.valuebit) <= const_u6b24 and num_ele = 0 and vbytedata /= "11111111") then
            v.fetch_state := memwait;
        elsif( vbytedata = "11111111" and r.byteselect = "11" and num_ele = 0 and unsigned(r.valuebit) <= const_u6b24) then
            v.fetch_state := ffmemwait;
        elsif( vbytedata = "11111111" and unsigned(r.valuebit) <= const_u6b24 and (r.byteselect /= "11" or (r.byteselect = "11" and num_ele /= 0))) then
            v.fetch_state := ffcheck;
        end if;    
    when ffmemwait =>
        if(num_ele /= 0) then 
            v.fetch_state := ffcheck;
        end if;
    when ffcheck =>
        if( (vbytedata = "00000000" and unsigned(r.valuebit) <=const_u6b24 and (r.byteselect /= "11" or num_ele /= 0))
             or (r.preg.through_bit = '1' and unsigned(r.valuebit)<=const_u6b24 and (r.byteselect /= "11" or num_ele /= 0) )) then
            v.fetch_state := bytefetch;
        elsif( (vbytedata = "00000000" and unsigned(r.valuebit)<=const_u6b24 and (r.byteselect = "11" and num_ele = 0))
             or( r.preg.through_bit = '1' and unsigned(r.valuebit)<=const_u6b24 and r.byteselect = "11" and num_ele = 0      )) then
            v.fetch_state := memwait;
        elsif ( vbytedata /= "00000000") then
            v.fetch_state := markermode;
        end if;
    when markermode =>
        if(r.marker_reg = "00000000" and (r.byteselect /= "11" or( r.byteselect = "11" and num_ele /= 0))) then
            v.fetch_state := bytefetch;
        elsif(r.marker_reg = "00000000" and (r.byteselect = "11" and num_ele =0)) then
            v.fetch_state := memwait;
        end if;
        if(r.preg.hardonly = '1' and r.marker_reg = x"D9")then
            if(r.byteselect /= "11" or( r.byteselect = "11" and num_ele /= 0))then
                v.marker_reg := "00000000";
                v.preg.through_bit := '1';
            elsif(r.byteselect = "11" and num_ele =0)then
                v.marker_reg := "00000000";
                v.preg.through_bit := '1';
            end if;
        end if;
    when others => 
    end case;
   
 -- cache, serial mem output
   if(r.dcac = '1') then
       vcache_symbit := "0" & dccachedout(7 downto 4);
       vcache_valbit := dccachedout(3 downto 0);
       vcache_runlength := "0000";
   else
       vcache_symbit := "0" & accachedout(11 downto 8);
       vcache_runlength := accachedout(7 downto 4);
       vcache_valbit := accachedout(3 downto 0);
   end if;
   vserial_symbit := r.serial_counter - "00010";
   vserial_runlength := servaldout(7 downto 4);
   vserial_valbit := servaldout(3 downto 0);
 
  -- valuebit calculation
   if(vfetching = '1') then
       vint_plusv := 8;
   else
       vint_plusv := 0;
   end if;
   if(r.dec_state = symcheck) then
       if(unsigned(r.reqbit_keep) >= unsigned(vcache_symbit) )then
           vint_minusv := to_integer(unsigned(vcache_symbit));
       else
           vint_minusv := 0;
       end if;
   elsif(r.dec_state = serialfinish) then
       vint_minusv := to_integer(unsigned(vserial_symbit));
   elsif(r.dec_state = valout) then
       vint_minusv := to_integer(unsigned(r.valbit_keep));
   else
       vint_minusv := 0;
   end if;

    v.valuebit := std_logic_vector(to_unsigned((to_integer(unsigned(r.valuebit)) + vint_plusv - vint_minusv), 6));
  
  -- Padding bit for Markers
   if(r.fetch_state =  markermode or r.preg.through_bit = '1') then
       if((r.valuebit = "000001" and r.fetch_reg(0) = '1')
           or (r.valuebit = "000010" and r.fetch_reg(1 downto 0) = "11")
           or (r.valuebit = "000011" and r.fetch_reg(2 downto 0) = "111")
           or (r.valuebit = "000100" and r.fetch_reg(3 downto 0) = "1111")
           or (r.valuebit = "000101" and r.fetch_reg(4 downto 0) = "11111")
           or (r.valuebit = "000110" and r.fetch_reg(5 downto 0) = "111111")
           or (r.valuebit = "000111" and r.fetch_reg(6 downto 0) = "1111111")) then
          v.valuebit := "000000";
      end if;
   end if;   
   if(r.dec_state = symreq)then
       if(r.valuebit >= const_6b8)then
           v.reqbit_keep := "1000";
       else
           v.reqbit_keep := r.valuebit(3 downto 0);
       end if;
   end if;
   
 -- valbit_keep register calculation
   if(r.dec_state = serialfinish)then
       v.valbit_keep := vserial_valbit;
   elsif(r.dec_state = symcheck)then
       v.valbit_keep := vcache_valbit;
   end if;
 
 -- shiftnum calculation
   vint_valuebit := to_integer(unsigned(r.valuebit));
   vint_valbkp := to_integer(unsigned(r.valbit_keep));
   vint_sercnt := to_integer(unsigned(r.serial_counter));
   vintshift := 0;

   if(r.dec_state = symreq)then
       if(vint_valuebit >= 8)then
           vintshift := vint_valuebit - 8;
       else
           vintshift := 0;
       end if;
   elsif(r.dec_state = valout)then
       vintshift := vint_valuebit - vint_valbkp;
   elsif(r.dec_state = serialcheck)then
       vintshift := 1 + vint_valuebit - vint_sercnt;
   elsif(r.dec_state = serialwait)then
       vintshift := 1 + vint_valuebit - vint_sercnt;
   end if;
   vshiftnum :=  std_logic_vector(to_unsigned(vintshift,5)); 
      
-- shifter instantiation
   vtmpshiftout := std_logic_vector(shift_right(unsigned(r.fetch_reg), vintshift));
   vshiftout := vtmpshiftout(15 downto 0);
   
   if(r.dcac = '1')then
       v.memaddcnt := "000000";
   elsif(((r.dec_state = symcheck and unsigned(vcache_symbit) <= unsigned(r.valuebit) and vcache_symbit /= "00000") and vcache_runlength = "0000" and vcache_valbit = "0000")
         or (r.dec_state = serialfinish and vserial_runlength = "0000" and vserial_valbit = "0000")) then
       v.memaddcnt := "111111";
   elsif(r.dec_state = symcheck and unsigned(vcache_symbit) <= unsigned(r.valuebit) and vcache_symbit /= "00000")then
       v.memaddcnt := r.memaddcnt + vcache_runlength + "0001";
   elsif(r.dec_state = serialfinish)then
       v.memaddcnt := r.memaddcnt + vserial_runlength + "0001";
   end if;
   
-- id, dcac calculation
   if(r.dec_state = valout and r.memaddcnt = "000000")then
       v.dcac := '0';
   elsif(r.dec_state = valout and r.memaddcnt = "111111") then
       v.dcac := '1';
   end if;
   
   if(r.dec_state = valout and r.memaddcnt = "111111") then
       v.idcounter := r.idcounter + '1';
       if(r.preg.sampf = '0')then
           if(v.idcounter = "0110")then
               v.idcounter := "0000";
           end if;
       else
           if(v.idcounter = "1000")then
               v.idcounter := "0000";
           end if;
       end if; 
   end if;
   if(r.preg.sampf = '0')then
       if(r.idcounter < const_4b4 )then
           vid := '0';
           vcompid := "00";
       elsif(r.idcounter < const_4b5)then
           vid := '1';
           vcompid := "01";
       else
           vid := '1';
           vcompid := "10";
       end if;
   else
        if(r.idcounter < const_4b4)then            
            vid := '0';
            vcompid := "00";
        elsif(r.idcounter < const_4b6)then
            vid := '1';
            vcompid := "01";
        else
            vid := '1';
            vcompid := "10";
        end if;
   end if;
   
-- cache access
   if(r.dec_state = symreq)then
       if(r.dcac = '1')then
           if(vint_valuebit >7)then
               vdccacheadd := vid & '0' & vshiftout(7 downto 0);
           elsif(vint_valuebit = 7)then
               vdccacheadd := vid & "10" & vshiftout(6 downto 0);
           elsif(vint_valuebit = 6)then
               vdccacheadd := vid & "110" & vshiftout(5 downto 0);
           elsif(vint_valuebit = 5)then
               vdccacheadd := vid & "1110" & vshiftout(4 downto 0);
           elsif(vint_valuebit = 4)then
               vdccacheadd := vid & "11110" & vshiftout(3 downto 0);
           elsif(vint_valuebit = 3)then
               vdccacheadd := vid & "111110" & vshiftout(2 downto 0);
           elsif(vint_valuebit = 2)then
               vdccacheadd := vid & "1111110" & vshiftout(1 downto 0);
           elsif(vint_valuebit = 1)then
               vdccacheadd := vid & "11111110" & vshiftout(0);
           end if;
           vdccachewr := '0';
       else
            if(vint_valuebit >7)then
                vaccacheadd := vid & '0' & vshiftout(7 downto 0);
            elsif(vint_valuebit = 7)then
                vaccacheadd := vid & "10" & vshiftout(6 downto 0);
            elsif(vint_valuebit = 6)then
                vaccacheadd := vid & "110" & vshiftout(5 downto 0);
            elsif(vint_valuebit = 5)then
                vaccacheadd := vid & "1110" & vshiftout(4 downto 0);
            elsif(vint_valuebit = 4)then
                vaccacheadd := vid & "11110" & vshiftout(3 downto 0);
            elsif(vint_valuebit = 3)then
                vaccacheadd := vid & "111110" & vshiftout(2 downto 0);
            elsif(vint_valuebit = 2)then
                vaccacheadd := vid & "1111110" & vshiftout(1 downto 0);
            elsif(vint_valuebit = 1)then
                vaccacheadd := vid & "11111110" & vshiftout(0);
            end if;      
           vaccachewr := '0';
       end if;
   end if;
  
 -- Serial Part
       vserial_mask := "00000000000000000";
   if(r.serial_counter = "01001")then
       vserial_mask := "00000000011111111";
   elsif(r.serial_counter = "01010")then
       vserial_mask := "00000000111111111";
   elsif(r.serial_counter = "01011")then
       vserial_mask := "00000001111111111";
   elsif(r.serial_counter = "01100")then
       vserial_mask := "00000011111111111";
   elsif(r.serial_counter = "01101")then
       vserial_mask := "00000111111111111";
   elsif(r.serial_counter = "01110")then
       vserial_mask := "00001111111111111";
   elsif(r.serial_counter = "01111")then
       vserial_mask := "00011111111111111";
   elsif(r.serial_counter = "10000")then
       vserial_mask := "00111111111111111";
   elsif(r.serial_counter = "10001")then
       vserial_mask := "01111111111111111";
   end if;
   vserial_tmpin := ('0' & vshiftout) and vserial_mask;

   if(r.dec_state = symcheck or r.dec_state = serialcheck or r.dec_state = serialwait or r.dec_state = serialfinish)then
       vsermaxadd := r.dcac & vid & r.serial_counter;
   end if;
   
   if(r.dec_state = symcheck or r.dec_state = serialcheck or r.dec_state = serialwait or r.dec_state = serialfinish)then
       vseroffadd := r.dcac & vid & r.serial_counter;
   end if;

   if(signed(vserial_tmpin) <= to_01(signed(sermaxdout)))then
       vserial_judge := '1';
   end if;
   vserial_tmpadd := std_logic_vector(signed(vserial_tmpin) + signed(seroffdout));
   if(r.dec_state = serialcheck or r.dec_state = serialwait or r.dec_state = serialfinish)then
       vservaladd := r.dcac & vid & vserial_tmpadd(7 downto 0);
   end if;
   if(r.dec_state = serialwait or r.dec_state = serialcheck or r.dec_state = serialfinish)then
       vservalwr := '0';
   end if;
   
   if(r.dec_state = symreq)then
       v.serial_counter := "01001";
   elsif((r.dec_state = symcheck and vint_valuebit > 8)
       or (r.dec_state = serialcheck and to_integer(unsigned(r.serial_counter))<= vint_valuebit)
       or (r.dec_state = serialwait and to_integer(unsigned(r.serial_counter))<= vint_valuebit ))
       or (r.dec_state = serialcheck and vserial_judge = '1')then 
       v.serial_counter := r.serial_counter + 1;
   end if;
   
 -- Sign extention & zigzag memory access
   vkdata := sign_ex(vshiftout(10 downto 0), r.valbit_keep );
   if(r.dec_state = valout and r.dcac = '1')then
       if(vcompid = "00")then
           vkdata := std_logic_vector(signed(vkdata) + signed(r.lastdc0));
           v.lastdc0 := vkdata;
       elsif(vcompid = "01")then
           vkdata := std_logic_vector(signed(vkdata) + signed(r.lastdc1));
           v.lastdc1 := vkdata;
       else
           vkdata := std_logic_vector(signed(vkdata) + signed(r.lastdc2));
           v.lastdc2 := vkdata;
       end if;
   end if;
   if(r.dec_state = valout)then
       vkstrobe := '1';
   else
       vkstrobe := '0';
   end if;

if(vstartgen = '1' or r.marker_reg = x"D9")then
    v.lastdc0 := (others => '0');
    v.lastdc1 := (others => '0');
    v.lastdc2 := (others => '0');
end if;

-- Decord part state-machine
-- state = symreq, symcheck, valout, symng, symokvalng, serialwait, serialcheck, serialfinish, standby
   vint_csymbit := 0; vint_reqbitkp := 0; vint_cvalbit := 0;
   vint_sersym := 0; vint_serval := 0;

   if notx(vcache_symbit) then 
       vint_csymbit := to_integer(unsigned(vcache_symbit)); 
   end if;
   if notx(r.reqbit_keep) then
       vint_reqbitkp := to_integer(unsigned(r.reqbit_keep));
   end if;
   if notx(vcache_valbit) then
       vint_cvalbit := to_integer(unsigned(vcache_valbit));
   end if;
   if notx(vserial_symbit) then
       vint_sersym := to_integer(unsigned(vserial_symbit));
   end if;
   if notx(vserial_valbit) then
       vint_serval := to_integer(unsigned(vserial_valbit));
   end if;
   
   case r.dec_state is
   when  standby => 
       if(kready = '1' and r.valuebit /= "000000")then
           v.dec_state := symreq;
       end if;
   when symreq =>
       if(r.valuebit = "000000")then
           v.dec_state := symreq;
       else
           v.dec_state := symcheck;
       end if;
   when symcheck =>
       if(vint_csymbit /= 0 and vint_csymbit <= vint_reqbitkp and vint_csymbit + vint_cvalbit <= vint_valuebit )then
           v.dec_state := valout;
       elsif(vint_csymbit /= 0 and vint_csymbit <= vint_reqbitkp and vint_csymbit + vint_cvalbit > vint_valuebit )then        
           v.dec_state := symokvalng;
       elsif(vint_reqbitkp = 8 and vint_csymbit = 0 and vint_valuebit >= 9)then
           v.dec_state := serialcheck;
       elsif(vint_reqbitkp = 8 and vint_csymbit = 0 and vint_valuebit < 9)then
           v.dec_state := serialwait;
       elsif(vint_reqbitkp < 8 and (vint_csymbit = 0 or vint_csymbit > vint_reqbitkp))then
           v.dec_state := symng;
       end if;
   when symng =>
       if(vint_reqbitkp = vint_valuebit)then
           v.dec_state := symng;
       else
           v.dec_state := symreq;
       end if;
   when valout =>
       if(r.memaddcnt = "111111")then
           v.dec_state := standby;
       else
           v.dec_state := symreq; 
       end if;
   when symokvalng =>
       if(vint_valbkp <= vint_valuebit)then
           v.dec_state := valout;
       else
           v.dec_state := symokvalng;
       end if;
   when serialwait =>
       if(vint_sercnt > vint_valuebit) then
           v.dec_state := serialwait;
       else
           v.dec_state := serialcheck;
       end if;
   when serialcheck =>
       if(vserial_judge = '1')then
           v.dec_state := serialfinish;
       elsif(vint_sercnt > vint_valuebit)then
           v.dec_state := serialwait;
       else
           v.dec_state := serialcheck;
       end if;
   when serialfinish =>
       if(vint_valuebit < vint_sersym + vint_serval)then
           v.dec_state := symokvalng;
       else
           v.dec_state := valout;
       end if;
   when others =>
   end case;
   
-- reset part
   if rst = '0' or vstartgen = '1' then
       v.hreg.getscan := '0';
       v.hreg.rdscan := '0';
       v.hreg.getq := '0';
       v.hreg.rdq := '0';
       v.hreg.getcache := '0';
       v.hreg.rdacache := '0';
       v.hreg.rddcache := '0';
       v.hreg.haddkeep := (others => '0');
       v.hreg.getmax := '0';
       v.hreg.rdmax := '0';
       v.hreg.getoffset := '0';
       v.hreg.rdoffset := '0';
       v.hreg.getval := '0';
       v.hreg.rdval := '0';
       v.hreg.hselff := '0';
       v.hreg.hreadyff := '0';
       v.fetch_state := memwait;
       v.dec_state := standby;
       v.fifo_rp := (others => '0');
       v.fifo_wp := (others => '0');
       v.fetch_reg := (others => '0');
       v.marker_reg := (others => '0');
       v.valuebit := (others => '0');
       v.byteselect := (others => '0');
       v.reqbit_keep := (others => '0');
       v.valbit_keep := (others => '0');
       v.dcac := '1';
       v.serial_counter := (others => '0');
       v.idcounter := (others => '0');
       v.memaddcnt := (others => '0');
       v.lastdc0 := (others => '0');
       v.lastdc1 := (others => '0');
       v.lastdc2 := (others => '0');
       v.byte3keep := (others => '0');
       v.cntdown := '0';
       v.capture := "00";
       v.skipcnt := (others => '0');
   end if;
   if rst = '0' then
       v.preg.sampf := '0';
       v.preg.xmcumax := (others => '0');
       v.preg.ymcumax := (others => '0');
       v.preg.incaddy := (others => '0');
       v.preg.incaddmcux := (others => '0');
       v.preg.incaddmcuy := (others => '0');
       v.preg.fbstartadd := (others => '0');       
       v.preg.through_bit := '0';
       v.preg.hardonly := '0';
   end if;
        
   
-- signals
   rin <= v;
   write_en_fifo <= r.hreg.getscan;
   write_pointer_fifo <= r.fifo_wp;
   data_in_fifo <= ahbsi.hwdata;
   read_en_fifo <= '1';
   read_pointer_fifo <= r.fifo_rp;
   
   dccachedin <= ahbsi.hwdata(7 downto 0);
   dccacheadd <= vdccacheadd;
   dccacheen <= '1';
   dccachewr <= vdccachewr;
   accachedin <= ahbsi.hwdata(11 downto 0);
   accacheadd <= vaccacheadd;
   accacheen <= '1';
   accachewr <= vaccachewr;
   sermaxdin <= ahbsi.hwdata(16 downto 0);
   sermaxadd <= vsermaxadd;
   sermaxen <= '1';
   sermaxwr <= vsermaxwr;
   seroffdin <= ahbsi.hwdata(16 downto 0);
   seroffadd <= vseroffadd;
   seroffen <= '1';
   seroffwr <= vseroffwr;
   seroffdin <= ahbsi.hwdata(16 downto 0);
   servaladd <= vservaladd;
   servalen <= '1';
   servalwr <= vservalwr;
   servaldin <= ahbsi.hwdata(7 downto 0);
      
   jpg_setting.xmcumax <= r.preg.xmcumax;
   jpg_setting.ymcumax <= r.preg.ymcumax;
   jpg_setting.incaddy <= r.preg.incaddy;
   jpg_setting.incaddmcux <= r.preg.incaddmcux;
   jpg_setting.incaddmcuy <= r.preg.incaddmcuy;
   jpg_setting.fbstartadd <= r.preg.fbstartadd;
   startgen <= vstartgen;
   jpg_setting.samp_fact <= r.preg.sampf;
   
   kstrobeq <= r.hreg.getq;
   kdataq <= ahbsi.hwdata(7 downto 0);
   
   apbo.prdata <= vprdata;
   ahbso.hirq <= virq;
   ahbso.hrdata <= vhrdata;
   
   kdata <= vkdata;
   kstrobe <= vkstrobe;
   kaddress <= r.memaddcnt;
   kaddq <= vkaddq;
   krdq <= vkrdq;
   


-- Work around for ISE
-- I don't know why ISE can't work correctly without this sentence.
-- Quartus works well without this sentence.   
   debug_vmemaddcnt <= v.memaddcnt;  


   end process;
   
   apbo.pirq <= (others => '0');
   apbo.pindex <= pindex;
   apbo.pconfig <= pconfig;
   ahbso.hconfig <= shconfig;
   ahbso.hresp <= "00";
   ahbso.hsplit <= (others => '0');
   ahbso.hcache <= '0';
   ahbso.hready <= '1';
   ahbso.hindex <= shindex; 
   
 -- registers
   reg : process(clk)
   begin
       if rising_edge(clk) then
           r <= rin;
       end if;
   end process;
     
end;   
