/*
 * serv_immdec.v : SERV module for decoding immediates from instruction words
 *
 * SPDX-FileCopyrightText: 2020 Olof Kindgren <olof@award-winning.me>
 * SPDX-License-Identifier: ISC
 */
`default_nettype none
module serv_immdec
  #(parameter SHARED_RFADDR_IMM_REGS = 1,
    parameter W = 1)
  (
   input wire 	     i_clk,
   //State
   input wire 	     i_cnt_en,
   input wire 	     i_cnt_done,
   //Control
   input wire [3:0]  i_immdec_en,
   input wire 	     i_csr_imm_en,
   input wire [3:0]  i_ctrl,
   output wire [4:0] o_rd_addr,
   output wire [4:0] o_rs1_addr,
   output wire [4:0] o_rs2_addr,
   //Data
   output wire [W-1:0] o_csr_imm,
   output wire [W-1:0] o_imm,
   //External
   input wire 	     i_wb_en,
   input wire [31:7] i_wb_rdt);

generate
   if (W == 1) begin : gen_immdec_w_eq_1
   reg 		     imm31;

   reg [8:0]  imm19_12_20;
   reg 	      imm7;
   reg [5:0]  imm30_25;
   reg [4:0]  imm24_20;
   reg [4:0]  imm11_7;

   assign o_csr_imm = imm19_12_20[4];

   wire       signbit = imm31 & !i_csr_imm_en;

      if (SHARED_RFADDR_IMM_REGS) begin : gen_shared_imm_regs
	 assign o_rs1_addr = imm19_12_20[8:4];
	 assign o_rs2_addr = imm24_20;
	 assign o_rd_addr  = imm11_7;

	 always @(posedge i_clk) begin
	    if (i_wb_en) begin
	       /* CSR immediates are always zero-extended, hence clear the signbit */
	       imm31     <= i_wb_rdt[31];
	    end
	    if (i_wb_en | (i_cnt_en & i_immdec_en[1]))
	      imm19_12_20 <= i_wb_en ? {i_wb_rdt[19:12],i_wb_rdt[20]} : {i_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]};
	    if (i_wb_en | (i_cnt_en))
	      imm7        <= i_wb_en ? i_wb_rdt[7]                    : signbit;

	    if (i_wb_en | (i_cnt_en & i_immdec_en[3]))
	      imm30_25    <= i_wb_en ? i_wb_rdt[30:25] : {i_ctrl[2] ? imm7 : i_ctrl[1] ? signbit : imm19_12_20[0], imm30_25[5:1]};

	    if (i_wb_en | (i_cnt_en & i_immdec_en[2]))
	      imm24_20    <= i_wb_en ? i_wb_rdt[24:20] : {imm30_25[0], imm24_20[4:1]};

	    if (i_wb_en | (i_cnt_en & i_immdec_en[0]))
	      imm11_7     <= i_wb_en ? i_wb_rdt[11:7] : {imm30_25[0], imm11_7[4:1]};
	 end
      end else begin : gen_separate_imm_regs
	 reg [4:0]  rd_addr;
	 reg [4:0]  rs1_addr;
	 reg [4:0]  rs2_addr;

	 assign o_rd_addr  = rd_addr;
	 assign o_rs1_addr = rs1_addr;
	 assign o_rs2_addr = rs2_addr;
	 always @(posedge i_clk) begin
	    if (i_wb_en) begin
	       /* CSR immediates are always zero-extended, hence clear the signbit */
	       imm31       <= i_wb_rdt[31];
	       imm19_12_20 <= {i_wb_rdt[19:12],i_wb_rdt[20]};
	       imm7        <= i_wb_rdt[7];
	       imm30_25    <= i_wb_rdt[30:25];
	       imm24_20    <= i_wb_rdt[24:20];
	       imm11_7     <= i_wb_rdt[11:7];

               rd_addr  <= i_wb_rdt[11:7];
               rs1_addr <= i_wb_rdt[19:15];
               rs2_addr <= i_wb_rdt[24:20];
	    end
	    if (i_cnt_en) begin
	       imm19_12_20 <= {i_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]};
	       imm7        <= signbit;
	       imm30_25    <= {i_ctrl[2] ? imm7 : i_ctrl[1] ? signbit : imm19_12_20[0], imm30_25[5:1]};
	       imm24_20    <= {imm30_25[0], imm24_20[4:1]};
	       imm11_7     <= {imm30_25[0], imm11_7[4:1]};
	    end
	 end
      end

	 assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0];
   end else begin : gen_immdec_w_eq_4
   reg [4:0]	     rd_addr;
   reg [4:0]	     rs1_addr;
   reg [4:0]	     rs2_addr;

   reg		     i31;
   reg		     i30;
   reg		     i29;
   reg		     i28;
   reg		     i27;
   reg		     i26;
   reg		     i25;
   reg		     i24;
   reg		     i23;
   reg		     i22;
   reg		     i21;
   reg		     i20;
   reg		     i19;
   reg		     i18;
   reg		     i17;
   reg		     i16;
   reg		     i15;
   reg		     i14;
   reg		     i13;
   reg		     i12;
   reg		     i11;
   reg		     i10;
   reg		     i9;
   reg		     i8;
   reg		     i7;

   reg		     i7_2;
   reg		     i20_2;

   wire		     signbit = i31 & !i_csr_imm_en;

   assign o_csr_imm[3] = i18;
   assign o_csr_imm[2] = i17;
   assign o_csr_imm[1] = i16;
   assign o_csr_imm[0] = i15;

   assign o_rd_addr  = rd_addr;
   assign o_rs1_addr = rs1_addr;
   assign o_rs2_addr = rs2_addr;
   always @(posedge i_clk) begin
      if (i_wb_en) begin
	 //Common
	 i31 <= i_wb_rdt[31];

	 //Bit lane 3
	 i19 <= i_wb_rdt[19];
	 i15 <= i_wb_rdt[15];
	 i20 <= i_wb_rdt[20];
	 i7  <= i_wb_rdt[7];
	 i27 <= i_wb_rdt[27];
	 i23 <= i_wb_rdt[23];
	 i10 <= i_wb_rdt[10];

	 //Bit lane 2
	 i22 <= i_wb_rdt[22];
	 i9  <= i_wb_rdt[ 9];
	 i26 <= i_wb_rdt[26];
	 i30 <= i_wb_rdt[30];
	 i14 <= i_wb_rdt[14];
	 i18 <= i_wb_rdt[18];

	 //Bit lane 1
	 i21 <= i_wb_rdt[21];
	 i8  <= i_wb_rdt[ 8];
	 i25 <= i_wb_rdt[25];
	 i29 <= i_wb_rdt[29];
	 i13 <= i_wb_rdt[13];
	 i17 <= i_wb_rdt[17];

	 //Bit lane 0
	 i11 <= i_wb_rdt[11];
	 i7_2  <= i_wb_rdt[7 ];
	 i20_2   <= i_wb_rdt[20];
	 i24   <= i_wb_rdt[24];
	 i28   <= i_wb_rdt[28];
	 i12   <= i_wb_rdt[12];
	 i16   <= i_wb_rdt[16];

         rd_addr  <= i_wb_rdt[11:7];
         rs1_addr <= i_wb_rdt[19:15];
         rs2_addr <= i_wb_rdt[24:20];
      end
      if (i_cnt_en) begin
	 //Bit lane 3
	 i10 <= i27;
	 i23 <= i27;
	 i27 <= i_ctrl[2] ? i7 : i_ctrl[1] ? signbit : i20;
	 i7  <= signbit;
	 i20 <= i15;
	 i15 <= i19;
	 i19 <= i_ctrl[3] ? signbit : i23;

	 //Bit lane 2
	 i22 <= i26;
	 i9  <= i26;
	 i26 <= i30;
	 i30 <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i14;
	 i14 <= i18;
	 i18 <= i_ctrl[3] ? signbit : i22;

	 //Bit lane 1
	 i21 <= i25;
	 i8  <= i25;
	 i25 <= i29;
	 i29 <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i13;
	 i13 <= i17;
	 i17 <= i_ctrl[3] ? signbit : i21;

	 //Bit lane 0
	 i7_2  <= i11;
	 i11   <= i28;
	 i20_2   <= i24;
	 i24   <= i28;
	 i28   <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i12;
	 i12   <= i16;
	 i16   <= i_ctrl[3] ? signbit : i20_2;

      end
   end

   assign o_imm[3] = (i_cnt_done ? signbit : (i_ctrl[0] ? i10 : i23));
   assign o_imm[2] = i_ctrl[0] ? i9 : i22;
   assign o_imm[1] = i_ctrl[0] ? i8 : i21;
   assign o_imm[0] = i_ctrl[0] ? i7_2 : i20_2;

   end
endgenerate

endmodule
