/*!
 \file Registers.h
 \brief Basic register file
 \author Màrius Montón
 \date August 2018
 */
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef REGISTERS_H
#define REGISTERS_H

#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <iomanip>
#include <unordered_map>

#include "systemc"
#include "tlm.h"

#include "Performance.h"
#include "Memory.h"

#define MISA_A_EXTENSION (1 << 0)
#define MISA_B_EXTENSION (1 << 1)
#define MISA_C_EXTENSION (1 << 2)
#define MISA_I_BASE (1 << 8)
#define MISA_M_EXTENSION (1 << 12)
#define MISA_MXL (1 << 30)

#define CSR_MVENDORID (0xF11)
#define CSR_MARCHID (0xF12)
#define CSR_MIMPID (0xF13)
#define CSR_MHARTID (0xF14)

#define CSR_MSTATUS (0x300)
#define CSR_MISA (0x301)
#define CSR_MEDELEG (0x302)
#define CSR_MIDELEG (0x303)
#define CSR_MIE (0x304)
#define CSR_MTVEC (0x305)
#define CSR_MCOUNTEREN (0x306)
#define CSR_MSTATUSH (0x310)

#define CSR_MSCRATCH (0x340)
#define CSR_MEPC (0x341)
#define CSR_MCAUSE (0x342)
#define CSR_MTVAL (0x343)
#define CSR_MIP (0x344)

#define CSR_SSCRATCH (0x140)
#define CSR_SEPC (0x141)
#define CSR_SCAUSE (0x142)
#define CSR_STVAL (0x143)
#define CSR_SIP (0x144)

#define CSR_MCYCLE (0xB00)
#define CSR_MINSTRET (0xB02)
#define CSR_MCYCLEH (0xB80)
#define CSR_MINSTRETH (0xB82)

#define CSR_CYCLE (0xC00)
#define CSR_TIME (0xC01)
#define CSR_INSTRET (0xC02)

#define CSR_CYCLEH (0xC80)
#define CSR_TIMEH (0xC81)
#define CSR_INSTRETH (0xC02)

#define CSR_STVEC (0x105)

#define MSTATUS_UIE (1 << 0)
#define MSTATUS_SIE (1 << 1)
#define MSTATUS_MIE (1 << 3)
#define MSTATUS_UPIE (1 << 4)
#define MSTATUS_SPIE (1 << 5)
#define MSTATUS_MPIE (1 << 7)
#define MSTATUS_SPP (1 << 8)
#define MSTATUS_MPP (1 << 11)
#define MSTATUS_FS  (1 << 13)
#define MSTATUS_XS	(1 << 15)
#define MSTATUS_MPRV (1 << 17)
#define MSTATUS_SUM (1 << 18)
#define MSTATUS_MXR (1 << 19)
#define MSTATUS_TVM (1 << 20)
#define MSTATUS_TW (1 << 21)
#define MSTATUS_TSR (1 << 22)

#define MIP_USIP (1 << 0)
#define MIP_SSIP (1 << 1)
#define MIP_MSIP (1 << 3)
#define MIP_UTIP (1 << 4)
#define MIP_STIP (1 << 5)
#define MIP_MTIP (1 << 7)
#define MIP_UEIP (1 << 8)
#define MIP_SEIP (1 << 9)
#define MIP_MEIP (1 << 11)

#define MIE_USIE (1 << 0)
#define MIE_SSIE (1 << 1)
#define MIE_MSIE (1 << 3)
#define MIE_UTIE (1 << 4)
#define MIE_STIE (1 << 5)
#define MIE_MTIE (1 << 7)
#define MIE_UEIE (1 << 8)
#define MIE_SEIE (1 << 9)
#define MIE_MEIE (1 << 11)

/* 1 ns tick in CYCLE & TIME counters */
#define TICKS_PER_SECOND (1000000)

/**
 * @brief Register file implementation
 */
class Registers {
public:

	enum {
		x0 = 0,
		x1 = 1,
		x2,
		x3,
		x4,
		x5,
		x6,
		x7,
		x8,
		x9,
		x10,
		x11,
		x12,
		x13,
		x14,
		x15,
		x16,
		x17,
		x18,
		x19,
		x20,
		x21,
		x22,
		x23,
		x24,
		x25,
		x26,
		x27,
		x28,
		x29,
		x30,
		x31,
		zero = x0,
		ra = x1,
		sp = x2,
		gp = x3,
		tp = x4,
		t0 = x5,
		t1 = x6,
		t2 = x7,
		s0 = x8,
		fp = x8,
		s1 = x9,
		a0 = x10,
		a1 = x11,
		a2 = x12,
		a3 = x13,
		a4 = x14,
		a5 = x15,
		a6 = x16,
		a7 = x17,
		s2 = x18,
		s3 = x19,
		s4 = x20,
		s5 = x21,
		s6 = x22,
		s7 = x23,
		s8 = x24,
		s9 = x25,
		s10 = x26,
		s11 = x27,
		t3 = x28,
		t4 = x29,
		t5 = x30,
		t6 = x31
	};
	/**
	 * Default constructor
	 */
	Registers();

	/**
	 * Set value for a register
	 * @param reg_num register number
	 * @param value   register value
	 */
	void setValue(int reg_num, int32_t value);

	/**
	 * Returns register value
	 * @param  reg_num register number
	 * @return         register value
	 */
	int32_t getValue(int reg_num);

	/**
	 * Returns PC value
	 * @return PC value
	 */
	uint32_t getPC();

	/**
	 * Sets arbitraty value to PC
	 * @param new_pc new address to PC
	 */
	void setPC(uint32_t new_pc);

	/**
	 * Increments PC couunter to next address
	 */
	inline void incPC(bool C_ext = false) {
		if (C_ext == true) {
			register_PC += 2;
		} else {
			register_PC += 4;
		}

	}

	/**
	 * @brief Get CSR value
	 * @param csr CSR number to access
	 * @return CSR value
	 */
	uint32_t getCSR(int csr);

	/**
	 * @brief Set CSR value
	 * @param csr   CSR number to access
	 * @param value new value to register
	 */
	void setCSR(int csr, uint32_t value);

	/**
	 * Dump register data to console
	 */
	void dump();
private:
	/**
	 * bank of registers (32 regs of 32bits each)
	 */
	int32_t register_bank[32];

	/**
	 * Program counter (32 bits width)
	 */
	uint32_t register_PC;

	/**
	 * CSR registers (4096 maximum)
	 */
	//uint32_t CSR[4096];
	std::unordered_map<unsigned int, uint32_t> CSR;


	Performance *perf;

	void initCSR(void);
};

#endif
