------------------------------------------------------------------------------
--  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:      huffmemcont
-- File:        huffmemcont.vhd
-- Author:      Kenichi Kurimoto 
-- Description: after huffman decode memory and controller
------------------------------------------------------------------------------

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

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

library techmap;
use techmap.gencomp.all;

entity huffmemcont is
   generic (
      memtech : integer := DEFMEMTECH);
   port (
      rst   : in std_ulogic;
      clk   : in std_ulogic;
      kready1  : out std_logic;
      kstrobe1 : in std_logic;
      kaddress1 : in std_logic_vector(5 downto 0);
      kdata1   : in std_logic_vector(11 downto 0);
      kready2  : in std_logic;
      kstrobe2 : out std_logic;
      kdata2   : out std_logic_vector(11 downto 0);
      error    : out std_logic;

      samp_fact : in std_logic;
      kstrobeq1 : in std_logic;
      kdataq1   : in std_logic_vector(7 downto 0);
      kdataq2   : out std_logic_vector(7 downto 0);
      
      kaddq : in std_logic_vector(7 downto 0);
      krdq : in std_logic;
      krddataq : out std_logic_vector(7 downto 0);
      
      startgen : in std_logic
   );
end;
 
architecture rtl of huffmemcont is

type sstate_type is (mem0, mem1);
type mstate_type is (empty, writing, full, reading, standby, fill0);

type control_reg is record
   swf : sstate_type;
   swb : sstate_type;
   mem0state : mstate_type;
   mem1state : mstate_type;
--   countf : std_logic_vector(5 downto 0);
   countb : std_logic_vector(5 downto 0);
   stb2keep : std_logic;
   mem0zeroc : std_logic_vector(5 downto 0);
   mem1zeroc : std_logic_vector(5 downto 0);
   countq : std_logic_vector(8 downto 0);
end record;

subtype coeff6 is std_logic_vector(5 downto 0);
type zigzag_array is array(0 to 63) of coeff6;
constant zigzag_romold : zigzag_array :=
("000000","000001","000101","000110","001110","001111","011011","011100",
 "000010","000100","000111","001101","010000","011010","011101","101010",
 "000011","001000","001100","010001","011001","011110","101001","101011",
 "001001","001011","010010","011000","011111","101000","101100","110101",
 "001010","010011","010111","100000","100111","101101","110100","110110",
 "010100","010110","100001","100110","101110","110011","110111","111100",
 "010101","100010","100101","101111","110010","111000","111011","111101",
 "100011","100100","110000","110001","111001","111010","111110","111111");

constant zigzag_rom : zigzag_array :=
("000000","000010","000011","001001","001010","010100","010101","100011",
 "000001","000100","001000","001011","010011","010110","100010","100100",
 "000101","000111","001100","010010","010111","100001","100101","110000",
 "000110","001101","010001","011000","100000","100110","101111","110001",
 "001110","010000","011001","011111","100111","101110","110010","111001",
 "001111","011010","011110","101000","101101","110011","111000","111010",
 "011011","011101","101001","101100","110100","110111","111011","111110",
 "011100","101010","101011","110101","110110","111100","111101","111111");

signal r, rin : control_reg;
signal m0address, m1address : std_logic_vector(5 downto 0);
signal m0datain, m1datain : std_logic_vector(11 downto 0);
signal m0dataout, m1dataout : std_logic_vector(11 downto 0);
signal m0enable, m1enable : std_logic;
signal m0write, m1write : std_logic;

signal qaddress : std_logic_vector(7 downto 0);
signal qdatain : std_logic_vector(7 downto 0);
signal qdataout : std_logic_vector(7 downto 0);
signal qenable : std_logic := '1';
signal qwrite : std_logic;
signal debug_bcountup : std_logic;

begin
    cram0 : syncram generic map(tech => memtech, abits => 6, dbits => 12)
                port map( clk, m0address, m0datain, m0dataout, m0enable, m0write);
    cram1 : syncram generic map(tech => memtech, abits => 6, dbits => 12)
                port map( clk, m1address, m1datain, m1dataout, m1enable, m1write);
    qram  : syncram generic map(tech => memtech, abits => 8, dbits => 8)
                port map( clk, qaddress, qdatain, qdataout, qenable, qwrite);
    
comb : process (r, rst, kstrobe1, kaddress1, kdata1, kready2, m0dataout, m1dataout, kstrobeq1, kdataq1, kaddq, krdq, samp_fact, startgen)
      variable v : control_reg;   
      variable vkready1 : std_logic;
      variable verror : std_logic;
      variable vm0address, vm1address : std_logic_vector(5 downto 0);
      variable vm0enable, vm1enable, vm0write, vm1write : std_logic;
      variable fcountup, bcountup : std_logic;
      variable fcntint : integer;
      variable vstrobe : std_logic;
      variable outdata : std_logic_vector(11 downto 0);
      
      variable vqaddress : std_logic_vector(7 downto 0);
      variable vqwrite : std_logic;
      variable vkdata0 : std_logic_vector(11 downto 0);      
      variable vkdata1 : std_logic_vector(11 downto 0);
   begin
   
   v := r;
   verror := '0';
   vm0enable := '0'; vm1enable := '0'; vm0write := '0'; vm1write := '0';
   fcountup := '0'; bcountup := '0';
   vm0address := (others => '0'); vm1address := (others => '0');
   vqaddress := (others => '0'); vqwrite := '0';
   
--   fcntint := to_integer(unsigned(r.countf));
   if (kstrobe1 = '1') then
       if ((r.swf = mem0 and (r.mem0state = full or r.mem0state = reading)) or
           (r.swf = mem1 and (r.mem1state = full or r.mem1state = reading))) then
           verror := '1';
       end if;
       fcountup := '1';
       if(r.swf = mem0) then
           vm0enable := '1';
           vm0write := '1';
--           vm0address := r.countf(2 downto 0) & r.countf(5 downto 3);
           vm0address := kaddress1;
       else
           vm1enable := '1';
           vm1write := '1';
--           vm1address := r.countf(2 downto 0) & r.countf(5 downto 3);
           vm1address := kaddress1;
       end if;
   end if;
   
   vkready1 := '0';
--   if(r.swf = mem0 and (r.mem0state = empty or r.mem0state = writing)) or
--     (r.swf = mem1 and (r.mem1state = empty or r.mem1state = writing)) then
--       vkready1 := '1';
--   end if;
   if(r.swf = mem0 and r.mem0state = empty) or
     (r.swf = mem1 and r.mem1state = empty) then
       vkready1 := '1';
   end if;   
   
      -- backward part
   v.stb2keep := '0';
   if (kready2 = '1') then
       if(r.swb = mem0 and (r.mem0state = full or r.mem0state = reading)) then
           bcountup := '1';
           v.stb2keep := '1';
           vm0enable := '1';
           vm0address := zigzag_rom(to_integer(unsigned(r.countb)));
       elsif(r.swb = mem1 and (r.mem1state = full or r.mem1state = reading)) then
           bcountup := '1';
           v.stb2keep := '1';
           vm1enable := '1';
           vm1address := zigzag_rom(to_integer(unsigned(r.countb)));
       end if;
   end if;
       
   if(r.swb = mem0) then
       outdata := m0dataout;
   else
       outdata := m1dataout;
   end if;
   
   -- fill 0 part
   vkdata0 := kdata1;
   vkdata1 := kdata1;
   if(r.mem0state = fill0) then
       vm0enable := '1';
       vm0address := r.mem0zeroc;
       v.mem0zeroc := r.mem0zeroc + 1;
       vm0write := '1';
       vkdata0 := (others => '0');
   end if;
   if(r.mem1state = fill0) then
       vm1enable := '1';
       vm1address := r.mem0zeroc;
       v.mem1zeroc := r.mem0zeroc + 1;
       vm1write := '1';
       vkdata1 := (others => '0');
   end if;   
   
   -- state-machine
       
   case r.mem0state is
   when empty =>
       if (r.swf = mem0 and fcountup = '1') then
           v.mem0state := writing;
       end if;
   when writing =>
--       if ( fcntint = 63 and fcountup = '1') then
       if(kaddress1 = "111111" and fcountup = '1') then
          v.mem0state := full; 
          v.swf := mem1;
       end if;
   when full => 
       if (r.swb = mem0 and kready2 = '1') then
           v.mem0state := reading;
       end if;
   when reading =>
       if (r.countb = "111111") then
           v.mem0state := standby;
       end if;
   when standby => 
       v.swb := mem1;
       v.mem0state := fill0;
   when fill0 =>
       if(r.mem0zeroc = "111111")then
           v.mem0state := empty;
       end if;
   when others =>
   end case;    

   case r.mem1state is
   when empty =>
       if (r.swf = mem1 and fcountup = '1') then
           v.mem1state := writing;
       end if;
   when writing =>
--       if ( fcntint = 63 and fcountup = '1') then
       if(kaddress1 = "111111" and fcountup = '1')then
          v.mem1state := full; 
          v.swf := mem0;
       end if;
   when full => 
       if (r.swb = mem1 and kready2 = '1') then
           v.mem1state := reading;
       end if;
   when reading =>
       if (r.countb = "111111") then
           v.mem1state := standby;
       end if;
   when standby => 
       v.swb := mem0;
       v.mem1state := fill0;
   when fill0 =>
       if(r.mem1zeroc = "111111")then
           v.mem1state := empty;
       end if;
   when others =>
   end case;    
       
-- counter
--   if(fcountup = '1') then
--       v.countf := r.countf + '1';
--   end if;


debug_bcountup <= bcountup;

   if(bcountup = '1') then
       v.countb := r.countb + '1';
   end if;

--quant memory part
   if(kstrobeq1 = '1' and bcountup = '0') then
       vqwrite := '1';
       vqaddress := kaddq;
   elsif(krdq = '1' and bcountup = '0')then
       vqwrite := '0';
       vqaddress := kaddq;
   elsif((kstrobeq1 = '1' or krdq = '1') and bcountup = '1') then
       verror := '1';
   end if;
   
   if(bcountup = '1') then
--       vqaddress := r.countq(7 downto 6) & r.countq(2 downto 0) & r.countq(5 downto 3);
       v.countq := v.countq + 1;
       if(samp_fact = '0') then
           if (r.countq = "101111111") then
               v.countq := (others => '0');
           end if;
           case r.countq(8 downto 6) is
           when "000" | "001" | "010" | "011" =>
               vqaddress := "00" & r.countq(2 downto 0) & r.countq(5 downto 3);
           when "100"  =>
               vqaddress := "01" & r.countq(2 downto 0) & r.countq(5 downto 3);
           when "101"  => 
               vqaddress := "10" & r.countq(2 downto 0) & r.countq(5 downto 3);
           when others =>
               verror := '1';
           end case;
       else
            case r.countq(8 downto 6) is
            when "000" | "001" | "010" | "011" =>
                vqaddress := "00" & r.countq(2 downto 0) & r.countq(5 downto 3);
            when "100" | "101"  =>
                vqaddress := "01" & r.countq(2 downto 0) & r.countq(5 downto 3);
            when "110" | "111" => 
                vqaddress := "10" & r.countq(2 downto 0) & r.countq(5 downto 3);
            when others =>
                verror := '1';
            end case;
       end if;
   end if;



-- reset part
   if rst = '0' or startgen= '1' then
       v.swf := mem0;
       v.swb := mem0;
       v.mem0state := fill0;
       v.mem1state := fill0;
       v.mem0zeroc := (others => '0');
       v.mem1zeroc := (others => '0');
  --     v.countf := (others => '0');
       v.countb := (others => '0');
       v.countq := (others => '0');
       v.stb2keep := '0';
   end if;
   
-- signal

   rin <= v;
   kready1 <= vkready1;
   kstrobe2 <= r.stb2keep;
   kdata2 <= outdata;
   error <= verror;
   m0address <= vm0address;
   m1address <= vm1address;
   m0enable <= vm0enable;
   m1enable <= vm1enable;
   m0write <= vm0write;
   m1write <= vm1write;
   m0datain <= vkdata0;
   m1datain <= vkdata1;
   qwrite <= vqwrite;
   qaddress <= vqaddress;
   
end process;


   qdatain <= kdataq1;
   kdataq2 <= qdataout;
   krddataq <= qdataout;
   
--registers
reg : process(clk)
begin
    if rising_edge(clk) then
        r <= rin;
    end if;
end process;

end;

   
   
       
