-------------------------------------------------------------------------------
--
-- Simple OPB peripheral: a BRAM controller
--
-- 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_bram is
  
  generic (
    C_OPB_AWIDTH : integer                   := 32;
    C_OPB_DWIDTH : integer                   := 32;
    C_BASEADDR   : std_logic_vector(0 to 31) := X"00000000";
    C_HIGHADDR   : std_logic_vector(0 to 31) := X"FFFFFFFF");

  port (
    OPB_Clk     : in  std_logic;
    OPB_Rst     : in  std_logic;
    OPB_ABus    : in  std_logic_vector(0 to C_OPB_AWIDTH-1);
    OPB_BE      : in  std_logic_vector(0 to C_OPB_DWIDTH/8-1);
    OPB_DBus    : in  std_logic_vector(0 to C_OPB_DWIDTH-1);
    OPB_RNW     : in  std_logic;
    OPB_select  : in  std_logic;
    OPB_seqAddr : in  std_logic;        -- Sequential Address
    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;
    PB_CE       : out std_logic);       -- Transfer acknowledge

    
  
end opb_bram;

architecture Behavioral of opb_bram is

  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 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, 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;     

  -- 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

 -- RAMBlock : RAMB4_S8
 -- port map (
 --   DO   => RAM_DO,
  --  ADDR => ABus,
  --  CLK  => OPB_Clk,
  --  DI   => RAM_DI,
  --  EN   => '1',
  --  RST  => RST,
  --  WE   => WE);
  
  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');
      RNW <= '0';
    elsif OPB_Clk'event and OPB_Clk = '1' then
      sramin <= OPB_DBus(0 to RAM_DWIDTH-1);
      saddin <= OPB_ABus(C_OPB_AWIDTH-3-(RAM_AWIDTH-1) to C_OPB_AWIDTH-3);
      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(0 to C_OPB_AWIDTH-3-RAM_AWIDTH) =
        C_BASEADDR(0 to C_OPB_AWIDTH-3-RAM_AWIDTH) else
    '0';
  
  PB_CE <= '0';
  PB_WE <= 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, RNW)
  begin                         -- Default values

    Sln_xferAck <= '0';
    next_state <= present_state;
    output_enable <= '0';
    tri_iob <= '1';
    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 RNW = '1' then           -- read
              OE <= '0';
              next_state <= Read;
            else
              -- write
              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;

-- Local Variables:
-- compile-command: "ghdl -a opb_bram.vhd"
-- End:
