// Copyright 2018-2019 Delft University of Technology
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

namespace fletchgen::top {

// TODO(johanpel): Insert this template through resource linking.
/// Simulation top level template source.
static char sim_source[] =
    "-- Copyright 2018-2019 Delft University of Technology\n"
    "--\n"
    "-- Licensed under the Apache License, Version 2.0 (the \"License\");\n"
    "-- you may not use this file except in compliance with the License.\n"
    "-- You may obtain a copy of the License at\n"
    "--\n"
    "--     http://www.apache.org/licenses/LICENSE-2.0\n"
    "--\n"
    "-- Unless required by applicable law or agreed to in writing, software\n"
    "-- distributed under the License is distributed on an \"AS IS\" BASIS,\n"
    "-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
    "-- See the License for the specific language governing permissions and\n"
    "-- limitations under the License.\n"
    "\n"
    "library ieee;\n"
    "use ieee.std_logic_1164.all;\n"
    "use ieee.numeric_std.all;\n"
    "use ieee.std_logic_misc.all;\n"
    "\n"
    "library work;\n"
    "use work.Interconnect_pkg.all;\n"
    "use work.UtilStr_pkg.all;\n"
    "use work.UtilConv_pkg.all;\n"
    "\n"
    "entity SimTop_tc is\n"
    "  generic (\n"
    "    -- Accelerator properties\n"
    "    INDEX_WIDTH                 : natural := 32;\n"
    "    TAG_WIDTH                   : natural := 1;\n"
    "\n"
    "    -- Host bus properties\n"
    "    BUS_ADDR_WIDTH              : natural := ${BUS_ADDR_WIDTH};\n"
    "    BUS_DATA_WIDTH              : natural := ${BUS_DATA_WIDTH};\n"
    "    BUS_LEN_WIDTH               : natural := ${BUS_LEN_WIDTH};\n"
    "    BUS_BURST_MAX_LEN           : natural := ${BUS_BURST_MAX_LEN};\n"
    "    BUS_BURST_STEP_LEN          : natural := ${BUS_BURST_STEP_LEN}\n"
    "  );\n"
    "end SimTop_tc;\n"
    "\n"
    "architecture Behavorial of SimTop_tc is\n"
    "\n"
    "  -----------------------------------------------------------------------------\n"
    "  -- Default wrapper component.\n"
    "  -----------------------------------------------------------------------------\n"
    "  ${MANTLE_DECL}"
    "  -----------------------------------------------------------------------------\n"
    "\n"
    "  -- Fletcher defaults\n"
    "  constant REG_CONTROL          : natural := 0;\n"
    "  constant REG_STATUS           : natural := 1;\n"
    "  constant REG_RETURN0          : natural := 2;\n"
    "  constant REG_RETURN1          : natural := 3;\n"
    "\n"
    "  constant CONTROL_CLEAR        : std_logic_vector(31 downto 0) := X\"00000000\";\n"
    "  constant CONTROL_START        : std_logic_vector(31 downto 0) := X\"00000001\";\n"
    "  constant CONTROL_STOP         : std_logic_vector(31 downto 0) := X\"00000002\";\n"
    "  constant CONTROL_RESET        : std_logic_vector(31 downto 0) := X\"00000004\";\n"
    "\n"
    "  constant STATUS_IDLE          : std_logic_vector(31 downto 0) := X\"00000001\";\n"
    "  constant STATUS_BUSY          : std_logic_vector(31 downto 0) := X\"00000002\";\n"
    "  constant STATUS_DONE          : std_logic_vector(31 downto 0) := X\"00000004\";\n"
    "\n"
    "  -- Sim signals\n"
    "  signal clock_stop             : boolean := false;\n"
    "\n"
    "  -- Accelerator signals\n"
    "  signal kcd_clk                : std_logic;\n"
    "  signal kcd_reset              : std_logic;\n"
    "\n"
    "  -- Fletcher bus signals\n"
    "  signal bcd_clk                : std_logic;\n"
    "  signal bcd_reset              : std_logic;\n"
    "\n"
    "  -- External signals\n"
    "${EXTERNAL_SIG_DECL}"
    "\n"
    "  -- MMIO signals\n"
    "  signal mmio_awvalid           : std_logic := '0';\n"
    "  signal mmio_awready           : std_logic := '0';\n"
    "  signal mmio_awaddr            : std_logic_vector(${MMIO_ADDR_WIDTH}-1 downto 0);\n"
    "  signal mmio_wvalid            : std_logic := '0';\n"
    "  signal mmio_wready            : std_logic := '0';\n"
    "  signal mmio_wdata             : std_logic_vector(${MMIO_DATA_WIDTH}-1 downto 0);\n"
    "  signal mmio_wstrb             : std_logic_vector(${MMIO_DATA_WIDTH}/8-1 downto 0);\n"
    "  signal mmio_bvalid            : std_logic := '0';\n"
    "  signal mmio_bready            : std_logic := '0';\n"
    "  signal mmio_bresp             : std_logic_vector(1 downto 0);\n"
    "  signal mmio_arvalid           : std_logic := '0';\n"
    "  signal mmio_arready           : std_logic := '0';\n"
    "  signal mmio_araddr            : std_logic_vector(${MMIO_ADDR_WIDTH}-1 downto 0);\n"
    "  signal mmio_rvalid            : std_logic := '0';\n"
    "  signal mmio_rready            : std_logic := '0';\n"
    "  signal mmio_rdata             : std_logic_vector(${MMIO_DATA_WIDTH}-1 downto 0);\n"
    "  signal mmio_rresp             : std_logic_vector(1 downto 0);\n"
    "\n"
    "  -- Mmio signals to source in mmio procedures.\n"
    "  type mmio_source_t is record\n"
    "    awvalid           : std_logic;\n"
    "    awaddr            : std_logic_vector(${MMIO_ADDR_WIDTH}-1 downto 0);\n"
    "    wvalid            : std_logic;\n"
    "    wdata             : std_logic_vector(${MMIO_DATA_WIDTH}-1 downto 0);\n"
    "    wstrb             : std_logic_vector(${MMIO_DATA_WIDTH}/8-1 downto 0);\n"
    "    bready            : std_logic;\n"
    "\n"
    "    arvalid           : std_logic;\n"
    "    araddr            : std_logic_vector(${MMIO_ADDR_WIDTH}-1 downto 0);\n"
    "    rready            : std_logic;\n"
    "  end record;\n"
    "\n"
    "  -- Mmio signals to sink in mmio procedures\n"
    "  type mmio_sink_t is record\n"
    "    wready            : std_logic;\n"
    "\n"
    "    awready           : std_logic;\n"
    "\n"
    "    bvalid            : std_logic;\n"
    "    bresp             : std_logic_vector(1 downto 0);\n"
    "\n"
    "    arready           : std_logic;\n"
    "\n"
    "    rvalid            : std_logic;\n"
    "    rdata             : std_logic_vector(${MMIO_DATA_WIDTH}-1 downto 0);\n"
    "    rresp             : std_logic_vector(1 downto 0);\n"
    "  end record;\n"
    "\n"
    "  signal mmio_source : mmio_source_t;\n"
    "  signal mmio_sink : mmio_sink_t;\n"
    "\n"
    "  -- Memory interface signals\n"
    "  signal bus_rreq_addr          : std_logic_vector(BUS_ADDR_WIDTH-1 downto 0);\n"
    "  signal bus_rreq_len           : std_logic_vector(BUS_LEN_WIDTH-1 downto 0);\n"
    "  signal bus_rreq_valid         : std_logic;\n"
    "  signal bus_rreq_ready         : std_logic;\n"
    "  signal bus_rdat_data          : std_logic_vector(BUS_DATA_WIDTH-1 downto 0);\n"
    "  signal bus_rdat_last          : std_logic;\n"
    "  signal bus_rdat_valid         : std_logic;\n"
    "  signal bus_rdat_ready         : std_logic;\n"
    "  signal bus_wreq_addr          : std_logic_vector(BUS_ADDR_WIDTH-1 downto 0);\n"
    "  signal bus_wreq_last          : std_logic;\n"
    "  signal bus_wreq_len           : std_logic_vector(BUS_LEN_WIDTH-1 downto 0);\n"
    "  signal bus_wreq_valid         : std_logic;\n"
    "  signal bus_wreq_ready         : std_logic;\n"
    "  signal bus_wdat_data          : std_logic_vector(BUS_DATA_WIDTH-1 downto 0);\n"
    "  signal bus_wdat_strobe        : std_logic_vector(BUS_DATA_WIDTH/8-1 downto 0);\n"
    "  signal bus_wdat_last          : std_logic;\n"
    "  signal bus_wdat_valid         : std_logic;\n"
    "  signal bus_wdat_ready         : std_logic;\n"
    "  signal bus_wrep_ok            : std_logic;\n"
    "  signal bus_wrep_valid         : std_logic;\n"
    "  signal bus_wrep_ready         : std_logic;\n"
    "\n"
    "  procedure mmio_write32 (constant idx    : in  natural;\n"
    "                          constant data   : in  std_logic_vector(31 downto 0);\n"
    "                          signal   source : out mmio_source_t;\n"
    "                          signal   sink   : in  mmio_sink_t;\n"
    "                          signal   clk    : in  std_logic;\n"
    "                          signal   reset  : in  std_logic)\n"
    "  is\n"
    "  begin\n"
    "    -- Wait for reset\n"
    "    loop\n"
    "      exit when reset = '0';\n"
    "      wait until rising_edge(clk);\n"
    "    end loop;\n"
    "    -- Address write channel\n"
    "    source.awaddr <= slv(${MMIO_OFFSET} + (32/8)*idx, ${MMIO_ADDR_WIDTH});\n"
    "    source.awvalid <= '1';\n"
    "    loop\n"
    "      wait until rising_edge(clk);\n"
    "      exit when sink.awready = '1';\n"
    "    end loop;\n"
    "    source.awvalid <= '0';\n"
    "    source.awaddr <= (others => 'U');\n"
    "    -- Write channel\n"
    "    source.wdata${MMIO_RW_DATA_RANGE} <= data;\n"
    "    source.wstrb <= ${MMIO_STRB};\n"
    "    source.wvalid <= '1';\n"
    "    loop\n"
    "      wait until rising_edge(clk);\n"
    "      exit when sink.wready = '1';\n"
    "    end loop;\n"
    "    source.wvalid <= '0';\n"
    "    source.wdata <= (others => 'U');\n"
    "    source.wstrb <= (others => 'U');\n"
    "    -- Write response channel.\n"
    "    source.bready <= '1';\n"
    "    loop\n"
    "      wait until rising_edge(clk);\n"
    "      exit when sink.bvalid = '1';\n"
    "    end loop;\n"
    "    source.bready <= '0';\n"
    "  end procedure;\n"
    "\n"
    "  procedure mmio_read32(constant idx    : in  natural;\n"
    "                        variable data   : out std_logic_vector(31 downto 0);\n"
    "                        signal   source : out mmio_source_t;\n"
    "                        signal   sink   : in  mmio_sink_t;\n"
    "                        signal   clk    : in  std_logic;\n"
    "                        signal   reset  : in  std_logic)\n"
    "  is\n"
    "  begin\n"
    "    -- Wait for reset\n"
    "    loop\n"
    "      exit when reset = '0';\n"
    "      wait until rising_edge(clk);\n"
    "    end loop;\n"
    "    -- Address read channel\n"
    "    source.araddr <= slv(${MMIO_OFFSET} + (32/8)*idx, ${MMIO_ADDR_WIDTH});\n"
    "    source.arvalid <= '1';\n"
    "    loop\n"
    "      wait until rising_edge(clk);\n"
    "      exit when sink.arready = '1';\n"
    "    end loop;\n"
    "    source.arvalid <= '0';\n"
    "    source.araddr <= (others => 'U');\n"
    "    -- Read channel\n"
    "    loop\n"
    "      source.rready <= '1';\n"
    "      wait until rising_edge(clk);\n"
    "      if sink.rvalid = '1' then\n"
    "        data := sink.rdata${MMIO_RW_DATA_RANGE};\n"
    "        exit;\n"
    "      end if;\n"
    "    end loop;\n"
    "    source.rready <= '0';\n"
    "  end procedure;\n"
    "\n"
    "\n"
    "begin\n"
    "\n"
    "  -- Connect to records for easier readibility downstream.\n"
    "  mmio_awvalid <= mmio_source.awvalid;\n"
    "  mmio_awaddr  <= mmio_source.awaddr;\n"
    "  mmio_wvalid  <= mmio_source.wvalid;\n"
    "  mmio_wdata   <= mmio_source.wdata;\n"
    "  mmio_wstrb   <= mmio_source.wstrb;\n"
    "  mmio_bready  <= mmio_source.bready;\n"
    "  mmio_arvalid <= mmio_source.arvalid;\n"
    "  mmio_araddr  <= mmio_source.araddr;\n"
    "  mmio_rready  <= mmio_source.rready;\n"
    "\n"
    "  mmio_sink.wready  <= mmio_wready;\n"
    "  mmio_sink.awready <= mmio_awready;\n"
    "  mmio_sink.bvalid  <= mmio_bvalid;\n"
    "  mmio_sink.bresp   <= mmio_bresp;\n"
    "  mmio_sink.arready <= mmio_arready;\n"
    "  mmio_sink.rvalid  <= mmio_rvalid;\n"
    "  mmio_sink.rdata   <= mmio_rdata;\n"
    "  mmio_sink.rresp   <= mmio_rresp;\n"
    "\n"
    "\n"
    "  -- Typical stimuli process:\n"
    "  stimuli_proc : process is\n"
    "    variable read_data        : std_logic_vector(31 downto 0) := X\"DEADBEEF\";\n"
    "    variable read_data_masked : std_logic_vector(31 downto 0);\n"
    "  begin\n"
    "    mmio_source.awvalid <= '0';\n"
    "    mmio_source.wvalid  <= '0';\n"
    "    mmio_source.bready  <= '0';\n"
    "\n"
    "    mmio_source.arvalid <= '0';\n"
    "    mmio_source.rready  <= '0';\n"
    "\n"
    "    wait until kcd_reset = '1' and bcd_reset = '1';\n"
    "\n"
    "    -- 1. Reset the user core\n"
    "    mmio_write32(REG_CONTROL, CONTROL_RESET, mmio_source, mmio_sink, bcd_clk, bcd_reset);\n"
    "\n"
    "    -- 2. Write addresses of the arrow buffers in the SREC file.\n"
    "${SREC_BUFFER_ADDRESSES}\n"
    "    -- 3. Write recordbatch bounds.\n"
    "${SREC_FIRSTLAST_INDICES}\n"
    "    -- 4. Write any kernel-specific registers.\n"
    "${KERNEL_REGS_INIT}\n"
    "    -- 5. Start the kernel.\n"
    "${PROFILE_START}"
    "    mmio_write32(REG_CONTROL, CONTROL_START, mmio_source, mmio_sink, bcd_clk, bcd_reset);\n"
    "\n"
    "    -- 6. Poll for completion\n"
    "    loop\n"
    "      -- Wait a bunch of cycles.\n"
    "      for I in 0 to 8 loop\n"
    "        wait until rising_edge(bcd_clk);\n"
    "      end loop;\n"
    "\n"
    "      -- Read the status register.\n"
    "      mmio_read32(REG_STATUS, read_data, mmio_source, mmio_sink, bcd_clk, bcd_reset);\n"
    "\n"
    "      -- Check if we're done.\n"
    "      read_data_masked := read_data and STATUS_DONE;\n"
    "      exit when read_data_masked = STATUS_DONE;\n"
    "    end loop;\n"
    "\n"
    "${PROFILE_STOP}\n"
    "    -- 7. Read return register.\n"
    "    mmio_read32(REG_RETURN0, read_data, mmio_source, mmio_sink, bcd_clk, bcd_reset);\n"
    "    println(\"Return register 0: \" & slvToHex(read_data));\n"
    "    mmio_read32(REG_RETURN1, read_data, mmio_source, mmio_sink, bcd_clk, bcd_reset);\n"
    "    println(\"Return register 1: \" & slvToHex(read_data));\n"
    "\n"
    "    -- 8. Read profile registers.\n"
    "${PROFILE_READ}\n"
    "    -- 9. Finish and stop simulation.\n"
    "    report \"Stimuli done.\";\n"
    "    clock_stop <= true;\n"
    "\n"
    "    wait;\n"
    "  end process;\n"
    "\n"
    "  clk_proc: process is\n"
    "  begin\n"
    "    if not clock_stop then\n"
    "      kcd_clk <= '1';\n"
    "      bcd_clk <= '1';\n"
    "      wait for 5 ns;\n"
    "      kcd_clk <= '0';\n"
    "      bcd_clk <= '0';\n"
    "      wait for 5 ns;\n"
    "    else\n"
    "      wait;\n"
    "    end if;\n"
    "  end process;\n"
    "\n"
    "  reset_proc: process is\n"
    "  begin\n"
    "    kcd_reset <= '1';\n"
    "    bcd_reset <= '1';\n"
    "    wait for 50 ns;\n"
    "    wait until rising_edge(kcd_clk);\n"
    "    kcd_reset <= '0';\n"
    "    bcd_reset <= '0';\n"
    "    wait;\n"
    "  end process;\n"
    "\n"
    "${BUS_READ_SLAVE_MOCK}\n"
    "${BUS_WRITE_SLAVE_MOCK}\n"
    "\n"
    "\n"
    "  -----------------------------------------------------------------------------\n"
    "  -- Fletcher generated wrapper\n"
    "  -----------------------------------------------------------------------------\n"
    "  ${FLETCHER_WRAPPER_INST_NAME} : ${FLETCHER_WRAPPER_NAME}\n"
    "    generic map (\n"
    "      BUS_ADDR_WIDTH            => BUS_ADDR_WIDTH,\n"
    "      BUS_DATA_WIDTH            => BUS_DATA_WIDTH,\n"
    "      BUS_BURST_STEP_LEN        => BUS_BURST_STEP_LEN,\n"
    "      BUS_BURST_MAX_LEN         => BUS_ADDR_WIDTH,\n"
    "      BUS_LEN_WIDTH             => BUS_LEN_WIDTH,\n"
    "      INDEX_WIDTH               => INDEX_WIDTH,\n"
    "      TAG_WIDTH                 => TAG_WIDTH\n"
    "    )\n"
    "    port map (\n"
    "      kcd_clk                   => kcd_clk,\n"
    "      kcd_reset                 => kcd_reset,\n"
    "      bcd_clk                   => bcd_clk,\n"
    "      bcd_reset                 => bcd_reset,\n"
    "${EXTERNAL_INST_MAP}\n"
    "${MST_RREQ_INSTANTIATE}\n"
    "${MST_WREQ_INSTANTIATE}\n"
    "      mmio_awvalid              => mmio_awvalid,\n"
    "      mmio_awready              => mmio_awready,\n"
    "      mmio_awaddr               => mmio_awaddr,\n"
    "      mmio_wvalid               => mmio_wvalid,\n"
    "      mmio_wready               => mmio_wready,\n"
    "      mmio_wdata                => mmio_wdata,\n"
    "      mmio_wstrb                => mmio_wstrb,\n"
    "      mmio_bvalid               => mmio_bvalid,\n"
    "      mmio_bready               => mmio_bready,\n"
    "      mmio_bresp                => mmio_bresp,\n"
    "      mmio_arvalid              => mmio_arvalid,\n"
    "      mmio_arready              => mmio_arready,\n"
    "      mmio_araddr               => mmio_araddr,\n"
    "      mmio_rvalid               => mmio_rvalid,\n"
    "      mmio_rready               => mmio_rready,\n"
    "      mmio_rdata                => mmio_rdata,\n"
    "      mmio_rresp                => mmio_rresp\n"
    "    );\n"
    "\n"
    "end architecture;\n";

}  // namespace fletchgen::top
