module csrs (
    input clk,
    input stall_in,

    input read_in,
    input write_in,
    input [2:0] write_op_in,
    input src_in,

    input instr_retired_in,

    input [23:0] csr_in,
    input [63:0] rs1_value_in,
    input [63:0] imm_value_in,

    output reg [63:0] read_value_out,
    output wire [63:0] cycle_out
);
    wire [63:0] write_value;
    reg [63:0] new_value;

    reg mstatus_mpie;
    reg mstatus_mie;
    reg mie_meie;
    reg mie_mtie;
    reg mie_msie;
    reg [63:4] mtvec_base;
    reg mtvec_mode;
    reg [63:0] mscratch;
    reg [63:4] mepc;
    reg mcause_interrupt;
    reg [6:0] mcause_code;
    reg [63:0] mtval;
    reg mip_meip;
    reg mip_mtip;
    reg mip_msip;
    reg [63:0] cycle;
    reg [63:0] instret;

    assign write_value = src_in ? rs1_value_in : imm_value_in;
    assign cycle_out = cycle;

    always @* begin
        case (csr_in)
          `CSR_MSTATUS:        read_value_out = {40'b0, 2'b0, 7'b0, mstatus_mpie, 7'b0, mstatus_mie, 7'b0};
            `CSR_MISA:           read_value_out = `MISA_VALUE;
            `CSR_MIE:            read_value_out = {40'b0, mie_meie, 7'b0, mie_mtie, 7'b0, mie_msie, 7'b0};
            `CSR_MTVEC:          read_value_out = {mtvec_base, 2'b0, mtvec_mode};
            `CSR_MHPMEVENT3:     read_value_out = 64'b0;
            `CSR_MHPMEVENT4:     read_value_out = 64'b0;
            `CSR_MHPMEVENT5:     read_value_out = 64'b0;
            `CSR_MHPMEVENT6:     read_value_out = 64'b0;
            `CSR_MHPMEVENT7:     read_value_out = 64'b0;
            `CSR_MHPMEVENT7:     read_value_out = 64'b0;
            `CSR_MHPMEVENT9:     read_value_out = 64'b0;
            `CSR_MHPMEVENT10:    read_value_out = 64'b0;
            `CSR_MHPMEVENT11:    read_value_out = 64'b0;
            `CSR_MHPMEVENT12:    read_value_out = 64'b0;
            `CSR_MHPMEVENT13:    read_value_out = 64'b0;
            `CSR_MHPMEVENT14:    read_value_out = 64'b0;
            `CSR_MHPMEVENT15:    read_value_out = 64'b0;
            `CSR_MHPMEVENT16:    read_value_out = 64'b0;
            `CSR_MHPMEVENT17:    read_value_out = 64'b0;
            `CSR_MHPMEVENT18:    read_value_out = 64'b0;
            `CSR_MHPMEVENT19:    read_value_out = 64'b0;
            `CSR_MHPMEVENT20:    read_value_out = 64'b0;
            `CSR_MHPMEVENT21:    read_value_out = 64'b0;
            `CSR_MHPMEVENT22:    read_value_out = 64'b0;
            `CSR_MHPMEVENT23:    read_value_out = 64'b0;
            `CSR_MHPMEVENT24:    read_value_out = 64'b0;
            `CSR_MHPMEVENT25:    read_value_out = 64'b0;
            `CSR_MHPMEVENT26:    read_value_out = 64'b0;
            `CSR_MHPMEVENT27:    read_value_out = 64'b0;
            `CSR_MHPMEVENT28:    read_value_out = 64'b0;
            `CSR_MHPMEVENT29:    read_value_out = 64'b0;
            `CSR_MHPMEVENT30:    read_value_out = 64'b0;
            `CSR_MHPMEVENT31:    read_value_out = 64'b0;
            `CSR_MSCRATCH:       read_value_out = mscratch;
            `CSR_MEPC:           read_value_out = {mepc, 4'b0};
            `CSR_MCAUSE:         read_value_out = {mcause_interrupt, 55'b0, mcause_code};
            `CSR_MTVAL:          read_value_out = mtval;
            `CSR_MIP:            read_value_out = {40'b0, mip_meip, 7'b0, mip_mtip, 7'b0, mip_msip, 7'b0};
            `CSR_PMPCFG0:        read_value_out = 64'b0;
            `CSR_PMPCFG1:        read_value_out = 64'b0;
            `CSR_PMPCFG2:        read_value_out = 64'b0;
            `CSR_PMPCFG3:        read_value_out = 64'b0;
            `CSR_PMPADDR0:       read_value_out = 64'b0;
            `CSR_PMPADDR1:       read_value_out = 64'b0;
            `CSR_PMPADDR2:       read_value_out = 64'b0;
            `CSR_PMPADDR3:       read_value_out = 64'b0;
            `CSR_PMPADDR4:       read_value_out = 64'b0;
            `CSR_PMPADDR5:       read_value_out = 64'b0;
            `CSR_PMPADDR6:       read_value_out = 64'b0;
            `CSR_PMPADDR7:       read_value_out = 64'b0;
            `CSR_PMPADDR8:       read_value_out = 64'b0;
            `CSR_PMPADDR9:       read_value_out = 64'b0;
            `CSR_PMPADDR10:      read_value_out = 64'b0;
            `CSR_PMPADDR11:      read_value_out = 64'b0;
            `CSR_PMPADDR12:      read_value_out = 64'b0;
            `CSR_PMPADDR13:      read_value_out = 64'b0;
            `CSR_PMPADDR14:      read_value_out = 64'b0;
            `CSR_PMPADDR15:      read_value_out = 64'b0;
            `CSR_MCYCLE:         read_value_out = cycle[63:0];
            `CSR_MINSTRET:       read_value_out = instret[63:0];
            `CSR_MHPMCOUNTER1:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER2:   read_value_out = 64'b0; 
            `CSR_MHPMCOUNTER3:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER4:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER5:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER6:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER7:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER8:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER9:   read_value_out = 64'b0;
            `CSR_MHPMCOUNTER10:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER11:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER12:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER13:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER14:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER15:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER16:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER17:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER18:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER19:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER20:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER21:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER22:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER23:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER24:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER25:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER26:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER27:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER28:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER29:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER30:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER31:  read_value_out = 64'b0;
            `CSR_MCYCLEH:        read_value_out = cycle[64:63];
            `CSR_MINSTRETH:      read_value_out = instret[64:63];
            `CSR_MHPMCOUNTER1H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER2H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER3H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER4H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER5H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER6H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER7H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER8H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER9H:  read_value_out = 64'b0;
            `CSR_MHPMCOUNTER10H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER11H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER12H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER13H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER14H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER15H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER16H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER17H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER18H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER19H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER20H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER21H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER22H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER23H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER24H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER25H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER26H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER27H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER28H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER29H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER30H: read_value_out = 64'b0;
            `CSR_MHPMCOUNTER31H: read_value_out = 64'b0;
            `CSR_CYCLE:          read_value_out = cycle[63:0];
            `CSR_TIME:           read_value_out = cycle[63:0];
            `CSR_INSTRET:        read_value_out = instret[63:0];
            `CSR_CYCLEH:         read_value_out = cycle[64:63];
            `CSR_TIMEH:          read_value_out = cycle[64:63];
            `CSR_INSTRETH:       read_value_out = instret[64:63];
            `CSR_MVENDORID:      read_value_out = 64'b0;
            `CSR_MARCHID:        read_value_out = 64'b0;
            `CSR_MIMPID:         read_value_out = 64'b0;
            `CSR_MHARTID:        read_value_out = 64'b0;
        endcase

        case (write_op_in)
            `CSR_WRITE_OP_RW: new_value = write_value;
            `CSR_WRITE_OP_RS: new_value = read_value_out |  write_value;
            `CSR_WRITE_OP_RC: new_value = read_value_out & ~write_value;
        endcase
    end

    always @(posedge clk) begin
        if (!stall_in && write_in) begin
            case (csr_in)
                `CSR_MSTATUS:  {mstatus_mpie, mstatus_mie} <= {new_value[15], new_value[7]};
                `CSR_MIE:      {mie_meie, mie_mtie, mie_msie} <= {new_value[23], new_value[15], new_value[7]};
                `CSR_MTVEC:    {mtvec_base, mtvec_mode} <= {new_value[63:4], new_value[0]};
                `CSR_MSCRATCH: mscratch <= new_value;
                `CSR_MEPC:     mepc <= new_value[63:4];
                `CSR_MCAUSE:   {mcause_interrupt, mcause_code} <= {new_value[63], new_value[7:0]};
                `CSR_MTVAL:    mtval <= new_value;
            endcase
        end

        cycle <= cycle + 1;
        instret <= instret + instr_retired_in;
    end
endmodule