--------------------------------------------------------------------------------
-- AVALON SCALER
--------------------------------------------------------------------------------
-- TEMLIB 10/2018
--------------------------------------------------------------------------------
-- This code can be used for any purpose, but, if you find any bug, or want to
-- suggest an enhancement, you ought to send a mail to info@temlib.org
--------------------------------------------------------------------------------

-- Features :
--  - Arbitrary output video format
--  - Autodetect input image size or fixed window
--  - Progressive and interleaved frames
--  - Interpolation
--    Upscaling   : Nearest, Bilinear, Bicubic, Polyphase, Sharp Bilinear
--    Downscaling : Nearest, Bilinear
--  - Avalon bus interface with 128 or 64 bits DATA
--  - Optional triple buffering
--  - 

-- AFAIRE:
--  - Configurable colour depth
--  - 64bits/32 bits Avalon
--  - Sync. pol. detect
--  - Border colour
--  - run/stop
--  - better rounding (linear)
--  - Downscaling
--  - 

--------------------------------------------
-- 4 clock domains
--  i_xxx    : Input video
--  o_xxx    : Output video
--  avl_xxx  : Avalon memory bus
--  poly_xxx : Polyphase filters memory

--------------------------------------------
-- Mode 24bits

--------------------------------------------
-- Image header. When HEADER = TRUE
-- Header Address = RAMBASE
-- Image Address  = RAMBASE + HEADER_SIZE

-- Header (Bytes. Big Endian.)
--  0    : Type = 1
--  1    : Pixel format
--         1 : 24 bits/pixel, packed RGB. Big Endian
--  3:2  : Header size : Offset to start of picture
--         = N_BURST
--  5:4  : Attributes. TBD
--  7:6  : Image width. Pixels.
--  9:8  : Image height. Pixels.
-- 11:10 : Line lenght. Bytes.
-- 13:12 : TBD.
-- 15:14 : TBD.
--------------------------------------------

-- VSYNC :
--   - DELAY_OV = Mesure délai entre VSYNC et début affichage
--   - DELAY_IN = Mesure délai image complète entrée
--
-- Quand première ligne affichée :
--   Attente de DELAY_IN - DELAY_OV -> Déclenche VSYNC.
-- 
-- 

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;


-- MODE[4]
-- 0 : Normal
-- 1 : Low Latency mode. Output vertical frequency locked on input signal.

-- MODE[3]
-- 0 : Direct. Single framebuffer.
-- 1 : Triple buffering

-- MODE[2:0]
-- 000 : Nearest
-- 001 : Bilinear
-- 010 : Sharp Bilinear
-- 011 : Bicubic
-- 100 : Polyphase 1
-- 101 : Polyphase 2
-- 110 : Polyphase 3
-- 111 : TEST

-- MASK    : Enable / Disable selected interpoler (0)=Nearest (1)=Bilinear <TODO>
-- RAMBASE : RAM base address for framebuffer
-- RAMSIZE : RAM allocated for one framebuffer (needs x3 if triple-buffering)
--           Must be a power of two
-- RO      : True = Read Only. Another block updates the framebuffer <TODO>
-- INTER   : True=Autodetect interleaved video False=Force progressive scan
-- HEADER  : True=Add image properties header
-- SYNCHRO : True=Support synchronized mode False=Force disabled
-- FRAC    : Fractional bits, subpixel resolution
-- NPOLY   : Number of polyphase filters stored
-- FORMAT  : TBD <TODO>
-- N_DW    : Avalon data bus width <TODO>
-- N_AW    : Avalon address bus width
-- N_BURST : Burst size in bytes. Power of two.
-- OHRES   : Max. output resolution. Must be a power of two

ENTITY ascal IS
  GENERIC (
    MASK    : unsigned(0 TO 7) :=x"FF";
    RAMBASE : unsigned(31 DOWNTO 0);
    RAMSIZE : unsigned(31 DOWNTO 0) := x"0080_0000"; -- =8MB
    RO      : boolean := false;
    INTER   : boolean := true;
    HEADER  : boolean := true; --false;
    SYNCHRO : boolean := true;
    FRAC    : natural RANGE 4 TO 6 :=4;
    NPOLY   : natural RANGE 0 TO 2 :=2;
    FORMAT  : natural RANGE 1 TO 8 :=8;
    OHRES   : natural RANGE 1 TO 2048 :=2048;
    N_DW    : natural RANGE 32 TO 128 := 128;
    N_AW    : natural RANGE 8 TO 32 := 32;
    N_BURST : natural := 256 -- 256 bytes per burst
    );
  PORT (
    ------------------------------------
    -- Input video
    i_r   : IN  unsigned(7 DOWNTO 0);
    i_g   : IN  unsigned(7 DOWNTO 0);
    i_b   : IN  unsigned(7 DOWNTO 0);
    i_hs  : IN  std_logic; -- Horizontal sync
    i_vs  : IN  std_logic; -- Vertical sync
    i_fl  : IN  std_logic; -- Interlaced field
    i_de  : IN  std_logic; -- Display Enable
    i_ce  : IN  std_logic; -- Clock Enable
    i_clk : IN  std_logic; -- Input clock

    ------------------------------------
    -- Output video
    o_r   : OUT unsigned(7 DOWNTO 0);
    o_g   : OUT unsigned(7 DOWNTO 0);
    o_b   : OUT unsigned(7 DOWNTO 0);
    o_hs  : OUT std_logic; -- H sychro
    o_vs  : OUT std_logic; -- V sychro
    o_de  : OUT std_logic; -- Display Enable
    o_ce  : IN  std_logic; -- Clock Enable
    o_clk : IN  std_logic; -- Output clock

    ------------------------------------
    -- Input video parameters
    iauto : IN std_logic; -- 1=Autodetect image size 0=Choose window
    himin : IN natural RANGE 0 TO 4095;
    himax : IN natural RANGE 0 TO 4095;
    vimin : IN natural RANGE 0 TO 4095;
    vimax : IN natural RANGE 0 TO 4095;
    
    -- Output video parameters
    run   : IN std_logic;
    mode  : IN unsigned(4 DOWNTO 0);
    -- SYNC  |________________________/"""""""""\______|
    -- DE    |""""""""""""""""""\______________________|
    -- RGB   |    <#IMAGE#>     ^HDISP                 |
    --            ^HMIN   ^HMAX       ^HSSTART  ^HSEND ^HTOTAL
    htotal  : IN natural RANGE 0 TO 4095;
    hsstart : IN natural RANGE 0 TO 4095;
    hsend   : IN natural RANGE 0 TO 4095;
    hdisp   : IN natural RANGE 0 TO 4095;
    hmin    : IN natural RANGE 0 TO 4095;
    hmax    : IN natural RANGE 0 TO 4095;
    vtotal  : IN natural RANGE 0 TO 4095;
    vsstart : IN natural RANGE 0 TO 4095;
    vsend   : IN natural RANGE 0 TO 4095;
    vdisp   : IN natural RANGE 0 TO 4095;
    vmin    : IN natural RANGE 0 TO 4095;
    vmax    : IN natural RANGE 0 TO 4095;
    
    ------------------------------------
    -- Polyphase filter coefficients
    poly_clk : IN std_logic;
    poly_dw  : IN unsigned(8 DOWNTO 0);
    poly_a   : IN unsigned(NPOLY+FRAC+2 DOWNTO 0);
    poly_wr  : IN std_logic;
    -- Order :
    --   [Filter 0] [Filter 1]
    --   [Horizontal] [Vertical]
    --   [0]...[2**FRAC-1]
    --   [-1][0][1][2]
    
    ------------------------------------
    -- Avalon
    avl_clk            : IN    std_logic; -- Avalon clock
    avl_waitrequest    : IN    std_logic;
    avl_readdata       : IN    std_logic_vector(N_DW-1 DOWNTO 0);
    avl_readdatavalid  : IN    std_logic;
    avl_burstcount     : OUT   std_logic_vector(7 DOWNTO 0);
    avl_writedata      : OUT   std_logic_vector(N_DW-1 DOWNTO 0);
    avl_address        : OUT   std_logic_vector(N_AW-1 DOWNTO 0);
    avl_write          : OUT   std_logic;
    avl_read           : OUT   std_logic;
    avl_byteenable     : OUT   std_logic_vector(N_DW/8-1 DOWNTO 0);

    ------------------------------------
    reset_na           : IN    std_logic
    );

BEGIN
  ASSERT N_DW=32 OR N_DW=64 OR N_DW=128 REPORT "DW" SEVERITY failure;
  
END ENTITY ascal;

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

ARCHITECTURE rtl OF ascal IS
  
  ----------------------------------------------------------
  FUNCTION ilog2 (CONSTANT v : natural) RETURN natural IS
    VARIABLE r : natural := 1;
    VARIABLE n : natural := 0;
  BEGIN
    WHILE v>r LOOP
      n:=n+1;
      r:=r*2;
    END LOOP;
    RETURN n;
  END FUNCTION ilog2;
  FUNCTION to_std_logic (a : boolean) RETURN std_logic IS
  BEGIN
    IF a THEN RETURN '1';
         ELSE RETURN '0';
    END IF;
  END FUNCTION to_std_logic;
  
  ----------------------------------------------------------
  CONSTANT NB_BURST : natural :=ilog2(N_BURST);
  CONSTANT NB_LA : natural :=ilog2(N_DW/8); -- Low address bits
  CONSTANT BLEN : natural :=N_BURST / N_DW * 8; -- Burst length
  CONSTANT NP : natural :=24;
  
  ----------------------------------------------------------
  SIGNAL bg_col : unsigned(23 DOWNTO 0); -- Background colour
  
  ----------------------------------------------------------
  -- Input image
  -- IHSIZE=IHMAX-IHMIN
  SIGNAL i_run : std_logic;
  SIGNAL i_hsize,i_hmin,i_hmax,i_rhmax,i_hcpt : natural RANGE 0 TO 4095;
  SIGNAL i_chmin,i_chmax,i_cvmin,i_cvmax : natural RANGE 0 TO 4095;
  SIGNAL i_vsize,i_vmin,i_vmax,i_vmaxc,i_vcpt : natural RANGE 0 TO 4095;
  SIGNAL i_vminset : std_logic;
  SIGNAL i_iauto : std_logic;
  SIGNAL i_ven : std_logic;
  SIGNAL i_wr : std_logic;
  SIGNAL i_rgb : unsigned(23 DOWNTO 0);
  SIGNAL i_de_pre,i_hs_pre,i_vs_pre,i_fl_pre : std_logic;
  SIGNAL i_intercnt : natural RANGE 0 TO 7;
  SIGNAL i_inter : std_logic;
  SIGNAL i_write,i_walt : std_logic;
  SIGNAL i_push,i_pushhead,i_pushhead2,i_pushhead3 : std_logic;
  SIGNAL i_hburst,i_hbcpt : natural RANGE 0 TO 31;
  SIGNAL i_shift : unsigned(0 TO 119) := (OTHERS =>'0');
  SIGNAL i_head : unsigned(127 DOWNTO 0);
  SIGNAL i_acpt : natural RANGE 0 TO 15;
  TYPE arr_dw IS  ARRAY (natural RANGE <>) OF unsigned(N_DW-1 DOWNTO 0);
  SIGNAL i_dpram,o_dpram : arr_dw(0 TO BLEN*2-1);
  ATTRIBUTE ramstyle : string;
  ATTRIBUTE ramstyle OF i_dpram : SIGNAL IS "no_rw_check";
  ATTRIBUTE ramstyle OF o_dpram : SIGNAL IS "no_rw_check";

  SIGNAL i_syncline : std_logic;
  SIGNAL i_wad,i_wad_pre : natural RANGE  0 TO BLEN*2-1;
  SIGNAL i_dw : unsigned(N_DW-1 DOWNTO 0);
  SIGNAL i_adrs,i_adrsi : unsigned(31 DOWNTO 0); -- Avalon address
  SIGNAL i_reset_na : std_logic;
  ----------------------------------------------------------
  -- Avalon
  TYPE type_avl_state IS (sIDLE,sWRITE,sREAD);
  SIGNAL avl_state : type_avl_state;
  SIGNAL avl_mode : unsigned(4 DOWNTO 0);
  SIGNAL avl_write_i,avl_write_sync,avl_write_sync2 : std_logic;
  SIGNAL avl_read_i,avl_read_sync,avl_read_sync2 : std_logic;
  SIGNAL avl_read_pulse,avl_write_pulse : std_logic;
  SIGNAL avl_i_vs,avl_i_vs_sync : std_logic;
  SIGNAL avl_o_vs,avl_o_vs_sync : std_logic;
  SIGNAL avl_reading : std_logic;
  SIGNAL avl_read_sr,avl_write_sr,avl_read_clr,avl_write_clr : std_logic;
  SIGNAL avl_rad,avl_rad_c,avl_wad : natural RANGE 0 TO 2*BLEN-1;
  SIGNAL avl_walt : std_logic;
  SIGNAL avl_fl,avl_inter : std_logic;
  SIGNAL avl_dw,avl_dr : unsigned(N_DW-1 DOWNTO 0);
  SIGNAL avl_wr : std_logic;
  SIGNAL avl_readack : std_logic;
  SIGNAL avl_radrs,avl_wadrs : unsigned(31 DOWNTO 0);
  SIGNAL avl_rbib : std_logic;
  SIGNAL avl_i_buf,avl_o_buf : natural RANGE 0 TO 2;
  SIGNAL avl_i_offset,avl_o_offset : unsigned(31 DOWNTO 0);
  SIGNAL avl_bufup : std_logic;
  SIGNAL avl_reset_na : std_logic;
  
  FUNCTION buf_next(a,b : natural RANGE 0 TO 2) RETURN natural IS
  BEGIN
    IF (a=0 AND b=1) OR (a=1 AND b=0) THEN RETURN 2; END IF;
    IF (a=1 AND b=2) OR (a=2 AND b=1) THEN RETURN 0; END IF;
    RETURN 1;                              
  END FUNCTION;
  FUNCTION buf_offset(b : natural RANGE 0 TO 2) RETURN unsigned IS
  BEGIN
    IF b=1 THEN RETURN RAMSIZE; END IF;
    IF b=2 THEN RETURN RAMSIZE(30 DOWNTO 0) & '0'; END IF;
    RETURN x"00000000"; 
  END FUNCTION;

  SIGNAL avl_debug_write : natural RANGE 0 TO 255;
  
  ----------------------------------------------------------
  -- Output
  SIGNAL o_run : std_logic;
  SIGNAL o_mode,o_modex : unsigned(4 DOWNTO 0);
  SIGNAL o_htotal,o_hsstart,o_hsend : natural RANGE 0 TO 4095;
  SIGNAL o_hmin,o_hmax,o_hdisp : natural RANGE 0 TO 4095;
  SIGNAL o_vtotal,o_vsstart,o_vsend : natural RANGE 0 TO 4095;
  SIGNAL o_vmin,o_vmax,o_vdisp : natural RANGE 0 TO 4095;
  SIGNAL o_divcpt : natural RANGE 0 TO 36;
  SIGNAL o_divstart : std_logic;
  SIGNAL o_divrun : std_logic;
  TYPE type_o_state IS (sDISP,sHSYNC,sREAD,sWAITREAD);
  SIGNAL o_state : type_o_state;
  SIGNAL o_copy,o_readack,o_readack_sync,o_readack_sync2 : std_logic;
  SIGNAL o_copy1,o_copy2,o_copy3,o_copy4,o_copy5 : std_logic;
  SIGNAL o_adrs : unsigned(31 DOWNTO 0); -- Avalon address
  SIGNAL o_adrs_pre : natural RANGE 0 TO 32*4096-1;
  SIGNAL o_ad : natural RANGE 0 TO 2*BLEN-1;
  SIGNAL o_adturn : std_logic;
  SIGNAL o_dr : unsigned(N_DW-1 DOWNTO 0);
  SIGNAL o_shift : unsigned(0 TO 143);
  SIGNAL o_reset_na : std_logic;

  TYPE arr_pix IS ARRAY (natural RANGE <>) OF unsigned(NP-1 DOWNTO 0);
  SIGNAL o_linem,o_line0,o_line1,o_line2 : arr_pix(0 TO OHRES-1);
  ATTRIBUTE ramstyle OF o_linem : SIGNAL IS "no_rw_check";
  ATTRIBUTE ramstyle OF o_line0 : SIGNAL IS "no_rw_check";
  ATTRIBUTE ramstyle OF o_line1 : SIGNAL IS "no_rw_check";
  ATTRIBUTE ramstyle OF o_line2 : SIGNAL IS "no_rw_check";
  SIGNAL o_wadl,o_radl : natural RANGE 0 TO OHRES-1;
  SIGNAL o_ldw,o_ldrm,o_ldr0,o_ldr1,o_ldr2 : unsigned(NP-1 DOWNTO 0);
  SIGNAL o_wr : unsigned(3 DOWNTO 0);
  SIGNAL o_hcpt,o_vcpt     : natural RANGE 0 TO 4095;
  SIGNAL o_hdelta,o_vdelta : unsigned(23 DOWNTO 0);
  SIGNAL o_ihsize,o_ivsize : natural RANGE 0 TO 4095;
  SIGNAL o_hdivi,o_vdivi   : unsigned(11 DOWNTO 0);
  SIGNAL o_hdivr,o_vdivr   : unsigned(35 DOWNTO 0);
  
  SIGNAL o_hpos,o_hpos_next : unsigned(23 DOWNTO 0);
  SIGNAL o_vpos,o_vpos2,o_vpos_pre : unsigned(23 DOWNTO 0); -- [23:12].[11.0]
  SIGNAL o_hpos1,o_hpos2,o_hpos3 : unsigned(23 DOWNTO 0);
  SIGNAL xxx_vposi : natural RANGE 0 TO 4095;
  SIGNAL o_hsp,o_hs0,o_hs1,o_hs2,o_hs3,o_hs4,o_hs5,o_hs6 : std_logic;
  SIGNAL o_vsp,o_vs0,o_vs1,o_vs2,o_vs3,o_vs4,o_vs5,o_vs6 : std_logic;
  SIGNAL o_de0,o_de1,o_de2,o_de3,o_de4,o_de5,o_de6 : std_logic;
  SIGNAL o_pe0,o_pe1,o_pe2,o_pe3,o_pe4,o_pe5,o_pe6 : std_logic;
  SIGNAL o_read,o_read_pre : std_logic;
  SIGNAL o_readlev,o_copylev : natural RANGE 0 TO 2;
  SIGNAL o_hburst,o_hbcpt : natural RANGE 0 TO 31;
  SIGNAL o_fload : natural RANGE 0 TO 3;
  SIGNAL o_acpt : natural RANGE 0 TO 15; -- Alternance pixels FIFO
  SIGNAL o_dshi : natural RANGE 0 TO 3;
  SIGNAL o_alt,o_altp,o_alt1,o_alt2,o_alt3,o_alt4,o_alt5 : unsigned(3 DOWNTO 0);
  SIGNAL o_altv : unsigned(0 TO 11);
  SIGNAL o_primv,o_lastv ,o_bibv : unsigned(0 TO 2);
  SIGNAL o_bibu,o_bib : std_logic :='0';
  SIGNAL o_dcpt,o_dcpt1,o_dcpt2,o_dcpt3,o_dcpt4,o_dcpt5 : natural RANGE 0 TO OHRES-1;
  TYPE type_pix IS RECORD
    r,g,b : unsigned(7 DOWNTO 0); -- 0.8
  END RECORD;
  
  SIGNAL o_hpixm,o_hpix0,o_hpix1,o_hpix2 : type_pix;
  SIGNAL o_hpix01,o_hpix02,o_hpix03,o_hpix11,o_hpix12,o_hpix13 : type_pix;
  SIGNAL o_hpixm1,o_hpix21 : type_pix;
  SIGNAL o_vpixm2,o_vpix02,o_vpix12,o_vpix22 : type_pix;
  SIGNAL o_vpixm3,o_vpix03,o_vpix13,o_vpix23 : type_pix;
  SIGNAL o_vpix04,o_vpix14 : type_pix;

  SIGNAL o_ivs,o_ivs2,o_ivs3,o_isyncline,o_isyncline2,o_ouda,o_oudb : std_logic;
  SIGNAL o_icnt,o_ocnt : natural RANGE 0 TO 4096*4096-1;
  SIGNAL o_osdelay,o_idelay,o_isdelay,o_dsync : natural RANGE 0 TO 4096*4096-1;
  
  -----------------------------------------------------------------------------
  FUNCTION altx (a : unsigned(1 DOWNTO 0)) RETURN unsigned IS
  BEGIN
    CASE a IS
      WHEN "00" => RETURN "0001";
      WHEN "01" => RETURN "0010";
      WHEN "10" => RETURN "0100";
      WHEN OTHERS => RETURN "1000";
    END CASE;
  END FUNCTION;     
  -----------------------------------------------------------------------------
  FUNCTION bound(a : unsigned;
                 s : natural) RETURN unsigned IS
  BEGIN
    IF a(a'left)='1' THEN
      RETURN x"00";
    ELSIF a(a'left DOWNTO s)/=0 THEN
      RETURN x"FF";
    ELSE
      RETURN a(s-1 DOWNTO s-8);
    END IF;
  END FUNCTION bound;
  -----------------------------------------------------------------------------
  -- Nearest
  FUNCTION near_frac(f : unsigned(11 DOWNTO 0)) RETURN unsigned IS
    VARIABLE x : unsigned(FRAC-1 DOWNTO 0);
  BEGIN
    x:=(OTHERS =>f(11));
    RETURN x;
  END FUNCTION;
  
  SIGNAL o_h_frac,o_v_frac : unsigned(FRAC-1 DOWNTO 0);
  SIGNAL o_h_frac2,o_h_fracn2,o_v_frac2,o_v_fracn2 : unsigned(FRAC DOWNTO 0);
  SIGNAL o_h_bil_pix,o_v_bil_pix : type_pix;
  
  -----------------------------------------------------------------------------
  -- Nearest + Bilinear + Sharp Bilinear
  FUNCTION bil_frac(f : unsigned(11 DOWNTO 0)) RETURN unsigned IS
    VARIABLE x : unsigned(FRAC-1 DOWNTO 0);
  BEGIN
    x:=f(11 DOWNTO 12-FRAC);
    RETURN x;
  END FUNCTION;
  
  FUNCTION bil_calc(f :unsigned(FRAC DOWNTO 0);
                    g :unsigned(FRAC DOWNTO 0);
                    p0,p1 : type_pix) RETURN type_pix IS
    VARIABLE u : unsigned(8+FRAC DOWNTO 0);
    VARIABLE x : type_pix;
    CONSTANT Z : unsigned(FRAC-1 DOWNTO 0):=(OTHERS =>'0');
  BEGIN
    u:=p1.r * f + p0.r * g;
    x.r:=bound(u,8+FRAC);
    u:=p1.g * f + p0.g * g;
    x.g:=bound(u,8+FRAC);
    u:=p1.b * f + p0.b * g;
    x.b:=bound(u,8+FRAC);
    RETURN x;
  END FUNCTION;
  
  -----------------------------------------------------------------------------
  -- Sharp Bilinear
  --  <0.5 : x*x*x*4
  --  >0.5 : 1 - (1-x)*(1-x)*(1-x)*4

  TYPE type_sbil_tt IS RECORD
    f : unsigned(FRAC-1 DOWNTO 0);
    s : unsigned(FRAC-1 DOWNTO 0);
  END RECORD;
  
  SIGNAL o_h_sbil_t,o_v_sbil_t : type_sbil_tt;
  
  FUNCTION sbil_frac1(f : unsigned(11 DOWNTO 0)) RETURN type_sbil_tt IS
    VARIABLE u : unsigned(FRAC-1 DOWNTO 0);
    VARIABLE v : unsigned(2*FRAC-1 DOWNTO 0);
    VARIABLE x : type_sbil_tt;
  BEGIN
    IF f(11)='0' THEN
      u:=f(11 DOWNTO 12-FRAC);
    ELSE
      u:=NOT f(11 DOWNTO 12-FRAC);
    END IF;
    v:=u*u;
    x.f:=u;
    x.s:=v(2*FRAC-2 DOWNTO FRAC-1);
    RETURN x;  
  END FUNCTION;
  
  FUNCTION sbil_frac2(f : unsigned(11 DOWNTO 0);
                      t : type_sbil_tt) RETURN unsigned IS
    VARIABLE v : unsigned(2*FRAC-1 DOWNTO 0);
  BEGIN
    v:=t.f*t.s;
    IF f(11)='0' THEN
      RETURN v(2*FRAC-2 DOWNTO FRAC-1);
    ELSE
      RETURN NOT v(2*FRAC-2 DOWNTO FRAC-1);
    END IF;
  END FUNCTION;
  
  -----------------------------------------------------------------------------
  -- Bicubic
  TYPE type_bic_abcd IS RECORD
    a : unsigned(7 DOWNTO 0);  --  0.8
    b : signed(8 DOWNTO 0);  --  0.9
    c : signed(11 DOWNTO 0); -- 3.9
    d : signed(10 DOWNTO 0); -- 2.9
  END RECORD;
  TYPE type_bic_pix_abcd IS RECORD
    r,g,b : type_bic_abcd;
  END RECORD;
  
  TYPE type_bic_tt1 IS RECORD -- Intermediate result
    r,g,b : signed(9 DOWNTO 0);
  END RECORD;
  TYPE type_bic_tt2 IS RECORD -- Intermediate result
    r,g,b : signed(12 DOWNTO 0); -- 4.9
  END RECORD;
  
  SIGNAL o_h_bic_pix,o_v_bic_pix : type_pix;
  SIGNAL o_h_bic_abcd1,o_h_bic_abcd2,o_h_bic_abcd3 : type_bic_pix_abcd;
  SIGNAL o_v_bic_abcd1,o_v_bic_abcd2,o_v_bic_abcd3 : type_bic_pix_abcd;
  SIGNAL o_h_bic_tt1,o_v_bic_tt1 : type_bic_tt1;
  SIGNAL o_h_bic_tt2,o_v_bic_tt2 : type_bic_tt2;
  
  -- Y = A + B.X + C.X.X + D.X.X.X = A + X.(B + X.(C + X.D))
  -- A = Y(0)                                       0 .. 1    unsigned
  -- B = Y(1)/2 - Y(-1)/2                        -1/2 .. +1/2 signed
  -- C = Y(-1) - 5*Y(0)/2 + 2*Y(1) - Y(2)/2        -3 .. +3   signed
  -- D = -Y(-1)/2 + 3*Y(0)/2 - 3*Y(1)/2 + Y(2)/2   -2 .. +2   signed

  FUNCTION bic_coeff(pm,p0,p1,p2 : unsigned(7 DOWNTO 0)) RETURN type_bic_abcd IS
  BEGIN
    RETURN type_bic_abcd'(
      a=>p0,-- 0.8
      b=>signed(('0' & p1) - ('0' & pm)), -- 0.9
      c=>signed(("000" & pm & '0') - ("00" & p0 & "00") - ("0000" & p0) +
                ("00" & p1 & "00") - ("0000" & p2)), -- 3.9
      d=>signed(("00" & p0 & '0') - ("00" & p1 & '0') - ("000" & p1) +
                ("000" & p0) + ("000" & p2) - ("000" & pm))); -- 2.9
  END FUNCTION;
  FUNCTION bic_coef(pm,p0,p1,p2 : type_pix) RETURN type_bic_pix_abcd IS
  BEGIN
    RETURN type_bic_pix_abcd'(r=>bic_coeff(pm.r,p0.r,p1.r,p2.r),
                              g=>bic_coeff(pm.g,p0.g,p1.g,p2.g),
                              b=>bic_coeff(pm.b,p0.b,p1.b,p2.b));
  END FUNCTION;

  ----------------------------------------------------------
  -- T1 = X.D + C
  FUNCTION bic_calc1(f    : unsigned(11 DOWNTO 0);
                     abcd : type_bic_pix_abcd) RETURN type_bic_tt1 IS
    VARIABLE u : signed(11+FRAC DOWNTO 0);
    VARIABLE v : signed(12+FRAC DOWNTO 0);
    VARIABLE x : type_bic_tt1;
    CONSTANT Z : signed(FRAC-1 DOWNTO 0):=(OTHERS =>'0');
  BEGIN
    u:=abcd.r.d * signed('0' & f(11 DOWNTO 12-FRAC)); -- 2.9 * 1.FRAC = 3.(9+FRAC) -> 2.(9+FRAC)
    v:=(u(11+FRAC) & u) + (abcd.r.c(11) & abcd.r.c & Z); -- 4.(9+FRAC)
    x.r:=v(12+FRAC DOWNTO 3+FRAC);
    
    u:=abcd.g.d * signed('0' & f(11 DOWNTO 12-FRAC)); -- 2.9 * 1.FRAC = 3.(9+FRAC) -> 2.(9+FRAC)
    v:=(u(11+FRAC) & u) + (abcd.g.c(11) & abcd.g.c & Z); -- 4.(9+FRAC)
    x.g:=v(12+FRAC DOWNTO 3+FRAC);
    
    u:=abcd.b.d * signed('0' & f(11 DOWNTO 12-FRAC)); -- 2.9 * 1.FRAC = 3.(9+FRAC) -> 2.(9+FRAC)
    v:=(u(11+FRAC) & u) + (abcd.b.c(11) & abcd.b.c & Z); -- 4.(9+FRAC)
    x.b:=v(12+FRAC DOWNTO 3+FRAC); -- 4.6
    RETURN x;
  END FUNCTION;
  
  ----------------------------------------------------------
  -- T2 = X.T1 + B
  FUNCTION bic_calc2(f    : unsigned(11 DOWNTO 0);
                     t    : type_bic_tt1;
                     abcd : type_bic_pix_abcd) RETURN type_bic_tt2 IS
    VARIABLE w : signed(10+FRAC DOWNTO 0);
    VARIABLE x : type_bic_tt2;
  BEGIN
    w:=t.r * signed('0' & f(11 DOWNTO 12-FRAC)); -- 5.(6+FRAC)
    x.r:=w(9+FRAC DOWNTO FRAC-3) +
          (abcd.r.b(8) & abcd.r.b(8) & abcd.r.b(8) & abcd.r.b(8) & abcd.r.b); -- 4.9
    
    w:=t.g * signed('0' & f(11 DOWNTO 12-FRAC)); -- 5.(6+FRAC)
    x.g:=w(9+FRAC DOWNTO FRAC-3) +
          (abcd.g.b(8) & abcd.g.b(8) & abcd.g.b(8) & abcd.g.b(8) & abcd.g.b); -- 4.9
    
    w:=t.b * signed('0' & f(11 DOWNTO 12-FRAC)); -- 5.(6+FRAC)
    x.b:=w(9+FRAC DOWNTO FRAC-3) +
          (abcd.b.b(8) & abcd.b.b(8) & abcd.b.b(8) & abcd.b.b(8) & abcd.b.b); -- 4.9
    RETURN x;
  END FUNCTION;
  
  ----------------------------------------------------------
  -- Y = X.T2 + A
  FUNCTION bic_calc3(f : unsigned(11 DOWNTO 0);
                     t : type_bic_tt2;
                     abcd : type_bic_pix_abcd) RETURN type_pix IS
    VARIABLE v : signed(13+FRAC DOWNTO 0); -- 5.(9+FRAC)
    VARIABLE x : type_pix;
    CONSTANT Z : signed(FRAC-1 DOWNTO 0):=(OTHERS =>'0');
  BEGIN
    v:=signed('0' & f(11 DOWNTO 12-FRAC))*t.r +("00000" & signed(abcd.r.a) & '0' & Z); -- 1.FRAC * 4.9 = 5.(FRAC+9)
    x.r:=bound(unsigned(v),9+FRAC);
    
    v:=signed('0' & f(11 DOWNTO 12-FRAC))*t.g +("00000" & signed(abcd.g.a) & '0' & Z); -- 1.FRAC * 4.9 = 5.(FRAC+9)
    x.g:=bound(unsigned(v),9+FRAC);
    
    v:=signed('0' & f(11 DOWNTO 12-FRAC))*t.b +("00000" & signed(abcd.b.a) & '0' & Z); -- 1.FRAC * 4.9 = 5.(FRAC+9)
    x.b:=bound(unsigned(v),9+FRAC);
    RETURN x;
  END FUNCTION;
  
  -----------------------------------------------------------------------------
  -- Polyphase
  
  TYPE arr_uv36 IS ARRAY (natural RANGE <>) OF unsigned(35 DOWNTO 0); -- 9/9/9/9
  TYPE arr_integer IS ARRAY (natural RANGE <>) OF integer;
  CONSTANT POLY16_A : arr_integer := (
    -24,-20,-16,-11,-6,-1,2,5,6,6,5,4,2,1,0,0,
    176,174,169,160,147,129,109,84,58,22,3,-12,-20,-25,-26,-25,
    -24,-26,-26,-23,-16,-4,11,32,58,96,119,140,154,165,172,175,
    0,0,1,2,3,4,6,7,6,4,1,-4,-8,-13,-18,-22);
  
  CONSTANT POLY16_B : arr_integer := (
    0,-4,-8,-10,-11,-11,-10,-9,-8,-6,-4,-3,-2,-1,0,0,
    127,126,124,119,111,103,93,82,72,56,45,35,25,17,9,3,
    0,6,13,20,30,40,50,61,72,88,98,107,115,121,125,127,
    0,0,-1,-1,-2,-4,-5,-6,-8,-10,-11,-11,-10,-9,-6,-2);

  CONSTANT POLY32_A : arr_integer := (
    -24,-22,-20,-18,-16,-13,-11,-8,-6,-3,-1,0,2,3,5,5,6,6,6,5,5,4,4,3,2,1,1,0,0,0,0,0,
    176,175,174,172,169,164,160,153,147,138,129,119,109,96,84,71,58,40,22,12,3,-4,-12,-16,-20,-22,-25,-25,-26,-25,-25,-25,
    -24,-25,-26,-26,-26,-24,-23,-19,-16,-10,-4,4,11,22,32,45,58,77,96,108,119,129,140,147,154,159,165,168,172,173,175,175,
    0,0,0,0,1,1,2,2,3,3,4,5,6,7,7,7,6,5,4,3,1,-1,-4,-6,-8,-10,-13,-15,-18,-20,-22,-22);
  
  CONSTANT POLY32_B : arr_integer := (
    0,-2,-4,-6,-8,-9,-10,-10,-11,-11,-11,-10,-10,-9,-9,-8,-8,-7,-6,-5,-4,-3,-3,-2,-2,-1,-1,0,0,0,0,0,
    128,127,126,125,124,121,119,115,111,107,103,98,93,87,82,77,72,64,56,50,45,40,35,30,25,21,17,13,9,6,3,3,
    0,3,6,9,13,17,20,25,30,35,40,45,50,55,61,66,72,80,88,93,98,102,107,111,115,118,121,123,125,126,127,127,
    0,0,0,0,-1,-1,-1,-2,-2,-3,-4,-5,-5,-5,-6,-7,-8,-9,-10,-10,-11,-11,-11,-11,-10,-10,-9,-8,-6,-4,-2,-2);
  
  FUNCTION gen_poly(n : natural) RETURN arr_uv36 IS
    VARIABLE p16 : arr_integer(0 TO 4*16-1);
    VARIABLE p32 : arr_integer(0 TO 4*32-1);
    VARIABLE m : arr_uv36(0 TO 2**FRAC-1) :=(OTHERS =>x"000000000");
  BEGIN
    IF FRAC=4 THEN
      IF n=0 THEN p16:=POLY16_A; ELSE p16:=POLY16_B; END IF;
      FOR i IN 0 TO 15 LOOP
        m(i):=unsigned(to_signed(p16(i),9) & to_signed(p16(i+16),9) &
                       to_signed(p16(i+32),9) & to_signed(p16(i+48),9));
      END LOOP;
    ELSIF FRAC=5 THEN
      IF n=0 THEN p32:=POLY32_A; ELSE p32:=POLY32_B; END IF;
      FOR i IN 0 TO 31 LOOP
        m(i):=unsigned(to_signed(p32(i),9) & to_signed(p32(i+32),9) &
                       to_signed(p32(i+64),9) & to_signed(p32(i+96),9));
      END LOOP;
    END IF;
    RETURN m;
  END FUNCTION;

  FUNCTION init_poly RETURN arr_uv36 IS
    VARIABLE p : arr_uv36(0 TO 2**(FRAC+NPOLY)-1);
  BEGIN
    FOR i IN 0 TO 2**NPOLY-1 LOOP
      p(i*2**FRAC TO (i+1)*2**FRAC-1):=gen_poly(i MOD 2);
    END LOOP;
    RETURN p;
  END;
  
  SIGNAL o_h_poly : arr_uv36(0 TO 2**(FRAC+NPOLY)-1):=init_poly;
  SIGNAL o_v_poly : arr_uv36(0 TO 2**(FRAC+NPOLY)-1):=init_poly;
  ATTRIBUTE ramstyle OF o_h_poly : SIGNAL IS "no_rw_check";
  ATTRIBUTE ramstyle OF o_v_poly : SIGNAL IS "no_rw_check";
  SIGNAL o_h_poly_a,o_v_poly_a : integer RANGE 0 TO 2**(FRAC+NPOLY)-1;
  SIGNAL o_h_poly_dr,o_v_poly_dr : unsigned(35 DOWNTO 0);
  SIGNAL o_h_poly_pix,o_v_poly_pix : type_pix;
  SIGNAL poly_h_wr,poly_v_wr : std_logic;
  SIGNAL poly_tdw : unsigned(35 DOWNTO 0);
  SIGNAL poly_a2 : unsigned(FRAC+NPOLY-1 DOWNTO 0);
  
  TYPE type_poly_t IS RECORD
    r0,r1,b0,b1,g0,g1 : signed(17 DOWNTO 0);
  END RECORD;
  TYPE type_poly_t2 IS RECORD
    r,g,b : unsigned(17 DOWNTO 0);
  END RECORD;
  
  SIGNAL o_h_poly_t,o_v_poly_t   : type_poly_t;
  SIGNAL o_h_poly_t2,o_v_poly_t2 : type_poly_t2;
  
  FUNCTION poly_calc1(fi : unsigned(35 DOWNTO 0);
                     pm,p0,p1,p2 : type_pix) RETURN type_poly_t IS
    VARIABLE t : type_poly_t;
  BEGIN
    -- 2.7 * 1.8 = 3.15 
    t.r0:=(signed(fi(35 DOWNTO 27)) * signed('0' & pm.r) +
           signed(fi(26 DOWNTO 18)) * signed('0' & p0.r));
    t.r1:=(signed(fi(17 DOWNTO  9)) * signed('0' & p1.r) +
           signed(fi( 8 DOWNTO  0)) * signed('0' & p2.r));
    t.g0:=(signed(fi(35 DOWNTO 27)) * signed('0' & pm.g) +
           signed(fi(26 DOWNTO 18)) * signed('0' & p0.g));
    t.g1:=(signed(fi(17 DOWNTO  9)) * signed('0' & p1.g) +
           signed(fi( 8 DOWNTO  0)) * signed('0' & p2.g));
    t.b0:=(signed(fi(35 DOWNTO 27)) * signed('0' & pm.b) +
           signed(fi(26 DOWNTO 18)) * signed('0' & p0.b));
    t.b1:=(signed(fi(17 DOWNTO  9)) * signed('0' & p1.b) +
           signed(fi( 8 DOWNTO  0)) * signed('0' & p2.b));
    RETURN t;
  END FUNCTION;
  
  FUNCTION poly_calc2(pt : type_poly_t) RETURN type_poly_t2 IS
    VARIABLE p : type_poly_t2;
    VARIABLE t : signed(17 DOWNTO 0); -- 3.15
  BEGIN
    p.r:=unsigned(pt.r0+pt.r1);
    p.g:=unsigned(pt.g0+pt.g1);
    p.b:=unsigned(pt.b0+pt.b1);
    RETURN p;
  END FUNCTION;
  
  FUNCTION poly_calc3(t : type_poly_t2) RETURN type_pix IS
    VARIABLE p : type_pix;
  BEGIN
    p.r:=bound(unsigned(t.r),15);
    p.g:=bound(unsigned(t.g),15);
    p.b:=bound(unsigned(t.b),15);
    RETURN p;
  END FUNCTION;
  
  -----------------------------------------------------------------------------
  -- DEBUG

  SIGNAL o_debug_read : integer RANGE 0 TO 255;
  SIGNAL o_debug_set : std_logic;
  SIGNAL o_debug_col : unsigned(7 DOWNTO 0);
  SIGNAL o_debug_vin0 : unsigned(0 TO 32*5-1) :=(OTHERS =>'0');
  SIGNAL o_debug_vin1 : unsigned(0 TO 32*5-1) :=(OTHERS =>'0');
  SIGNAL o_hcpt2,o_hcpt3 : natural RANGE 0 TO 4095;
  SIGNAL o_debug_char : unsigned(4 DOWNTO 0);
  
  FUNCTION CC(i : character) RETURN unsigned IS
  BEGIN
    CASE i IS
      WHEN '0' => RETURN "00000";
      WHEN '1' => RETURN "00001";
      WHEN '2' => RETURN "00010";
      WHEN '3' => RETURN "00011";
      WHEN '4' => RETURN "00100";
      WHEN '5' => RETURN "00101";
      WHEN '6' => RETURN "00110";
      WHEN '7' => RETURN "00111";
      WHEN '8' => RETURN "01000";
      WHEN '9' => RETURN "01001";
      WHEN 'A' => RETURN "01010";
      WHEN 'B' => RETURN "01011";
      WHEN 'C' => RETURN "01100";
      WHEN 'D' => RETURN "01101";
      WHEN 'E' => RETURN "01110";
      WHEN 'F' => RETURN "01111";
      WHEN ' ' => RETURN "10000";
      WHEN '=' => RETURN "10001";
      WHEN '+' => RETURN "10010";
      WHEN '-' => RETURN "10011";
      WHEN '<' => RETURN "10100";
      WHEN '>' => RETURN "10101";
      WHEN '^' => RETURN "10110";
      WHEN 'v' => RETURN "10111";
      WHEN '(' => RETURN "11000";
      WHEN ')' => RETURN "11001";
      WHEN ':' => RETURN "11010";
      WHEN '.' => RETURN "11011";
      WHEN ',' => RETURN "11100";
      WHEN '?' => RETURN "11101";
      WHEN '|' => RETURN "11110";
      WHEN '#' => RETURN "11111";
      WHEN OTHERS => RETURN "10000";
    END CASE;
  END FUNCTION CC;
  FUNCTION CS(s : string) RETURN unsigned IS
    VARIABLE r : unsigned(0 TO s'length*5-1);
    VARIABLE j : natural :=0;
  BEGIN
    FOR i IN s'RANGE LOOP
      r(j TO j+4) :=CC(s(i));
      j:=j+5;
    END LOOP;
    RETURN r;
  END FUNCTION CS;
  FUNCTION CN(v : unsigned) RETURN unsigned IS
    VARIABLE t : unsigned(0 TO v'length-1);
    VARIABLE o : unsigned(0 TO v'length/4*5-1);
  BEGIN
    t:=v;
    FOR i IN 0 TO v'length/4-1 LOOP
      o(i*5 TO i*5+4):='0' & t(i*4 TO i*4+3);
    END LOOP;
    RETURN o;
  END FUNCTION CN;
  
BEGIN

  i_reset_na<='0'   WHEN reset_na='0' ELSE '1' WHEN rising_edge(i_clk);
  o_reset_na<='0'   WHEN reset_na='0' ELSE '1' WHEN rising_edge(o_clk);
  avl_reset_na<='0' WHEN reset_na='0' ELSE '1' WHEN rising_edge(avl_clk);
  
  -----------------------------------------------------------------------------
  -- Input pixels FIFO and shreg
  InAT:PROCESS(i_clk,i_reset_na) IS
  BEGIN
    IF i_reset_na='0' THEN
      i_write<='0';
      
    ELSIF rising_edge(i_clk) THEN
      i_push<='0';
      i_run <= run; -- <ASYNC>
      i_iauto<=iauto; -- <ASYNC> ?

      --------------------------------
      i_head(127 DOWNTO 120)<=x"01"; -- Header type
      i_head(119 DOWNTO 112)<=x"01"; -- 24bits/pixels, packed RGB, big endian
      i_head(111 DOWNTO 96)<=to_unsigned(N_BURST,16); -- Header size
      i_head(95 DOWNTO 80)<=x"0000"; -- Attributes. TBD
      i_head(79 DOWNTO 64)<=to_unsigned(i_hsize+1,16); -- Image width
      i_head(63 DOWNTO 48)<=to_unsigned(i_vsize+1,16); -- Image height
      i_head(47 DOWNTO 32)<=
        to_unsigned(N_BURST * i_hburst,16); -- Line Length. Bytes
      i_head(31 DOWNTO 16)<=x"0000"; -- TBD
      i_head(15 DOWNTO  0)<=x"0000"; -- TBD
      
      IF i_ce='1' THEN
        --------------------------------
        i_hs_pre<=i_hs;
        i_vs_pre<=i_vs;
        i_de_pre<=i_de;
        i_fl_pre<=i_fl;
        
        --------------------------------
        -- Detect interleaved video
        IF NOT INTER THEN
          i_intercnt<=0;
        ELSIF i_fl/=i_fl_pre AND i_intercnt<4 THEN
          i_intercnt<=i_intercnt+2;
        ELSIF i_vs='1' AND i_vs_pre='0' AND i_intercnt>0 THEN
          i_intercnt<=i_intercnt-1;
        END IF;
        i_inter<=to_std_logic(i_intercnt>0);
        
        --------------------------------
        IF i_hs='1' THEN
          i_hcpt<=0;
        ELSE
          i_hcpt<=i_hcpt+1;
        END IF;
        
        IF i_vs='1' THEN
          i_vcpt<=0;
          IF i_inter='1' AND i_fl='1' AND INTER THEN
            i_adrsi<=to_unsigned(N_BURST * i_hburst,32);
          ELSE
            i_adrsi<=(OTHERS =>'0');
          END IF;
          i_vminset<='0';
          i_wad_pre<=0;
        END IF;
        
        IF i_hs='1' AND i_hs_pre='0' THEN
          i_vcpt<=i_vcpt+1;
        END IF;
        
        i_ven<=to_std_logic(i_hcpt+1>=i_hmin AND i_hcpt+1<=i_rhmax AND
                            i_vcpt>=i_vmin AND i_vcpt<=i_vmax AND
                            i_hs='0' AND i_vs='0');

        i_syncline<=to_std_logic(i_vcpt=i_vmin + 3);
        
        ----------------------------------------------------
        -- Auto-sizing of the input image
        IF i_de='1' AND i_de_pre='0' THEN
          i_chmin<=i_hcpt+1;
        END IF;
        IF i_de='0' AND i_de_pre='1' THEN
          i_chmax<=i_hcpt;
        END IF;
        
        IF i_de='1' AND i_de_pre='0' AND i_vminset='0' THEN
          i_cvmin<=i_vcpt;
          i_vminset<='1';
        END if;
        IF i_de='1' AND i_de_pre='0' THEN
          i_vmaxc<=i_vcpt;
        END IF;
        IF i_vs='1' THEN
          i_cvmax<=i_vmaxc;
        END IF;
        
        IF i_iauto='1' THEN
          i_hmin<=i_chmin;
          i_hmax<=i_chmax;
          i_vmin<=i_cvmin;
          i_vmax<=i_cvmax;
        ELSE
          -- Forced image
          i_hmin<=himin+i_chmin;
          i_hmax<=himax+i_chmin;
          i_vmin<=vimin+i_cvmin;
          i_vmax<=vimax+i_cvmin;
        END IF;
        
        i_rhmax<=(i_hmax/16)*16 + 15 + (i_hmin MOD 16); -- Rounded to 16 pixels
        
        -- VS   __/""""\_______________________
        -- DE   _________________/"""""""\_____
        -- VCPT   0 0 0 1 2 3 4 5 6 7 8 9 10
        --   VMIN = 6 VMAX=9
        
        -- HS   __/""""\_______________________
        -- DE   _________________/"""""""\_____
        -- HCPT   0 0 0 1 2 3 4 5 6 7 8 9 10
        --   HMIN = 6 HMAX=409
        i_hsize<=(4096+i_hmax-i_hmin) MOD 4096;
        
        IF i_inter='0' THEN
          i_vsize<=(4096+i_vmax-i_vmin) MOD 4096;
        ELSE
          -- Interlaced : Double image height
          i_vsize<=2*((4096+i_vmax-i_vmin) MOD 4096) +1;
        END IF;
        
        ----------------------------------------------------
        -- Assemble pixels
        -- ACPT
        --  0 ...............RGB
        --  1 ............RGBrgb
        --  2 .........RGBrgbRGB
        --  3 ......RGBrgbRGBrgb
        --  4 ...RGBrgbRGBrgbRGB
        --  5 RGBrgbRGBrgbRGBrgb => PUSH RGBrgbRGBrgbRGBr
        --  6 .............gbRGB
        --  7 ..........gbRGBrgb
        --  8 .......gbRGBrgbRGB
        --  9 ....gbRGBrgbRGBrgb
        -- 10 .gbRGBrgbRGBrgbRGB => PUSH gbRGBrgbRGBrgbRG
        -- 11 ..............Brgb
        -- 12 ...........BrgbRGB
        -- 13 ........BrgbRGBrgb
        -- 14 .....BrgbRGBrgbRGB
        -- 15 ..BrgbRGBrgbRGBrgb => PUSH BrgbRGBrgbRGBrgb
        
        i_rgb<=i_r & i_g & i_b;
        i_shift<=i_shift(24 TO 119) & i_rgb;
        IF i_acpt=5 THEN
          i_dw<=i_shift(0 TO 119) & i_rgb(23 DOWNTO 16);
        ELSIF i_acpt=10 THEN
          i_dw<=i_shift(8 TO 119) & i_rgb(23 DOWNTO 8);
        ELSIF i_acpt=15 THEN
          i_dw<=i_shift(16 TO 119) & i_rgb;
        ELSIF i_pushhead='1' THEN
          i_dw<=i_head;
        END IF;
        
        IF i_ven='1' THEN
          i_acpt<=(i_acpt+1) MOD 16;
          IF i_acpt=5 OR i_acpt=10 OR i_acpt=15 OR i_pushhead='1' THEN
            i_push<='1';
          END IF;
          i_pushhead<='0';
        END IF;
        
        IF i_hs='1' AND i_hs_pre='0' THEN
          i_acpt<=0;
        END IF;
        
        IF i_vs='0' AND i_vs_pre='1' THEN
          -- Push header :
          -- 1) Trigger write to dual port memory
          -- 2) Trigger AVALON write burst
          -- 3) Skip "number of bursts per line" for this line.
          i_pushhead<=to_std_logic(HEADER);
          i_pushhead2<=to_std_logic(HEADER);
          i_pushhead3<=to_std_logic(HEADER);
        END IF;
        
        IF i_hs='0' AND i_hs_pre='1' AND i_vs='0' THEN
          i_hbcpt<=0; -- Bursts per line counter
          IF i_hbcpt>0 AND i_pushhead3='0' THEN
            i_hburst<=i_hbcpt;
          END IF;
          IF i_hbcpt>0 THEN
            i_pushhead3<='0';
          END IF;
          IF i_wad_pre MOD BLEN/=0 THEN
            i_wad_pre<=((i_wad_pre/BLEN+1) MOD 2)*BLEN;
          END IF;
        END IF;
        
      END IF; -- IF i_ce='1'
      
      ------------------------------------------------------
      -- Push pixels to DPRAM
      i_wr<='0';
      IF i_push='1' AND i_run='1' THEN
        i_wr<='1';
        i_wad_pre<=(i_wad_pre+1) MOD (BLEN*2);
        IF (i_wad MOD BLEN=BLEN-1) OR i_ven='0' OR i_pushhead2='1' THEN
          IF i_pushhead2='1' THEN
            i_wad_pre<=0;
          END IF;
          i_pushhead2<='0';
          i_hbcpt<=(i_hbcpt+1) MOD 32;
          i_write<=NOT i_write;
          IF i_wad/BLEN=0 THEN
            i_walt<='0';
          ELSE
            i_walt<='1';
          END IF;
          i_adrs<=i_adrsi;
          i_adrsi<=i_adrsi+N_BURST;
          IF i_inter='1' AND i_ven='0' THEN
            -- Skip every other line for interlaced video
            i_adrsi<=i_adrsi + N_BURST * (i_hburst + 1);
          END IF;
        END IF;
      END IF;
      
      i_wad<=i_wad_pre;
      
    END IF;
  END PROCESS;

  -----------------------------------------------------------------------------
  -- DPRAM INPUT
  
  PROCESS (i_clk) IS
  BEGIN
    IF rising_edge(i_clk) THEN
      IF i_wr='1' THEN
        i_dpram(i_wad)<=i_dw;
      END IF;
    END IF;
  END PROCESS;
  
  avl_dr<=i_dpram(avl_rad_c) WHEN rising_edge(avl_clk);
  
  -----------------------------------------------------------------------------
  -- AVALON interface
  Avaloir:PROCESS(avl_clk,avl_reset_na) IS
    VARIABLE debug_write_inc,debug_write_dec : std_logic;
  BEGIN
    IF avl_reset_na='0' THEN
      avl_reading<='0';
      avl_state<=sIDLE;
      avl_write_sr<='0';
      avl_read_sr<='0';
      avl_readack<='0';
      
    ELSIF rising_edge(avl_clk) THEN
      ----------------------------------
      avl_mode<=mode; -- <ASYNC> ?
      avl_write_sync<=i_write; -- <ASYNC>
      avl_write_sync2<=avl_write_sync;
      avl_write_pulse<=avl_write_sync XOR avl_write_sync2;
      debug_write_inc:=avl_write_pulse;
      debug_write_dec:='0';
      avl_wadrs <=i_adrs AND (RAMSIZE - 1); -- <ASYNC>
      avl_walt  <=i_walt; -- <ASYNC>
      
      ----------------------------------
      avl_read_sync<=o_read; -- <ASYNC>
      avl_read_sync2<=avl_read_sync;
      avl_read_pulse<=avl_read_sync XOR avl_read_sync2;
      avl_rbib  <=o_bib;
      avl_radrs <=o_adrs AND (RAMSIZE - 1); -- <ASYNC>
      --------------------------------------------
      avl_i_vs_sync<=i_vs; -- <ASYNC>
      avl_i_vs<=avl_i_vs_sync;
      avl_o_vs_sync<=o_vs0; -- <ASYNC>
      avl_o_vs<=avl_o_vs_sync;

      avl_inter<=i_inter; -- <ASYNC>
      avl_fl   <=i_fl;    -- <ASYNC>
      
      --------------------------------------------
      -- Triple buffering.
      IF avl_i_vs_sync='1' AND avl_i_vs='0' AND
        (avl_inter='0' OR avl_fl='1') THEN
        -- Input : Frees framebuffer at begin of VSYNC pulse
        -- Waits for second frame of interleaved video
        avl_i_buf<=buf_next(avl_i_buf,avl_o_buf);
        avl_bufup<='1';
      END IF;
      
      IF avl_o_vs_sync='0' AND avl_o_vs='1' THEN
        -- Output : Pick new framebuffer at end of VSYNC pulse
        IF avl_bufup='1' THEN
          avl_o_buf<=buf_next(avl_o_buf,avl_i_buf);
          avl_bufup<='0';
        END IF;
      END IF;
      
      IF avl_mode(3)='0' THEN
        -- Triple buffer disabled
        avl_o_buf<=0;
        avl_i_buf<=0;
      END IF;
      avl_o_offset<=buf_offset(avl_o_buf);
      avl_i_offset<=buf_offset(avl_i_buf);
      
      --------------------------------------------
      avl_dw<=unsigned(avl_readdata);
      avl_read_i<='0';
      avl_write_i<='0';
      
      avl_write_sr<=(avl_write_sr OR avl_write_pulse) AND NOT avl_write_clr;
      avl_read_sr <=(avl_read_sr OR avl_read_pulse) AND NOT avl_read_clr;
      avl_write_clr<='0';
      avl_read_clr <='0';
      
      avl_rad<=avl_rad_c;
      
      --------------------------------------------
      CASE avl_state IS
        WHEN sIDLE =>
          IF avl_o_vs='0' AND avl_o_vs_sync='1' THEN
            avl_wad<=0;
          END IF;
          IF avl_write_sr='1' THEN
            avl_state<=sWRITE;
            avl_write_clr<='1';
            debug_write_dec:='1';
            IF avl_walt='0' THEN
              avl_rad<=0;
            ELSE
              avl_rad<=BLEN;
            END IF;
          ELSIF avl_read_sr='1' AND avl_reading='0' THEN
            IF avl_rbib='0' THEN
              avl_wad<=2*BLEN-1;
            ELSE
              avl_wad<=BLEN-1;
            END IF;
            avl_state<=sREAD;
            avl_read_clr<='1';
          END IF;
          
        WHEN sWRITE =>
          avl_address<=std_logic_vector(RAMBASE(N_AW+NB_LA-1 DOWNTO NB_LA) +
            avl_wadrs(N_AW+NB_LA-1 DOWNTO NB_LA) +
            avl_i_offset(N_AW+NB_LA-1 DOWNTO NB_LA));
          avl_write_i<='1';
          IF avl_write_i='1' AND avl_waitrequest='0' THEN
            IF (avl_rad MOD BLEN)=BLEN-1 THEN
              avl_write_i<='0';
              avl_state<=sIDLE;
            END IF;
          END IF;
          
        WHEN sREAD =>
          avl_address<=std_logic_vector(RAMBASE(N_AW+NB_LA-1 DOWNTO NB_LA) +
            avl_radrs(N_AW+NB_LA-1 DOWNTO NB_LA) +
            avl_o_offset(N_AW+NB_LA-1 DOWNTO NB_LA));
          avl_read_i<='1';
          avl_reading<='1';
          IF avl_read_i='1' AND avl_waitrequest='0' THEN
            avl_state<=sIDLE;
            avl_read_i<='0';
          END IF;
      END CASE;
      
      --------------------------------------------
      IF debug_write_inc='1' AND debug_write_dec='0' THEN
        avl_debug_write<=avl_debug_write+1;
      ELSIF debug_write_dec='1' AND debug_write_inc='0' THEN
        avl_debug_write<=avl_debug_write-1;
      END IF;
      
      --------------------------------------------
      -- Pipelined data read
      avl_wr<='0';
      IF avl_readdatavalid='1' THEN
        avl_wr<='1';
        avl_wad<=(avl_wad+1) MOD (2*BLEN);
        IF (avl_wad MOD BLEN)=BLEN-2 THEN
          avl_reading<='0';
          avl_readack<=NOT avl_readack;
        END IF;
      END IF;
      
      --------------------------------------------
    END IF;
  END PROCESS Avaloir;

  avl_read<=avl_read_i;
  avl_write<=avl_write_i;
  avl_writedata<=std_logic_vector(avl_dr);
  avl_burstcount<=std_logic_vector(to_unsigned(BLEN,8));
  avl_byteenable<=(OTHERS =>'1');
  
  avl_rad_c<=(avl_rad+1) MOD (2*BLEN)
              WHEN avl_write_i='1' AND avl_waitrequest='0' ELSE avl_rad;
  
  -----------------------------------------------------------------------------
  -- DPRAM OUTPUT
  PROCESS (avl_clk) IS
  BEGIN
    IF rising_edge(avl_clk) THEN
      IF avl_wr='1' THEN
        o_dpram(avl_wad)<=avl_dw;
      END IF;
    END IF;
  END PROCESS;
      
  o_dr<=o_dpram(o_ad) WHEN rising_edge(o_clk);

  -----------------------------------------------------------------------------
  -- Dividers
  o_ihsize<=i_hsize WHEN rising_edge(o_clk); -- <ASYNC>
  o_ivsize<=i_vsize WHEN rising_edge(o_clk); -- <ASYNC>
  
  --------------------------------------
  -- Hdelta = IHsize / (OHmax-OHmin)
  -- Vdelta = IVsize / (OVmax-OVmin)

  -- Division : 12 / 12 --> 12.12
  
  Dividers:PROCESS (o_clk,o_reset_na) IS
  BEGIN
    IF o_reset_na='0' THEN
      o_hdelta<=x"001000"; -- Simu !
      o_vdelta<=x"001000";
      
    ELSIF rising_edge(o_clk) THEN
      o_hdivi<=to_unsigned(o_hmax - o_hmin+1,12);
      o_vdivi<=to_unsigned(o_vmax - o_vmin+1,12);
      o_hdivr<=to_unsigned((o_ihsize+1) * 4096,36);
      o_vdivr<=to_unsigned((o_ivsize+1) * 4096,36);

      --------------------------------------------
      IF o_divstart='1' THEN
        o_divcpt<=0;
        o_divrun<='1';
        
      ELSIF o_divrun='1' THEN
        ------------------------------------------
        IF o_divcpt=24 THEN
          o_divrun<='0';
          o_hdelta<=o_hdivr(22 DOWNTO 0) & NOT o_hdivr(35);
          o_vdelta<=o_vdivr(22 DOWNTO 0) & NOT o_vdivr(35);
        ELSE
          o_divcpt<=o_divcpt+1;
        END IF;
        
        ------------------------------------------
        IF o_hdivr(35)='0' THEN
          o_hdivr(35 DOWNTO 24)<=o_hdivr(34 DOWNTO 23) - o_hdivi;
        ELSE
          o_hdivr(35 DOWNTO 24)<=o_hdivr(34 DOWNTO 23) + o_hdivi;
        END IF;
        o_hdivr(23 DOWNTO 0)<=o_hdivr(22 DOWNTO 0) & NOT o_hdivr(35);
        
        IF o_vdivr(35)='0' THEN
          o_vdivr(35 DOWNTO 24)<=o_vdivr(34 DOWNTO 23) - o_vdivi;
        ELSE
          o_vdivr(35 DOWNTO 24)<=o_vdivr(34 DOWNTO 23) + o_vdivi;
        END IF;
        o_vdivr(23 DOWNTO 0)<=o_vdivr(22 DOWNTO 0) & NOT o_vdivr(35);
        
        ------------------------------------------
      END IF;
    END IF;
  END PROCESS Dividers;
  
  -----------------------------------------------------------------------------
  Scalaire:PROCESS (o_clk,o_reset_na) IS
    VARIABLE mul_v : unsigned(47 DOWNTO 0);
    VARIABLE lev_inc_v,lev_dec_v : std_logic;
    VARIABLE debug_read_dec,debug_read_inc : std_logic;
    VARIABLE prim_v,last_v,bib_v : std_logic;
  BEGIN
    IF o_reset_na='0' THEN
      o_copy<='0';
      o_state<=sDISP;
      o_read_pre<='0';
      o_readlev<=0;
      o_copylev<=0;
      o_hsp<='0';
      
    ELSIF rising_edge(o_clk) THEN
      ------------------------------------------------------
      o_mode   <=mode; -- <ASYNC> ?
      o_run    <=run; -- <ASYNC> ?
      
      o_htotal <=htotal; -- <ASYNC> ?
      o_hsstart<=hsstart; -- <ASYNC> ?
      o_hsend  <=hsend; -- <ASYNC> ?
      o_hdisp  <=hdisp; -- <ASYNC> ?
      o_hmin   <=hmin; -- <ASYNC> ?
      o_hmax   <=hmax; -- <ASYNC> ?
      
      o_vtotal <=vtotal; -- <ASYNC> ?
      o_vsstart<=vsstart; -- <ASYNC> ?
      o_vsend  <=vsend; -- <ASYNC> ?
      o_vdisp  <=vdisp; -- <ASYNC> ?
      o_vmin   <=vmin; -- <ASYNC> ?
      o_vmax   <=vmax; -- <ASYNC> ?
      
      o_hburst<=i_hburst; -- <ASYNC> Bursts per line

      ------------------------------------------------------
      -- Test mode
      o_modex<=o_mode;
      IF o_mode(2 DOWNTO 0)="111" THEN
        o_modex(2 DOWNTO 0)<=to_unsigned((o_hcpt/128 + o_vcpt/128) MOD 8,3);
      END IF;
      
      ------------------------------------------------------
      -- End DRAM READ
      o_readack_sync<=avl_readack; -- <ASYNC>
      o_readack_sync2<=o_readack_sync;
      o_readack<=o_readack_sync XOR o_readack_sync2;
      debug_read_dec:=o_readack;
      debug_read_inc:='0';
      
      o_divstart<=o_vs1 AND NOT o_vs0;
      
      ------------------------------------------------------
      lev_inc_v:='0';
      lev_dec_v:='0';
      
      -- acpt : Pixel position within current data word
      -- dcpt : Destination image position
      -- hpos : Source image position, fixed point 12.12
      IF o_hs0='1' AND o_hs1='0' THEN
        o_hsp<='1';
        IF o_vcpt=o_vtotal-3 THEN
          o_vsp<='1';
        END IF;
      END IF;
      
      CASE o_state IS
          --------------------------------------------------
        WHEN sDISP =>
          IF o_hsp='1' THEN
            o_state<=sHSYNC;
            o_hsp<='0';
          END IF;
          IF o_vsp='1' THEN
            o_fload<=2; -- Force preload 3 lines at top of screen
            o_vsp<='0';
          END IF;
          
          --------------------------------------------------
        WHEN sHSYNC =>
          o_vpos_pre<=o_vpos;
          o_hbcpt<=0; -- Clear burst counter on line
          IF o_vpos(12)/=o_vpos_pre(12) OR o_fload>0 THEN
            o_state<=sREAD;
          ELSE
            o_state<=sDISP;
          END IF;
          
        WHEN sREAD =>
          -- Read a block
          IF o_readlev<2 THEN
            lev_inc_v:='1';
            o_read_pre<=NOT o_read_pre;
            debug_read_inc:='1';
            o_state <=sWAITREAD;
            o_bibu<=NOT o_bibu;
          END IF;
          prim_v:=to_std_logic(o_hbcpt=0);
          last_v:=to_std_logic(o_hbcpt=o_hburst-1);
          bib_v :=o_bibu;
          o_bib <=o_bibu;
          
          IF o_fload=2 THEN
            o_adrs_pre<=0;
            o_altp<="1111";
            
          ELSIF o_fload=1 THEN
            o_adrs_pre<=o_hburst;
            o_altp<="0100";
          ELSE
            o_adrs_pre<=to_integer(o_vpos(23 DOWNTO 12)) * o_hburst
                         + o_hburst;
            o_altp<=altx(o_vpos(13 DOWNTO 12) + 2);
          END IF;
          
        WHEN sWAITREAD =>
          IF o_readack='1' THEN
            o_hbcpt<=o_hbcpt+1;
            IF o_hbcpt<o_hburst-1 THEN
              -- If more lines to load, or not finshed line, read more
              o_state<=sREAD;
            ELSE
              o_state<=sDISP;
              IF o_fload>=1 THEN
                o_fload<=o_fload-1;
              END IF;
            END IF;
          END IF;
          
          --------------------------------------------------
      END CASE;
      
      IF HEADER THEN
        o_adrs<=to_unsigned((o_adrs_pre + o_hbcpt + 1) * N_BURST,32);
      ELSE
        o_adrs<=to_unsigned((o_adrs_pre + o_hbcpt) * N_BURST,32);
      END IF;
      o_read<=o_read_pre;
      
      IF debug_read_inc='1' AND debug_read_dec='0' THEN
        o_debug_read<=o_debug_read+1;
      ELSIF debug_read_dec='1' AND debug_read_inc='0' THEN
        o_debug_read<=o_debug_read-1;
      END IF;
      
      ------------------------------------------------------
      -- Copy from buffered memory to pixel lines
      IF o_copy='0' THEN
        IF o_copylev>0 AND o_copy1='0' THEN
          o_copy<='1';
        END IF;
        o_adturn<='0';
        
        IF o_acpt=0 THEN
          o_shift(0 TO 127)<=o_dr;
        ELSIF o_acpt=5 THEN
          o_shift(8 TO 135)<=o_dr;
        ELSIF o_acpt=10 THEN
          o_shift(16 TO 143)<=o_dr;
        END IF;
        
        IF o_primv(0)='1' THEN
          -- First memcopy of a horizontal line, carriage return !
          o_hpos<=x"000000";
          o_hpos_next<=o_hdelta;
          o_dcpt<=0;
          o_dshi<=2;
          o_acpt<=0;
        END IF;
        
        IF o_bibv(0)='0' THEN
          o_ad<=0;
        ELSE
          o_ad<=BLEN;
        END IF;
      ELSE
        IF o_dshi=0 THEN
          o_dcpt<=(o_dcpt+1) MOD OHRES;
          o_hpos_next<=o_hpos_next+o_hdelta;
          o_hpos<=o_hpos_next;
        ELSE
          -- dshi : Force shift first two pixels of each line
          o_dshi<=o_dshi-1;
        END IF;
        
        -- Pixels -1 / 0 / 1 / 2
        IF o_hpos(12)/=o_hpos_next(12) OR o_dshi>0 THEN
          o_hpixm<=o_hpix0;
          o_hpix0<=o_hpix1;
          o_hpix1<=o_hpix2;
          o_hpix2<=(r=>o_shift(0 TO 7),g=>o_shift(8 TO 15),b=>o_shift(16 TO 23));
          
          -- 0 >RGBrgbRGBrgbRGBr
          -- 1  rgbRGBrgbRGBr
          -- 2  RGBrgbRGBr
          -- 3  rgbRGBr
          -- 4  RGBr
          -- 5 >r gbRGBrgbRGBrgbRG
          -- 6  RGBrgbRGBrgbRG
          -- 7  rgbRGBrgbRG
          -- 8  RGBrgbRG
          -- 9  rgbRG
          -- 10>RG BrgbRGBrgbRGBrgb
          -- 11 rgbRGBrgbRGBrgb
          -- 12 RGBrgbRGBrgb
          -- 13 rgbRGBrgb
          -- 14 RGBrgb
          -- 15 rgb
          IF o_acpt=15 THEN
            o_shift(0 TO 127)<=o_dr;
          ELSIF o_acpt=4 THEN
            o_shift(0 TO 135)<=o_shift(24 TO 31) & o_dr;
          ELSIF o_acpt=9 THEN
            o_shift<=o_shift(24 TO 39) & o_dr;
          ELSE
            o_shift(0 TO 119)<=o_shift(24 TO 143);
          END IF;
          
          IF o_adturn='1' AND (o_acpt=15 OR o_acpt=9 OR o_acpt=4) AND
            (((o_ad MOD BLEN=0) AND o_lastv(0)='0') OR
             ((o_ad MOD BLEN=2) AND o_lastv(0)='1')) THEN
            o_copy<='0';
            lev_dec_v:='1';
            o_ad<=(o_ad/BLEN)*BLEN;
          END IF;
          IF o_acpt=2 OR o_acpt=7 OR o_acpt=12 THEN
            o_ad<=(o_ad+1) MOD (2*BLEN);
          END IF;
          IF o_ad MOD BLEN=4 THEN
            o_adturn<='1';
          END IF;
          
          o_acpt<=(o_acpt+1) MOD 16;
        END IF;
      END IF;
      
      ------------------------------------------------------
      -- READLEV : Number of ongoing Avalon Reads
      IF lev_dec_v='1'  AND lev_inc_v='0' THEN
        o_readlev<=o_readlev-1;
      ELSIF lev_dec_v='0' AND lev_inc_v='1' THEN
        o_readlev<=o_readlev+1;
      END IF;
      
      -- COPYLEV : Number of ongoing copies to line buffers
      IF lev_dec_v='1'  AND o_readack='0' THEN
        o_copylev<=o_copylev-1;
      ELSIF lev_dec_v='0' AND o_readack='1' THEN
        o_copylev<=o_copylev+1;
      END IF;

      -- FIFOs
      IF lev_dec_v='1' THEN
        o_primv(0 TO 1)<=o_primv(1 TO 2); -- First buffer of line
        o_lastv(0 TO 1)<=o_lastv(1 TO 2); -- Last buffer of line
        o_bibv (0 TO 1)<=o_bibv (1 TO 2); -- Double buffer select
      END IF;

      IF lev_inc_v='1' THEN
        IF o_readlev=0 OR (o_readlev=1 AND lev_dec_v='1') THEN
          o_primv(0)<=prim_v;
          o_lastv(0)<=last_v;
          o_bibv (0)<=bib_v;
        ELSIF (o_readlev=1 AND lev_dec_v='0') OR
              (o_readlev=2 AND lev_dec_v='1') THEN
          o_primv(1)<=prim_v;
          o_lastv(1)<=last_v;
          o_bibv (1)<=bib_v;
        END IF;
        o_primv(2)<=prim_v;
        o_lastv(2)<=last_v;
        o_bibv (2)<=bib_v;
      END IF;
      
      ------------------------------------------------------
      -- FIFO that stores which line buffer should be updated
      IF lev_dec_v='1' THEN
        o_altv(0 TO 7)<=o_altv(4 TO 11);
      END IF;
      IF o_readack='1' THEN -- push
        IF o_copylev=0 OR (o_copylev=1 AND lev_dec_v='1') THEN
          o_altv(0 TO 3)<=o_altp;
        ELSIF (o_copylev=1 AND lev_dec_v='0') OR
              (o_copylev=2 AND lev_dec_v='1') THEN
          o_altv(4 TO 7)<=o_altp;
        END IF;
        o_altv(8 TO 11)<=o_altp;
      END IF;
      
      ------------------------------------------------------
    END IF;
  END PROCESS Scalaire;
  
  o_alt<=o_altv(0 TO 3);
  
  o_h_poly_a<=to_integer(o_modex(0) & o_hpos(11 DOWNTO 12-FRAC));
  o_v_poly_a<=to_integer(o_modex(0) & o_vpos(11 DOWNTO 12-FRAC));
  
  -----------------------------------------------------------------------------
  -- Polyphase ROMs
  o_h_poly_dr<=o_h_poly(o_h_poly_a) WHEN rising_edge(o_clk);
  o_v_poly_dr<=o_v_poly(o_v_poly_a) WHEN rising_edge(o_clk);
  
  Polikarpov:PROCESS(poly_clk) IS
  BEGIN
    IF rising_edge(poly_clk) THEN
      IF poly_wr='1' THEN
        poly_tdw(8+9*(3-to_integer(poly_a(1 DOWNTO 0))) DOWNTO
                 9*(3-to_integer(poly_a(1 DOWNTO 0))))<=poly_dw;
      END IF;
      
      poly_h_wr<=poly_wr AND NOT poly_a(FRAC+2);
      poly_v_wr<=poly_wr AND poly_a(FRAC+2);
      poly_a2<=poly_a(NPOLY+FRAC+2 DOWNTO FRAC+3) & poly_a(FRAC+1 DOWNTO 2);
      
      IF poly_h_wr='1' THEN
        o_h_poly(to_integer(poly_a2))<=poly_tdw;
      END IF;
      IF poly_v_wr='1' THEN
        o_v_poly(to_integer(poly_a2))<=poly_tdw;
      END IF;
    END IF;
  END PROCESS Polikarpov;
  
  -----------------------------------------------------------------------------
  -- Horizontal Scaler
  HSCAL:PROCESS(o_clk) IS
    CONSTANT Z : unsigned(FRAC-1 DOWNTO 0):=(OTHERS =>'0');
  BEGIN
    IF rising_edge(o_clk) THEN
      -- Pipeline signals
      o_hpos1<=o_hpos; o_hpos2<=o_hpos1; o_hpos3<=o_hpos2;
      o_alt1 <=o_alt;  o_alt2 <=o_alt1;  o_alt3 <=o_alt2;  o_alt4 <=o_alt3;
      o_copy1<=o_copy; o_copy2<=o_copy1; o_copy3<=o_copy2; o_copy4<=o_copy3;
      o_dcpt1<=o_dcpt; o_dcpt2<=o_dcpt1; o_dcpt3<=o_dcpt2; o_dcpt4<=o_dcpt3;

      o_hpixm1<=o_hpixm;
      o_hpix01<=o_hpix0; o_hpix02<=o_hpix01; o_hpix03<=o_hpix02;
      o_hpix11<=o_hpix1; o_hpix12<=o_hpix11; o_hpix13<=o_hpix12;
      o_hpix21<=o_hpix2;

      o_dcpt5<=o_dcpt4;
      o_alt5<=o_alt4;
      o_copy5<=o_copy4;
      
      -- NEAREST / BILINEAR / SHARP BILINEAR -------------
      -- C1 : Pre-calc Sharp Bilinear
      o_h_sbil_t<=sbil_frac1(o_hpos(11 DOWNTO 0));
      
      -- C2 : Select
      CASE o_modex(1 DOWNTO 0) IS
        WHEN "00" => -- Nearest
          o_h_frac<=near_frac(o_hpos1(11 DOWNTO 0));
        WHEN "01" => -- Bilinear
          o_h_frac<=bil_frac(o_hpos1(11 DOWNTO 0));
        WHEN "10" => -- Sharp Bilinear
          o_h_frac<=sbil_frac2(o_hpos1(11 DOWNTO 0),o_h_sbil_t);
        WHEN OTHERS =>
          o_h_frac<=near_frac(o_hpos1(11 DOWNTO 0));
      END CASE;

      -- C3 : Opposite frac
      o_h_frac2 <='0' & o_h_frac;
      o_h_fracn2<=('1' & Z) - ('0' & o_h_frac);
      
      -- C4 : Nearest / Bilinear / Sharp Bilinear
      o_h_bil_pix<=bil_calc(o_h_frac2,o_h_fracn2,o_hpix03,o_hpix13);
      
      -- BICUBIC -----------------------------------------
      -- C1 : Bicubic coefficients A,B,C,D
      o_h_bic_abcd1<=bic_coef(o_hpixm,o_hpix0,o_hpix1,o_hpix2);
      
      -- C2 : Bicubic calc T1 = X.D + C
      o_h_bic_abcd2<=o_h_bic_abcd1;
      o_h_bic_tt1<=bic_calc1(o_hpos1(11 DOWNTO 0),o_h_bic_abcd1);

      -- C3 : Bicubic calc T2 = X.T1 + B
      o_h_bic_abcd3<=o_h_bic_abcd2;
      o_h_bic_tt2<=bic_calc2(o_hpos2(11 DOWNTO 0),o_h_bic_tt1,o_h_bic_abcd2);
      
      -- C4 : Bicubic final Y = X.T2 + A
      o_h_bic_pix<=bic_calc3(o_hpos3(11 DOWNTO 0),o_h_bic_tt2,o_h_bic_abcd3);
      
      -- POLYPHASE ---------------------------------------
      -- C1 : Read memory
      
      -- C2 : Filter calc
      o_h_poly_t<=poly_calc1(o_h_poly_dr,o_hpixm1,o_hpix01,o_hpix11,o_hpix21);
      
      -- C3 : Add
      o_h_poly_t2<=poly_calc2(o_h_poly_t);

      -- C4 : Bounding
      o_h_poly_pix<=poly_calc3(o_h_poly_t2);
      
      -- C4 : Select interpoler --------------------------
      o_wadl<=o_dcpt5;
      o_wr(0)<=o_alt5(0) AND o_copy5;
      o_wr(1)<=o_alt5(1) AND o_copy5;
      o_wr(2)<=o_alt5(2) AND o_copy5;
      o_wr(3)<=o_alt5(3) AND o_copy5;
          
      CASE o_modex(2 DOWNTO 0) IS
        WHEN "000" | "001" | "010" => -- Nearest | Bilinear | Sharp Bilinear
          o_ldw<=o_h_bil_pix.r & o_h_bil_pix.g & o_h_bil_pix.b;
         
        WHEN "011" => -- BiCubic
          o_ldw<=o_h_bic_pix.r & o_h_bic_pix.g & o_h_bic_pix.b;
          
        WHEN OTHERS => -- PolyPhase
          o_ldw<=o_h_poly_pix.r & o_h_poly_pix.g & o_h_poly_pix.b;
          
      END CASE;
      ----------------------------------------------------
    END IF;
  END PROCESS HSCAL;
  
  -----------------------------------------------------------------------------
  -- Line buffers 4 x OHSIZE x (R+G+B)
  OLBUF:PROCESS(o_clk) IS
  BEGIN
    IF rising_edge(o_clk) THEN
      -- WRITES
      IF o_wr(0)='1' THEN
        o_linem(o_wadl)<=o_ldw; -- -1
      END IF;
      IF o_wr(1)='1' THEN
        o_line0(o_wadl)<=o_ldw; -- 0
      END IF;
      IF o_wr(2)='1' THEN
        o_line1(o_wadl)<=o_ldw; -- 1
      END IF;
      IF o_wr(3)='1' THEN
        o_line2(o_wadl)<=o_ldw; -- 2
      END IF;

      -- READS
      o_ldrm<=o_linem(o_radl);
      o_ldr0<=o_line0(o_radl);
      o_ldr1<=o_line1(o_radl);
      o_ldr2<=o_line2(o_radl);
    END IF;
  END PROCESS OLBUF;
  
  o_radl<=(o_hcpt-o_hmin+OHRES) MOD OHRES;
  xxx_vposi<=to_integer(o_vpos(23 DOWNTO 12)); -- Simu!
  
  -----------------------------------------------------------------------------
  -- Output video sweep
  OSWEEP:PROCESS(o_clk) IS
  BEGIN
    IF rising_edge(o_clk) THEN
      IF o_ce='1' THEN
        -- Output pixels count
        IF o_hcpt<o_htotal THEN
          o_hcpt<=(o_hcpt+1) MOD 4096;
        ELSE
          o_hcpt<=0;
          IF o_vcpt<o_vtotal-1 OR (SYNCHRO AND mode(4)='1') THEN
            o_vcpt<=(o_vcpt+1) MOD 4096;
          ELSE
            o_vcpt<=0;
          END IF;
        END IF;
        
        IF SYNCHRO AND mode(4)='1' AND o_icnt=o_dsync THEN
          o_hcpt<=0;
          o_vcpt<=o_vsstart;
        END IF;
        
        -- Input pixels position
        IF o_hs0='1' AND o_hs1='0' THEN
          o_vpos<=o_vpos+o_vdelta;
          IF o_vcpt=o_vtotal-1 THEN
            o_vpos<=NOT o_vdelta;
          END IF;
        END IF;
        
        o_de0<=to_std_logic(o_hcpt<o_hdisp AND o_vcpt<o_vdisp);
        o_pe0<=to_std_logic(o_hcpt>=o_hmin AND o_hcpt<=o_hmax AND
                            o_vcpt>=o_vmin AND o_vcpt<=o_vmax);
        o_hs0<=to_std_logic(o_hcpt>=o_hsstart AND o_hcpt<o_hsend);
        o_vs0<=to_std_logic(o_vcpt>=o_vsstart AND o_vcpt<o_vsend);
        
        ------------------------------------------
        -- SYNCHRONIZED LOW LATENCY MODE
        IF o_isdelay>=o_osdelay THEN
          o_dsync<=o_isdelay-o_osdelay;
        ELSE
          o_dsync<=(o_isdelay+o_idelay-o_osdelay) MOD (4096*4096);
        END IF;
        
        -- Measure total input image size
        o_ivs<=i_vs; -- <ASYNC>
        o_ivs2<=o_ivs;
        o_ivs3<=o_ivs2;
        
        IF o_ivs2='1' AND o_ivs3='0' THEN
          o_icnt<=0;
          o_idelay<=o_icnt;
          o_oudb<='1';
        ELSE
          o_icnt<=(o_icnt+1) MOD (4096*4096);
        END IF;
        
        -- Measure delay between input Vsync and first displayed line
        o_isyncline<=i_syncline; -- <ASYNC>
        o_isyncline2<=o_isyncline;

        IF o_isyncline2='1' AND o_oudb='1' THEN
          o_oudb<='0';
          o_isdelay<=o_icnt;
        END IF;
        
        -- Measure delay between output Vsync and first displayed line
        IF o_vs0='1' AND o_vs1='0' THEN
          o_ocnt<=0;
          o_ouda<='1';
        ELSE
          o_ocnt<=(o_ocnt+1) MOD (4096*4096);
        END IF;
        
        IF o_pe0='1' AND o_ouda='1' THEN
          o_osdelay<=o_ocnt;
          o_ouda<='0';
        END IF;
      END IF;
    END IF;
    
  END PROCESS OSWEEP;
  
  -----------------------------------------------------------------------------
  -- Vertical Scaler
  VSCAL:PROCESS(o_clk) IS
    VARIABLE pixm_v ,pix0_v ,pix1_v ,pix2_v  : type_pix;
    VARIABLE pixtm_v,pixt0_v,pixt1_v,pixt2_v : type_pix;
    CONSTANT Z : unsigned(FRAC-1 DOWNTO 0):=(OTHERS =>'0');
  BEGIN
    IF rising_edge(o_clk) THEN
      IF o_ce='1' THEN
        -- Pipeline signals
        o_hs1<=o_hs0;  o_hs2<=o_hs1;  o_hs3<=o_hs2;  o_hs4<=o_hs3; o_hs5<=o_hs4; o_hs6<=o_hs5;
        o_vs1<=o_vs0;  o_vs2<=o_vs1;  o_vs3<=o_vs2;  o_vs4<=o_vs3; o_vs5<=o_vs4; o_vs6<=o_vs5;
        o_de1<=o_de0;  o_de2<=o_de1;  o_de3<=o_de2;  o_de4<=o_de3; o_de5<=o_de4; o_de6<=o_de5;
        o_pe1<=o_pe0;  o_pe2<=o_pe1;  o_pe3<=o_pe2;  o_pe4<=o_pe3; o_pe5<=o_pe4; o_pe6<=o_pe5;
        
        -- CYCLE 1 -----------------------------------------
        -- Read mem
        
        -- CYCLE 2 -----------------------------------------
        -- Lines reordering
        pixm_v:=(r=>o_ldrm(23 DOWNTO 16),
                 g=>o_ldrm(15 DOWNTO 8),b=>o_ldrm(7 DOWNTO 0));
        pix0_v:=(r=>o_ldr0(23 DOWNTO 16),
                 g=>o_ldr0(15 DOWNTO 8),b=>o_ldr0(7 DOWNTO 0));
        pix1_v:=(r=>o_ldr1(23 DOWNTO 16),
                 g=>o_ldr1(15 DOWNTO 8),b=>o_ldr1(7 DOWNTO 0));
        pix2_v:=(r=>o_ldr2(23 DOWNTO 16),
                 g=>o_ldr2(15 DOWNTO 8),b=>o_ldr2(7 DOWNTO 0));

        -- TOP
        IF o_vpos(23)='1' THEN
          o_vpos2<=(OTHERS =>'0');
        ELSE
          o_vpos2<=o_vpos;
        END IF;
        -- BOTTOM -> TODO
        
        CASE o_vpos2(13 DOWNTO 12) IS
          WHEN "00" =>
            pixtm_v:=pixm_v;
            pixt0_v:=pix0_v;
            pixt1_v:=pix1_v;
            pixt2_v:=pix2_v;
          WHEN "01" =>
            pixtm_v:=pix0_v;
            pixt0_v:=pix1_v;
            pixt1_v:=pix2_v;
            pixt2_v:=pixm_v;
          WHEN "10" =>
            pixtm_v:=pix1_v;
            pixt0_v:=pix2_v;
            pixt1_v:=pixm_v;
            pixt2_v:=pix0_v;
          WHEN OTHERS =>
            pixtm_v:=pix2_v;
            pixt0_v:=pixm_v;
            pixt1_v:=pix0_v;
            pixt2_v:=pix1_v;
        END CASE;
        
        o_vpixm2<=pixtm_v;
        o_vpix02<=pixt0_v;
        o_vpix12<=pixt1_v;
        o_vpix22<=pixt2_v;
          
        -- CYCLE 3 -----------------------------------------
        o_vpixm3<=o_vpixm2;
        o_vpix03<=o_vpix02;  o_vpix04<=o_vpix03;
        o_vpix13<=o_vpix12;  o_vpix14<=o_vpix13;
        o_vpix23<=o_vpix22;
        
        -- NEAREST / BILINEAR / SHARP BILINEAR -------------
        -- C3 : Pre-calc Sharp Bilinear
        o_v_sbil_t<=sbil_frac1(o_vpos2(11 DOWNTO 0));
        
        -- C4 : Select
        CASE o_modex(1 DOWNTO 0) IS
          WHEN "00" => -- Nearest
            o_v_frac<=near_frac(o_vpos2(11 DOWNTO 0));
          WHEN "01" => -- Bilinear
            o_v_frac<=bil_frac(o_vpos2(11 DOWNTO 0));
          WHEN "10" => -- Sharp Bilinear
            o_v_frac<=sbil_frac2(o_vpos2(11 DOWNTO 0),o_v_sbil_t);
          WHEN OTHERS =>
            o_v_frac<=near_frac(o_vpos2(11 DOWNTO 0));
        END CASE;

        o_v_frac2 <='0' & o_v_frac;
        o_v_fracn2<=('1' & Z) - ('0' & o_v_frac);
        
        -- C5 : Nearest / Bilinear / Sharp Bilinear
        o_v_bil_pix<=bil_calc(o_v_frac2,o_v_fracn2,o_vpix04,o_vpix14);
        
        -- BICUBIC -----------------------------------------
        -- C3 : Bicubic coefficients A,B,C,D
        o_v_bic_abcd1<=bic_coef(o_vpixm2,o_vpix02,o_vpix12,o_vpix22);
        
        -- C4 : Bicubic calc T1 = X.D + C
        o_v_bic_abcd2<=o_v_bic_abcd1;
        o_v_bic_tt1<=bic_calc1(o_vpos2(11 DOWNTO 0),o_v_bic_abcd1);
        
        -- C5 : Bicubic calc T2 = X.T1 + B
        o_v_bic_abcd3<=o_v_bic_abcd2;
        o_v_bic_tt2<=bic_calc2(o_vpos2(11 DOWNTO 0),o_v_bic_tt1,o_v_bic_abcd2);
        
        -- C6 : Bicubic final Y = X.T2 + A
        o_v_bic_pix<=bic_calc3(o_vpos2(11 DOWNTO 0),o_v_bic_tt2,o_v_bic_abcd3);
        
        -- POLYPHASE ---------------------------------------
        -- C3 : Read memory
        
        -- C4 : Filter calc
        o_v_poly_t<=poly_calc1(o_v_poly_dr,o_vpixm3,o_vpix03,o_vpix13,o_vpix23);
        
        -- C5 : Add
        o_v_poly_t2<=poly_calc2(o_v_poly_t);
        
        -- C6 : Bounding
        o_v_poly_pix<=poly_calc3(o_v_poly_t2);
        
        -- CYCLE 6 -----------------------------------------
        o_hs<=o_hs4;
        o_vs<=o_vs4;
        o_de<=o_de4;
 
        CASE o_modex(2 DOWNTO 0) IS
          WHEN "000" | "001" | "010" => -- Nearest | Bilinear | Sharp Bilinear
            o_r<=o_v_bil_pix.r;
            o_g<=o_v_bil_pix.g;
            o_b<=o_v_bil_pix.b;
            
          WHEN "011" => -- BiCubic
            o_r<=o_v_bic_pix.r;
            o_g<=o_v_bic_pix.g;
            o_b<=o_v_bic_pix.b;
            
          WHEN OTHERS => -- Polyphase
            o_r<=o_v_poly_pix.r;
            o_g<=o_v_poly_pix.g;
            o_b<=o_v_poly_pix.b;
            
        END CASE;
        
        IF o_pe4='0' THEN
          o_r<=x"00"; -- Border colour
          o_g<=x"00";
          o_b<=x"00";
        END IF;
        
        IF o_mode(2 DOWNTO 0)="111" AND o_vcpt<2*8 THEN
          o_r<=(OTHERS => o_debug_set);
          o_g<=(OTHERS => o_debug_set);
          o_b<=(OTHERS => o_debug_set);
        END IF;
        
        ----------------------------------------------------
      END IF;
    END IF;

  END PROCESS VSCAL;
  
  -----------------------------------------------------------------------------
  -- DEBUG
  Debug:PROCESS(o_clk) IS
    TYPE arr_uv8 IS ARRAY (natural RANGE <>) OF unsigned(7 DOWNTO 0);
    CONSTANT chars : arr_uv8 :=(
      x"3E", x"63", x"73", x"7B", x"6F", x"67", x"3E", x"00",  -- 0
      x"0C", x"0E", x"0C", x"0C", x"0C", x"0C", x"3F", x"00",  -- 1
      x"1E", x"33", x"30", x"1C", x"06", x"33", x"3F", x"00",  -- 2
      x"1E", x"33", x"30", x"1C", x"30", x"33", x"1E", x"00",  -- 3
      x"38", x"3C", x"36", x"33", x"7F", x"30", x"78", x"00",  -- 4
      x"3F", x"03", x"1F", x"30", x"30", x"33", x"1E", x"00",  -- 5
      x"1C", x"06", x"03", x"1F", x"33", x"33", x"1E", x"00",  -- 6
      x"3F", x"33", x"30", x"18", x"0C", x"0C", x"0C", x"00",  -- 7
      x"1E", x"33", x"33", x"1E", x"33", x"33", x"1E", x"00",  -- 8
      x"1E", x"33", x"33", x"3E", x"30", x"18", x"0E", x"00",  -- 9
      x"0C", x"1E", x"33", x"33", x"3F", x"33", x"33", x"00",  -- A
      x"3F", x"66", x"66", x"3E", x"66", x"66", x"3F", x"00",  -- B
      x"3C", x"66", x"03", x"03", x"03", x"66", x"3C", x"00",  -- C
      x"1F", x"36", x"66", x"66", x"66", x"36", x"1F", x"00",  -- D
      x"7F", x"46", x"16", x"1E", x"16", x"46", x"7F", x"00",  -- E
      x"7F", x"46", x"16", x"1E", x"16", x"06", x"0F", x"00",  -- F
      x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00",  --' ' 10
      x"00", x"00", x"3F", x"00", x"00", x"3F", x"00", x"00",  -- =  11
      x"00", x"0C", x"0C", x"3F", x"0C", x"0C", x"00", x"00",  -- +  12
      x"00", x"00", x"00", x"3F", x"00", x"00", x"00", x"00",  -- -  13
      x"18", x"0C", x"06", x"03", x"06", x"0C", x"18", x"00",  -- <  14
      x"06", x"0C", x"18", x"30", x"18", x"0C", x"06", x"00",  -- >  15
      x"08", x"1C", x"36", x"63", x"41", x"00", x"00", x"00",  -- ^  16
      x"08", x"1C", x"36", x"63", x"41", x"00", x"00", x"00",  -- v  17
      x"18", x"0C", x"06", x"06", x"06", x"0C", x"18", x"00",  -- (  18
      x"06", x"0C", x"18", x"18", x"18", x"0C", x"06", x"00",  -- )  19
      x"00", x"0C", x"0C", x"00", x"00", x"0C", x"0C", x"00",  -- :  1A
      x"00", x"00", x"00", x"00", x"00", x"0C", x"0C", x"00",  -- .  1B
      x"00", x"00", x"00", x"00", x"00", x"0C", x"0C", x"06",  -- ,  1C
      x"1E", x"33", x"30", x"18", x"0C", x"00", x"0C", x"00",  -- ?  1D
      x"18", x"18", x"18", x"00", x"18", x"18", x"18", x"00",  -- |  1E
      x"36", x"36", x"7F", x"36", x"7F", x"36", x"36", x"00"); -- #  1F
    
    VARIABLE vin_v  : unsigned(0 TO 32*5-1);
  BEGIN
    IF rising_edge(o_clk) THEN
      IF o_ce='1' THEN
        o_hcpt2<=o_hcpt;
        o_hcpt3<=o_hcpt2;
        IF (o_vcpt/8) MOD 2=0 THEN
          vin_v:=o_debug_vin0;
        ELSE
          vin_v:=o_debug_vin1;
        END IF;
        IF o_hcpt<32 * 8 AND o_vcpt<2 * 8 THEN
          o_debug_char<=vin_v((o_hcpt/8)*5 TO (o_hcpt/8)*5+4);
        ELSE
          o_debug_char<="10000"; -- " " : Blank character
        END IF;
        
        o_debug_col<=chars(to_integer(o_debug_char)*8+(o_vcpt MOD 8));
        
        IF o_debug_col(o_hcpt3 MOD 8)='1' THEN
          o_debug_set<='1';
        ELSE
          o_debug_set<='0';
        END IF;
      END IF;
    END IF;
  END PROCESS Debug;

  o_debug_vin0<=
    CN(to_unsigned(o_hburst,8)) & -- 2
    CC(' ') & -- 1
    CN(to_unsigned(o_ihsize,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_ivsize,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_hmin,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_hmax,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_vmin,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_vmax,12)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(i_intercnt,4)) & -- 1
    CC(' ') & -- 1
    CN(to_unsigned(avl_i_buf,4)) & -- 1
    CC(' ') & -- 1
    CN(to_unsigned(avl_o_buf,4)) ; -- 1

  o_debug_vin1<=
    CC(' ') & -- 1
    CN(to_unsigned(o_debug_read,4)) & -- 1
    CN(to_unsigned(avl_debug_write,4)) & -- 1
    CC(' ') & -- 1
    CN(o_hdelta(11 DOWNTO 0)) & -- 3
    CC(' ') & -- 1
    CN(o_vdelta(11 DOWNTO 0)) & -- 3
    CC(' ') & -- 1
    CN(to_unsigned(o_osdelay,24)) & -- 6
    CC(' ') & -- 1
    CN(to_unsigned(o_isdelay,24)) & -- 6
    CC(' ') & -- 1
    CN(to_unsigned(o_idelay,24)); -- 6
  
  -----------------------------------------------------------------------------  
END ARCHITECTURE rtl;
