library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity sysbus is
  port(
        clk: in std_logic;
        rst: in std_logic;

  -- master port 0
        m0_addr: in std_logic_vector(15 downto 0);
        m0_wdata: in std_logic_vector(15 downto 0);
        m0_rdata: out std_logic_vector(15 downto 0);
        m0_re: in std_logic;
        m0_we: in std_logic;
        m0_busy: out std_logic;

  -- master port 1
        m1_addr: in std_logic_vector(15 downto 0);
        m1_wdata: in std_logic_vector(15 downto 0);
        m1_rdata: out std_logic_vector(15 downto 0);
        m1_re: in std_logic;
        m1_we: in std_logic;
        m1_busy: out std_logic;

  -- actual bus
        bus_addr: out std_logic_vector(15 downto 0);
        bus_wdata: out std_logic_vector(15 downto 0);
        bus_rdata: in std_logic_vector(15 downto 0);
        bus_re: out std_logic;
        bus_we: out std_logic
      );
end entity sysbus;

architecture behavior of sysbus is
  type bus_state is (IDLE, READING, PENDING);

  signal owner_next, owner: std_logic_vector(0 to 1);
  signal state_next, state: bus_state;

  signal pend_addr, pend_addr_next: std_logic_vector(15 downto 0);
  signal pend_wdata, pend_wdata_next: std_logic_vector(15 downto 0);
  signal pend, pend_next: std_logic_vector(0 to 1);

  signal were: std_logic_vector(0 to 3);
begin

  were <= m0_we & m1_we & m0_re & m1_re;

  process(were, m0_addr, m0_wdata, m1_addr, m1_wdata, bus_rdata, state, owner, pend, pend_addr, pend_wdata)
  begin
    if pend = "00" then
      m0_busy <= '0';
      m1_busy <= '0';
    else
      m0_busy <= '1';
      m1_busy <= '1';
    end if;

    pend_next <= pend;
    pend_addr_next <= pend_addr;
    pend_wdata_next <= pend_wdata;
    owner_next <= "00";
    state_next <= IDLE;

    bus_we <= '0';
    bus_re <= '0';

    -- some defaults
    bus_addr <= x"0000";
    bus_wdata <= x"0000";

    m0_rdata <= x"0000";
    m1_rdata <= x"0000";

    case state is
      when READING =>
        if owner = "10" then
          bus_addr <= m0_addr;
          m0_rdata <= bus_rdata;
          m1_busy <= '1';
        else
          bus_addr <= m1_addr;
          m1_rdata <= bus_rdata;
          m0_busy <= '1';
        end if;
        if pend = "00" then
          state_next <= IDLE;
        else
          state_next <= PENDING;
        end if;

      when PENDING =>
        bus_addr <= pend_addr;
        bus_we <= '1';
        bus_wdata <= pend_wdata;
        pend_next <= "00";
        state_next <= IDLE;

      when IDLE =>
        case were is
          when "0000" =>
            -- chill
          when "1000" =>
            bus_addr <= m0_addr;
            bus_wdata <= m0_wdata;
            bus_we <= '1';
            bus_re <= '0';
          when "0100" =>
            bus_addr <= m1_addr;
            bus_wdata <= m1_wdata;
            bus_we <= '1';
            bus_re <= '0';
          when "1100" =>
            bus_addr <= m0_addr;
            bus_wdata <= m0_wdata;
            bus_we <= '1';
            bus_re <= '0';

            pend_next <= "01";
            pend_addr_next <= m1_addr;
            pend_wdata_next <= m1_wdata;
            state_next <= PENDING;

          when "0010" =>
            state_next <= READING;
            owner_next <= "10";

            bus_addr <= m0_addr;
            bus_we <= '0';
            bus_re <= '1';

            m1_busy <= '1';

          when "0001" =>
            state_next <= READING;
            owner_next <= "01";

            bus_addr <= m1_addr;
            bus_we <= '0';
            bus_re <= '1';

            m0_busy <= '1';

          when "0011" =>
            state_next <= READING;
            owner_next <= "10";

            bus_addr <= m0_addr;
            bus_we <= '0';
            bus_re <= '1';

            m1_busy <= '1';

          when "1001" =>
            state_next <= READING;
            owner_next <= "01";

            bus_addr <= m1_addr;
            bus_we <= '0';
            bus_re <= '1';

            pend_next <= "10";
            pend_addr_next <= m0_addr;
            pend_wdata_next <= m0_wdata;

            m0_busy <= '1';

          when "0110" =>
            state_next <= READING;
            owner_next <= "10";

            bus_addr <= m0_addr;
            bus_we <= '0';
            bus_re <= '1';

            pend_next <= "01";
            pend_addr_next <= m1_addr;
            pend_wdata_next <= m1_wdata;

            m1_busy <= '1';

          when others =>
            -- blarg
        end case;
    end case;
  end process;

  process(clk, rst)
  begin
    if rst = '1' then
      state <= IDLE;
      owner <= "00";
      pend <= "00";
      pend_addr <= x"0000";
      pend_wdata <= x"0000";
    elsif rising_edge(clk) then
      state <= state_next;
      owner <= owner_next;
      pend <= pend_next;
      pend_addr <= pend_addr_next;
      pend_wdata <= pend_wdata_next;
    end if;
  end process;

end behavior;
