--------------------------------------------------------------------------------
-- AVALON SCALER
--------------------------------------------------------------------------------
-- DO 10/2018
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

USE std.textio.ALL;

LIBRARY work;
USE work.base_pack.ALL;

ENTITY tb_ascal_test IS

END ENTITY tb_ascal_test;

--------------------------------------------------------------------------------

ARCHITECTURE beh OF tb_ascal_test IS

  
  CONSTANT NPOLY : natural :=2;
  CONSTANT FRAC  : natural :=4;
  
  COMPONENT ascal IS
    GENERIC (
    MASK    : unsigned(0 TO 7) :=x"FF";
    RAMBASE : unsigned(31 DOWNTO 0);
    RO      : boolean := false;
    INTER   : 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 (
    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;
    i_vs              : IN  std_logic;
    i_fl              : IN  std_logic;
    i_de              : IN  std_logic;
    i_ce              : IN  std_logic;
    i_clk             : IN  std_logic;
    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;
    o_vs              : OUT std_logic;
    o_de              : OUT std_logic;
    o_ce              : IN  std_logic;
    o_clk             : IN  std_logic;
    iauto             : IN  std_logic;
    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;
    run               : IN  std_logic;
    mode              : IN  unsigned(3 DOWNTO 0);
    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;
    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;
    avl_clk           : IN  std_logic;
    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);
  END COMPONENT ascal;

  COMPONENT emu IS
    PORT (
      clk_50m          : IN    std_logic;
      reset            : IN    std_logic;
      hps_bus          : INOUT std_logic_vector(44 DOWNTO 0);
      sconf            : OUT   std_logic_vector(3 DOWNTO 0);
      clk_video        : OUT   std_logic;
      ce_pixel         : OUT   std_logic;
      video_arx        : OUT   std_logic_vector(7 DOWNTO 0);
      video_ary        : OUT   std_logic_vector(7 DOWNTO 0);
      vga_r            : OUT   std_logic_vector(7 DOWNTO 0);
      vga_g            : OUT   std_logic_vector(7 DOWNTO 0);
      vga_b            : OUT   std_logic_vector(7 DOWNTO 0);
      vga_hs           : OUT   std_logic;
      vga_vs           : OUT   std_logic;
      vga_de           : OUT   std_logic;
      led_user         : OUT   std_logic;
      led_power        : OUT   std_logic_vector(1 DOWNTO 0);
      led_disk         : OUT   std_logic_vector(1 DOWNTO 0);
      audio_l          : OUT   std_logic_vector(15 DOWNTO 0);
      audio_r          : OUT   std_logic_vector(15 DOWNTO 0);
      audio_s          : OUT   std_logic;
      audio_mix        : OUT   std_logic_vector(1 DOWNTO 0);
      tape_in          : IN    std_logic;
      sd_sck           : OUT   std_logic := 'Z';
      sd_mosi          : OUT   std_logic := 'Z';
      sd_miso          : IN    std_logic;
      sd_cs            : OUT   std_logic := 'Z';
      sd_cd            : IN    std_logic;
      iauto            : OUT   std_logic;
      himin            : OUT   natural RANGE 0 TO 4095;
      himax            : OUT   natural RANGE 0 TO 4095;
      vimin            : OUT   natural RANGE 0 TO 4095;
      vimax            : OUT   natural RANGE 0 TO 4095;
      hdisp            : IN    natural RANGE 0 TO 4095;
      hmin             : OUT   natural RANGE 0 TO 4095;
      hmax             : OUT   natural RANGE 0 TO 4095;
      vdisp            : IN    natural RANGE 0 TO 4095;
      vmin             : OUT   natural RANGE 0 TO 4095;
      vmax             : OUT   natural RANGE 0 TO 4095;
      ddram_clk        : OUT   std_logic;
      ddram_busy       : IN    std_logic;
      ddram_burstcnt   : OUT   std_logic_vector(7 DOWNTO 0);
      ddram_addr       : OUT   std_logic_vector(28 DOWNTO 0);
      ddram_dout       : IN    std_logic_vector(63 DOWNTO 0);
      ddram_dout_ready : IN    std_logic;
      ddram_rd         : OUT   std_logic;
      ddram_din        : OUT   std_logic_vector(63 DOWNTO 0);
      ddram_be         : OUT   std_logic_vector(7 DOWNTO 0);
      ddram_we         : OUT   std_logic;
      sdram_clk        : OUT   std_logic;
      sdram_cke        : OUT   std_logic;
      sdram_a          : OUT   std_logic_vector(12 DOWNTO 0);
      sdram_ba         : OUT   std_logic_vector(1 DOWNTO 0);
      sdram_dq         : INOUT std_logic_vector(15 DOWNTO 0);
      sdram_dqml       : OUT   std_logic;
      sdram_dqmh       : OUT   std_logic;
      sdram_ncs        : OUT   std_logic;
      sdram_ncas       : OUT   std_logic;
      sdram_nras       : OUT   std_logic;
      sdram_nwe        : OUT   std_logic);
  END COMPONENT emu;

  SIGNAL i_r             : std_logic_vector(7 DOWNTO 0);
  SIGNAL i_g             : std_logic_vector(7 DOWNTO 0);
  SIGNAL i_b             : std_logic_vector(7 DOWNTO 0);
  SIGNAL i_hs            : std_logic;
  SIGNAL i_vs            : std_logic;
  SIGNAL i_fl            : std_logic :='0';
  SIGNAL i_de            : std_logic;
  SIGNAL i_ce            : std_logic;
  SIGNAL i_clk           : std_logic :='0';
  SIGNAL o_r             : unsigned(7 DOWNTO 0);
  SIGNAL o_g             : unsigned(7 DOWNTO 0);
  SIGNAL o_b             : unsigned(7 DOWNTO 0);
  SIGNAL o_hs            : std_logic;
  SIGNAL o_vs            : std_logic;
  SIGNAL o_de            : std_logic;
  SIGNAL o_ce            : std_logic;
  SIGNAL o_clk           : std_logic :='0';
  SIGNAL run             : std_logic;
  SIGNAL mode            : unsigned(3 DOWNTO 0);
  SIGNAL htotal          : natural RANGE 0 TO 4095;
  SIGNAL hsstart         : natural RANGE 0 TO 4095;
  SIGNAL hsend           : natural RANGE 0 TO 4095;
  SIGNAL hdisp           : natural RANGE 0 TO 4095;
  SIGNAL hmin            : natural RANGE 0 TO 4095;
  SIGNAL hmax            : natural RANGE 0 TO 4095;
  SIGNAL vtotal          : natural RANGE 0 TO 4095;
  SIGNAL vsstart         : natural RANGE 0 TO 4095;
  SIGNAL vsend           : natural RANGE 0 TO 4095;
  SIGNAL vdisp           : natural RANGE 0 TO 4095;
  SIGNAL vmin            : natural RANGE 0 TO 4095;
  SIGNAL vmax            : natural RANGE 0 TO 4095;
  SIGNAL avl_clk         : std_logic :='0';
  SIGNAL avl_waitrequest : std_logic;
  SIGNAL avl_readdata    : std_logic_vector(127 DOWNTO 0);
  SIGNAL avl_readdatavalid : std_logic;
  SIGNAL avl_burstcount  : std_logic_vector(7 DOWNTO 0);
  SIGNAL avl_writedata   : std_logic_vector(127 DOWNTO 0);
  SIGNAL avl_address     : std_logic_vector(19 DOWNTO 0);
  SIGNAL avl_write       : std_logic;
  SIGNAL avl_read        : std_logic;
  SIGNAL avl_byteenable  : std_logic_vector(15 DOWNTO 0);
  SIGNAL reset_na        : std_logic;

  SIGNAL poly_clk     : std_logic;
  SIGNAL poly_dw      : unsigned(8 DOWNTO 0);
  SIGNAL poly_a       : unsigned(NPOLY+FRAC+2 DOWNTO 0);
  SIGNAL poly_wr      : std_logic;

  FILE fil : text OPEN write_mode IS "mem.log";
  FILE fvic : text OPEN write_mode IS "videi.log";
  FILE fvid : text OPEN write_mode IS "video.log";
  
  TYPE arr_uv128 IS ARRAY (natural RANGE <>) OF unsigned(127 DOWNTO 0);
  SHARED VARIABLE mem : arr_uv128(0 TO 2**15-1);
  
  SIGNAL iauto   : std_logic;
  SIGNAL himin   : natural RANGE 0 TO 4095;
  SIGNAL himax   : natural RANGE 0 TO 4095;
  SIGNAL vimin   : natural RANGE 0 TO 4095;
  SIGNAL vimax   : natural RANGE 0 TO 4095;
  
  SIGNAL clk_50m : std_logic :='0';
  SIGNAL reset : std_logic :='0';
  SIGNAL hps_bus : std_logic_vector(44 DOWNTO 0);
  SIGNAL sconf : std_logic_vector(3 DOWNTO 0) :="0000";
  
BEGIN

  avl_clk<=NOT avl_clk AFTER 0.5 us / 80;
  clk_50m<=NOT clk_50m AFTER 0.5 us / 50;

  reset_na<='0','1' AFTER 1.1 us;

  ----------------------------------------------------------
  i_ascal: ascal
    GENERIC MAP (
      RAMBASE => x"00000000",
      RO      => false,
      FORMAT  => 8,
      N_DW    => 128,
      N_AW    => 20,
      N_BURST => 256)
    PORT MAP (
      i_r               => unsigned(i_r),
      i_g               => unsigned(i_g),
      i_b               => unsigned(i_b),
      i_hs              => i_hs,
      i_vs              => i_vs,
      i_fl              => i_fl,
      i_de              => i_de,
      i_ce              => i_ce,
      i_clk             => clk_50m, --i_clk,
      o_r               => o_r,
      o_g               => o_g,
      o_b               => o_b,
      o_hs              => o_hs,
      o_vs              => o_vs,
      o_de              => o_de,
      o_ce              => o_ce,
      o_clk             => o_clk,
      iauto             => iauto,
      himin             => himin,
      himax             => himax,
      vimin             => vimin,
      vimax             => vimax,
      run               => run,
      mode              => mode,
      htotal            => htotal,
      hsstart           => hsstart,
      hsend             => hsend,
      hdisp             => hdisp,
      hmin              => hmin,
      hmax              => hmax,
      vtotal            => vtotal,
      vsstart           => vsstart,
      vsend             => vsend,
      vdisp             => vdisp,
      vmin              => vmin,
      vmax              => vmax,
      poly_clk          => poly_clk,
      poly_dw           => poly_dw,
      poly_a            => poly_a,
      poly_wr           => poly_wr,
      avl_clk           => avl_clk,
      avl_waitrequest   => avl_waitrequest,
      avl_readdata      => avl_readdata,
      avl_readdatavalid => avl_readdatavalid,
      avl_burstcount    => avl_burstcount,
      avl_writedata     => avl_writedata,
      avl_address       => avl_address,
      avl_write         => avl_write,
      avl_read          => avl_read,
      avl_byteenable    => avl_byteenable,
      reset_na          => reset_na);

  i_emu: emu
    PORT MAP (
      clk_50m          => clk_50m,
      reset            => reset,
      hps_bus          => hps_bus,
      sconf            => sconf,
      clk_video        => i_clk,
      ce_pixel         => i_ce,
      video_arx        => OPEN,
      video_ary        => OPEN,
      vga_r            => i_r,
      vga_g            => i_g,
      vga_b            => i_b,
      vga_hs           => i_hs,
      vga_vs           => i_vs,
      vga_de           => i_de,
      led_user         => OPEN,
      led_power        => OPEN, 
      led_disk         => OPEN,
      audio_l          => OPEN,
      audio_r          => OPEN ,
      audio_s          => OPEN,
      audio_mix        => OPEN,
      tape_in          => '0',
      sd_sck           => OPEN,
      sd_mosi          => OPEN,
      sd_miso          => '0',
      sd_cs            => OPEN,
      sd_cd            => '0',
      iauto            => iauto,
      himin            => himin,
      himax            => himax,
      vimin            => vimin,
      vimax            => vimax,
      hdisp            => hdisp,
      hmin             => hmin,
      hmax             => hmax,
      vdisp            => vdisp,
      vmin             => vmin,
      vmax             => vmax,
      ddram_clk        => OPEN,
      ddram_busy       => '0',
      ddram_burstcnt   => OPEN,
      ddram_addr       => OPEN,
      ddram_dout       => x"00000000_00000000",
      ddram_dout_ready => '0',
      ddram_rd         => OPEN,
      ddram_din        => OPEN,
      ddram_be         => OPEN,
      ddram_we         => OPEN,
      sdram_clk        => OPEN,
      sdram_cke        => OPEN,
      sdram_a          => OPEN,
      sdram_ba         => OPEN,
      sdram_dq         => OPEN,
      sdram_dqml       => OPEN,
      sdram_dqmh       => OPEN,
      sdram_ncs        => OPEN,
      sdram_ncas       => OPEN,
      sdram_nras       => OPEN,
      sdram_nwe        => OPEN);
  
  ----------------------------------------------------------
  AvaleW:PROCESS IS
    VARIABLE a : unsigned(15 DOWNTO 0);
  BEGIN
    wure(avl_clk);
    IF avl_write='1' AND avl_waitrequest='0' THEN
      a:=unsigned(avl_address(15 DOWNTO 0));
      FOR i IN 0 TO 15 LOOP
        mem(to_integer(a)+i):=unsigned(avl_writedata);
        wure(avl_clk);
      END LOOP;
    END IF;
  END PROCESS AvaleW;

  AvaleR:PROCESS IS
    VARIABLE a : unsigned(15 DOWNTO 0);
  BEGIN
    avl_readdatavalid<='0';
    wure(avl_clk);
    IF avl_read='1' AND avl_waitrequest='0' THEN
      a:=unsigned(avl_address(15 DOWNTO 0));
      wure(avl_clk);
      FOR i IN 0 TO 7 LOOP
        avl_readdata<=std_logic_vector(mem(to_integer(a)+i));
        avl_readdatavalid<='1';
        wure(avl_clk);
      END LOOP;
      --avl_readdatavalid<='0';
      --wure(avl_clk);
      --avl_readdatavalid<='0';
      --wure(avl_clk);
      --avl_readdatavalid<='0';
      --wure(avl_clk);
      
      FOR i IN 8 TO 15 LOOP
        avl_readdata<=std_logic_vector(mem(to_integer(a)+i));
        avl_readdatavalid<='1';
        wure(avl_clk);
      END LOOP;
      
    END IF;
  END PROCESS AvaleR;
  
  PROCESS IS
  BEGIN
    wure(avl_clk);
    avl_waitrequest<='0';
    WAIT;
    --wure(avl_clk,5);
    --avl_waitrequest<='0';
    --wure(avl_clk,2);
    --avl_waitrequest<='1';
    --wure(avl_clk,3);
    --avl_waitrequest<='0';
  END PROCESS;
  
  ----------------------------------------------------------
  --Aargh:IF 0=1 GENERATE
  Trace:PROCESS IS
    VARIABLE rad,wad : unsigned(19 DOWNTO 0);
    VARIABLE rcpt,rlen,wcpt,wlen : natural RANGE 0 TO 255;
    VARIABLE rdata,wdata : arr_uv128(0 TO 15);
    VARIABLE rpend,wpend : boolean;
    VARIABLE lout : line;
  BEGIN
    wure(avl_clk);
    IF avl_read='1' AND avl_waitrequest='0' THEN
      rad:=unsigned(avl_address);
      rpend:=true;
      rcpt:=0;
      rlen:=to_integer(unsigned(avl_burstcount));
    END IF;
    IF rpend AND avl_readdatavalid='1' THEN
      rdata(rcpt):=unsigned(avl_readdata);
      rcpt:=rcpt+1;
      IF rcpt=rlen THEN
        rpend:=false;
        write(lout,string'("R : " & to_hstring(rad) & ": "));
        FOR i IN 0 TO rlen-1 LOOP
          write(lout,to_hstring(rdata(i)) & " ");
        END LOOP;
        write (lout,now);
        writeline(fil,lout);
      END IF;
    END IF;
    IF avl_write='1' AND avl_waitrequest='0' THEN
      IF NOT wpend THEN
        wad:=unsigned(avl_address);
        wpend:=true;
        wcpt:=1;
        wlen:=to_integer(unsigned(avl_burstcount));
        wdata(0):=unsigned(avl_writedata);
      ELSE
        wdata(wcpt):=unsigned(avl_writedata);
        wcpt:=wcpt+1;
        IF wcpt=wlen THEN
          write(lout,string'("W : " & to_hstring(wad) & ": "));
          FOR i IN 0 TO wlen-1 LOOP
            write(lout,to_hstring(wdata(i)) & " ");
          END LOOP;
          write (lout,now);
          writeline(fil,lout);
          wpend:=false;
        END IF;
      END IF;
    END IF;
    
  END PROCESS Trace;
  --END GENERATE;
  ----------------------------------------------------------
  Ooops:IF 0=1 GENERATE
  VideoI:PROCESS IS
    VARIABLE pix : string(1 TO 10000) :=(OTHERS =>nul);
    VARIABLE i : natural :=1;
    VARIABLE rgb : unsigned(2 DOWNTO 0);
    VARIABLE lout : line;
    VARIABLE l : natural;
  BEGIN
    WAIT UNTIL rising_edge(i_clk);
    WAIT FOR 0.1 ns;
    IF i_ce='1' THEN
      rgb:=i_r(7) & i_g(7) & i_b(7);
      IF i_de='1' THEN
        CASE rgb IS
          WHEN "000"  => pix(i):=' ';
          WHEN "001"  => pix(i):='B';
          WHEN "010"  => pix(i):='G';
          WHEN "011"  => pix(i):='C';
          WHEN "100"  => pix(i):='R';
          WHEN "101"  => pix(i):='M';
          WHEN "110"  => pix(i):='Y';
          WHEN "111"  => pix(i):='W';
          WHEN OTHERS => pix(i):='?';
        END CASE;
      ELSE
        pix(i):='.';
      END IF;
      i:=i+1;
      
      IF i_hs='1' THEN
        write (lout,pix(1 TO i-1));
        write (lout,string'(" "));
        write (lout,l);
        write (lout,string'(" "));
        write (lout,now);
        writeline(fvic,lout);
        WAIT UNTIL i_hs='0';
        i:=1;
        l:=l+1;
        pix:=(OTHERS =>nul);
      END IF;

      IF i_vs='1' THEN
        i:=1;
        l:=0;
        WAIT UNTIL i_vs='0';
      END IF;
    END IF;
  END PROCESS;
  END GENERATE;
  
  ----------------------------------------------------------
  VideoO:PROCESS IS
    VARIABLE pix : string(1 TO 10000) :=(OTHERS =>nul);
    VARIABLE i : natural :=1;
    VARIABLE rgb : unsigned(2 DOWNTO 0);
    VARIABLE lout : line;
    VARIABLE l : natural;
  BEGIN
    WAIT UNTIL rising_edge(o_clk);
    WAIT FOR 0.1 ns;
    IF o_ce='1' THEN
      rgb:=o_r(7) & o_g(7) & o_b(7);
      IF o_de='1' THEN
        CASE rgb IS
          WHEN "000"  => pix(i):=' ';
          WHEN "001"  => pix(i):='B';
          WHEN "010"  => pix(i):='G';
          WHEN "011"  => pix(i):='C';
          WHEN "100"  => pix(i):='R';
          WHEN "101"  => pix(i):='M';
          WHEN "110"  => pix(i):='Y';
          WHEN "111"  => pix(i):='W';
          WHEN OTHERS => pix(i):='?';
        END CASE;
      ELSE
        pix(i):='.';
      END IF;
      i:=i+1;
      
      IF o_hs='1' THEN
        write (lout,pix(1 TO i-1));
        write (lout,string'(" "));
        write (lout,l);
        write (lout,string'(" "));
        write (lout,now);
        writeline(fvid,lout);
        WAIT UNTIL o_hs='0';
        i:=1;
        l:=l+1;
        pix:=(OTHERS =>nul);
      END IF;

      IF o_vs='1' THEN
        i:=1;
        l:=0;
        WAIT UNTIL o_vs='0';
      END IF;
    END IF;
  END PROCESS;
  
  ----------------------------------------------------------
 
  o_clk<=NOT o_clk AFTER 0.5 us / 65;
  o_ce<='1';


  --1920x1080@60 PCLK=148.5MHz CEA
--reg  [11:0] WIDTH  = 1920;
--reg  [11:0] HFP    = 88;
--reg  [11:0] HS     = 48;
--reg  [11:0] HBP    = 148;
--reg  [11:0] HEIGHT = 1080;
--reg  [11:0] VFP    = 4;
--reg  [11:0] VS     = 5;
--reg  [11:0] VBP    = 36;
--reg  [11:0] VSET   = 0;
  hdisp<=1280; -- 1920;
  hsstart<=1920+88;
  hsend<=1920+88+48;
  htotal<=1920+88+48+148;
  
  vdisp<=768; -- 1080;
  vsstart<=1080+4;
  vsend<=1080+4+5;
  vtotal<=1080+4+5+36;
  
  
  run<='1';

  mode<="0100";
  
  
END ARCHITECTURE beh;
