--------------------------------------------------------------------------------
-- Video
--------------------------------------------------------------------------------
-- DO 8/2017
--------------------------------------------------------------------------------
-- - DMA to framebuffer memory
-- - 1,2,4,8,16 bits per pixel
-- - Colours/BW
-- - Programmable resolution
                        
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

LIBRARY work;
USE work.base_pack.ALL;
USE work.plomb_pack.ALL;
USE work.vid_pack.ALL;

ENTITY vidvec IS
  GENERIC (
    BURSTLEN    : natural := 8;
    FIFO_SIZE   : natural := 32;
    FIFO_LEVMAX : natural := 15;
    FIFO_AEC    : natural := 0);
  PORT (
    pw     : OUT type_plomb_w;
    pr     : IN  type_plomb_r;

    adr    : IN uv32;
    adr_h  : IN uv4;
    run    : IN std_logic;
    mode   : IN type_modeline;

    phos  : IN uv3;
    
    -- Fresh
    fresh_a    : OUT unsigned(20 DOWNTO 5);
    fresh_req  : OUT std_logic;

    date     : IN uv8;
    
    -- Video
    vga_r    : OUT uv8;
    vga_g    : OUT uv8;
    vga_b    : OUT uv8;
    vga_de   : OUT std_logic;
    vga_hsyn : OUT std_logic;
    vga_vsyn : OUT std_logic;
    vga_hpos : OUT uint12;
    vga_vpos : OUT uint12;
    vga_clk  : IN  std_logic;
    vga_en   : IN  std_logic;           -- Clock enable
    
    -- Global
    clk      : IN std_logic;
    reset_na : IN std_logic
    );
END ENTITY vidvec;

--##############################################################################

ARCHITECTURE rtl OF vidvec IS
  CONSTANT N_LINE : natural := ilog2(BURSTLEN);
  
  SIGNAL vfifo_ra,vfifo_wa,vfifo_lev : natural RANGE 0 TO FIFO_SIZE-1;
  SIGNAL vfifo_walt : std_logic;
  SIGNAL vfifo : arr_uv64(0 TO FIFO_SIZE-1);

  SIGNAL dma_stopreq : std_logic; 
  SIGNAL vfifo_data : uv64;
  
  SIGNAL pw_i : type_plomb_w;
  SIGNAL pre_d : uv32;

  TYPE enum_etat IS (sSTOP,sRUN,sRAZ,sRAZ2);
  SIGNAL etat  : enum_etat;
  SIGNAL dma_a,dma_da : uv32;
  SIGNAL plomb_aec : natural RANGE 0 TO FIFO_AEC + BURSTLEN + 1;
  
  SIGNAL pop,popsync,popsync2 : std_logic;
  SIGNAL raz,razsync,razsync2 : std_logic;
  
  ---------------------------------------------
  SIGNAL g_clk,g_en,g_reset_na,g_reset_na2 : std_logic;
  SIGNAL g_run,g_raz,g_razp : std_logic;
  SIGNAL g_pop  : std_logic;
  
  SIGNAL g_hdisp,g_hdisp2,g_vdisp : std_logic;
  SIGNAL g_hsync,g_vsync,g_de : std_logic;
  SIGNAL g_hcpt,g_vcpt : uint12;
  SIGNAL g_pixcpt : natural RANGE 0 TO 63;
  SIGNAL g_rad : uv64;
  
  TYPE type_col IS RECORD
    r : uv8;
    g : uv8;
    b : uv8;
  END RECORD type_col;
  TYPE arr_col IS ARRAY(natural RANGE <>) OF type_col;
  
  SIGNAL g_col : type_col;
  
  SIGNAL fresh_pre_req : std_logic;
  SIGNAL fresh_ap : unsigned(20 DOWNTO 5);
  
---------------------------------------------
  FUNCTION convi(CONSTANT data : uv16;
                 CONSTANT date : uv8;
                 CONSTANT phos : uv3) RETURN unsigned IS
    VARIABLE delay,v : uv8;
    VARIABLE v1,d1 : uv9;
    VARIABLE v2 : uv11;
    VARIABLE do : uv16;
    VARIABLE r,g,b : uv8;
  BEGIN
    
    delay:=date-data(15 DOWNTO 8);
    d1:=delay & '0';
    v1:=('0' & data(7 DOWNTO 0)) - (delay & '0');
    v2:=("000" & data(7 DOWNTO 0)) - (delay & "000");
    v:=v1(7 DOWNTO 0);
    IF d1>('0' & data(7 DOWNTO 0))  THEN v:=x"00"; END IF;
    IF v2>("000" & data(7 DOWNTO 0)) THEN v2:="000" & x"00"; END IF;

    --IF delay>x"D0" THEN
    --  v:=x"00";
    --  v2:="00" & x"00";
    --END IF;
    
    IF phos="000" THEN
      do:=x"00" & v; -- Blanc
    ELSIF phos="001" THEN
      do:=v(7 DOWNTO 4) & x"0" & v;
    ELSIF phos="010" THEN
      do:=x"0" & v(7 DOWNTO 4) & v;

    ELSIF phos="011" THEN
      g:=v;
      b:=v2(7 DOWNTO 0);
      r:=v-v2(7 DOWNTO 0);
      do:=v(7 DOWNTO 4) & r(7 DOWNTO 4) & v;
    ELSIF phos="100" THEN
      g:=v;
      b:=v2(7 DOWNTO 0);
      r:=v-v2(7 DOWNTO 0);
      do:=r(7 DOWNTO 4) & v(7 DOWNTO 4) & v;
    ELSIF phos="101" THEN
      g:=v;
      b:=v2(7 DOWNTO 0);
      r:=v-v2(7 DOWNTO 0);
      do:=r(7 DOWNTO 4) & r(7 DOWNTO 4) & v;
    ELSIF phos="110" THEN
      g:=v;
      b:=v2(7 DOWNTO 0);
      r:=v-v2(7 DOWNTO 0);
      do:=x"0" & r(7 DOWNTO 4) & v;
    ELSIF phos="111" THEN
      g:=v;
      b:=v2(7 DOWNTO 0);
      r:=v-v2(7 DOWNTO 0);
      do:=r(7 DOWNTO 4) & x"0" & v;
    ELSE
      do:=x"00" & v;
    END IF;
    
    RETURN do;
  END FUNCTION convi;
  
  FUNCTION conv(CONSTANT data : uv32;
                CONSTANT date : uv8;
                CONSTANT phos : uv3) RETURN unsigned IS
    VARIABLE delay, v : uv8;
    VARIABLE do : uv32;
  BEGIN
    do(15 DOWNTO  0):=convi(data(15 DOWNTO  0),date,phos);
    do(31 DOWNTO 16):=convi(data(31 DOWNTO 16),date,phos);
    RETURN do;
  END FUNCTION conv;

  FUNCTION refr(CONSTANT data : uv32;
                CONSTANT date : uv8) RETURN boolean IS
  BEGIN
    RETURN ((date-data(15 DOWNTO  8)>=x"C0" AND data( 7 DOWNTO  0)/=x"00") OR
            (date-data(31 DOWNTO 24)>=x"C0" AND data(23 DOWNTO 16)/=x"00"));
  END FUNCTION;
  
  FUNCTION cool(CONSTANT d : uv16) RETURN type_col IS
  BEGIN
    -- 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    -- [ Delta R ] [ Delta B ]  [     Intens         ]
    RETURN type_col'(d(7 DOWNTO 0)-(d(15 DOWNTO 12) & "0000"),   -- R
                     d(7 DOWNTO 0),                              -- G
                     d(7 DOWNTO 0)-(d(11 DOWNTO  8) & "0000"));  -- B
  END FUNCTION;
  --------------------------------------
  CONSTANT PLOMB_W_INIT : type_plomb_w :=(
    a=>x"00000000",ah=>"0000",asi=>ASI_USER_DATA,
    d=>x"00000000",be=>"1111",mode=>PB_MODE_RD,
    burst=>PB_SINGLE,cont=>'0',cache=>'0',lock=>'0',req=>'0',dack=>'1');

  CONSTANT ZERO : uv32 := x"00000000";
  
BEGIN
  
  ------------------------------------------------------------------------------
  -- Plomb DMA burst
  Plombage:PROCESS(clk,reset_na,adr_h)
    VARIABLE ptr : unsigned(N_LINE-1 DOWNTO 0);
    VARIABLE pop_v : std_logic;
  BEGIN
    IF reset_na='0' THEN
      pw_i<=PLOMB_W_INIT;
      pw_i.ah<=adr_h;
      plomb_aec<=0;
      vfifo_lev<=0;
      vfifo_ra<=0;
      vfifo_wa<=0;
      dma_stopreq<='1';
      dma_a<=x"00000000";
      fresh_pre_req<='0';
    ELSIF rising_edge(clk) THEN
      pw_i<=PLOMB_W_INIT;
      pw_i.ah<=adr_h;

      ---------------------------------------------
      -- Pipe adresses
      IF pw_i.req='1' AND pr.ack='0' THEN
        -- Attente
        pw_i<=pw_i;
      ELSIF pw_i.req='0' OR pr.ack='1' THEN
        -- Fin d'accès
        IF dma_stopreq='1' AND
          dma_a(N_LINE+1 DOWNTO 2)=ZERO(N_LINE+1 DOWNTO 2) THEN
          pw_i.req<='0';
        ELSE
          pw_i<=PLOMB_W_INIT;
          pw_i.req<='1';
          pw_i.a<=dma_a;
          IF dma_a(N_LINE+1 DOWNTO 2)=ZERO(N_LINE+1 DOWNTO 2) THEN
            pw_i.burst<=pb_blen(BURSTLEN);
          ELSE
            pw_i.burst<=PB_SINGLE;
          END IF;
          dma_a<=dma_a+4;
        END IF;
      END IF;
      
      ---------------------------------------------
      -- Pipe data, FIFO
      fresh_req<='0';
      pop_v:=popsync2 XOR popsync;
      IF pr.dreq='1' THEN
        pre_d<=conv(pr.d,date,phos);
        vfifo(vfifo_wa)<=pre_d & conv(pr.d,date,phos);
        IF refr(pr.d,date) THEN
          fresh_pre_req<='1';
          fresh_ap<=dma_da(20 DOWNTO 5);
        END IF;
        IF dma_da(4 DOWNTO 0)="00000" AND fresh_pre_req='1' THEN
          fresh_req<='1';
          fresh_pre_req<='0';
          fresh_a<=fresh_ap;
        END IF;
        IF vfifo_wa=FIFO_SIZE-1 AND vfifo_walt='1' THEN
          vfifo_wa<=0;
          vfifo_walt<='0';
        ELSE
          vfifo_walt<=NOT vfifo_walt;
          IF vfifo_walt='1' THEN
            vfifo_wa<=vfifo_wa+1;
          END IF;
        END IF;
        dma_da<=dma_da+4;
      END IF;
      
      IF pop_v='1' THEN
        IF vfifo_ra=FIFO_SIZE-1 THEN
          vfifo_ra<=0;
        ELSE
          vfifo_ra<=vfifo_ra+1;
        END IF;
      END IF;
      IF pr.dreq='1' AND vfifo_walt='1' AND pop='0' THEN
        vfifo_lev<=vfifo_lev+1;
      ELSIF (pr.dreq='0' OR vfifo_walt='0') AND pop='1' AND vfifo_lev>0 THEN
        vfifo_lev<=vfifo_lev-1;
      END IF;

      IF vfifo_lev>FIFO_LEVMAX OR plomb_aec>FIFO_AEC THEN
        dma_stopreq<='1';
      ELSE
        dma_stopreq<='0';
      END IF;
      
      ---------------------------------------------
      IF pw_i.req='1' AND pr.ack='1' AND NOT pr.dreq='1' THEN
        plomb_aec<=plomb_aec+1;
      ELSIF NOT (pw_i.req='1' AND pr.ack='1') AND pr.dreq='1' THEN
        plomb_aec<=plomb_aec-1;
      END IF;
      
      ---------------------------------------------
      CASE etat IS
        WHEN sSTOP =>
          dma_stopreq<='1';
          IF run='1' THEN
            etat<=sRUN;
          END IF;
          
        WHEN sRUN =>
          IF run='0' THEN
            etat<=sSTOP;
          END IF;
          IF raz='1' THEN
            etat<=sRAZ;
          END IF;
          
        WHEN sRAZ =>
          dma_stopreq<='1';
          IF plomb_aec=0 AND
            dma_a(N_LINE+1 DOWNTO 2)=ZERO(N_LINE+1 DOWNTO 2) THEN
            etat<=sRAZ2;
            vfifo_lev<=0;
            vfifo_ra<=0;
            vfifo_wa<=0;
            vfifo_walt<='0';
          END IF;

        WHEN sRAZ2 =>
          dma_stopreq<='0';
          dma_a<=adr;
          dma_da<=adr;
          etat<=sRUN;
      END CASE;
      
    END IF;
  END PROCESS Plombage;
  
  pw<=pw_i;
  vfifo_data<=vfifo(vfifo_ra);
  --vfifo_data<=(dma_a(15 DOWNTO 0) XOR dma_a(23 DOWNTO 8)) &
  --            (dma_a(15 DOWNTO 0) XOR dma_a(23 DOWNTO 8)) &
  --            (dma_a(15 DOWNTO 0) XOR dma_a(23 DOWNTO 8)) &
  --            (dma_a(15 DOWNTO 0) XOR dma_a(23 DOWNTO 8));
  
  ------------------------------------------------------------------------------
  -- Resync
  Sync: PROCESS (clk, reset_na) IS
  BEGIN
    IF reset_na='0' THEN
      pop<='0';
      raz<='0';
      popsync<='0';
      popsync2<='0';
      razsync<='0';
      razsync2<='0';
    ELSIF rising_edge(clk) THEN
      -----------------------------------------------
      popsync<=g_pop;
      popsync2<=popsync;
      razsync<=g_raz;
      razsync2<=razsync;
      pop<=popsync2 XOR popsync;
      raz<=razsync2 XOR razsync;
    END IF;
  END PROCESS Sync;

  GenGRESET: PROCESS (g_clk, reset_na) IS
  BEGIN
    IF reset_na='0' THEN
      g_reset_na<='0';
      g_reset_na2<='0';
    ELSIF rising_edge(g_clk) THEN
      g_reset_na2<='1';
      g_reset_na<=g_reset_na2;
    END IF;
  END PROCESS GenGRESET;
    
  SyncG: PROCESS (g_clk, g_reset_na) IS
  BEGIN
    IF g_reset_na='0' THEN
      g_run<='0';
    ELSIF rising_edge(g_clk) THEN
      g_run<=run;
      
    END IF;
  END PROCESS SyncG;
  
  ------------------------------------------------------------------------------
  g_clk<=vga_clk;
  g_en <=vga_en;
  
  GenVideo: PROCESS (g_clk, g_reset_na) IS
  BEGIN
    IF g_reset_na = '0' THEN
      g_raz<='0';
      g_pop<='0';
      g_hsync<='0';
      g_vsync<='0';
      g_hdisp<='1';
      g_vdisp<='1';
      g_de <='0';
    ELSIF rising_edge(g_clk) THEN
      IF g_en='1' THEN
        ---------------------------------------------
        IF g_run='0' THEN
          g_hcpt<=0;
          g_vcpt<=0;
        ELSE
          IF g_hcpt=mode.htotal-1 THEN
            g_hcpt<=0;
            g_hdisp<='1';
            IF g_vcpt=mode.vtotal-1 THEN
              g_vcpt<=0;
              g_vdisp<='1';
            ELSE
              g_vcpt<=g_vcpt+1;
            END IF;
          ELSE
            g_hcpt<=g_hcpt+1;
          END IF;
        END IF;
        IF g_hcpt=mode.hdisp-1 THEN
          g_hdisp<='0';
        END IF;
        IF g_vcpt=mode.vdisp THEN
          g_vdisp<='0';
          g_razp<='1';
          IF g_razp='0' THEN
            g_raz<=NOT g_raz;
          ELSE
            g_raz<=g_raz;
          END IF;
        ELSE
          g_razp<='0';
          g_raz<=g_raz;
        END IF;
        IF g_hcpt=mode.hsyncstart THEN
          g_hsync<='1';
        ELSIF g_hcpt=mode.hsyncend THEN
          g_hsync<='0';
        END IF;
        IF g_vcpt=mode.vsyncstart THEN
          g_vsync<='1';
        ELSIF g_vcpt=mode.vsyncend THEN
          g_vsync<='0';
        END IF;
        ------------------
        IF g_hdisp='1' AND g_vdisp='1' THEN
          -- 16bpp
          IF g_pixcpt=3 THEN
            g_pixcpt<=0;
            g_rad<=vfifo_data;
          ELSE
            g_pixcpt<=g_pixcpt+1;
            g_rad<=g_rad(47 DOWNTO 0) & vfifo_data(15 DOWNTO 0);
          END IF;
          IF g_pixcpt=3  THEN
            g_pop<=NOT g_pop;
          END IF;
          
        ELSE
          g_pixcpt<= 3;
          
        END IF;
        
        g_hdisp2<=g_hdisp;
        IF g_hdisp2='1' AND g_vdisp='1' THEN
          g_col<=cool(g_rad(63 DOWNTO 48));
          --g_col.r<=to_unsigned(g_hcpt MOD 256,8);
          --g_col.g<=to_unsigned(g_vcpt MOD 256,8);
          --g_col.b<=to_unsigned(g_vcpt MOD 256,8) XOR
          --         to_unsigned(g_hcpt MOD 256,8);
          g_de <='1';
        ELSE
          g_col<=(x"00",x"00",x"00");
          g_de <='0';
        END IF;
      END IF;
    END IF;
  END PROCESS GenVideo;

  vga_r<=g_col.r;
  vga_g<=g_col.g;
  vga_b<=g_col.b;
  vga_de<=g_de;
  vga_hsyn<=g_hsync XOR mode.hsyncpol;
  vga_vsyn<=g_vsync XOR mode.vsyncpol;
  vga_hpos<=g_hcpt;
  vga_vpos<=g_vcpt;

END ARCHITECTURE rtl;
