------------------------------------------------------------------------------
--  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:      yccmemcont
-- File:        yccmemcont.vhd
-- Author:      Kenichi Kurimoto 
-- Description: YCbCr memory + controller
------------------------------------------------------------------------------

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;

entity yccmemcont 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(7 downto 0);
      kready2  : in std_logic;
      kstrobe2 : out std_logic;
      kdata2   : out std_logic_vector(23 downto 0);
      samp_fact : in std_logic;
      error     : out std_logic;
      startgen  : in std_logic
   );
end;
-- samp_fact = 0 -> 4:1:1
-- samp_fact = 1 -> 4:2:2

architecture rtl of yccmemcont 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(8 downto 0);
--    selectf  : std_logic_vector(1 downto 0);
    countb   : std_logic_vector(7 downto 0);
    stb2keep : std_logic;
end record;

signal r, rin : control_reg;
signal y0address, y1address : std_logic_vector(7 downto 0);
signal cb0address, cb1address, cr0address, cr1address : std_logic_vector(6 downto 0);
signal y0datain, y1datain, cb0datain, cb1datain, cr0datain, cr1datain : std_logic_vector(7 downto 0);
signal y0dataout, y1dataout, cb0dataout, cb1dataout, cr0dataout, cr1dataout : std_logic_vector(7 downto 0);
signal y0enable, y1enable, cb0enable, cb1enable, cr0enable, cr1enable : std_logic;
signal y0write, y1write, cb0write, cb1write, cr0write, cr1write : std_logic;

begin
yram0 : syncram generic map(tech => memtech, abits => 8, dbits => 8)
            port map( clk, y0address, y0datain, y0dataout, y0enable, y0write);
yram1 : syncram generic map(tech => memtech, abits => 8, dbits => 8)
            port map( clk, y1address, y1datain, y1dataout, y1enable, y1write);
cbram0 : syncram generic map(tech => memtech, abits => 7, dbits => 8)
             port map( clk, cb0address, cb0datain, cb0dataout, cb0enable, cb0write);
cbram1 : syncram generic map(tech => memtech, abits => 7, dbits => 8)
             port map( clk, cb1address, cb1datain, cb1dataout, cb1enable, cb1write);
crram0 : syncram generic map(tech => memtech, abits => 7, dbits => 8)
             port map( clk, cr0address, cr0datain, cr0dataout, cr0enable, cr0write);
crram1 : syncram generic map(tech => memtech, abits => 7, dbits => 8)
             port map( clk, cr1address, cr1datain, cr1dataout, cr1enable, cr1write);                 
                 
                 
comb : process (r, rst, kstrobe1, kdata1, kready2, samp_fact, y0dataout, y1dataout, 
                   cb0dataout, cb1dataout, cr0dataout, cr1dataout, startgen)
      variable v : control_reg;     
      variable vkready1 : std_logic;
      variable verror : std_logic;
      variable vy0address, vy1address : std_logic_vector(7 downto 0);
      variable vcb0address, vcb1address, vcr0address, vcr1address : std_logic_vector(6 downto 0);
      variable vy0enable, vy1enable, vcb0enable, vcb1enable, vcr0enable, vcr1enable : std_logic;
      variable vy0write, vy1write, vcb0write, vcb1write, vcr0write, vcr1write : std_logic;
      variable fcountup, bcountup : std_logic;
      variable fcntint : integer;
      variable vstrobe : std_logic;
      variable outdata : std_logic_vector(23 downto 0);
   begin

   v := r;
   verror := '0';
   vy0enable := '0'; vy1enable := '0'; vcb0enable := '0'; vcb1enable := '0'; vcr0enable := '0'; vcr1enable := '0';
   vy0write := '0'; vy1write := '0'; vcb0write := '0'; vcb1write := '0'; vcr0write := '0'; vcr1write := '0';
   fcountup := '0'; bcountup := '0';
   vy0address := (others => '0'); vy1address := (others => '0'); 
   vcb0address := (others => '0'); vcb1address := (others => '0');
   vcr0address := (others => '0'); vcr1address := (others => '0');
   
   -- forward part
   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
           if(samp_fact = '0') then
               if(fcntint < 256) then
                   vy0enable := '1';
                   vy0write := '1';
                   vy0address := r.countf(7 downto 0);
               elsif(fcntint < 320) then
                   vcb0enable := '1';
                   vcb0write := '1';
                   vcb0address := r.countf(6 downto 0);
               elsif(fcntint < 384) then
                   vcr0enable := '1';
                   vcr0write := '1';
                   vcr0address := '0' & r.countf(5 downto 0);
               else
                   verror := '1';
               end if;
           else
               if(fcntint < 256) then
                   vy0enable := '1';
                   vy0write := '1';
                   vy0address := r.countf(7 downto 0);
               elsif(fcntint < 384) then
                   vcb0enable := '1';
                   vcb0write := '1';
                   vcb0address := r.countf(6 downto 0);
               elsif(fcntint < 512) then
                   vcr0enable := '1';
                   vcr0write := '1';
                   vcr0address := r.countf(6 downto 0);
               else
                   verror := '1';
               end if;           
           end if;
       else
           if(samp_fact = '0') then
               if(fcntint < 256) then
                   vy1enable := '1';
                   vy1write := '1';
                   vy1address := r.countf(7 downto 0);
               elsif(fcntint < 320) then
                   vcb1enable := '1';
                   vcb1write := '1';
                   vcb1address := r.countf(6 downto 0);
               elsif(fcntint < 384) then
                   vcr1enable := '1';
                   vcr1write := '1';
                   vcr1address := '0' & r.countf(5 downto 0);
               else
                   verror := '1';
               end if;
           else
               if(fcntint < 256) then
                   vy1enable := '1';
                   vy1write := '1';
                   vy1address := r.countf(7 downto 0);
               elsif(fcntint < 384) then
                   vcb1enable := '1';
                   vcb1write := '1';
                   vcb1address := r.countf(6 downto 0);
               elsif(fcntint < 512) then
                   vcr1enable := '1';
                   vcr1write := '1';
                   vcr1address := r.countf(6 downto 0);
               else
                   verror := '1';
               end if;           
           end if;
       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';
           vy0enable := '1';         
           vcb0enable := '1';
           vcr0enable := '1';
           vy0address := r.countb(7) & r.countb(3) & r.countb(6 downto 4) & r.countb(2 downto 0);
           if(samp_fact = '0') then   
               vcb0address := '0' & r.countb(7 downto 5) & r.countb(3 downto 1);
               vcr0address := '0' & r.countb(7 downto 5) & r.countb(3 downto 1);
           else
               vcb0address := r.countb(7 downto 1);  
               vcr0address := r.countb(7 downto 1);
           end if;
       elsif(r.swb = mem1 and (r.mem1state = full or r.mem1state = reading))then
           bcountup := '1';
           v.stb2keep := '1';
           vy1enable := '1';
           vcb1enable := '1';
           vcr1enable := '1';
           vy1address := r.countb(7) & r.countb(3) & r.countb(6 downto 4) & r.countb(2 downto 0);
           if(samp_fact = '0') then   
               vcb1address := '0' & r.countb(7 downto 5) & r.countb(3 downto 1);
               vcr1address := '0' & r.countb(7 downto 5) & r.countb(3 downto 1);
           else
               vcb1address := r.countb(7 downto 1);  
               vcr1address := r.countb(7 downto 1);
           end if;
       end if;
   end if;
   
   if(r.swb = mem0)then
       outdata := y0dataout & cb0dataout & cr0dataout;
   else
       outdata := y1dataout & cb1dataout & cr1dataout;
   end if;
   
   
   --state-machine
   --check empty case batting memory read write access
   

   case r.mem0state is
   when empty =>
       if (r.swf = mem0 and fcountup = '1') then
           v.mem0state := writing;
       end if;
   when writing =>
       if ((samp_fact = '0' and fcntint = 383 and fcountup = '1')or(samp_fact = '1' and fcntint = 511 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 = "11111111") 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 ((samp_fact = '0' and fcntint = 383 and fcountup = '1')or(samp_fact = '1' and fcntint = 511 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 = "11111111") 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';
       if (samp_fact = '0') and (fcntint = 383) then
           v.countf := "000000000";
       elsif (samp_fact = '1') and (fcntint = 511) then
           v.countf := "000000000";
       end if; 
   end if;
   if (bcountup = '1') then
       v.countb := r.countb + '1';
   end if;
  
-- reset part
   if rst = '0' or startgen = '1' then
       v.swf := mem0;
       v.swb := mem0;
       v.mem0state := empty;
       v.mem1state := empty;
       v.countf := (others => '0');
       v.countb := (others => '0');
       v.stb2keep := '0';
   end if;
  
-- signal

   rin <= v;   
   kready1 <= vkready1;
   kstrobe2 <= r.stb2keep;
   kdata2 <= outdata;
   error <= verror;
   y0address <= vy0address;
   y1address <= vy1address;
   cb0address <= vcb0address;
   cb1address <= vcb1address;
   cr0address <= vcr0address;
   cr1address <= vcr1address;
   y0enable <= vy0enable;
   y1enable <= vy1enable;
   cb0enable <= vcb0enable;
   cb1enable <= vcb1enable;
   cr0enable <= vcr0enable;
   cr1enable <= vcr1enable;
   y0write <= vy0write;
   y1write <= vy1write;
   cb0write <= vcb0write;
   cb1write <= vcb1write;
   cr0write <= vcr0write;
   cr1write <= vcr1write;
   
end process;

   y0datain <= kdata1;
   y1datain <= kdata1;
   cb0datain <= kdata1;
   cb1datain <= kdata1;
   cr0datain <= kdata1;
   cr1datain <= kdata1;
   
-- registers 
reg : process(clk)
begin
   if rising_edge(clk) then
        r <= rin;
   end if;
end process;


end;
   
