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

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

    led: out std_logic_vector(7 downto 0);

    uart_rx: in std_logic;
    uart_tx: out std_logic
  );
end hello;

architecture rtl of hello is

  component cpu is port
    (
      clk: in std_logic;
      rst: in std_logic;

      code_data: in std_logic_vector(15 downto 0);
      code_addr: out std_logic_vector(15 downto 0);

      mem_in: in std_logic_vector(15 downto 0);
      mem_out: out std_logic_vector(15 downto 0);
      mem_addr: out std_logic_vector(15 downto 0);
      mem_write: out std_logic;
      mem_read: out std_logic
    );
  end component;

--  component boot_rom IS
--  PORT (
--    clka : IN STD_LOGIC;
--    addra : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
--    douta : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
--    clkb : IN STD_LOGIC;
--    addrb : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
--    doutb : OUT STD_LOGIC_VECTOR(15 DOWNTO 0)
--  );
--  END component;


--COMPONENT ram_mem
--  PORT (
--    clka : IN STD_LOGIC;
--    wea : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
--    addra : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
--    dina : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
--    douta : OUT STD_LOGIC_VECTOR(15 DOWNTO 0)
--  );
--END COMPONENT;

  component ram is
    generic
    (
      addressWidth : in positive := 16;
      busWidth : in positive := 16;
      size : in positive := 1024
    );
    port
    (
      clk : in std_logic;
      address : in std_logic_vector(addressWidth - 1 downto 0);
      writeEnable : in std_logic;
      dataIn : in std_logic_vector(busWidth - 1 downto 0);
      dataOut : out std_logic_vector(busWidth - 1 downto 0)
    );
  end component;

  component boot_rom is port
    (
      clk: in std_logic;

      code_addr : in std_logic_vector(15 downto 0);
      code_out : out std_logic_vector(15 downto 0);

      data_addr : in std_logic_vector(15 downto 0);
      data_out : out std_logic_vector(15 downto 0)
    );
  end component;

  component uart is
    port
    (
      clk     : in std_logic;
      rst     : in std_logic;

    -- hardware
      rx_pin  : in std_logic;
      tx_pin  : out std_logic;

    -- bus interface
      we      : in std_logic;
      re      : in std_logic;
      addr    : in std_logic_vector(15 downto 0);
      din     : in std_logic_vector(15 downto 0);
      dout    : out std_logic_vector(15 downto 0)
    );
  end component;

  signal mem_write, mem_read: std_logic;
  signal rom_code_addr, rom_code_out, mem_in, mem_out, mem_addr: std_logic_vector(15 downto 0);
  signal rom_data_addr, rom_data_out: std_logic_vector(15 downto 0);

  signal uart_din, uart_dout, uart_addr: std_logic_vector(15 downto 0);
  signal uart_we, uart_re: std_logic;

  signal bus_write, bus_read: std_logic;
  signal bus_mosi, bus_miso, bus_addr: std_logic_vector(15 downto 0);

  signal led_r, led_next: std_logic_vector(7 downto 0);

begin
  cpu0: cpu port map(clk, rst, rom_code_out, rom_code_addr, bus_miso, bus_mosi, bus_addr, bus_write, bus_read);
--  rom: boot_rom port map(
--    clka => clk, addra => rom_code_addr(8 downto 1), douta => rom_code_out,
--    clkb => clk, addrb => rom_data_addr(8 downto 1), doutb => rom_data_out
--  );
--  mem: ram_mem port map(clka => clk, wea(0) => mem_write, addra => mem_addr(8 downto 1), dina => mem_in, douta => mem_out);
  rom: boot_rom port map(clk => clk, code_addr => rom_code_addr, code_out => rom_code_out,
                         data_addr => rom_data_addr, data_out => rom_data_out);
  mem: ram port map(clk => clk, address => mem_addr, writeEnable => mem_write, dataIn => mem_in, dataOut => mem_out);

  uart0: uart port map(clk => clk, rst => rst, rx_pin => uart_rx, tx_pin => uart_tx,
                       addr => uart_addr, din => uart_din, dout => uart_dout, re => uart_re, we => uart_we);

  -- system map
  --  0x0000 - 0x0fff  ROM
  --  0x1000 - 0x1fff  RAM
  --  0xc000 - 0xc00f  LED
  --  0xc010 - 0xc01f  UART

  led <= led_r;

  process(clk, rst)
  begin
    if rising_edge(clk) then
      led_r <= led_next;
    end if;

    if rst = '1' then
      led_r <= x"00";
    end if;
  end process;

  process(bus_addr, bus_mosi, bus_write, mem_out, rst, rom_data_out, led_r, bus_read)
  begin

    bus_miso <= x"0000";

    rom_data_addr <= bus_addr and x"0fff";

    mem_addr <= bus_addr and x"0fff";
    mem_in <= bus_mosi;
    mem_write <= '0';

    led_next <= led_r;

    uart_din <= bus_mosi;
    uart_addr <= bus_addr and x"000f";
    uart_we <= '0';
    uart_re <= '0';

    case bus_addr(15 downto 12) is
      when x"0" =>
        bus_miso <= rom_data_out;
      when x"1" =>
        bus_miso <= mem_out;
        mem_write <= bus_write;
      when x"c" =>
        case bus_addr(7 downto 4) is
          when x"0" =>  -- LED
            if bus_write = '1' then
              led_next <= bus_mosi(7 downto 0);
            end if;
          when x"1" =>  -- UART
            bus_miso <= uart_dout;
            uart_we <= bus_write;
            uart_re <= bus_read;
          when others =>
        end case;
      when others =>
    end case;
  end process;

end rtl;
