`default_nettype none
module serv_csr
  (
   input wire 	    i_clk,
   input wire 	    i_en,
   input wire 	    i_cnt0to3,
   input wire 	    i_cnt2,
   input wire 	    i_cnt3,
   input wire 	    i_cnt7,
   input wire 	    i_cnt_done,
   input wire 	    i_e_op,
   input wire 	    i_ebreak,
   input wire 	    i_mem_cmd,
   input wire 	    i_mem_misalign,
   //From mpram
   input wire 	    i_rf_csr_out,
   //to mpram
   output wire 	    o_csr_in,
   //Stuff
   input wire 	    i_mtip,
   output wire 	    o_new_irq,
   input wire 	    i_pending_irq,
   input wire 	    i_trap_taken,
   input wire 	    i_mstatus_en,
   input wire 	    i_mie_en,
   input wire 	    i_mcause_en,
   input wire [1:0] i_csr_source,
   input wire 	    i_mret,
   input wire 	    i_d,
   output wire 	    o_q);

`include "serv_params.vh"

   reg 		    mstatus;
   reg 		    mstatus_mie;
   reg 		    mstatus_mpie;
   reg 		    mie_mtie;

   reg 		mcause31;
   reg [3:0] 	mcause3_0;
   wire 	mcause;

   wire 	csr_in;
   wire 	csr_out;

   reg 		timer_irq_r;

   assign csr_in = (i_csr_source == CSR_SOURCE_EXT) ? i_d :
		   (i_csr_source == CSR_SOURCE_SET) ? csr_out | i_d :
		   (i_csr_source == CSR_SOURCE_CLR) ? csr_out & ~i_d :
		   (i_csr_source == CSR_SOURCE_CSR) ? csr_out :
		   1'bx;

   assign csr_out = (i_mstatus_en & i_en & mstatus) |
		    i_rf_csr_out |
		    (i_mcause_en & i_en & mcause);

   assign o_q = csr_out;

   wire 	timer_irq = i_mtip & mstatus_mie & mie_mtie;

   assign mcause = i_cnt0to3 ? mcause3_0[0] : //[3:0]
		   i_cnt_done ? mcause31 //[31]
		   : 1'b0;

   assign o_csr_in = csr_in;

   assign o_new_irq = !timer_irq_r & timer_irq;


   always @(posedge i_clk) begin
      /*
       Note: To save resources mstatus_mpie (mstatus bit 7) is not
       readable or writable from sw
       */
      if (i_mstatus_en & i_cnt3)
	mstatus_mie <= csr_in;

      if (i_mie_en & i_cnt7)
	mie_mtie <= csr_in;

      mstatus <= i_cnt2 & mstatus_mie;

      timer_irq_r <= timer_irq;

      if (i_mret) begin
	 mstatus_mie <= mstatus_mpie;
      end

      if (i_trap_taken) begin
	 mstatus_mpie <= mstatus_mie;
	 mstatus_mie <= 1'b0;
	 mcause31  <= i_pending_irq;
	 mcause3_0 <= i_pending_irq ? 4'd7 :
		      i_e_op ? {!i_ebreak, 3'b011} :
		      i_mem_misalign ? {2'b01, i_mem_cmd, 1'b0} :
		      4'd0;
      end

      if (i_mcause_en & i_en) begin
	 if (i_cnt0to3)
	   mcause3_0 <= {csr_in, mcause3_0[3:1]};
	 if (i_cnt_done)
	   mcause31 <= csr_in;
      end
   end

endmodule
