-------------------------------------------------------------------------------
--
-- Text-mode VGA controller for the XESS-300E
--
-- Uses an OPB interface, e.g., for use with the Microblaze soft core
--
-- Stephen A. Edwards
-- sedwards@cs.columbia.edu
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity opb_xsb300e_vga is
  
  generic (
    C_OPB_AWIDTH : integer                   := 32;
    C_OPB_DWIDTH : integer                   := 32;
    C_BASEADDR   : std_logic_vector(31 downto 0) := X"FEFF1000";
    C_HIGHADDR   : std_logic_vector(31 downto 0) := X"FEFF1FFF");

  port (
    OPB_Clk        : in std_logic;
    OPB_Rst        : in std_logic;

    -- OPB signals
    OPB_ABus       : in std_logic_vector (31 downto 0);
    OPB_BE         : in std_logic_vector (3 downto 0);
    OPB_DBus       : in std_logic_vector (31 downto 0);
    OPB_RNW        : in std_logic;
    OPB_select     : in std_logic;
    OPB_seqAddr    : in std_logic;

    VGA_DBus       : out std_logic_vector (31 downto 0);
    VGA_errAck     : out std_logic;
    VGA_retry      : out std_logic;
    VGA_toutSup    : out std_logic;
    VGA_xferAck    : out std_logic;

    Pixel_Clock    : in std_logic;

    -- Video signals
    VIDOUT_CLK     : out std_logic;
    VIDOUT_RED     : out std_logic_vector(9 downto 0);
    VIDOUT_GREEN   : out std_logic_vector(9 downto 0);
    VIDOUT_BLUE    : out std_logic_vector(9 downto 0);
    VIDOUT_BLANK_N : out std_logic;
    VIDOUT_HSYNC_N : out std_logic;
    VIDOUT_VSYNC_N : out std_logic;

    -- SRAM signals
    Sln_DBus    : out std_logic_vector(0 to C_OPB_DWIDTH-1);
    Sln_errAck  : out std_logic;        -- (unused)
    Sln_retry   : out std_logic;        -- (unused)
    Sln_toutSup : out std_logic;        -- Timeout suppress
    Sln_xferAck : out std_logic;
    PB_D        : inout std_logic_vector(15 downto 0);
    PB_A        : out std_logic_vector(17 downto 0);
    PB_WE       : out std_logic;
    PB_OE       : out std_logic;
    PB_LB       : out std_logic;
    PB_UB       : out std_logic;
    RAM_CE       : out std_logic);       -- Transfer acknowledge

end opb_xsb300e_vga;

architecture Behavioral of opb_xsb300e_vga is
  
  constant BASEADDR : std_logic_vector(31 downto 0) := X"FEFF1000";

  -- Video parameters
  
  constant HTOTAL : integer := 800;
  constant HSYNC : integer := 96;
  constant HBACK_PORCH : integer := 48;
  constant HACTIVE : integer := 640;
  constant HFRONT_PORCH : integer := 16;
  
  constant VTOTAL : integer := 525;
  constant VSYNC : integer := 2;
  constant VBACK_PORCH : integer := 33;
  constant VACTIVE : integer := 480;
  constant VFRONT_PORCH : integer := 10;

  -- 512 X 8 dual-ported Xilinx block RAM
  component RAMB4_S8_S8
    port (
      DOA   : out std_logic_vector (7 downto 0);
      ADDRA : in std_logic_vector (8 downto 0);
      CLKA  : in std_logic;
      DIA   : in std_logic_vector (7 downto 0);
      ENA   : in std_logic;
      RSTA  : in std_logic;
      WEA   : in std_logic;
      DOB   : out std_logic_vector (7 downto 0);
      ADDRB : in std_logic_vector (8 downto 0);
      CLKB  : in std_logic;
      DIB   : in std_logic_vector (7 downto 0);
      ENB   : in std_logic;
      RSTB  : in std_logic;
      WEB   : in std_logic);
  end component;

  -- Signals latched from the OPB
  signal ABus : std_logic_vector (31 downto 0);
  signal DBus : std_logic_vector (31 downto 0);
  signal RNW  : std_logic;
  signal select_delayed : std_logic;

  -- Latched output signals for the OPB
  signal DBus_out : std_logic_vector (31 downto 0);

  -- Signals for the OPB-mapped RAM
  signal ChipSelect : std_logic;            -- Address decode
  signal MemCycle1, MemCycle2 : std_logic;  -- State bits
  signal RamPageAddress : std_logic_vector(2 downto 0); 
  signal RamSelect : std_logic_vector (7 downto 0);
  signal RST, WE : std_logic_vector (7 downto 0);
  signal DOUT0, DOUT1, DOUT2, DOUT3 : std_logic_vector(7 downto 0);
  signal DOUT4, DOUT5, DOUT6, DOUT7 : std_logic_vector(7 downto 0);
  signal ReadData : std_logic_vector(7 downto 0);

  -- Signals for the video controller
  signal LoadNShift : std_logic;        -- Shift register control
  signal FontData : std_logic_vector(7 downto 0);   -- Input to shift register
  signal ShiftData : std_logic_vector(7 downto 0);  -- Shift register data
  signal VideoData : std_logic;         -- Serial out ANDed with blanking
  --signal RedData : std_logic_vector(7 downto 0); -- Red value from video memory
  --signal GreenData : std_logic_vector(7 downto 0);--Grn value from video memory
  --signal BlueData : std_logic_vector(7 downto 0);--Blue value from video memory


  -- signals for the video decoder
  signal decoded_SRAM_data : std_logic_vector(23 downto 0);
  signal raw_SRAM_data : std_logic_vector(15 downto 0);
  signal next_pixel_to_display : std_logic;
  
  signal Hcount : std_logic_vector(9 downto 0);  -- Horizontal position (0-800)
  signal Vcount : std_logic_vector(9 downto 0);  -- Vertical position (0-524)
  signal HBLANK_N, VBLANK_N : std_logic;   -- Blanking signals

  signal FontLoad, LoadChar : std_logic;  -- Font/Character RAM read triggers
  signal FontAddr : std_logic_vector(10 downto 0);
  signal CharRamPage : std_logic_vector(2 downto 0);
  signal CharRamSelect_N : std_logic_vector(4 downto 0);
  signal FontRamPage : std_logic_vector(1 downto 0);
  signal FontRamSelect_N : std_logic_vector(2 downto 0);
  signal CharAddr : std_logic_vector(11 downto 0);
  signal CharColumn : std_logic_vector(9 downto 0);
  signal CharRow : std_logic_vector(9 downto 0);
  signal Column : std_logic_vector(6 downto 0); -- 0-79
  signal Row : std_logic_vector(4 downto 0);   -- 0-29
  signal EndOfLine, EndOfField : std_logic;

  signal DOUTB0, DOUTB1, DOUTB2, DOUTB3 : std_logic_vector(7 downto 0);
  signal DOUTB4, DOUTB5, DOUTB6, DOUTB7 : std_logic_vector(7 downto 0);

  attribute INIT_00 : string;
  attribute INIT_01 : string;
  attribute INIT_02 : string;
  attribute INIT_03 : string;
  attribute INIT_04 : string;
  attribute INIT_05 : string;
  attribute INIT_06 : string;
  attribute INIT_07 : string;
  attribute INIT_08 : string;
  attribute INIT_09 : string;
  attribute INIT_0a : string;
  attribute INIT_0b : string;
  attribute INIT_0c : string;
  attribute INIT_0d : string;
  attribute INIT_0e : string;
  attribute INIT_0f : string;

  attribute INIT_00 of RAMB4_S8_S8_0 : label is
   "21646c726f57206f6c6c6548";

  -- Standard 8x16 font taken from the Linux console font file "lat0-16.psfu"
  
  attribute INIT_00 of RAMB4_S8_S8_5 : label is
   "000000001818001818183c3c3c18000000000000000000000000000000000000";
  attribute INIT_01 of RAMB4_S8_S8_5 : label is
   "000000006c6cfe6c6c6cfe6c6c00000000000000000000000000002466666600";
  attribute INIT_02 of RAMB4_S8_S8_5 : label is
   "0000000086c66030180cc6c200000000000010107cd616167cd0d0d67c101000";
  attribute INIT_03 of RAMB4_S8_S8_5 : label is
   "000000000000000000000030181818000000000076ccccccdc76386c6c380000";
  attribute INIT_04 of RAMB4_S8_S8_5 : label is
   "0000000030180c0c0c0c0c0c18300000000000000c18303030303030180c0000";
  attribute INIT_05 of RAMB4_S8_S8_5 : label is
   "00000000000018187e18180000000000000000000000663cff3c660000000000";
  attribute INIT_06 of RAMB4_S8_S8_5 : label is
   "0000000000000000fe0000000000000000000030181818000000000000000000";
  attribute INIT_07 of RAMB4_S8_S8_5 : label is
   "0000000000c06030180c06000000000000000000181800000000000000000000";
  attribute INIT_08 of RAMB4_S8_S8_5 : label is
   "000000007e1818181818187838180000000000007cc6e6e6d6d6cecec67c0000";
  attribute INIT_09 of RAMB4_S8_S8_5 : label is
   "000000007cc60606063c0606c67c000000000000fec6c06030180c06c67c0000";
  attribute INIT_0a of RAMB4_S8_S8_5 : label is
   "000000007cc6060606fcc0c0c0fe0000000000001e0c0c0cfecc6c3c1c0c0000";
  attribute INIT_0b of RAMB4_S8_S8_5 : label is
   "0000000030303030180c0606c6fe0000000000007cc6c6c6c6fcc0c060380000";
  attribute INIT_0c of RAMB4_S8_S8_5 : label is
   "00000000780c0606067ec6c6c67c0000000000007cc6c6c6c67cc6c6c67c0000";
  attribute INIT_0d of RAMB4_S8_S8_5 : label is
   "0000000030181800000018180000000000000000001818000000181800000000";
  attribute INIT_0e of RAMB4_S8_S8_5 : label is
   "00000000000000fe0000fe000000000000000000060c18306030180c06000000";
  attribute INIT_0f of RAMB4_S8_S8_5 : label is
   "000000001818001818180cc6c67c0000000000006030180c060c183060000000";
  attribute INIT_00 of RAMB4_S8_S8_6 : label is
   "00000000c6c6c6c6fec6c66c38100000000000007cc0dcdededec6c6c67c0000";
  attribute INIT_01 of RAMB4_S8_S8_6 : label is
   "000000003c66c2c0c0c0c0c2663c000000000000fc666666667c666666fc0000";
  attribute INIT_02 of RAMB4_S8_S8_6 : label is
   "00000000fe6662606878686266fe000000000000f86c6666666666666cf80000";
  attribute INIT_03 of RAMB4_S8_S8_6 : label is
   "000000003a66c6c6dec0c0c2663c000000000000f06060606878686266fe0000";
  attribute INIT_04 of RAMB4_S8_S8_6 : label is
   "000000003c18181818181818183c000000000000c6c6c6c6c6fec6c6c6c60000";
  attribute INIT_05 of RAMB4_S8_S8_6 : label is
   "00000000e666666c78786c6666e600000000000078cccccc0c0c0c0c0c1e0000";
  attribute INIT_06 of RAMB4_S8_S8_6 : label is
   "00000000c6c6c6c6c6d6fefeeec6000000000000fe6662606060606060f00000";
  attribute INIT_07 of RAMB4_S8_S8_6 : label is
   "000000007cc6c6c6c6c6c6c6c67c000000000000c6c6c6c6cedefef6e6c60000";
  attribute INIT_08 of RAMB4_S8_S8_6 : label is
   "00000e0c7cded6c6c6c6c6c6c67c000000000000f0606060607c666666fc0000";
  attribute INIT_09 of RAMB4_S8_S8_6 : label is
   "000000007cc6c6060c3860c6c67c000000000000e66666666c7c666666fc0000";
  attribute INIT_0a of RAMB4_S8_S8_6 : label is
   "000000007cc6c6c6c6c6c6c6c6c60000000000003c1818181818185a7e7e0000";
  attribute INIT_0b of RAMB4_S8_S8_6 : label is
   "000000006ceefed6d6d6c6c6c6c600000000000010386cc6c6c6c6c6c6c60000";
  attribute INIT_0c of RAMB4_S8_S8_6 : label is
   "000000003c181818183c66666666000000000000c6c66c7c38387c6cc6c60000";
  attribute INIT_0d of RAMB4_S8_S8_6 : label is
   "000000003c30303030303030303c000000000000fec6c26030180c86c6fe0000";
  attribute INIT_0e of RAMB4_S8_S8_6 : label is
   "000000003c0c0c0c0c0c0c0c0c3c00000000000000060c183060c00000000000";
  attribute INIT_0f of RAMB4_S8_S8_6 : label is
   "00ff0000000000000000000000000000000000000000000000000000c66c3810";
  attribute INIT_00 of RAMB4_S8_S8_7 : label is
   "0000000076cccccc7c0c78000000000000000000000000000000001830303000";
  attribute INIT_01 of RAMB4_S8_S8_7 : label is
   "000000007cc6c0c0c0c67c0000000000000000007c666666666c786060e00000";
  attribute INIT_02 of RAMB4_S8_S8_7 : label is
   "000000007cc6c0c0fec67c00000000000000000076cccccccc6c3c0c0c1c0000";
  attribute INIT_03 of RAMB4_S8_S8_7 : label is
   "0078cc0c7ccccccccccc76000000000000000000f060606060f060646c380000";
  attribute INIT_04 of RAMB4_S8_S8_7 : label is
   "000000003c181818181838001818000000000000e666666666766c6060e00000";
  attribute INIT_05 of RAMB4_S8_S8_7 : label is
   "00000000e6666c78786c666060e00000003c66660606060606060e0006060000";
  attribute INIT_06 of RAMB4_S8_S8_7 : label is
   "00000000c6d6d6d6d6feec000000000000000000183430303030303030700000";
  attribute INIT_07 of RAMB4_S8_S8_7 : label is
   "000000007cc6c6c6c6c67c000000000000000000666666666666dc0000000000";
  attribute INIT_08 of RAMB4_S8_S8_7 : label is
   "001e0c0c7ccccccccccc76000000000000f060607c6666666666dc0000000000";
  attribute INIT_09 of RAMB4_S8_S8_7 : label is
   "000000007cc60c3860c67c000000000000000000f06060606676dc0000000000";
  attribute INIT_0a of RAMB4_S8_S8_7 : label is
   "0000000076cccccccccccc0000000000000000001c3630303030fc3030100000";
  attribute INIT_0b of RAMB4_S8_S8_7 : label is
   "000000006cfed6d6d6c6c6000000000000000000183c66666666660000000000";
  attribute INIT_0c of RAMB4_S8_S8_7 : label is
   "00f80c067ec6c6c6c6c6c6000000000000000000c66c3838386cc60000000000";
  attribute INIT_0d of RAMB4_S8_S8_7 : label is
   "000000000e18181818701818180e000000000000fec6603018ccfe0000000000";
  attribute INIT_0e of RAMB4_S8_S8_7 : label is
   "0000000070181818180e18181870000000000000181818181818181818180000";
  attribute INIT_0f of RAMB4_S8_S8_7 : label is
   "000000003c1818183c66666666006600000000000000000000000000dc760000";


  -----------------------------------------------------------------------------
  --BEGIN SRAM STUFF
  -----------------------------------------------------------------------------

  constant RAM_AWIDTH : integer := 18;  -- Number of address lines on the RAM
  constant RAM_DWIDTH : integer := 16;  -- Number of data lines on the RAM
  
 -- component RAMB4_S8
 --   port (
 --     DO   : out std_logic_vector(RAM_DWIDTH-1 downto 0);
 --     ADDR : in  std_logic_vector(RAM_AWIDTH-1 downto 0);
  --    CLK  : in  std_logic;
---     DI   : in  std_logic_vector(RAM_DWIDTH-1 downto 0);
 --     EN   : in  std_logic;
--      RST  : in  std_logic;
--      WE   : in  std_logic);
 -- end component;

  component OBUF_F_24
    port (
      O : out std_logic;
      I : in std_logic);
  end component;
  
  component IOBUF_F_24
    port (
      O : out std_logic;
      IO : inout std_logic;
      I : in std_logic;
      T : in std_logic);
  end component;
  
  signal sram_RNW : std_logic;
  --signal RAM_DI, RAM_DO : std_logic_vector(0 to RAM_DWIDTH-1);
  --signal ABus : std_logic_vector(0 to RAM_AWIDTH-1);
  signal chip_select : std_logic;
  signal output_enable : std_logic;
  signal tri_iob: std_logic;
  signal OE, sram_WE, LB, UB : std_logic;
  signal saddin : std_logic_vector(0 to 17);
  signal sramout : std_logic_vector(0 to 15);
  signal sramin : std_logic_vector(0 to 15);
  signal iobuf : std_logic;

  signal SRAM_START_ADDRESS : std_logic_vector(0 to 17);

  -- Critical: Sln_xferAck is generated directly from state bit 0!

  type opb_state is (Idle, Selected, Read, Xfer);
  signal present_state, next_state : opb_state;

  
begin  -- Behavioral

  -----------------------------------------------------------------------------
  --
  -- Instances of the block RAMs
  -- Each is 512 bytes, so 4K total
  --
  -----------------------------------------------------------------------------

  -- First 2.5K is used for characters (5 block RAMs)
  -- Remaining 1.5K holds the font     (3 block RAMs)
  --
 
  -- Port A is used for communication with the OPB
  -- Port B is for video
 
  RAMB4_S8_S8_0 : RAMB4_S8_S8
    port map (
      DOA   => DOUT0,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(0),
      WEA   => WE(0),
      DOB   => DOUTB0,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(0),
      WEB   => '0');

  RAMB4_S8_S8_1 : RAMB4_S8_S8
    port map (
      DOA   => DOUT1,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(1),
      WEA   => WE(1),
      DOB   => DOUTB1,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(1),
      WEB   => '0');

    RAMB4_S8_S8_2 : RAMB4_S8_S8
    port map (
      DOA   => DOUT2,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(2),
      WEA   => WE(2),
      DOB   => DOUTB2,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(2),
      WEB   => '0');

    RAMB4_S8_S8_3 : RAMB4_S8_S8
    port map (
      DOA   => DOUT3,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(3),
      WEA   => WE(3),
      DOB   => DOUTB3,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(3),
      WEB   => '0');

    RAMB4_S8_S8_4 : RAMB4_S8_S8
    port map (
      DOA   => DOUT4,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(4),
      WEA   => WE(4),
      DOB   => DOUTB4,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(4),
      WEB   => '0');

    RAMB4_S8_S8_5 : RAMB4_S8_S8
    port map (
      DOA   => DOUT5,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(5),
      WEA   => WE(5),
      DOB   => DOUTB5,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(0),
      WEB   => '0');

    RAMB4_S8_S8_6 : RAMB4_S8_S8
    port map (
      DOA   => DOUT6,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(6),
      WEA   => WE(6),
      DOB   => DOUTB6,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(1),
      WEB   => '0');

    RAMB4_S8_S8_7 : RAMB4_S8_S8
    port map (
      DOA   => DOUT7,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(7),
      WEA   => WE(7),
      DOB   => DOUTB7,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(2),
      WEB   => '0');

  -----------------------------------------------------------------------------
  --  
  -- OPB-RAM controller
  --
  -----------------------------------------------------------------------------
  
  -- Unused OPB control signals
  VGA_errAck <= '0';
  VGA_retry <= '0';
  VGA_toutSup <= '0';

  -- Latch the relevant OPB signals from the OPB, since they arrive late
  LatchOPB: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Abus <= ( others => '0' );
      DBus <= ( others => '0' );
      RNW <= '1';
      select_delayed <= '0';
    elsif OPB_Clk'event and OPB_Clk = '1' then
      ABus <= OPB_ABus;
      DBus <= OPB_DBus;
      RNW <= OPB_RNW;
      select_delayed <= OPB_Select;
    end if;
  end process LatchOPB;

  -- Address bits 31 downto 12 is our chip select
  -- 11 downto 9 is the RAM page select
  -- 8 downto 0 is the RAM byte select
  
  ChipSelect <=
    '1' when select_delayed = '1' and
       (ABus(31 downto 12) = BASEADDR(31 downto 12)) and
       MemCycle1 = '0' and MemCycle2 = '0' else
    '0';

  RamPageAddress <= ABus(11 downto 9);
  
  RamSelect <=
    "00000001" when RamPageAddress = "000" else
    "00000010" when RamPageAddress = "001" else
    "00000100" when RamPageAddress = "010" else
    "00001000" when RamPageAddress = "011" else
    "00010000" when RamPageAddress = "100" else
    "00100000" when RamPageAddress = "101" else
    "01000000" when RamPageAddress = "110" else
    "10000000" when RamPageAddress = "111" else
    "00000000";

  MemCycleFSM : process(OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      MemCycle1 <= '0';
      MemCycle2 <= '0';
    elsif OPB_Clk'event and OPB_Clk = '1' then
      MemCycle2 <= MemCycle1;
      MemCycle1 <= ChipSelect;
    end if;
  end process MemCycleFSM;
  
  VGA_xferAck <= MemCycle2;

  WE <=
  RamSelect when ChipSelect = '1' and RNW = '0' and OPB_Rst = '0' else
    "00000000";
 
  RST <=
   not RamSelect when ChipSelect = '1' and RNW = '1' and OPB_Rst = '0' else
    "11111111"; 

  ReadData <= DOUT0 or DOUT1 or DOUT2 or DOUT3 or
              DOUT4 or DOUT5 or DOUT6 or DOUT7 when MemCycle1 = '1'
              else "00000000";

  -- DBus(31 downto 24) is the byte for addresses ending in 0

  GenDOut: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      DBus_out <= ( others => '0');
    elsif OPB_Clk'event and OPB_Clk = '1' then
      DBus_out <= ReadData & ReadData & ReadData & ReadData;
    end if;
  end process GenDOut;

  VGA_DBus <= DBus_out;

  -----------------------------------------------------------------------------
  --
  -- video decoder
  --
  -----------------------------------------------------------------------------

  SRAM_START_ADDRESS <= (others => '0');     -- hardwired for now

  SRAM_address : process(Hcount)  -- assert address to SRAM
  begin
  -- if(hcount == 0)   ? restarting screendraw
       PB_A <= SRAM_START_ADDRESS;   -- plus whatever...
  end process SRAM_address;

  read_SRAM : process(sln_xferAck)
  begin
    raw_SRAM_data <= PB_D;
  end process read_SRAM;
  
  video_decode : process(raw_SRAM_data)
  begin
    --decoded_SRAM_data <= (some voodoo) <= raw_SRAM_data; -- decode the raw data
    decoded_SRAM_data(15 downto 0) <= raw_SRAM_data; -- decode the raw data    
  end process video_decode;
    
  update_pixel : process(Pixel_Clock)
  begin
    if Pixel_Clock'event and Pixel_Clock = '1' then
      next_pixel_to_display <= decoded_SRAM_data(0);
    end if;
  end process update_pixel;

  ----------------------------------------------------------------------------
  --
  -- Video controller
  --
  -----------------------------------------------------------------------------

  -- Horizontal and vertical counters

  HCounter : process (Pixel_Clock, OPB_Rst)
  begin    
    if OPB_Rst = '1' then
      Hcount <= (others => '0');
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if EndOfLine = '1' then
        Hcount <= (others => '0');
      else
        Hcount <= Hcount + 1;
      end if;      
    end if;
  end process HCounter;

  EndOfLine <= '1' when Hcount = HTOTAL - 1 else '0';
 
  VCounter: process (Pixel_Clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Vcount <= (others => '0');
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if EndOfLine = '1' then
        if EndOfField = '1' then
          Vcount <= (others => '0');
        else
          Vcount <= Vcount + 1;
        end if;
      end if;
    end if;
  end process VCounter;

  EndOfField <= '1' when Vcount = VTOTAL - 1 else '0';

  -- State machines to generate HSYNC, VSYNC, HBLANK, and VBLANK

  HSyncGen : process (Pixel_Clock, OPB_Rst)
  begin    
    if OPB_Rst = '1' then
      VIDOUT_HSYNC_N <= '0';
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if EndOfLine = '1' then
        VIDOUT_HSYNC_N <= '0';
      elsif Hcount = HSYNC - 1 then
        VIDOUT_HSYNC_N <= '1';
      end if;      
    end if;
  end process HSyncGen;

  -- The -1 correction doesn't appear here to correct for the
  -- registered video signal outputs.
  
  HBlankGen : process (Pixel_Clock, OPB_Rst)
  begin    
    if OPB_Rst = '1' then
      HBLANK_N <= '0';
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if Hcount = HSYNC + HBACK_PORCH then
        HBLANK_N <= '1';
      elsif Hcount = HSYNC + HBACK_PORCH + HACTIVE then
        HBLANK_N <= '0';
      end if;      
    end if;
  end process HBlankGen;

  VSyncGen : process (Pixel_Clock, OPB_Rst)
  begin    
    if OPB_Rst = '1' then
      VIDOUT_VSYNC_N <= '0';
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if EndOfLine ='1' then
        if EndOfField = '1' then
          VIDOUT_VSYNC_N <= '0';
        elsif VCount = VSYNC - 1 then
          VIDOUT_VSYNC_N <= '1';
        end if;
      end if;      
    end if;
  end process VSyncGen;

  VBlankGen : process (Pixel_Clock, OPB_Rst)
  begin    
    if OPB_Rst = '1' then
      VBLANK_N <= '0';
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if EndOfLine ='1' then
        if Vcount = VSYNC + VBACK_PORCH - 1 then
          VBLANK_N <= '1';
        elsif VCount = VSYNC + VBACK_PORCH + VACTIVE - 1 then
          VBLANK_N <= '0';
        end if;
      end if;      
    end if;
  end process VBlankGen;

  -- RAM read triggers and shift register control

  LoadChar   <= '1' when Hcount(2 downto 0) = X"5" else '0';
  FontLoad   <= '1' when Hcount(2 downto 0) = X"6" else '0'; 
  LoadNShift <= '1' when Hcount(2 downto 0) = X"7" else '0';

  -- Correction of 4 needed to calculate the character address before the
  -- character is displayed
  CharColumn <= Hcount - HSYNC - HBACK_PORCH + 4;
  Column <= CharColumn(9 downto 3);  -- /8
  CharRow <= Vcount - VSYNC - VBACK_PORCH;
  Row <= CharRow(8 downto 4);  -- / 16

  -- Column + Row * 80
  CharAddr <= Column +
              ("0" & Row(4 downto 0) & "000000") +
              ("000" & Row(4 downto 0) & "0000");

  CharRamPage <= CharAddr(11 downto 9);
  CharRamSelet_N <=
    "11110" when CharRamPage = "000" else
    "11101" when CharRamPage = "001" else
    "11011" when CharRamPage = "010" else
    "10111" when CharRamPage = "011" else
    "01111" when CharRamPage = "100" else
    "11111";

  -- Most significant bit of character ignored
  FontAddr(10 downto 4) <=
    (DOUTB0(6 downto 0) or DOUTB1(6 downto 0) or DOUTB2(6 downto 0) or
     DOUTB3(6 downto 0) or DOUTB4(6 downto 0));
  FontAddr(3 downto 0) <= CharRow(3 downto 0);

  -- Unusual addressing: font only holds 96 of 128 possible characters
  -- First 32 characters appear twice
  FontRamPage <= FontAddr(10 downto 9);
  FontRamSelect_N <=
    "110" when FontRamPage = "00" else
    "110" when FontRamPage = "01" else
    "101" when FontRamPage = "10" else
    "011" when FontRamPage = "11" else
    "111";

  FontData <= DOUTB5 or DOUTB6 or DOUTB7;
  
  -- Shift register

  ShiftRegister: process (Pixel_Clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      ShiftData <= X"AB";
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if LoadNShift = '1' then
        ShiftData <= FontData;
      else
        ShiftData <= ShiftData(6 downto 0) & ShiftData(7);
      end if;
    end if;
  end process ShiftRegister;

  VideoData <= ShiftData(7);

  -- Registered video signals going to the video DAC

  VideoOut: process (Pixel_Clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      VIDOUT_BLANK_N <= '0';
      VIDOUT_RED   <= "0000000000";
      VIDOUT_BLUE  <= "0000000000";
      VIDOUT_GREEN <= "0000000000";
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      VIDOUT_BLANK_N <= VBLANK_N and HBLANK_N;
    --if VideoData = '1' then
    if next_pixel_to_display = '1' then
--      if (Vcount(6) xor Hcount(6)) = '1' then
        VIDOUT_RED   <= "1111111111";
        VIDOUT_GREEN <= "1111111111";--GreenData & "00";
        VIDOUT_BLUE  <= "1111111111";--BlueData & "00";
        -- display next_pixel_to_display
        --VIDOUT_GREEN <= "1111111111";
        --VIDOUT_BLUE  <= "1111111111";
      else
        VIDOUT_RED   <= "0000000000";
        VIDOUT_GREEN <= "0000000000";
        VIDOUT_BLUE  <= "0000000000";
      end if;
    end if;
  end process VideoOut;

  VIDOUT_CLK <= Pixel_Clock;


  -----------------------------------------------------------------------------
  --BEGIN SRAM STUFF
  -----------------------------------------------------------------------------

  address: for i in 0 to 17 generate
  OBUFBlock  : OBUF_F_24
    port map (
      O => PB_A(i),
      I  => saddin(i));
  end generate;

  data : for j in 0 to 15 generate
  IOBUFBlock : IOBUF_F_24
    port map (
      O => sramout(j),
      IO => PB_D(j),
      I => sramin(j),
      T => tri_iob);
  end generate;


  
  register_opb_inputs: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      sramin <= (others => '0');
      saddin <= (others => '0');
      sram_RNW <= '0';
    elsif OPB_Clk'event and OPB_Clk = '1' then
      sramin <= OPB_DBus( RAM_DWIDTH-1 downto 0);
      saddin <= OPB_ABus(C_OPB_AWIDTH-3 downto C_OPB_AWIDTH-3-(RAM_AWIDTH-1));
      sram_RNW <= OPB_RNW;
    end if;
  end process register_opb_inputs;

  register_opb_outputs: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Sln_DBus(0 to RAM_DWIDTH-1) <= (others => '0');

    elsif OPB_Clk'event and OPB_Clk = '1' then
     if output_enable = '1' then
        Sln_DBus(0 to RAM_DWIDTH-1) <= sramout;
     else
       Sln_DBus(0 to RAM_DWIDTH-1) <= (others => '0');
     end if;
    end if;
  end process register_opb_outputs;

  -- Unused outputs
  Sln_errAck  <= '0';
  Sln_retry   <= '0';
  Sln_toutSup <= '0';
  Sln_DBus(RAM_DWIDTH to C_OPB_DWIDTH-1) <= (others => '0');

  chip_select <=
    '1' when OPB_select = '1' and
        OPB_ABus(C_OPB_AWIDTH-3-RAM_AWIDTH downto 0) =
        C_BASEADDR(C_OPB_AWIDTH-3-RAM_AWIDTH downto 0) else
    '0';
  
  RAM_CE <= '0';
  PB_WE <= sram_WE;
  PB_OE <= OE;
  PB_UB <= UB;
  PB_LB <= LB;
 -- iobuf <= '0' when output_enable = '1' else '1';
 --iobuf <= output_eable;
  -- Sequential part of the FSM
  fsm_seq : process(OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      present_state <= Idle;
    elsif OPB_Clk'event and OPB_Clk = '1' then
      present_state <= next_state;
      
    end if;
  end process fsm_seq;

  -- Combinational part of the FSM
  fsm_comb : process(OPB_Rst, present_state, chip_select, OPB_Select, sram_RNW)
  begin                         -- Default values

    Sln_xferAck <= '0';
    next_state <= present_state;
    output_enable <= '0';
    tri_iob <= '1';
    sram_WE <= '1';
    UB <= '0';
    LB <= '0';
    OE <= '1';
    
    case present_state is

        when Idle =>        
          if chip_select = '1' then
            next_state <= Selected;
          end if;

      when Selected =>
          if OPB_Select = '1' then          
            if sram_RNW = '1' then           -- read
              OE <= '0';
              next_state <= Read;
            else
              -- write
              sram_WE <= '0';
              tri_iob <= '0';
              next_state <= Xfer;
            end if;
          else
            next_state <= Idle;
          end if;

        when Read =>
      
          if OPB_Select = '1' then
            OE <= '0';
            output_enable <= '1';
            next_state <= Xfer;
          else
            next_state <= Idle;
          end if;

        -- State encoding is critical here: xfer must only be true here
        when Xfer =>
          Sln_xferAck <= '1';
          next_state <= Idle;
         
      end case;

end process fsm_comb;

  
end Behavioral;

-- Only for syntax debugging:
--
-- Local Variables:
-- compile-command: "ghdl -a --ieee=synopsys opb_xsb300e_vga.vhd"
-- End:
