class Consts:
    # control signals
    X          = False
    Y          = True
    N          = False
    # PC Select Signal
    SZ_PC_SEL  = 2
    PC_4       = 0
    PC_BRJMP   = 1
    PC_JALR    = 2
    PC_EXC     = 3
    _PC_4      = modbv(0)[SZ_PC_SEL:]
    _PC_BRJMP  = modbv(1)[SZ_PC_SEL:]
    _PC_JALR   = modbv(2)[SZ_PC_SEL:]
    _PC_EXC    = modbv(3)[SZ_PC_SEL:]
    # Branch type
    SZ_BR      = 4
    BR_X       = 0
    BR_N       = 0
    BR_NE      = 1
    BR_EQ      = 2
    BR_GE      = 3
    BR_GEU     = 4
    BR_LT      = 5
    BR_LTU     = 6
    BR_J       = 7
    BR_JR      = 8
    _BR_X      = modbv(0)[SZ_BR:]
    _BR_N      = modbv(0)[SZ_BR:]
    _BR_NE     = modbv(1)[SZ_BR:]
    _BR_EQ     = modbv(2)[SZ_BR:]
    _BR_GE     = modbv(3)[SZ_BR:]
    _BR_GEU    = modbv(4)[SZ_BR:]
    _BR_LT     = modbv(5)[SZ_BR:]
    _BR_LTU    = modbv(6)[SZ_BR:]
    _BR_J      = modbv(7)[SZ_BR:]
    _BR_JR     = modbv(8)[SZ_BR:]
    # RS1 Operand Select Signal
    SZ_OP1     = 2
    OP1_X      = 0
    OP1_RS1    = 0
    OP1_PC     = 1
    OP1_ZERO   = 2
    _OP1_X     = modbv(0)[SZ_OP1:]
    _OP1_RS1   = modbv(0)[SZ_OP1:]
    _OP1_PC    = modbv(1)[SZ_OP1:]
    _OP1_ZERO  = modbv(2)[SZ_OP1:]
    # RS2 Operand Select Signal
    SZ_OP2     = 2
    OP2_X      = 0
    OP2_RS2    = 0
    OP2_IMM    = 1
    OP2_FOUR   = 2
    OP2_ZERO   = 3
    _OP2_X     = modbv(0)[SZ_OP2:]
    _OP2_RS2   = modbv(0)[SZ_OP2:]
    _OP2_IMM   = modbv(1)[SZ_OP2:]
    _OP2_FOUR  = modbv(2)[SZ_OP2:]
    _OP2_ZERO  = modbv(3)[SZ_OP2:]
    # IMM
    SZ_IMM     = 3
    IMM_X      = 0
    IMM_S      = 0
    IMM_SB     = 1
    IMM_U      = 2
    IMM_UJ     = 3
    IMM_I      = 4
    IMM_Z      = 5
    _IMM_X     = modbv(0)[SZ_IMM:]
    _IMM_S     = modbv(0)[SZ_IMM:]
    _IMM_SB    = modbv(1)[SZ_IMM:]
    _IMM_U     = modbv(2)[SZ_IMM:]
    _IMM_UJ    = modbv(3)[SZ_IMM:]
    _IMM_I     = modbv(4)[SZ_IMM:]
    _IMM_Z     = modbv(5)[SZ_IMM:]
    # Forwarding
    SZ_FWD     = 2
    FWD_X      = 0
    FWD_N      = 0
    FWD_EX     = 1
    FWD_MEM    = 2
    FWD_WB     = 3
    _FWD_X     = modbv(0)[SZ_FWD:]
    _FWD_N     = modbv(0)[SZ_FWD:]
    _FWD_EX    = modbv(1)[SZ_FWD:]
    _FWD_MEM   = modbv(2)[SZ_FWD:]
    _FWD_WB    = modbv(3)[SZ_FWD:]
    # WB signals
    SZ_WB      = 2
    WB_X       = 0
    WB_ALU     = 0
    WB_MEM     = 1
    WB_CSR     = 2
    _WB_X      = modbv(0)[SZ_WB:]
    _WB_ALU    = modbv(0)[SZ_WB:]
    _WB_MEM    = modbv(1)[SZ_WB:]
    _WB_CSR    = modbv(2)[SZ_WB:]
    # Data Type
    SZ_MT  = 3
    MT_X   = 0
    MT_B   = 1
    MT_H   = 2
    MT_W   = 3
    MT_BU  = 5
    MT_HU  = 6
    _MT_X  = modbv(0)[SZ_MT:]
    _MT_B  = modbv(1)[SZ_MT:]
    _MT_H  = modbv(2)[SZ_MT:]
    _MT_W  = modbv(3)[SZ_MT:]
    _MT_BU = modbv(5)[SZ_MT:]
    _MT_HU = modbv(6)[SZ_MT:]
    # Memory function
    SZ_M   = 1
    M_X    = False
    M_RD   = False
    M_WR   = True
    # PRIV
    MTVEC      = 0x100
    START_ADDR = MTVEC + 0x100
    # NOP
    BUBBLE     = 0x4033  # XOR r0, r0, r0
    NOP        = 0x13    # ADDI r0, r0, 0 class ALUOp:
    """
    List of ALU opcodes.
    """
    SZ_OP      = 5
    OP_ADD     = 0
    OP_SLL     = 1
    OP_XOR     = 2
    OP_SRL     = 3
    OP_OR      = 4
    OP_AND     = 5
    OP_SUB     = 6
    OP_SRA     = 7
    OP_SLT     = 8
    OP_SLTU    = 9
    OP_MUL     = 10
    OP_MULH    = 11
    OP_MULHSU  = 12
    OP_MULHU   = 13
    OP_DIV     = 14
    OP_DIVU    = 15
    OP_REM     = 16
    OP_REMU    = 17
    _OP_ADD    = modbv(OP_ADD)[SZ_OP:]
    _OP_SLL    = modbv(OP_SLL)[SZ_OP:]
    _OP_XOR    = modbv(OP_XOR)[SZ_OP:]
    _OP_SRL    = modbv(OP_SRL)[SZ_OP:]
    _OP_OR     = modbv(OP_OR)[SZ_OP:]
    _OP_AND    = modbv(OP_AND)[SZ_OP:]
    _OP_SUB    = modbv(OP_SUB)[SZ_OP:]
    _OP_SRA    = modbv(OP_SRA)[SZ_OP:]
    _OP_SLT    = modbv(OP_SLT)[SZ_OP:]
    _OP_SLTU   = modbv(OP_SLTU)[SZ_OP:]
    _OP_MUL    = modbv(OP_MUL)[SZ_OP:]
    _OP_MULH   = modbv(OP_MULH)[SZ_OP:]
    _OP_MULHSU = modbv(OP_MULHSU)[SZ_OP:]
    _OP_MULHU  = modbv(OP_MULHU)[SZ_OP:]
    _OP_DIV    = modbv(OP_DIV)[SZ_OP:]
    _OP_DIVU   = modbv(OP_DIVU)[SZ_OP:]
    _OP_REM    = modbv(OP_REM)[SZ_OP:]
    _OP_REMU   = modbv(OP_REMU)[SZ_OP:] class CSRCMD:
    """
    CSR commands.

    The CSR_READ command is for those cases when the 'rs1' field is zero.
    """
    SZ_CMD     = 3
    CSR_IDLE   = 0
    CSR_READ   = 4
    CSR_WRITE  = 5
    CSR_SET    = 6
    CSR_CLEAR  = 7
    _CSR_IDLE  = modbv(0)[SZ_CMD:]
    _CSR_READ  = modbv(4)[SZ_CMD:]
    _CSR_WRITE = modbv(5)[SZ_CMD:]
    _CSR_SET   = modbv(6)[SZ_CMD:]
    _CSR_CLEAR = modbv(7)[SZ_CMD:] class CSRExceptionCode:
    """
    Exception codes.
    (Priviledge mode v1.7)
    """
    SZ_ECODE               = 4
    # Exception codes
    E_INST_ADDR_MISALIGNED = 0
    E_INST_ACCESS_FAULT    = 1
    E_ILLEGAL_INST         = 2
    E_BREAKPOINT           = 3
    E_LOAD_ADDR_MISALIGNED = 4
    E_LOAD_ACCESS_FAULT    = 5
    E_AMO_ADDR_MISALIGNED  = 6
    E_AMO_ACCESS_FAULT     = 7
    E_ECALL_FROM_U         = 8
    E_ECALL_FROM_S         = 9
    E_ECALL_FROM_H         = 10
    E_ECALL_FROM_M         = 11
    # Interrupt codes
    I_SOFTWARE             = 0
    I_TIMER                = 1 class CSRModes:
    """
    Priviledge modes.
    """
    SZ_MODE = 2
    PRV_U   = 0
    PRV_S   = 1
    PRV_H   = 2
    PRV_M   = 3 class WishboneMaster:
    """
    Defines the wishbone master signals.
    """
    def __init__(self, intercon):
        """
        Initializes the IO ports, using the intercon.

        :param intercon: the intercon bus.
        """
        if not isinstance(intercon, WishboneIntercon):
            raise AttributeError("Unknown intercon type for {0}".format(str(intercon)))

        self.addr_o  = intercon.addr
        self.dat_o   = intercon.dat_o
        self.dat_i   = intercon.dat_i
        self.sel_o   = intercon.sel
        self.cyc_o   = intercon.cyc
        self.we_o    = intercon.we
        self.stb_o   = intercon.stb
        self.ack_i   = intercon.ack
        self.err_i   = intercon.err class WishboneMasterGenerator():
    """
    Wishbone Master generator.

    This clase generates the state machine for a master device.
    The class requires trigger signals to start a wishbone cycle access.

    Support for classic cycle only.
    """
    def __init__(self, clk_i, rst_i, master_signals, flagread, flagwrite, flagrmw):
        """
        Initializes the class.

        :param clk_i:          Wishbone clock
        :param rst_i:          Wishbone reset
        :param master_signals: Wishbone master signals to drive.
        :param flagread:       Initiates a read cycle.
        :param flagwrite:      Initiates a write cycle.
        :param flagrmw:        Initiates a read-modify-write access.
        """
        if isinstance(master_signals, WishboneMaster):
            self.wbmsig = master_signals
        else:
            raise AttributeError("Argument slave_signals must be of type WishboneSlave. Argument: {0}".format(str(master_signals)))

        self.clk       = clk_i
        self.rst       = rst_i
        self.flagread  = flagread
        self.flagwrite = flagwrite
        self.flagrmw   = flagrmw

    def gen_wbm(self):
        """
        State machine for master.
        Creates the state machine for a master device.
        """
        self.wbm_states_t = enum('WBM_IDLE',
                                 'WBM_INCYCLE',
                                 'WBM_READ_WAIT',
                                 'WBM_WRITE_WAIT',
                                 'WBM_RMW_RD_WAIT',
                                 'WBM_RMW_MID_WAIT',
                                 'WBM_RMW_WR_WAIT')
        self.wbm_state = Signal(self.wbm_states_t.WBM_IDLE)

        trig_vector = Signal(modbv(0)[3:])
        ack_vector  = Signal(modbv(0)[2:])

        @always_comb
        def concat_trig_vector():
            trig_vector.next = concat(self.flagread,
                                      self.flagwrite,
                                      self.flagrmw)

        @always_comb
        def concat_ack_vector():
            ack_vector.next = concat(self.wbmsig.ack_i, self.wbmsig.err_i)

        # state machine
        @always(self.clk.posedge)
        def wbmstate_fsm():
            if self.rst:
                self.wbm_state.next = self.wbm_states_t.WBM_IDLE
            else:
                if self.wbm_state == self.wbm_states_t.WBM_IDLE:
                    # No access to master bus
                    # Check for trigger signals
                    if trig_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                    else:
                        if self.flagread:
                            self.wbm_state.next = self.wbm_states_t.WBM_READ_WAIT
                        elif self.flagwrite:
                            self.wbm_state.next = self.wbm_states_t.WBM_WRITE_WAIT
                        elif self.flagrmw:
                            self.wbm_state.next = self.wbm_states_t.WBM_RMW_RD_WAIT
                elif self.wbm_state == self.wbm_states_t.WBM_INCYCLE:
                    # Current cycle in progress, but inactive.
                    if trig_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                    else:
                        if self.flagread:
                            self.wbm_state.next = self.wbm_states_t.WBM_READ_WAIT
                        elif self.flagwrite:
                            self.wbm_state.next = self.wbm_states_t.WBM_WRITE_WAIT
                        elif self.flagrmw:
                            self.wbm_state.next = self.wbm_states_t.WBM_RMW_RD_WAIT
                elif self.wbm_state == self.wbm_states_t.WBM_READ_WAIT:
                    # Read operaton in the bus
                    if ack_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_READ_WAIT
                    else:
                        # check for ACK or ERR
                        if not self.wbmsig.err_i:
                            # No error: ACK
                            # Check for end of cycle
                            if trig_vector == 0:
                                self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                            else:
                                self.wbm_state.next = self.wbm_states_t.WBM_INCYCLE
                        else:
                            # Error in cycle
                            self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                elif self.wbm_state == self.wbm_states_t.WBM_WRITE_WAIT:
                    # Write operation in the bus
                    if ack_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_WRITE_WAIT
                    else:
                        # check for ACK or ERR
                        if not self.wbmsig.err_i:
                            # No error: ACK
                            # Check for end of cycle
                            if trig_vector == 0:
                                self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                            else:
                                self.wbm_state.next = self.wbm_states_t.WBM_INCYCLE
                        else:
                            # Error in cycle
                            self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                elif self.wbm_state == self.wbm_states_t.WBM_RMW_RD_WAIT:
                    # Read stage for RMW operation
                    if ack_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_RMW_RD_WAIT
                    else:
                        # check for ACK or ERR
                        if not self.wbmsig.err_i:
                            # No error: ACK
                            self.wbm_state.next = self.wbm_states_t.WBM_RMW_MID_WAIT
                        else:
                            # Error in cycle
                            self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                elif self.wbm_state == self.wbm_states_t.WBM_RMW_MID_WAIT:
                    # middle stage for RMW operation
                    self.wbm_state.next = self.wbm_states_t.WBM_RMW_WR_WAIT
                elif self.wbm_state == self.wbm_states_t.WBM_RMW_WR_WAIT:
                    # Write state for RMW operation
                    if ack_vector == 0:
                        self.wbm_state.next = self.wbm_states_t.WBM_RMW_WR_WAIT
                    else:
                        self.wbm_state.next = self.wbm_states_t.WBM_IDLE
                else:
                    self.wbm_state.next = self.wbm_states_t.WBM_IDLE

        @always_comb
        def wbmstate_signals():
            # CYC generation
            if self.wbm_state == self.wbm_states_t.WBM_IDLE:
                self.wbmsig.cyc_o.next = False
            elif self.wbm_state == self.wbm_states_t.WBM_INCYCLE and not self.flagread:
                self.wbmsig.cyc_o.next = False
            else:
                self.wbmsig.cyc_o.next = True
            # STB generation
            if self.wbm_state == self.wbm_states_t.WBM_IDLE or self.wbm_state == self.wbm_states_t.WBM_RMW_MID_WAIT:
                self.wbmsig.stb_o.next = False
            elif self.wbm_state == self.wbm_states_t.WBM_INCYCLE and not self.flagread:
                self.wbmsig.stb_o.next = False
            else:
                self.wbmsig.stb_o.next = True
            # WE generation
            if self.wbm_state == self.wbm_states_t.WBM_WRITE_WAIT or self.wbm_state == self.wbm_states_t.WBM_RMW_WR_WAIT:
                self.wbmsig.we_o.next = True
            else:
                self.wbmsig.we_o.next = False

        return instances() class Opcodes:
    """
    List all the RV32I opcodes
    """
    RV32_NOP    = 0b0010011
    RV32_LUI    = 0b0110111
    RV32_AUIPC  = 0b0010111
    RV32_JAL    = 0b1101111
    RV32_JALR   = 0b1100111
    RV32_BRANCH = 0b1100011
    RV32_LOAD   = 0b0000011
    RV32_STORE  = 0b0100011
    RV32_IMM    = 0b0010011
    RV32_OP     = 0b0110011
    RV32_FENCE  = 0b0001111
    RV32_SYSTEM = 0b1110011 class BranchFunct3:
    RV32_F3_BEQ  = 0
    RV32_F3_BNE  = 1
    RV32_F3_BLT  = 4
    RV32_F3_BGE  = 5
    RV32_F3_BLTU = 6
    RV32_F3_BGEU = 7 class LoadFunct3:
    RV32_F3_LB  = 0
    RV32_F3_LH  = 1
    RV32_F3_LW  = 2
    RV32_F3_LBU = 4
    RV32_F3_LHU = 5 class StoreFunct3:
    RV32_F3_SB = 0
    RV32_F3_SH = 1
    RV32_F3_SW = 2 class ArithmeticFunct3:
    RV32_F3_ADD_SUB = 0
    RV32_F3_SLL     = 1
    RV32_F3_SLT     = 2
    RV32_F3_SLTU    = 3
    RV32_F3_XOR     = 4
    RV32_F3_SRL_SRA = 5
    RV32_F3_OR      = 6
    RV32_F3_AND     = 7 class FenceFunct3:
    RV32_F3_FENCE   = 0
    RV32_F3_FENCE_I = 1 class SystemFunct3:
    RV32_F3_PRIV   = 0
    RV32_F3_CSRRW  = 1
    RV32_F3_CSRRS  = 2
    RV32_F3_CSRRC  = 3
    RV32_F3_CSRRWI = 5
    RV32_F3_CSRRSI = 6
    RV32_F3_CSRRCI = 7 class PrivFunct12:
    RV32_F12_ECALL  = 0b000000000000
    RV32_F12_EBREAK = 0b000000000001
    RV32_F12_ERET   = 0b000100000000 class MulDivFunct:
    RV32_F7_MUL_DIV = 0b0000001
    RV32_F3_MUL     = 0
    RV32_F3_MULH    = 1
    RV32_F3_MULHSU  = 2
    RV32_F3_MULHU   = 3
    RV32_F3_DIV     = 4
    RV32_F3_DIVU    = 5
    RV32_F3_REM     = 6
    RV32_F3_REMU    = 7
from myhdl import Signal
from myhdl import always
from myhdl import always_comb
from myhdl import modbv
from myhdl import instances
from myhdl import concat
from Core.consts import Consts
from Core.alu import ALUOp
from Core.csr import CSRCMD
from Core.csr import CSRExceptionCode
from Core.csr import CSRModes
from Core.wishbone import WishboneMaster
from Core.wishbone import WishboneMasterGenerator
from Core.instructions import Opcodes
from Core.instructions import BranchFunct3
from Core.instructions import LoadFunct3
from Core.instructions import StoreFunct3
from Core.instructions import ArithmeticFunct3
from Core.instructions import FenceFunct3
from Core.instructions import SystemFunct3
from Core.instructions import PrivFunct12
from Core.instructions import MulDivFunct
        self.imem_pipeline      = MemDpathIO()
        self.dmem_pipeline      = MemDpathIO()


class MemDpathIO:
    """
    Interface for memory accesses from dpath

    :ivar addr:  Memory address
    :ivar wdata: Write data
    :ivar typ:   Data ype: byte, half-word, word
    :ivar fcn:   Access type: read or write
    :ivar valid: The request is valid
    :ivar rdata: Read data
    """
    def __init__(self):
        self.addr  = Signal(modbv(0)[32:])
        self.wdata = Signal(modbv(0)[32:])
        self.typ   = Signal(modbv(0)[3:])
        self.fcn   = Signal(False)
        self.valid = Signal(False)
        self.rdata = Signal(modbv(0)[32:])


def Ctrlpath(clk,
             rst,
             io,
             icache_flush,
             dcache_flush,
             imem,
             dmem):
    """
    The decoder, exception, hazard detection, and control unit.

    :param clk:          Main clock
    :param rst:          Main reset
    :param io:           Interface with dapath
    :param icache_flush: Flush the I$
    :param dcache_flush: Flush the D$
    :param imem:         Wishbone master (instruction port)
    :param dmem:         Wishbone master (data port)
    """
    imem_m = WishboneMaster(imem)
    dmem_m = WishboneMaster(dmem)

    id_br_type            = Signal(modbv(0)[Consts.SZ_BR:])
    id_eq                 = Signal(False)
    id_lt                 = Signal(False)
    id_ltu                = Signal(False)
    id_fence_i            = Signal(False)
    # id_fence              = Signal(False)

    if_imem_misalign      = Signal(False)
    if_imem_fault         = Signal(False)
    id_breakpoint         = Signal(False)
    id_eret               = Signal(False)
    id_ebreak             = Signal(False)
    id_ecall              = Signal(False)
    id_illegal_inst       = Signal(False)
    mem_ld_misalign       = Signal(False)
    mem_ld_fault          = Signal(False)
    mem_st_misalign       = Signal(False)
    mem_st_fault          = Signal(False)

    id_imem_misalign      = Signal(False)
    id_imem_fault         = Signal(False)
    ex_breakpoint         = Signal(False)
    ex_eret               = Signal(False)
    ex_ecall              = Signal(False)
    ex_exception          = Signal(False)
    ex_exception_code     = Signal(modbv(0)[CSRExceptionCode.SZ_ECODE:])
    ex_mem_funct          = Signal(modbv(0)[Consts.SZ_M:])
    ex_mem_valid          = Signal(False)
    ex_csr_cmd            = Signal(modbv(0)[CSRCMD.SZ_CMD:])
    mem_breakpoint        = Signal(False)
    mem_eret              = Signal(False)
    mem_ecall             = Signal(False)
    mem_ecall_u           = Signal(False)
    mem_ecall_s           = Signal(False)
    mem_ecall_h           = Signal(False)
    mem_ecall_m           = Signal(False)
    mem_exception_ex      = Signal(False)
    mem_exception_code_ex = Signal(modbv(0)[CSRExceptionCode.SZ_ECODE:])
    mem_exception         = Signal(False)
    mem_exception_code    = Signal(modbv(0)[CSRExceptionCode.SZ_ECODE:])
    mem_mem_funct         = Signal(modbv(0)[Consts.SZ_M:])
    wb_mem_funct          = Signal(modbv(0)[Consts.SZ_M:])
    control               = Signal(modbv(0)[33:])

    if_misalign           = Signal(False)
    mem_misalign          = Signal(False)

    instruction_r         = Signal(modbv(0)[32:])
    cyc_ended             = Signal(False)

    opcode                = Signal(modbv(0)[7:])
    funct3                = Signal(modbv(0)[3:])
    funct7                = Signal(modbv(0)[7:])

    @always_comb
    def _ctrl_assignment():
        opcode.next = io.id_instruction[7:0]
        funct3.next = io.id_instruction[15:12]
        funct7.next = io.id_instruction[32:25]

    @always_comb
    def _ctrl_signal_assignment():
        if opcode == Opcodes.RV32_LUI:
            control.next = CtrlSignals.LUI
        elif opcode == Opcodes.RV32_AUIPC:
            control.next = CtrlSignals.AUIPC
        elif opcode == Opcodes.RV32_JAL:
            control.next = CtrlSignals.JAL
        elif opcode == Opcodes.RV32_JALR:
            control.next = CtrlSignals.JALR
        elif opcode == Opcodes.RV32_BRANCH:
            if funct3 == BranchFunct3.RV32_F3_BEQ:
                control.next = CtrlSignals.BEQ
            elif funct3 == BranchFunct3.RV32_F3_BNE:
                control.next = CtrlSignals.BNE

            elif funct3 == BranchFunct3.RV32_F3_BLT: