Add zero-riscy files
diff --git a/verilog/rtl/ips/zero-riscy b/verilog/rtl/ips/zero-riscy
deleted file mode 160000
index ab3ec36..0000000
--- a/verilog/rtl/ips/zero-riscy
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ab3ec361be6f8bd7abad1accb3700659890f7ad3
diff --git a/verilog/rtl/ips/zero-riscy/include/zeroriscy_config.sv b/verilog/rtl/ips/zero-riscy/include/zeroriscy_config.sv
new file mode 100644
index 0000000..6442a76
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/include/zeroriscy_config.sv
@@ -0,0 +1,34 @@
+// Copyright 2017 ETH Zurich and University of Bologna.
+// Copyright and related rights are licensed under the Solderpad Hardware
+// License, Version 0.51 (the “License”); you may not use this file except in
+// compliance with the License.  You may obtain a copy of the License at
+// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
+// or agreed to in writing, software, hardware and materials distributed under
+// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// Engineer:       Michael Gautschi - gautschi@iis.ee.ethz.ch                 //
+//                                                                            //
+// Additional contributions by:                                               //
+//                 Markus Wegmann - markus.wegmann@technokrat.ch              //
+//                 Davide Schiavone - pschiavo@iis.ee.ethz.ch                 //
+//                                                                            //
+// Design Name:    RISC-V config file                                         //
+// Project Name:   zero-riscy                                                 //
+// Language:       SystemVerilog                                              //
+//                                                                            //
+// Description:    Configure optional simulation modules                      //
+//                                                                            //
+////////////////////////////////////////////////////////////////////////////////
+
+// no traces for synthesis, they are not synthesizable
+`ifndef SYNTHESIS
+`ifndef PULP_FPGA_EMUL
+`define TRACE_EXECUTION
+`endif
+//`define SIMCHECKER
+`endif
+
+//`define CHECK_MISALIGNED
\ No newline at end of file
diff --git a/verilog/rtl/ips/zero-riscy/include/zeroriscy_defines.sv b/verilog/rtl/ips/zero-riscy/include/zeroriscy_defines.sv
new file mode 100644
index 0000000..4fdc806
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/include/zeroriscy_defines.sv
@@ -0,0 +1,316 @@
+// Copyright 2017 ETH Zurich and University of Bologna.
+// Copyright and related rights are licensed under the Solderpad Hardware
+// License, Version 0.51 (the “License”); you may not use this file except in
+// compliance with the License.  You may obtain a copy of the License at
+// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
+// or agreed to in writing, software, hardware and materials distributed under
+// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// Engineer:       Matthias Baer - baermatt@student.ethz.ch                   //
+//                                                                            //
+// Additional contributions by:                                               //
+//                 Sven Stucki - svstucki@student.ethz.ch                     //
+//                                                                            //
+//                                                                            //
+// Design Name:    RISC-V processor core                                      //
+// Project Name:   zero-riscy                                                 //
+// Language:       SystemVerilog                                              //
+//                                                                            //
+// Description:    Defines for various constants used by the processor core.  //
+//                                                                            //
+////////////////////////////////////////////////////////////////////////////////
+
+package zeroriscy_defines;
+
+////////////////////////////////////////////////
+//    ___         ____          _             //
+//   / _ \ _ __  / ___|___   __| | ___  ___   //
+//  | | | | '_ \| |   / _ \ / _` |/ _ \/ __|  //
+//  | |_| | |_) | |__| (_) | (_| |  __/\__ \  //
+//   \___/| .__/ \____\___/ \__,_|\___||___/  //
+//        |_|                                 //
+////////////////////////////////////////////////
+
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+
+endpackage
diff --git a/verilog/rtl/ips/zero-riscy/include/zeroriscy_tracer_defines.sv b/verilog/rtl/ips/zero-riscy/include/zeroriscy_tracer_defines.sv
new file mode 100644
index 0000000..478f6ab
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/include/zeroriscy_tracer_defines.sv
@@ -0,0 +1,74 @@
+// Copyright 2017 ETH Zurich and University of Bologna.
+// Copyright and related rights are licensed under the Solderpad Hardware
+// License, Version 0.51 (the “License”); you may not use this file except in
+// compliance with the License.  You may obtain a copy of the License at
+// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
+// or agreed to in writing, software, hardware and materials distributed under
+// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+package zeroriscy_tracer_defines;
+import zeroriscy_defines::*;
+
+// instruction masks (for tracer)
+// parameter INSTR_CUSTOM0   = { 25'b?, OPCODE_CUST0 };
+// parameter INSTR_CUSTOM1   = { 25'b?, OPCODE_CUST1 };
+parameter INSTR_LUI       = { 25'b?, OPCODE_LUI };
+parameter INSTR_AUIPC     = { 25'b?, OPCODE_AUIPC };
+parameter INSTR_JAL       = { 25'b?, OPCODE_JAL };
+parameter INSTR_JALR      = { 17'b?, 3'b000, 5'b?, OPCODE_JALR };
+// BRANCH
+parameter INSTR_BEQ      =  { 17'b?, 3'b000, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BNE      =  { 17'b?, 3'b001, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BLT      =  { 17'b?, 3'b100, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BGE      =  { 17'b?, 3'b101, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BLTU     =  { 17'b?, 3'b110, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BGEU     =  { 17'b?, 3'b111, 5'b?, OPCODE_BRANCH };
+parameter INSTR_BALL     =  { 17'b?, 3'b010, 5'b?, OPCODE_BRANCH };
+// OPIMM
+parameter INSTR_ADDI     =  { 17'b?, 3'b000, 5'b?, OPCODE_OPIMM };
+parameter INSTR_SLTI     =  { 17'b?, 3'b010, 5'b?, OPCODE_OPIMM };
+parameter INSTR_SLTIU    =  { 17'b?, 3'b011, 5'b?, OPCODE_OPIMM };
+parameter INSTR_XORI     =  { 17'b?, 3'b100, 5'b?, OPCODE_OPIMM };
+parameter INSTR_ORI      =  { 17'b?, 3'b110, 5'b?, OPCODE_OPIMM };
+parameter INSTR_ANDI     =  { 17'b?, 3'b111, 5'b?, OPCODE_OPIMM };
+parameter INSTR_SLLI     =  { 7'b0000000, 10'b?, 3'b001, 5'b?, OPCODE_OPIMM };
+parameter INSTR_SRLI     =  { 7'b0000000, 10'b?, 3'b101, 5'b?, OPCODE_OPIMM };
+parameter INSTR_SRAI     =  { 7'b0100000, 10'b?, 3'b101, 5'b?, OPCODE_OPIMM };
+// OP
+parameter INSTR_ADD      =  { 7'b0000000, 10'b?, 3'b000, 5'b?, OPCODE_OP };
+parameter INSTR_SUB      =  { 7'b0100000, 10'b?, 3'b000, 5'b?, OPCODE_OP };
+parameter INSTR_SLL      =  { 7'b0000000, 10'b?, 3'b001, 5'b?, OPCODE_OP };
+parameter INSTR_SLT      =  { 7'b0000000, 10'b?, 3'b010, 5'b?, OPCODE_OP };
+parameter INSTR_SLTU     =  { 7'b0000000, 10'b?, 3'b011, 5'b?, OPCODE_OP };
+parameter INSTR_XOR      =  { 7'b0000000, 10'b?, 3'b100, 5'b?, OPCODE_OP };
+parameter INSTR_SRL      =  { 7'b0000000, 10'b?, 3'b101, 5'b?, OPCODE_OP };
+parameter INSTR_SRA      =  { 7'b0100000, 10'b?, 3'b101, 5'b?, OPCODE_OP };
+parameter INSTR_OR       =  { 7'b0000000, 10'b?, 3'b110, 5'b?, OPCODE_OP };
+parameter INSTR_AND      =  { 7'b0000000, 10'b?, 3'b111, 5'b?, OPCODE_OP };
+
+// SYSTEM
+parameter INSTR_CSRRW    =  { 17'b?, 3'b001, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_CSRRS    =  { 17'b?, 3'b010, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_CSRRC    =  { 17'b?, 3'b011, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_CSRRWI   =  { 17'b?, 3'b101, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_CSRRSI   =  { 17'b?, 3'b110, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_CSRRCI   =  { 17'b?, 3'b111, 5'b?, OPCODE_SYSTEM };
+parameter INSTR_ECALL    =  { 12'b000000000000, 13'b0, OPCODE_SYSTEM };
+parameter INSTR_EBREAK   =  { 12'b000000000001, 13'b0, OPCODE_SYSTEM };
+parameter INSTR_MRET     =  { 12'b001100000010, 13'b0, OPCODE_SYSTEM };
+parameter INSTR_WFI      =  { 12'b000100000101, 13'b0, OPCODE_SYSTEM };
+
+// RV32M
+parameter INSTR_DIV      =  { 7'b0000001, 10'b?, 3'b100, 5'b?, OPCODE_OP };
+parameter INSTR_DIVU     =  { 7'b0000001, 10'b?, 3'b101, 5'b?, OPCODE_OP };
+parameter INSTR_REM      =  { 7'b0000001, 10'b?, 3'b110, 5'b?, OPCODE_OP };
+parameter INSTR_REMU     =  { 7'b0000001, 10'b?, 3'b111, 5'b?, OPCODE_OP };
+parameter INSTR_PMUL     =  { 7'b0000001, 10'b?, 3'b000, 5'b?, OPCODE_OP };
+parameter INSTR_PMUH     =  { 7'b0000001, 10'b?, 3'b001, 5'b?, OPCODE_OP };
+parameter INSTR_PMULHSU  =  { 7'b0000001, 10'b?, 3'b010, 5'b?, OPCODE_OP };
+parameter INSTR_PMULHU   =  { 7'b0000001, 10'b?, 3'b011, 5'b?, OPCODE_OP };
+
+
+endpackage
\ No newline at end of file
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_alu.v b/verilog/rtl/ips/zero-riscy/zeroriscy_alu.v
new file mode 100644
index 0000000..5c94c24
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_alu.v
@@ -0,0 +1,398 @@
+//`include "zeroriscy_defines.sv"
+
+module zeroriscy_alu (
+	operator_i,
+	operand_a_i,
+	operand_b_i,
+	multdiv_operand_a_i,
+	multdiv_operand_b_i,
+	multdiv_en_i,
+	adder_result_o,
+	adder_result_ext_o,
+	result_o,
+	comparison_result_o,
+	is_equal_result_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	input wire [ALU_OP_WIDTH - 1:0] operator_i;
+	input wire [31:0] operand_a_i;
+	input wire [31:0] operand_b_i;
+	input wire [32:0] multdiv_operand_a_i;
+	input wire [32:0] multdiv_operand_b_i;
+	input wire multdiv_en_i;
+	output wire [31:0] adder_result_o;
+	output wire [33:0] adder_result_ext_o;
+	output reg [31:0] result_o;
+	output wire comparison_result_o;
+	output wire is_equal_result_o;
+	wire [31:0] operand_a_rev;
+	wire [32:0] operand_b_neg;
+	genvar k;
+	generate
+		for (k = 0; k < 32; k = k + 1) begin : g_revloop
+			assign operand_a_rev[k] = operand_a_i[31 - k];
+		end
+	endgenerate
+	reg adder_op_b_negate;
+	wire [32:0] adder_in_a;
+	wire [32:0] adder_in_b;
+	wire [31:0] adder_result;
+	always @(*) begin
+		adder_op_b_negate = 1'b0;
+		case (operator_i)
+			ALU_SUB, ALU_EQ, ALU_NE, ALU_GTU, ALU_GEU, ALU_LTU, ALU_LEU, ALU_GTS, ALU_GES, ALU_LTS, ALU_LES, ALU_SLTS, ALU_SLTU, ALU_SLETS, ALU_SLETU: adder_op_b_negate = 1'b1;
+			default:
+				;
+		endcase
+	end
+	assign adder_in_a = (multdiv_en_i ? multdiv_operand_a_i : {operand_a_i, 1'b1});
+	assign operand_b_neg = {operand_b_i, 1'b0} ^ {33 {adder_op_b_negate}};
+	assign adder_in_b = (multdiv_en_i ? multdiv_operand_b_i : operand_b_neg);
+	assign adder_result_ext_o = $unsigned(adder_in_a) + $unsigned(adder_in_b);
+	assign adder_result = adder_result_ext_o[32:1];
+	assign adder_result_o = adder_result;
+	wire shift_left;
+	wire shift_arithmetic;
+	wire [31:0] shift_amt;
+	wire [31:0] shift_op_a;
+	wire [31:0] shift_result;
+	wire [31:0] shift_right_result;
+	wire [31:0] shift_left_result;
+	assign shift_amt = operand_b_i;
+	assign shift_left = operator_i == ALU_SLL;
+	assign shift_arithmetic = operator_i == ALU_SRA;
+	assign shift_op_a = (shift_left ? operand_a_rev : operand_a_i);
+	wire [32:0] shift_op_a_32;
+	assign shift_op_a_32 = {shift_arithmetic & shift_op_a[31], shift_op_a};
+	assign shift_right_result = $signed(shift_op_a_32) >>> shift_amt[4:0];
+	genvar j;
+	generate
+		for (j = 0; j < 32; j = j + 1) begin : g_resrevloop
+			assign shift_left_result[j] = shift_right_result[31 - j];
+		end
+	endgenerate
+	assign shift_result = (shift_left ? shift_left_result : shift_right_result);
+	wire is_equal;
+	reg is_greater_equal;
+	reg cmp_signed;
+	always @(*) begin
+		cmp_signed = 1'b0;
+		case (operator_i)
+			ALU_GTS, ALU_GES, ALU_LTS, ALU_LES, ALU_SLTS, ALU_SLETS: cmp_signed = 1'b1;
+			default:
+				;
+		endcase
+	end
+	assign is_equal = adder_result == 32'b00000000000000000000000000000000;
+	assign is_equal_result_o = is_equal;
+	always @(*)
+		if ((operand_a_i[31] ^ operand_b_i[31]) == 0)
+			is_greater_equal = adder_result[31] == 0;
+		else
+			is_greater_equal = operand_a_i[31] ^ cmp_signed;
+	reg cmp_result;
+	always @(*) begin
+		cmp_result = is_equal;
+		case (operator_i)
+			ALU_EQ: cmp_result = is_equal;
+			ALU_NE: cmp_result = ~is_equal;
+			ALU_GTS, ALU_GTU: cmp_result = is_greater_equal && ~is_equal;
+			ALU_GES, ALU_GEU: cmp_result = is_greater_equal;
+			ALU_LTS, ALU_SLTS, ALU_LTU, ALU_SLTU: cmp_result = ~is_greater_equal;
+			ALU_SLETS, ALU_SLETU, ALU_LES, ALU_LEU: cmp_result = ~is_greater_equal || is_equal;
+			default:
+				;
+		endcase
+	end
+	assign comparison_result_o = cmp_result;
+	always @(*) begin
+		result_o = 1'sb0;
+		case (operator_i)
+			ALU_AND: result_o = operand_a_i & operand_b_i;
+			ALU_OR: result_o = operand_a_i | operand_b_i;
+			ALU_XOR: result_o = operand_a_i ^ operand_b_i;
+			ALU_ADD, ALU_SUB: result_o = adder_result;
+			ALU_SLL, ALU_SRL, ALU_SRA: result_o = shift_result;
+			ALU_EQ, ALU_NE, ALU_GTU, ALU_GEU, ALU_LTU, ALU_LEU, ALU_GTS, ALU_GES, ALU_LTS, ALU_LES, ALU_SLTS, ALU_SLTU, ALU_SLETS, ALU_SLETU: result_o = cmp_result;
+			default:
+				;
+		endcase
+	end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_compressed_decoder.v b/verilog/rtl/ips/zero-riscy/zeroriscy_compressed_decoder.v
new file mode 100644
index 0000000..e41c611
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_compressed_decoder.v
@@ -0,0 +1,381 @@
+module zeroriscy_compressed_decoder (
+	instr_i,
+	instr_o,
+	is_compressed_o,
+	illegal_instr_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	input wire [31:0] instr_i;
+	output reg [31:0] instr_o;
+	output wire is_compressed_o;
+	output reg illegal_instr_o;
+	always @(*) begin
+		illegal_instr_o = 1'b0;
+		instr_o = 1'sb0;
+		case (instr_i[1:0])
+			2'b00:
+				case (instr_i[15:13])
+					3'b000: begin
+						instr_o = {2'b00, instr_i[10:7], instr_i[12:11], instr_i[5], instr_i[6], 2'b00, 5'h02, 3'b000, 2'b01, instr_i[4:2], OPCODE_OPIMM};
+						if (instr_i[12:5] == 8'b00000000)
+							illegal_instr_o = 1'b1;
+					end
+					3'b010: instr_o = {5'b00000, instr_i[5], instr_i[12:10], instr_i[6], 2'b00, 2'b01, instr_i[9:7], 3'b010, 2'b01, instr_i[4:2], OPCODE_LOAD};
+					3'b110: instr_o = {5'b00000, instr_i[5], instr_i[12], 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b010, instr_i[11:10], instr_i[6], 2'b00, OPCODE_STORE};
+					default: illegal_instr_o = 1'b1;
+				endcase
+			2'b01:
+				case (instr_i[15:13])
+					3'b000: instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], instr_i[11:7], 3'b000, instr_i[11:7], OPCODE_OPIMM};
+					3'b001, 3'b101: instr_o = {instr_i[12], instr_i[8], instr_i[10:9], instr_i[6], instr_i[7], instr_i[2], instr_i[11], instr_i[5:3], {9 {instr_i[12]}}, 4'b0000, ~instr_i[15], OPCODE_JAL};
+					3'b010: begin
+						instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 5'b00000, 3'b000, instr_i[11:7], OPCODE_OPIMM};
+						if (instr_i[11:7] == 5'b00000)
+							illegal_instr_o = 1'b1;
+					end
+					3'b011: begin
+						instr_o = {{15 {instr_i[12]}}, instr_i[6:2], instr_i[11:7], OPCODE_LUI};
+						if (instr_i[11:7] == 5'h02)
+							instr_o = {{3 {instr_i[12]}}, instr_i[4:3], instr_i[5], instr_i[2], instr_i[6], 4'b0000, 5'h02, 3'b000, 5'h02, OPCODE_OPIMM};
+						else if (instr_i[11:7] == 5'b00000)
+							illegal_instr_o = 1'b1;
+						if ({instr_i[12], instr_i[6:2]} == 6'b000000)
+							illegal_instr_o = 1'b1;
+					end
+					3'b100:
+						case (instr_i[11:10])
+							2'b00, 2'b01: begin
+								instr_o = {1'b0, instr_i[10], 5'b00000, instr_i[6:2], 2'b01, instr_i[9:7], 3'b101, 2'b01, instr_i[9:7], OPCODE_OPIMM};
+								if (instr_i[12] == 1'b1)
+									illegal_instr_o = 1'b1;
+								if (instr_i[6:2] == 5'b00000)
+									illegal_instr_o = 1'b1;
+							end
+							2'b10: instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 2'b01, instr_i[9:7], 3'b111, 2'b01, instr_i[9:7], OPCODE_OPIMM};
+							2'b11:
+								case ({instr_i[12], instr_i[6:5]})
+									3'b000: instr_o = {9'b010000001, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], OPCODE_OP};
+									3'b001: instr_o = {9'b000000001, instr_i[4:2], 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[9:7], OPCODE_OP};
+									3'b010: instr_o = {9'b000000001, instr_i[4:2], 2'b01, instr_i[9:7], 3'b110, 2'b01, instr_i[9:7], OPCODE_OP};
+									3'b011: instr_o = {9'b000000001, instr_i[4:2], 2'b01, instr_i[9:7], 3'b111, 2'b01, instr_i[9:7], OPCODE_OP};
+									3'b100, 3'b101, 3'b110, 3'b111: illegal_instr_o = 1'b1;
+								endcase
+						endcase
+					3'b110, 3'b111: instr_o = {{4 {instr_i[12]}}, instr_i[6:5], instr_i[2], 5'b00000, 2'b01, instr_i[9:7], 2'b00, instr_i[13], instr_i[11:10], instr_i[4:3], instr_i[12], OPCODE_BRANCH};
+					default: illegal_instr_o = 1'b1;
+				endcase
+			2'b10:
+				case (instr_i[15:13])
+					3'b000: begin
+						instr_o = {7'b0000000, instr_i[6:2], instr_i[11:7], 3'b001, instr_i[11:7], OPCODE_OPIMM};
+						if (instr_i[11:7] == 5'b00000)
+							illegal_instr_o = 1'b1;
+						if ((instr_i[12] == 1'b1) || (instr_i[6:2] == 5'b00000))
+							illegal_instr_o = 1'b1;
+					end
+					3'b010: begin
+						instr_o = {4'b0000, instr_i[3:2], instr_i[12], instr_i[6:4], 2'b00, 5'h02, 3'b010, instr_i[11:7], OPCODE_LOAD};
+						if (instr_i[11:7] == 5'b00000)
+							illegal_instr_o = 1'b1;
+					end
+					3'b100:
+						if (instr_i[12] == 1'b0) begin
+							instr_o = {7'b0000000, instr_i[6:2], 5'b00000, 3'b000, instr_i[11:7], OPCODE_OP};
+							if (instr_i[6:2] == 5'b00000)
+								instr_o = {12'b000000000000, instr_i[11:7], 3'b000, 5'b00000, OPCODE_JALR};
+						end
+						else begin
+							instr_o = {7'b0000000, instr_i[6:2], instr_i[11:7], 3'b000, instr_i[11:7], OPCODE_OP};
+							if (instr_i[11:7] == 5'b00000) begin
+								instr_o = 32'h00100073;
+								if (instr_i[6:2] != 5'b00000)
+									illegal_instr_o = 1'b1;
+							end
+							else if (instr_i[6:2] == 5'b00000)
+								instr_o = {12'b000000000000, instr_i[11:7], 3'b000, 5'b00001, OPCODE_JALR};
+						end
+					3'b110: instr_o = {4'b0000, instr_i[8:7], instr_i[12], instr_i[6:2], 5'h02, 3'b010, instr_i[11:9], 2'b00, OPCODE_STORE};
+					default: illegal_instr_o = 1'b1;
+				endcase
+			default: instr_o = instr_i;
+		endcase
+	end
+	assign is_compressed_o = instr_i[1:0] != 2'b11;
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_controller.v b/verilog/rtl/ips/zero-riscy/zeroriscy_controller.v
new file mode 100644
index 0000000..2e8ef88
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_controller.v
@@ -0,0 +1,621 @@
+module zeroriscy_controller 
+#(
+  parameter REG_ADDR_WIDTH      = 5
+)
+(
+	clk,
+	rst_n,
+	fetch_enable_i,
+	ctrl_busy_o,
+	first_fetch_o,
+	is_decoding_o,
+	deassert_we_o,
+	illegal_insn_i,
+	ecall_insn_i,
+	mret_insn_i,
+	pipe_flush_i,
+	ebrk_insn_i,
+	csr_status_i,
+	instr_valid_i,
+	instr_req_o,
+	pc_set_o,
+	pc_mux_o,
+	exc_pc_mux_o,
+	data_misaligned_i,
+	branch_in_id_i,
+	branch_taken_ex_i,
+	branch_set_i,
+	jump_set_i,
+	instr_multicyle_i,
+	irq_req_ctrl_i,
+	irq_id_ctrl_i,
+	m_IE_i,
+	irq_ack_o,
+	irq_id_o,
+	exc_cause_o,
+	exc_ack_o,
+	exc_kill_o,
+	csr_save_if_o,
+	csr_save_id_o,
+	csr_cause_o,
+	csr_restore_mret_id_o,
+	csr_save_cause_o,
+	dbg_req_i,
+	dbg_ack_o,
+	dbg_stall_i,
+	dbg_jump_req_i,
+	dbg_settings_i,
+	dbg_trap_o,
+	operand_a_fw_mux_sel_o,
+	halt_if_o,
+	halt_id_o,
+	id_ready_i,
+	perf_jump_o,
+	perf_tbranch_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	//parameter REG_ADDR_WIDTH = 5;
+	input wire clk;
+	input wire rst_n;
+	input wire fetch_enable_i;
+	output reg ctrl_busy_o;
+	output reg first_fetch_o;
+	output reg is_decoding_o;
+	output reg deassert_we_o;
+	input wire illegal_insn_i;
+	input wire ecall_insn_i;
+	input wire mret_insn_i;
+	input wire pipe_flush_i;
+	input wire ebrk_insn_i;
+	input wire csr_status_i;
+	input wire instr_valid_i;
+	output reg instr_req_o;
+	output reg pc_set_o;
+	output reg [2:0] pc_mux_o;
+	output reg [1:0] exc_pc_mux_o;
+	input wire data_misaligned_i;
+	input wire branch_in_id_i;
+	input wire branch_taken_ex_i;
+	input wire branch_set_i;
+	input wire jump_set_i;
+	input wire instr_multicyle_i;
+	input wire irq_req_ctrl_i;
+	input wire [4:0] irq_id_ctrl_i;
+	input wire m_IE_i;
+	output reg irq_ack_o;
+	output reg [4:0] irq_id_o;
+	output reg [5:0] exc_cause_o;
+	output reg exc_ack_o;
+	output reg exc_kill_o;
+	output reg csr_save_if_o;
+	output reg csr_save_id_o;
+	output reg [5:0] csr_cause_o;
+	output reg csr_restore_mret_id_o;
+	output reg csr_save_cause_o;
+	input wire dbg_req_i;
+	output reg dbg_ack_o;
+	input wire dbg_stall_i;
+	input wire dbg_jump_req_i;
+	input wire [DBG_SETS_W - 1:0] dbg_settings_i;
+	output reg dbg_trap_o;
+	output wire [1:0] operand_a_fw_mux_sel_o;
+	output reg halt_if_o;
+	output reg halt_id_o;
+	input wire id_ready_i;
+	output reg perf_jump_o;
+	output reg perf_tbranch_o;
+	reg [3:0] ctrl_fsm_cs;
+	reg [3:0] ctrl_fsm_ns;
+	reg irq_enable_int;
+	always @(*) begin
+		instr_req_o = 1'b1;
+		exc_ack_o = 1'b0;
+		exc_kill_o = 1'b0;
+		csr_save_if_o = 1'b0;
+		csr_save_id_o = 1'b0;
+		csr_restore_mret_id_o = 1'b0;
+		csr_save_cause_o = 1'b0;
+		exc_cause_o = 1'sb0;
+		exc_pc_mux_o = EXC_PC_IRQ;
+		csr_cause_o = 1'sb0;
+		pc_mux_o = PC_BOOT;
+		pc_set_o = 1'b0;
+		ctrl_fsm_ns = ctrl_fsm_cs;
+		ctrl_busy_o = 1'b1;
+		is_decoding_o = 1'b0;
+		first_fetch_o = 1'b0;
+		halt_if_o = 1'b0;
+		halt_id_o = 1'b0;
+		dbg_ack_o = 1'b0;
+		irq_ack_o = 1'b0;
+		irq_id_o = irq_id_ctrl_i;
+		irq_enable_int = m_IE_i;
+		dbg_trap_o = 1'b0;
+		perf_tbranch_o = 1'b0;
+		perf_jump_o = 1'b0;
+		case (ctrl_fsm_cs)
+			4'd0: begin
+				ctrl_busy_o = 1'b0;
+				instr_req_o = 1'b0;
+				if (fetch_enable_i == 1'b1)
+					ctrl_fsm_ns = 4'd1;
+				else if (dbg_req_i)
+					ctrl_fsm_ns = 4'd8;
+			end
+			4'd1: begin
+				instr_req_o = 1'b1;
+				pc_mux_o = PC_BOOT;
+				pc_set_o = 1'b1;
+				ctrl_fsm_ns = 4'd4;
+			end
+			4'd2: begin
+				ctrl_busy_o = 1'b0;
+				instr_req_o = 1'b0;
+				halt_if_o = 1'b1;
+				halt_id_o = 1'b1;
+				ctrl_fsm_ns = 4'd3;
+			end
+			4'd3: begin
+				ctrl_busy_o = 1'b0;
+				instr_req_o = 1'b0;
+				halt_if_o = 1'b1;
+				halt_id_o = 1'b1;
+				dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+				if (dbg_req_i) begin
+					if (fetch_enable_i || irq_req_ctrl_i)
+						ctrl_fsm_ns = 4'd8;
+					else
+						ctrl_fsm_ns = 4'd9;
+				end
+				else if (fetch_enable_i || irq_req_ctrl_i)
+					ctrl_fsm_ns = 4'd4;
+			end
+			4'd4: begin
+				first_fetch_o = 1'b1;
+				if ((id_ready_i == 1'b1) && (dbg_stall_i == 1'b0))
+					ctrl_fsm_ns = 4'd5;
+				if (irq_req_ctrl_i & irq_enable_int) begin
+					ctrl_fsm_ns = 4'd7;
+					halt_if_o = 1'b1;
+					halt_id_o = 1'b1;
+				end
+			end
+			4'd5: begin
+				is_decoding_o = 1'b0;
+				if (instr_valid_i) begin
+					is_decoding_o = 1'b1;
+					case (1'b1)
+						branch_set_i: begin
+							pc_mux_o = PC_JUMP;
+							pc_set_o = 1'b1;
+							perf_tbranch_o = 1'b1;
+							dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+							if (dbg_req_i)
+								ctrl_fsm_ns = 4'd8;
+						end
+						jump_set_i: begin
+							pc_mux_o = PC_JUMP;
+							pc_set_o = 1'b1;
+							perf_jump_o = 1'b1;
+							dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+						end
+						((((mret_insn_i | ecall_insn_i) | pipe_flush_i) | ebrk_insn_i) | illegal_insn_i) | csr_status_i: begin
+							ctrl_fsm_ns = 4'd6;
+							halt_if_o = 1'b1;
+							halt_id_o = 1'b1;
+						end
+						default: begin
+							dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+							case (1'b1)
+								((irq_req_ctrl_i & irq_enable_int) & ~instr_multicyle_i) & ~branch_in_id_i: begin
+									ctrl_fsm_ns = 4'd7;
+									halt_if_o = 1'b1;
+									halt_id_o = 1'b1;
+								end
+								dbg_req_i & ~branch_taken_ex_i: begin
+									halt_if_o = 1'b1;
+									if (id_ready_i)
+										ctrl_fsm_ns = 4'd8;
+								end
+								default: exc_kill_o = ((irq_req_ctrl_i & ~instr_multicyle_i) & ~branch_in_id_i ? 1'b1 : 1'b0);
+							endcase
+						end
+					endcase
+				end
+				else if (irq_req_ctrl_i & irq_enable_int) begin
+					ctrl_fsm_ns = 4'd7;
+					halt_if_o = 1'b1;
+					halt_id_o = 1'b1;
+				end
+			end
+			4'd8: begin
+				dbg_ack_o = 1'b1;
+				halt_if_o = 1'b1;
+				ctrl_fsm_ns = 4'd10;
+			end
+			4'd9: begin
+				dbg_ack_o = 1'b1;
+				halt_if_o = 1'b1;
+				ctrl_fsm_ns = 4'd12;
+			end
+			4'd12: begin
+				halt_if_o = 1'b1;
+				if (dbg_jump_req_i) begin
+					pc_mux_o = PC_DBG_NPC;
+					pc_set_o = 1'b1;
+					ctrl_fsm_ns = 4'd10;
+				end
+				if (dbg_stall_i == 1'b0)
+					ctrl_fsm_ns = 4'd3;
+			end
+			4'd10: begin
+				halt_if_o = 1'b1;
+				if (dbg_jump_req_i) begin
+					pc_mux_o = PC_DBG_NPC;
+					pc_set_o = 1'b1;
+					ctrl_fsm_ns = 4'd10;
+				end
+				if (dbg_stall_i == 1'b0)
+					ctrl_fsm_ns = 4'd5;
+			end
+			4'd7: begin
+				pc_mux_o = PC_EXCEPTION;
+				pc_set_o = 1'b1;
+				exc_pc_mux_o = EXC_PC_IRQ;
+				exc_cause_o = {1'b0, irq_id_ctrl_i};
+				csr_save_cause_o = 1'b1;
+				csr_cause_o = {1'b1, irq_id_ctrl_i};
+				csr_save_if_o = 1'b1;
+				irq_ack_o = 1'b1;
+				exc_ack_o = 1'b1;
+				ctrl_fsm_ns = 4'd5;
+			end
+			4'd6: begin
+				halt_if_o = (fetch_enable_i ? dbg_req_i : 1'b1);
+				halt_id_o = 1'b1;
+				ctrl_fsm_ns = (dbg_req_i ? 4'd8 : 4'd5);
+				case (1'b1)
+					ecall_insn_i: begin
+						pc_mux_o = PC_EXCEPTION;
+						pc_set_o = 1'b1;
+						csr_save_id_o = 1'b1;
+						csr_save_cause_o = 1'b1;
+						exc_pc_mux_o = EXC_PC_ECALL;
+						exc_cause_o = EXC_CAUSE_ECALL_MMODE;
+						csr_cause_o = EXC_CAUSE_ECALL_MMODE;
+						dbg_trap_o = dbg_settings_i[DBG_SETS_ECALL] | dbg_settings_i[DBG_SETS_SSTE];
+					end
+					illegal_insn_i: begin
+						pc_mux_o = PC_EXCEPTION;
+						pc_set_o = 1'b1;
+						csr_save_id_o = 1'b1;
+						csr_save_cause_o = 1'b1;
+						exc_pc_mux_o = EXC_PC_ILLINSN;
+						exc_cause_o = EXC_CAUSE_ILLEGAL_INSN;
+						csr_cause_o = EXC_CAUSE_ILLEGAL_INSN;
+						dbg_trap_o = dbg_settings_i[DBG_SETS_EILL] | dbg_settings_i[DBG_SETS_SSTE];
+					end
+					mret_insn_i: begin
+						pc_mux_o = PC_ERET;
+						pc_set_o = 1'b1;
+						csr_restore_mret_id_o = 1'b1;
+						dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+					end
+					ebrk_insn_i: begin
+						dbg_trap_o = dbg_settings_i[DBG_SETS_EBRK] | dbg_settings_i[DBG_SETS_SSTE];
+						exc_cause_o = EXC_CAUSE_BREAKPOINT;
+					end
+					csr_status_i: dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+					pipe_flush_i: dbg_trap_o = dbg_settings_i[DBG_SETS_SSTE];
+					default:
+						;
+				endcase
+				if (fetch_enable_i) begin
+					if (dbg_req_i)
+						ctrl_fsm_ns = 4'd8;
+					else
+						ctrl_fsm_ns = 4'd5;
+				end
+				else if (dbg_req_i)
+					ctrl_fsm_ns = 4'd9;
+				else
+					ctrl_fsm_ns = (mret_insn_i | pipe_flush_i ? 4'd2 : 4'd5);
+			end
+			default: begin
+				instr_req_o = 1'b0;
+				ctrl_fsm_ns = 4'd0;
+			end
+		endcase
+	end
+	always @(*) begin
+		deassert_we_o = 1'b0;
+		if (~is_decoding_o)
+			deassert_we_o = 1'b1;
+		if (illegal_insn_i)
+			deassert_we_o = 1'b1;
+	end
+	assign operand_a_fw_mux_sel_o = (data_misaligned_i ? SEL_MISALIGNED : SEL_REGFILE);
+	always @(posedge clk or negedge rst_n) begin : UPDATE_REGS
+		if (rst_n == 1'b0)
+			ctrl_fsm_cs <= 4'd0;
+		else
+			ctrl_fsm_cs <= ctrl_fsm_ns;
+	end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_core.v b/verilog/rtl/ips/zero-riscy/zeroriscy_core.v
new file mode 100644
index 0000000..371e9c5
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_core.v
@@ -0,0 +1,711 @@
+module zeroriscy_core 
+#(
+  parameter N_EXT_PERF_COUNTERS = 0,
+  parameter RV32E               = 0,
+  parameter RV32M               = 1
+)
+(
+	clk_i,
+	rst_ni,
+	clock_en_i,
+	test_en_i,
+	core_id_i,
+	cluster_id_i,
+	boot_addr_i,
+	instr_req_o,
+	instr_gnt_i,
+	instr_rvalid_i,
+	instr_addr_o,
+	instr_rdata_i,
+	data_req_o,
+	data_gnt_i,
+	data_rvalid_i,
+	data_we_o,
+	data_be_o,
+	data_addr_o,
+	data_wdata_o,
+	data_rdata_i,
+	data_err_i,
+	irq_i,
+	irq_id_i,
+	irq_ack_o,
+	irq_id_o,
+	debug_req_i,
+	debug_gnt_o,
+	debug_rvalid_o,
+	debug_addr_i,
+	debug_we_i,
+	debug_wdata_i,
+	debug_rdata_o,
+	debug_halted_o,
+	debug_halt_i,
+	debug_resume_i,
+	fetch_enable_i,
+	core_busy_o,
+	ext_perf_counters_i
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	//parameter N_EXT_PERF_COUNTERS = 0;
+	//parameter RV32E = 0;
+	//parameter RV32M = 1;
+	input wire clk_i;
+	input wire rst_ni;
+	input wire clock_en_i;
+	input wire test_en_i;
+	input wire [3:0] core_id_i;
+	input wire [5:0] cluster_id_i;
+	input wire [31:0] boot_addr_i;
+	output wire instr_req_o;
+	input wire instr_gnt_i;
+	input wire instr_rvalid_i;
+	output wire [31:0] instr_addr_o;
+	input wire [31:0] instr_rdata_i;
+	output wire data_req_o;
+	input wire data_gnt_i;
+	input wire data_rvalid_i;
+	output wire data_we_o;
+	output wire [3:0] data_be_o;
+	output wire [31:0] data_addr_o;
+	output wire [31:0] data_wdata_o;
+	input wire [31:0] data_rdata_i;
+	input wire data_err_i;
+	input wire irq_i;
+	input wire [4:0] irq_id_i;
+	output wire irq_ack_o;
+	output wire [4:0] irq_id_o;
+	input wire debug_req_i;
+	output wire debug_gnt_o;
+	output wire debug_rvalid_o;
+	input wire [14:0] debug_addr_i;
+	input wire debug_we_i;
+	input wire [31:0] debug_wdata_i;
+	output wire [31:0] debug_rdata_o;
+	output wire debug_halted_o;
+	input wire debug_halt_i;
+	input wire debug_resume_i;
+	input wire fetch_enable_i;
+	output wire core_busy_o;
+	input wire [N_EXT_PERF_COUNTERS - 1:0] ext_perf_counters_i;
+	localparam N_HWLP = 2;
+	localparam N_HWLP_BITS = 1;
+	wire instr_valid_id;
+	wire [31:0] instr_rdata_id;
+	wire is_compressed_id;
+	wire illegal_c_insn_id;
+	wire [31:0] pc_if;
+	wire [31:0] pc_id;
+	wire clear_instr_valid;
+	wire pc_set;
+	wire [2:0] pc_mux_id;
+	wire [1:0] exc_pc_mux_id;
+	wire [5:0] exc_cause;
+	wire lsu_load_err;
+	wire lsu_store_err;
+	wire is_decoding;
+	wire data_misaligned;
+	wire [31:0] misaligned_addr;
+	wire [31:0] jump_target_ex;
+	wire branch_in_ex;
+	wire branch_decision;
+	wire ctrl_busy;
+	wire if_busy;
+	wire lsu_busy;
+	wire [ALU_OP_WIDTH - 1:0] alu_operator_ex;
+	wire [31:0] alu_operand_a_ex;
+	wire [31:0] alu_operand_b_ex;
+	wire [31:0] alu_adder_result_ex;
+	wire [31:0] regfile_wdata_ex;
+	wire mult_en_ex;
+	wire div_en_ex;
+	wire [1:0] multdiv_operator_ex;
+	wire [1:0] multdiv_signed_mode_ex;
+	wire [31:0] multdiv_operand_a_ex;
+	wire [31:0] multdiv_operand_b_ex;
+	wire csr_access_ex;
+	wire [1:0] csr_op_ex;
+	wire csr_access;
+	wire [1:0] csr_op;
+	wire [11:0] csr_addr;
+	wire [11:0] csr_addr_int;
+	wire [31:0] csr_rdata;
+	wire [31:0] csr_wdata;
+	wire data_we_ex;
+	wire [1:0] data_type_ex;
+	wire data_sign_ext_ex;
+	wire [1:0] data_reg_offset_ex;
+	wire data_req_ex;
+	wire [31:0] data_wdata_ex;
+	wire data_load_event_ex;
+	wire data_misaligned_ex;
+	wire [31:0] regfile_wdata_lsu;
+	wire halt_if;
+	wire id_ready;
+	wire ex_ready;
+	wire if_valid;
+	wire id_valid;
+	wire wb_valid;
+	wire lsu_ready_ex;
+	wire data_valid_lsu;
+	wire instr_req_int;
+	wire m_irq_enable;
+	wire [31:0] mepc;
+	wire csr_save_cause;
+	wire csr_save_if;
+	wire csr_save_id;
+	wire [5:0] csr_cause;
+	wire csr_restore_mret_id;
+	wire csr_restore_uret_id;
+	wire [DBG_SETS_W - 1:0] dbg_settings;
+	wire dbg_req;
+	wire dbg_ack;
+	wire dbg_stall;
+	wire dbg_trap;
+	wire dbg_reg_rreq;
+	wire [4:0] dbg_reg_raddr;
+	wire [31:0] dbg_reg_rdata;
+	wire dbg_reg_wreq;
+	wire [4:0] dbg_reg_waddr;
+	wire [31:0] dbg_reg_wdata;
+	wire dbg_csr_req;
+	wire [11:0] dbg_csr_addr;
+	wire dbg_csr_we;
+	wire [31:0] dbg_csr_wdata;
+	wire [31:0] dbg_jump_addr;
+	wire dbg_jump_req;
+	wire perf_imiss;
+	wire perf_jump;
+	wire perf_branch;
+	wire perf_tbranch;
+	wire core_ctrl_firstfetch;
+	wire core_busy_int;
+	reg core_busy_q;
+	wire clk;
+	wire clock_en;
+	wire dbg_busy;
+	wire sleeping;
+	assign core_busy_int = (data_load_event_ex & data_req_o ? if_busy : (if_busy | ctrl_busy) | lsu_busy);
+	always @(posedge clk or negedge rst_ni)
+		if (rst_ni == 1'b0)
+			core_busy_q <= 1'b0;
+		else
+			core_busy_q <= core_busy_int;
+	assign core_busy_o = (core_ctrl_firstfetch ? 1'b1 : core_busy_q);
+	assign dbg_busy = (((dbg_req | dbg_csr_req) | dbg_jump_req) | dbg_reg_wreq) | debug_req_i;
+	assign clock_en = (clock_en_i | core_busy_o) | dbg_busy;
+	assign sleeping = ~fetch_enable_i & ~core_busy_o;
+	cluster_clock_gating core_clock_gate_i(
+		.clk_i(clk_i),
+		.en_i(clock_en),
+		.test_en_i(test_en_i),
+		.clk_o(clk)
+	);
+	zeroriscy_if_stage if_stage_i(
+		.clk(clk),
+		.rst_n(rst_ni),
+		.boot_addr_i(boot_addr_i),
+		.req_i(instr_req_int),
+		.instr_req_o(instr_req_o),
+		.instr_addr_o(instr_addr_o),
+		.instr_gnt_i(instr_gnt_i),
+		.instr_rvalid_i(instr_rvalid_i),
+		.instr_rdata_i(instr_rdata_i),
+		.instr_valid_id_o(instr_valid_id),
+		.instr_rdata_id_o(instr_rdata_id),
+		.is_compressed_id_o(is_compressed_id),
+		.illegal_c_insn_id_o(illegal_c_insn_id),
+		.pc_if_o(pc_if),
+		.pc_id_o(pc_id),
+		.clear_instr_valid_i(clear_instr_valid),
+		.pc_set_i(pc_set),
+		.exception_pc_reg_i(mepc),
+		.pc_mux_i(pc_mux_id),
+		.exc_pc_mux_i(exc_pc_mux_id),
+		.exc_vec_pc_mux_i(exc_cause[4:0]),
+		.dbg_jump_addr_i(dbg_jump_addr),
+		.jump_target_ex_i(jump_target_ex),
+		.halt_if_i(halt_if),
+		.id_ready_i(id_ready),
+		.if_valid_o(if_valid),
+		.if_busy_o(if_busy),
+		.perf_imiss_o(perf_imiss)
+	);
+	zeroriscy_id_stage #(
+		.RV32E(RV32E),
+		.RV32M(RV32M)
+	) id_stage_i(
+		.clk(clk),
+		.rst_n(rst_ni),
+		.test_en_i(test_en_i),
+		.fetch_enable_i(fetch_enable_i),
+		.ctrl_busy_o(ctrl_busy),
+		.core_ctrl_firstfetch_o(core_ctrl_firstfetch),
+		.is_decoding_o(is_decoding),
+		.instr_valid_i(instr_valid_id),
+		.instr_rdata_i(instr_rdata_id),
+		.instr_req_o(instr_req_int),
+		.branch_in_ex_o(branch_in_ex),
+		.branch_decision_i(branch_decision),
+		.clear_instr_valid_o(clear_instr_valid),
+		.pc_set_o(pc_set),
+		.pc_mux_o(pc_mux_id),
+		.exc_pc_mux_o(exc_pc_mux_id),
+		.exc_cause_o(exc_cause),
+		.illegal_c_insn_i(illegal_c_insn_id),
+		.is_compressed_i(is_compressed_id),
+		.pc_id_i(pc_id),
+		.halt_if_o(halt_if),
+		.id_ready_o(id_ready),
+		.ex_ready_i(ex_ready),
+		.id_valid_o(id_valid),
+		.alu_operator_ex_o(alu_operator_ex),
+		.alu_operand_a_ex_o(alu_operand_a_ex),
+		.alu_operand_b_ex_o(alu_operand_b_ex),
+		.mult_en_ex_o(mult_en_ex),
+		.div_en_ex_o(div_en_ex),
+		.multdiv_operator_ex_o(multdiv_operator_ex),
+		.multdiv_signed_mode_ex_o(multdiv_signed_mode_ex),
+		.multdiv_operand_a_ex_o(multdiv_operand_a_ex),
+		.multdiv_operand_b_ex_o(multdiv_operand_b_ex),
+		.csr_access_ex_o(csr_access_ex),
+		.csr_op_ex_o(csr_op_ex),
+		.csr_cause_o(csr_cause),
+		.csr_save_if_o(csr_save_if),
+		.csr_save_id_o(csr_save_id),
+		.csr_restore_mret_id_o(csr_restore_mret_id),
+		.csr_save_cause_o(csr_save_cause),
+		.data_req_ex_o(data_req_ex),
+		.data_we_ex_o(data_we_ex),
+		.data_type_ex_o(data_type_ex),
+		.data_sign_ext_ex_o(data_sign_ext_ex),
+		.data_reg_offset_ex_o(data_reg_offset_ex),
+		.data_load_event_ex_o(data_load_event_ex),
+		.data_wdata_ex_o(data_wdata_ex),
+		.data_misaligned_i(data_misaligned),
+		.misaligned_addr_i(misaligned_addr),
+		.irq_i(irq_i),
+		.irq_id_i(irq_id_i),
+		.m_irq_enable_i(m_irq_enable),
+		.irq_ack_o(irq_ack_o),
+		.irq_id_o(irq_id_o),
+		.lsu_load_err_i(lsu_load_err),
+		.lsu_store_err_i(lsu_store_err),
+		.dbg_settings_i(dbg_settings),
+		.dbg_req_i(dbg_req),
+		.dbg_ack_o(dbg_ack),
+		.dbg_stall_i(dbg_stall),
+		.dbg_trap_o(dbg_trap),
+		.dbg_reg_rreq_i(dbg_reg_rreq),
+		.dbg_reg_raddr_i(dbg_reg_raddr),
+		.dbg_reg_rdata_o(dbg_reg_rdata),
+		.dbg_reg_wreq_i(dbg_reg_wreq),
+		.dbg_reg_waddr_i(dbg_reg_waddr),
+		.dbg_reg_wdata_i(dbg_reg_wdata),
+		.dbg_jump_req_i(dbg_jump_req),
+		.regfile_wdata_lsu_i(regfile_wdata_lsu),
+		.regfile_wdata_ex_i(regfile_wdata_ex),
+		.csr_rdata_i(csr_rdata),
+		.perf_jump_o(perf_jump),
+		.perf_branch_o(perf_branch),
+		.perf_tbranch_o(perf_tbranch)
+	);
+	zeroriscy_ex_block #(.RV32M(RV32M)) ex_block_i(
+		.clk(clk),
+		.rst_n(rst_ni),
+		.alu_operator_i(alu_operator_ex),
+		.multdiv_operator_i(multdiv_operator_ex),
+		.alu_operand_a_i(alu_operand_a_ex),
+		.alu_operand_b_i(alu_operand_b_ex),
+		.mult_en_i(mult_en_ex),
+		.div_en_i(div_en_ex),
+		.multdiv_signed_mode_i(multdiv_signed_mode_ex),
+		.multdiv_operand_a_i(multdiv_operand_a_ex),
+		.multdiv_operand_b_i(multdiv_operand_b_ex),
+		.alu_adder_result_ex_o(alu_adder_result_ex),
+		.regfile_wdata_ex_o(regfile_wdata_ex),
+		.jump_target_o(jump_target_ex),
+		.branch_decision_o(branch_decision),
+		.lsu_en_i(data_req_ex),
+		.lsu_ready_ex_i(data_valid_lsu),
+		.ex_ready_o(ex_ready)
+	);
+	zeroriscy_load_store_unit load_store_unit_i(
+		.clk(clk),
+		.rst_n(rst_ni),
+		.data_req_o(data_req_o),
+		.data_gnt_i(data_gnt_i),
+		.data_rvalid_i(data_rvalid_i),
+		.data_err_i(data_err_i),
+		.data_addr_o(data_addr_o),
+		.data_we_o(data_we_o),
+		.data_be_o(data_be_o),
+		.data_wdata_o(data_wdata_o),
+		.data_rdata_i(data_rdata_i),
+		.data_we_ex_i(data_we_ex),
+		.data_type_ex_i(data_type_ex),
+		.data_wdata_ex_i(data_wdata_ex),
+		.data_reg_offset_ex_i(data_reg_offset_ex),
+		.data_sign_ext_ex_i(data_sign_ext_ex),
+		.data_rdata_ex_o(regfile_wdata_lsu),
+		.data_req_ex_i(data_req_ex),
+		.adder_result_ex_i(alu_adder_result_ex),
+		.data_misaligned_o(data_misaligned),
+		.misaligned_addr_o(misaligned_addr),
+		.load_err_o(lsu_load_err),
+		.store_err_o(lsu_store_err),
+		.data_valid_o(data_valid_lsu),
+		.lsu_update_addr_o(),
+		.busy_o(lsu_busy)
+	);
+	zeroriscy_cs_registers #(.N_EXT_CNT(N_EXT_PERF_COUNTERS)) cs_registers_i(
+		.clk(clk),
+		.rst_n(rst_ni),
+		.core_id_i(core_id_i),
+		.cluster_id_i(cluster_id_i),
+		.boot_addr_i(boot_addr_i[31:8]),
+		.csr_access_i(csr_access),
+		.csr_addr_i(csr_addr),
+		.csr_wdata_i(csr_wdata),
+		.csr_op_i(csr_op),
+		.csr_rdata_o(csr_rdata),
+		.m_irq_enable_o(m_irq_enable),
+		.mepc_o(mepc),
+		.pc_if_i(pc_if),
+		.pc_id_i(pc_id),
+		.csr_save_if_i(csr_save_if),
+		.csr_save_id_i(csr_save_id),
+		.csr_restore_mret_i(csr_restore_mret_id),
+		.csr_cause_i(csr_cause),
+		.csr_save_cause_i(csr_save_cause),
+		.if_valid_i(if_valid),
+		.id_valid_i(id_valid),
+		.is_compressed_i(is_compressed_id),
+		.is_decoding_i(is_decoding),
+		.imiss_i(perf_imiss),
+		.pc_set_i(pc_set),
+		.jump_i(perf_jump),
+		.branch_i(perf_branch),
+		.branch_taken_i(perf_tbranch),
+		.mem_load_i((data_req_o & data_gnt_i) & ~data_we_o),
+		.mem_store_i((data_req_o & data_gnt_i) & data_we_o),
+		.ext_counters_i(ext_perf_counters_i)
+	);
+	assign csr_access = (dbg_csr_req == 1'b0 ? csr_access_ex : 1'b1);
+	assign csr_addr = (dbg_csr_req == 1'b0 ? csr_addr_int : dbg_csr_addr);
+	assign csr_wdata = (dbg_csr_req == 1'b0 ? alu_operand_a_ex : dbg_csr_wdata);
+	assign csr_op = (dbg_csr_req == 1'b0 ? csr_op_ex : (dbg_csr_we == 1'b1 ? CSR_OP_WRITE : CSR_OP_NONE));
+	assign csr_addr_int = (csr_access_ex ? alu_operand_b_ex[11:0] : {12 {1'sb0}});
+	zeroriscy_debug_unit debug_unit_i(
+		.clk(clk_i),
+		.rst_n(rst_ni),
+		.debug_req_i(debug_req_i),
+		.debug_gnt_o(debug_gnt_o),
+		.debug_rvalid_o(debug_rvalid_o),
+		.debug_addr_i(debug_addr_i),
+		.debug_we_i(debug_we_i),
+		.debug_wdata_i(debug_wdata_i),
+		.debug_rdata_o(debug_rdata_o),
+		.debug_halt_i(debug_halt_i),
+		.debug_resume_i(debug_resume_i),
+		.debug_halted_o(debug_halted_o),
+		.settings_o(dbg_settings),
+		.trap_i(dbg_trap),
+		.exc_cause_i(exc_cause),
+		.stall_o(dbg_stall),
+		.dbg_req_o(dbg_req),
+		.dbg_ack_i(dbg_ack),
+		.regfile_rreq_o(dbg_reg_rreq),
+		.regfile_raddr_o(dbg_reg_raddr),
+		.regfile_rdata_i(dbg_reg_rdata),
+		.regfile_wreq_o(dbg_reg_wreq),
+		.regfile_waddr_o(dbg_reg_waddr),
+		.regfile_wdata_o(dbg_reg_wdata),
+		.csr_req_o(dbg_csr_req),
+		.csr_addr_o(dbg_csr_addr),
+		.csr_we_o(dbg_csr_we),
+		.csr_wdata_o(dbg_csr_wdata),
+		.csr_rdata_i(csr_rdata),
+		.pc_if_i(pc_if),
+		.pc_id_i(pc_id),
+		.instr_valid_id_i(instr_valid_id),
+		.sleeping_i(sleeping),
+		.jump_addr_o(dbg_jump_addr),
+		.jump_req_o(dbg_jump_req)
+	);
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_cs_registers.v b/verilog/rtl/ips/zero-riscy/zeroriscy_cs_registers.v
new file mode 100644
index 0000000..74c5b8a
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_cs_registers.v
@@ -0,0 +1,584 @@
+`define ASIC_SYNTHESIS
+
+module zeroriscy_cs_registers (
+	clk,
+	rst_n,
+	core_id_i,
+	cluster_id_i,
+	boot_addr_i,
+	csr_access_i,
+	csr_addr_i,
+	csr_wdata_i,
+	csr_op_i,
+	csr_rdata_o,
+	m_irq_enable_o,
+	mepc_o,
+	pc_if_i,
+	pc_id_i,
+	csr_save_if_i,
+	csr_save_id_i,
+	csr_restore_mret_i,
+	csr_cause_i,
+	csr_save_cause_i,
+	if_valid_i,
+	id_valid_i,
+	is_compressed_i,
+	is_decoding_i,
+	imiss_i,
+	pc_set_i,
+	jump_i,
+	branch_i,
+	branch_taken_i,
+	mem_load_i,
+	mem_store_i,
+	ext_counters_i
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	parameter N_EXT_CNT = 0;
+	input wire clk;
+	input wire rst_n;
+	input wire [3:0] core_id_i;
+	input wire [5:0] cluster_id_i;
+	input wire [23:0] boot_addr_i;
+	input wire csr_access_i;
+	input wire [11:0] csr_addr_i;
+	input wire [31:0] csr_wdata_i;
+	input wire [1:0] csr_op_i;
+	output reg [31:0] csr_rdata_o;
+	output wire m_irq_enable_o;
+	output wire [31:0] mepc_o;
+	input wire [31:0] pc_if_i;
+	input wire [31:0] pc_id_i;
+	input wire csr_save_if_i;
+	input wire csr_save_id_i;
+	input wire csr_restore_mret_i;
+	input wire [5:0] csr_cause_i;
+	input wire csr_save_cause_i;
+	input wire if_valid_i;
+	input wire id_valid_i;
+	input wire is_compressed_i;
+	input wire is_decoding_i;
+	input wire imiss_i;
+	input wire pc_set_i;
+	input wire jump_i;
+	input wire branch_i;
+	input wire branch_taken_i;
+	input wire mem_load_i;
+	input wire mem_store_i;
+	input wire [N_EXT_CNT - 1:0] ext_counters_i;
+	localparam N_PERF_COUNTERS = 11 + N_EXT_CNT;
+	localparam N_PERF_REGS = 1;
+	reg id_valid_q;
+	wire [N_PERF_COUNTERS - 1:0] PCCR_in;
+	wire [N_PERF_COUNTERS - 1:0] PCCR_inc;
+	reg [N_PERF_COUNTERS - 1:0] PCCR_inc_q;
+	reg [31:0] PCCR_q;
+	reg [31:0] PCCR_n;
+	reg [1:0] PCMR_n;
+	reg [1:0] PCMR_q;
+	reg [N_PERF_COUNTERS - 1:0] PCER_n;
+	reg [N_PERF_COUNTERS - 1:0] PCER_q;
+	reg [31:0] perf_rdata;
+	reg [4:0] pccr_index;
+	reg pccr_all_sel;
+	reg is_pccr;
+	reg is_pcer;
+	reg is_pcmr;
+	reg [31:0] csr_wdata_int;
+	reg [31:0] csr_rdata_int;
+	reg csr_we_int;
+	reg [31:0] mepc_q;
+	reg [31:0] mepc_n;
+	reg [5:0] mcause_q;
+	reg [5:0] mcause_n;
+	struct packed {
+		logic mie;
+		logic mpie;
+		PrivLvl_t mpp;
+	} mstatus_q;
+	struct packed {
+		logic mie;
+		logic mpie;
+		PrivLvl_t mpp;
+	} mstatus_n;
+	always @(*) begin
+		csr_rdata_int = 1'sb0;
+		case (csr_addr_i)
+			12'h300: csr_rdata_int = {19'b0000000000000000000, mstatus_q.mpp, 3'b000, mstatus_q.mpie, 3'h0, mstatus_q.mie, 3'h0};
+			12'h305: csr_rdata_int = {boot_addr_i, 8'h00};
+			12'h341: csr_rdata_int = mepc_q;
+			12'h342: csr_rdata_int = {mcause_q[5], 26'b00000000000000000000000000, mcause_q[4:0]};
+			12'hf14: csr_rdata_int = {21'b000000000000000000000, cluster_id_i[5:0], 1'b0, core_id_i[3:0]};
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		mepc_n = mepc_q;
+		mstatus_n = mstatus_q;
+		mcause_n = mcause_q;
+		case (csr_addr_i)
+			12'h300:
+				if (csr_we_int)
+					mstatus_n = {
+						csr_wdata_int[3],
+						csr_wdata_int[7],
+						1'b0
+					};
+			12'h341:
+				if (csr_we_int)
+					mepc_n = csr_wdata_int;
+			12'h342:
+				if (csr_we_int)
+					mcause_n = {csr_wdata_int[31], csr_wdata_int[4:0]};
+			default:
+				;
+		endcase
+		case (1'b1)
+			csr_save_cause_i: begin
+				case (1'b1)
+					csr_save_if_i: mepc_n = pc_if_i;
+					csr_save_id_i: mepc_n = pc_id_i;
+					default:
+						;
+				endcase
+				mstatus_n.mpie = mstatus_q.mie;
+				mstatus_n.mie = 1'b0;
+				mcause_n = csr_cause_i;
+			end
+			csr_restore_mret_i: begin
+				mstatus_n.mie = mstatus_q.mpie;
+				mstatus_n.mpie = 1'b1;
+			end
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		csr_wdata_int = csr_wdata_i;
+		csr_we_int = 1'b1;
+		case (csr_op_i)
+			CSR_OP_WRITE: csr_wdata_int = csr_wdata_i;
+			CSR_OP_SET: csr_wdata_int = csr_wdata_i | csr_rdata_o;
+			CSR_OP_CLEAR: csr_wdata_int = ~csr_wdata_i & csr_rdata_o;
+			CSR_OP_NONE: begin
+				csr_wdata_int = csr_wdata_i;
+				csr_we_int = 1'b0;
+			end
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		csr_rdata_o = csr_rdata_int;
+		if ((is_pccr || is_pcer) || is_pcmr)
+			csr_rdata_o = perf_rdata;
+	end
+	assign m_irq_enable_o = mstatus_q.mie;
+	assign mepc_o = mepc_q;
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			mstatus_q <= {
+				1'b0,
+				1'b0,
+				PRIV_LVL_M
+			};
+			mepc_q <= 1'sb0;
+			mcause_q <= 1'sb0;
+		end
+		else begin
+			mstatus_q <= {
+				mstatus_n.mie,
+				mstatus_n.mpie,
+				PRIV_LVL_M
+			};
+			mepc_q <= mepc_n;
+			mcause_q <= mcause_n;
+		end
+	assign PCCR_in[0] = 1'b1;
+	assign PCCR_in[1] = if_valid_i;
+	assign PCCR_in[2] = 1'b0;
+	assign PCCR_in[3] = 1'b0;
+	assign PCCR_in[4] = imiss_i & ~pc_set_i;
+	assign PCCR_in[5] = mem_load_i;
+	assign PCCR_in[6] = mem_store_i;
+	assign PCCR_in[7] = jump_i;
+	assign PCCR_in[8] = branch_i;
+	assign PCCR_in[9] = branch_taken_i;
+	assign PCCR_in[10] = (id_valid_i & is_decoding_i) & is_compressed_i;
+	genvar i;
+	generate
+		for (i = 0; i < N_EXT_CNT; i = i + 1) begin : g_extcounters
+			assign PCCR_in[(N_PERF_COUNTERS - N_EXT_CNT) + i] = ext_counters_i[i];
+		end
+	endgenerate
+	always @(*) begin
+		is_pccr = 1'b0;
+		is_pcmr = 1'b0;
+		is_pcer = 1'b0;
+		pccr_all_sel = 1'b0;
+		pccr_index = 1'sb0;
+		perf_rdata = 1'sb0;
+		if (csr_access_i) begin
+			case (csr_addr_i)
+				12'h7a0: begin
+					is_pcer = 1'b1;
+					perf_rdata[15:0] = PCER_q;
+				end
+				12'h7a1: begin
+					is_pcmr = 1'b1;
+					perf_rdata[1:0] = PCMR_q;
+				end
+				12'h79f: begin
+					is_pccr = 1'b1;
+					pccr_all_sel = 1'b1;
+				end
+				default:
+					;
+			endcase
+			if (csr_addr_i[11:5] == 7'b0111100) begin
+				is_pccr = 1'b1;
+				pccr_index = csr_addr_i[4:0];
+				perf_rdata = PCCR_q[0+:32];
+			end
+		end
+	end
+	assign PCCR_inc[0] = |(PCCR_in & PCER_q) & PCMR_q[0];
+	always @(*) begin
+		PCCR_n[0+:32] = PCCR_q[0+:32];
+		if ((PCCR_inc_q[0] == 1'b1) && ((PCCR_q[0+:32] != 32'hffffffff) || (PCMR_q[1] == 1'b0)))
+			PCCR_n[0+:32] = PCCR_q[0+:32] + 1;
+		if (is_pccr == 1'b1)
+			case (csr_op_i)
+				CSR_OP_NONE:
+					;
+				CSR_OP_WRITE: PCCR_n[0+:32] = csr_wdata_i;
+				CSR_OP_SET: PCCR_n[0+:32] = csr_wdata_i | PCCR_q[0+:32];
+				CSR_OP_CLEAR: PCCR_n[0+:32] = csr_wdata_i & ~PCCR_q[0+:32];
+			endcase
+	end
+	always @(*) begin
+		PCMR_n = PCMR_q;
+		PCER_n = PCER_q;
+		if (is_pcmr)
+			case (csr_op_i)
+				CSR_OP_NONE:
+					;
+				CSR_OP_WRITE: PCMR_n = csr_wdata_i[1:0];
+				CSR_OP_SET: PCMR_n = csr_wdata_i[1:0] | PCMR_q;
+				CSR_OP_CLEAR: PCMR_n = csr_wdata_i[1:0] & ~PCMR_q;
+			endcase
+		if (is_pcer)
+			case (csr_op_i)
+				CSR_OP_NONE:
+					;
+				CSR_OP_WRITE: PCER_n = csr_wdata_i[N_PERF_COUNTERS - 1:0];
+				CSR_OP_SET: PCER_n = csr_wdata_i[N_PERF_COUNTERS - 1:0] | PCER_q;
+				CSR_OP_CLEAR: PCER_n = csr_wdata_i[N_PERF_COUNTERS - 1:0] & ~PCER_q;
+			endcase
+	end
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			id_valid_q <= 1'b0;
+			PCER_q <= 1'sb0;
+			PCMR_q <= 2'h3;
+			begin : sv2v_autoblock_1
+				reg signed [31:0] i;
+				for (i = 0; i < N_PERF_REGS; i = i + 1)
+					begin
+						PCCR_q[i * 32+:32] <= 1'sb0;
+						PCCR_inc_q[i] <= 1'sb0;
+					end
+			end
+		end
+		else begin
+			id_valid_q <= id_valid_i;
+			PCER_q <= PCER_n;
+			PCMR_q <= PCMR_n;
+			begin : sv2v_autoblock_2
+				reg signed [31:0] i;
+				for (i = 0; i < N_PERF_REGS; i = i + 1)
+					begin
+						PCCR_q[i * 32+:32] <= PCCR_n[i * 32+:32];
+						PCCR_inc_q[i] <= PCCR_inc[i];
+					end
+			end
+		end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_debug_unit.v b/verilog/rtl/ips/zero-riscy/zeroriscy_debug_unit.v
new file mode 100644
index 0000000..8afbeb4
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_debug_unit.v
@@ -0,0 +1,629 @@
+module zeroriscy_debug_unit 
+#(
+    parameter REG_ADDR_WIDTH      = 5
+)
+(
+	clk,
+	rst_n,
+	debug_req_i,
+	debug_gnt_o,
+	debug_rvalid_o,
+	debug_addr_i,
+	debug_we_i,
+	debug_wdata_i,
+	debug_rdata_o,
+	debug_halted_o,
+	debug_halt_i,
+	debug_resume_i,
+	settings_o,
+	trap_i,
+	exc_cause_i,
+	stall_o,
+	dbg_req_o,
+	dbg_ack_i,
+	regfile_rreq_o,
+	regfile_raddr_o,
+	regfile_rdata_i,
+	regfile_wreq_o,
+	regfile_waddr_o,
+	regfile_wdata_o,
+	csr_req_o,
+	csr_addr_o,
+	csr_we_o,
+	csr_wdata_o,
+	csr_rdata_i,
+	pc_if_i,
+	pc_id_i,
+	instr_valid_id_i,
+	sleeping_i,
+	jump_req_o,
+	jump_addr_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	//parameter REG_ADDR_WIDTH = 5;
+	input wire clk;
+	input wire rst_n;
+	input wire debug_req_i;
+	output reg debug_gnt_o;
+	output reg debug_rvalid_o;
+	input wire [14:0] debug_addr_i;
+	input wire debug_we_i;
+	input wire [31:0] debug_wdata_i;
+	output reg [31:0] debug_rdata_o;
+	output reg debug_halted_o;
+	input wire debug_halt_i;
+	input wire debug_resume_i;
+	output wire [DBG_SETS_W - 1:0] settings_o;
+	input wire trap_i;
+	input wire [5:0] exc_cause_i;
+	output reg stall_o;
+	output reg dbg_req_o;
+	input wire dbg_ack_i;
+	output wire regfile_rreq_o;
+	output wire [REG_ADDR_WIDTH - 1:0] regfile_raddr_o;
+	input wire [31:0] regfile_rdata_i;
+	output wire regfile_wreq_o;
+	output wire [REG_ADDR_WIDTH - 1:0] regfile_waddr_o;
+	output wire [31:0] regfile_wdata_o;
+	output wire csr_req_o;
+	output wire [11:0] csr_addr_o;
+	output reg csr_we_o;
+	output wire [31:0] csr_wdata_o;
+	input wire [31:0] csr_rdata_i;
+	input wire [31:0] pc_if_i;
+	input wire [31:0] pc_id_i;
+	input wire instr_valid_id_i;
+	input wire sleeping_i;
+	output wire jump_req_o;
+	output wire [31:0] jump_addr_o;
+	reg [2:0] rdata_sel_q;
+	reg [2:0] rdata_sel_n;
+	reg [0:0] state_q;
+	reg [0:0] state_n;
+	reg [DBG_SETS_W - 1:0] settings_q;
+	reg [DBG_SETS_W - 1:0] settings_n;
+	reg [14:0] addr_q;
+	reg [31:0] wdata_q;
+	reg regfile_rreq_q;
+	reg regfile_rreq_n;
+	reg jump_req_q;
+	reg jump_req_n;
+	reg csr_req_q;
+	reg csr_req_n;
+	reg regfile_wreq;
+	reg [1:0] stall_cs;
+	reg [1:0] stall_ns;
+	reg [31:0] dbg_rdata;
+	reg dbg_resume;
+	reg dbg_halt;
+	reg [5:0] dbg_cause_q;
+	reg [5:0] dbg_cause_n;
+	reg dbg_ssth_q;
+	reg dbg_ssth_n;
+	reg ssth_clear;
+	wire [31:0] ppc_int;
+	wire [31:0] npc_int;
+	always @(*) begin
+		rdata_sel_n = 3'd0;
+		state_n = 1'd0;
+		debug_gnt_o = 1'b0;
+		regfile_rreq_n = 1'b0;
+		regfile_wreq = 1'b0;
+		csr_req_n = 1'b0;
+		csr_we_o = 1'b0;
+		jump_req_n = 1'b0;
+		dbg_resume = 1'b0;
+		dbg_halt = 1'b0;
+		settings_n = settings_q;
+		ssth_clear = 1'b0;
+		if (debug_req_i)
+			if (debug_we_i) begin
+				if (debug_addr_i[14]) begin
+					if (state_q == 1'd0) begin
+						debug_gnt_o = 1'b0;
+						state_n = 1'd1;
+						if (debug_halted_o)
+							csr_req_n = 1'b1;
+					end
+					else begin
+						debug_gnt_o = 1'b1;
+						state_n = 1'd0;
+						csr_we_o = 1'b1;
+					end
+				end
+				else
+					case (debug_addr_i[13:8])
+						6'b000000: begin
+							debug_gnt_o = 1'b1;
+							case (debug_addr_i[6:2])
+								5'b00000: begin
+									if (debug_wdata_i[16]) begin
+										if (~debug_halted_o)
+											dbg_halt = 1'b1;
+									end
+									else if (debug_halted_o)
+										dbg_resume = 1'b1;
+									settings_n[DBG_SETS_SSTE] = debug_wdata_i[0];
+								end
+								5'b00001: ssth_clear = debug_wdata_i[0];
+								5'b00010: begin
+									settings_n[DBG_SETS_ECALL] = debug_wdata_i[11];
+									settings_n[DBG_SETS_ELSU] = debug_wdata_i[7] | debug_wdata_i[5];
+									settings_n[DBG_SETS_EBRK] = debug_wdata_i[3];
+									settings_n[DBG_SETS_EILL] = debug_wdata_i[2];
+								end
+								default:
+									;
+							endcase
+						end
+						6'b100000: begin
+							debug_gnt_o = 1'b1;
+							if (debug_halted_o)
+								case (debug_addr_i[6:2])
+									5'b00000: jump_req_n = 1'b1;
+									default:
+										;
+								endcase
+						end
+						6'b000100: begin
+							debug_gnt_o = 1'b1;
+							if (debug_halted_o)
+								regfile_wreq = 1'b1;
+						end
+						default: debug_gnt_o = 1'b1;
+					endcase
+			end
+			else if (debug_addr_i[14]) begin
+				debug_gnt_o = 1'b1;
+				if (debug_halted_o) begin
+					csr_req_n = 1'b1;
+					rdata_sel_n = 3'd1;
+				end
+			end
+			else
+				case (debug_addr_i[13:8])
+					6'b000000: begin
+						debug_gnt_o = 1'b1;
+						rdata_sel_n = 3'd3;
+					end
+					6'b100000: begin
+						debug_gnt_o = 1'b1;
+						rdata_sel_n = 3'd4;
+					end
+					6'b000100: begin
+						debug_gnt_o = 1'b1;
+						if (debug_halted_o) begin
+							regfile_rreq_n = 1'b1;
+							rdata_sel_n = 3'd2;
+						end
+					end
+					default: debug_gnt_o = 1'b1;
+				endcase
+	end
+	always @(*) begin
+		dbg_rdata = 1'sb0;
+		case (rdata_sel_q)
+			3'd3:
+				case (addr_q[6:2])
+					5'h00: dbg_rdata[31:0] = {15'b000000000000000, debug_halted_o, 15'b000000000000000, settings_q[DBG_SETS_SSTE]};
+					5'h01: dbg_rdata[31:0] = {15'b000000000000000, sleeping_i, 15'b000000000000000, dbg_ssth_q};
+					5'h02: begin
+						dbg_rdata[31:16] = 1'sb0;
+						dbg_rdata[15:12] = 1'sb0;
+						dbg_rdata[11] = settings_q[DBG_SETS_ECALL];
+						dbg_rdata[10:8] = 1'sb0;
+						dbg_rdata[7] = settings_q[DBG_SETS_ELSU];
+						dbg_rdata[6] = 1'b0;
+						dbg_rdata[5] = settings_q[DBG_SETS_ELSU];
+						dbg_rdata[4] = 1'b0;
+						dbg_rdata[3] = settings_q[DBG_SETS_EBRK];
+						dbg_rdata[2] = settings_q[DBG_SETS_EILL];
+						dbg_rdata[1:0] = 1'sb0;
+					end
+					5'h03: dbg_rdata = {dbg_cause_q[5], 26'b00000000000000000000000000, dbg_cause_q[4:0]};
+					5'h10: dbg_rdata = 1'sb0;
+					5'h12: dbg_rdata = 1'sb0;
+					5'h14: dbg_rdata = 1'sb0;
+					5'h16: dbg_rdata = 1'sb0;
+					5'h18: dbg_rdata = 1'sb0;
+					5'h1a: dbg_rdata = 1'sb0;
+					5'h1c: dbg_rdata = 1'sb0;
+					5'h1e: dbg_rdata = 1'sb0;
+					default:
+						;
+				endcase
+			3'd4:
+				case (addr_q[2:2])
+					1'b0: dbg_rdata = npc_int;
+					1'b1: dbg_rdata = ppc_int;
+					default:
+						;
+				endcase
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		debug_rdata_o = 1'sb0;
+		case (rdata_sel_q)
+			3'd1: debug_rdata_o = csr_rdata_i;
+			3'd2: debug_rdata_o = regfile_rdata_i;
+			3'd3: debug_rdata_o = dbg_rdata;
+			3'd4: debug_rdata_o = dbg_rdata;
+			default:
+				;
+		endcase
+	end
+	always @(posedge clk or negedge rst_n)
+		if (~rst_n)
+			debug_rvalid_o <= 1'b0;
+		else
+			debug_rvalid_o <= debug_gnt_o;
+	always @(*) begin
+		stall_ns = stall_cs;
+		dbg_req_o = 1'b0;
+		stall_o = 1'b0;
+		debug_halted_o = 1'b0;
+		dbg_cause_n = dbg_cause_q;
+		dbg_ssth_n = dbg_ssth_q;
+		case (stall_cs)
+			2'd0: begin
+				dbg_ssth_n = 1'b0;
+				if ((dbg_halt | debug_halt_i) | trap_i) begin
+					dbg_req_o = 1'b1;
+					stall_ns = 2'd1;
+					if (trap_i) begin
+						if (settings_q[DBG_SETS_SSTE])
+							dbg_ssth_n = 1'b1;
+						dbg_cause_n = exc_cause_i;
+					end
+					else
+						dbg_cause_n = DBG_CAUSE_HALT;
+				end
+			end
+			2'd1: begin
+				dbg_req_o = 1'b1;
+				if (dbg_ack_i)
+					stall_ns = 2'd2;
+				if (dbg_resume | debug_resume_i)
+					stall_ns = 2'd0;
+			end
+			2'd2: begin
+				stall_o = 1'b1;
+				debug_halted_o = 1'b1;
+				if (dbg_resume | debug_resume_i) begin
+					stall_ns = 2'd0;
+					stall_o = 1'b0;
+				end
+			end
+		endcase
+		if (ssth_clear)
+			dbg_ssth_n = 1'b0;
+	end
+	always @(posedge clk or negedge rst_n)
+		if (~rst_n) begin
+			stall_cs <= 2'd0;
+			dbg_cause_q <= DBG_CAUSE_HALT;
+			dbg_ssth_q <= 1'b0;
+		end
+		else begin
+			stall_cs <= stall_ns;
+			dbg_cause_q <= dbg_cause_n;
+			dbg_ssth_q <= dbg_ssth_n;
+		end
+	assign ppc_int = pc_id_i;
+	assign npc_int = pc_if_i;
+	always @(posedge clk or negedge rst_n)
+		if (~rst_n) begin
+			addr_q <= 1'sb0;
+			wdata_q <= 1'sb0;
+			state_q <= 1'd0;
+			rdata_sel_q <= 3'd0;
+			regfile_rreq_q <= 1'b0;
+			csr_req_q <= 1'b0;
+			jump_req_q <= 1'b0;
+			settings_q <= 1'b0;
+		end
+		else begin
+			settings_q <= settings_n;
+			if (debug_req_i) begin
+				addr_q <= debug_addr_i;
+				wdata_q <= debug_wdata_i;
+				state_q <= state_n;
+			end
+			if (debug_req_i | debug_rvalid_o) begin
+				regfile_rreq_q <= regfile_rreq_n;
+				csr_req_q <= csr_req_n;
+				jump_req_q <= jump_req_n;
+				rdata_sel_q <= rdata_sel_n;
+			end
+		end
+	assign regfile_rreq_o = regfile_rreq_q;
+	assign regfile_raddr_o = addr_q[6:2];
+	assign regfile_wreq_o = regfile_wreq;
+	assign regfile_waddr_o = debug_addr_i[6:2];
+	assign regfile_wdata_o = debug_wdata_i;
+	assign csr_req_o = csr_req_q;
+	assign csr_addr_o = addr_q[13:2];
+	assign csr_wdata_o = wdata_q;
+	assign jump_req_o = jump_req_q;
+	assign jump_addr_o = wdata_q;
+	assign settings_o = settings_q;
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_decoder.v b/verilog/rtl/ips/zero-riscy/zeroriscy_decoder.v
new file mode 100644
index 0000000..38b064a
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_decoder.v
@@ -0,0 +1,663 @@
+module zeroriscy_decoder 
+#(
+  parameter RV32M      = 1
+)
+(
+	deassert_we_i,
+	data_misaligned_i,
+	branch_mux_i,
+	jump_mux_i,
+	illegal_insn_o,
+	ebrk_insn_o,
+	mret_insn_o,
+	ecall_insn_o,
+	pipe_flush_o,
+	instr_rdata_i,
+	illegal_c_insn_i,
+	alu_operator_o,
+	alu_op_a_mux_sel_o,
+	alu_op_b_mux_sel_o,
+	imm_a_mux_sel_o,
+	imm_b_mux_sel_o,
+	mult_int_en_o,
+	div_int_en_o,
+	multdiv_operator_o,
+	multdiv_signed_mode_o,
+	regfile_we_o,
+	csr_access_o,
+	csr_op_o,
+	csr_status_o,
+	data_req_o,
+	data_we_o,
+	data_type_o,
+	data_sign_extension_o,
+	data_reg_offset_o,
+	data_load_event_o,
+	jump_in_id_o,
+	branch_in_id_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	//parameter RV32M = 1;
+	input wire deassert_we_i;
+	input wire data_misaligned_i;
+	input wire branch_mux_i;
+	input wire jump_mux_i;
+	output reg illegal_insn_o;
+	output reg ebrk_insn_o;
+	output reg mret_insn_o;
+	output reg ecall_insn_o;
+	output reg pipe_flush_o;
+	input wire [31:0] instr_rdata_i;
+	input wire illegal_c_insn_i;
+	output reg [ALU_OP_WIDTH - 1:0] alu_operator_o;
+	output reg [2:0] alu_op_a_mux_sel_o;
+	output reg [2:0] alu_op_b_mux_sel_o;
+	output reg [0:0] imm_a_mux_sel_o;
+	output reg [3:0] imm_b_mux_sel_o;
+	output wire mult_int_en_o;
+	output wire div_int_en_o;
+	output reg [1:0] multdiv_operator_o;
+	output reg [1:0] multdiv_signed_mode_o;
+	output wire regfile_we_o;
+	output reg csr_access_o;
+	output wire [1:0] csr_op_o;
+	output reg csr_status_o;
+	output wire data_req_o;
+	output reg data_we_o;
+	output reg [1:0] data_type_o;
+	output reg data_sign_extension_o;
+	output reg [1:0] data_reg_offset_o;
+	output reg data_load_event_o;
+	output wire jump_in_id_o;
+	output wire branch_in_id_o;
+	reg regfile_we;
+	reg data_req;
+	reg mult_int_en;
+	reg div_int_en;
+	reg branch_in_id;
+	reg jump_in_id;
+	reg [1:0] csr_op;
+	reg csr_illegal;
+	always @(*) begin
+		jump_in_id = 1'b0;
+		branch_in_id = 1'b0;
+		alu_operator_o = ALU_SLTU;
+		alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
+		alu_op_b_mux_sel_o = OP_B_REGB_OR_FWD;
+		imm_a_mux_sel_o = IMMA_ZERO;
+		imm_b_mux_sel_o = IMMB_I;
+		mult_int_en = 1'b0;
+		div_int_en = 1'b0;
+		multdiv_operator_o = MD_OP_MULL;
+		multdiv_signed_mode_o = 2'b00;
+		regfile_we = 1'b0;
+		csr_access_o = 1'b0;
+		csr_status_o = 1'b0;
+		csr_illegal = 1'b0;
+		csr_op = CSR_OP_NONE;
+		data_we_o = 1'b0;
+		data_type_o = 2'b00;
+		data_sign_extension_o = 1'b0;
+		data_reg_offset_o = 2'b00;
+		data_req = 1'b0;
+		data_load_event_o = 1'b0;
+		illegal_insn_o = 1'b0;
+		ebrk_insn_o = 1'b0;
+		mret_insn_o = 1'b0;
+		ecall_insn_o = 1'b0;
+		pipe_flush_o = 1'b0;
+		case (instr_rdata_i[6:0])
+			OPCODE_JAL: begin
+				jump_in_id = 1'b1;
+				if (jump_mux_i) begin
+					alu_op_a_mux_sel_o = OP_A_CURRPC;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_b_mux_sel_o = IMMB_UJ;
+					alu_operator_o = ALU_ADD;
+					regfile_we = 1'b0;
+				end
+				else begin
+					alu_op_a_mux_sel_o = OP_A_CURRPC;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_b_mux_sel_o = IMMB_PCINCR;
+					alu_operator_o = ALU_ADD;
+					regfile_we = 1'b1;
+				end
+			end
+			OPCODE_JALR: begin
+				jump_in_id = 1'b1;
+				if (jump_mux_i) begin
+					alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_b_mux_sel_o = IMMB_I;
+					alu_operator_o = ALU_ADD;
+					regfile_we = 1'b0;
+				end
+				else begin
+					alu_op_a_mux_sel_o = OP_A_CURRPC;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_b_mux_sel_o = IMMB_PCINCR;
+					alu_operator_o = ALU_ADD;
+					regfile_we = 1'b1;
+				end
+				if (instr_rdata_i[14:12] != 3'b000) begin
+					jump_in_id = 1'b0;
+					regfile_we = 1'b0;
+					illegal_insn_o = 1'b1;
+				end
+			end
+			OPCODE_BRANCH: begin
+				branch_in_id = 1'b1;
+				if (branch_mux_i)
+					case (instr_rdata_i[14:12])
+						3'b000: alu_operator_o = ALU_EQ;
+						3'b001: alu_operator_o = ALU_NE;
+						3'b100: alu_operator_o = ALU_LTS;
+						3'b101: alu_operator_o = ALU_GES;
+						3'b110: alu_operator_o = ALU_LTU;
+						3'b111: alu_operator_o = ALU_GEU;
+						default: illegal_insn_o = 1'b1;
+					endcase
+				else begin
+					alu_op_a_mux_sel_o = OP_A_CURRPC;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_b_mux_sel_o = IMMB_SB;
+					alu_operator_o = ALU_ADD;
+					regfile_we = 1'b0;
+				end
+			end
+			OPCODE_STORE: begin
+				data_req = 1'b1;
+				data_we_o = 1'b1;
+				alu_operator_o = ALU_ADD;
+				if (instr_rdata_i[14] == 1'b0) begin
+					imm_b_mux_sel_o = IMMB_S;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+				end
+				else begin
+					data_req = 1'b0;
+					data_we_o = 1'b0;
+					illegal_insn_o = 1'b1;
+				end
+				case (instr_rdata_i[13:12])
+					2'b00: data_type_o = 2'b10;
+					2'b01: data_type_o = 2'b01;
+					2'b10: data_type_o = 2'b00;
+					default: begin
+						data_req = 1'b0;
+						data_we_o = 1'b0;
+						illegal_insn_o = 1'b1;
+					end
+				endcase
+			end
+			OPCODE_LOAD: begin
+				data_req = 1'b1;
+				regfile_we = 1'b1;
+				data_type_o = 2'b00;
+				alu_operator_o = ALU_ADD;
+				alu_op_b_mux_sel_o = OP_B_IMM;
+				imm_b_mux_sel_o = IMMB_I;
+				data_sign_extension_o = ~instr_rdata_i[14];
+				case (instr_rdata_i[13:12])
+					2'b00: data_type_o = 2'b10;
+					2'b01: data_type_o = 2'b01;
+					2'b10: data_type_o = 2'b00;
+					default: data_type_o = 2'b00;
+				endcase
+				if (instr_rdata_i[14:12] == 3'b111) begin
+					alu_op_b_mux_sel_o = OP_B_REGB_OR_FWD;
+					data_sign_extension_o = ~instr_rdata_i[30];
+					case (instr_rdata_i[31:25])
+						7'b0000000, 7'b0100000: data_type_o = 2'b10;
+						7'b0001000, 7'b0101000: data_type_o = 2'b01;
+						7'b0010000: data_type_o = 2'b00;
+						default: illegal_insn_o = 1'b1;
+					endcase
+				end
+				if (instr_rdata_i[14:12] == 3'b110)
+					data_load_event_o = 1'b1;
+				if (instr_rdata_i[14:12] == 3'b011)
+					illegal_insn_o = 1'b1;
+			end
+			OPCODE_LUI: begin
+				alu_op_a_mux_sel_o = OP_A_IMM;
+				alu_op_b_mux_sel_o = OP_B_IMM;
+				imm_a_mux_sel_o = IMMA_ZERO;
+				imm_b_mux_sel_o = IMMB_U;
+				alu_operator_o = ALU_ADD;
+				regfile_we = 1'b1;
+			end
+			OPCODE_AUIPC: begin
+				alu_op_a_mux_sel_o = OP_A_CURRPC;
+				alu_op_b_mux_sel_o = OP_B_IMM;
+				imm_b_mux_sel_o = IMMB_U;
+				alu_operator_o = ALU_ADD;
+				regfile_we = 1'b1;
+			end
+			OPCODE_OPIMM: begin
+				alu_op_b_mux_sel_o = OP_B_IMM;
+				imm_b_mux_sel_o = IMMB_I;
+				regfile_we = 1'b1;
+				case (instr_rdata_i[14:12])
+					3'b000: alu_operator_o = ALU_ADD;
+					3'b010: alu_operator_o = ALU_SLTS;
+					3'b011: alu_operator_o = ALU_SLTU;
+					3'b100: alu_operator_o = ALU_XOR;
+					3'b110: alu_operator_o = ALU_OR;
+					3'b111: alu_operator_o = ALU_AND;
+					3'b001: begin
+						alu_operator_o = ALU_SLL;
+						if (instr_rdata_i[31:25] != 7'b0000000)
+							illegal_insn_o = 1'b1;
+					end
+					3'b101:
+						if (instr_rdata_i[31:25] == 7'b0000000)
+							alu_operator_o = ALU_SRL;
+						else if (instr_rdata_i[31:25] == 7'b0100000)
+							alu_operator_o = ALU_SRA;
+						else
+							illegal_insn_o = 1'b1;
+					default: illegal_insn_o = 1'b1;
+				endcase
+			end
+			OPCODE_OP: begin
+				regfile_we = 1'b1;
+				if (instr_rdata_i[31])
+					illegal_insn_o = 1'b1;
+				else if (~instr_rdata_i[28])
+					case ({instr_rdata_i[30:25], instr_rdata_i[14:12]})
+						9'b000000000: alu_operator_o = ALU_ADD;
+						9'b100000000: alu_operator_o = ALU_SUB;
+						9'b000000010: alu_operator_o = ALU_SLTS;
+						9'b000000011: alu_operator_o = ALU_SLTU;
+						9'b000000100: alu_operator_o = ALU_XOR;
+						9'b000000110: alu_operator_o = ALU_OR;
+						9'b000000111: alu_operator_o = ALU_AND;
+						9'b000000001: alu_operator_o = ALU_SLL;
+						9'b000000101: alu_operator_o = ALU_SRL;
+						9'b100000101: alu_operator_o = ALU_SRA;
+						9'b000001000: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_MULL;
+							mult_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b00;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001001: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_MULH;
+							mult_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b11;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001010: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_MULH;
+							mult_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b01;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001011: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_MULH;
+							mult_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b00;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001100: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_DIV;
+							div_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b11;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001101: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_DIV;
+							div_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b00;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001110: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_REM;
+							div_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b11;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						9'b000001111: begin
+							alu_operator_o = ALU_ADD;
+							multdiv_operator_o = MD_OP_REM;
+							div_int_en = 1'b1;
+							multdiv_signed_mode_o = 2'b00;
+							illegal_insn_o = (RV32M ? 1'b0 : 1'b1);
+						end
+						default: illegal_insn_o = 1'b1;
+					endcase
+			end
+			OPCODE_SYSTEM:
+				if (instr_rdata_i[14:12] == 3'b000)
+					case (instr_rdata_i[31:20])
+						12'h000: ecall_insn_o = 1'b1;
+						12'h001: ebrk_insn_o = 1'b1;
+						12'h302: mret_insn_o = 1'b1;
+						12'h105: pipe_flush_o = 1'b1;
+						default: illegal_insn_o = 1'b1;
+					endcase
+				else begin
+					csr_access_o = 1'b1;
+					regfile_we = 1'b1;
+					alu_op_b_mux_sel_o = OP_B_IMM;
+					imm_a_mux_sel_o = IMMA_Z;
+					imm_b_mux_sel_o = IMMB_I;
+					if (instr_rdata_i[14] == 1'b1)
+						alu_op_a_mux_sel_o = OP_A_IMM;
+					else
+						alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
+					case (instr_rdata_i[13:12])
+						2'b01: csr_op = CSR_OP_WRITE;
+						2'b10: csr_op = CSR_OP_SET;
+						2'b11: csr_op = CSR_OP_CLEAR;
+						default: csr_illegal = 1'b1;
+					endcase
+					if (~csr_illegal)
+						if (instr_rdata_i[31:20] == 12'h300)
+							csr_status_o = 1'b1;
+					illegal_insn_o = csr_illegal;
+				end
+			default: illegal_insn_o = 1'b1;
+		endcase
+		if (illegal_c_insn_i)
+			illegal_insn_o = 1'b1;
+		if (data_misaligned_i == 1'b1) begin
+			alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
+			alu_op_b_mux_sel_o = OP_B_IMM;
+			imm_b_mux_sel_o = IMMB_PCINCR;
+			regfile_we = 1'b0;
+		end
+	end
+	assign regfile_we_o = (deassert_we_i ? 1'b0 : regfile_we);
+	assign mult_int_en_o = (RV32M ? (deassert_we_i ? 1'b0 : mult_int_en) : 1'b0);
+	assign div_int_en_o = (RV32M ? (deassert_we_i ? 1'b0 : div_int_en) : 1'b0);
+	assign data_req_o = (deassert_we_i ? 1'b0 : data_req);
+	assign csr_op_o = (deassert_we_i ? CSR_OP_NONE : csr_op);
+	assign jump_in_id_o = (deassert_we_i ? 1'b0 : jump_in_id);
+	assign branch_in_id_o = (deassert_we_i ? 1'b0 : branch_in_id);
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_ex_block.v b/verilog/rtl/ips/zero-riscy/zeroriscy_ex_block.v
new file mode 100644
index 0000000..a1066a0
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_ex_block.v
@@ -0,0 +1,386 @@
+module zeroriscy_ex_block 
+#(
+  parameter RV32M      = 1
+)
+(
+	clk,
+	rst_n,
+	alu_operator_i,
+	multdiv_operator_i,
+	mult_en_i,
+	div_en_i,
+	alu_operand_a_i,
+	alu_operand_b_i,
+	multdiv_signed_mode_i,
+	multdiv_operand_a_i,
+	multdiv_operand_b_i,
+	alu_adder_result_ex_o,
+	regfile_wdata_ex_o,
+	jump_target_o,
+	branch_decision_o,
+	lsu_en_i,
+	lsu_ready_ex_i,
+	ex_ready_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	//parameter RV32M = 1;
+	input wire clk;
+	input wire rst_n;
+	input wire [ALU_OP_WIDTH - 1:0] alu_operator_i;
+	input wire [1:0] multdiv_operator_i;
+	input wire mult_en_i;
+	input wire div_en_i;
+	input wire [31:0] alu_operand_a_i;
+	input wire [31:0] alu_operand_b_i;
+	input wire [1:0] multdiv_signed_mode_i;
+	input wire [31:0] multdiv_operand_a_i;
+	input wire [31:0] multdiv_operand_b_i;
+	output wire [31:0] alu_adder_result_ex_o;
+	output wire [31:0] regfile_wdata_ex_o;
+	output wire [31:0] jump_target_o;
+	output wire branch_decision_o;
+	input wire lsu_en_i;
+	input wire lsu_ready_ex_i;
+	output reg ex_ready_o;
+	localparam MULT_TYPE = 1;
+	wire [31:0] alu_result;
+	wire [31:0] multdiv_result;
+	wire [32:0] multdiv_alu_operand_b;
+	wire [32:0] multdiv_alu_operand_a;
+	wire [33:0] alu_adder_result_ext;
+	wire alu_cmp_result;
+	wire alu_is_equal_result;
+	wire multdiv_ready;
+	wire multdiv_en_sel;
+	wire multdiv_en;
+	generate
+		if (RV32M) begin : genblk1
+			assign multdiv_en_sel = div_en_i;
+			assign multdiv_en = mult_en_i | div_en_i;
+		end
+		else begin : genblk1
+			assign multdiv_en_sel = 1'b0;
+			assign multdiv_en = 1'b0;
+		end
+	endgenerate
+	assign regfile_wdata_ex_o = (multdiv_en ? multdiv_result : alu_result);
+	assign branch_decision_o = alu_cmp_result;
+	assign jump_target_o = alu_adder_result_ex_o;
+	zeroriscy_alu alu_i(
+		.operator_i(alu_operator_i),
+		.operand_a_i(alu_operand_a_i),
+		.operand_b_i(alu_operand_b_i),
+		.multdiv_operand_a_i(multdiv_alu_operand_a),
+		.multdiv_operand_b_i(multdiv_alu_operand_b),
+		.multdiv_en_i(multdiv_en_sel),
+		.adder_result_o(alu_adder_result_ex_o),
+		.adder_result_ext_o(alu_adder_result_ext),
+		.result_o(alu_result),
+		.comparison_result_o(alu_cmp_result),
+		.is_equal_result_o(alu_is_equal_result)
+	);
+	generate
+		if (1) begin : multdiv_fast
+			zeroriscy_multdiv_fast multdiv_i(
+				.clk(clk),
+				.rst_n(rst_n),
+				.mult_en_i(mult_en_i),
+				.div_en_i(div_en_i),
+				.operator_i(multdiv_operator_i),
+				.signed_mode_i(multdiv_signed_mode_i),
+				.op_a_i(multdiv_operand_a_i),
+				.op_b_i(multdiv_operand_b_i),
+				.alu_operand_a_o(multdiv_alu_operand_a),
+				.alu_operand_b_o(multdiv_alu_operand_b),
+				.alu_adder_ext_i(alu_adder_result_ext),
+				.alu_adder_i(alu_adder_result_ex_o),
+				.equal_to_zero(alu_is_equal_result),
+				.ready_o(multdiv_ready),
+				.multdiv_result_o(multdiv_result)
+			);
+		end
+	endgenerate
+	always @(*)
+		case (1'b1)
+			multdiv_en: ex_ready_o = multdiv_ready;
+			lsu_en_i: ex_ready_o = lsu_ready_ex_i;
+			default: ex_ready_o = 1'b1;
+		endcase
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_fetch_fifo.v b/verilog/rtl/ips/zero-riscy/zeroriscy_fetch_fifo.v
new file mode 100644
index 0000000..3b736e3
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_fetch_fifo.v
@@ -0,0 +1,142 @@
+module zeroriscy_fetch_fifo (
+	clk,
+	rst_n,
+	clear_i,
+	in_addr_i,
+	in_rdata_i,
+	in_valid_i,
+	in_ready_o,
+	out_valid_o,
+	out_ready_i,
+	out_rdata_o,
+	out_addr_o,
+	out_valid_stored_o
+);
+	input wire clk;
+	input wire rst_n;
+	input wire clear_i;
+	input wire [31:0] in_addr_i;
+	input wire [31:0] in_rdata_i;
+	input wire in_valid_i;
+	output wire in_ready_o;
+	output reg out_valid_o;
+	input wire out_ready_i;
+	output reg [31:0] out_rdata_o;
+	output wire [31:0] out_addr_o;
+	output reg out_valid_stored_o;
+	localparam DEPTH = 3;
+	reg [95:0] addr_n;
+	reg [95:0] addr_int;
+	reg [95:0] addr_Q;
+	reg [95:0] rdata_n;
+	reg [95:0] rdata_int;
+	reg [95:0] rdata_Q;
+	reg [2:0] valid_n;
+	reg [2:0] valid_int;
+	reg [2:0] valid_Q;
+	wire [31:0] addr_next;
+	wire [31:0] rdata;
+	wire [31:0] rdata_unaligned;
+	wire valid;
+	wire valid_unaligned;
+	wire aligned_is_compressed;
+	wire unaligned_is_compressed;
+	wire aligned_is_compressed_st;
+	wire unaligned_is_compressed_st;
+	assign rdata = (valid_Q[0] ? rdata_Q[0+:32] : in_rdata_i);
+	assign valid = valid_Q[0] || in_valid_i;
+	assign rdata_unaligned = (valid_Q[1] ? {rdata_Q[47-:16], rdata[31:16]} : {in_rdata_i[15:0], rdata[31:16]});
+	assign valid_unaligned = valid_Q[1] || (valid_Q[0] && in_valid_i);
+	assign unaligned_is_compressed = rdata[17:16] != 2'b11;
+	assign aligned_is_compressed = rdata[1:0] != 2'b11;
+	assign unaligned_is_compressed_st = rdata_Q[17-:2] != 2'b11;
+	assign aligned_is_compressed_st = rdata_Q[1-:2] != 2'b11;
+	always @(*)
+		if (out_addr_o[1]) begin
+			out_rdata_o = rdata_unaligned;
+			if (unaligned_is_compressed)
+				out_valid_o = valid;
+			else
+				out_valid_o = valid_unaligned;
+		end
+		else begin
+			out_rdata_o = rdata;
+			out_valid_o = valid;
+		end
+	assign out_addr_o = (valid_Q[0] ? addr_Q[0+:32] : in_addr_i);
+	always @(*) begin
+		out_valid_stored_o = 1'b1;
+		if (out_addr_o[1]) begin
+			if (unaligned_is_compressed_st)
+				out_valid_stored_o = 1'b1;
+			else
+				out_valid_stored_o = valid_Q[1];
+		end
+		else
+			out_valid_stored_o = valid_Q[0];
+	end
+	assign in_ready_o = ~valid_Q[1];
+	always @(*) begin : sv2v_autoblock_1
+		reg [0:1] _sv2v_jump;
+		_sv2v_jump = 2'b00;
+		begin : sv2v_autoblock_2
+			reg signed [31:0] j;
+			addr_int = addr_Q;
+			rdata_int = rdata_Q;
+			valid_int = valid_Q;
+			if (in_valid_i) begin : sv2v_autoblock_3
+				reg signed [31:0] _sv2v_value_on_break;
+				for (j = 0; j < DEPTH; j = j + 1)
+					if (_sv2v_jump < 2'b10) begin
+						_sv2v_jump = 2'b00;
+						if (~valid_Q[j]) begin
+							addr_int[j * 32+:32] = in_addr_i;
+							rdata_int[j * 32+:32] = in_rdata_i;
+							valid_int[j] = 1'b1;
+							_sv2v_jump = 2'b10;
+						end
+						_sv2v_value_on_break = j;
+					end
+				if (!(_sv2v_jump < 2'b10))
+					j = _sv2v_value_on_break;
+				if (_sv2v_jump != 2'b11)
+					_sv2v_jump = 2'b00;
+			end
+		end
+	end
+	assign addr_next = {addr_int[31-:30], 2'b00} + 32'h00000004;
+	always @(*) begin
+		addr_n = addr_int;
+		rdata_n = rdata_int;
+		valid_n = valid_int;
+		if (out_ready_i && out_valid_o)
+			if (addr_int[1]) begin
+				if (unaligned_is_compressed)
+					addr_n[0+:32] = {addr_next[31:2], 2'b00};
+				else
+					addr_n[0+:32] = {addr_next[31:2], 2'b10};
+				rdata_n = {32'b00000000000000000000000000000000, rdata_int[32+:64]};
+				valid_n = {1'b0, valid_int[2:1]};
+			end
+			else if (aligned_is_compressed)
+				addr_n[0+:32] = {addr_int[31-:30], 2'b10};
+			else begin
+				addr_n[0+:32] = {addr_next[31:2], 2'b00};
+				rdata_n = {32'b00000000000000000000000000000000, rdata_int[32+:64]};
+				valid_n = {1'b0, valid_int[2:1]};
+			end
+	end
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			addr_Q <= {DEPTH {32'b00000000000000000000000000000000}};
+			rdata_Q <= {DEPTH {32'b00000000000000000000000000000000}};
+			valid_Q <= 1'sb0;
+		end
+		else if (clear_i)
+			valid_Q <= 1'sb0;
+		else begin
+			addr_Q <= addr_n;
+			rdata_Q <= rdata_n;
+			valid_Q <= valid_n;
+		end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_id_stage.v b/verilog/rtl/ips/zero-riscy/zeroriscy_id_stage.v
new file mode 100644
index 0000000..dcc270f
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_id_stage.v
@@ -0,0 +1,803 @@
+module zeroriscy_id_stage (
+	clk,
+	rst_n,
+	test_en_i,
+	fetch_enable_i,
+	ctrl_busy_o,
+	core_ctrl_firstfetch_o,
+	is_decoding_o,
+	instr_valid_i,
+	instr_rdata_i,
+	instr_req_o,
+	branch_in_ex_o,
+	branch_decision_i,
+	clear_instr_valid_o,
+	pc_set_o,
+	pc_mux_o,
+	exc_pc_mux_o,
+	illegal_c_insn_i,
+	is_compressed_i,
+	pc_id_i,
+	halt_if_o,
+	id_ready_o,
+	ex_ready_i,
+	id_valid_o,
+	alu_operator_ex_o,
+	alu_operand_a_ex_o,
+	alu_operand_b_ex_o,
+	mult_en_ex_o,
+	div_en_ex_o,
+	multdiv_operator_ex_o,
+	multdiv_signed_mode_ex_o,
+	multdiv_operand_a_ex_o,
+	multdiv_operand_b_ex_o,
+	csr_access_ex_o,
+	csr_op_ex_o,
+	csr_cause_o,
+	csr_save_if_o,
+	csr_save_id_o,
+	csr_restore_mret_id_o,
+	csr_save_cause_o,
+	data_req_ex_o,
+	data_we_ex_o,
+	data_type_ex_o,
+	data_sign_ext_ex_o,
+	data_reg_offset_ex_o,
+	data_load_event_ex_o,
+	data_wdata_ex_o,
+	data_misaligned_i,
+	misaligned_addr_i,
+	irq_i,
+	irq_id_i,
+	m_irq_enable_i,
+	irq_ack_o,
+	irq_id_o,
+	exc_cause_o,
+	lsu_load_err_i,
+	lsu_store_err_i,
+	dbg_settings_i,
+	dbg_req_i,
+	dbg_ack_o,
+	dbg_stall_i,
+	dbg_trap_o,
+	dbg_reg_rreq_i,
+	dbg_reg_raddr_i,
+	dbg_reg_rdata_o,
+	dbg_reg_wreq_i,
+	dbg_reg_waddr_i,
+	dbg_reg_wdata_i,
+	dbg_jump_req_i,
+	regfile_wdata_lsu_i,
+	regfile_wdata_ex_i,
+	csr_rdata_i,
+	perf_jump_o,
+	perf_branch_o,
+	perf_tbranch_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	parameter RV32M = 1;
+	parameter RV32E = 0;
+	input wire clk;
+	input wire rst_n;
+	input wire test_en_i;
+	input wire fetch_enable_i;
+	output wire ctrl_busy_o;
+	output wire core_ctrl_firstfetch_o;
+	output wire is_decoding_o;
+	input wire instr_valid_i;
+	input wire [31:0] instr_rdata_i;
+	output wire instr_req_o;
+	output wire branch_in_ex_o;
+	input wire branch_decision_i;
+	output wire clear_instr_valid_o;
+	output wire pc_set_o;
+	output wire [2:0] pc_mux_o;
+	output wire [1:0] exc_pc_mux_o;
+	input wire illegal_c_insn_i;
+	input wire is_compressed_i;
+	input wire [31:0] pc_id_i;
+	output wire halt_if_o;
+	output wire id_ready_o;
+	input wire ex_ready_i;
+	output wire id_valid_o;
+	output wire [ALU_OP_WIDTH - 1:0] alu_operator_ex_o;
+	output wire [31:0] alu_operand_a_ex_o;
+	output wire [31:0] alu_operand_b_ex_o;
+	output wire mult_en_ex_o;
+	output wire div_en_ex_o;
+	output wire [1:0] multdiv_operator_ex_o;
+	output wire [1:0] multdiv_signed_mode_ex_o;
+	output wire [31:0] multdiv_operand_a_ex_o;
+	output wire [31:0] multdiv_operand_b_ex_o;
+	output wire csr_access_ex_o;
+	output wire [1:0] csr_op_ex_o;
+	output wire [5:0] csr_cause_o;
+	output wire csr_save_if_o;
+	output wire csr_save_id_o;
+	output wire csr_restore_mret_id_o;
+	output wire csr_save_cause_o;
+	output wire data_req_ex_o;
+	output wire data_we_ex_o;
+	output wire [1:0] data_type_ex_o;
+	output wire data_sign_ext_ex_o;
+	output wire [1:0] data_reg_offset_ex_o;
+	output wire data_load_event_ex_o;
+	output wire [31:0] data_wdata_ex_o;
+	input wire data_misaligned_i;
+	input wire [31:0] misaligned_addr_i;
+	input wire irq_i;
+	input wire [4:0] irq_id_i;
+	input wire m_irq_enable_i;
+	output wire irq_ack_o;
+	output wire [4:0] irq_id_o;
+	output wire [5:0] exc_cause_o;
+	input wire lsu_load_err_i;
+	input wire lsu_store_err_i;
+	input wire [DBG_SETS_W - 1:0] dbg_settings_i;
+	input wire dbg_req_i;
+	output wire dbg_ack_o;
+	input wire dbg_stall_i;
+	output wire dbg_trap_o;
+	input wire dbg_reg_rreq_i;
+	input wire [4:0] dbg_reg_raddr_i;
+	output wire [31:0] dbg_reg_rdata_o;
+	input wire dbg_reg_wreq_i;
+	input wire [4:0] dbg_reg_waddr_i;
+	input wire [31:0] dbg_reg_wdata_i;
+	input wire dbg_jump_req_i;
+	input wire [31:0] regfile_wdata_lsu_i;
+	input wire [31:0] regfile_wdata_ex_i;
+	input wire [31:0] csr_rdata_i;
+	output wire perf_jump_o;
+	output reg perf_branch_o;
+	output wire perf_tbranch_o;
+	wire [31:0] instr;
+	wire deassert_we;
+	wire illegal_insn_dec;
+	wire illegal_reg_rv32e;
+	wire ebrk_insn;
+	wire mret_insn_dec;
+	wire ecall_insn_dec;
+	wire pipe_flush_dec;
+	wire branch_taken_ex;
+	wire branch_in_id;
+	reg branch_set_n;
+	reg branch_set_q;
+	reg branch_mux_dec;
+	reg jump_set;
+	reg jump_mux_dec;
+	wire jump_in_id;
+	reg instr_multicyle;
+	reg load_stall;
+	reg multdiv_stall;
+	reg branch_stall;
+	reg jump_stall;
+	wire halt_id;
+	reg regfile_we;
+	reg select_data_rf;
+	wire [31:0] imm_i_type;
+	wire [31:0] imm_iz_type;
+	wire [31:0] imm_s_type;
+	wire [31:0] imm_sb_type;
+	wire [31:0] imm_u_type;
+	wire [31:0] imm_uj_type;
+	wire [31:0] imm_z_type;
+	wire [31:0] imm_s2_type;
+	wire [31:0] imm_bi_type;
+	wire [31:0] imm_s3_type;
+	wire [31:0] imm_vs_type;
+	wire [31:0] imm_vu_type;
+	reg [31:0] imm_a;
+	reg [31:0] imm_b;
+	wire irq_req_ctrl;
+	wire [4:0] irq_id_ctrl;
+	wire exc_ack;
+	wire exc_kill;
+	wire [4:0] regfile_addr_ra_id;
+	wire [4:0] regfile_addr_rb_id;
+	wire [4:0] regfile_alu_waddr_id;
+	wire regfile_we_id;
+	wire [31:0] regfile_data_ra_id;
+	wire [31:0] regfile_data_rb_id;
+	wire [ALU_OP_WIDTH - 1:0] alu_operator;
+	wire [2:0] alu_op_a_mux_sel;
+	wire [2:0] alu_op_b_mux_sel;
+	wire [0:0] imm_a_mux_sel;
+	wire [3:0] imm_b_mux_sel;
+	wire mult_int_en;
+	wire div_int_en;
+	wire multdiv_int_en;
+	wire [1:0] multdiv_operator;
+	wire [1:0] multdiv_signed_mode;
+	wire data_we_id;
+	wire [1:0] data_type_id;
+	wire data_sign_ext_id;
+	wire [1:0] data_reg_offset_id;
+	wire data_req_id;
+	wire data_load_event_id;
+	wire csr_access;
+	wire [1:0] csr_op;
+	wire csr_status;
+	wire [1:0] operand_a_fw_mux_sel;
+	reg [31:0] operand_a_fw_id;
+	wire [31:0] operand_b_fw_id;
+	reg [31:0] operand_b;
+	reg [31:0] alu_operand_a;
+	wire [31:0] alu_operand_b;
+	assign instr = instr_rdata_i;
+	assign imm_i_type = {{20 {instr[31]}}, instr[31:20]};
+	assign imm_iz_type = {20'b00000000000000000000, instr[31:20]};
+	assign imm_s_type = {{20 {instr[31]}}, instr[31:25], instr[11:7]};
+	assign imm_sb_type = {{19 {instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0};
+	assign imm_u_type = {instr[31:12], 12'b000000000000};
+	assign imm_uj_type = {{12 {instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0};
+	assign imm_z_type = {27'b000000000000000000000000000, instr[19:15]};
+	assign imm_s2_type = {27'b000000000000000000000000000, instr[24:20]};
+	assign imm_bi_type = {{27 {instr[24]}}, instr[24:20]};
+	assign imm_s3_type = {27'b000000000000000000000000000, instr[29:25]};
+	assign imm_vs_type = {{26 {instr[24]}}, instr[24:20], instr[25]};
+	assign imm_vu_type = {26'b00000000000000000000000000, instr[24:20], instr[25]};
+	assign regfile_addr_ra_id = instr[19:15];
+	assign regfile_addr_rb_id = instr[24:20];
+	assign regfile_alu_waddr_id = instr[11:7];
+	assign illegal_reg_rv32e = 1'b0;
+	assign clear_instr_valid_o = id_ready_o | halt_id;
+	assign branch_taken_ex = branch_in_id & branch_decision_i;
+	always @(*) begin : alu_operand_a_mux
+		case (alu_op_a_mux_sel)
+			OP_A_REGA_OR_FWD: alu_operand_a = operand_a_fw_id;
+			OP_A_CURRPC: alu_operand_a = pc_id_i;
+			OP_A_IMM: alu_operand_a = imm_a;
+			default: alu_operand_a = operand_a_fw_id;
+		endcase
+	end
+	always @(*) begin : immediate_a_mux
+		case (imm_a_mux_sel)
+			IMMA_Z: imm_a = imm_z_type;
+			IMMA_ZERO: imm_a = 1'sb0;
+			default: imm_a = 1'sb0;
+		endcase
+	end
+	always @(*) begin : operand_a_fw_mux
+		case (operand_a_fw_mux_sel)
+			SEL_MISALIGNED: operand_a_fw_id = misaligned_addr_i;
+			SEL_REGFILE: operand_a_fw_id = regfile_data_ra_id;
+			default: operand_a_fw_id = regfile_data_ra_id;
+		endcase
+	end
+	always @(*) begin : immediate_b_mux
+		case (imm_b_mux_sel)
+			IMMB_I: imm_b = imm_i_type;
+			IMMB_S: imm_b = imm_s_type;
+			IMMB_U: imm_b = imm_u_type;
+			IMMB_PCINCR: imm_b = (is_compressed_i && ~data_misaligned_i ? 32'h00000002 : 32'h00000004);
+			IMMB_S2: imm_b = imm_s2_type;
+			IMMB_BI: imm_b = imm_bi_type;
+			IMMB_S3: imm_b = imm_s3_type;
+			IMMB_VS: imm_b = imm_vs_type;
+			IMMB_VU: imm_b = imm_vu_type;
+			IMMB_UJ: imm_b = imm_uj_type;
+			IMMB_SB: imm_b = imm_sb_type;
+			default: imm_b = imm_i_type;
+		endcase
+	end
+	always @(*) begin : alu_operand_b_mux
+		case (alu_op_b_mux_sel)
+			OP_B_REGB_OR_FWD: operand_b = regfile_data_rb_id;
+			OP_B_IMM: operand_b = imm_b;
+			default: operand_b = regfile_data_rb_id;
+		endcase
+	end
+	assign alu_operand_b = operand_b;
+	assign operand_b_fw_id = regfile_data_rb_id;
+	reg [31:0] regfile_wdata_mux;
+	reg regfile_we_mux;
+	reg [4:0] regfile_waddr_mux;
+	always @(*)
+		if (dbg_reg_wreq_i) begin
+			regfile_wdata_mux = dbg_reg_wdata_i;
+			regfile_waddr_mux = dbg_reg_waddr_i;
+			regfile_we_mux = 1'b1;
+		end
+		else begin
+			regfile_we_mux = regfile_we;
+			regfile_waddr_mux = regfile_alu_waddr_id;
+			if (select_data_rf == 1'd0)
+				regfile_wdata_mux = regfile_wdata_lsu_i;
+			else if (csr_access)
+				regfile_wdata_mux = csr_rdata_i;
+			else
+				regfile_wdata_mux = regfile_wdata_ex_i;
+		end
+	zeroriscy_register_file #(.RV32E(RV32E)) registers_i(
+		.clk(clk),
+		.rst_n(rst_n),
+		.test_en_i(test_en_i),
+		.raddr_a_i(regfile_addr_ra_id),
+		.rdata_a_o(regfile_data_ra_id),
+		.raddr_b_i((dbg_reg_rreq_i == 1'b0 ? regfile_addr_rb_id : dbg_reg_raddr_i)),
+		.rdata_b_o(regfile_data_rb_id),
+		.waddr_a_i(regfile_waddr_mux),
+		.wdata_a_i(regfile_wdata_mux),
+		.we_a_i(regfile_we_mux)
+	);
+	assign dbg_reg_rdata_o = regfile_data_rb_id;
+	assign multdiv_int_en = mult_int_en | div_int_en;
+	zeroriscy_decoder #(.RV32M(RV32M)) decoder_i(
+		.deassert_we_i(deassert_we),
+		.data_misaligned_i(data_misaligned_i),
+		.branch_mux_i(branch_mux_dec),
+		.jump_mux_i(jump_mux_dec),
+		.illegal_insn_o(illegal_insn_dec),
+		.ebrk_insn_o(ebrk_insn),
+		.mret_insn_o(mret_insn_dec),
+		.ecall_insn_o(ecall_insn_dec),
+		.pipe_flush_o(pipe_flush_dec),
+		.instr_rdata_i(instr),
+		.illegal_c_insn_i(illegal_c_insn_i),
+		.alu_operator_o(alu_operator),
+		.alu_op_a_mux_sel_o(alu_op_a_mux_sel),
+		.alu_op_b_mux_sel_o(alu_op_b_mux_sel),
+		.imm_a_mux_sel_o(imm_a_mux_sel),
+		.imm_b_mux_sel_o(imm_b_mux_sel),
+		.mult_int_en_o(mult_int_en),
+		.div_int_en_o(div_int_en),
+		.multdiv_operator_o(multdiv_operator),
+		.multdiv_signed_mode_o(multdiv_signed_mode),
+		.regfile_we_o(regfile_we_id),
+		.csr_access_o(csr_access),
+		.csr_op_o(csr_op),
+		.csr_status_o(csr_status),
+		.data_req_o(data_req_id),
+		.data_we_o(data_we_id),
+		.data_type_o(data_type_id),
+		.data_sign_extension_o(data_sign_ext_id),
+		.data_reg_offset_o(data_reg_offset_id),
+		.data_load_event_o(data_load_event_id),
+		.jump_in_id_o(jump_in_id),
+		.branch_in_id_o(branch_in_id)
+	);
+	zeroriscy_controller controller_i(
+		.clk(clk),
+		.rst_n(rst_n),
+		.fetch_enable_i(fetch_enable_i),
+		.ctrl_busy_o(ctrl_busy_o),
+		.first_fetch_o(core_ctrl_firstfetch_o),
+		.is_decoding_o(is_decoding_o),
+		.deassert_we_o(deassert_we),
+		.illegal_insn_i(illegal_insn_dec | illegal_reg_rv32e),
+		.ecall_insn_i(ecall_insn_dec),
+		.mret_insn_i(mret_insn_dec),
+		.pipe_flush_i(pipe_flush_dec),
+		.ebrk_insn_i(ebrk_insn),
+		.csr_status_i(csr_status),
+		.instr_valid_i(instr_valid_i),
+		.instr_req_o(instr_req_o),
+		.pc_set_o(pc_set_o),
+		.pc_mux_o(pc_mux_o),
+		.exc_pc_mux_o(exc_pc_mux_o),
+		.exc_cause_o(exc_cause_o),
+		.data_misaligned_i(data_misaligned_i),
+		.branch_in_id_i(branch_in_id),
+		.branch_taken_ex_i(branch_taken_ex),
+		.branch_set_i(branch_set_q),
+		.jump_set_i(jump_set),
+		.instr_multicyle_i(instr_multicyle),
+		.irq_req_ctrl_i(irq_req_ctrl),
+		.irq_id_ctrl_i(irq_id_ctrl),
+		.m_IE_i(m_irq_enable_i),
+		.irq_ack_o(irq_ack_o),
+		.irq_id_o(irq_id_o),
+		.exc_ack_o(exc_ack),
+		.exc_kill_o(exc_kill),
+		.csr_save_cause_o(csr_save_cause_o),
+		.csr_cause_o(csr_cause_o),
+		.csr_save_if_o(csr_save_if_o),
+		.csr_save_id_o(csr_save_id_o),
+		.csr_restore_mret_id_o(csr_restore_mret_id_o),
+		.dbg_req_i(dbg_req_i),
+		.dbg_ack_o(dbg_ack_o),
+		.dbg_stall_i(dbg_stall_i),
+		.dbg_jump_req_i(dbg_jump_req_i),
+		.dbg_settings_i(dbg_settings_i),
+		.dbg_trap_o(dbg_trap_o),
+		.operand_a_fw_mux_sel_o(operand_a_fw_mux_sel),
+		.halt_if_o(halt_if_o),
+		.halt_id_o(halt_id),
+		.id_ready_i(id_ready_o),
+		.perf_jump_o(perf_jump_o),
+		.perf_tbranch_o(perf_tbranch_o)
+	);
+	zeroriscy_int_controller int_controller_i(
+		.clk(clk),
+		.rst_n(rst_n),
+		.irq_req_ctrl_o(irq_req_ctrl),
+		.irq_id_ctrl_o(irq_id_ctrl),
+		.ctrl_ack_i(exc_ack),
+		.ctrl_kill_i(exc_kill),
+		.irq_i(irq_i),
+		.irq_id_i(irq_id_i),
+		.m_IE_i(m_irq_enable_i)
+	);
+	assign data_we_ex_o = data_we_id;
+	assign data_type_ex_o = data_type_id;
+	assign data_sign_ext_ex_o = data_sign_ext_id;
+	assign data_wdata_ex_o = regfile_data_rb_id;
+	assign data_req_ex_o = data_req_id;
+	assign data_reg_offset_ex_o = data_reg_offset_id;
+	assign data_load_event_ex_o = data_load_event_id;
+	assign alu_operator_ex_o = alu_operator;
+	assign alu_operand_a_ex_o = alu_operand_a;
+	assign alu_operand_b_ex_o = alu_operand_b;
+	assign csr_access_ex_o = csr_access;
+	assign csr_op_ex_o = csr_op;
+	assign branch_in_ex_o = branch_in_id;
+	assign mult_en_ex_o = mult_int_en;
+	assign div_en_ex_o = div_int_en;
+	assign multdiv_operator_ex_o = multdiv_operator;
+	assign multdiv_signed_mode_ex_o = multdiv_signed_mode;
+	assign multdiv_operand_a_ex_o = regfile_data_ra_id;
+	assign multdiv_operand_b_ex_o = regfile_data_rb_id;
+	reg id_wb_fsm_cs;
+	reg id_wb_fsm_ns;
+	always @(posedge clk or negedge rst_n) begin : EX_WB_Pipeline_Register
+		if (~rst_n) begin
+			id_wb_fsm_cs <= 1'd0;
+			branch_set_q <= 1'b0;
+		end
+		else begin
+			id_wb_fsm_cs <= id_wb_fsm_ns;
+			branch_set_q <= branch_set_n;
+		end
+	end
+	always @(*) begin
+		id_wb_fsm_ns = id_wb_fsm_cs;
+		regfile_we = regfile_we_id;
+		load_stall = 1'b0;
+		multdiv_stall = 1'b0;
+		jump_stall = 1'b0;
+		branch_stall = 1'b0;
+		select_data_rf = 1'd1;
+		instr_multicyle = 1'b0;
+		branch_set_n = 1'b0;
+		branch_mux_dec = 1'b0;
+		jump_set = 1'b0;
+		jump_mux_dec = 1'b0;
+		perf_branch_o = 1'b0;
+		case (id_wb_fsm_cs)
+			1'd0: begin
+				jump_mux_dec = 1'b1;
+				branch_mux_dec = 1'b1;
+				case (1'b1)
+					data_req_id: begin
+						regfile_we = 1'b0;
+						id_wb_fsm_ns = 1'd1;
+						load_stall = 1'b1;
+						instr_multicyle = 1'b1;
+					end
+					branch_in_id: begin
+						id_wb_fsm_ns = (branch_decision_i ? 1'd1 : 1'd0);
+						branch_stall = branch_decision_i;
+						instr_multicyle = branch_decision_i;
+						branch_set_n = branch_decision_i;
+						perf_branch_o = 1'b1;
+					end
+					multdiv_int_en: begin
+						regfile_we = 1'b0;
+						id_wb_fsm_ns = 1'd1;
+						multdiv_stall = 1'b1;
+						instr_multicyle = 1'b1;
+					end
+					jump_in_id: begin
+						regfile_we = 1'b0;
+						id_wb_fsm_ns = 1'd1;
+						jump_stall = 1'b1;
+						instr_multicyle = 1'b1;
+						jump_set = 1'b1;
+					end
+					default:
+						;
+				endcase
+			end
+			1'd1:
+				if (ex_ready_i) begin
+					regfile_we = regfile_we_id;
+					id_wb_fsm_ns = 1'd0;
+					load_stall = 1'b0;
+					multdiv_stall = 1'b0;
+					select_data_rf = (data_req_id ? 1'd0 : 1'd1);
+				end
+				else begin
+					regfile_we = 1'b0;
+					instr_multicyle = 1'b1;
+					case (1'b1)
+						data_req_id: load_stall = 1'b1;
+						multdiv_int_en: multdiv_stall = 1'b1;
+						default:
+							;
+					endcase
+				end
+			default:
+				;
+		endcase
+	end
+	assign id_ready_o = ((~load_stall & ~branch_stall) & ~jump_stall) & ~multdiv_stall;
+	assign id_valid_o = ~halt_id & id_ready_o;
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_if_stage.v b/verilog/rtl/ips/zero-riscy/zeroriscy_if_stage.v
new file mode 100644
index 0000000..f80080c
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_if_stage.v
@@ -0,0 +1,452 @@
+module zeroriscy_if_stage (
+	clk,
+	rst_n,
+	boot_addr_i,
+	req_i,
+	instr_req_o,
+	instr_addr_o,
+	instr_gnt_i,
+	instr_rvalid_i,
+	instr_rdata_i,
+	instr_valid_id_o,
+	instr_rdata_id_o,
+	is_compressed_id_o,
+	illegal_c_insn_id_o,
+	pc_if_o,
+	pc_id_o,
+	clear_instr_valid_i,
+	pc_set_i,
+	exception_pc_reg_i,
+	pc_mux_i,
+	exc_pc_mux_i,
+	exc_vec_pc_mux_i,
+	jump_target_ex_i,
+	dbg_jump_addr_i,
+	halt_if_i,
+	id_ready_i,
+	if_valid_o,
+	if_busy_o,
+	perf_imiss_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	input wire clk;
+	input wire rst_n;
+	input wire [31:0] boot_addr_i;
+	input wire req_i;
+	output wire instr_req_o;
+	output wire [31:0] instr_addr_o;
+	input wire instr_gnt_i;
+	input wire instr_rvalid_i;
+	input wire [31:0] instr_rdata_i;
+	output reg instr_valid_id_o;
+	output reg [31:0] instr_rdata_id_o;
+	output reg is_compressed_id_o;
+	output reg illegal_c_insn_id_o;
+	output wire [31:0] pc_if_o;
+	output reg [31:0] pc_id_o;
+	input wire clear_instr_valid_i;
+	input wire pc_set_i;
+	input wire [31:0] exception_pc_reg_i;
+	input wire [2:0] pc_mux_i;
+	input wire [1:0] exc_pc_mux_i;
+	input wire [4:0] exc_vec_pc_mux_i;
+	input wire [31:0] jump_target_ex_i;
+	input wire [31:0] dbg_jump_addr_i;
+	input wire halt_if_i;
+	input wire id_ready_i;
+	output wire if_valid_o;
+	output wire if_busy_o;
+	output wire perf_imiss_o;
+	reg [0:0] offset_fsm_cs;
+	reg [0:0] offset_fsm_ns;
+	reg valid;
+	wire if_ready;
+	wire prefetch_busy;
+	reg branch_req;
+	reg [31:0] fetch_addr_n;
+	wire fetch_valid;
+	reg fetch_ready;
+	wire [31:0] fetch_rdata;
+	wire [31:0] fetch_addr;
+	reg [31:0] exc_pc;
+	always @(*) begin : EXC_PC_MUX
+		exc_pc = 1'sb0;
+		case (exc_pc_mux_i)
+			EXC_PC_ILLINSN: exc_pc = {boot_addr_i[31:8], EXC_OFF_ILLINSN};
+			EXC_PC_ECALL: exc_pc = {boot_addr_i[31:8], EXC_OFF_ECALL};
+			EXC_PC_IRQ: exc_pc = {boot_addr_i[31:8], 1'b0, exc_vec_pc_mux_i[4:0], 2'b00};
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		fetch_addr_n = 1'sb0;
+		case (pc_mux_i)
+			PC_BOOT: fetch_addr_n = {boot_addr_i[31:8], EXC_OFF_RST};
+			PC_JUMP: fetch_addr_n = jump_target_ex_i;
+			PC_EXCEPTION: fetch_addr_n = exc_pc;
+			PC_ERET: fetch_addr_n = exception_pc_reg_i;
+			PC_DBG_NPC: fetch_addr_n = dbg_jump_addr_i;
+			default:
+				;
+		endcase
+	end
+	zeroriscy_prefetch_buffer prefetch_buffer_i(
+		.clk(clk),
+		.rst_n(rst_n),
+		.req_i(req_i),
+		.branch_i(branch_req),
+		.addr_i({fetch_addr_n[31:1], 1'b0}),
+		.ready_i(fetch_ready),
+		.valid_o(fetch_valid),
+		.rdata_o(fetch_rdata),
+		.addr_o(fetch_addr),
+		.instr_req_o(instr_req_o),
+		.instr_addr_o(instr_addr_o),
+		.instr_gnt_i(instr_gnt_i),
+		.instr_rvalid_i(instr_rvalid_i),
+		.instr_rdata_i(instr_rdata_i),
+		.busy_o(prefetch_busy)
+	);
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0)
+			offset_fsm_cs <= 1'd1;
+		else
+			offset_fsm_cs <= offset_fsm_ns;
+	always @(*) begin
+		offset_fsm_ns = offset_fsm_cs;
+		fetch_ready = 1'b0;
+		branch_req = 1'b0;
+		valid = 1'b0;
+		case (offset_fsm_cs)
+			1'd1:
+				if (req_i) begin
+					branch_req = 1'b1;
+					offset_fsm_ns = 1'd0;
+				end
+			1'd0:
+				if (fetch_valid) begin
+					valid = 1'b1;
+					if (req_i && if_valid_o) begin
+						fetch_ready = 1'b1;
+						offset_fsm_ns = 1'd0;
+					end
+				end
+			default: offset_fsm_ns = 1'd1;
+		endcase
+		if (pc_set_i) begin
+			valid = 1'b0;
+			branch_req = 1'b1;
+			offset_fsm_ns = 1'd0;
+		end
+	end
+	assign pc_if_o = fetch_addr;
+	assign if_busy_o = prefetch_busy;
+	assign perf_imiss_o = ~fetch_valid | branch_req;
+	wire [31:0] instr_decompressed;
+	wire illegal_c_insn;
+	wire instr_compressed_int;
+	zeroriscy_compressed_decoder compressed_decoder_i(
+		.instr_i(fetch_rdata),
+		.instr_o(instr_decompressed),
+		.is_compressed_o(instr_compressed_int),
+		.illegal_instr_o(illegal_c_insn)
+	);
+	always @(posedge clk or negedge rst_n) begin : IF_ID_PIPE_REGISTERS
+		if (rst_n == 1'b0) begin
+			instr_valid_id_o <= 1'b0;
+			instr_rdata_id_o <= 1'sb0;
+			illegal_c_insn_id_o <= 1'b0;
+			is_compressed_id_o <= 1'b0;
+			pc_id_o <= 1'sb0;
+		end
+		else if (if_valid_o) begin
+			instr_valid_id_o <= 1'b1;
+			instr_rdata_id_o <= instr_decompressed;
+			illegal_c_insn_id_o <= illegal_c_insn;
+			is_compressed_id_o <= instr_compressed_int;
+			pc_id_o <= pc_if_o;
+		end
+		else if (clear_instr_valid_i)
+			instr_valid_id_o <= 1'b0;
+	end
+	assign if_ready = valid & id_ready_i;
+	assign if_valid_o = ~halt_if_i & if_ready;
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_int_controller.v b/verilog/rtl/ips/zero-riscy/zeroriscy_int_controller.v
new file mode 100644
index 0000000..72b8738
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_int_controller.v
@@ -0,0 +1,48 @@
+module zeroriscy_int_controller (
+	clk,
+	rst_n,
+	irq_req_ctrl_o,
+	irq_id_ctrl_o,
+	ctrl_ack_i,
+	ctrl_kill_i,
+	irq_i,
+	irq_id_i,
+	m_IE_i
+);
+	input wire clk;
+	input wire rst_n;
+	output wire irq_req_ctrl_o;
+	output wire [4:0] irq_id_ctrl_o;
+	input wire ctrl_ack_i;
+	input wire ctrl_kill_i;
+	input wire irq_i;
+	input wire [4:0] irq_id_i;
+	input wire m_IE_i;
+	reg [1:0] exc_ctrl_cs;
+	wire [1:0] exc_ctrl_ns;
+	wire irq_enable_ext;
+	reg [4:0] irq_id_q;
+	assign irq_enable_ext = m_IE_i;
+	assign irq_req_ctrl_o = exc_ctrl_cs == 2'd1;
+	assign irq_id_ctrl_o = irq_id_q;
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			irq_id_q <= 1'sb0;
+			exc_ctrl_cs <= 2'd0;
+		end
+		else
+			case (exc_ctrl_cs)
+				2'd0:
+					if (irq_enable_ext & irq_i) begin
+						exc_ctrl_cs <= 2'd1;
+						irq_id_q <= irq_id_i;
+					end
+				2'd1:
+					case (1'b1)
+						ctrl_ack_i: exc_ctrl_cs <= 2'd2;
+						ctrl_kill_i: exc_ctrl_cs <= 2'd0;
+						default: exc_ctrl_cs <= 2'd1;
+					endcase
+				2'd2: exc_ctrl_cs <= 2'd0;
+			endcase
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_load_store_unit.v b/verilog/rtl/ips/zero-riscy/zeroriscy_load_store_unit.v
new file mode 100644
index 0000000..e8daa39
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_load_store_unit.v
@@ -0,0 +1,295 @@
+module zeroriscy_load_store_unit (
+	clk,
+	rst_n,
+	data_req_o,
+	data_gnt_i,
+	data_rvalid_i,
+	data_err_i,
+	data_addr_o,
+	data_we_o,
+	data_be_o,
+	data_wdata_o,
+	data_rdata_i,
+	data_we_ex_i,
+	data_type_ex_i,
+	data_wdata_ex_i,
+	data_reg_offset_ex_i,
+	data_sign_ext_ex_i,
+	data_rdata_ex_o,
+	data_req_ex_i,
+	adder_result_ex_i,
+	data_misaligned_o,
+	misaligned_addr_o,
+	load_err_o,
+	store_err_o,
+	lsu_update_addr_o,
+	data_valid_o,
+	busy_o
+);
+	input wire clk;
+	input wire rst_n;
+	output reg data_req_o;
+	input wire data_gnt_i;
+	input wire data_rvalid_i;
+	input wire data_err_i;
+	output wire [31:0] data_addr_o;
+	output wire data_we_o;
+	output wire [3:0] data_be_o;
+	output wire [31:0] data_wdata_o;
+	input wire [31:0] data_rdata_i;
+	input wire data_we_ex_i;
+	input wire [1:0] data_type_ex_i;
+	input wire [31:0] data_wdata_ex_i;
+	input wire [1:0] data_reg_offset_ex_i;
+	input wire data_sign_ext_ex_i;
+	output wire [31:0] data_rdata_ex_o;
+	input wire data_req_ex_i;
+	input wire [31:0] adder_result_ex_i;
+	output reg data_misaligned_o;
+	output reg [31:0] misaligned_addr_o;
+	output wire load_err_o;
+	output wire store_err_o;
+	output reg lsu_update_addr_o;
+	output reg data_valid_o;
+	output wire busy_o;
+	wire [31:0] data_addr_int;
+	reg [1:0] data_type_q;
+	reg [1:0] rdata_offset_q;
+	reg data_sign_ext_q;
+	reg data_we_q;
+	wire [1:0] wdata_offset;
+	reg [3:0] data_be;
+	reg [31:0] data_wdata;
+	wire misaligned_st;
+	reg data_misaligned;
+	reg data_misaligned_q;
+	reg increase_address;
+	reg [2:0] CS;
+	reg [2:0] NS;
+	reg [31:0] rdata_q;
+	always @(*)
+		case (data_type_ex_i)
+			2'b00:
+				if (misaligned_st == 1'b0)
+					case (data_addr_int[1:0])
+						2'b00: data_be = 4'b1111;
+						2'b01: data_be = 4'b1110;
+						2'b10: data_be = 4'b1100;
+						2'b11: data_be = 4'b1000;
+					endcase
+				else
+					case (data_addr_int[1:0])
+						2'b00: data_be = 4'b0000;
+						2'b01: data_be = 4'b0001;
+						2'b10: data_be = 4'b0011;
+						2'b11: data_be = 4'b0111;
+					endcase
+			2'b01:
+				if (misaligned_st == 1'b0)
+					case (data_addr_int[1:0])
+						2'b00: data_be = 4'b0011;
+						2'b01: data_be = 4'b0110;
+						2'b10: data_be = 4'b1100;
+						2'b11: data_be = 4'b1000;
+					endcase
+				else
+					data_be = 4'b0001;
+			2'b10, 2'b11:
+				case (data_addr_int[1:0])
+					2'b00: data_be = 4'b0001;
+					2'b01: data_be = 4'b0010;
+					2'b10: data_be = 4'b0100;
+					2'b11: data_be = 4'b1000;
+				endcase
+		endcase
+	assign wdata_offset = data_addr_int[1:0] - data_reg_offset_ex_i[1:0];
+	always @(*)
+		case (wdata_offset)
+			2'b00: data_wdata = data_wdata_ex_i[31:0];
+			2'b01: data_wdata = {data_wdata_ex_i[23:0], data_wdata_ex_i[31:24]};
+			2'b10: data_wdata = {data_wdata_ex_i[15:0], data_wdata_ex_i[31:16]};
+			2'b11: data_wdata = {data_wdata_ex_i[7:0], data_wdata_ex_i[31:8]};
+		endcase
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			data_type_q <= 1'sb0;
+			rdata_offset_q <= 1'sb0;
+			data_sign_ext_q <= 1'sb0;
+			data_we_q <= 1'b0;
+		end
+		else if (data_gnt_i == 1'b1) begin
+			data_type_q <= data_type_ex_i;
+			rdata_offset_q <= data_addr_int[1:0];
+			data_sign_ext_q <= data_sign_ext_ex_i;
+			data_we_q <= data_we_ex_i;
+		end
+	reg [31:0] data_rdata_ext;
+	reg [31:0] rdata_w_ext;
+	reg [31:0] rdata_h_ext;
+	reg [31:0] rdata_b_ext;
+	always @(*)
+		case (rdata_offset_q)
+			2'b00: rdata_w_ext = data_rdata_i[31:0];
+			2'b01: rdata_w_ext = {data_rdata_i[7:0], rdata_q[31:8]};
+			2'b10: rdata_w_ext = {data_rdata_i[15:0], rdata_q[31:16]};
+			2'b11: rdata_w_ext = {data_rdata_i[23:0], rdata_q[31:24]};
+		endcase
+	always @(*)
+		case (rdata_offset_q)
+			2'b00:
+				if (data_sign_ext_q == 1'b0)
+					rdata_h_ext = {16'h0000, data_rdata_i[15:0]};
+				else
+					rdata_h_ext = {{16 {data_rdata_i[15]}}, data_rdata_i[15:0]};
+			2'b01:
+				if (data_sign_ext_q == 1'b0)
+					rdata_h_ext = {16'h0000, data_rdata_i[23:8]};
+				else
+					rdata_h_ext = {{16 {data_rdata_i[23]}}, data_rdata_i[23:8]};
+			2'b10:
+				if (data_sign_ext_q == 1'b0)
+					rdata_h_ext = {16'h0000, data_rdata_i[31:16]};
+				else
+					rdata_h_ext = {{16 {data_rdata_i[31]}}, data_rdata_i[31:16]};
+			2'b11:
+				if (data_sign_ext_q == 1'b0)
+					rdata_h_ext = {16'h0000, data_rdata_i[7:0], rdata_q[31:24]};
+				else
+					rdata_h_ext = {{16 {data_rdata_i[7]}}, data_rdata_i[7:0], rdata_q[31:24]};
+		endcase
+	always @(*)
+		case (rdata_offset_q)
+			2'b00:
+				if (data_sign_ext_q == 1'b0)
+					rdata_b_ext = {24'h000000, data_rdata_i[7:0]};
+				else
+					rdata_b_ext = {{24 {data_rdata_i[7]}}, data_rdata_i[7:0]};
+			2'b01:
+				if (data_sign_ext_q == 1'b0)
+					rdata_b_ext = {24'h000000, data_rdata_i[15:8]};
+				else
+					rdata_b_ext = {{24 {data_rdata_i[15]}}, data_rdata_i[15:8]};
+			2'b10:
+				if (data_sign_ext_q == 1'b0)
+					rdata_b_ext = {24'h000000, data_rdata_i[23:16]};
+				else
+					rdata_b_ext = {{24 {data_rdata_i[23]}}, data_rdata_i[23:16]};
+			2'b11:
+				if (data_sign_ext_q == 1'b0)
+					rdata_b_ext = {24'h000000, data_rdata_i[31:24]};
+				else
+					rdata_b_ext = {{24 {data_rdata_i[31]}}, data_rdata_i[31:24]};
+		endcase
+	always @(*)
+		case (data_type_q)
+			2'b00: data_rdata_ext = rdata_w_ext;
+			2'b01: data_rdata_ext = rdata_h_ext;
+			2'b10, 2'b11: data_rdata_ext = rdata_b_ext;
+		endcase
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			CS <= 3'd0;
+			rdata_q <= 1'sb0;
+			data_misaligned_q <= 1'sb0;
+			misaligned_addr_o <= 32'b00000000000000000000000000000000;
+		end
+		else begin
+			CS <= NS;
+			if (lsu_update_addr_o) begin
+				data_misaligned_q <= data_misaligned;
+				if (increase_address)
+					misaligned_addr_o <= data_addr_int;
+			end
+			if (data_rvalid_i && ~data_we_q)
+				if ((data_misaligned_q == 1'b1) || (data_misaligned == 1'b1))
+					rdata_q <= data_rdata_i;
+				else
+					rdata_q <= data_rdata_ext;
+		end
+	assign data_rdata_ex_o = (data_rvalid_i == 1'b1 ? data_rdata_ext : rdata_q);
+	assign data_addr_o = data_addr_int;
+	assign data_wdata_o = data_wdata;
+	assign data_we_o = data_we_ex_i;
+	assign data_be_o = data_be;
+	assign misaligned_st = data_misaligned_q;
+	assign load_err_o = 1'b0;
+	assign store_err_o = 1'b0;
+	always @(*) begin
+		NS = CS;
+		data_req_o = 1'b0;
+		lsu_update_addr_o = 1'b0;
+		data_valid_o = 1'b0;
+		increase_address = 1'b0;
+		data_misaligned_o = 1'b0;
+		case (CS)
+			3'd0:
+				if (data_req_ex_i) begin
+					data_req_o = data_req_ex_i;
+					if (data_gnt_i) begin
+						lsu_update_addr_o = 1'b1;
+						increase_address = data_misaligned;
+						NS = (data_misaligned ? 3'd2 : 3'd4);
+					end
+					else
+						NS = (data_misaligned ? 3'd1 : 3'd3);
+				end
+			3'd1: begin
+				data_req_o = 1'b1;
+				if (data_gnt_i) begin
+					lsu_update_addr_o = 1'b1;
+					increase_address = data_misaligned;
+					NS = 3'd2;
+				end
+			end
+			3'd2: begin
+				increase_address = 1'b0;
+				data_misaligned_o = 1'b1;
+				data_req_o = 1'b0;
+				lsu_update_addr_o = data_gnt_i;
+				if (data_rvalid_i) begin
+					data_req_o = 1'b1;
+					if (data_gnt_i)
+						NS = 3'd4;
+					else
+						NS = 3'd3;
+				end
+				else
+					NS = 3'd2;
+			end
+			3'd3: begin
+				data_misaligned_o = data_misaligned_q;
+				data_req_o = 1'b1;
+				if (data_gnt_i) begin
+					lsu_update_addr_o = 1'b1;
+					NS = 3'd4;
+				end
+			end
+			3'd4: begin
+				data_req_o = 1'b0;
+				if (data_rvalid_i) begin
+					data_valid_o = 1'b1;
+					NS = 3'd0;
+				end
+				else
+					NS = 3'd4;
+			end
+			default: NS = 3'd0;
+		endcase
+	end
+	always @(*) begin
+		data_misaligned = 1'b0;
+		if ((data_req_ex_i == 1'b1) && (data_misaligned_q == 1'b0))
+			case (data_type_ex_i)
+				2'b00:
+					if (data_addr_int[1:0] != 2'b00)
+						data_misaligned = 1'b1;
+				2'b01:
+					if (data_addr_int[1:0] == 2'b11)
+						data_misaligned = 1'b1;
+				default:
+					;
+			endcase
+	end
+	assign data_addr_int = adder_result_ex_i;
+	assign busy_o = (CS == 3'd4) || (data_req_o == 1'b1);
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_fast.v b/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_fast.v
new file mode 100644
index 0000000..dedff19
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_fast.v
@@ -0,0 +1,528 @@
+module zeroriscy_multdiv_fast (
+	clk,
+	rst_n,
+	mult_en_i,
+	div_en_i,
+	operator_i,
+	signed_mode_i,
+	op_a_i,
+	op_b_i,
+	alu_adder_ext_i,
+	alu_adder_i,
+	equal_to_zero,
+	alu_operand_a_o,
+	alu_operand_b_o,
+	multdiv_result_o,
+	ready_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	input wire clk;
+	input wire rst_n;
+	input wire mult_en_i;
+	input wire div_en_i;
+	input wire [1:0] operator_i;
+	input wire [1:0] signed_mode_i;
+	input wire [31:0] op_a_i;
+	input wire [31:0] op_b_i;
+	input wire [33:0] alu_adder_ext_i;
+	input wire [31:0] alu_adder_i;
+	input wire equal_to_zero;
+	output reg [32:0] alu_operand_a_o;
+	output reg [32:0] alu_operand_b_o;
+	output wire [31:0] multdiv_result_o;
+	output wire ready_o;
+	reg [4:0] div_counter_q;
+	reg [4:0] div_counter_n;
+	reg [1:0] mult_state_q;
+	reg [1:0] mult_state_n;
+	reg [2:0] divcurr_state_q;
+	reg [2:0] divcurr_state_n;
+	wire [34:0] mac_res_ext;
+	reg [33:0] mac_res_q;
+	reg [33:0] mac_res_n;
+	wire [33:0] mac_res;
+	reg [33:0] op_reminder_n;
+	reg [15:0] mult_op_a;
+	reg [15:0] mult_op_b;
+	reg [33:0] accum;
+	reg sign_a;
+	reg sign_b;
+	wire div_sign_a;
+	wire div_sign_b;
+	wire signed_mult;
+	reg is_greater_equal;
+	wire div_change_sign;
+	wire rem_change_sign;
+	wire [31:0] one_shift;
+	reg [31:0] op_denominator_q;
+	reg [31:0] op_numerator_q;
+	reg [31:0] op_quotient_q;
+	reg [31:0] op_denominator_n;
+	reg [31:0] op_numerator_n;
+	reg [31:0] op_quotient_n;
+	wire [32:0] next_reminder;
+	wire [32:0] next_quotient;
+	wire [32:0] res_adder_h;
+	reg mult_is_ready;
+	always @(posedge clk or negedge rst_n) begin : proc_mult_state_q
+		if (~rst_n) begin
+			mult_state_q <= 2'd0;
+			mac_res_q <= 1'sb0;
+			div_counter_q <= 1'sb0;
+			divcurr_state_q <= 3'd0;
+			op_denominator_q <= 1'sb0;
+			op_numerator_q <= 1'sb0;
+			op_quotient_q <= 1'sb0;
+		end
+		else begin
+			if (mult_en_i)
+				mult_state_q <= mult_state_n;
+			if (div_en_i) begin
+				div_counter_q <= div_counter_n;
+				op_denominator_q <= op_denominator_n;
+				op_numerator_q <= op_numerator_n;
+				op_quotient_q <= op_quotient_n;
+				divcurr_state_q <= divcurr_state_n;
+			end
+			case (1'b1)
+				mult_en_i: mac_res_q <= mac_res_n;
+				div_en_i: mac_res_q <= op_reminder_n;
+				default: mac_res_q <= mac_res_q;
+			endcase
+		end
+	end
+	assign signed_mult = signed_mode_i != 2'b00;
+	assign multdiv_result_o = (div_en_i ? mac_res_q[31:0] : mac_res_n[31:0]);
+	assign mac_res_ext = ($signed({sign_a, mult_op_a}) * $signed({sign_b, mult_op_b})) + $signed(accum);
+	assign mac_res = mac_res_ext[33:0];
+	assign res_adder_h = alu_adder_ext_i[33:1];
+	assign next_reminder = (is_greater_equal ? res_adder_h : mac_res_q[32:0]);
+	assign next_quotient = (is_greater_equal ? op_quotient_q | one_shift : op_quotient_q);
+	assign one_shift = 32'b00000000000000000000000000000001 << div_counter_q;
+	always @(*)
+		if ((mac_res_q[31] ^ op_denominator_q[31]) == 0)
+			is_greater_equal = res_adder_h[31] == 0;
+		else
+			is_greater_equal = mac_res_q[31];
+	assign div_sign_a = op_a_i[31] & signed_mode_i[0];
+	assign div_sign_b = op_b_i[31] & signed_mode_i[1];
+	assign div_change_sign = div_sign_a ^ div_sign_b;
+	assign rem_change_sign = div_sign_a;
+	always @(*) begin : div_fsm
+		div_counter_n = div_counter_q - 1;
+		op_reminder_n = mac_res_q;
+		op_quotient_n = op_quotient_q;
+		divcurr_state_n = divcurr_state_q;
+		op_numerator_n = op_numerator_q;
+		op_denominator_n = op_denominator_q;
+		alu_operand_a_o = 33'b000000000000000000000000000000001;
+		alu_operand_b_o = {~op_b_i, 1'b1};
+		case (divcurr_state_q)
+			3'd0: begin
+				case (operator_i)
+					MD_OP_DIV: begin
+						op_reminder_n = 1'sb1;
+						divcurr_state_n = (equal_to_zero ? 3'd6 : 3'd1);
+					end
+					default: begin
+						op_reminder_n = {2'b00, op_a_i};
+						divcurr_state_n = (equal_to_zero ? 3'd6 : 3'd1);
+					end
+				endcase
+				alu_operand_a_o = 33'b000000000000000000000000000000001;
+				alu_operand_b_o = {~op_b_i, 1'b1};
+				div_counter_n = 5'd31;
+			end
+			3'd1: begin
+				op_quotient_n = 1'sb0;
+				op_numerator_n = (div_sign_a ? alu_adder_i : op_a_i);
+				divcurr_state_n = 3'd2;
+				div_counter_n = 5'd31;
+				alu_operand_a_o = 33'b000000000000000000000000000000001;
+				alu_operand_b_o = {~op_a_i, 1'b1};
+			end
+			3'd2: begin
+				op_reminder_n = {33'h000000000, op_numerator_q[31]};
+				op_denominator_n = (div_sign_b ? alu_adder_i : op_b_i);
+				divcurr_state_n = 3'd3;
+				div_counter_n = 5'd31;
+				alu_operand_a_o = 33'b000000000000000000000000000000001;
+				alu_operand_b_o = {~op_b_i, 1'b1};
+			end
+			3'd3: begin
+				op_reminder_n = {1'b0, next_reminder[31:0], op_numerator_q[div_counter_n]};
+				op_quotient_n = next_quotient;
+				if (div_counter_q == 5'd1)
+					divcurr_state_n = 3'd4;
+				else
+					divcurr_state_n = 3'd3;
+				alu_operand_a_o = {mac_res_q[31:0], 1'b1};
+				alu_operand_b_o = {~op_denominator_q[31:0], 1'b1};
+			end
+			3'd4: begin
+				case (operator_i)
+					MD_OP_DIV: op_reminder_n = {1'b0, next_quotient};
+					default: op_reminder_n = {2'b00, next_reminder[31:0]};
+				endcase
+				alu_operand_a_o = {mac_res_q[31:0], 1'b1};
+				alu_operand_b_o = {~op_denominator_q[31:0], 1'b1};
+				divcurr_state_n = 3'd5;
+			end
+			3'd5: begin
+				divcurr_state_n = 3'd6;
+				case (operator_i)
+					MD_OP_DIV: op_reminder_n = (div_change_sign ? alu_adder_i : mac_res_q);
+					default: op_reminder_n = (rem_change_sign ? alu_adder_i : mac_res_q);
+				endcase
+				alu_operand_a_o = 33'b000000000000000000000000000000001;
+				alu_operand_b_o = {~mac_res_q[31:0], 1'b1};
+			end
+			3'd6: divcurr_state_n = 3'd0;
+			default:
+				;
+		endcase
+	end
+	assign ready_o = mult_is_ready | (divcurr_state_q == 3'd6);
+	always @(*) begin : mult_fsm
+		mult_op_a = op_a_i[15:0];
+		mult_op_b = op_b_i[15:0];
+		sign_a = 1'b0;
+		sign_b = 1'b0;
+		accum = mac_res_q;
+		mac_res_n = mac_res;
+		mult_state_n = mult_state_q;
+		mult_is_ready = 1'b0;
+		case (mult_state_q)
+			2'd0: begin
+				mult_op_a = op_a_i[15:0];
+				mult_op_b = op_b_i[15:0];
+				sign_a = 1'b0;
+				sign_b = 1'b0;
+				accum = 1'sb0;
+				mac_res_n = mac_res;
+				mult_state_n = 2'd1;
+			end
+			2'd1: begin
+				mult_op_a = op_a_i[15:0];
+				mult_op_b = op_b_i[31:16];
+				sign_a = 1'b0;
+				sign_b = signed_mode_i[1] & op_b_i[31];
+				accum = {18'b000000000000000000, mac_res_q[31:16]};
+				case (operator_i)
+					MD_OP_MULL: mac_res_n = {2'b00, mac_res[15:0], mac_res_q[15:0]};
+					default: mac_res_n = mac_res;
+				endcase
+				mult_state_n = 2'd2;
+			end
+			2'd2: begin
+				mult_op_a = op_a_i[31:16];
+				mult_op_b = op_b_i[15:0];
+				sign_a = signed_mode_i[0] & op_a_i[31];
+				sign_b = 1'b0;
+				case (operator_i)
+					MD_OP_MULL: begin
+						accum = {18'b000000000000000000, mac_res_q[31:16]};
+						mac_res_n = {2'b00, mac_res[15:0], mac_res_q[15:0]};
+						mult_is_ready = 1'b1;
+						mult_state_n = 2'd0;
+					end
+					default: begin
+						accum = mac_res_q;
+						mac_res_n = mac_res;
+						mult_state_n = 2'd3;
+					end
+				endcase
+			end
+			2'd3: begin
+				mult_op_a = op_a_i[31:16];
+				mult_op_b = op_b_i[31:16];
+				sign_a = signed_mode_i[0] & op_a_i[31];
+				sign_b = signed_mode_i[1] & op_b_i[31];
+				accum[17:0] = mac_res_q[33:16];
+				accum[33:18] = {18 {signed_mult & mac_res_q[33]}};
+				mac_res_n = mac_res;
+				mult_state_n = 2'd0;
+				mult_is_ready = 1'b1;
+			end
+			default:
+				;
+		endcase
+	end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_slow.v b/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_slow.v
new file mode 100644
index 0000000..03e6501
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_multdiv_slow.v
@@ -0,0 +1,491 @@
+module zeroriscy_multdiv_slow (
+	clk,
+	rst_n,
+	mult_en_i,
+	div_en_i,
+	operator_i,
+	signed_mode_i,
+	op_a_i,
+	op_b_i,
+	alu_adder_ext_i,
+	alu_adder_i,
+	equal_to_zero,
+	alu_operand_a_o,
+	alu_operand_b_o,
+	multdiv_result_o,
+	ready_o
+);
+parameter OPCODE_SYSTEM    = 7'h73;
+parameter OPCODE_FENCE     = 7'h0f;
+parameter OPCODE_OP        = 7'h33;
+parameter OPCODE_OPIMM     = 7'h13;
+parameter OPCODE_STORE     = 7'h23;
+parameter OPCODE_LOAD      = 7'h03;
+parameter OPCODE_BRANCH    = 7'h63;
+parameter OPCODE_JALR      = 7'h67;
+parameter OPCODE_JAL       = 7'h6f;
+parameter OPCODE_AUIPC     = 7'h17;
+parameter OPCODE_LUI       = 7'h37;
+
+// those opcodes are now used for PULP custom instructions
+// parameter OPCODE_CUST0     = 7'h0b
+// parameter OPCODE_CUST1     = 7'h2b
+
+// PULP custom
+parameter OPCODE_LOAD_POST  = 7'h0b;
+parameter OPCODE_STORE_POST = 7'h2b;
+parameter OPCODE_PULP_OP    = 7'h5b;
+parameter OPCODE_VECOP      = 7'h57;
+parameter OPCODE_HWLOOP     = 7'h7b;
+
+parameter REGC_S1   = 2'b10;
+parameter REGC_RD   = 2'b01;
+parameter REGC_ZERO = 2'b11;
+
+
+//////////////////////////////////////////////////////////////////////////////
+//      _    _    _   _    ___                       _   _                  //
+//     / \  | |  | | | |  / _ \ _ __   ___ _ __ __ _| |_(_) ___  _ __  ___  //
+//    / _ \ | |  | | | | | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \/ __| //
+//   / ___ \| |__| |_| | | |_| | |_) |  __/ | | (_| | |_| | (_) | | | \__ \ //
+//  /_/   \_\_____\___/   \___/| .__/ \___|_|  \__,_|\__|_|\___/|_| |_|___/ //
+//                             |_|                                          //
+//////////////////////////////////////////////////////////////////////////////
+
+parameter ALU_OP_WIDTH = 6;
+
+parameter ALU_ADD   = 6'b011000;
+parameter ALU_SUB   = 6'b011001;
+parameter ALU_ADDU  = 6'b011010;
+parameter ALU_SUBU  = 6'b011011;
+parameter ALU_ADDR  = 6'b011100;
+parameter ALU_SUBR  = 6'b011101;
+parameter ALU_ADDUR = 6'b011110;
+parameter ALU_SUBUR = 6'b011111;
+
+parameter ALU_XOR   = 6'b101111;
+parameter ALU_OR    = 6'b101110;
+parameter ALU_AND   = 6'b010101;
+
+// Shifts
+parameter ALU_SRA   = 6'b100100;
+parameter ALU_SRL   = 6'b100101;
+parameter ALU_ROR   = 6'b100110;
+parameter ALU_SLL   = 6'b100111;
+
+// bit manipulation
+parameter ALU_BEXT  = 6'b101000;
+parameter ALU_BEXTU = 6'b101001;
+parameter ALU_BINS  = 6'b101010;
+parameter ALU_BCLR  = 6'b101011;
+parameter ALU_BSET  = 6'b101100;
+
+// Bit counting
+parameter ALU_FF1   = 6'b110110;
+parameter ALU_FL1   = 6'b110111;
+parameter ALU_CNT   = 6'b110100;
+parameter ALU_CLB   = 6'b110101;
+
+// Sign-/zero-extensions
+parameter ALU_EXTS  = 6'b111110;
+parameter ALU_EXT   = 6'b111111;
+
+// Comparisons
+parameter ALU_LTS   = 6'b000000;
+parameter ALU_LTU   = 6'b000001;
+parameter ALU_LES   = 6'b000100;
+parameter ALU_LEU   = 6'b000101;
+parameter ALU_GTS   = 6'b001000;
+parameter ALU_GTU   = 6'b001001;
+parameter ALU_GES   = 6'b001010;
+parameter ALU_GEU   = 6'b001011;
+parameter ALU_EQ    = 6'b001100;
+parameter ALU_NE    = 6'b001101;
+
+// Set Lower Than operations
+parameter ALU_SLTS  = 6'b000010;
+parameter ALU_SLTU  = 6'b000011;
+parameter ALU_SLETS = 6'b000110;
+parameter ALU_SLETU = 6'b000111;
+
+// Absolute value
+parameter ALU_ABS   = 6'b010100;
+parameter ALU_CLIP  = 6'b010110;
+parameter ALU_CLIPU = 6'b010111;
+
+// Insert/extract
+parameter ALU_INS   = 6'b101101;
+
+// min/max
+parameter ALU_MIN   = 6'b010000;
+parameter ALU_MINU  = 6'b010001;
+parameter ALU_MAX   = 6'b010010;
+parameter ALU_MAXU  = 6'b010011;
+
+// div/rem
+parameter ALU_DIVU  = 6'b110000; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_DIV   = 6'b110001; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REMU  = 6'b110010; // bit 0 is used for signed mode, bit 1 is used for remdiv
+parameter ALU_REM   = 6'b110011; // bit 0 is used for signed mode, bit 1 is used for remdiv
+
+parameter ALU_SHUF  = 6'b111010;
+parameter ALU_SHUF2 = 6'b111011;
+parameter ALU_PCKLO = 6'b111000;
+parameter ALU_PCKHI = 6'b111001;
+
+
+parameter MD_OP_MULL  = 2'b00;
+parameter MD_OP_MULH  = 2'b01;
+parameter MD_OP_DIV   = 2'b10;
+parameter MD_OP_REM   = 2'b11;
+
+// vector modes
+parameter VEC_MODE32 = 2'b00;
+parameter VEC_MODE16 = 2'b10;
+parameter VEC_MODE8  = 2'b11;
+
+
+/////////////////////////////////////////////////////////
+//    ____ ____    ____            _     _             //
+//   / ___/ ___|  |  _ \ ___  __ _(_)___| |_ ___ _ __  //
+//  | |   \___ \  | |_) / _ \/ _` | / __| __/ _ \ '__| //
+//  | |___ ___) | |  _ <  __/ (_| | \__ \ ||  __/ |    //
+//   \____|____/  |_| \_\___|\__, |_|___/\__\___|_|    //
+//                           |___/                     //
+/////////////////////////////////////////////////////////
+
+// CSR operations
+parameter CSR_OP_NONE  = 2'b00;
+parameter CSR_OP_WRITE = 2'b01;
+parameter CSR_OP_SET   = 2'b10;
+parameter CSR_OP_CLEAR = 2'b11;
+
+
+// SPR for debugger, not accessible by CPU
+parameter SP_DVR0       = 16'h3000;
+parameter SP_DCR0       = 16'h3008;
+parameter SP_DMR1       = 16'h3010;
+parameter SP_DMR2       = 16'h3011;
+
+parameter SP_DVR_MSB = 8'h00;
+parameter SP_DCR_MSB = 8'h01;
+parameter SP_DMR_MSB = 8'h02;
+parameter SP_DSR_MSB = 8'h04;
+
+// Privileged mode
+typedef enum logic[1:0] {
+  PRIV_LVL_M = 2'b11,
+  PRIV_LVL_H = 2'b10,
+  PRIV_LVL_S = 2'b01,
+  PRIV_LVL_U = 2'b00
+} PrivLvl_t;
+
+///////////////////////////////////////////////
+//   ___ ____    ____  _                     //
+//  |_ _|  _ \  / ___|| |_ __ _  __ _  ___   //
+//   | || | | | \___ \| __/ _` |/ _` |/ _ \  //
+//   | || |_| |  ___) | || (_| | (_| |  __/  //
+//  |___|____/  |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// forwarding operand mux
+parameter SEL_REGFILE      = 2'b00;
+parameter SEL_FW_EX        = 2'b01;
+parameter SEL_FW_WB        = 2'b10;
+parameter SEL_MISALIGNED   = 2'b11;
+
+// operand a selection
+parameter OP_A_REGA_OR_FWD = 3'b000;
+parameter OP_A_CURRPC      = 3'b001;
+parameter OP_A_IMM         = 3'b010;
+parameter OP_A_REGB_OR_FWD = 3'b011;
+parameter OP_A_REGC_OR_FWD = 3'b100;
+
+// immediate a selection
+parameter IMMA_Z      = 1'b0;
+parameter IMMA_ZERO   = 1'b1;
+
+// operand b selection
+parameter OP_B_REGB_OR_FWD = 3'b000;
+parameter OP_B_REGC_OR_FWD = 3'b001;
+parameter OP_B_IMM         = 3'b010;
+parameter OP_B_REGA_OR_FWD = 3'b011;
+parameter OP_B_BMASK       = 3'b100;
+parameter OP_B_ZERO        = 3'b101;
+
+// immediate b selection
+parameter IMMB_I      = 4'b0000;
+parameter IMMB_S      = 4'b0001;
+parameter IMMB_U      = 4'b0010;
+parameter IMMB_PCINCR = 4'b0011;
+parameter IMMB_S2     = 4'b0100;
+parameter IMMB_S3     = 4'b0101;
+parameter IMMB_VS     = 4'b0110;
+parameter IMMB_VU     = 4'b0111;
+parameter IMMB_SHUF   = 4'b1000;
+parameter IMMB_CLIP   = 4'b1001;
+parameter IMMB_BI     = 4'b1011;
+parameter IMMB_UJ	  = 4'b1100;
+parameter IMMB_SB	  = 4'b1101;
+
+// bit mask selection
+parameter BMASK_A_ZERO = 1'b0;
+parameter BMASK_A_S3   = 1'b1;
+
+parameter BMASK_B_S2   = 2'b00;
+parameter BMASK_B_S3   = 2'b01;
+parameter BMASK_B_ZERO = 2'b10;
+parameter BMASK_B_ONE  = 2'b11;
+
+parameter BMASK_A_REG  = 1'b0;
+parameter BMASK_A_IMM  = 1'b1;
+parameter BMASK_B_REG  = 1'b0;
+parameter BMASK_B_IMM  = 1'b1;
+
+
+
+///////////////////////////////////////////////
+//   ___ _____   ____  _                     //
+//  |_ _|  ___| / ___|| |_ __ _  __ _  ___   //
+//   | || |_    \___ \| __/ _` |/ _` |/ _ \  //
+//   | ||  _|    ___) | || (_| | (_| |  __/  //
+//  |___|_|     |____/ \__\__,_|\__, |\___|  //
+//                              |___/        //
+///////////////////////////////////////////////
+
+// PC mux selector defines
+parameter PC_BOOT          = 3'b000;
+parameter PC_JUMP          = 3'b010;
+parameter PC_EXCEPTION     = 3'b100;
+parameter PC_ERET          = 3'b101;
+parameter PC_DBG_NPC       = 3'b111;
+
+// Exception PC mux selector defines
+parameter EXC_PC_ILLINSN   = 2'b00;
+parameter EXC_PC_ECALL     = 2'b01;
+parameter EXC_PC_LOAD      = 2'b10;
+parameter EXC_PC_STORE     = 2'b10;
+parameter EXC_PC_IRQ       = 2'b11;
+
+// Exception Cause
+parameter EXC_CAUSE_ILLEGAL_INSN = 6'h02;
+parameter EXC_CAUSE_BREAKPOINT   = 6'h03;
+parameter EXC_CAUSE_ECALL_MMODE  = 6'h0B;
+
+// Exceptions offsets
+// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
+// offset 00 to 7e is used for external interrupts
+parameter EXC_OFF_RST      = 8'h80;
+parameter EXC_OFF_ILLINSN  = 8'h84;
+parameter EXC_OFF_ECALL    = 8'h88;
+parameter EXC_OFF_LSUERR   = 8'h8c;
+
+
+// Debug module
+parameter DBG_SETS_W = 6;
+
+parameter DBG_SETS_IRQ    = 5;
+parameter DBG_SETS_ECALL  = 4;
+parameter DBG_SETS_EILL   = 3;
+parameter DBG_SETS_ELSU   = 2;
+parameter DBG_SETS_EBRK   = 1;
+parameter DBG_SETS_SSTE   = 0;
+
+parameter DBG_CAUSE_HALT   = 6'h1F;
+	input wire clk;
+	input wire rst_n;
+	input wire mult_en_i;
+	input wire div_en_i;
+	input wire [1:0] operator_i;
+	input wire [1:0] signed_mode_i;
+	input wire [31:0] op_a_i;
+	input wire [31:0] op_b_i;
+	input wire [33:0] alu_adder_ext_i;
+	input wire [31:0] alu_adder_i;
+	input wire equal_to_zero;
+	output reg [32:0] alu_operand_a_o;
+	output reg [32:0] alu_operand_b_o;
+	output reg [31:0] multdiv_result_o;
+	output wire ready_o;
+	reg [4:0] multdiv_state_q;
+	wire [4:0] multdiv_state_n;
+	reg [2:0] curr_state_q;
+	reg [32:0] accum_window_q;
+	wire [32:0] res_adder_l;
+	wire [32:0] res_adder_h;
+	reg [32:0] op_b_shift_q;
+	reg [32:0] op_a_shift_q;
+	wire [32:0] op_a_ext;
+	wire [32:0] op_b_ext;
+	wire [32:0] one_shift;
+	wire [32:0] op_a_bw_pp;
+	wire [32:0] op_a_bw_last_pp;
+	wire [31:0] b_0;
+	wire sign_a;
+	wire sign_b;
+	wire [32:0] next_reminder;
+	wire [32:0] next_quotient;
+	wire [32:0] op_remainder;
+	reg [31:0] op_numerator_q;
+	reg is_greater_equal;
+	wire div_change_sign;
+	wire rem_change_sign;
+	assign res_adder_l = alu_adder_ext_i[32:0];
+	assign res_adder_h = alu_adder_ext_i[33:1];
+	always @(*) begin
+		alu_operand_a_o = accum_window_q;
+		multdiv_result_o = (div_en_i ? accum_window_q[31:0] : res_adder_l);
+		case (operator_i)
+			MD_OP_MULL: alu_operand_b_o = op_a_bw_pp;
+			MD_OP_MULH:
+				if (curr_state_q == 3'd4)
+					alu_operand_b_o = op_a_bw_last_pp;
+				else
+					alu_operand_b_o = op_a_bw_pp;
+			default:
+				case (curr_state_q)
+					3'd0: begin
+						alu_operand_a_o = 33'b000000000000000000000000000000001;
+						alu_operand_b_o = {~op_b_i, 1'b1};
+					end
+					3'd1: begin
+						alu_operand_a_o = 33'b000000000000000000000000000000001;
+						alu_operand_b_o = {~op_a_i, 1'b1};
+					end
+					3'd2: begin
+						alu_operand_a_o = 33'b000000000000000000000000000000001;
+						alu_operand_b_o = {~op_b_i, 1'b1};
+					end
+					3'd5: begin
+						alu_operand_a_o = 33'b000000000000000000000000000000001;
+						alu_operand_b_o = {~accum_window_q[31:0], 1'b1};
+					end
+					default: begin
+						alu_operand_a_o = {accum_window_q[31:0], 1'b1};
+						alu_operand_b_o = {~op_b_shift_q[31:0], 1'b1};
+					end
+				endcase
+		endcase
+	end
+	always @(*)
+		if ((accum_window_q[31] ^ op_b_shift_q[31]) == 0)
+			is_greater_equal = res_adder_h[31] == 0;
+		else
+			is_greater_equal = accum_window_q[31];
+	assign one_shift = 33'b000000000000000000000000000000001 << multdiv_state_q;
+	assign next_reminder = (is_greater_equal ? res_adder_h : op_remainder);
+	assign next_quotient = (is_greater_equal ? op_a_shift_q | one_shift : op_a_shift_q);
+	assign b_0 = {32 {op_b_shift_q[0]}};
+	assign op_a_bw_pp = {~(op_a_shift_q[32] & op_b_shift_q[0]), op_a_shift_q[31:0] & b_0};
+	assign op_a_bw_last_pp = {op_a_shift_q[32] & op_b_shift_q[0], ~(op_a_shift_q[31:0] & b_0)};
+	assign sign_a = op_a_i[31] & signed_mode_i[0];
+	assign sign_b = op_b_i[31] & signed_mode_i[1];
+	assign op_a_ext = {sign_a, op_a_i};
+	assign op_b_ext = {sign_b, op_b_i};
+	assign op_remainder = accum_window_q[32:0];
+	assign multdiv_state_n = multdiv_state_q - 1;
+	assign div_change_sign = sign_a ^ sign_b;
+	assign rem_change_sign = sign_a;
+	always @(posedge clk or negedge rst_n) begin : proc_multdiv_state_q
+		if (~rst_n) begin
+			multdiv_state_q <= 1'sb0;
+			accum_window_q <= 1'sb0;
+			op_b_shift_q <= 1'sb0;
+			op_a_shift_q <= 1'sb0;
+			curr_state_q <= 3'd0;
+			op_numerator_q <= 1'sb0;
+		end
+		else if (mult_en_i | div_en_i)
+			case (curr_state_q)
+				3'd0: begin
+					case (operator_i)
+						MD_OP_MULL: begin
+							op_a_shift_q <= op_a_ext << 1;
+							accum_window_q <= {~(op_a_ext[32] & op_b_i[0]), op_a_ext[31:0] & {32 {op_b_i[0]}}};
+							op_b_shift_q <= op_b_ext >> 1;
+							curr_state_q <= 3'd3;
+						end
+						MD_OP_MULH: begin
+							op_a_shift_q <= op_a_ext;
+							accum_window_q <= {1'b1, ~(op_a_ext[32] & op_b_i[0]), op_a_ext[31:1] & {31 {op_b_i[0]}}};
+							op_b_shift_q <= op_b_ext >> 1;
+							curr_state_q <= 3'd3;
+						end
+						MD_OP_DIV: begin
+							accum_window_q <= 1'sb1;
+							curr_state_q <= (equal_to_zero ? 3'd6 : 3'd1);
+						end
+						default: begin
+							accum_window_q <= op_a_ext;
+							curr_state_q <= (equal_to_zero ? 3'd6 : 3'd1);
+						end
+					endcase
+					multdiv_state_q <= 5'd31;
+				end
+				3'd1: begin
+					op_a_shift_q <= 1'sb0;
+					op_numerator_q <= (sign_a ? alu_adder_i : op_a_i);
+					curr_state_q <= 3'd2;
+				end
+				3'd2: begin
+					accum_window_q <= {32'h00000000, op_numerator_q[31]};
+					op_b_shift_q <= (sign_b ? alu_adder_i : op_b_i);
+					curr_state_q <= 3'd3;
+				end
+				3'd3: begin
+					multdiv_state_q <= multdiv_state_n;
+					case (operator_i)
+						MD_OP_MULL: begin
+							accum_window_q <= res_adder_l;
+							op_a_shift_q <= op_a_shift_q << 1;
+							op_b_shift_q <= op_b_shift_q >> 1;
+						end
+						MD_OP_MULH: begin
+							accum_window_q <= res_adder_h;
+							op_a_shift_q <= op_a_shift_q;
+							op_b_shift_q <= op_b_shift_q >> 1;
+						end
+						default: begin
+							accum_window_q <= {next_reminder[31:0], op_numerator_q[multdiv_state_n]};
+							op_a_shift_q <= next_quotient;
+						end
+					endcase
+					if (multdiv_state_q == 5'd1)
+						curr_state_q <= 3'd4;
+					else
+						curr_state_q <= 3'd3;
+				end
+				3'd4:
+					case (operator_i)
+						MD_OP_MULL: begin
+							accum_window_q <= res_adder_l;
+							curr_state_q <= 3'd0;
+						end
+						MD_OP_MULH: begin
+							accum_window_q <= res_adder_l;
+							curr_state_q <= 3'd0;
+						end
+						MD_OP_DIV: begin
+							accum_window_q <= next_quotient;
+							curr_state_q <= 3'd5;
+						end
+						default: begin
+							accum_window_q <= {1'b0, next_reminder[31:0]};
+							curr_state_q <= 3'd5;
+						end
+					endcase
+				3'd5: begin
+					curr_state_q <= 3'd6;
+					case (operator_i)
+						MD_OP_DIV: accum_window_q <= (div_change_sign ? alu_adder_i : accum_window_q);
+						default: accum_window_q <= (rem_change_sign ? alu_adder_i : accum_window_q);
+					endcase
+				end
+				3'd6: curr_state_q <= 3'd0;
+				default:
+					;
+			endcase
+	end
+	assign ready_o = (curr_state_q == 3'd6) | ((curr_state_q == 3'd4) & ((operator_i == MD_OP_MULL) | (operator_i == MD_OP_MULH)));
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_prefetch_buffer.v b/verilog/rtl/ips/zero-riscy/zeroriscy_prefetch_buffer.v
new file mode 100644
index 0000000..a10d2d5
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_prefetch_buffer.v
@@ -0,0 +1,146 @@
+module zeroriscy_prefetch_buffer (
+	clk,
+	rst_n,
+	req_i,
+	branch_i,
+	addr_i,
+	ready_i,
+	valid_o,
+	rdata_o,
+	addr_o,
+	instr_req_o,
+	instr_gnt_i,
+	instr_addr_o,
+	instr_rdata_i,
+	instr_rvalid_i,
+	busy_o
+);
+	input wire clk;
+	input wire rst_n;
+	input wire req_i;
+	input wire branch_i;
+	input wire [31:0] addr_i;
+	input wire ready_i;
+	output wire valid_o;
+	output wire [31:0] rdata_o;
+	output wire [31:0] addr_o;
+	output reg instr_req_o;
+	input wire instr_gnt_i;
+	output reg [31:0] instr_addr_o;
+	input wire [31:0] instr_rdata_i;
+	input wire instr_rvalid_i;
+	output wire busy_o;
+	reg [1:0] CS;
+	reg [1:0] NS;
+	reg [31:0] instr_addr_q;
+	wire [31:0] fetch_addr;
+	reg addr_valid;
+	reg fifo_valid;
+	wire fifo_ready;
+	reg fifo_clear;
+	wire valid_stored;
+	assign busy_o = (CS != 2'd0) || instr_req_o;
+	zeroriscy_fetch_fifo fifo_i(
+		.clk(clk),
+		.rst_n(rst_n),
+		.clear_i(fifo_clear),
+		.in_addr_i(instr_addr_q),
+		.in_rdata_i(instr_rdata_i),
+		.in_valid_i(fifo_valid),
+		.in_ready_o(fifo_ready),
+		.out_valid_o(valid_o),
+		.out_ready_i(ready_i),
+		.out_rdata_o(rdata_o),
+		.out_addr_o(addr_o),
+		.out_valid_stored_o(valid_stored)
+	);
+	assign fetch_addr = {instr_addr_q[31:2], 2'b00} + 32'd4;
+	always @(*) fifo_clear = branch_i;
+	always @(*) begin
+		instr_req_o = 1'b0;
+		instr_addr_o = fetch_addr;
+		fifo_valid = 1'b0;
+		addr_valid = 1'b0;
+		NS = CS;
+		case (CS)
+			2'd0: begin
+				instr_addr_o = fetch_addr;
+				instr_req_o = 1'b0;
+				if (branch_i)
+					instr_addr_o = addr_i;
+				if (req_i & (fifo_ready | branch_i)) begin
+					instr_req_o = 1'b1;
+					addr_valid = 1'b1;
+					if (instr_gnt_i)
+						NS = 2'd2;
+					else
+						NS = 2'd1;
+				end
+			end
+			2'd1: begin
+				instr_addr_o = instr_addr_q;
+				instr_req_o = 1'b1;
+				if (branch_i) begin
+					instr_addr_o = addr_i;
+					addr_valid = 1'b1;
+				end
+				if (instr_gnt_i)
+					NS = 2'd2;
+				else
+					NS = 2'd1;
+			end
+			2'd2: begin
+				instr_addr_o = fetch_addr;
+				if (branch_i)
+					instr_addr_o = addr_i;
+				if (req_i & (fifo_ready | branch_i)) begin
+					if (instr_rvalid_i) begin
+						instr_req_o = 1'b1;
+						fifo_valid = 1'b1;
+						addr_valid = 1'b1;
+						if (instr_gnt_i)
+							NS = 2'd2;
+						else
+							NS = 2'd1;
+					end
+					else if (branch_i) begin
+						addr_valid = 1'b1;
+						NS = 2'd3;
+					end
+				end
+				else if (instr_rvalid_i) begin
+					fifo_valid = 1'b1;
+					NS = 2'd0;
+				end
+			end
+			2'd3: begin
+				instr_addr_o = instr_addr_q;
+				if (branch_i) begin
+					instr_addr_o = addr_i;
+					addr_valid = 1'b1;
+				end
+				if (instr_rvalid_i) begin
+					instr_req_o = 1'b1;
+					if (instr_gnt_i)
+						NS = 2'd2;
+					else
+						NS = 2'd1;
+				end
+			end
+			default: begin
+				NS = 2'd0;
+				instr_req_o = 1'b0;
+			end
+		endcase
+	end
+	always @(posedge clk or negedge rst_n)
+		if (rst_n == 1'b0) begin
+			CS <= 2'd0;
+			instr_addr_q <= 1'sb0;
+		end
+		else begin
+			CS <= NS;
+			if (addr_valid)
+				instr_addr_q <= instr_addr_o;
+		end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_register_file.v b/verilog/rtl/ips/zero-riscy/zeroriscy_register_file.v
new file mode 100644
index 0000000..b0498d0
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_register_file.v
@@ -0,0 +1,88 @@
+module zeroriscy_register_file 
+#(
+  parameter RV32E         = 0,
+  parameter DATA_WIDTH    = 32
+)
+(
+	clk,
+	rst_n,
+	test_en_i,
+	raddr_a_i,
+	rdata_a_o,
+	raddr_b_i,
+	rdata_b_o,
+	waddr_a_i,
+	wdata_a_i,
+	we_a_i
+);
+	//parameter RV32E = 0;
+	//parameter DATA_WIDTH = 32;
+	input wire clk;
+	input wire rst_n;
+	input wire test_en_i;
+	input wire [4:0] raddr_a_i;
+	output wire [DATA_WIDTH - 1:0] rdata_a_o;
+	input wire [4:0] raddr_b_i;
+	output wire [DATA_WIDTH - 1:0] rdata_b_o;
+	input wire [4:0] waddr_a_i;
+	input wire [DATA_WIDTH - 1:0] wdata_a_i;
+	input wire we_a_i;
+	localparam ADDR_WIDTH = (RV32E ? 4 : 5);
+	localparam NUM_WORDS = 2 ** ADDR_WIDTH;
+	reg [DATA_WIDTH - 1:0] mem [0:NUM_WORDS - 1];
+	reg [NUM_WORDS - 1:1] waddr_onehot_a;
+	wire [NUM_WORDS - 1:1] mem_clocks;
+	reg [DATA_WIDTH - 1:0] wdata_a_q;
+	wire [ADDR_WIDTH - 1:0] raddr_a_int;
+	wire [ADDR_WIDTH - 1:0] raddr_b_int;
+	wire [ADDR_WIDTH - 1:0] waddr_a_int;
+	assign raddr_a_int = raddr_a_i[ADDR_WIDTH - 1:0];
+	assign raddr_b_int = raddr_b_i[ADDR_WIDTH - 1:0];
+	assign waddr_a_int = waddr_a_i[ADDR_WIDTH - 1:0];
+	wire clk_int;
+	reg [31:0] i;
+	wire [31:0] j;
+	reg [31:0] k;
+	genvar x;
+	assign rdata_a_o = mem[raddr_a_int];
+	assign rdata_b_o = mem[raddr_b_int];
+	cluster_clock_gating CG_WE_GLOBAL(
+		.clk_i(clk),
+		.en_i(we_a_i),
+		.test_en_i(test_en_i),
+		.clk_o(clk_int)
+	);
+	always @(posedge clk_int or negedge rst_n) begin : sample_waddr
+		if (~rst_n)
+			wdata_a_q <= 1'sb0;
+		else if (we_a_i)
+			wdata_a_q <= wdata_a_i;
+	end
+	always @(*) begin : p_WADa
+		for (i = 1; i < NUM_WORDS; i = i + 1)
+			begin : p_WordItera
+				if ((we_a_i == 1'b1) && (waddr_a_int == i))
+					waddr_onehot_a[i] = 1'b1;
+				else
+					waddr_onehot_a[i] = 1'b0;
+			end
+	end
+	generate
+		for (x = 1; x < NUM_WORDS; x = x + 1) begin : CG_CELL_WORD_ITER
+			cluster_clock_gating CG_Inst(
+				.clk_i(clk_int),
+				.en_i(waddr_onehot_a[x]),
+				.test_en_i(test_en_i),
+				.clk_o(mem_clocks[x])
+			);
+		end
+	endgenerate
+	always @(*) begin : latch_wdata
+		mem[0] = 1'sb0;
+		for (k = 1; k < NUM_WORDS; k = k + 1)
+			begin : w_WordIter
+				if (mem_clocks[k] == 1'b1)
+					mem[k] = wdata_a_q;
+			end
+	end
+endmodule
diff --git a/verilog/rtl/ips/zero-riscy/zeroriscy_register_file_ff.v b/verilog/rtl/ips/zero-riscy/zeroriscy_register_file_ff.v
new file mode 100644
index 0000000..418a6db
--- /dev/null
+++ b/verilog/rtl/ips/zero-riscy/zeroriscy_register_file_ff.v
@@ -0,0 +1,60 @@
+module zeroriscy_register_file 
+#(
+  parameter RV32E         = 0,
+  parameter DATA_WIDTH    = 32
+)
+(
+	clk,
+	rst_n,
+	test_en_i,
+	raddr_a_i,
+	rdata_a_o,
+	raddr_b_i,
+	rdata_b_o,
+	waddr_a_i,
+	wdata_a_i,
+	we_a_i
+);
+	//parameter RV32E = 0;
+	//parameter DATA_WIDTH = 32;
+	input wire clk;
+	input wire rst_n;
+	input wire test_en_i;
+	input wire [4:0] raddr_a_i;
+	output wire [DATA_WIDTH - 1:0] rdata_a_o;
+	input wire [4:0] raddr_b_i;
+	output wire [DATA_WIDTH - 1:0] rdata_b_o;
+	input wire [4:0] waddr_a_i;
+	input wire [DATA_WIDTH - 1:0] wdata_a_i;
+	input wire we_a_i;
+	localparam ADDR_WIDTH = (RV32E ? 4 : 5);
+	localparam NUM_WORDS = 2 ** ADDR_WIDTH;
+	wire [(NUM_WORDS * DATA_WIDTH) - 1:0] rf_reg;
+	reg [(NUM_WORDS * DATA_WIDTH) - 1:0] rf_reg_tmp;
+	reg [NUM_WORDS - 1:0] we_a_dec;
+	always @(*) begin : we_a_decoder
+		begin : sv2v_autoblock_1
+			reg signed [31:0] i;
+			for (i = 0; i < NUM_WORDS; i = i + 1)
+				if (waddr_a_i == i)
+					we_a_dec[i] = we_a_i;
+				else
+					we_a_dec[i] = 1'b0;
+		end
+	end
+	genvar i;
+	generate
+		for (i = 1; i < NUM_WORDS; i = i + 1) begin : rf_gen
+			always @(posedge clk or negedge rst_n) begin : register_write_behavioral
+				if (rst_n == 1'b0)
+					rf_reg_tmp[i * DATA_WIDTH+:DATA_WIDTH] <= 'b0;
+				else if (we_a_dec[i])
+					rf_reg_tmp[i * DATA_WIDTH+:DATA_WIDTH] <= wdata_a_i;
+			end
+		end
+	endgenerate
+	assign rf_reg[0+:DATA_WIDTH] = 1'sb0;
+	assign rf_reg[DATA_WIDTH * (((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : ((NUM_WORDS - 1) + ((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS)) - 1) - (((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS) - 1))+:DATA_WIDTH * ((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS)] = rf_reg_tmp[DATA_WIDTH * (((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : ((NUM_WORDS - 1) + ((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS)) - 1) - (((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS) - 1))+:DATA_WIDTH * ((NUM_WORDS - 1) >= 1 ? NUM_WORDS - 1 : 3 - NUM_WORDS)];
+	assign rdata_a_o = rf_reg[raddr_a_i * DATA_WIDTH+:DATA_WIDTH];
+	assign rdata_b_o = rf_reg[raddr_b_i * DATA_WIDTH+:DATA_WIDTH];
+endmodule