---------------------------------------------------------------------------------------------------------------------
--                                                                                --
--  Author(s): Abdallah Cheikh abdallah.cheikh@uniroma1.it (abdallah93.as@gmail.com)                               --
--                                                                                --
--  Date Modified: 07-04-2020                                                                                --
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
--  Klessydra-T13 core v.5.0:                                                                                --
--  RISCY core pinout, RISC-V core, RV32IMA support plus the RISC-VEmbedded E-extension and custom                 --
--  K-extension. T13 has 4 pipeline stages F/RD/E/W, in order execution. With the execute stage being superscalar  --
--  Supports interleaved multithreading (IMT), with maximum configurable thread pool size = 16 threads.            --
--  Pure RISCV exception and interrupt handling. Only thread 0 can be interrupted extenranlly. inter-thread ints   --
--  are allowed, and used for thread synchronization. Pulpino irq/exception table fully supported by SW            --
--  runtime system.                                                                                --
--  Contributors to the Klessydra Project: Abdallah Cheikh, Francesco Vigli, Luigi Blasi,                          --
--                Stefano Sordillo, Gianmarco Cerutti, Simone Ponzio, Ivan Matraxia, Mauro Olivieri.               --
--  last update: 17-11-2019                                                                                --
---------------------------------------------------------------------------------------------------------------------

--package riscv_kless is new work.riscv_klessydra
--  generic map (RV32E => 0);

-- ieee packages ------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;

-- local packages ------------
use work.riscv_klessydra.all;
--  use work.riscv_kless.all;

-------------------------------------------------------------------------------------------------------------
--  ██╗  ██╗██╗     ███████╗███████╗███████╗██╗   ██╗██████╗ ██████╗  █████╗        ████████╗ ██╗██████╗   --
--  ██║ ██╔╝██║     ██╔════╝██╔════╝██╔════╝╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗       ╚══██╔══╝███║╚════██╗  --
--  █████╔╝ ██║     █████╗  ███████╗███████╗ ╚████╔╝ ██║  ██║██████╔╝███████║ █████╗   ██║   ╚██║ █████╔╝  --
--  ██╔═██╗ ██║     ██╔══╝  ╚════██║╚════██║  ╚██╔╝  ██║  ██║██╔══██╗██╔══██║ ╚════╝   ██║    ██║ ╚═══██╗  --
--  ██║  ██╗███████╗███████╗███████║███████║   ██║   ██████╔╝██║  ██║██║  ██║          ██║    ██║██████╔╝  --
--  ╚═╝  ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝   ╚═╝   ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝          ╚═╝    ╚═╝╚═════╝   --
-------------------------------------------------------------------------------------------------------------

-- core entity declaration --
entity klessydra_t1_3th_core is
  generic (
    THREAD_POOL_SIZE      : integer := 3;   -- Changing the TPS to less than "number of pipeline stages-1" is not allowed. And making it bigger than "pipeline stages-1" is okay but not recommended
    LUTRAM_RF             : natural := 1;   -- Changes the regfile from flip-flop type into BRAM type
    RV32E                 : natural := 0;   -- Regfile size, Can be set to 32 for RV32E being 0 else 16 for RV32E being set to 1
    RV32M                 : natural := 1;   -- Enable the M-extension of the risc-v instruction set
    superscalar_exec_en   : natural := 1;   -- Enables superscalar execution when set to 1, else the stall of the pipeline will depend on tha latency of the instruction
    accl_en               : natural := 1;   -- Enable the generation of the special purpose accelerator
    replicate_accl_en     : natural := 0;   -- Set to 1 to replicate the accelerator for every thread
    multithreaded_accl_en : natural := 0;   -- Set to 1 to let the replicated accelerator share the functional units (note: replicate_accl_en must be set to '1')
    SPM_NUM               : natural := 4;   -- The number of scratchpads available "Minimum allowed is two"
    Addr_Width            : natural := 14;  -- This address is for scratchpads. Setting this will make the size of the spm to be: "2^Addr_Width -1"
    SPM_STRT_ADDR         : std_logic_vector(31 downto 0) := x"1000_0000";  -- This is starting address of the spms, it shouldn't overlap any sections in the memory map
    SIMD                  : natural := 1;   -- Changing the SIMD, would change the number of the functional units in the dsp, and the number of banks in the spms (can be power of 2 only e.g. 1,2,4,8)
    MCYCLE_EN             : natural := 0;   -- Can be set to 1 or 0 only. Setting to zero will disable MCYCLE and MCYCLEH
    MINSTRET_EN           : natural := 0;   -- Can be set to 1 or 0 only. Setting to zero will disable MINSTRET and MINSTRETH
    MHPMCOUNTER_EN        : natural := 0;   -- Can be set to 1 or 0 only. Setting to zero will disable all performance counters except "MCYCLE/H" and "MINSTRET/H"
    count_all             : natural := 1;   -- Perfomance counters count for all the harts instead of there own hart
    debug_en              : natural := 0;   -- Generates the debug unit
    tracer_en             : natural := 0;   -- Enables the generation of the instruction tracer disable in extremely long simulations in order to save storage space
     ----------------------------------------------------------------------------------------
    N_EXT_PERF_COUNTERS : integer := 0;   -- ignored in Klessydra
    INSTR_RDATA_WIDTH   : integer := 32;  -- ignored in Klessydra
    N_HWLP              : integer := 2;   -- ignored in Klessydra
    N_HWLP_BITS         : integer := 4    -- ignored in Klessydra
    );
  port (
    -- clock, reset active low, test enable
    clk_i               : in  std_logic;
    clock_en_i          : in  std_logic;
    rst_ni              : in  std_logic;
    test_en_i           : in  std_logic;
    -- initialization signals
    boot_addr_i         : in  std_logic_vector(31 downto 0);
    core_id_i           : in  std_logic_vector(3 downto 0);
    cluster_id_i        : in  std_logic_vector(5 downto 0);
    -- program memory interface
    instr_req_o         : out std_logic;
    instr_gnt_i         : in  std_logic;
    instr_rvalid_i      : in  std_logic;
    instr_addr_o        : out std_logic_vector(31 downto 0);
    instr_rdata_i       : in  std_logic_vector(31 downto 0);
    -- data memory interface
    data_req_o          : out std_logic;
    data_gnt_i          : in  std_logic;
    data_rvalid_i       : in  std_logic;
    data_we_o           : out std_logic;
    data_be_o           : out std_logic_vector(3 downto 0);
    data_addr_o         : out std_logic_vector(31 downto 0);
    data_wdata_o        : out std_logic_vector(31 downto 0);
    data_rdata_i        : in  std_logic_vector(31 downto 0);
    data_err_i          : in  std_logic;
    -- interrupt request interface
    irq_i               : in  std_logic;
    irq_id_i            : in  std_logic_vector(4 downto 0);
    irq_ack_o           : out std_logic;
    irq_id_o            : out std_logic_vector(4 downto 0);
    irq_sec_i           : in  std_logic;  -- unused in Pulpino
    sec_lvl_o           : out std_logic;  -- unused in Pulpino
    -- debug interface
    debug_req_i         : in  std_logic;
    debug_gnt_o         : out std_logic;
    debug_rvalid_o      : out std_logic;
    debug_addr_i        : in  std_logic_vector(14 downto 0);
    debug_we_i          : in  std_logic;
    debug_wdata_i       : in  std_logic_vector(31 downto 0);
    debug_rdata_o       : out std_logic_vector(31 downto 0);
    debug_halted_o      : out std_logic;
    debug_halt_i        : in  std_logic;
    debug_resume_i      : in  std_logic;
    -- miscellanous control signals
    fetch_enable_i      : in  std_logic;
    core_busy_o         : out std_logic;
    ext_perf_counters_i : in  std_logic_vector(N_EXT_PERF_COUNTERS to 1)
    );

end entity klessydra_t1_3th_core;

architecture Klessydra_T1 of klessydra_t1_3th_core is

  constant RF_SIZE           : natural := 32-16*RV32E;
  constant RF_CEIL           : natural := integer(ceil(log2(real(RF_SIZE))));
  constant TPS_CEIL          : natural := integer(ceil(log2(real(THREAD_POOL_SIZE))));
  constant TPS_BUF_CEIL      : natural := integer(ceil(log2(real(THREAD_POOL_SIZE-1))));
  constant SPM_ADDR_WID      : natural := integer(ceil(log2(real(SPM_NUM+1))));
  constant SIMD_BITS         : natural := integer(ceil(log2(real(SIMD))));
  constant Data_Width        : natural := 32;
  constant SIMD_Width        : natural := SIMD*Data_Width;

  subtype harc_range is integer range THREAD_POOL_SIZE - 1 downto 0;  -- will be used replicated units in the core

  constant ACCL_NUM : natural := (THREAD_POOL_SIZE - (THREAD_POOL_SIZE-1)*(1-replicate_accl_en));
  constant FU_NUM   : natural := (ACCL_NUM - (ACCL_NUM-1)*(multithreaded_accl_en));

  subtype accl_range is integer range ACCL_NUM - 1 downto 0;  -- will be used replicated accelerators in the core
  subtype fu_range   is integer range FU_NUM - 1 downto 0; -- will be used replicated accelerators in the core

  signal reset_state            : std_logic;

  -- Control Status Register (CSR) signals
  signal MVSIZE      : array_2d(harc_range)(Addr_Width downto 0);
  signal MVTYPE      : array_2d(harc_range)(3 downto 0);
  signal MPSCLFAC    : array_2d(harc_range)(4 downto 0);
  signal MSTATUS     : array_2d(harc_range)(1 downto 0);
  signal MEPC        : array_2d(harc_range)(31 downto 0);
  signal MCAUSE      : array_2d(harc_range)(31 downto 0);
  signal MIP         : array_2d(harc_range)(31 downto 0);
  signal MTVEC       : array_2d(harc_range)(31 downto 0);
  signal PCER        : array_2d(harc_range)(31 downto 0);

  signal irq_pending     : std_logic_vector(harc_range);
  signal WFI_Instr       : std_logic;
  signal except_pc_vec_o : std_logic_vector(31 downto 0);

  -- Memory fault signals
  signal load_err, store_err : std_logic;

  -- Interface signals from EXEC unit to CSR management unit
  signal csr_instr_req       : std_logic;
  signal csr_instr_done      : std_logic;
  signal csr_access_denied_o : std_logic;
  signal csr_wdata_i         : std_logic_vector(31 downto 0);
  signal csr_op_i            : std_logic_vector(2 downto 0);
  signal csr_rdata_o         : std_logic_vector(31 downto 0);
  signal csr_addr_i          : std_logic_vector (11 downto 0);

  -- CSR management unit internal signal
  signal csr_instr_req_replicated       : std_logic_vector(harc_range);
  signal csr_instr_done_replicated      : std_logic_vector(harc_range);
  signal csr_access_denied_o_replicated : std_logic_vector(harc_range);
  signal csr_rdata_o_replicated         : array_2d(harc_range)(31 downto 0);

  -- program counters --
  signal pc        : array_2d(harc_range)(31 downto 0);
  signal pc_IF     : std_logic_vector(31 downto 0);  -- pc_IF is the actual pc
  signal pc_ID     : std_logic_vector(31 downto 0);  -- pc_ID is the orogram counter of the Decode stage
  signal pc_IE     : std_logic_vector(31 downto 0);  -- pc_IE is pc entering stage IE

  -- instruction register and instr. propagation registers --
  signal instr_word_IE          : std_logic_vector(31 downto 0);
  signal instr_rvalid_IE        : std_logic;  -- validity bit at IE input

  -- pc updater signals
  signal pc_update_enable                : std_logic_vector(harc_range);
  signal wfi_condition_pending           : std_logic_vector(harc_range);
  signal served_ie_except_condition      : std_logic_vector(harc_range);
  signal served_ls_except_condition      : std_logic_vector(harc_range);
  signal served_dsp_except_condition     : std_logic_vector(harc_range);
  signal served_except_condition         : std_logic_vector(harc_range);
  signal served_mret_condition           : std_logic_vector(harc_range);
  signal served_irq                      : std_logic_vector(harc_range);
  signal taken_branch_pending            : std_logic_vector(harc_range);
  signal ie_except_data                  : std_logic_vector(31 downto 0);
  signal ls_except_data                  : std_logic_vector(31 downto 0);
  signal dsp_except_data                 : array_2d(accl_range)(31 downto 0);
  signal taken_branch                    : std_logic;
  signal ie_taken_branch                 : std_logic;
  signal ls_taken_branch                 : std_logic;
  signal dsp_taken_branch                : std_logic_vector(accl_range);
  signal set_branch_condition            : std_logic;
  signal ie_except_condition             : std_logic;
  signal ls_except_condition             : std_logic;
  signal dsp_except_condition            : std_logic_vector(accl_range);
  signal set_except_condition            : std_logic;
  signal set_mret_condition              : std_logic;
  signal PC_offset                       : array_2d(harc_range)(31 downto 0);
  signal pc_except_value                 : array_2d(harc_range)(31 downto 0);
  signal pc_except_value_wire            : array_2d(harc_range)(31 downto 0);
  signal taken_branch_pc_lat             : array_2d(harc_range)(31 downto 0);
  signal incremented_pc                  : array_2d(harc_range)(31 downto 0);
  signal mepc_incremented_pc             : array_2d(harc_range)(31 downto 0) := (others => (others => '0'));
  signal mepc_interrupt_pc               : array_2d(harc_range)(31 downto 0) := (others => (others => '0'));
  signal relative_to_PC                  : array_2d(harc_range)(31 downto 0);
  signal absolute_jump                   : std_logic;
  signal data_we_o_lat                   : std_logic;
  signal misaligned_err                  : std_logic;

  --//parte probabilmente da eliminare
  -- signals for counting intructions
  --signal clock_cycle         : std_logic_vector(63 downto 0);  -- RDCYCLE
  --signal external_counter    : std_logic_vector(63 downto 0);  -- RDTIME
  --signal instruction_counter : std_logic_vector(63 downto 0);  -- RDINSTRET

  -- regfile replicated array
  signal regfile            : array_3d(harc_range)(RF_SIZE-1 downto 0)(31 downto 0);

  --signal used by counters
  signal set_wfi_condition          : std_logic;
  signal harc_to_csr                : harc_range;
  signal jump_instr                 : std_logic;
  signal jump_instr_lat             : std_logic;
  signal branch_instr               : std_logic;
  signal branch_instr_lat           : std_logic;

  -- auxiliary data memory interface signals
  signal data_addr_internal     : std_logic_vector(31 downto 0);
  signal data_be_internal       : std_logic_vector(3 downto 0);

  --DeBug Unit signal and state
  signal dbg_req_o       : std_logic;
  signal dbg_ack_i       : std_logic;
  signal ebreak_instr    : std_logic;

  -- hardware context id at fetch, and propagated hardware context ids
  --signal harc_count            : harc_min_range;
  signal harc_IF         : harc_range;
  signal harc_ID         : harc_range;
  signal harc_EXEC       : harc_range;

  component Program_Counter
  generic (
    THREAD_POOL_SIZE      : integer;
    ACCL_NUM              : natural
  );
  port (
    absolute_jump                     : in  std_logic;
    data_we_o_lat                     : in  std_logic;
    PC_offset                         : in  array_2d(harc_range)(31 downto 0);
    taken_branch                      : in  std_logic;
    ie_taken_branch                   : in  std_logic;
    ls_taken_branch                   : in  std_logic;
    dsp_taken_branch                  : in  std_logic_vector(accl_range);
    set_branch_condition              : in  std_logic;
    ie_except_condition               : in  std_logic;
    ls_except_condition               : in  std_logic;
    dsp_except_condition              : in  std_logic_vector(accl_range);
    set_except_condition              : in  std_logic;
    set_mret_condition                : in  std_logic;
    set_wfi_condition                 : in  std_logic;
    harc_ID                           : in  harc_range;
    harc_EXEC                         : in  harc_range;
    instr_rvalid_IE                   : in  std_logic;
    pc_IE                             : in  std_logic_vector(31 downto 0);
    MSTATUS                           : in  array_2d(harc_range)(1 downto 0);
    MIP, MEPC, MCAUSE, MTVEC          : in  array_2d(harc_range)(31 downto 0);
    instr_word_IE                     : in  std_logic_vector(31 downto 0);
    reset_state                       : in  std_logic;
    pc_IF                             : out std_logic_vector(31 downto 0);
    harc_IF                           : out harc_range;
    served_ie_except_condition        : out std_logic_vector(harc_range);
    served_ls_except_condition        : out std_logic_vector(harc_range);
    served_dsp_except_condition       : out std_logic_vector(harc_range);
    served_except_condition           : out std_logic_vector(harc_range);
    served_mret_condition             : out std_logic_vector(harc_range);
    served_irq                        : in  std_logic_vector(harc_range);
    taken_branch_pending              : out std_logic_vector(harc_range);
    taken_branch_pc_lat               : out array_2d(harc_range)(31 downto 0);
    incremented_pc                    : out array_2d(harc_range)(31 downto 0);
    mepc_incremented_pc               : out array_2d(harc_range)(31 downto 0);
    mepc_interrupt_pc                 : out array_2d(harc_range)(31 downto 0);
    irq_pending                       : out std_logic_vector(harc_range);
    clk_i                             : in  std_logic;
    rst_ni                            : in  std_logic;
    irq_i                             : in  std_logic;
    fetch_enable_i                    : in  std_logic;
    boot_addr_i                       : in  std_logic_vector(31 downto 0);
    instr_gnt_i                       : in  std_logic
    );
  end component;

  component CSR_Unit
  generic (
    THREAD_POOL_SIZE            : integer;
    ACCL_NUM                    : natural;
    Addr_Width                  : natural;
    replicate_accl_en           : natural;
    accl_en                     : natural;
    MCYCLE_EN                   : natural;
    MINSTRET_EN                 : natural;
    MHPMCOUNTER_EN              : natural;
    RF_CEIL                     : natural;
    count_all                   : natural
  );
  port (
    pc_IE                       : in  std_logic_vector(31 downto 0);
    ie_except_data              : in  std_logic_vector(31 downto 0);
    ls_except_data              : in  std_logic_vector(31 downto 0);
    dsp_except_data             : in  array_2d(accl_range)(31 downto 0);
    served_ie_except_condition  : in  std_logic_vector(harc_range);
    served_ls_except_condition  : in  std_logic_vector(harc_range);
    served_dsp_except_condition : in  std_logic_vector(harc_range);
    harc_EXEC                   : in  harc_range;
    harc_to_csr                 : in  harc_range;
    instr_word_IE               : in  std_logic_vector(31 downto 0);
    served_except_condition     : in  std_logic_vector(harc_range);
    served_mret_condition       : in  std_logic_vector(harc_range);
    served_irq                  : in  std_logic_vector(harc_range);
    pc_except_value_wire        : in  array_2d(harc_range)(31 downto 0);
    dbg_req_o                   : in  std_logic;
    data_addr_internal          : in  std_logic_vector(31 downto 0);
    jump_instr                  : in  std_logic;
    branch_instr                : in  std_logic;
    set_branch_condition        : in  std_logic;
    csr_instr_req               : in  std_logic;
    misaligned_err              : in  std_logic;
    WFI_Instr                   : in  std_logic;
    csr_wdata_i                 : in  std_logic_vector(31 downto 0);
    csr_op_i                    : in  std_logic_vector(2 downto 0);
    csr_addr_i                  : in  std_logic_vector(11 downto 0);
    csr_instr_done              : out std_logic;
    csr_access_denied_o         : out std_logic;
    csr_rdata_o                 : out std_logic_vector (31 downto 0);
    MVSIZE                      : out array_2d(harc_range)(Addr_Width downto 0);
    MVTYPE                      : out array_2d(harc_range)(3 downto 0);
    MPSCLFAC                    : out array_2d(harc_range)(4 downto 0);
    MSTATUS                     : out array_2d(harc_range)(1 downto 0);
    MEPC                        : out array_2d(harc_range)(31 downto 0);
    MCAUSE                      : out array_2d(harc_range)(31 downto 0);
    MIP                         : out array_2d(harc_range)(31 downto 0);
    MTVEC                       : out array_2d(harc_range)(31 downto 0);
    PCER                        : out array_2d(harc_range)(31 downto 0);
    fetch_enable_i              : in  std_logic;
    clk_i                       : in  std_logic;
    rst_ni                      : in  std_logic;
    cluster_id_i                : in  std_logic_vector(5 downto 0);
    instr_rvalid_i              : in  std_logic;
    instr_rvalid_IE             : in  std_logic;
    data_we_o                   : in  std_logic;
    data_req_o                  : in  std_logic;
    data_gnt_i                  : in  std_logic;
    irq_i                       : in  std_logic;
    irq_id_i                    : in  std_logic_vector(4 downto 0);
    irq_id_o                    : out std_logic_vector(4 downto 0);
    irq_ack_o                   : out std_logic
    );
  end component;

  component Debug_Unit
  generic(
      THREAD_POOL_SIZE         : integer;
      LUTRAM_RF                : natural;
      ACCL_NUM                 : natural;
      RF_SIZE                  : natural
      );
  port(
      set_branch_condition     : in  std_logic;
      ie_except_condition      : in  std_logic;
      ls_except_condition      : in  std_logic;
      dsp_except_condition     : in  std_logic_vector(accl_range);
      set_except_condition     : in  std_logic;
      set_mret_condition       : in  std_logic;
      served_irq               : in  std_logic_vector(harc_range);
      irq_pending              : in  std_logic_vector(harc_range);
      taken_branch_pc_lat      : in  array_2d(harc_range)(31 downto 0);
      incremented_pc           : in  array_2d(harc_range)(31 downto 0);
      MTVEC                    : in  array_2d(harc_range)(31 downto 0);
      MIP                      : in  array_2d(harc_range)(31 downto 0);
      MSTATUS                  : in  array_2d(harc_range)(1 downto 0);
      MCAUSE                   : in  array_2d(harc_range)(31 downto 0);
      mepc_incremented_pc      : in  array_2d(harc_range)(31 downto 0) := (others => (others => '0'));
      mepc_interrupt_pc        : in  array_2d(harc_range)(31 downto 0) := (others => (others => '0'));
      regfile                  : in  array_3d(harc_range)(RF_SIZE-1 downto 0)(31 downto 0);
      pc_ID                    : in  std_logic_vector (31 downto 0);
      pc_IE                    : in  std_logic_vector (31 downto 0);
      harc_ID                  : in  harc_range;
      ebreak_instr             : in  std_logic;
      dbg_ack_i                : in  std_logic;
      taken_branch             : in  std_logic;
      taken_branch_pending     : in  std_logic_vector(harc_range);
      dbg_req_o                : out std_logic;
      clk_i                    : in  std_logic;
      rst_ni                   : in  std_logic;
      debug_req_i              : in  std_logic;
      debug_gnt_o              : out std_logic;
      debug_rvalid_o           : out std_logic;
      debug_addr_i             : in  std_logic_vector(14 downto 0);
      debug_we_i               : in  std_logic;
      debug_wdata_i            : in  std_logic_vector(31 downto 0);
      debug_rdata_o            : out std_logic_vector(31 downto 0);
      debug_halted_o           : out std_logic;
      debug_halt_i             : in  std_logic;
      debug_resume_i           : in  std_logic
      );
  end component;

  component Pipeline
  generic(
    THREAD_POOL_SIZE           : integer;
    LUTRAM_RF                  : natural;
    RV32E                      : natural;
    RV32M                      : natural;
    superscalar_exec_en        : natural;
    accl_en                    : natural;
    replicate_accl_en          : natural;
    multithreaded_accl_en      : natural;
    SPM_NUM                    : natural;
    Addr_Width                 : natural;
    SPM_STRT_ADDR              : std_logic_vector(31 downto 0);
    SIMD                       : natural;
    MCYCLE_EN                  : natural;
    MINSTRET_EN                : natural;
    MHPMCOUNTER_EN             : natural;
    count_all                  : natural;
    debug_en                   : natural;
    tracer_en                  : natural;
    -------------------------------------
    ACCL_NUM                   : natural;
    FU_NUM                     : natural;
    RF_SIZE                    : natural;
    RF_CEIL                    : natural;
    TPS_CEIL                   : natural;
    TPS_BUF_CEIL               : natural;
    SPM_ADDR_WID               : natural;
    SIMD_BITS                  : natural;
    Data_Width                 : natural;
    SIMD_Width                 : natural
    );
  port (
    pc_IF                      : in  std_logic_vector(31 downto 0);
    harc_IF                    : in  harc_range;
    irq_pending                : in  std_logic_vector(harc_range);
    csr_instr_done             : in  std_logic;
    csr_access_denied_o        : in  std_logic;
    csr_rdata_o                : in  std_logic_vector (31 downto 0);
    dbg_req_o                  : in  std_logic;
    MVSIZE                     : in  array_2d(harc_range)(Addr_Width downto 0);
    MVTYPE                     : in  array_2d(harc_range)(3 downto 0);
    MPSCLFAC                   : in  array_2d(harc_range)(4 downto 0);
    MSTATUS                    : in  array_2d(harc_range)(1 downto 0);
    PCER                       : in  array_2d(harc_range)(31 downto 0);
    served_irq     	           : out std_logic_vector(harc_range);
    WFI_Instr                  : out std_logic;
    reset_state                : out std_logic;
    misaligned_err             : out std_logic;
    pc_ID                      : out std_logic_vector(31 downto 0);
    pc_IE                      : out std_logic_vector(31 downto 0);
    ie_except_data             : out std_logic_vector(31 downto 0);
    ls_except_data             : out std_logic_vector(31 downto 0);
    dsp_except_data            : out array_2d(accl_range)(31 downto 0);
    taken_branch               : out std_logic;
    ie_taken_branch            : out std_logic;
    ls_taken_branch            : out std_logic;
    dsp_taken_branch           : out std_logic_vector(accl_range);
    set_branch_condition       : out std_logic;
    ie_except_condition        : out std_logic;
    ls_except_condition        : out std_logic;
    dsp_except_condition       : out std_logic_vector(accl_range);
    set_mret_condition         : out std_logic;
    set_wfi_condition          : out std_logic;
    csr_instr_req              : out std_logic;
    instr_rvalid_IE            : out std_logic;  -- validity bit at IE input
    csr_addr_i                 : out std_logic_vector (11 downto 0);
    csr_wdata_i                : out std_logic_vector (31 downto 0);
    csr_op_i                   : out std_logic_vector (2 downto 0);
    jump_instr                 : out std_logic;
    jump_instr_lat             : out std_logic;
    branch_instr               : out std_logic;
    branch_instr_lat           : out std_logic;
    harc_ID                    : out harc_range;
    harc_EXEC                  : out harc_range;
    harc_to_csr                : out harc_range;
    instr_word_IE              : out std_logic_vector(31 downto 0);
    PC_offset                  : out array_2d(harc_range)(31 downto 0);
    dbg_ack_i                  : out std_logic;
    ebreak_instr               : out std_logic;
    data_addr_internal         : out std_logic_vector(31 downto 0);
    absolute_jump              : out std_logic;
    regfile                    : out array_3d(harc_range)(RF_SIZE-1 downto 0)(31 downto 0);
    -- clock, reset active low, test enable
    clk_i                      : in  std_logic;
    rst_ni                     : in  std_logic;
    -- program memory interface
    instr_req_o                : out std_logic;
    instr_gnt_i                : in  std_logic;
    instr_rvalid_i             : in  std_logic;
    instr_rdata_i              : in  std_logic_vector(31 downto 0);
    -- data memory interface
    data_req_o                 : out std_logic;
    data_gnt_i                 : in  std_logic;
    data_rvalid_i              : in  std_logic;
    data_we_o                  : out std_logic;
    data_be_o                  : out std_logic_vector(3 downto 0);
    data_addr_o                : out std_logic_vector(31 downto 0);
    data_wdata_o               : out std_logic_vector(31 downto 0);
    data_rdata_i               : in  std_logic_vector(31 downto 0);
    data_err_i                 : in  std_logic;
    -- interrupt request interface
	irq_i               	   : in  std_logic;
    -- miscellanous control signals
    fetch_enable_i             : in  std_logic;
    core_busy_o                : out std_logic
  );
  end component;

--------------------------------------------------------------------------------------------------
----------------------- ARCHITECTURE BEGIN -------------------------------------------------------
begin

  assert (LUTRAM_RF /= debug_en and LUTRAM_RF /= 1) report "Debug-Unit cannot read from a LUTRAM regfile." severity WARNING;

  instr_addr_o <= pc_IF;

  set_except_condition <= '1' when (IE_except_condition = '1' or LS_except_condition = '1' or DSP_except_condition /= (accl_range => '0')) else '0';

  process(all)
  begin
  pc_except_value_wire <= pc_except_value;
    if set_except_condition  = '1' then
      pc_except_value_wire(harc_EXEC) <=  pc_IE;
    end if;
  end process;

  process(clk_i, rst_ni)

  Prg_Ctr : Program_Counter
    generic map (
      THREAD_POOL_SIZE            => THREAD_POOL_SIZE,
      ACCL_NUM                    => ACCL_NUM
      )
    port map(
      absolute_jump               => absolute_jump,
      data_we_o_lat               => data_we_o_lat,
      PC_offset                   => PC_offset,
      taken_branch                => taken_branch,
      ie_taken_branch             => ie_taken_branch,
      ls_taken_branch             => ls_taken_branch,
      dsp_taken_branch            => dsp_taken_branch,
      set_branch_condition        => set_branch_condition,
      ie_except_condition         => ie_except_condition,
      ls_except_condition         => ls_except_condition,
      dsp_except_condition        => dsp_except_condition,
      set_except_condition        => set_except_condition,
      set_mret_condition          => set_mret_condition,
      set_wfi_condition           => set_wfi_condition,
      harc_ID                     => harc_ID,
      harc_EXEC                   => harc_EXEC,
      instr_rvalid_IE             => instr_rvalid_IE,
      pc_IE                       => pc_IE,
      MIP                         => MIP,
      MEPC                        => MEPC,
      MSTATUS                     => MSTATUS,
      MCAUSE                      => MCAUSE,
      MTVEC                       => MTVEC,
      instr_word_IE               => instr_word_IE,
      reset_state                 => reset_state,
      pc_IF                       => pc_IF,
      harc_IF                     => harc_IF,
      served_ie_except_condition  => served_ie_except_condition,
      served_ls_except_condition  => served_ls_except_condition,
      served_dsp_except_condition => served_dsp_except_condition,
      served_except_condition     => served_except_condition,
      served_mret_condition       => served_mret_condition,
      served_irq                  => served_irq,
      taken_branch_pending        => taken_branch_pending,
      taken_branch_pc_lat         => taken_branch_pc_lat,
      incremented_pc              => incremented_pc,
      mepc_incremented_pc         => mepc_incremented_pc,
      mepc_interrupt_pc           => mepc_interrupt_pc,
      irq_pending                 => irq_pending,
      clk_i                       => clk_i,
      rst_ni                      => rst_ni,
      irq_i                       => irq_i,
      fetch_enable_i              => fetch_enable_i,
      boot_addr_i                 => boot_addr_i,
      instr_gnt_i                 => instr_gnt_i
      );

  CSR : CSR_Unit
    generic map (
      THREAD_POOL_SIZE            =>  THREAD_POOL_SIZE,
      ACCL_NUM                    =>  ACCL_NUM,
      Addr_Width                  =>  Addr_Width,
      replicate_accl_en           =>  replicate_accl_en,
      accl_en                     =>  accl_en,
      MCYCLE_EN                   =>  MCYCLE_EN,
      MINSTRET_EN                 =>  MINSTRET_EN,
      MHPMCOUNTER_EN              =>  MHPMCOUNTER_EN,
      RF_CEIL                     =>  RF_CEIL,
      count_all                   =>  count_all
    )
    port map(
      pc_IE                       => pc_IE,
      ie_except_data              => ie_except_data,
      ls_except_data              => ls_except_data,
      dsp_except_data             => dsp_except_data,
      served_ie_except_condition  => served_ie_except_condition,
      served_ls_except_condition  => served_ls_except_condition,
      served_dsp_except_condition => served_dsp_except_condition,
      harc_EXEC                   => harc_EXEC,
      harc_to_csr                 => harc_to_csr,
      instr_word_IE               => instr_word_IE,
      served_except_condition     => served_except_condition,
      served_mret_condition       => served_mret_condition,
      served_irq                  => served_irq,
      pc_except_value_wire        => pc_except_value_wire,
      dbg_req_o                   => dbg_req_o,
      data_addr_internal          => data_addr_internal,
      jump_instr                  => jump_instr,
      branch_instr                => branch_instr,
      set_branch_condition        => set_branch_condition,
      csr_instr_req               => csr_instr_req,
      misaligned_err              => misaligned_err,
      WFI_Instr                   => WFI_Instr,
      csr_wdata_i                 => csr_wdata_i,
      csr_op_i                    => csr_op_i,
      csr_addr_i                  => csr_addr_i,
      csr_instr_done              => csr_instr_done,
      csr_access_denied_o         => csr_access_denied_o,
      csr_rdata_o                 => csr_rdata_o,
      MVSIZE                      => MVSIZE,
      MVTYPE                      => MVTYPE,
      MPSCLFAC                    => MPSCLFAC,
      MSTATUS                     => MSTATUS,
      MEPC                        => MEPC,
      MCAUSE                      => MCAUSE,
      MIP                         => MIP,
      MTVEC                       => MTVEC,
      PCER                        => PCER,
      fetch_enable_i              => fetch_enable_i,
      clk_i                       => clk_i,
      rst_ni                      => rst_ni,
      cluster_id_i                => cluster_id_i,
      instr_rvalid_i              => instr_rvalid_i,
      instr_rvalid_IE             => instr_rvalid_IE,
      data_we_o                   => data_we_o,
      data_req_o                  => data_req_o,
      data_gnt_i                  => data_gnt_i,
      irq_i                       => irq_i,
      irq_id_i                    => irq_id_i,
      irq_id_o                    => irq_id_o,
      irq_ack_o                   => irq_ack_o
      );

  DEBUG_generate : if debug_en = 1 generate

  DBG : Debug_Unit
    generic map(
      THREAD_POOL_SIZE         => THREAD_POOL_SIZE,
      LUTRAM_RF                => LUTRAM_RF,
      ACCL_NUM                 => ACCL_NUM,
      RF_SIZE                  => RF_SIZE
      )
    port map(
      set_branch_condition     => set_branch_condition,
      ie_except_condition      => ie_except_condition,
      ls_except_condition      => ls_except_condition,
      dsp_except_condition     => dsp_except_condition,
      set_except_condition     => set_except_condition,
      set_mret_condition       => set_mret_condition,
      served_irq               => served_irq,
      irq_pending              => irq_pending,
      taken_branch_pc_lat      => taken_branch_pc_lat,
      incremented_pc           => incremented_pc,
      MTVEC                    => MTVEC,
      MIP                      => MIP,
      MSTATUS                  => MSTATUS,
      MCAUSE                   => MCAUSE,
      mepc_incremented_pc      => mepc_incremented_pc,
      mepc_interrupt_pc        => mepc_interrupt_pc,
      regfile                  => regfile,
      pc_ID                    => pc_ID,
      pc_IE                    => pc_IE,
      harc_ID                  => harc_ID,
      ebreak_instr             => ebreak_instr,
      dbg_ack_i                => dbg_ack_i,
      taken_branch	           => taken_branch,
      taken_branch_pending     => taken_branch_pending,
      dbg_req_o                => dbg_req_o,
      clk_i                    => clk_i,
      rst_ni                   => rst_ni,
      debug_req_i              => debug_req_i,
      debug_gnt_o              => debug_gnt_o,
      debug_rvalid_o           => debug_rvalid_o,
      debug_addr_i             => debug_addr_i,
      debug_we_i               => debug_we_i,
      debug_wdata_i            => debug_wdata_i,
      debug_rdata_o            => debug_rdata_o,
      debug_halted_o           => debug_halted_o,
      debug_halt_i             => debug_halt_i,
      debug_resume_i           => debug_resume_i
      );

  end generate;

  Pipe : Pipeline
    generic map(
      THREAD_POOL_SIZE      => THREAD_POOL_SIZE,
      LUTRAM_RF             => LUTRAM_RF,
      RV32E                 => RV32E,
      RV32M                 => RV32M,
      superscalar_exec_en   => superscalar_exec_en,
      accl_en               => accl_en,
      replicate_accl_en     => replicate_accl_en,
      multithreaded_accl_en => multithreaded_accl_en,
      SPM_NUM               => SPM_NUM,
      Addr_Width            => Addr_Width,
      SPM_STRT_ADDR         => SPM_STRT_ADDR,
      SIMD                  => SIMD,
      MCYCLE_EN             => MCYCLE_EN,
      MINSTRET_EN           => MINSTRET_EN,
      MHPMCOUNTER_EN        => MHPMCOUNTER_EN,
      count_all             => count_all,
      debug_en              => debug_en,
      tracer_en             => tracer_en,
      -----------------------------------
      ACCL_NUM              => ACCL_NUM,
      FU_NUM                => FU_NUM,
      RF_SIZE               => RF_SIZE,
      RF_CEIL               => RF_CEIL,
      TPS_CEIL              => TPS_CEIL,
      TPS_BUF_CEIL          => TPS_BUF_CEIL,
      SPM_ADDR_WID          => SPM_ADDR_WID,
      SIMD_BITS             => SIMD_BITS,
      Data_Width            => Data_Width,
      SIMD_Width            => SIMD_Width
      )
    port map(
      pc_IF                      => pc_IF,
      harc_IF                    => harc_IF,
      irq_pending                => irq_pending,
      csr_instr_done             => csr_instr_done,
      csr_access_denied_o        => csr_access_denied_o,
      csr_rdata_o                => csr_rdata_o,
      dbg_req_o                  => dbg_req_o,
      pc_ID                      => pc_ID,
      pc_IE                      => pc_IE,
      ie_except_data             => ie_except_data,
      ls_except_data             => ls_except_data,
      dsp_except_data            => dsp_except_data,
      MVSIZE                     => MVSIZE,
      MVTYPE                     => MVTYPE,
      MPSCLFAC                   => MPSCLFAC,
      MSTATUS                    => MSTATUS,
      PCER                       => PCER,
      served_irq                 => served_irq,
      WFI_Instr                  => WFI_Instr,
      reset_state                => reset_state,
      misaligned_err             => misaligned_err,
      taken_branch               => taken_branch,
      ie_taken_branch            => ie_taken_branch,
      ls_taken_branch            => ls_taken_branch,
      dsp_taken_branch           => dsp_taken_branch,
      set_branch_condition       => set_branch_condition,
      ie_except_condition        => ie_except_condition,
      ls_except_condition        => ls_except_condition,
      dsp_except_condition       => dsp_except_condition,
      set_mret_condition         => set_mret_condition,
      set_wfi_condition          => set_wfi_condition,
      csr_instr_req              => csr_instr_req,
      instr_rvalid_IE            => instr_rvalid_IE,
      csr_addr_i                 => csr_addr_i,
      csr_wdata_i                => csr_wdata_i,
      csr_op_i                   => csr_op_i,
      jump_instr                 => jump_instr,
      jump_instr_lat             => jump_instr_lat,
      branch_instr               => branch_instr,
      branch_instr_lat           => branch_instr_lat,
      harc_ID                    => harc_ID,
      harc_EXEC                  => harc_EXEC,
      harc_to_csr                => harc_to_csr,
      instr_word_IE              => instr_word_IE,
      PC_offset                  => PC_offset,
      dbg_ack_i                  => dbg_ack_i,
      ebreak_instr               => ebreak_instr,
      data_addr_internal         => data_addr_internal,
      absolute_jump              => absolute_jump,
      regfile                    => regfile,
      clk_i                      => clk_i,
      rst_ni                     => rst_ni,
      instr_req_o                => instr_req_o,
      instr_gnt_i                => instr_gnt_i,
      instr_rvalid_i             => instr_rvalid_i,
      instr_rdata_i              => instr_rdata_i,
      data_req_o                 => data_req_o,
      data_gnt_i                 => data_gnt_i,
      data_rvalid_i              => data_rvalid_i,
      data_we_o                  => data_we_o,
      data_be_o                  => data_be_o,
      data_addr_o                => data_addr_o,
      data_wdata_o               => data_wdata_o,
      data_rdata_i               => data_rdata_i,
      data_err_i                 => data_err_i,
      irq_i                      => irq_i,
      fetch_enable_i             => fetch_enable_i,
      core_busy_o                => core_busy_o
      );

end Klessydra_T1;
--------------------------------------------------------------------------------------------------
-- END of Klessydra T13 core architecture --------------------------------------------------------
--------------------------------------------------------------------------------------------------

  begin
    if rst_ni = '0' then
    elsif rising_edge(clk_i) then
      pc_except_value <= pc_except_value_wire;  -- AAA verify if it is working for DSP and LSU (not verified yet)


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.ocs.all;
use work.priv.all;

entity denise is
  generic (
    CFG_STRDEBUG            : boolean := false;
    CFG_BLANK_DURING_VBLANK : boolean := true
  );
  port (
    deni  : in    denise_in_t;
    deno  : out   denise_out_t
  );
end;

architecture rtl of denise is
  -- In the NTSC television standard, horizontal blanking occupies
  -- 10.9 μs (17.2%) out of every 63.6 μs scan line. In PAL, it
  -- occupies 12 μs (18.8%) out of every 64 μs scan line.
  -- NTSC: main clock 28.63636 MHz
  -- PAL:  main clock 28.37516 MHz, hblank is 85.12548 clk (7 MHz)
  constant HBLANK_START_PAL : natural range 0 to 511 := 3;
  constant HBLANK_NCLK_PAL  : natural range 0 to 511 := 85;
  constant NBURST_START_PAL : natural range 0 to 511 := 41;
  constant NBURST_NCLK_PAL  : natural range 0 to 511 := 18;

  subtype color_index_t is std_ulogic_vector(4 downto 0);
  type color_index_array_t is array (integer range <>) of color_index_t;

  type sprite_shift_t is record
    a : word_t;
    b : word_t;
  end record;

  type sprite_shift_array_t is array (integer range <>) of sprite_shift_t;
  type shreg_t is record
    bpl   : word_array_t(0 to 5);
    bpld  : word_array_t(0 to 5);
    spr   : sprite_shift_array_t(0 to 7);
  end record;

  type state_a is record
    sel       : sel_t;
    cck       : std_ulogic;
    rga       : rga_t;
  end record;

  type state_b is record
    sel       : sel_t;
    drdx      : word_t;
    colori    : color_index_t;
  end record;

  type bplcon_t is record
    hires     : std_ulogic;
    bpu       : std_ulogic_vector(2 downto 0);
    homod     : std_ulogic;
    dblpf     : std_ulogic;
    color     : std_ulogic;
    gaud      : std_ulogic;
    pf1h      : std_ulogic_vector(3 downto 0);
    pf2h      : std_ulogic_vector(3 downto 0);
    pf1p      : std_ulogic_vector(2 downto 0);
    pf2p      : std_ulogic_vector(2 downto 0);
    pf2pri    : std_ulogic;
  end record;

  type state_c is record
    -- horizontal video beam position, lores
    h         : unsigned(8 downto 0);
    hblank    : std_ulogic;
    vblank    : std_ulogic;
    -- Indicates that beam is currently in the horizontal display window.
    diw       : std_ulogic;
    diwstrth  : std_ulogic_vector(8 downto 0);
    diwstoph  : std_ulogic_vector(8 downto 0);
    bplcon    : bplcon_t;
    clxcon    : std_ulogic_vector(15 downto 0);
    clxdat    : std_ulogic_vector(14 downto 0);
    bpldat    : word_array_t(0 to 5);
    spr       : sprite_reg_array_t(0 to 7);
    color     : rgb4_array_t(0 to 31);
    bpltrig   : std_ulogic;
  end record;

  type state_d is record
    bplen     : std_ulogic_vector(5 downto 0);
    shreg     : shreg_t;
  end record;

  type bplbus_array_t is array (integer range <>)
   of std_ulogic_vector(5 downto 0);

  type state_e is record
    bplbus    : bplbus_array_t(0 to 1);
    -- 8 sprites * 2 lines
    sprbus    : std_ulogic_vector(15 downto 0);
  end record;

  type pfp_array_t is array (integer range <>) of std_ulogic_vector(2 downto 0);

  type state_f is record
    bplcolor  : color_index_array_t(0 to 1);
    hamop     : std_ulogic_vector(1 downto 0);
    sprcolor  : std_ulogic_vector(3 downto 0);
    -- playfield X priority code (with respect to sprites)
    pfp       : pfp_array_t(0 to 1);
    -- sprite group number
    spp       : std_ulogic_vector(2 downto 0);
  end record;

  type state_g is record
    color     : color_index_array_t(0 to 1);
    issprite  : std_ulogic;
    hamop     : std_ulogic_vector(1 downto 0);
  end record;

  type state_h is record
    rgb       : rgb4_array_t(0 to 1);
    nzd       : std_ulogic_vector(0 to 1);
  end record;

  type state_t is record
    a         : state_a;
    b         : state_b;
    c         : state_c;
    d         : state_d;
    e         : state_e;
    f         : state_f;
    g         : state_g;
    h         : state_h;
    nburst    : std_ulogic;
    drd       : word_t;
    drd_oe    : std_ulogic;
    drd_ext_noe       : std_ulogic;
    drd_ext_to_denice : std_ulogic;
  end record;
  function STATE_SIMINIT return state_t is
    variable v : state_t;
  begin
    v.c.h := (others => '0');
    v.c.bplcon.pf1h := (others => '0');
    v.c.bplcon.pf2h := (others => '0');
    v.c.bplcon.pf1p := (others => '0');
    v.c.bplcon.pf2p := (others => '0');
    v.f.pfp := (others => (others => '0'));
    v.f.spp := (others => '0');
    return v;
  end;

  -- joystick inputs are asynchronous to clk7
  signal m0v_sync : std_ulogic;
  signal m0h_sync : std_ulogic;
  signal m1v_sync : std_ulogic;
  signal m1h_sync : std_ulogic;
  signal joy0daty : std_ulogic_vector(7 downto 0);
  signal joy0datx : std_ulogic_vector(7 downto 0);
  signal joy1daty : std_ulogic_vector(7 downto 0);
  signal joy1datx : std_ulogic_vector(7 downto 0);

  -- Current pipeline state is in "r". Next state is in "rin".
  signal r    : state_t
-- pragma translate_off
    := STATE_SIMINIT
-- pragma translate_on
  ;
  signal rin  : state_t;

begin

  -- Main pipeline with flow from stage "a" "h".
  --  * Stage "a" and "b" is for RGA bus and register decoding.
  --  * Stage "c" does register writes.
  --  * Stage "d" and forward is where sprite and bitplane data is shifted out,
  --    including bitplane/sprite priority logic, collision detection, color
  --    lookup, HAM, and more.
  --
  -- Note that the "hires pixel clock" is double the frequency of clk7. Some
  -- logic from stage "e" and forward is "replicated" to handle the "double
  -- bandwidth" while staying in the clk7 domain. The effective hires pixel
  -- clock is achieved at the output stage with DDR registers. Thus, we trade
  -- logic resources to avoid introducing another clock. This may change in the
  -- future. (Super-hires clock is 4x clk7.)

  process (r, deni, joy0daty, joy0datx, joy1daty, joy1datx)
    variable v : state_t;
    variable readreg : boolean;
  begin
    v := r;

    v.a.cck := deni.cck;
    v.a.rga := deni.rga;
    v.a.sel := SEL_NONE;
    readreg := false;
    if deni.cck = '1' then
      v.a.sel := gen_sel(deni.rga);
      if
        v.a.sel.joy0dat = '1' or
        v.a.sel.joy1dat = '1' or
        v.a.sel.clxdat  = '1'
      then
        readreg := true;
      end if;
    end if;

    v.drd_ext_noe := '1';
    v.drd_ext_to_denice := '1';
    if deni.cck = '1' then
      v.drd_ext_noe := '0';
    end if;

    if readreg then
      v.drd_ext_noe := '1';
      v.drd_ext_to_denice := '0';
    end if;

    -- read
    v.drd_oe := '0';
    if
      r.a.sel.joy0dat = '1' or
      r.a.sel.joy1dat = '1' or
      r.a.sel.clxdat  = '1'
    then
      v.drd_oe := '1';
      v.drd_ext_noe := '0';
      v.drd_ext_to_denice := '0';
    end if;
    v.drd := (others => '-');
    if r.a.sel.joy0dat = '1' then
      v.drd := joy0daty & joy0datx;
    end if;
    if r.a.sel.joy1dat = '1' then
      v.drd := joy1daty & joy1datx;
    end if;
    if r.a.sel.clxdat = '1' then
      v.drd(r.c.clxdat'range) := r.c.clxdat;
    end if;

    -- register address decoder
    v.b.sel := SEL_NONE;
    if r.a.cck = '1' then
      v.b.sel := gen_sel(r.a.rga);
    end if;
    v.b.drdx := deni.drd;
    v.b.colori := r.a.rga(5 downto 1);

    -- Advance beam counter every clk7 cycle.
    v.c.h := r.c.h + 1;
    -- Horizontal counter is reset when Agnus writes a sync strobe.
    if r.b.sel.strhor = '1' or r.b.sel.strvbl = '1' or r.b.sel.strequ = '1' then
      v.c.h := "111111110";
    end if;

    -- Match display window horizontal start and stop position.
    if std_ulogic_vector(r.c.h) = r.c.diwstrth then
      v.c.diw := '1';
    end if;
    if std_ulogic_vector(r.c.h) = r.c.diwstoph then
      v.c.diw := '0';
    end if;

    if r.c.h = to_unsigned(HBLANK_START_PAL, r.c.h'length) then
      v.c.hblank := '1';
    end if;
    if r.c.h = to_unsigned(HBLANK_NCLK_PAL, r.c.h'length) then
      v.c.hblank := '0';
    end if;
    if r.b.sel.strvbl = '1' or r.b.sel.strequ = '1' then
      v.c.vblank := '1';
    elsif r.b.sel.strhor = '1' then
      v.c.vblank := '0';
    end if;

    -- ref: http://eab.abime.net/showthread.php?p=1304764
    -- ref: Motorola MC1377
    -- NOTE: The width of these comparators can likely be reduced.
    if
      r.c.bplcon.color = '1' and
      r.c.h = to_unsigned(HBLANK_START_PAL, r.c.h'length)
    then
      v.nburst := '0';
    elsif
      r.c.h = to_unsigned(NBURST_START_PAL+NBURST_NCLK_PAL, r.c.h'length)
    then
      v.nburst := '1';
    end if;

    -- user writes display window start or stop
    if r.b.sel.diwstrt = '1' then
      v.c.diwstrth := '0' & r.b.drdx(7 downto 0);
    end if;
    if r.b.sel.diwstop = '1' then
      v.c.diwstoph := '1' & r.b.drdx(7 downto 0);
    end if;

    -- write color table register
    if false then
      for i in r.c.color'range loop
        if r.b.sel.color(i) = '1' then
          v.c.color(i) := r.b.drdx(r.c.color(i)'range);
        end if;
      end loop;
    else
      if r.b.sel.colorx = '1' then
        v.c.color(to_integer(unsigned(r.b.colori))) :=
         r.b.drdx(r.c.color(0)'range);
      end if;
    end if;

    for i in r.c.bpldat'range loop
      if r.b.sel.bpldat(i) = '1' then
        v.c.bpldat(i) := r.b.drdx;
      end if;
    end loop;
    -- A Write to "bpl1dat" triggers load of all bitplane shift registers.
    v.c.bpltrig := r.b.sel.bpldat(0);

    -- write sprite registers
    for i in r.c.spr'range loop
      if r.b.sel.spr(i).pos = '1' then
        v.c.spr(i).sh(8 downto 1) := r.b.drdx(7 downto 0);
      end if;
      if r.b.sel.spr(i).ctl = '1' then
        v.c.spr(i).sh(0) := r.b.drdx(0);
        -- NOTE: Sprite attach control bit (odd sprites)
        v.c.spr(i).att := r.b.drdx(7);
        v.c.spr(i).en := '0';
      end if;
      if r.b.sel.spr(i).data = '1' then
        v.c.spr(i).data := r.b.drdx;
        v.c.spr(i).en := '1';
      end if;
      if r.b.sel.spr(i).datb = '1' then
        v.c.spr(i).datb := r.b.drdx;
      end if;
    end loop;

    -- Bitplane and collision control registers
    if r.b.sel.bplcon0 = '1' then
      v.c.bplcon.hires  := r.b.drdx(15);
      v.c.bplcon.bpu    := r.b.drdx(14 downto 12);
      v.c.bplcon.homod  := r.b.drdx(11);
      v.c.bplcon.dblpf  := r.b.drdx(10);
      v.c.bplcon.color  := r.b.drdx( 9);
      v.c.bplcon.gaud   := r.b.drdx( 8);
    end if;
    if r.b.sel.bplcon1 = '1' then
      v.c.bplcon.pf2h   := r.b.drdx( 7 downto  4);
      v.c.bplcon.pf1h   := r.b.drdx( 3 downto  0);
    end if;
    if r.b.sel.bplcon2 = '1' then
      v.c.bplcon.pf2pri := r.b.drdx( 6);
      v.c.bplcon.pf2p   := r.b.drdx( 5 downto  3);
      v.c.bplcon.pf1p   := r.b.drdx( 2 downto  0);
    end if;

    if r.b.sel.clxcon = '1' then
      v.c.clxcon := r.b.drdx;
    end if;

    v.c.clxdat := (
      collision_detection_logic(r.e.sprbus, r.e.bplbus(0), r.c.clxcon) or
      collision_detection_logic(r.e.sprbus, r.e.bplbus(1), r.c.clxcon)
    );
    -- The collision data register is automatically cleared after it is read.
    if r.a.sel.clxdat = '1' then
      v.c.clxdat := (others => '0');
    end if;

    -- parallel to serial converters

    case r.c.bplcon.bpu is

      when  "011" => v.d.bplen := "000111";
      when  "100" => v.d.bplen := "001111";
      when  "101" => v.d.bplen := "011111";
      when  "110" => v.d.bplen := "111111";
      when others => null;
    end case;

    -- Shift or load the bitplane registers.
    -- The "bpld" is for the 16-bit horizontal scroll buffer.
    for i in r.c.bpldat'range loop
      v.d.shreg.bpld(i) := (
        r.d.shreg.bpld(i)(r.d.shreg.bpld(i)'high - 1 downto 0) &
        r.d.shreg.bpl (i)(r.d.shreg.bpl (i)'high)
      );
      if r.c.bpltrig = '1' then
        v.d.shreg.bpl (i) := r.c.bpldat(i);
      else
        v.d.shreg.bpl(i) :=
         r.d.shreg.bpl (i)(r.d.shreg.bpl (i)'high - 1 downto 0) & '0';
      end if;
    end loop;

    if r.c.bplcon.hires = '1' then
      for i in 0 to 3 loop
        v.d.shreg.bpld(i) := (
          r.d.shreg.bpld(i)(15-2 downto 0) &
          r.d.shreg.bpl (i)(15 downto 14)
        );
        if r.c.bpltrig = '1' then
          v.d.shreg.bpl(i) := r.c.bpldat(i);
        else
          v.d.shreg.bpl(i) := r.d.shreg.bpl (i)(15-2 downto 0) & "00";
        end if;
      end loop;
    end if;

    -- Shift out sprite pixels.
    for i in r.c.spr'range loop
      v.d.shreg.spr(i).a :=
       r.d.shreg.spr(i).a(r.d.shreg.spr(i).a'high - 1 downto 0) & '0';
      v.d.shreg.spr(i).b :=
       r.d.shreg.spr(i).b(r.d.shreg.spr(i).b'high - 1 downto 0) & '0';
      if
        (r.c.spr(i).en = '1') and
        (std_ulogic_vector(r.c.h) = r.c.spr(i).sh)
      then
        v.d.shreg.spr(i).a := r.c.spr(i).data;
        v.d.shreg.spr(i).b := r.c.spr(i).datb;
      end if;
    end loop;

    -- Generate pixel bus

    for i in 0 to 1 loop
      if r.c.bplcon.hires = '1' then
        v.e.bplbus(1-i)(0) := (
          r.d.bplen(0) and
          r.d.shreg.bpld(0)(to_integer(unsigned(r.c.bplcon.pf1h and "1110"))+i)
        );
        v.e.bplbus(1-i)(2) := (
          r.d.bplen(2) and
          r.d.shreg.bpld(2)(to_integer(unsigned(r.c.bplcon.pf1h and "1110"))+i)
        );
        v.e.bplbus(1-i)(4) := '0';
        v.e.bplbus(1-i)(1) := (
          r.d.bplen(1) and
          r.d.shreg.bpld(1)(to_integer(unsigned(r.c.bplcon.pf2h and "1110"))+i)
        );
        v.e.bplbus(1-i)(3) := (
          r.d.bplen(3) and
          r.d.shreg.bpld(3)(to_integer(unsigned(r.c.bplcon.pf2h and "1110"))+i)
        );
        v.e.bplbus(1-i)(5) := '0';
      else
        v.e.bplbus(i)(0) := (
          r.d.bplen(0) and
          r.d.shreg.bpld(0)(to_integer(unsigned(r.c.bplcon.pf1h)))
        );
        v.e.bplbus(i)(2) := (
          r.d.bplen(2) and
          r.d.shreg.bpld(2)(to_integer(unsigned(r.c.bplcon.pf1h)))
        );
        v.e.bplbus(i)(4) := (
          r.d.bplen(4) and
          r.d.shreg.bpld(4)(to_integer(unsigned(r.c.bplcon.pf1h)))
        );
        v.e.bplbus(i)(1) := (
          r.d.bplen(1) and
          r.d.shreg.bpld(1)(to_integer(unsigned(r.c.bplcon.pf2h)))
        );
        v.e.bplbus(i)(3) := (
          r.d.bplen(3) and
          r.d.shreg.bpld(3)(to_integer(unsigned(r.c.bplcon.pf2h)))
        );
        v.e.bplbus(i)(5) := (
          r.d.bplen(5) and
          r.d.shreg.bpld(5)(to_integer(unsigned(r.c.bplcon.pf2h)))
        );
      end if;
    end loop;

    -- Transform 8 individual 2-line sprites into 4 groups of 4-line sprites.
    -- Each group has the same color registers.
    for i in 0 to 3 loop
      v.e.sprbus(4*i+3) := r.d.shreg.spr(2*i+1).b(15);
      v.e.sprbus(4*i+2) := r.d.shreg.spr(2*i+1).a(15);
      v.e.sprbus(4*i+1) := r.d.shreg.spr(2*i+0).b(15);
      v.e.sprbus(4*i+0) := r.d.shreg.spr(2*i+0).a(15);
      if r.c.spr(2*i+1).att = '0' then
        -- Offset into color register space of this sprite,
        -- but only if there is a pixel.
        v.e.sprbus(4*i+3 downto 4*i+2) := std_ulogic_vector(to_unsigned(i, 2));
        if
          (r.d.shreg.spr(2*i+1).b(15) = '0') and
          (r.d.shreg.spr(2*i+1).a(15) = '0') and
          (r.d.shreg.spr(2*i+0).b(15) = '0') and
          (r.d.shreg.spr(2*i+0).a(15) = '0')
        then
          v.e.sprbus(4*i+3 downto 4*i+2) := "00";
        end if;
        if
          (r.d.shreg.spr(2*i).b(15) = '0') and
          (r.d.shreg.spr(2*i).a(15) = '0')
        then
          -- High priority sprite is not visible so low priority sprite wins.
          v.e.sprbus(4*i+1) := r.d.shreg.spr(2*i+1).b(15);
          v.e.sprbus(4*i+0) := r.d.shreg.spr(2*i+1).a(15);
        end if;
      end if;
    end loop;

    -- Display priority control: select between playfields 1, 2.
    -- The "pfp" is the selected playfield placement with respect to sprites.
    -- This thing is a bit tricky. Please see the HRM

    for i in 0 to 1 loop
      -- select color and priority for bitplanes
      if r.c.bplcon.pf2pri = '1' then
        -- Playfield 2 shall have priority according to BPLCON.
        -- It means our odd numbered planes have priority over our even planes.
        v.f.bplcolor(i) :=
         "01" & r.e.bplbus(i)(5) & r.e.bplbus(i)(3) & r.e.bplbus(i)(1);
        v.f.pfp(i) := r.c.bplcon.pf2p;
        if v.f.bplcolor(i) = "01000" then
          -- Playfield 2 says color index 0 so playfield 1 wins.
          v.f.bplcolor(i) :=
           "00" & r.e.bplbus(i)(4) & r.e.bplbus(i)(2) & r.e.bplbus(i)(0);
          v.f.pfp(i) := r.c.bplcon.pf1p;
        end if;
      else
        -- Playfield 1 shall have priority according to BPLCON.
        v.f.bplcolor(i) :=
          "00" & r.e.bplbus(i)(4) & r.e.bplbus(i)(2) & r.e.bplbus(i)(0);
        v.f.pfp(i) := r.c.bplcon.pf1p;
        if
          (v.f.bplcolor(i) = "00000") and
          ((r.e.bplbus(i)(5) or r.e.bplbus(i)(3) or r.e.bplbus(i)(1)) /= '0')
        then
          -- Playfield 1 says color index 0 so playfield 2 wins.  However, if
          -- playfield 2 also selected its color index 0, then it is
          -- transparent in both playfields. In that case, either the
          -- background color or a sprite shall be visible.
          v.f.bplcolor(i) :=
           "01" & r.e.bplbus(i)(5) & r.e.bplbus(i)(3) & r.e.bplbus(i)(1);
          v.f.pfp(i) := r.c.bplcon.pf2p;
        end if;
      end if;

      if r.c.bplcon.dblpf = '0' then
        -- Dual-playfield not enabled so bypass the priority logic above.
        -- TODO: Remember bplbus(5 downto 0) and get rid of r.f.hamop?
        v.f.bplcolor(i) := r.e.bplbus(i)(4 downto 0);
        -- HRM says:
        --   "Be careful: PF2P2 - PF2P0, bits 5-3, are priority bits for
        --   normal (non-dual) playfields."
        v.f.pfp(i) := r.c.bplcon.pf2p;
      end if;
    end loop;

    v.f.hamop := r.e.bplbus(0)(5 downto 4);
    if r.c.bplcon.dblpf = '0' then
      if r.c.bplcon.homod = '1' then
        v.f.bplcolor(0)(4) := '0';
      end if;
    end if;

    -- Select color and priority for sprites.
    v.f.spp := "111";
    v.f.sprcolor := "0000";
    for i in 3 downto 0 loop
      if r.e.sprbus(4*i+3 downto 4*i) /= "0000" then
        v.f.sprcolor := r.e.sprbus(4*i+3 downto 4*i);
        v.f.spp := std_ulogic_vector(to_unsigned(i, 3));
      end if;
    end loop;

    -- Prio between any playfield and any sprite.
    v.g.issprite := '0';
    for i in 0 to 1 loop
      if unsigned(r.f.pfp(i)) <= unsigned(r.f.spp) then
        if r.f.bplcolor(i) = "00000" and r.f.sprcolor /= "0000" then
          v.g.color(i) := '1' & r.f.sprcolor;
          if i = 0 then
            v.g.issprite := '1';
          end if;
        else
          v.g.color(i) := r.f.bplcolor(i);
        end if;
      else
        if r.f.sprcolor = "0000" then
          v.g.color(i) := r.f.bplcolor(i);
        else
          v.g.color(i) := '1' & r.f.sprcolor;
          if i = 0 then
            v.g.issprite := '1';
          end if;
        end if;
      end if;

      -- Color 0 to left and right of display window.
      if r.c.diw = '0' then
        v.g.color(i) := (others => '0');
      end if;
    end loop;
    v.g.hamop := r.f.hamop;

    -- Color lookup
    for i in 0 to 1 loop
      if isx(r.g.color(i)) then
        v.h.rgb(i) := (others => 'X');
      else
        v.h.rgb(i) := r.c.color(to_integer(unsigned(r.g.color(i))));
      end if;
    end loop;

    if r.g.issprite = '0' and (true or r.c.bplcon.hires = '0') then
      if r.c.bplcon.dblpf = '0' then
        if r.c.bplcon.homod = '1' then
          -- Feedback previous RGB output and use current HAM opcode.
          v.h.rgb(0) := hold_and_modify(
            v.h.rgb(0),
            r.h.rgb(0),
            r.g.hamop,
            r.g.color(0)(3 downto 0)
          );
          v.h.rgb(1) := v.h.rgb(0);
        else
          -- EHB is a right-shift of looked-up color and no RGB feedback
          if r.g.hamop(1) = '1' then
            v.h.rgb(0)(11 downto 8) := '0' & v.h.rgb(0)(11 downto 9);
            v.h.rgb(0)( 7 downto 4) := '0' & v.h.rgb(0)( 7 downto 5);
            v.h.rgb(0)( 3 downto 0) := '0' & v.h.rgb(0)( 3 downto 1);
            v.h.rgb(1) := v.h.rgb(0);
          end if;
        end if;
      end if;
    end if;

    for i in 0 to 1 loop
      if r.c.hblank = '1' then
        v.h.rgb(i) := (others => '0');
      end if;
      if r.c.vblank = '1' and CFG_BLANK_DURING_VBLANK then
        v.h.rgb(i) := (others => '0');
      end if;
    end loop;

    for i in 0 to 1 loop
      v.h.nzd(i) := '1';
      if r.g.color(i) = "00000" then
        v.h.nzd(i) := '0';
      end if;
      if r.c.vblank = '1' then
        v.h.nzd(i) := r.c.bplcon.gaud;
      end if;
    end loop;

    if CFG_STRDEBUG then
      -- debug output for various Agnus sync strobes
      if r.b.sel.strhor   = '1' then v.h.rgb(0) := x"0f0"; end if;
      if r.b.sel.strvbl   = '1' then v.h.rgb(0) := x"f00"; end if;
      if r.b.sel.strequ   = '1' then v.h.rgb(0) := x"00f"; end if;
      if r.b.sel.strlong  = '1' then v.h.rgb(0) := x"f0f"; end if;
    end if;

    rin <= v;
  end process;

  -- Update all clk7 registers for the pipeline.
  r <= rin when rising_edge(deni.clk7);

  -- Drive the outputs, registered.
  deno.drd                <= r.drd;
  deno.drd_oe             <= r.drd_oe;
  deno.rgb                <= r.h.rgb;
  deno.nburst             <= r.nburst;
  deno.nzd                <= r.h.nzd;
  deno.drd_ext_noe        <= r.drd_ext_noe;
  deno.drd_ext_to_denice  <= r.drd_ext_to_denice;

  -- The mouse counter logic is independent from the pixel pipeline.

  joy0 : block
  begin
    -- Synchronize to clk7 domain.
    m0vs : entity work.syncer
    generic map (width => 2)
    port map (clk => deni.clk7, d => deni.m0v, q => m0v_sync);

    m0hs : entity work.syncer
    generic map (width => 2)
    port map (clk => deni.clk7, d => deni.m0h, q => m0h_sync);

    -- Decode quadrature inputs.
    q0v : entity work.joyquad
    port map (
      clk7    => deni.clk7,
      cck     => deni.cck,
      mv      => m0v_sync,
      wen     => r.b.sel.joytest,
      wdata   => r.b.drdx(15 downto 10),
      rdata   => joy0daty
    );

    q0h : entity work.joyquad
    port map (
      clk7    => deni.clk7,
      cck     => deni.cck,
      mv      => m0h_sync,
      wen     => r.b.sel.joytest,
      wdata   => r.b.drdx( 7 downto  2),
      rdata   => joy0datx
    );
  end block;

  joy1 : block
  begin
    m1vs : entity work.syncer
    generic map (width => 2)
    port map (clk => deni.clk7, d => deni.m1v, q => m1v_sync);

    m1hs : entity work.syncer
    generic map (width => 2)
    port map (clk => deni.clk7, d => deni.m1h, q => m1h_sync);

    q1v : entity work.joyquad
    port map (
      clk7    => deni.clk7,
      cck     => deni.cck,
      mv      => m1v_sync,
      wen     => r.b.sel.joytest,
      wdata   => r.b.drdx(15 downto 10),
      rdata   => joy1daty
    );

    q1h : entity work.joyquad
    port map (
      clk7    => deni.clk7,
      cck     => deni.cck,
      mv      => m1h_sync,
      wen     => r.b.sel.joytest,
      wdata   => r.b.drdx( 7 downto  2),
      rdata   => joy1datx
    );
  end block;

end;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

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

-- "mv" is a time multiplexed input signal which carries both phases. Current
-- phase is indicated by "cck".
entity joyquad is
  port (
    clk7    : in  std_ulogic;
    cck     : in  std_ulogic;
    mv      : in  std_ulogic;
    wen     : in  std_ulogic;
    wdata   : in  std_ulogic_vector(7 downto 2);
    rdata   : out std_ulogic_vector(7 downto 0)
  );
end;

begin

  process (r, cck, mv, wen, wdata)
    variable v : reg_t;
  begin
    v := r;
    -- demux the two phases
    if cck = '1' then
      v.v := mv;
    else
      v.vq := mv;
    end if;
    -- (r.vq, r.v)  cycles through states "00", "01", "11", "10", or reverse
    -- (r.d1, r.d0) cycles through states "10", "11", "00", "01", or reverse
    v.d(0) := r.v xor r.vq;
    v.d(1) := not r.vq;
    if isx(std_ulogic_vector(r.d)) or isx(std_ulogic_vector(v.d)) then
      null;
    else
      -- Detect increment and decrement conditions.
      if r.d(1 downto 0) = "11" and v.d(1 downto 0) = "00" then
        v.d(7 downto 2) := r.d(7 downto 2) + 1;
      end if;
      if r.d(1 downto 0) = "00" and v.d(1 downto 0) = "11" then
        v.d(7 downto 2) := r.d(7 downto 2) - 1;
      end if;
    end if;

    if wen = '1' then
      -- Parallel load has been requested by user.
      v.d(wdata'range) := unsigned(wdata);
    end if;
    rdata <= std_ulogic_vector(r.d);

    rin <= v;
  end process;

  r <= rin when rising_edge(clk7);
end;

architecture rtl of joyquad is
  type reg_t is record
    d   : unsigned(7 downto 0);
    v   : std_ulogic;
    vq  : std_ulogic;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;

-- Definitions for Amiga custom chipset.
package ocs is
  subtype addr_t  is std_ulogic_vector(11 downto  0);
  subtype word_t  is std_ulogic_vector(15 downto  0);
  subtype rgb4_t  is std_ulogic_vector(11 downto  0);
  subtype byte_t  is std_ulogic_vector( 7 downto  0);
  subtype rga_t   is std_ulogic_vector( 8 downto  1);
  type rgb4_array_t is array (integer range <>) of rgb4_t;
  type word_array_t is array (integer range <>) of word_t;
  type byte_array_t is array (integer range <>) of byte_t;

  constant RGA_STRHOR   : addr_t := x"03C";

  constant RGA_DIWSTOP  : addr_t := x"090";
  constant RGA_JOY0DAT  : addr_t := x"00A";
  constant RGA_JOY1DAT  : addr_t := x"00C";
  constant RGA_JOYTEST  : addr_t := x"036";
  constant RGA_BPLCON0  : addr_t := x"100";
  constant RGA_BPLCON1  : addr_t := x"102";
  constant RGA_BPLCON2  : addr_t := x"104";

  constant RGA_BPL1DAT  : addr_t := x"110";
  constant RGA_BPL2DAT  : addr_t := x"112";
  constant RGA_BPL3DAT  : addr_t := x"114";
  constant RGA_BPL4DAT  : addr_t := x"116";
  constant RGA_BPL5DAT  : addr_t := x"118";
  constant RGA_BPL6DAT  : addr_t := x"11A";

  constant RGA_COLOR00  : addr_t := x"180";
  constant RGA_COLOR01  : addr_t := x"182";
  constant RGA_COLOR02  : addr_t := x"184";
  constant RGA_COLOR03  : addr_t := x"186";
  constant RGA_COLOR04  : addr_t := x"188";
  constant RGA_COLOR05  : addr_t := x"18A";
  constant RGA_COLOR06  : addr_t := x"18C";
  constant RGA_COLOR07  : addr_t := x"18E";

  type denise_in_t is record
    clk7    : std_ulogic;
    cck     : std_ulogic;
    rga     : std_ulogic_vector( 8 downto  1);
    drd     : std_ulogic_vector(15 downto  0);
    m0v     : std_ulogic;
    m0h     : std_ulogic;
    m1v     : std_ulogic;
    m1h     : std_ulogic;
    ncsync  : std_ulogic;
    ncdac   : std_ulogic;
  end record;

  type denise_out_t is record
    drd     : std_ulogic_vector(15 downto  0);
    drd_oe  : std_ulogic;
    rgb     : rgb4_array_t(0 to 1);
    nzd     : std_ulogic_vector(0 to 1);
    nburst  : std_ulogic;

    -- external bus driver control
    drd_ext_noe       : std_ulogic;
    drd_ext_to_denice : std_ulogic;
  end record;
end;

constant RGA_DIWSTRT  : addr_t := x"08E";


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;

    d0  : in  std_ulogic;
    d1  : in  std_ulogic;
    q   : out std_ulogic
  );
end;

-- FPGA-TN-02065-1-0, Figure 2.1
architecture rtl of oddr is
  signal q0   : std_ulogic;
  signal q1   : std_ulogic;
  signal d1s  : std_ulogic;
begin
  q0  <= d0   when rising_edge(clk);
  d1s <= d1   when rising_edge(clk);
  q1  <= d1s  when falling_edge(clk);

  q <= q0 when clk = '0' else q1;
end;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;
library machxo3d;
use machxo3d.components;

entity oddr is
  port (
    clk : in  std_ulogic;
    d0  : in  std_ulogic;

  r : component components.oddrxe
  port map (
    d0    => d0,
    d1    => d1,
    sclk  => clk,
    rst   => '0',
    q     => q
  );
end;

    d1  : in  std_ulogic;
    q   : out std_ulogic
  );
end;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;
use work.ocs.all;

-- Definitions for Denise implementation
package priv is

  -- Represent one hardware sprite
  type sprite_reg_t is record
    en      : std_ulogic;
    att     : std_ulogic;
    sh      : std_ulogic_vector(8 downto 0);
    data    : word_t;
    datb    : word_t;
  end record;
  type sprite_reg_array_t is array (integer range <>) of sprite_reg_t;

  type sel_sprite_t is record
    pos     : std_ulogic; -- --2 W
    ctl     : std_ulogic; -- --4 W
    data    : std_ulogic; -- --6 W
    datb    : std_ulogic; -- --8 W
  end record;
  constant SEL_SPRITE_NONE : sel_sprite_t := (
    pos   => '0',
    ctl   => '0',
    data  => '0',
    datb  => '0'
  );
  type sel_sprite_array_t is array (natural range <>) of sel_sprite_t;

  -- All register select targets
  type sel_t is record
    joy0dat : std_ulogic; -- 00A R
    joy1dat : std_ulogic; -- 00C R
    clxdat  : std_ulogic; -- 00E R
    joytest : std_ulogic; -- 036 W
    strequ  : std_ulogic; -- 038 S
    strvbl  : std_ulogic; -- 03A S
    strhor  : std_ulogic; -- 03C S
    strlong : std_ulogic; -- 03E S
    diwstrt : std_ulogic; -- 08E S
    diwstop : std_ulogic; -- 090 S
    clxcon  : std_ulogic; -- 098 W
    bplcon0 : std_ulogic; -- 100 W
    bplcon1 : std_ulogic; -- 102 W
    bplcon2 : std_ulogic; -- 104 W
    bpldat  : std_ulogic_vector(0 to 5);  -- 110..11A W
    spr     : sel_sprite_array_t(0 to 7); -- 140..17E W
    colorx  : std_ulogic;
    color   : std_ulogic_vector(0 to 31); -- 180..1BE W
  end record;
  constant SEL_NONE : sel_t := (
    joy0dat => '0',
    joy1dat => '0',
    clxdat  => '0',
    joytest => '0',
    strequ  => '0',
    strvbl  => '0',
    strhor  => '0',
    strlong => '0',
    diwstrt => '0',
    diwstop => '0',
    clxcon  => '0',
    bplcon0 => '0',
    bplcon1 => '0',
    bplcon2 => '0',
    bpldat  => (others => '0'),
    spr     => (others => SEL_SPRITE_NONE),
    colorx  => '0',
    color   => (others => '0')
  );

  -- register address decoder
  function gen_sel(
    rga     : rga_t
  ) return sel_t;

  -- return: clxdat
  function collision_detection_logic(
    sprbus  : std_ulogic_vector(15 downto 0);
    bplbus  : std_ulogic_vector( 5 downto 0);
    clxcon  : std_ulogic_vector(15 downto 0)
  ) return std_ulogic_vector;

  -- Either normal color selection procedure, or replace R/G/B bits.
  -- return: new rgb
  function hold_and_modify(
    normal  : rgb4_t;
    left    : rgb4_t;
    op      : std_ulogic_vector(1 downto 0);
    replace : std_ulogic_vector(3 downto 0)
  ) return rgb4_t;

  function isx(
    v : std_ulogic_vector
  ) return boolean;

end;

package body priv is
  function gen_sel(
    rga     : rga_t
  ) return sel_t
  is
    variable a : std_ulogic_vector(11 downto 0) := "000" & rga & "0";
    variable s : sel_t := SEL_NONE;
  begin
    if isx(a) then
      return s;
    end if;
    case a is
      when x"00A" => s.joy0dat    := '1';
      when x"00C" => s.joy1dat    := '1';
      when x"036" => s.joytest    := '1';
      when x"00E" => s.clxdat     := '1';
      when x"098" => s.clxcon     := '1';
      when x"038" => s.strequ     := '1';
      when x"03A" => s.strvbl     := '1';
      when x"03C" => s.strhor     := '1';
      when x"03E" => s.strlong    := '1';
      when x"08E" => s.diwstrt    := '1';
      when x"090" => s.diwstop    := '1';
      when x"100" => s.bplcon0    := '1';
      when x"102" => s.bplcon1    := '1';
      when x"104" => s.bplcon2    := '1';
      when x"110" => s.bpldat(0)  := '1';
      when x"112" => s.bpldat(1)  := '1';
      when x"114" => s.bpldat(2)  := '1';
      when x"116" => s.bpldat(3)  := '1';
      when x"118" => s.bpldat(4)  := '1';
      when x"11A" => s.bpldat(5)  := '1';

      when x"140" => s.spr(0).pos := '1';
      when x"142" => s.spr(0).ctl := '1';
      when x"144" => s.spr(0).data:= '1';
      when x"146" => s.spr(0).datb:= '1';
      when x"148" => s.spr(1).pos := '1';
      when x"14A" => s.spr(1).ctl := '1';
      when x"14C" => s.spr(1).data:= '1';
      when x"14E" => s.spr(1).datb:= '1';

      when x"150" => s.spr(2).pos := '1';
      when x"152" => s.spr(2).ctl := '1';
      when x"154" => s.spr(2).data:= '1';


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

-- Bus Functional Model for RGA bus

library ieee;
use ieee.std_logic_1164.all;
use work.ocs.all;
use work.text.all;

package rga_bfm is
  type rga_op is (
    IDLE,
    READ,
    WRITE
  );

  type rga_log is (LOG_FAIL, LOG_ERROR, LOG_INFO, LOG_DEBUG);
  type rga_cmd is record
    op    : rga_op;
    addr  : std_logic_vector(11 downto 0);
    wdata : std_logic_vector(15 downto 0);
    rdata : std_logic_vector(15 downto 0);
    req   : std_logic;
    ack   : std_logic;
    log   : rga_log;
  end record;

  signal rgacmd : rga_cmd := (
    op    => IDLE,
    addr  => (others => 'X'),
    wdata => (others => 'X'),
    rdata => (others => 'Z'),
    req   => '0',
    ack   => 'Z',
    log   => LOG_INFO
  );

  procedure rga_loglevel (
    signal cmd      : inout rga_cmd;
    constant level  : rga_log
  );

  procedure dbg (constant cmd : rga_cmd; str : string);
  procedure info(constant cmd : rga_cmd; str : string);
  procedure fail(constant cmd : rga_cmd; str : string);

  procedure rga_write (
    signal cmd      : inout rga_cmd;
    constant addr   : in    addr_t;
    constant data   : in    word_t
  );

  procedure rga_read (
    signal cmd      : inout rga_cmd;
    constant addr   : in    addr_t;
    variable data   : out   word_t
  );

end;

package body rga_bfm is

  procedure dbg(constant cmd : rga_cmd; str : string) is
  begin

    puts("DEBUG RGA: " & str);
  end;

  procedure info(constant cmd : rga_cmd; str : string) is
  begin
    if cmd.log < LOG_INFO then return; end if;
    puts("INFO  RGA: " & str);
  end;

  procedure fail(constant cmd : rga_cmd; str : string) is
  begin
    report "FAIL  RGA: " & str severity failure;
  end;

  procedure rga_loglevel (
    signal cmd      : inout rga_cmd;
    constant level  : rga_log
  ) is
  begin
    cmd.log <= level;
  end;

  procedure rga_begin (
    signal cmd      : inout rga_cmd
  ) is
  begin
    cmd.req <= '1';
    wait for 0 ns;
    if cmd.ack /= '1' then
      wait until cmd.ack = '1';
    end if;
  end;

  procedure rga_end (
    signal cmd      : inout rga_cmd
  ) is
  begin
    cmd.req <= '0';
    wait for 0 ns;
    if cmd.ack /= '0' then
      wait until cmd.ack = '0';
    end if;
  end;

  procedure rga_write (
    signal cmd      : inout rga_cmd;
    constant addr   : in    addr_t;
    constant data   : in    word_t
  ) is
  begin
    cmd.addr <= addr;
    cmd.wdata <= data;
    cmd.op <= WRITE;

    rga_begin(cmd);
    rga_end(cmd);
    dbg(cmd, "write $" & to_hstring(addr) & " <- $" & to_hstring(data));
  end;

  procedure rga_read (
    signal cmd      : inout rga_cmd;
    constant addr   : in    addr_t;
    variable data   : out   word_t
  ) is
  begin
    cmd.addr <= addr;
    cmd.op <= READ;

    rga_begin(cmd);
    rga_end(cmd);
    data := cmd.rdata;

    dbg(cmd, "read  $" & to_hstring(addr) & " -> $" & to_hstring(data));
  end;

end;

if cmd.log < LOG_DEBUG then return; end if;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;
use work.rga_bfm.all;
use work.ocs.all;
use work.text.all;

entity rga_bfm_impl is
  port (
    clk7  : in    std_ulogic;
    cck   : in    std_ulogic;
    deni  : out   denise_in_t;
    deno  : in    denise_out_t
  );
end;

architecture beh of rga_bfm_impl is
begin
  process
  begin
    info(rgacmd, "power-on");
    deni.drd <= (others => 'Z');
    deni.rga <= (others => '1');
    wait until rising_edge(cck);
    wait until rising_edge(cck);

    for i in 2 to 9 loop
      wait until rising_edge(cck);
    end loop;

    info(rgacmd, "power-up reset done");

    -- Main loop Ready to take BFM commands
    loop
      if rgacmd.req /= '1' then
        wait until rgacmd.req = '1';
      end if;
      rgacmd.ack <= '1';
      wait for 0 ns;

      case rgacmd.op is
      when WRITE|READ =>
          wait until rising_edge(cck);
          wait for 110 ns - 70 ns;
          deni.rga <= rgacmd.addr(8 downto 1);
          wait until falling_edge(cck);
          wait for 10 ns;
          deni.rga <= (others => '1');

          wait for 40 ns;
          if rgacmd.op = WRITE then
            deni.drd <= rgacmd.wdata;
          end if;
          wait until rising_edge(cck);
          if rgacmd.op = WRITE then

      when others =>
        fail(rgacmd, "Unimplemented operation: " & rga_op'image(rgacmd.op));

      end case;
      rgacmd.ack <= '0';
      wait for 0 ns;
      while rgacmd.req = '1' loop
      end loop;
    end loop;
  end process;

  deni.clk7   <= clk7;
  deni.cck    <= cck;
  deni.m0v    <= '0';
  deni.m0h    <= '0';
  deni.m1v    <= '0';
  deni.m1h    <= '0';
  deni.ncsync <= 'X';
  deni.ncdac  <= 'X';
end;

            deni.drd <= (others => 'Z') after 20 ns;
          else
            rgacmd.rdata <= deno.drd;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;
use work.tbtest.all;
library ocs;
use ocs.ocs.all;
use ocs.text.all;

architecture beh of tb is
  constant CCK_PERIOD : time := 280 ns;
  signal clk7       : std_ulogic := '0';
  signal cck        : std_ulogic := '0';
  signal deni       : denise_in_t;
  signal deno       : denise_out_t;

  signal drd            : std_logic_vector (15 downto  0);
begin

  rga_bfm0 : entity ocs.rga_bfm_impl
  port map (
    -- clocks are driven out on deni
    clk7      => clk7,
    cck       => cck,
    deni      => deni,
    deno      => deno
  );

  deno.drd <= drd;
  drd <= deni.drd;

  dut : entity ocs.top
  port map (
    clk7    => clk7,
    cck     => cck,
    rga     => deni.rga,
    drd     => drd,
    m0v     => deni.m0v,
    m0h     => deni.m0h,
    m1v     => deni.m1v,
    m1h     => deni.m1h,
    ncsync  => 'X',
    ncdac   => 'X'
  );

  stim_cck : process
  begin
    if end_of_simulation then
      -- may have PLL or similar running so force stop
      -- std.env.stop(0);
      wait;
    else
      cck <= '0';
      wait for CCK_PERIOD / 2;
      cck <= '1';
      wait for CCK_PERIOD / 2;
    end if;
  end process;

  stim_7m : process
  begin
    wait for 15 ns;
    loop
      if end_of_simulation then
        wait;
      else
        clk7 <= '0';
        wait for CCK_PERIOD / 4;
        clk7 <= '1';
        wait for CCK_PERIOD / 4;
      end if;
    end loop;
  end process;

  tc: component testcode;

  confinfo : process
  begin
    ocs.text.puts("sysfreq:  " & integer'image((1000.0 us) / CCK_PERIOD) & " KHz");
    ocs.text.puts("period:   " & time'image(CCK_PERIOD));
    ocs.text.puts("--- simulation begin");
    wait until end_of_simulation;
    wait for 0 ns;
    ocs.text.puts("--- simulation end");
    wait;
  end process;
end;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

package tbtest is
  component testcode
  end component;

  signal end_of_simulation : boolean := false;
end;

library ieee;
use ieee.std_logic_1164.all;
library ocs;
use ocs.ocs.all;
use ocs.rga_bfm.all;
use work.tbtest.all;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use std.textio.all;

package text is
  procedure puts(constant s : in string);
end;

package body text is
  procedure puts(constant s : in string) is

  begin
    write(l, s);
    writeline(output, l);
  end;
end;


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

library ieee;
use ieee.std_logic_1164.all;

entity syncer is
  generic (
    width   : positive := 2
  );
  port (
    clk     : in  std_ulogic;
    d       : in  std_ulogic;

  );
end;

architecture rtl of syncer is
  signal rlev0_async, rlev1_async : std_ulogic;
  signal r : std_ulogic_vector(width-1 downto 0);

begin
  r0 : process(clk)
  begin
    if rising_edge(clk) then
      rlev0_async <= d;
      rlev1_async <= rlev0_async;
    end if;
  end process;

  many : if 2 < width generate
    r1 : process(clk)
    begin
      if rising_edge(clk) then
        r <= r(r'high-1 downto 0) & rlev1_async;
      end if;
    end process;
  end generate;

  q <= rlev1_async when width <= 2 else r(r'high);
end;


architecture joy0 of test is
begin
  process
    variable data0 : word_t;
  begin
    rga_loglevel(rgacmd, LOG_DEBUG);
    wait for 1 us;
    rga_write(rgacmd, RGA_STRHOR,  x"cafe");

    rga_write(rgacmd, RGA_DIWSTOP, x"f4c1");
    rga_write(rgacmd, RGA_BPLCON0, x"2000");
    rga_write(rgacmd, RGA_BPLCON1, x"0022");
    wait for 8 us;

    rga_read (rgacmd, RGA_JOY0DAT, data0);
    rga_write(rgacmd, RGA_JOYTEST, x"55aa");
    rga_read (rgacmd, RGA_JOY0DAT, data0);
    assert data0(15 downto 10) = "010101";
    assert data0( 7 downto  2) = "101010";

    wait for 3 us;
    end_of_simulation <= true;
    wait;
  end process;
end;

configuration joy0 of tb is
  for beh for tc: testcode
      use entity work.test(joy0);
  end for; end for;
end;


architecture wb0 of test is
begin
  process
  begin
    rga_loglevel(rgacmd, LOG_DEBUG);
    wait for 1 us;
    rga_write(rgacmd, RGA_STRHOR,  x"cafe");
    rga_write(rgacmd, RGA_DIWSTRT, x"2c81");
    rga_write(rgacmd, RGA_DIWSTOP, x"f4c1");
    rga_write(rgacmd, RGA_BPLCON0, x"2000");
    rga_write(rgacmd, RGA_BPLCON1, x"0022");
    wait for 8 us;

    rga_write(rgacmd, RGA_COLOR06, x"0666");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"3c33");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"5555");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"0f30");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"0f50");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    wait for 1 us;
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    rga_write(rgacmd, RGA_BPL2DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");
    rga_write(rgacmd, RGA_BPL1DAT, x"aaaa");
    rga_write(rgacmd, RGA_COLOR07, x"0777");

    wait for 8*10 us;

    wait for 3 us;
    end_of_simulation <= true;
    wait;
  end process;
end;

configuration wb0 of tb is
  for beh for tc: testcode
      use entity work.test(wb0);
  end for; end for;
end;

    rga_write(rgacmd, RGA_COLOR00, x"0000");
    rga_write(rgacmd, RGA_COLOR01, x"0111");
    rga_write(rgacmd, RGA_COLOR02, x"0222");


--  Copyright (C) 2020-2021 Martin Åberg
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with this program; if not, write to the Free Software Foundation, Inc.,
--  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

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

-- Technology-independent top level
entity top is
  port (
    -- 7.15909 MHz
    clk7            : in    std_ulogic;
    -- Color clock
    cck             : in    std_ulogic;
    rga             : in    std_ulogic_vector( 8 downto  1);
    drd             : inout std_ulogic_vector(15 downto  0);
    video_r         : out   std_ulogic_vector( 3 downto  0);
    video_b         : out   std_ulogic_vector( 3 downto  0);
    video_g         : out   std_ulogic_vector( 3 downto  0);

    m1h             : in    std_ulogic;

    -- Color burst, only for color composite
    nburst          : out   std_ulogic;
    -- "PIXELSW", "Background indicator", "zero detect", to RGB port
    nzd             : out   std_ulogic;

    -- Composite sync (ECS)
    ncsync          : in    std_ulogic;
    -- CDAC (ECS)
    ncdac           : in    std_ulogic;

    -- external bus driver control
    drd_noe         : out   std_ulogic;
    drd_rl_to_fpga  : out   std_ulogic;

    user0           : inout std_ulogic;
    user1           : inout std_ulogic;
    led0            : out   std_ulogic
  );
end;

architecture rtl of top is
  signal deni       : work.ocs.denise_in_t;
  signal deno       : work.ocs.denise_out_t;

begin
  den0 : entity ocs.denise
  port map (
    deni  => deni,
    deno  => deno
  );

  -- Connect the inputs

  deni.clk7   <= clk7;
  deni.cck    <= cck;
  deni.rga    <= rga;
  deni.drd    <= drd;
  deni.m0v    <= m0v;
  deni.m0h    <= m0h;
  deni.m1v    <= m1v;
  deni.m1h    <= m1h;
  deni.ncsync <= ncsync;
  deni.ncdac  <= ncdac;

  -- Drive the outputs

  drd             <= deno.drd when deno.drd_oe = '1' else "ZZZZZZZZZZZZZZZZ";
  drd_noe         <= deno.drd_ext_noe;
  drd_rl_to_fpga  <= deno.drd_ext_to_denice;
  nburst          <= deno.nburst;

  -- All logic is clocked in the clk7 domain which corresponds to lores pixel
  -- resolution. DDR output registers are used to emit hires RGB pixels.
  vidx : for i in 0 to 3 generate
    r : entity ocs.oddr
    port map (
      d0  => deno.rgb(0)(8+i),
      d1  => deno.rgb(1)(8+i),
      clk => clk7,
      q   => video_r(i)
    );

    g : entity ocs.oddr
    port map (
      d0  => deno.rgb(0)(4+i),
      d1  => deno.rgb(1)(4+i),
      clk => clk7,
      q   => video_g(i)
    );

    b : entity ocs.oddr
    port map (
      d0  => deno.rgb(0)(0+i),
      d1  => deno.rgb(1)(0+i),
      clk => clk7,
      q   => video_b(i)
    );
  end generate;

  vidnzd : entity ocs.oddr
  port map (
    d0  => deno.nzd(0),
    d1  => deno.nzd(1),
    clk => clk7,
    q   => nzd
  );

  -- Set low brightness on user LED when clk7 is available.

  blinkz : block
    signal cnt : unsigned(15 downto 0) := (others => '0');
  begin
    cnt <= cnt + 1 when rising_edge(clk7);
    led0 <= '0' when cnt(cnt'high downto cnt'high-4) = "1111" else '1';
  end block;

end;

    m0v             : in    std_ulogic;
    m0h             : in    std_ulogic;


package ENV is

  procedure STOP (STATUS   : INTEGER);
  procedure FINISH (STATUS : INTEGER);

  function RESOLUTION_LIMIT return DELAY_LENGTH;

end package ENV;
library ieee_proposed;
use ieee_proposed.standard_additions.all;
package body ENV is

  procedure STOP (STATUS   : INTEGER) is

  begin
    report "Procedure FINISH called with status: " & INTEGER'image(STATUS)
      severity failure;
  end procedure FINISH;

  constant BASE_TIME_ARRAY : time_vector :=
    (
      1 fs, 10 fs, 100 fs,
      1 ps, 10 ps, 100 ps,
      1 ns, 10 ns, 100 ns,
      1 us, 10 us, 100 us,
      1 ms, 10 ms, 100 ms,
      1 sec, 10 sec, 100 sec,
      1 min, 10 min, 100 min,
      1 hr, 10 hr, 100 hr
      ) ;

  function RESOLUTION_LIMIT return DELAY_LENGTH is
  begin
    for i in BASE_TIME_ARRAY'range loop
      if BASE_TIME_ARRAY(i) > 0 hr then
        return BASE_TIME_ARRAY(i);
      end if;
    end loop;
    report "STANDATD.RESOLUTION_LIMIT: Simulator resolution not less than 100 hr"
      severity failure;
    return 1 ns;
  end function RESOLUTION_LIMIT;

end package body ENV;

  begin
    report "Procedure STOP called with status: " & INTEGER'image(STATUS)
      severity failure;