------------------------------------------------------------------------------
--  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:      dctmem1cont
-- File:        dctmem1cont.vhd
-- Author:      Kenichi Kurimoto 
-- Description: DCT memory and controller for input of idct1  
------------------------------------------------------------------------------

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 dctmem1cont is
   generic (
      memtech : integer := DEFMEMTECH);
   port (
      rst   : in std_ulogic;
      clk   : in std_ulogic;
      kready1  : out std_logic;
      kstrobe1 : in std_logic;
      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)
   );
end;

architecture rtl of dctmem1cont is

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

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;  
   countq : std_logic_vector(8 downto 0);
end record;

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;

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, kdata1, kready2, m0dataout, m1dataout, kstrobeq1, kdataq1)
      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;
      
   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);
       else
           vm1enable := '1';
           vm1write := '1';
           vm1address := r.countf(2 downto 0) & r.countf(5 downto 3);
       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;
   
   -- 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 := r.countb;
       elsif(r.swb = mem1 and (r.mem1state = full or r.mem1state = reading)) then
           bcountup := '1';
           v.stb2keep := '1';
           vm1enable := '1';
           vm1address := r.countb;
       end if;
   end if;
       
   if(r.swb = mem0) then
       outdata := m0dataout;
   else
       outdata := m1dataout;
   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
          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 := empty;
   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
          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 := empty;
   when others =>
   end case;    
       
-- counter
   if(fcountup = '1') then
       v.countf := r.countf + '1';
   end if;
   if(bcountup = '1') then
       v.countb := r.countb + '1';
   end if;

--quant memory part
   if(kstrobeq1 = '1' and bcountup = '0') then
       vqwrite := '1';
       vqaddress := r.countq(7 downto 0);
       v.countq := v.countq + 1;
       if(r.countq = "010111111") then
           v.countq := (others => '0');
       end if;
   elsif(kstrobeq1 = '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' then
       v.swf := mem0;
       v.swb := mem0;
       v.mem0state := empty;
       v.mem1state := empty;
       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;
   qwrite <= vqwrite;
   qaddress <= vqaddress;
   
end process;

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

end;

   
   
       
