Add apb directory
diff --git a/verilog/rtl/ips/apb/apb2per b/verilog/rtl/ips/apb/apb2per
deleted file mode 160000
index 537239d..0000000
--- a/verilog/rtl/ips/apb/apb2per
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 537239d4eb9110774164fd9f13283bb2fe7d0ea5
diff --git a/verilog/rtl/ips/apb/apb2per/apb2per.v b/verilog/rtl/ips/apb/apb2per/apb2per.v
new file mode 100644
index 0000000..77bf637
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb2per/apb2per.v
@@ -0,0 +1,103 @@
+module apb2per 
+#(
+   parameter PER_ADDR_WIDTH = 32,
+   parameter APB_ADDR_WIDTH = 32
+)
+(
+	clk_i,
+	rst_ni,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	per_master_req_o,
+	per_master_add_o,
+	per_master_we_o,
+	per_master_wdata_o,
+	per_master_be_o,
+	per_master_gnt_i,
+	per_master_r_valid_i,
+	per_master_r_opc_i,
+	per_master_r_rdata_i
+);
+	//parameter PER_ADDR_WIDTH = 32;
+	//parameter APB_ADDR_WIDTH = 32;
+	input wire clk_i;
+	input wire rst_ni;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output wire [31:0] PRDATA;
+	output reg PREADY;
+	output wire PSLVERR;
+	output reg per_master_req_o;
+	output wire [PER_ADDR_WIDTH - 1:0] per_master_add_o;
+	output reg per_master_we_o;
+	output wire [31:0] per_master_wdata_o;
+	output wire [3:0] per_master_be_o;
+	input wire per_master_gnt_i;
+	input wire per_master_r_valid_i;
+	input wire per_master_r_opc_i;
+	input wire [31:0] per_master_r_rdata_i;
+	reg CS;
+	reg NS;
+	always @(posedge clk_i or negedge rst_ni)
+		if (rst_ni == 1'b0)
+			CS <= 1'd0;
+		else
+			CS <= NS;
+	always @(*) begin
+		per_master_we_o = 0;
+		per_master_req_o = 0;
+		PREADY = 0;
+		case (CS)
+			1'd0:
+				if ((PSEL == 1) && (PENABLE == 1)) begin
+					per_master_req_o = 1;
+					if (PWRITE == 1)
+						per_master_we_o = 1'b1;
+					else
+						per_master_we_o = 1'b0;
+					if (per_master_gnt_i == 1) begin
+						if (PWRITE == 1) begin
+							PREADY = 1;
+							NS = 1'd0;
+						end
+						else begin
+							PREADY = 0;
+							NS = 1'd1;
+						end
+					end
+					else begin
+						PREADY = 0;
+						NS = 1'd0;
+					end
+				end
+				else begin
+					NS = 1'd0;
+					PREADY = 0;
+				end
+			1'd1:
+				if (per_master_r_valid_i == 1) begin
+					PREADY = 1;
+					NS = 1'd0;
+				end
+				else begin
+					PREADY = 0;
+					NS = 1'd1;
+				end
+			default: NS = 1'd0;
+		endcase
+	end
+	assign PRDATA = per_master_r_rdata_i;
+	assign PSLVERR = 1'sb0;
+	assign per_master_add_o = PADDR;
+	assign per_master_wdata_o = PWDATA;
+	assign per_master_be_o = 1'sb1;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_event_unit b/verilog/rtl/ips/apb/apb_event_unit
deleted file mode 160000
index 0cd8574..0000000
--- a/verilog/rtl/ips/apb/apb_event_unit
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0cd85740331eeec70d39ef9e68e536b6dbe16f2a
diff --git a/verilog/rtl/ips/apb/apb_event_unit/apb_event_unit.v b/verilog/rtl/ips/apb/apb_event_unit/apb_event_unit.v
new file mode 100644
index 0000000..39ef403
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_event_unit/apb_event_unit.v
@@ -0,0 +1,124 @@
+module apb_event_unit 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	clk_i,
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	irq_i,
+	event_i,
+	irq_o,
+	fetch_enable_i,
+	fetch_enable_o,
+	clk_gate_core_o,
+	core_busy_i
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire clk_i;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output reg PREADY;
+	output reg PSLVERR;
+	input wire [31:0] irq_i;
+	input wire [31:0] event_i;
+	output wire [31:0] irq_o;
+	input wire fetch_enable_i;
+	output wire fetch_enable_o;
+	output wire clk_gate_core_o;
+	input wire core_busy_i;
+	wire [31:0] events;
+	reg [2:0] psel_int;
+	wire [2:0] pready;
+	wire [2:0] pslverr;
+	wire [1:0] slave_address_int;
+	wire [95:0] prdata;
+	reg fetch_enable_ff1;
+	reg fetch_enable_ff2;
+	wire fetch_enable_int;
+	assign fetch_enable_o = fetch_enable_ff2 & fetch_enable_int;
+	assign slave_address_int = PADDR[5:4];
+	always @(*) begin
+		psel_int = 3'b000;
+		psel_int[slave_address_int] = PSEL;
+	end
+	always @(*)
+		if (psel_int != 2'b00) begin
+			PRDATA = prdata[slave_address_int * 32+:32];
+			PREADY = pready[slave_address_int];
+			PSLVERR = pslverr[slave_address_int];
+		end
+		else begin
+			PRDATA = 1'sb0;
+			PREADY = 1'b1;
+			PSLVERR = 1'b0;
+		end
+	generic_service_unit #(.APB_ADDR_WIDTH(APB_ADDR_WIDTH)) i_interrupt_unit(
+		.HCLK(HCLK),
+		.HRESETn(HRESETn),
+		.PADDR(PADDR),
+		.PWDATA(PWDATA),
+		.PWRITE(PWRITE),
+		.PSEL(psel_int[0]),
+		.PENABLE(PENABLE),
+		.PRDATA(prdata[0+:32]),
+		.PREADY(pready[0]),
+		.PSLVERR(pslverr[0]),
+		.signal_i(irq_i),
+		.irq_o(irq_o)
+	);
+	generic_service_unit #(.APB_ADDR_WIDTH(APB_ADDR_WIDTH)) i_event_unit(
+		.HCLK(HCLK),
+		.HRESETn(HRESETn),
+		.PADDR(PADDR),
+		.PWDATA(PWDATA),
+		.PWRITE(PWRITE),
+		.PSEL(psel_int[1]),
+		.PENABLE(PENABLE),
+		.PRDATA(prdata[32+:32]),
+		.PREADY(pready[1]),
+		.PSLVERR(pslverr[1]),
+		.signal_i(event_i),
+		.irq_o(events)
+	);
+	sleep_unit #(.APB_ADDR_WIDTH(APB_ADDR_WIDTH)) i_sleep_unit(
+		.HCLK(HCLK),
+		.HRESETn(HRESETn),
+		.PADDR(PADDR),
+		.PWDATA(PWDATA),
+		.PWRITE(PWRITE),
+		.PSEL(psel_int[2]),
+		.PENABLE(PENABLE),
+		.PRDATA(prdata[64+:32]),
+		.PREADY(pready[2]),
+		.PSLVERR(pslverr[2]),
+		.irq_i(|irq_o),
+		.event_i(|events),
+		.core_busy_i(core_busy_i),
+		.fetch_en_o(fetch_enable_int),
+		.clk_gate_core_o(clk_gate_core_o)
+	);
+	always @(posedge clk_i or negedge HRESETn)
+		if (~HRESETn) begin
+			fetch_enable_ff1 <= 1'b0;
+			fetch_enable_ff2 <= 1'b0;
+		end
+		else begin
+			fetch_enable_ff1 <= fetch_enable_i;
+			fetch_enable_ff2 <= fetch_enable_ff1;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_event_unit/generic_service_unit.v b/verilog/rtl/ips/apb/apb_event_unit/generic_service_unit.v
new file mode 100644
index 0000000..4da1941
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_event_unit/generic_service_unit.v
@@ -0,0 +1,102 @@
+module generic_service_unit 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	signal_i,
+	irq_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	input wire [31:0] signal_i;
+	output reg [31:0] irq_o;
+	reg [127:0] regs_q;
+	reg [127:0] regs_n;
+	reg [4:0] highest_pending_int;
+	wire [1:0] register_adr;
+	reg [31:0] irq_n;
+	assign register_adr = PADDR[3:2];
+	always @(*) begin : sv2v_autoblock_1
+		reg [0:1] _sv2v_jump;
+		_sv2v_jump = 2'b00;
+		highest_pending_int = 'b0;
+		irq_n = 32'b00000000000000000000000000000000;
+		begin : sv2v_autoblock_2
+			reg signed [31:0] i;
+			for (i = 0; i < 32; i = i + 1)
+				if (_sv2v_jump < 2'b10) begin
+					_sv2v_jump = 2'b00;
+					if (regs_q[64 + i]) begin
+						highest_pending_int = i;
+						_sv2v_jump = 2'b10;
+					end
+				end
+			if (_sv2v_jump != 2'b11)
+				_sv2v_jump = 2'b00;
+		end
+		if (_sv2v_jump == 2'b00)
+			if (regs_q[64+:32] != 'b0)
+				irq_n[highest_pending_int] = 1'b1;
+	end
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+	reg [31:0] pending_int;
+	always @(*) begin
+		regs_n = regs_q;
+		regs_n[32+:32] = 32'b00000000000000000000000000000000;
+		regs_n[0+:32] = 32'b00000000000000000000000000000000;
+		pending_int = (regs_q[96+:32] & signal_i) | regs_q[64+:32];
+		pending_int = pending_int | regs_q[32+:32];
+		begin : sv2v_autoblock_3
+			reg signed [31:0] i;
+			for (i = 0; i < 32; i = i + 1)
+				if (regs_q[i])
+					pending_int[i] = 1'b0;
+		end
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				2'b00: regs_n[96+:32] = PWDATA;
+				2'b01: pending_int = PWDATA;
+				2'b10: regs_n[32+:32] = PWDATA;
+				2'b11: regs_n[0+:32] = PWDATA;
+			endcase
+		regs_n[64+:32] = pending_int;
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				2'b00: PRDATA = regs_q[96+:32];
+				2'b01: PRDATA = regs_q[64+:32];
+				default: PRDATA = 'b0;
+			endcase
+	end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			regs_q <= {4 {32'b00000000000000000000000000000000}};
+			irq_o <= 32'b00000000000000000000000000000000;
+		end
+		else begin
+			regs_q <= regs_n;
+			irq_o <= irq_n;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_event_unit/include/defines_event_unit.sv b/verilog/rtl/ips/apb/apb_event_unit/include/defines_event_unit.sv
new file mode 100644
index 0000000..5ff98a5
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_event_unit/include/defines_event_unit.sv
@@ -0,0 +1,44 @@
+// 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.
+
+///////////////////////////////////////////////
+//  _____            _     _                 //
+// |  __ \          (_)   | |                //
+// | |__) |___  __ _ _ ___| |_ ___ _ __ ___  //
+// |  _  // _ \/ _` | / __| __/ _ \ '__/ __| //
+// | | \ \  __/ (_| | \__ \ ||  __/ |  \__ \ //
+// |_|  \_\___|\__, |_|___/\__\___|_|  |___/ //
+//              __/ |                        //
+//             |___/                         //
+///////////////////////////////////////////////
+
+// total number of address space reserved for the apb_event_unit
+`define ADR_MAX_ADR				'd2 // number of bits needed to access all subunits
+
+`define IRQ						2'b00
+`define EVENT					2'b01
+`define SLEEP					2'b10
+
+// number of registers per (interrupt, event) service unit - 8 regs in total
+`define REGS_MAX_IDX			'd3 // number of bits needed to access all registers
+`define REGS_MAX_ADR				'd2
+
+`define REG_ENABLE 				2'b00
+`define REG_PENDING      		2'b01
+`define REG_SET_PENDING			2'b10
+`define REG_CLEAR_PENDING		2'b11
+
+`define REGS_SLEEP_MAX_IDX		'd1
+
+`define REG_SLEEP_CTRL        	2'b0
+`define REG_SLEEP_STATUS		2'b1
+
+`define SLEEP_ENABLE			1'b0
+`define SLEEP_STATUS 			1'b0
diff --git a/verilog/rtl/ips/apb/apb_event_unit/sleep_unit.v b/verilog/rtl/ips/apb/apb_event_unit/sleep_unit.v
new file mode 100644
index 0000000..21d8e5d
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_event_unit/sleep_unit.v
@@ -0,0 +1,118 @@
+module sleep_unit 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	irq_i,
+	event_i,
+	core_busy_i,
+	fetch_en_o,
+	clk_gate_core_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	input wire irq_i;
+	input wire event_i;
+	input wire core_busy_i;
+	output reg fetch_en_o;
+	output reg clk_gate_core_o;
+	reg [1:0] SLEEP_STATE_N;
+	reg [1:0] SLEEP_STATE_Q;
+	reg [63:0] regs_q;
+	reg [63:0] regs_n;
+	reg core_sleeping_int;
+	always @(*) begin
+		SLEEP_STATE_N = SLEEP_STATE_Q;
+		case (SLEEP_STATE_Q)
+			2'd0:
+				if (regs_q[32])
+					if (~event_i)
+						SLEEP_STATE_N = 2'd1;
+			2'd1:
+				if (event_i)
+					SLEEP_STATE_N = 2'd0;
+				else if (~core_busy_i && ~irq_i)
+					SLEEP_STATE_N = 2'd2;
+			2'd2:
+				if (event_i)
+					SLEEP_STATE_N = 2'd0;
+				else if (irq_i)
+					SLEEP_STATE_N = 2'd1;
+			default: SLEEP_STATE_N = 2'd0;
+		endcase
+	end
+	always @(*) begin
+		fetch_en_o = 1'b1;
+		clk_gate_core_o = 1'b1;
+		core_sleeping_int = 1'b0;
+		case (SLEEP_STATE_Q)
+			2'd0:
+				if (regs_q[32] && ~event_i)
+					fetch_en_o = 1'b0;
+				else
+					fetch_en_o = 1'b1;
+			2'd1: fetch_en_o = 1'b0;
+			2'd2: begin
+				clk_gate_core_o = (event_i ? 1'b1 : 1'b0);
+				core_sleeping_int = 1'b1;
+				fetch_en_o = 1'b0;
+			end
+			default: begin
+				fetch_en_o = 1'b1;
+				clk_gate_core_o = 1'b1;
+				core_sleeping_int = 1'b0;
+			end
+		endcase
+	end
+	wire [0:0] register_adr;
+	assign register_adr = PADDR[3:2];
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+	always @(*) begin
+		regs_n = regs_q;
+		regs_n[1'b0] = core_sleeping_int;
+		if (core_sleeping_int || event_i)
+			regs_n[32] = 1'b0;
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				2'b00: regs_n[32+:32] = PWDATA;
+			endcase
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				2'b00: PRDATA = regs_q[32+:32];
+				2'b01: PRDATA = regs_q[0+:32];
+				default: PRDATA = 'b0;
+			endcase
+	end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			SLEEP_STATE_Q <= 2'd0;
+			regs_q <= {2 {32'b00000000000000000000000000000000}};
+		end
+		else begin
+			SLEEP_STATE_Q <= SLEEP_STATE_N;
+			regs_q <= regs_n;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_fll_if b/verilog/rtl/ips/apb/apb_fll_if
deleted file mode 160000
index d07fbbd..0000000
--- a/verilog/rtl/ips/apb/apb_fll_if
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d07fbbdb910aab39a6d471de30c1913370d662e4
diff --git a/verilog/rtl/ips/apb/apb_fll_if/apb_fll_if.v b/verilog/rtl/ips/apb/apb_fll_if/apb_fll_if.v
new file mode 100644
index 0000000..154f629
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_fll_if/apb_fll_if.v
@@ -0,0 +1,202 @@
+module apb_fll_if 
+#(
+    parameter APB_ADDR_WIDTH = 12
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	fll1_req,
+	fll1_wrn,
+	fll1_add,
+	fll1_data,
+	fll1_ack,
+	fll1_r_data,
+	fll1_lock,
+	fll2_req,
+	fll2_wrn,
+	fll2_add,
+	fll2_data,
+	fll2_ack,
+	fll2_r_data,
+	fll2_lock
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output wire [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output reg fll1_req;
+	output wire fll1_wrn;
+	output wire [1:0] fll1_add;
+	output wire [31:0] fll1_data;
+	input wire fll1_ack;
+	input wire [31:0] fll1_r_data;
+	input wire fll1_lock;
+	output reg fll2_req;
+	output wire fll2_wrn;
+	output wire [1:0] fll2_add;
+	output wire [31:0] fll2_data;
+	input wire fll2_ack;
+	input wire [31:0] fll2_r_data;
+	input wire fll2_lock;
+	reg fll1_rd_access;
+	reg fll1_wr_access;
+	reg fll2_rd_access;
+	reg fll2_wr_access;
+	reg read_ready;
+	reg write_ready;
+	reg [31:0] read_data;
+	reg rvalid;
+	reg fll1_ack_sync0;
+	reg fll1_ack_sync;
+	reg fll2_ack_sync0;
+	reg fll2_ack_sync;
+	reg fll1_lock_sync0;
+	reg fll1_lock_sync;
+	reg fll2_lock_sync0;
+	reg fll2_lock_sync;
+	reg fll1_valid;
+	reg fll2_valid;
+	reg [2:0] state;
+	reg [2:0] state_next;
+	always @(posedge HCLK or negedge HRESETn)
+		if (!HRESETn) begin
+			fll1_ack_sync0 <= 1'b0;
+			fll1_ack_sync <= 1'b0;
+			fll2_ack_sync0 <= 1'b0;
+			fll2_ack_sync <= 1'b0;
+			fll1_lock_sync0 <= 1'b0;
+			fll1_lock_sync <= 1'b0;
+			fll2_lock_sync0 <= 1'b0;
+			fll2_lock_sync <= 1'b0;
+			state <= 3'd0;
+		end
+		else begin
+			fll1_ack_sync0 <= fll1_ack;
+			fll1_ack_sync <= fll1_ack_sync0;
+			fll2_ack_sync0 <= fll2_ack;
+			fll2_ack_sync <= fll2_ack_sync0;
+			fll1_lock_sync0 <= fll1_lock;
+			fll1_lock_sync <= fll1_lock_sync0;
+			fll2_lock_sync0 <= fll2_lock;
+			fll2_lock_sync <= fll2_lock_sync0;
+			state <= state_next;
+		end
+	always @(*) begin
+		state_next = 3'd0;
+		rvalid = 1'b0;
+		fll1_req = 1'b0;
+		fll2_req = 1'b0;
+		fll1_valid = 1'b0;
+		fll2_valid = 1'b0;
+		case (state)
+			3'd0:
+				if (fll2_rd_access || fll2_wr_access) begin
+					fll2_valid = 1'b1;
+					state_next = 3'd3;
+				end
+				else if (fll1_rd_access || fll1_wr_access) begin
+					fll1_valid = 1'b1;
+					state_next = 3'd1;
+				end
+			3'd1:
+				if (fll1_ack_sync) begin
+					fll1_req = 1'b0;
+					fll1_valid = 1'b0;
+					state_next = 3'd2;
+					rvalid = 1'b1;
+				end
+				else begin
+					fll1_req = 1'b1;
+					fll1_valid = 1'b1;
+					state_next = 3'd1;
+				end
+			3'd2:
+				if (!fll1_ack_sync)
+					state_next = 3'd0;
+				else
+					state_next = 3'd2;
+			3'd3:
+				if (fll2_ack_sync) begin
+					fll2_req = 1'b0;
+					fll2_valid = 1'b0;
+					state_next = 3'd4;
+					rvalid = 1'b1;
+				end
+				else begin
+					fll2_req = 1'b1;
+					fll2_valid = 1'b1;
+					state_next = 3'd3;
+				end
+			3'd4:
+				if (!fll2_ack_sync)
+					state_next = 3'd0;
+				else
+					state_next = 3'd4;
+		endcase
+	end
+	always @(*) begin
+		fll1_wr_access = 1'b0;
+		fll2_wr_access = 1'b0;
+		write_ready = 1'b0;
+		if ((PSEL && PENABLE) && PWRITE)
+			case (PADDR[5:2])
+				4'b0000, 4'b0001, 4'b0010, 4'b0011: begin
+					fll1_wr_access = 1'b1;
+					write_ready = rvalid;
+				end
+				4'b0100, 4'b0101, 4'b0110, 4'b0111: begin
+					fll2_wr_access = 1'b1;
+					write_ready = rvalid;
+				end
+				default: write_ready = 1'b1;
+			endcase
+	end
+	always @(*) begin
+		fll1_rd_access = 1'b0;
+		fll2_rd_access = 1'b0;
+		read_ready = 1'b0;
+		read_data = 1'sb0;
+		if ((PSEL && PENABLE) && ~PWRITE)
+			case (PADDR[5:2])
+				4'b0000, 4'b0001, 4'b0010, 4'b0011: begin
+					fll1_rd_access = 1'b1;
+					read_data = fll1_r_data;
+					read_ready = rvalid;
+				end
+				4'b0100, 4'b0101, 4'b0110, 4'b0111: begin
+					fll2_rd_access = 1'b1;
+					read_data = fll2_r_data;
+					read_ready = rvalid;
+				end
+				4'b1000: begin
+					read_data[1:0] = {fll2_lock_sync, fll1_lock_sync};
+					read_ready = 1'b1;
+				end
+				default: read_ready = 1'b1;
+			endcase
+	end
+	assign fll1_wrn = (fll1_valid ? ~PWRITE : 1'b0);
+	assign fll1_add = (fll1_valid ? PADDR[3:2] : {2 {1'sb0}});
+	assign fll1_data = (fll1_valid ? PWDATA : {32 {1'sb0}});
+	assign fll2_wrn = (fll2_valid ? ~PWRITE : 1'b0);
+	assign fll2_add = (fll2_valid ? PADDR[3:2] : {2 {1'sb0}});
+	assign fll2_data = (fll2_valid ? PWDATA : {32 {1'sb0}});
+	assign PREADY = (PWRITE ? write_ready : read_ready);
+	assign PRDATA = read_data;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_gpio b/verilog/rtl/ips/apb/apb_gpio
deleted file mode 160000
index d6e1e85..0000000
--- a/verilog/rtl/ips/apb/apb_gpio
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d6e1e8593dad6043ff48c28a8a4158a1f396bd0e
diff --git a/verilog/rtl/ips/apb/apb_gpio/apb_gpio.v b/verilog/rtl/ips/apb/apb_gpio/apb_gpio.v
new file mode 100644
index 0000000..9278bd3
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_gpio/apb_gpio.v
@@ -0,0 +1,199 @@
+module apb_gpio 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	gpio_in,
+	gpio_in_sync,
+	gpio_out,
+	gpio_dir,
+	gpio_padcfg,
+	power_event,
+	interrupt
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	input wire [31:0] gpio_in;
+	output wire [31:0] gpio_in_sync;
+	output wire [31:0] gpio_out;
+	output wire [31:0] gpio_dir;
+	output reg [191:0] gpio_padcfg;
+	output reg power_event;
+	output reg interrupt;
+	reg [31:0] r_gpio_inten;
+	reg [31:0] r_gpio_inttype0;
+	reg [31:0] r_gpio_inttype1;
+	reg [31:0] r_gpio_out;
+	reg [31:0] r_gpio_dir;
+	reg [31:0] r_gpio_sync0;
+	reg [31:0] r_gpio_sync1;
+	reg [31:0] r_gpio_in;
+	reg [31:0] r_powerevent;
+	wire [31:0] s_gpio_rise;
+	wire [31:0] s_gpio_fall;
+	wire [31:0] s_is_int_rise;
+	wire [31:0] s_is_int_fall;
+	wire [31:0] s_is_int_lev0;
+	wire [31:0] s_is_int_lev1;
+	wire [31:0] s_is_int_all;
+	wire s_rise_int;
+	wire [3:0] s_apb_addr;
+	reg [31:0] r_status;
+	assign s_apb_addr = PADDR[5:2];
+	assign gpio_in_sync = r_gpio_sync1;
+	assign s_gpio_rise = r_gpio_sync1 & ~r_gpio_in;
+	assign s_gpio_fall = ~r_gpio_sync1 & r_gpio_in;
+	assign s_is_int_rise = (r_gpio_inttype1 & ~r_gpio_inttype0) & s_gpio_rise;
+	assign s_is_int_fall = (r_gpio_inttype1 & r_gpio_inttype0) & s_gpio_fall;
+	assign s_is_int_lev0 = (~r_gpio_inttype1 & r_gpio_inttype0) & ~r_gpio_in;
+	assign s_is_int_lev1 = (~r_gpio_inttype1 & ~r_gpio_inttype0) & r_gpio_in;
+	assign s_is_int_all = r_gpio_inten & (((s_is_int_rise | s_is_int_fall) | s_is_int_lev0) | s_is_int_lev1);
+	assign s_rise_int = |s_is_int_all;
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			interrupt <= 1'b0;
+			r_status <= 'h0;
+		end
+		else if (!interrupt && s_rise_int) begin
+			interrupt <= 1'b1;
+			r_status <= s_is_int_all;
+		end
+		else if ((((interrupt && PSEL) && PENABLE) && !PWRITE) && (s_apb_addr == 4'b0110)) begin
+			interrupt <= 1'b0;
+			r_status <= 'h0;
+		end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			r_gpio_sync0 <= 'h0;
+			r_gpio_sync1 <= 'h0;
+			r_gpio_in <= 'h0;
+		end
+		else begin
+			r_gpio_sync0 <= gpio_in;
+			r_gpio_sync1 <= r_gpio_sync0;
+			r_gpio_in <= r_gpio_sync1;
+		end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			r_gpio_inten <= 1'sb0;
+			r_gpio_inttype0 <= 1'sb0;
+			r_gpio_inttype1 <= 1'sb0;
+			r_gpio_out <= 1'sb0;
+			r_gpio_dir <= 1'sb0;
+			r_powerevent <= 1'sb0;
+			begin : sv2v_autoblock_1
+				reg signed [31:0] i;
+				for (i = 0; i < 32; i = i + 1)
+					gpio_padcfg[i * 6+:6] <= 6'b000010;
+			end
+		end
+		else if ((PSEL && PENABLE) && PWRITE)
+			case (s_apb_addr)
+				4'b0000: r_gpio_dir <= PWDATA;
+				4'b0010: r_gpio_out <= PWDATA;
+				4'b0011: r_gpio_inten <= PWDATA;
+				4'b0100: r_gpio_inttype0 <= PWDATA;
+				4'b0101: r_gpio_inttype1 <= PWDATA;
+				4'b0111: r_powerevent <= PWDATA;
+				4'b1000: begin
+					gpio_padcfg[0+:6] <= PWDATA[5:0];
+					gpio_padcfg[6+:6] <= PWDATA[13:8];
+					gpio_padcfg[12+:6] <= PWDATA[21:16];
+					gpio_padcfg[18+:6] <= PWDATA[29:24];
+				end
+				4'b1001: begin
+					gpio_padcfg[24+:6] <= PWDATA[5:0];
+					gpio_padcfg[30+:6] <= PWDATA[13:8];
+					gpio_padcfg[36+:6] <= PWDATA[21:16];
+					gpio_padcfg[42+:6] <= PWDATA[29:24];
+				end
+				4'b1010: begin
+					gpio_padcfg[48+:6] <= PWDATA[5:0];
+					gpio_padcfg[54+:6] <= PWDATA[13:8];
+					gpio_padcfg[60+:6] <= PWDATA[21:16];
+					gpio_padcfg[66+:6] <= PWDATA[29:24];
+				end
+				4'b1011: begin
+					gpio_padcfg[72+:6] <= PWDATA[5:0];
+					gpio_padcfg[78+:6] <= PWDATA[13:8];
+					gpio_padcfg[84+:6] <= PWDATA[21:16];
+					gpio_padcfg[90+:6] <= PWDATA[29:24];
+				end
+				4'b1100: begin
+					gpio_padcfg[96+:6] <= PWDATA[5:0];
+					gpio_padcfg[102+:6] <= PWDATA[13:8];
+					gpio_padcfg[108+:6] <= PWDATA[21:16];
+					gpio_padcfg[114+:6] <= PWDATA[29:24];
+				end
+				4'b1101: begin
+					gpio_padcfg[120+:6] <= PWDATA[5:0];
+					gpio_padcfg[126+:6] <= PWDATA[13:8];
+					gpio_padcfg[132+:6] <= PWDATA[21:16];
+					gpio_padcfg[138+:6] <= PWDATA[29:24];
+				end
+				4'b1110: begin
+					gpio_padcfg[144+:6] <= PWDATA[5:0];
+					gpio_padcfg[150+:6] <= PWDATA[13:8];
+					gpio_padcfg[156+:6] <= PWDATA[21:16];
+					gpio_padcfg[162+:6] <= PWDATA[29:24];
+				end
+				4'b1111: begin
+					gpio_padcfg[168+:6] <= PWDATA[5:0];
+					gpio_padcfg[174+:6] <= PWDATA[13:8];
+					gpio_padcfg[180+:6] <= PWDATA[21:16];
+					gpio_padcfg[186+:6] <= PWDATA[29:24];
+				end
+			endcase
+	always @(*)
+		case (s_apb_addr)
+			4'b0000: PRDATA = r_gpio_dir;
+			4'b0001: PRDATA = r_gpio_in;
+			4'b0010: PRDATA = r_gpio_out;
+			4'b0011: PRDATA = r_gpio_inten;
+			4'b0100: PRDATA = r_gpio_inttype0;
+			4'b0101: PRDATA = r_gpio_inttype1;
+			4'b0110: PRDATA = r_status;
+			4'b0111: PRDATA = r_powerevent;
+			4'b1000: PRDATA = {2'b00, gpio_padcfg[18+:6], 2'b00, gpio_padcfg[12+:6], 2'b00, gpio_padcfg[6+:6], 2'b00, gpio_padcfg[0+:6]};
+			4'b1001: PRDATA = {2'b00, gpio_padcfg[42+:6], 2'b00, gpio_padcfg[36+:6], 2'b00, gpio_padcfg[30+:6], 2'b00, gpio_padcfg[24+:6]};
+			4'b1010: PRDATA = {2'b00, gpio_padcfg[66+:6], 2'b00, gpio_padcfg[60+:6], 2'b00, gpio_padcfg[54+:6], 2'b00, gpio_padcfg[48+:6]};
+			4'b1011: PRDATA = {2'b00, gpio_padcfg[90+:6], 2'b00, gpio_padcfg[84+:6], 2'b00, gpio_padcfg[78+:6], 2'b00, gpio_padcfg[72+:6]};
+			4'b1100: PRDATA = {2'b00, gpio_padcfg[114+:6], 2'b00, gpio_padcfg[108+:6], 2'b00, gpio_padcfg[102+:6], 2'b00, gpio_padcfg[96+:6]};
+			4'b1101: PRDATA = {2'b00, gpio_padcfg[138+:6], 2'b00, gpio_padcfg[132+:6], 2'b00, gpio_padcfg[126+:6], 2'b00, gpio_padcfg[120+:6]};
+			4'b1110: PRDATA = {2'b00, gpio_padcfg[162+:6], 2'b00, gpio_padcfg[156+:6], 2'b00, gpio_padcfg[150+:6], 2'b00, gpio_padcfg[144+:6]};
+			4'b1111: PRDATA = {2'b00, gpio_padcfg[186+:6], 2'b00, gpio_padcfg[180+:6], 2'b00, gpio_padcfg[174+:6], 2'b00, gpio_padcfg[168+:6]};
+			default: PRDATA = 'h0;
+		endcase
+	always @(*) begin
+		power_event = 1'b0;
+		begin : sv2v_autoblock_2
+			reg signed [31:0] e;
+			for (e = 0; e < 32; e = e + 1)
+				if (r_powerevent[e] == 1'b1)
+					power_event = gpio_in[e];
+		end
+	end
+	assign gpio_out = r_gpio_out;
+	assign gpio_dir = r_gpio_dir;
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_i2c b/verilog/rtl/ips/apb/apb_i2c
deleted file mode 160000
index 0131c30..0000000
--- a/verilog/rtl/ips/apb/apb_i2c
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0131c30607897959862302caa3910bfd3e9a422d
diff --git a/verilog/rtl/ips/apb/apb_i2c/apb_i2c.v b/verilog/rtl/ips/apb/apb_i2c/apb_i2c.v
new file mode 100644
index 0000000..b0c102e
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_i2c/apb_i2c.v
@@ -0,0 +1,154 @@
+module apb_i2c 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	interrupt_o,
+	scl_pad_i,
+	scl_pad_o,
+	scl_padoen_o,
+	sda_pad_i,
+	sda_pad_o,
+	sda_padoen_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output reg interrupt_o;
+	input wire scl_pad_i;
+	output wire scl_pad_o;
+	output wire scl_padoen_o;
+	input wire sda_pad_i;
+	output wire sda_pad_o;
+	output wire sda_padoen_o;
+	wire [3:0] s_apb_addr;
+	reg [15:0] r_pre;
+	reg [7:0] r_ctrl;
+	reg [7:0] r_tx;
+	wire [7:0] s_rx;
+	reg [7:0] r_cmd;
+	wire [7:0] s_status;
+	wire s_done;
+	wire s_core_en;
+	wire s_ien;
+	wire s_irxack;
+	reg rxack;
+	reg tip;
+	reg irq_flag;
+	wire i2c_busy;
+	wire i2c_al;
+	reg al;
+	assign s_apb_addr = PADDR[5:2];
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			r_pre <= 'h0;
+			r_ctrl <= 'h0;
+			r_tx <= 'h0;
+			r_cmd <= 'h0;
+		end
+		else if ((PSEL && PENABLE) && PWRITE) begin
+			if (s_done | i2c_al)
+				r_cmd[7:4] <= 4'h0;
+			r_cmd[2:1] <= 2'b00;
+			r_cmd[0] <= 1'b0;
+			case (s_apb_addr)
+				3'b000: r_pre <= PWDATA[15:0];
+				3'b001: r_ctrl <= PWDATA[7:0];
+				3'b100: r_tx <= PWDATA[7:0];
+				3'b101:
+					if (s_core_en)
+						r_cmd <= PWDATA[7:0];
+			endcase
+		end
+		else begin
+			if (s_done | i2c_al)
+				r_cmd[7:4] <= 4'h0;
+			r_cmd[2:1] <= 2'b00;
+			r_cmd[0] <= 1'b0;
+		end
+	always @(*)
+		case (s_apb_addr)
+			3'b000: PRDATA = {16'h0000, r_pre};
+			3'b001: PRDATA = {24'h000000, r_ctrl};
+			3'b010: PRDATA = {24'h000000, s_rx};
+			3'b011: PRDATA = {24'h000000, s_status};
+			3'b100: PRDATA = {24'h000000, r_tx};
+			3'b101: PRDATA = {24'h000000, r_cmd};
+			default: PRDATA = 'h0;
+		endcase
+	wire sta = r_cmd[7];
+	wire sto = r_cmd[6];
+	wire rd = r_cmd[5];
+	wire wr = r_cmd[4];
+	wire ack = r_cmd[3];
+	wire iack = r_cmd[0];
+	assign s_core_en = r_ctrl[7];
+	assign s_ien = r_ctrl[6];
+	i2c_master_byte_ctrl byte_controller(
+		.clk(HCLK),
+		.nReset(HRESETn),
+		.ena(s_core_en),
+		.clk_cnt(r_pre),
+		.start(sta),
+		.stop(sto),
+		.read(rd),
+		.write(wr),
+		.ack_in(ack),
+		.din(r_tx),
+		.cmd_ack(s_done),
+		.ack_out(s_irxack),
+		.dout(s_rx),
+		.i2c_busy(i2c_busy),
+		.i2c_al(i2c_al),
+		.scl_i(scl_pad_i),
+		.scl_o(scl_pad_o),
+		.scl_oen(scl_padoen_o),
+		.sda_i(sda_pad_i),
+		.sda_o(sda_pad_o),
+		.sda_oen(sda_padoen_o)
+	);
+	always @(posedge HCLK or negedge HRESETn)
+		if (!HRESETn) begin
+			al <= 1'b0;
+			rxack <= 1'b0;
+			tip <= 1'b0;
+			irq_flag <= 1'b0;
+		end
+		else begin
+			al <= i2c_al | (al & ~sta);
+			rxack <= s_irxack;
+			tip <= rd | wr;
+			irq_flag <= ((s_done | i2c_al) | irq_flag) & ~iack;
+		end
+	always @(posedge HCLK or negedge HRESETn)
+		if (!HRESETn)
+			interrupt_o <= 1'b0;
+		else
+			interrupt_o <= irq_flag && s_ien;
+	assign s_status[7] = rxack;
+	assign s_status[6] = i2c_busy;
+	assign s_status[5] = al;
+	assign s_status[4:2] = 3'h0;
+	assign s_status[1] = tip;
+	assign s_status[0] = irq_flag;
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_i2c/i2c_master_bit_ctrl.v b/verilog/rtl/ips/apb/apb_i2c/i2c_master_bit_ctrl.v
new file mode 100644
index 0000000..5b856b6
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_i2c/i2c_master_bit_ctrl.v
@@ -0,0 +1,303 @@
+module i2c_master_bit_ctrl (
+	clk,
+	nReset,
+	ena,
+	clk_cnt,
+	cmd,
+	cmd_ack,
+	busy,
+	al,
+	din,
+	dout,
+	scl_i,
+	scl_o,
+	scl_oen,
+	sda_i,
+	sda_o,
+	sda_oen
+);
+	input clk;
+	input nReset;
+	input ena;
+	input [15:0] clk_cnt;
+	input [3:0] cmd;
+	output reg cmd_ack;
+	output reg busy;
+	output reg al;
+	input din;
+	output reg dout;
+	input scl_i;
+	output wire scl_o;
+	output reg scl_oen;
+	input sda_i;
+	output wire sda_o;
+	output reg sda_oen;
+	reg [1:0] cSCL;
+	reg [1:0] cSDA;
+	reg [2:0] fSCL;
+	reg [2:0] fSDA;
+	reg sSCL;
+	reg sSDA;
+	reg dSCL;
+	reg dSDA;
+	reg dscl_oen;
+	reg sda_chk;
+	reg clk_en;
+	reg slave_wait;
+	reg [15:0] cnt;
+	reg [13:0] filter_cnt;
+	reg [17:0] c_state;
+	always @(posedge clk) dscl_oen <= scl_oen;
+	always @(posedge clk or negedge nReset)
+		if (!nReset)
+			slave_wait <= 1'b0;
+		else
+			slave_wait <= ((scl_oen & ~dscl_oen) & ~sSCL) | (slave_wait & ~sSCL);
+	wire scl_sync = (dSCL & ~sSCL) & scl_oen;
+	always @(posedge clk or negedge nReset)
+		if (~nReset) begin
+			cnt <= 16'h0000;
+			clk_en <= 1'b1;
+		end
+		else if ((~|cnt || !ena) || scl_sync) begin
+			cnt <= clk_cnt;
+			clk_en <= 1'b1;
+		end
+		else if (slave_wait) begin
+			cnt <= cnt;
+			clk_en <= 1'b0;
+		end
+		else begin
+			cnt <= cnt - 16'h0001;
+			clk_en <= 1'b0;
+		end
+	always @(posedge clk or negedge nReset)
+		if (!nReset) begin
+			cSCL <= 2'b00;
+			cSDA <= 2'b00;
+		end
+		else begin
+			cSCL <= {cSCL[0], scl_i};
+			cSDA <= {cSDA[0], sda_i};
+		end
+	always @(posedge clk or negedge nReset)
+		if (!nReset)
+			filter_cnt <= 14'h0000;
+		else if (!ena)
+			filter_cnt <= 14'h0000;
+		else if (~|filter_cnt)
+			filter_cnt <= clk_cnt >> 2;
+		else
+			filter_cnt <= filter_cnt - 1;
+	always @(posedge clk or negedge nReset)
+		if (!nReset) begin
+			fSCL <= 3'b111;
+			fSDA <= 3'b111;
+		end
+		else if (~|filter_cnt) begin
+			fSCL <= {fSCL[1:0], cSCL[1]};
+			fSDA <= {fSDA[1:0], cSDA[1]};
+		end
+	always @(posedge clk or negedge nReset)
+		if (~nReset) begin
+			sSCL <= 1'b1;
+			sSDA <= 1'b1;
+			dSCL <= 1'b1;
+			dSDA <= 1'b1;
+		end
+		else begin
+			sSCL <= (&fSCL[2:1] | &fSCL[1:0]) | (fSCL[2] & fSCL[0]);
+			sSDA <= (&fSDA[2:1] | &fSDA[1:0]) | (fSDA[2] & fSDA[0]);
+			dSCL <= sSCL;
+			dSDA <= sSDA;
+		end
+	reg sta_condition;
+	reg sto_condition;
+	always @(posedge clk or negedge nReset)
+		if (~nReset) begin
+			sta_condition <= 1'b0;
+			sto_condition <= 1'b0;
+		end
+		else begin
+			sta_condition <= (~sSDA & dSDA) & sSCL;
+			sto_condition <= (sSDA & ~dSDA) & sSCL;
+		end
+	always @(posedge clk or negedge nReset)
+		if (!nReset)
+			busy <= 1'b0;
+		else
+			busy <= (sta_condition | busy) & ~sto_condition;
+	reg cmd_stop;
+	always @(posedge clk or negedge nReset)
+		if (~nReset)
+			cmd_stop <= 1'b0;
+		else if (clk_en)
+			cmd_stop <= cmd == 4'b0010;
+	always @(posedge clk or negedge nReset)
+		if (~nReset)
+			al <= 1'b0;
+		else
+			al <= ((sda_chk & ~sSDA) & sda_oen) | ((|c_state & sto_condition) & ~cmd_stop);
+	always @(posedge clk)
+		if (sSCL & ~dSCL)
+			dout <= sSDA;
+	parameter [17:0] idle = 18'b000000000000000000;
+	parameter [17:0] start_a = 18'b000000000000000001;
+	parameter [17:0] start_b = 18'b000000000000000010;
+	parameter [17:0] start_c = 18'b000000000000000100;
+	parameter [17:0] start_d = 18'b000000000000001000;
+	parameter [17:0] start_e = 18'b000000000000010000;
+	parameter [17:0] stop_a = 18'b000000000000100000;
+	parameter [17:0] stop_b = 18'b000000000001000000;
+	parameter [17:0] stop_c = 18'b000000000010000000;
+	parameter [17:0] stop_d = 18'b000000000100000000;
+	parameter [17:0] rd_a = 18'b000000001000000000;
+	parameter [17:0] rd_b = 18'b000000010000000000;
+	parameter [17:0] rd_c = 18'b000000100000000000;
+	parameter [17:0] rd_d = 18'b000001000000000000;
+	parameter [17:0] wr_a = 18'b000010000000000000;
+	parameter [17:0] wr_b = 18'b000100000000000000;
+	parameter [17:0] wr_c = 18'b001000000000000000;
+	parameter [17:0] wr_d = 18'b010000000000000000;
+	always @(posedge clk or negedge nReset)
+		if (!nReset) begin
+			c_state <= idle;
+			cmd_ack <= 1'b0;
+			scl_oen <= 1'b1;
+			sda_oen <= 1'b1;
+			sda_chk <= 1'b0;
+		end
+		else if (al) begin
+			c_state <= idle;
+			cmd_ack <= 1'b0;
+			scl_oen <= 1'b1;
+			sda_oen <= 1'b1;
+			sda_chk <= 1'b0;
+		end
+		else begin
+			cmd_ack <= 1'b0;
+			if (clk_en)
+				case (c_state)
+					idle: begin
+						case (cmd)
+							4'b0001: c_state <= start_a;
+							4'b0010: c_state <= stop_a;
+							4'b0100: c_state <= wr_a;
+							4'b1000: c_state <= rd_a;
+							default: c_state <= idle;
+						endcase
+						scl_oen <= scl_oen;
+						sda_oen <= sda_oen;
+						sda_chk <= 1'b0;
+					end
+					start_a: begin
+						c_state <= start_b;
+						scl_oen <= scl_oen;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					start_b: begin
+						c_state <= start_c;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					start_c: begin
+						c_state <= start_d;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					start_d: begin
+						c_state <= start_e;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					start_e: begin
+						c_state <= idle;
+						cmd_ack <= 1'b1;
+						scl_oen <= 1'b0;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					stop_a: begin
+						c_state <= stop_b;
+						scl_oen <= 1'b0;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					stop_b: begin
+						c_state <= stop_c;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					stop_c: begin
+						c_state <= stop_d;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b0;
+						sda_chk <= 1'b0;
+					end
+					stop_d: begin
+						c_state <= idle;
+						cmd_ack <= 1'b1;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					rd_a: begin
+						c_state <= rd_b;
+						scl_oen <= 1'b0;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					rd_b: begin
+						c_state <= rd_c;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					rd_c: begin
+						c_state <= rd_d;
+						scl_oen <= 1'b1;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					rd_d: begin
+						c_state <= idle;
+						cmd_ack <= 1'b1;
+						scl_oen <= 1'b0;
+						sda_oen <= 1'b1;
+						sda_chk <= 1'b0;
+					end
+					wr_a: begin
+						c_state <= wr_b;
+						scl_oen <= 1'b0;
+						sda_oen <= din;
+						sda_chk <= 1'b0;
+					end
+					wr_b: begin
+						c_state <= wr_c;
+						scl_oen <= 1'b1;
+						sda_oen <= din;
+						sda_chk <= 1'b0;
+					end
+					wr_c: begin
+						c_state <= wr_d;
+						scl_oen <= 1'b1;
+						sda_oen <= din;
+						sda_chk <= 1'b1;
+					end
+					wr_d: begin
+						c_state <= idle;
+						cmd_ack <= 1'b1;
+						scl_oen <= 1'b0;
+						sda_oen <= din;
+						sda_chk <= 1'b0;
+					end
+				endcase
+		end
+	assign scl_o = 1'b0;
+	assign sda_o = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_i2c/i2c_master_byte_ctrl.v b/verilog/rtl/ips/apb/apb_i2c/i2c_master_byte_ctrl.v
new file mode 100644
index 0000000..b3eb193
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_i2c/i2c_master_byte_ctrl.v
@@ -0,0 +1,202 @@
+module i2c_master_byte_ctrl (
+	clk,
+	nReset,
+	ena,
+	clk_cnt,
+	start,
+	stop,
+	read,
+	write,
+	ack_in,
+	din,
+	cmd_ack,
+	ack_out,
+	dout,
+	i2c_busy,
+	i2c_al,
+	scl_i,
+	scl_o,
+	scl_oen,
+	sda_i,
+	sda_o,
+	sda_oen
+);
+	input clk;
+	input nReset;
+	input ena;
+	input [15:0] clk_cnt;
+	input start;
+	input stop;
+	input read;
+	input write;
+	input ack_in;
+	input [7:0] din;
+	output reg cmd_ack;
+	output reg ack_out;
+	output wire i2c_busy;
+	output wire i2c_al;
+	output wire [7:0] dout;
+	input scl_i;
+	output wire scl_o;
+	output wire scl_oen;
+	input sda_i;
+	output wire sda_o;
+	output wire sda_oen;
+	parameter [4:0] ST_IDLE = 5'b00000;
+	parameter [4:0] ST_START = 5'b00001;
+	parameter [4:0] ST_READ = 5'b00010;
+	parameter [4:0] ST_WRITE = 5'b00100;
+	parameter [4:0] ST_ACK = 5'b01000;
+	parameter [4:0] ST_STOP = 5'b10000;
+	reg [3:0] core_cmd;
+	reg core_txd;
+	wire core_ack;
+	wire core_rxd;
+	reg [7:0] sr;
+	reg shift;
+	reg ld;
+	wire go;
+	reg [2:0] dcnt;
+	wire cnt_done;
+	i2c_master_bit_ctrl bit_controller(
+		.clk(clk),
+		.nReset(nReset),
+		.ena(ena),
+		.clk_cnt(clk_cnt),
+		.cmd(core_cmd),
+		.cmd_ack(core_ack),
+		.busy(i2c_busy),
+		.al(i2c_al),
+		.din(core_txd),
+		.dout(core_rxd),
+		.scl_i(scl_i),
+		.scl_o(scl_o),
+		.scl_oen(scl_oen),
+		.sda_i(sda_i),
+		.sda_o(sda_o),
+		.sda_oen(sda_oen)
+	);
+	assign go = ((read | write) | stop) & ~cmd_ack;
+	assign dout = sr;
+	always @(posedge clk or negedge nReset)
+		if (!nReset)
+			sr <= #(1) 8'h00;
+		else if (ld)
+			sr <= #(1) din;
+		else if (shift)
+			sr <= #(1) {sr[6:0], core_rxd};
+	always @(posedge clk or negedge nReset)
+		if (!nReset)
+			dcnt <= #(1) 3'h0;
+		else if (ld)
+			dcnt <= #(1) 3'h7;
+		else if (shift)
+			dcnt <= #(1) dcnt - 3'h1;
+	assign cnt_done = ~(|dcnt);
+	reg [4:0] c_state;
+	always @(posedge clk or negedge nReset)
+		if (!nReset) begin
+			core_cmd <= #(1) 4'b0000;
+			core_txd <= #(1) 1'b0;
+			shift <= #(1) 1'b0;
+			ld <= #(1) 1'b0;
+			cmd_ack <= #(1) 1'b0;
+			c_state <= #(1) ST_IDLE;
+			ack_out <= #(1) 1'b0;
+		end
+		else if (i2c_al) begin
+			core_cmd <= #(1) 4'b0000;
+			core_txd <= #(1) 1'b0;
+			shift <= #(1) 1'b0;
+			ld <= #(1) 1'b0;
+			cmd_ack <= #(1) 1'b0;
+			c_state <= #(1) ST_IDLE;
+			ack_out <= #(1) 1'b0;
+		end
+		else begin
+			core_txd <= #(1) sr[7];
+			shift <= #(1) 1'b0;
+			ld <= #(1) 1'b0;
+			cmd_ack <= #(1) 1'b0;
+			case (c_state)
+				ST_IDLE:
+					if (go) begin
+						if (start) begin
+							c_state <= #(1) ST_START;
+							core_cmd <= #(1) 4'b0001;
+						end
+						else if (read) begin
+							c_state <= #(1) ST_READ;
+							core_cmd <= #(1) 4'b1000;
+						end
+						else if (write) begin
+							c_state <= #(1) ST_WRITE;
+							core_cmd <= #(1) 4'b0100;
+						end
+						else begin
+							c_state <= #(1) ST_STOP;
+							core_cmd <= #(1) 4'b0010;
+						end
+						ld <= #(1) 1'b1;
+					end
+				ST_START:
+					if (core_ack) begin
+						if (read) begin
+							c_state <= #(1) ST_READ;
+							core_cmd <= #(1) 4'b1000;
+						end
+						else begin
+							c_state <= #(1) ST_WRITE;
+							core_cmd <= #(1) 4'b0100;
+						end
+						ld <= #(1) 1'b1;
+					end
+				ST_WRITE:
+					if (core_ack)
+						if (cnt_done) begin
+							c_state <= #(1) ST_ACK;
+							core_cmd <= #(1) 4'b1000;
+						end
+						else begin
+							c_state <= #(1) ST_WRITE;
+							core_cmd <= #(1) 4'b0100;
+							shift <= #(1) 1'b1;
+						end
+				ST_READ:
+					if (core_ack) begin
+						if (cnt_done) begin
+							c_state <= #(1) ST_ACK;
+							core_cmd <= #(1) 4'b0100;
+						end
+						else begin
+							c_state <= #(1) ST_READ;
+							core_cmd <= #(1) 4'b1000;
+						end
+						shift <= #(1) 1'b1;
+						core_txd <= #(1) ack_in;
+					end
+				ST_ACK:
+					if (core_ack) begin
+						if (stop) begin
+							c_state <= #(1) ST_STOP;
+							core_cmd <= #(1) 4'b0010;
+						end
+						else begin
+							c_state <= #(1) ST_IDLE;
+							core_cmd <= #(1) 4'b0000;
+							cmd_ack <= #(1) 1'b1;
+						end
+						ack_out <= #(1) core_rxd;
+						core_txd <= #(1) 1'b1;
+					end
+					else
+						core_txd <= #(1) ack_in;
+				ST_STOP:
+					if (core_ack) begin
+						c_state <= #(1) ST_IDLE;
+						core_cmd <= #(1) 4'b0000;
+						cmd_ack <= #(1) 1'b1;
+					end
+			endcase
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_i2c/i2c_master_defines.sv b/verilog/rtl/ips/apb/apb_i2c/i2c_master_defines.sv
new file mode 100644
index 0000000..673ecc4
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_i2c/i2c_master_defines.sv
@@ -0,0 +1,62 @@
+/////////////////////////////////////////////////////////////////////
+////                                                             ////
+////  WISHBONE rev.B2 compliant I2C Master controller defines    ////
+////                                                             ////
+////                                                             ////
+////  Author: Richard Herveille                                  ////
+////          richard@asics.ws                                   ////
+////          www.asics.ws                                       ////
+////                                                             ////
+////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
+////                                                             ////
+/////////////////////////////////////////////////////////////////////
+////                                                             ////
+//// Copyright (C) 2001 Richard Herveille                        ////
+////                    richard@asics.ws                         ////
+////                                                             ////
+//// This source file may be used and distributed without        ////
+//// restriction provided that this copyright statement is not   ////
+//// removed from the file and that any derivative work contains ////
+//// the original copyright notice and the associated disclaimer.////
+////                                                             ////
+////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
+//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
+//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
+//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
+//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
+//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
+//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
+//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
+//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
+//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
+//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
+//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
+//// POSSIBILITY OF SUCH DAMAGE.                                 ////
+////                                                             ////
+/////////////////////////////////////////////////////////////////////
+
+//  CVS Log
+//
+//  $Id: i2c_master_defines.v,v 1.3 2001-11-05 11:59:25 rherveille Exp $
+//
+//  $Date: 2001-11-05 11:59:25 $
+//  $Revision: 1.3 $
+//  $Author: rherveille $
+//  $Locker:  $
+//  $State: Exp $
+//
+// Change History:
+//               $Log: not supported by cvs2svn $
+
+
+// I2C registers wishbone addresses
+
+// bitcontroller states
+`define I2C_CMD_NOP   4'b0000
+`define I2C_CMD_START 4'b0001
+`define I2C_CMD_STOP  4'b0010
+`define I2C_CMD_WRITE 4'b0100
+`define I2C_CMD_READ  4'b1000
+
+// Modelsim-ASE requires a timescale directive
+`timescale 1 ns / 1 ns
\ No newline at end of file
diff --git a/verilog/rtl/ips/apb/apb_node b/verilog/rtl/ips/apb/apb_node
deleted file mode 160000
index b69afc8..0000000
--- a/verilog/rtl/ips/apb/apb_node
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit b69afc8ab03c9bc0c399aa86a36ea91978b01ccc
diff --git a/verilog/rtl/ips/apb/apb_node/apb_node.v b/verilog/rtl/ips/apb/apb_node/apb_node.v
new file mode 100644
index 0000000..df901bf
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_node/apb_node.v
@@ -0,0 +1,101 @@
+module apb_node 
+#(
+    parameter NB_MASTER = 8,
+    parameter APB_DATA_WIDTH = 32,
+    parameter APB_ADDR_WIDTH = 32
+    )
+(
+	penable_i,
+	pwrite_i,
+	paddr_i,
+	pwdata_i,
+	prdata_o,
+	pready_o,
+	pslverr_o,
+	penable_o,
+	pwrite_o,
+	paddr_o,
+	psel_o,
+	pwdata_o,
+	prdata_i,
+	pready_i,
+	pslverr_i,
+	START_ADDR_i,
+	END_ADDR_i
+);
+	//parameter NB_MASTER = 8;
+	//parameter APB_DATA_WIDTH = 32;
+	//parameter APB_ADDR_WIDTH = 32;
+	input wire penable_i;
+	input wire pwrite_i;
+	input wire [31:0] paddr_i;
+	input wire [31:0] pwdata_i;
+	output reg [31:0] prdata_o;
+	output reg pready_o;
+	output reg pslverr_o;
+	output reg [NB_MASTER - 1:0] penable_o;
+	output reg [NB_MASTER - 1:0] pwrite_o;
+	output reg [(NB_MASTER * 32) - 1:0] paddr_o;
+	output wire [NB_MASTER - 1:0] psel_o;
+	output reg [(NB_MASTER * 32) - 1:0] pwdata_o;
+	input wire [(NB_MASTER * 32) - 1:0] prdata_i;
+	input wire [NB_MASTER - 1:0] pready_i;
+	input wire [NB_MASTER - 1:0] pslverr_i;
+	input wire [(NB_MASTER * APB_ADDR_WIDTH) - 1:0] START_ADDR_i;
+	input wire [(NB_MASTER * APB_ADDR_WIDTH) - 1:0] END_ADDR_i;
+	genvar i;
+	integer s_loop1;
+	integer s_loop2;
+	integer s_loop3;
+	integer s_loop4;
+	integer s_loop5;
+	integer s_loop6;
+	integer s_loop7;
+	generate
+		for (i = 0; i < NB_MASTER; i = i + 1) begin : genblk1
+			assign psel_o[i] = (paddr_i >= START_ADDR_i[i * APB_ADDR_WIDTH+:APB_ADDR_WIDTH]) && (paddr_i <= END_ADDR_i[i * APB_ADDR_WIDTH+:APB_ADDR_WIDTH]);
+		end
+	endgenerate
+	always @(*)
+		for (s_loop1 = 0; s_loop1 < NB_MASTER; s_loop1 = s_loop1 + 1)
+			if (psel_o[s_loop1] == 1'b1)
+				penable_o[s_loop1] = penable_i;
+			else
+				penable_o[s_loop1] = 1'sb0;
+	always @(*)
+		for (s_loop2 = 0; s_loop2 < NB_MASTER; s_loop2 = s_loop2 + 1)
+			if (psel_o[s_loop2] == 1'b1)
+				pwrite_o[s_loop2] = pwrite_i;
+			else
+				pwrite_o[s_loop2] = 1'sb0;
+	always @(*)
+		for (s_loop3 = 0; s_loop3 < NB_MASTER; s_loop3 = s_loop3 + 1)
+			if (psel_o[s_loop3] == 1'b1)
+				paddr_o[s_loop3 * 32+:32] = paddr_i;
+			else
+				paddr_o[s_loop3 * 32+:32] = 1'sb0;
+	always @(*)
+		for (s_loop4 = 0; s_loop4 < NB_MASTER; s_loop4 = s_loop4 + 1)
+			if (psel_o[s_loop4] == 1'b1)
+				pwdata_o[s_loop4 * 32+:32] = pwdata_i;
+			else
+				pwdata_o[s_loop4 * 32+:32] = 1'sb0;
+	always @(*) begin
+		prdata_o = 1'sb0;
+		for (s_loop5 = 0; s_loop5 < NB_MASTER; s_loop5 = s_loop5 + 1)
+			if (psel_o[s_loop5] == 1'b1)
+				prdata_o = prdata_i[s_loop5 * 32+:32];
+	end
+	always @(*) begin
+		pready_o = 1'sb0;
+		for (s_loop6 = 0; s_loop6 < NB_MASTER; s_loop6 = s_loop6 + 1)
+			if (psel_o[s_loop6] == 1'b1)
+				pready_o = pready_i[s_loop6];
+	end
+	always @(*) begin
+		pslverr_o = 1'sb0;
+		for (s_loop7 = 0; s_loop7 < NB_MASTER; s_loop7 = s_loop7 + 1)
+			if (psel_o[s_loop7] == 1'b1)
+				pslverr_o = pslverr_i[s_loop7];
+	end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_node/apb_node_wrap.v b/verilog/rtl/ips/apb/apb_node/apb_node_wrap.v
new file mode 100644
index 0000000..0c638bc
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_node/apb_node_wrap.v
@@ -0,0 +1,284 @@
+module apb_node_wrap 
+#(
+    parameter NB_MASTER = 8,
+    parameter APB_DATA_WIDTH = 32,
+    parameter APB_ADDR_WIDTH = 32
+    )
+(
+	clk_i,
+	rst_ni,
+	apb_slave_paddr,
+	apb_slave_pwdata,
+	apb_slave_pwrite,
+	apb_slave_psel,
+	apb_slave_penable,
+	apb_slave_prdata,
+	apb_slave_pready,
+	apb_slave_pslverr,
+	apb_masters00_paddr,
+	apb_masters00_pwdata,
+	apb_masters00_pwrite,
+	apb_masters00_psel,
+	apb_masters00_penable,
+	apb_masters00_prdata,
+	apb_masters00_pready,
+	apb_masters00_pslverr,
+	apb_masters01_paddr,
+	apb_masters01_pwdata,
+	apb_masters01_pwrite,
+	apb_masters01_psel,
+	apb_masters01_penable,
+	apb_masters01_prdata,
+	apb_masters01_pready,
+	apb_masters01_pslverr,
+	apb_masters02_paddr,
+	apb_masters02_pwdata,
+	apb_masters02_pwrite,
+	apb_masters02_psel,
+	apb_masters02_penable,
+	apb_masters02_prdata,
+	apb_masters02_pready,
+	apb_masters02_pslverr,
+	apb_masters03_paddr,
+	apb_masters03_pwdata,
+	apb_masters03_pwrite,
+	apb_masters03_psel,
+	apb_masters03_penable,
+	apb_masters03_prdata,
+	apb_masters03_pready,
+	apb_masters03_pslverr,
+	apb_masters04_paddr,
+	apb_masters04_pwdata,
+	apb_masters04_pwrite,
+	apb_masters04_psel,
+	apb_masters04_penable,
+	apb_masters04_prdata,
+	apb_masters04_pready,
+	apb_masters04_pslverr,
+	apb_masters05_paddr,
+	apb_masters05_pwdata,
+	apb_masters05_pwrite,
+	apb_masters05_psel,
+	apb_masters05_penable,
+	apb_masters05_prdata,
+	apb_masters05_pready,
+	apb_masters05_pslverr,
+	apb_masters06_paddr,
+	apb_masters06_pwdata,
+	apb_masters06_pwrite,
+	apb_masters06_psel,
+	apb_masters06_penable,
+	apb_masters06_prdata,
+	apb_masters06_pready,
+	apb_masters06_pslverr,
+	apb_masters07_paddr,
+	apb_masters07_pwdata,
+	apb_masters07_pwrite,
+	apb_masters07_psel,
+	apb_masters07_penable,
+	apb_masters07_prdata,
+	apb_masters07_pready,
+	apb_masters07_pslverr,
+	apb_masters08_paddr,
+	apb_masters08_pwdata,
+	apb_masters08_pwrite,
+	apb_masters08_psel,
+	apb_masters08_penable,
+	apb_masters08_prdata,
+	apb_masters08_pready,
+	apb_masters08_pslverr,
+	start_addr_i,
+	end_addr_i
+);
+	//parameter NB_MASTER = 9;
+	//parameter APB_DATA_WIDTH = 32;
+	//parameter APB_ADDR_WIDTH = 32;
+	input wire clk_i;
+	input wire rst_ni;
+	input wire [APB_ADDR_WIDTH - 1:0] apb_slave_paddr;
+	input wire [APB_DATA_WIDTH - 1:0] apb_slave_pwdata;
+	input wire apb_slave_pwrite;
+	input wire apb_slave_psel;
+	input wire apb_slave_penable;
+	output wire [APB_DATA_WIDTH - 1:0] apb_slave_prdata;
+	output wire apb_slave_pready;
+	output wire apb_slave_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters00_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters00_pwdata;
+	output wire apb_masters00_pwrite;
+	output wire apb_masters00_psel;
+	output wire apb_masters00_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters00_prdata;
+	input wire apb_masters00_pready;
+	input wire apb_masters00_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters01_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters01_pwdata;
+	output wire apb_masters01_pwrite;
+	output wire apb_masters01_psel;
+	output wire apb_masters01_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters01_prdata;
+	input wire apb_masters01_pready;
+	input wire apb_masters01_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters02_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters02_pwdata;
+	output wire apb_masters02_pwrite;
+	output wire apb_masters02_psel;
+	output wire apb_masters02_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters02_prdata;
+	input wire apb_masters02_pready;
+	input wire apb_masters02_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters03_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters03_pwdata;
+	output wire apb_masters03_pwrite;
+	output wire apb_masters03_psel;
+	output wire apb_masters03_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters03_prdata;
+	input wire apb_masters03_pready;
+	input wire apb_masters03_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters04_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters04_pwdata;
+	output wire apb_masters04_pwrite;
+	output wire apb_masters04_psel;
+	output wire apb_masters04_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters04_prdata;
+	input wire apb_masters04_pready;
+	input wire apb_masters04_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters05_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters05_pwdata;
+	output wire apb_masters05_pwrite;
+	output wire apb_masters05_psel;
+	output wire apb_masters05_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters05_prdata;
+	input wire apb_masters05_pready;
+	input wire apb_masters05_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters06_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters06_pwdata;
+	output wire apb_masters06_pwrite;
+	output wire apb_masters06_psel;
+	output wire apb_masters06_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters06_prdata;
+	input wire apb_masters06_pready;
+	input wire apb_masters06_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters07_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters07_pwdata;
+	output wire apb_masters07_pwrite;
+	output wire apb_masters07_psel;
+	output wire apb_masters07_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters07_prdata;
+	input wire apb_masters07_pready;
+	input wire apb_masters07_pslverr;
+	output wire [APB_ADDR_WIDTH - 1:0] apb_masters08_paddr;
+	output wire [APB_DATA_WIDTH - 1:0] apb_masters08_pwdata;
+	output wire apb_masters08_pwrite;
+	output wire apb_masters08_psel;
+	output wire apb_masters08_penable;
+	input wire [APB_DATA_WIDTH - 1:0] apb_masters08_prdata;
+	input wire apb_masters08_pready;
+	input wire apb_masters08_pslverr;
+	input wire [(NB_MASTER * APB_ADDR_WIDTH) - 1:0] start_addr_i;
+	input wire [(NB_MASTER * APB_ADDR_WIDTH) - 1:0] end_addr_i;
+	genvar i;
+	wire [NB_MASTER - 1:0] penable;
+	wire [NB_MASTER - 1:0] pwrite;
+	wire [(NB_MASTER * 32) - 1:0] paddr;
+	wire [NB_MASTER - 1:0] psel;
+	wire [(NB_MASTER * 32) - 1:0] pwdata;
+	wire [(NB_MASTER * 32) - 1:0] prdata;
+	wire [NB_MASTER - 1:0] pready;
+	wire [NB_MASTER - 1:0] pslverr;
+	assign apb_masters00_penable = penable[0];
+	assign apb_masters00_pwrite = pwrite[0];
+	assign apb_masters00_paddr = paddr[0+:32];
+	assign apb_masters00_psel = psel[0];
+	assign apb_masters00_pwdata = pwdata[0+:32];
+	assign prdata[0+:32] = apb_masters00_prdata;
+	assign pready[0] = apb_masters00_pready;
+	assign pslverr[0] = apb_masters00_pslverr;
+	assign apb_masters01_penable = penable[1];
+	assign apb_masters01_pwrite = pwrite[1];
+	assign apb_masters01_paddr = paddr[32+:32];
+	assign apb_masters01_psel = psel[1];
+	assign apb_masters01_pwdata = pwdata[32+:32];
+	assign prdata[32+:32] = apb_masters01_prdata;
+	assign pready[1] = apb_masters01_pready;
+	assign pslverr[1] = apb_masters01_pslverr;
+	assign apb_masters02_penable = penable[2];
+	assign apb_masters02_pwrite = pwrite[2];
+	assign apb_masters02_paddr = paddr[64+:32];
+	assign apb_masters02_psel = psel[2];
+	assign apb_masters02_pwdata = pwdata[64+:32];
+	assign prdata[64+:32] = apb_masters02_prdata;
+	assign pready[2] = apb_masters02_pready;
+	assign pslverr[2] = apb_masters02_pslverr;
+	assign apb_masters03_penable = penable[3];
+	assign apb_masters03_pwrite = pwrite[3];
+	assign apb_masters03_paddr = paddr[96+:32];
+	assign apb_masters03_psel = psel[3];
+	assign apb_masters03_pwdata = pwdata[96+:32];
+	assign prdata[96+:32] = apb_masters03_prdata;
+	assign pready[3] = apb_masters03_pready;
+	assign pslverr[3] = apb_masters03_pslverr;
+	assign apb_masters04_penable = penable[4];
+	assign apb_masters04_pwrite = pwrite[4];
+	assign apb_masters04_paddr = paddr[128+:32];
+	assign apb_masters04_psel = psel[4];
+	assign apb_masters04_pwdata = pwdata[128+:32];
+	assign prdata[128+:32] = apb_masters04_prdata;
+	assign pready[4] = apb_masters04_pready;
+	assign pslverr[4] = apb_masters04_pslverr;
+	assign apb_masters05_penable = penable[5];
+	assign apb_masters05_pwrite = pwrite[5];
+	assign apb_masters05_paddr = paddr[160+:32];
+	assign apb_masters05_psel = psel[5];
+	assign apb_masters05_pwdata = pwdata[160+:32];
+	assign prdata[160+:32] = apb_masters05_prdata;
+	assign pready[5] = apb_masters05_pready;
+	assign pslverr[5] = apb_masters05_pslverr;
+	assign apb_masters06_penable = penable[6];
+	assign apb_masters06_pwrite = pwrite[6];
+	assign apb_masters06_paddr = paddr[192+:32];
+	assign apb_masters06_psel = psel[6];
+	assign apb_masters06_pwdata = pwdata[192+:32];
+	assign prdata[192+:32] = apb_masters06_prdata;
+	assign pready[6] = apb_masters06_pready;
+	assign pslverr[6] = apb_masters06_pslverr;
+	assign apb_masters07_penable = penable[7];
+	assign apb_masters07_pwrite = pwrite[7];
+	assign apb_masters07_paddr = paddr[224+:32];
+	assign apb_masters07_psel = psel[7];
+	assign apb_masters07_pwdata = pwdata[224+:32];
+	assign prdata[224+:32] = apb_masters07_prdata;
+	assign pready[7] = apb_masters07_pready;
+	assign pslverr[7] = apb_masters07_pslverr;
+	assign apb_masters08_penable = penable[8];
+	assign apb_masters08_pwrite = pwrite[8];
+	assign apb_masters08_paddr = paddr[256+:32];
+	assign apb_masters08_psel = psel[8];
+	assign apb_masters08_pwdata = pwdata[256+:32];
+	assign prdata[256+:32] = apb_masters08_prdata;
+	assign pready[8] = apb_masters08_pready;
+	assign pslverr[8] = apb_masters08_pslverr;
+	apb_node #(
+		.NB_MASTER(NB_MASTER),
+		.APB_DATA_WIDTH(APB_DATA_WIDTH),
+		.APB_ADDR_WIDTH(APB_ADDR_WIDTH)
+	) apb_node_i(
+		.penable_i(apb_slave_penable),
+		.pwrite_i(apb_slave_pwrite),
+		.paddr_i(apb_slave_paddr),
+		.pwdata_i(apb_slave_pwdata),
+		.prdata_o(apb_slave_prdata),
+		.pready_o(apb_slave_pready),
+		.pslverr_o(apb_slave_pslverr),
+		.penable_o(penable),
+		.pwrite_o(pwrite),
+		.paddr_o(paddr),
+		.psel_o(psel),
+		.pwdata_o(pwdata),
+		.prdata_i(prdata),
+		.pready_i(pready),
+		.pslverr_i(pslverr),
+		.START_ADDR_i(start_addr_i),
+		.END_ADDR_i(end_addr_i)
+	);
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_pulpino b/verilog/rtl/ips/apb/apb_pulpino
deleted file mode 160000
index 6ed2cc8..0000000
--- a/verilog/rtl/ips/apb/apb_pulpino
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 6ed2cc81265682ef2d6b46eff3c2b84f5efc9573
diff --git a/verilog/rtl/ips/apb/apb_pulpino/apb_pulpino.v b/verilog/rtl/ips/apb/apb_pulpino/apb_pulpino.v
new file mode 100644
index 0000000..1c00fdb
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_pulpino/apb_pulpino.v
@@ -0,0 +1,174 @@
+module apb_pulpino 
+#(
+    parameter APB_ADDR_WIDTH = 12,  //APB slaves are 4KB by default
+    parameter BOOT_ADDR      = 32'h8000
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	pad_cfg_o,
+	clk_gate_o,
+	pad_mux_o,
+	boot_addr_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	//parameter BOOT_ADDR = 32'h00008000;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output wire [191:0] pad_cfg_o;
+	output wire [31:0] clk_gate_o;
+	output wire [31:0] pad_mux_o;
+	output wire [31:0] boot_addr_o;
+	reg [31:0] pad_mux_q;
+	reg [31:0] pad_mux_n;
+	reg [31:0] boot_adr_q;
+	reg [31:0] boot_adr_n;
+	reg [31:0] clk_gate_q;
+	reg [31:0] clk_gate_n;
+	reg [191:0] pad_cfg_q;
+	reg [191:0] pad_cfg_n;
+	wire [3:0] register_adr;
+	reg [1:0] status_n;
+	reg [1:0] status_q;
+	assign register_adr = PADDR[5:2];
+	assign pad_mux_o = pad_mux_q;
+	assign clk_gate_o = clk_gate_q;
+	assign pad_cfg_o = pad_cfg_q;
+	assign boot_addr_o = boot_adr_q;
+	always @(*) begin
+		pad_mux_n = pad_mux_q;
+		pad_cfg_n = pad_cfg_q;
+		clk_gate_n = clk_gate_q;
+		boot_adr_n = boot_adr_q;
+		status_n = status_q;
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				4'b0000: pad_mux_n = PWDATA;
+				4'b0001: clk_gate_n = PWDATA;
+				4'b0010: boot_adr_n = PWDATA;
+				4'b1000: begin
+					pad_cfg_n[0+:6] = PWDATA[5:0];
+					pad_cfg_n[6+:6] = PWDATA[13:8];
+					pad_cfg_n[12+:6] = PWDATA[21:16];
+					pad_cfg_n[18+:6] = PWDATA[29:24];
+				end
+				4'b1001: begin
+					pad_cfg_n[24+:6] = PWDATA[5:0];
+					pad_cfg_n[30+:6] = PWDATA[13:8];
+					pad_cfg_n[36+:6] = PWDATA[21:16];
+					pad_cfg_n[42+:6] = PWDATA[29:24];
+				end
+				4'b1010: begin
+					pad_cfg_n[48+:6] = PWDATA[5:0];
+					pad_cfg_n[54+:6] = PWDATA[13:8];
+					pad_cfg_n[60+:6] = PWDATA[21:16];
+					pad_cfg_n[66+:6] = PWDATA[29:24];
+				end
+				4'b1011: begin
+					pad_cfg_n[72+:6] = PWDATA[5:0];
+					pad_cfg_n[78+:6] = PWDATA[13:8];
+					pad_cfg_n[84+:6] = PWDATA[21:16];
+					pad_cfg_n[90+:6] = PWDATA[29:24];
+				end
+				4'b1100: begin
+					pad_cfg_n[96+:6] = PWDATA[5:0];
+					pad_cfg_n[102+:6] = PWDATA[13:8];
+					pad_cfg_n[108+:6] = PWDATA[21:16];
+					pad_cfg_n[114+:6] = PWDATA[29:24];
+				end
+				4'b1101: begin
+					pad_cfg_n[120+:6] = PWDATA[5:0];
+					pad_cfg_n[126+:6] = PWDATA[13:8];
+					pad_cfg_n[132+:6] = PWDATA[21:16];
+					pad_cfg_n[138+:6] = PWDATA[29:24];
+				end
+				4'b1110: begin
+					pad_cfg_n[144+:6] = PWDATA[5:0];
+					pad_cfg_n[150+:6] = PWDATA[13:8];
+					pad_cfg_n[156+:6] = PWDATA[21:16];
+					pad_cfg_n[162+:6] = PWDATA[29:24];
+				end
+				4'b1111: begin
+					pad_cfg_n[168+:6] = PWDATA[5:0];
+					pad_cfg_n[174+:6] = PWDATA[13:8];
+					pad_cfg_n[180+:6] = PWDATA[21:16];
+					pad_cfg_n[186+:6] = PWDATA[29:24];
+				end
+				4'b0101: status_n = PWDATA[1:0];
+			endcase
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				4'b0000: PRDATA = pad_mux_q;
+				4'b0010: PRDATA = boot_adr_q;
+				4'b0001: PRDATA = clk_gate_q;
+				4'b1000: PRDATA = {2'b00, pad_cfg_q[18+:6], 2'b00, pad_cfg_q[12+:6], 2'b00, pad_cfg_q[6+:6], 2'b00, pad_cfg_q[0+:6]};
+				4'b1001: PRDATA = {2'b00, pad_cfg_q[42+:6], 2'b00, pad_cfg_q[36+:6], 2'b00, pad_cfg_q[30+:6], 2'b00, pad_cfg_q[24+:6]};
+				4'b1010: PRDATA = {2'b00, pad_cfg_q[66+:6], 2'b00, pad_cfg_q[60+:6], 2'b00, pad_cfg_q[54+:6], 2'b00, pad_cfg_q[48+:6]};
+				4'b1011: PRDATA = {2'b00, pad_cfg_q[90+:6], 2'b00, pad_cfg_q[84+:6], 2'b00, pad_cfg_q[78+:6], 2'b00, pad_cfg_q[72+:6]};
+				4'b1100: PRDATA = {2'b00, pad_cfg_q[114+:6], 2'b00, pad_cfg_q[108+:6], 2'b00, pad_cfg_q[102+:6], 2'b00, pad_cfg_q[96+:6]};
+				4'b1101: PRDATA = {2'b00, pad_cfg_q[138+:6], 2'b00, pad_cfg_q[132+:6], 2'b00, pad_cfg_q[126+:6], 2'b00, pad_cfg_q[120+:6]};
+				4'b1110: PRDATA = {2'b00, pad_cfg_q[162+:6], 2'b00, pad_cfg_q[156+:6], 2'b00, pad_cfg_q[150+:6], 2'b00, pad_cfg_q[144+:6]};
+				4'b1111: PRDATA = {2'b00, pad_cfg_q[186+:6], 2'b00, pad_cfg_q[180+:6], 2'b00, pad_cfg_q[174+:6], 2'b00, pad_cfg_q[168+:6]};
+				4'b0100: PRDATA = 32'b00000000000000001000000010000010;
+				4'b0101: PRDATA = {30'b000000000000000000000000000000, status_q[1:0]};
+				default: PRDATA = 'b0;
+			endcase
+	end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			pad_mux_q <= 32'b00000000000000000000000000000000;
+			clk_gate_q <= 1'sb1;
+			pad_cfg_q <= {32 {6'b000000}};
+			boot_adr_q <= BOOT_ADDR;
+			status_q <= 1'sb1;
+			pad_cfg_q[0+:6] <= 6'b000000;
+			pad_cfg_q[6+:6] <= 6'b000000;
+			pad_cfg_q[12+:6] <= 6'b000000;
+			pad_cfg_q[18+:6] <= 6'b000000;
+			pad_cfg_q[24+:6] <= 6'b000000;
+			pad_cfg_q[30+:6] <= 6'b000000;
+			pad_cfg_q[36+:6] <= 6'b000000;
+			pad_cfg_q[42+:6] <= 6'b000000;
+			pad_cfg_q[48+:6] <= 6'b000000;
+			pad_cfg_q[54+:6] <= 6'b000000;
+			pad_cfg_q[60+:6] <= 6'b000000;
+			pad_cfg_q[66+:6] <= 6'b000000;
+			pad_cfg_q[72+:6] <= 6'b000000;
+			pad_cfg_q[78+:6] <= 6'b000000;
+			pad_cfg_q[84+:6] <= 6'b000000;
+			pad_cfg_q[90+:6] <= 6'b000000;
+			pad_cfg_q[96+:6] <= 6'b000000;
+			pad_cfg_q[102+:6] <= 6'b000000;
+			pad_cfg_q[108+:6] <= 6'b000000;
+			pad_cfg_q[114+:6] <= 6'b000000;
+			pad_cfg_q[120+:6] <= 6'b000000;
+		end
+		else begin
+			pad_mux_q <= pad_mux_n;
+			clk_gate_q <= clk_gate_n;
+			pad_cfg_q <= pad_cfg_n;
+			boot_adr_q <= boot_adr_n;
+			status_q <= status_n;
+		end
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master b/verilog/rtl/ips/apb/apb_spi_master
deleted file mode 160000
index ae6db76..0000000
--- a/verilog/rtl/ips/apb/apb_spi_master
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ae6db7620b20cd843a2e2daf17bf50e9be8ca8e7
diff --git a/verilog/rtl/ips/apb/apb_spi_master/apb_spi_master.v b/verilog/rtl/ips/apb/apb_spi_master/apb_spi_master.v
new file mode 100644
index 0000000..769ebd0
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/apb_spi_master.v
@@ -0,0 +1,305 @@
+module apb_spi_master 
+#(
+    parameter BUFFER_DEPTH   = 10,
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	events_o,
+	spi_clk,
+	spi_csn0,
+	spi_csn1,
+	spi_csn2,
+	spi_csn3,
+	spi_mode,
+	spi_sdo0,
+	spi_sdo1,
+	spi_sdo2,
+	spi_sdo3,
+	spi_sdi0,
+	spi_sdi1,
+	spi_sdi2,
+	spi_sdi3
+);
+	//parameter BUFFER_DEPTH = 10;
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output wire [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output wire [1:0] events_o;
+	output wire spi_clk;
+	output wire spi_csn0;
+	output wire spi_csn1;
+	output wire spi_csn2;
+	output wire spi_csn3;
+	output wire [1:0] spi_mode;
+	output wire spi_sdo0;
+	output wire spi_sdo1;
+	output wire spi_sdo2;
+	output wire spi_sdo3;
+	input wire spi_sdi0;
+	input wire spi_sdi1;
+	input wire spi_sdi2;
+	input wire spi_sdi3;
+	localparam LOG_BUFFER_DEPTH = (BUFFER_DEPTH < 1 ? 0 : (BUFFER_DEPTH < 2 ? 1 : (BUFFER_DEPTH < 4 ? 2 : (BUFFER_DEPTH < 8 ? 3 : (BUFFER_DEPTH < 16 ? 4 : (BUFFER_DEPTH < 32 ? 5 : (BUFFER_DEPTH < 64 ? 6 : (BUFFER_DEPTH < 128 ? 7 : (BUFFER_DEPTH < 256 ? 8 : (BUFFER_DEPTH < 512 ? 9 : (BUFFER_DEPTH < 1024 ? 10 : (BUFFER_DEPTH < 2048 ? 11 : (BUFFER_DEPTH < 4096 ? 12 : (BUFFER_DEPTH < 8192 ? 13 : (BUFFER_DEPTH < 16384 ? 14 : (BUFFER_DEPTH < 32768 ? 15 : (BUFFER_DEPTH < 65536 ? 16 : (BUFFER_DEPTH < 131072 ? 17 : (BUFFER_DEPTH < 262144 ? 18 : (BUFFER_DEPTH < 524288 ? 19 : (BUFFER_DEPTH < 1048576 ? 20 : (BUFFER_DEPTH < 2097152 ? 21 : (BUFFER_DEPTH < 4194304 ? 22 : (BUFFER_DEPTH < 8388608 ? 23 : (BUFFER_DEPTH < 16777216 ? 24 : 25)))))))))))))))))))))))));
+	wire [7:0] spi_clk_div;
+	wire spi_clk_div_valid;
+	wire [31:0] spi_status;
+	wire [31:0] spi_addr;
+	wire [5:0] spi_addr_len;
+	wire [31:0] spi_cmd;
+	wire [5:0] spi_cmd_len;
+	wire [15:0] spi_data_len;
+	wire [15:0] spi_dummy_rd;
+	wire [15:0] spi_dummy_wr;
+	wire spi_swrst;
+	wire spi_rd;
+	wire spi_wr;
+	wire spi_qrd;
+	wire spi_qwr;
+	wire [3:0] spi_csreg;
+	wire [31:0] spi_data_tx;
+	wire spi_data_tx_valid;
+	wire spi_data_tx_ready;
+	wire [31:0] spi_data_rx;
+	wire spi_data_rx_valid;
+	wire spi_data_rx_ready;
+	wire [6:0] spi_ctrl_status;
+	wire [31:0] spi_ctrl_data_tx;
+	wire spi_ctrl_data_tx_valid;
+	wire spi_ctrl_data_tx_ready;
+	wire [31:0] spi_ctrl_data_rx;
+	wire spi_ctrl_data_rx_valid;
+	wire spi_ctrl_data_rx_ready;
+	wire s_eot;
+	wire [LOG_BUFFER_DEPTH:0] elements_tx;
+	wire [LOG_BUFFER_DEPTH:0] elements_rx;
+	reg [LOG_BUFFER_DEPTH:0] r_counter_tx;
+	reg [LOG_BUFFER_DEPTH:0] r_counter_rx;
+	wire [LOG_BUFFER_DEPTH:0] s_th_tx;
+	wire [LOG_BUFFER_DEPTH:0] s_th_rx;
+	wire [LOG_BUFFER_DEPTH:0] s_cnt_tx;
+	wire [LOG_BUFFER_DEPTH:0] s_cnt_rx;
+	wire s_rise_int_tx;
+	wire s_rise_int_rx;
+	reg s_int_tx;
+	reg s_int_rx;
+	wire s_int_en;
+	wire s_int_cnt_en;
+	wire s_int_rd_intsta;
+	reg [1:0] r_state_rx;
+	reg [1:0] s_state_rx_next;
+	reg [1:0] r_state_tx;
+	reg [1:0] s_state_tx_next;
+	localparam FILL_BITS = 7 - LOG_BUFFER_DEPTH;
+	assign s_rise_int_tx = elements_tx <= s_th_tx;
+	assign s_rise_int_rx = elements_rx >= s_th_rx;
+	assign spi_status = {{FILL_BITS {1'b0}}, elements_tx, {FILL_BITS {1'b0}}, elements_rx, 9'h000, spi_ctrl_status};
+	assign events_o[0] = s_int_tx | s_int_rx;
+	assign events_o[1] = s_eot;
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			r_state_tx <= 2'd0;
+			r_state_rx <= 2'd0;
+		end
+		else begin
+			r_state_tx <= s_state_tx_next;
+			r_state_rx <= s_state_rx_next;
+		end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			r_counter_tx <= 'h0;
+			r_counter_rx <= 'h0;
+		end
+		else if (s_int_cnt_en) begin
+			if (spi_ctrl_data_tx_valid && spi_ctrl_data_tx_ready)
+				if (r_counter_tx == (s_cnt_tx - 1))
+					r_counter_tx <= 'h0;
+				else
+					r_counter_tx <= r_counter_tx + 1;
+			if (spi_ctrl_data_rx_valid && spi_ctrl_data_rx_ready)
+				if (r_counter_rx == (s_cnt_rx - 1))
+					r_counter_rx <= 'h0;
+				else
+					r_counter_rx <= r_counter_rx + 1;
+		end
+		else begin
+			r_counter_tx <= 'h0;
+			r_counter_rx <= 'h0;
+		end
+	always @(*) begin
+		s_state_tx_next = r_state_tx;
+		s_int_tx = 1'b0;
+		case (r_state_tx)
+			2'd0:
+				if (s_rise_int_tx && s_int_en)
+					s_state_tx_next = 2'd1;
+			2'd1: begin
+				s_int_tx = 1'b1;
+				s_state_tx_next = 2'd2;
+			end
+			2'd2:
+				if (s_int_cnt_en) begin
+					if ((spi_ctrl_data_tx_valid && spi_ctrl_data_tx_ready) && (r_counter_tx == (s_cnt_tx - 1)))
+						s_state_tx_next = 2'd0;
+				end
+				else if (s_int_rd_intsta)
+					s_state_tx_next = 2'd0;
+			default: begin
+				s_state_tx_next = r_state_tx;
+				s_int_tx = 1'b0;
+			end
+		endcase
+	end
+	always @(*) begin
+		s_state_rx_next = r_state_rx;
+		s_int_rx = 1'b0;
+		case (r_state_rx)
+			2'd0:
+				if (s_rise_int_rx && s_int_en)
+					s_state_rx_next = 2'd1;
+			2'd1: begin
+				s_int_rx = 1'b1;
+				s_state_rx_next = 2'd2;
+			end
+			2'd2:
+				if (s_int_cnt_en) begin
+					if ((spi_ctrl_data_rx_valid && spi_ctrl_data_rx_ready) && (r_counter_rx == (s_cnt_rx - 1)))
+						s_state_rx_next = 2'd0;
+				end
+				else if (s_int_rd_intsta)
+					s_state_rx_next = 2'd0;
+		endcase
+	end
+	spi_master_apb_if #(
+		.BUFFER_DEPTH(BUFFER_DEPTH),
+		.APB_ADDR_WIDTH(APB_ADDR_WIDTH)
+	) u_axiregs(
+		.HCLK(HCLK),
+		.HRESETn(HRESETn),
+		.PADDR(PADDR),
+		.PWDATA(PWDATA),
+		.PWRITE(PWRITE),
+		.PSEL(PSEL),
+		.PENABLE(PENABLE),
+		.PRDATA(PRDATA),
+		.PREADY(PREADY),
+		.PSLVERR(PSLVERR),
+		.spi_clk_div(spi_clk_div),
+		.spi_clk_div_valid(spi_clk_div_valid),
+		.spi_status(spi_status),
+		.spi_addr(spi_addr),
+		.spi_addr_len(spi_addr_len),
+		.spi_cmd(spi_cmd),
+		.spi_cmd_len(spi_cmd_len),
+		.spi_data_len(spi_data_len),
+		.spi_dummy_rd(spi_dummy_rd),
+		.spi_dummy_wr(spi_dummy_wr),
+		.spi_swrst(spi_swrst),
+		.spi_rd(spi_rd),
+		.spi_wr(spi_wr),
+		.spi_qrd(spi_qrd),
+		.spi_qwr(spi_qwr),
+		.spi_csreg(spi_csreg),
+		.spi_int_th_rx(s_th_rx),
+		.spi_int_th_tx(s_th_tx),
+		.spi_int_cnt_rx(s_cnt_rx),
+		.spi_int_cnt_tx(s_cnt_tx),
+		.spi_int_en(s_int_en),
+		.spi_int_cnt_en(s_int_cnt_en),
+		.spi_int_rd_sta(s_int_rd_intsta),
+		.spi_data_tx(spi_data_tx),
+		.spi_data_tx_valid(spi_data_tx_valid),
+		.spi_data_tx_ready(spi_data_tx_ready),
+		.spi_data_rx(spi_data_rx),
+		.spi_data_rx_valid(spi_data_rx_valid),
+		.spi_data_rx_ready(spi_data_rx_ready)
+	);
+	spi_master_fifo #(
+		.DATA_WIDTH(32),
+		.BUFFER_DEPTH(BUFFER_DEPTH)
+	) u_txfifo(
+		.clk_i(HCLK),
+		.rst_ni(HRESETn),
+		.clr_i(spi_swrst),
+		.elements_o(elements_tx),
+		.data_o(spi_ctrl_data_tx),
+		.valid_o(spi_ctrl_data_tx_valid),
+		.ready_i(spi_ctrl_data_tx_ready),
+		.valid_i(spi_data_tx_valid),
+		.data_i(spi_data_tx),
+		.ready_o(spi_data_tx_ready)
+	);
+	spi_master_fifo #(
+		.DATA_WIDTH(32),
+		.BUFFER_DEPTH(BUFFER_DEPTH)
+	) u_rxfifo(
+		.clk_i(HCLK),
+		.rst_ni(HRESETn),
+		.clr_i(spi_swrst),
+		.elements_o(elements_rx),
+		.data_o(spi_data_rx),
+		.valid_o(spi_data_rx_valid),
+		.ready_i(spi_data_rx_ready),
+		.valid_i(spi_ctrl_data_rx_valid),
+		.data_i(spi_ctrl_data_rx),
+		.ready_o(spi_ctrl_data_rx_ready)
+	);
+	spi_master_controller u_spictrl(
+		.clk(HCLK),
+		.rstn(HRESETn),
+		.eot(s_eot),
+		.spi_clk_div(spi_clk_div),
+		.spi_clk_div_valid(spi_clk_div_valid),
+		.spi_status(spi_ctrl_status),
+		.spi_addr(spi_addr),
+		.spi_addr_len(spi_addr_len),
+		.spi_cmd(spi_cmd),
+		.spi_cmd_len(spi_cmd_len),
+		.spi_data_len(spi_data_len),
+		.spi_dummy_rd(spi_dummy_rd),
+		.spi_dummy_wr(spi_dummy_wr),
+		.spi_swrst(spi_swrst),
+		.spi_rd(spi_rd),
+		.spi_wr(spi_wr),
+		.spi_qrd(spi_qrd),
+		.spi_qwr(spi_qwr),
+		.spi_csreg(spi_csreg),
+		.spi_ctrl_data_tx(spi_ctrl_data_tx),
+		.spi_ctrl_data_tx_valid(spi_ctrl_data_tx_valid),
+		.spi_ctrl_data_tx_ready(spi_ctrl_data_tx_ready),
+		.spi_ctrl_data_rx(spi_ctrl_data_rx),
+		.spi_ctrl_data_rx_valid(spi_ctrl_data_rx_valid),
+		.spi_ctrl_data_rx_ready(spi_ctrl_data_rx_ready),
+		.spi_clk(spi_clk),
+		.spi_csn0(spi_csn0),
+		.spi_csn1(spi_csn1),
+		.spi_csn2(spi_csn2),
+		.spi_csn3(spi_csn3),
+		.spi_mode(spi_mode),
+		.spi_sdo0(spi_sdo0),
+		.spi_sdo1(spi_sdo1),
+		.spi_sdo2(spi_sdo2),
+		.spi_sdo3(spi_sdo3),
+		.spi_sdi0(spi_sdi0),
+		.spi_sdi1(spi_sdi1),
+		.spi_sdi2(spi_sdi2),
+		.spi_sdi3(spi_sdi3)
+	);
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_apb_if.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_apb_if.v
new file mode 100644
index 0000000..32690de
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_apb_if.v
@@ -0,0 +1,209 @@
+`define log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 )  ? 4 : (VALUE) < ( 32 )  ? 5 : (VALUE) < ( 64 )  ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25)
+
+`define REG_STATUS 4'b0000 // BASEREG + 0x00
+`define REG_CLKDIV 4'b0001 // BASEREG + 0x04
+`define REG_SPICMD 4'b0010 // BASEREG + 0x08
+`define REG_SPIADR 4'b0011 // BASEREG + 0x0C
+`define REG_SPILEN 4'b0100 // BASEREG + 0x10
+`define REG_SPIDUM 4'b0101 // BASEREG + 0x14
+`define REG_TXFIFO 4'b0110 // BASEREG + 0x18
+`define REG_RXFIFO 4'b1000 // BASEREG + 0x20
+`define REG_INTCFG 4'b1001 // BASEREG + 0x24
+`define REG_INTSTA 4'b1010 // BASEREG + 0x28
+
+module spi_master_apb_if 
+#(
+    parameter BUFFER_DEPTH   = 10,
+    parameter APB_ADDR_WIDTH = 12,  //APB slaves are 4KB by default
+    parameter LOG_BUFFER_DEPTH = `log2(BUFFER_DEPTH)
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	spi_clk_div,
+	spi_clk_div_valid,
+	spi_status,
+	spi_addr,
+	spi_addr_len,
+	spi_cmd,
+	spi_cmd_len,
+	spi_csreg,
+	spi_data_len,
+	spi_dummy_rd,
+	spi_dummy_wr,
+	spi_int_th_tx,
+	spi_int_th_rx,
+	spi_int_cnt_tx,
+	spi_int_cnt_rx,
+	spi_int_en,
+	spi_int_cnt_en,
+	spi_int_rd_sta,
+	spi_swrst,
+	spi_rd,
+	spi_wr,
+	spi_qrd,
+	spi_qwr,
+	spi_data_tx,
+	spi_data_tx_valid,
+	spi_data_tx_ready,
+	spi_data_rx,
+	spi_data_rx_valid,
+	spi_data_rx_ready
+);
+	//parameter BUFFER_DEPTH = 10;
+	//parameter APB_ADDR_WIDTH = 12;
+	//parameter LOG_BUFFER_DEPTH = (BUFFER_DEPTH < 1 ? 0 : (BUFFER_DEPTH < 2 ? 1 : (BUFFER_DEPTH < 4 ? 2 : (BUFFER_DEPTH < 8 ? 3 : (BUFFER_DEPTH < 16 ? 4 : (BUFFER_DEPTH < 32 ? 5 : (BUFFER_DEPTH < 64 ? 6 : (BUFFER_DEPTH < 128 ? 7 : (BUFFER_DEPTH < 256 ? 8 : (BUFFER_DEPTH < 512 ? 9 : (BUFFER_DEPTH < 1024 ? 10 : (BUFFER_DEPTH < 2048 ? 11 : (BUFFER_DEPTH < 4096 ? 12 : (BUFFER_DEPTH < 8192 ? 13 : (BUFFER_DEPTH < 16384 ? 14 : (BUFFER_DEPTH < 32768 ? 15 : (BUFFER_DEPTH < 65536 ? 16 : (BUFFER_DEPTH < 131072 ? 17 : (BUFFER_DEPTH < 262144 ? 18 : (BUFFER_DEPTH < 524288 ? 19 : (BUFFER_DEPTH < 1048576 ? 20 : (BUFFER_DEPTH < 2097152 ? 21 : (BUFFER_DEPTH < 4194304 ? 22 : (BUFFER_DEPTH < 8388608 ? 23 : (BUFFER_DEPTH < 16777216 ? 24 : 25)))))))))))))))))))))))));
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output reg [7:0] spi_clk_div;
+	output reg spi_clk_div_valid;
+	input wire [31:0] spi_status;
+	output reg [31:0] spi_addr;
+	output reg [5:0] spi_addr_len;
+	output reg [31:0] spi_cmd;
+	output reg [5:0] spi_cmd_len;
+	output reg [3:0] spi_csreg;
+	output reg [15:0] spi_data_len;
+	output reg [15:0] spi_dummy_rd;
+	output reg [15:0] spi_dummy_wr;
+	output reg [LOG_BUFFER_DEPTH:0] spi_int_th_tx;
+	output reg [LOG_BUFFER_DEPTH:0] spi_int_th_rx;
+	output reg [LOG_BUFFER_DEPTH:0] spi_int_cnt_tx;
+	output reg [LOG_BUFFER_DEPTH:0] spi_int_cnt_rx;
+	output reg spi_int_en;
+	output reg spi_int_cnt_en;
+	output wire spi_int_rd_sta;
+	output reg spi_swrst;
+	output reg spi_rd;
+	output reg spi_wr;
+	output reg spi_qrd;
+	output reg spi_qwr;
+	output wire [31:0] spi_data_tx;
+	output wire spi_data_tx_valid;
+	input wire spi_data_tx_ready;
+	input wire [31:0] spi_data_rx;
+	input wire spi_data_rx_valid;
+	output wire spi_data_rx_ready;
+	wire [3:0] write_address;
+	wire [3:0] read_address;
+	assign write_address = PADDR[5:2];
+	assign read_address = PADDR[5:2];
+	assign PSLVERR = 1'b0;
+	assign PREADY = 1'b1;
+	assign spi_int_rd_sta = ((PSEL & PENABLE) & ~PWRITE) & (read_address == 4'b1010);
+	always @(posedge HCLK or negedge HRESETn)
+		if (HRESETn == 1'b0) begin
+			spi_swrst <= 1'b0;
+			spi_rd <= 1'b0;
+			spi_wr <= 1'b0;
+			spi_qrd <= 1'b0;
+			spi_qwr <= 1'b0;
+			spi_clk_div_valid <= 1'b0;
+			spi_clk_div <= 1'sb0;
+			spi_cmd <= 1'sb0;
+			spi_addr <= 1'sb0;
+			spi_cmd_len <= 1'sb0;
+			spi_addr_len <= 1'sb0;
+			spi_data_len <= 1'sb0;
+			spi_dummy_rd <= 1'sb0;
+			spi_dummy_wr <= 1'sb0;
+			spi_csreg <= 1'sb0;
+			spi_int_th_tx <= 1'sb0;
+			spi_int_th_rx <= 1'sb0;
+			spi_int_cnt_tx <= 1'sb0;
+			spi_int_cnt_rx <= 1'sb0;
+			spi_int_cnt_en <= 1'b0;
+			spi_int_en <= 1'b0;
+		end
+		else if ((PSEL && PENABLE) && PWRITE) begin
+			spi_swrst <= 1'b0;
+			spi_rd <= 1'b0;
+			spi_wr <= 1'b0;
+			spi_qrd <= 1'b0;
+			spi_qwr <= 1'b0;
+			spi_clk_div_valid <= 1'b0;
+			case (write_address)
+				4'b0000: begin
+					spi_rd <= PWDATA[0];
+					spi_wr <= PWDATA[1];
+					spi_qrd <= PWDATA[2];
+					spi_qwr <= PWDATA[3];
+					spi_swrst <= PWDATA[4];
+					spi_csreg <= PWDATA[11:8];
+				end
+				4'b0001: begin
+					spi_clk_div <= PWDATA[7:0];
+					spi_clk_div_valid <= 1'b1;
+				end
+				4'b0010: spi_cmd <= PWDATA;
+				4'b0011: spi_addr <= PWDATA;
+				4'b0100: begin
+					spi_cmd_len <= PWDATA[5:0];
+					spi_addr_len <= PWDATA[13:8];
+					spi_data_len[7:0] <= PWDATA[23:16];
+					spi_data_len[15:8] <= PWDATA[31:24];
+				end
+				4'b0101: begin
+					spi_dummy_rd[7:0] <= PWDATA[7:0];
+					spi_dummy_rd[15:8] <= PWDATA[15:8];
+					spi_dummy_wr[7:0] <= PWDATA[23:16];
+					spi_dummy_wr[15:8] <= PWDATA[31:24];
+				end
+				4'b1001: begin
+					spi_int_th_tx <= PWDATA[LOG_BUFFER_DEPTH:0];
+					spi_int_th_rx <= PWDATA[8 + LOG_BUFFER_DEPTH:8];
+					spi_int_cnt_tx <= PWDATA[16 + LOG_BUFFER_DEPTH:16];
+					spi_int_cnt_rx <= PWDATA[24 + LOG_BUFFER_DEPTH:24];
+					spi_int_cnt_en <= PWDATA[30];
+					spi_int_en <= PWDATA[31];
+				end
+			endcase
+		end
+		else begin
+			spi_swrst <= 1'b0;
+			spi_rd <= 1'b0;
+			spi_wr <= 1'b0;
+			spi_qrd <= 1'b0;
+			spi_qwr <= 1'b0;
+			spi_clk_div_valid <= 1'b0;
+		end
+	always @(*)
+		case (read_address)
+			4'b0000: PRDATA = spi_status;
+			4'b0001: PRDATA = {24'h000000, spi_clk_div};
+			4'b0010: PRDATA = spi_cmd;
+			4'b0011: PRDATA = spi_addr;
+			4'b0100: PRDATA = {spi_data_len, 2'b00, spi_addr_len, 2'b00, spi_cmd_len};
+			4'b0101: PRDATA = {spi_dummy_wr, spi_dummy_rd};
+			4'b1000: PRDATA = spi_data_rx;
+			4'b1001: begin
+				PRDATA = 1'sb0;
+				PRDATA[LOG_BUFFER_DEPTH:0] = spi_int_th_tx;
+				PRDATA[8 + LOG_BUFFER_DEPTH:8] = spi_int_th_rx;
+				PRDATA[16 + LOG_BUFFER_DEPTH:16] = spi_int_cnt_tx;
+				PRDATA[24 + LOG_BUFFER_DEPTH:24] = spi_int_cnt_rx;
+				PRDATA[30] = spi_int_cnt_en;
+				PRDATA[31] = spi_int_en;
+			end
+			default: PRDATA = 1'sb0;
+		endcase
+	assign spi_data_tx = PWDATA;
+	assign spi_data_tx_valid = ((PSEL & PENABLE) & PWRITE) & (write_address == 4'b0110);
+	assign spi_data_rx_ready = ((PSEL & PENABLE) & ~PWRITE) & (read_address == 4'b1000);
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_clkgen.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_clkgen.v
new file mode 100644
index 0000000..787aa57
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_clkgen.v
@@ -0,0 +1,62 @@
+module spi_master_clkgen (
+	clk,
+	rstn,
+	en,
+	clk_div,
+	clk_div_valid,
+	spi_clk,
+	spi_fall,
+	spi_rise
+);
+	input wire clk;
+	input wire rstn;
+	input wire en;
+	input wire [7:0] clk_div;
+	input wire clk_div_valid;
+	output reg spi_clk;
+	output reg spi_fall;
+	output reg spi_rise;
+	reg [7:0] counter_trgt;
+	reg [7:0] counter_trgt_next;
+	reg [7:0] counter;
+	reg [7:0] counter_next;
+	reg spi_clk_next;
+	reg running;
+	always @(*) begin
+		spi_rise = 1'b0;
+		spi_fall = 1'b0;
+		if (clk_div_valid)
+			counter_trgt_next = clk_div;
+		else
+			counter_trgt_next = counter_trgt;
+		if (counter == counter_trgt) begin
+			counter_next = 0;
+			spi_clk_next = ~spi_clk;
+			if (spi_clk == 1'b0)
+				spi_rise = running;
+			else
+				spi_fall = running;
+		end
+		else begin
+			counter_next = counter + 1;
+			spi_clk_next = spi_clk;
+		end
+	end
+	always @(posedge clk or negedge rstn)
+		if (rstn == 1'b0) begin
+			counter_trgt <= 'h0;
+			counter <= 'h0;
+			spi_clk <= 1'b0;
+			running <= 1'b0;
+		end
+		else begin
+			counter_trgt <= counter_trgt_next;
+			if (!((spi_clk == 1'b0) && ~en)) begin
+				running <= 1'b1;
+				spi_clk <= spi_clk_next;
+				counter <= counter_next;
+			end
+			else
+				running <= 1'b0;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_controller.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_controller.v
new file mode 100644
index 0000000..83fb576
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_controller.v
@@ -0,0 +1,488 @@
+`define SPI_STD     2'b00
+`define SPI_QUAD_TX 2'b01
+`define SPI_QUAD_RX 2'b10
+
+module spi_master_controller (
+	clk,
+	rstn,
+	eot,
+	spi_clk_div,
+	spi_clk_div_valid,
+	spi_status,
+	spi_addr,
+	spi_addr_len,
+	spi_cmd,
+	spi_cmd_len,
+	spi_data_len,
+	spi_dummy_rd,
+	spi_dummy_wr,
+	spi_csreg,
+	spi_swrst,
+	spi_rd,
+	spi_wr,
+	spi_qrd,
+	spi_qwr,
+	spi_ctrl_data_tx,
+	spi_ctrl_data_tx_valid,
+	spi_ctrl_data_tx_ready,
+	spi_ctrl_data_rx,
+	spi_ctrl_data_rx_valid,
+	spi_ctrl_data_rx_ready,
+	spi_clk,
+	spi_csn0,
+	spi_csn1,
+	spi_csn2,
+	spi_csn3,
+	spi_mode,
+	spi_sdo0,
+	spi_sdo1,
+	spi_sdo2,
+	spi_sdo3,
+	spi_sdi0,
+	spi_sdi1,
+	spi_sdi2,
+	spi_sdi3
+);
+	input wire clk;
+	input wire rstn;
+	output reg eot;
+	input wire [7:0] spi_clk_div;
+	input wire spi_clk_div_valid;
+	output reg [6:0] spi_status;
+	input wire [31:0] spi_addr;
+	input wire [5:0] spi_addr_len;
+	input wire [31:0] spi_cmd;
+	input wire [5:0] spi_cmd_len;
+	input wire [15:0] spi_data_len;
+	input wire [15:0] spi_dummy_rd;
+	input wire [15:0] spi_dummy_wr;
+	input wire [3:0] spi_csreg;
+	input wire spi_swrst;
+	input wire spi_rd;
+	input wire spi_wr;
+	input wire spi_qrd;
+	input wire spi_qwr;
+	input wire [31:0] spi_ctrl_data_tx;
+	input wire spi_ctrl_data_tx_valid;
+	output reg spi_ctrl_data_tx_ready;
+	output wire [31:0] spi_ctrl_data_rx;
+	output wire spi_ctrl_data_rx_valid;
+	input wire spi_ctrl_data_rx_ready;
+	output wire spi_clk;
+	output wire spi_csn0;
+	output wire spi_csn1;
+	output wire spi_csn2;
+	output wire spi_csn3;
+	output reg [1:0] spi_mode;
+	output wire spi_sdo0;
+	output wire spi_sdo1;
+	output wire spi_sdo2;
+	output wire spi_sdo3;
+	input wire spi_sdi0;
+	input wire spi_sdi1;
+	input wire spi_sdi2;
+	input wire spi_sdi3;
+	wire spi_rise;
+	wire spi_fall;
+	reg spi_clock_en;
+	reg spi_en_tx;
+	reg spi_en_rx;
+	reg [15:0] counter_tx;
+	reg counter_tx_valid;
+	reg [15:0] counter_rx;
+	reg counter_rx_valid;
+	reg [31:0] data_to_tx;
+	reg data_to_tx_valid;
+	wire data_to_tx_ready;
+	wire en_quad;
+	reg en_quad_int;
+	reg do_tx;
+	reg do_rx;
+	wire tx_done;
+	wire rx_done;
+	reg [1:0] s_spi_mode;
+	reg ctrl_data_valid;
+	reg spi_cs;
+	wire tx_clk_en;
+	wire rx_clk_en;
+	reg [2:0] ctrl_data_mux;
+	reg [4:0] state;
+	reg [4:0] state_next;
+	assign en_quad = (spi_qrd | spi_qwr) | en_quad_int;
+	spi_master_clkgen u_clkgen(
+		.clk(clk),
+		.rstn(rstn),
+		.en(spi_clock_en),
+		.clk_div(spi_clk_div),
+		.clk_div_valid(spi_clk_div_valid),
+		.spi_clk(spi_clk),
+		.spi_fall(spi_fall),
+		.spi_rise(spi_rise)
+	);
+	spi_master_tx u_txreg(
+		.clk(clk),
+		.rstn(rstn),
+		.en(spi_en_tx),
+		.tx_edge(spi_fall),
+		.tx_done(tx_done),
+		.sdo0(spi_sdo0),
+		.sdo1(spi_sdo1),
+		.sdo2(spi_sdo2),
+		.sdo3(spi_sdo3),
+		.en_quad_in(en_quad),
+		.counter_in(counter_tx),
+		.counter_in_upd(counter_tx_valid),
+		.data(data_to_tx),
+		.data_valid(data_to_tx_valid),
+		.data_ready(data_to_tx_ready),
+		.clk_en_o(tx_clk_en)
+	);
+	spi_master_rx u_rxreg(
+		.clk(clk),
+		.rstn(rstn),
+		.en(spi_en_rx),
+		.rx_edge(spi_rise),
+		.rx_done(rx_done),
+		.sdi0(spi_sdi0),
+		.sdi1(spi_sdi1),
+		.sdi2(spi_sdi2),
+		.sdi3(spi_sdi3),
+		.en_quad_in(en_quad),
+		.counter_in(counter_rx),
+		.counter_in_upd(counter_rx_valid),
+		.data(spi_ctrl_data_rx),
+		.data_valid(spi_ctrl_data_rx_valid),
+		.data_ready(spi_ctrl_data_rx_ready),
+		.clk_en_o(rx_clk_en)
+	);
+	always @(*) begin
+		data_to_tx = 'h0;
+		data_to_tx_valid = 1'b0;
+		spi_ctrl_data_tx_ready = 1'b0;
+		case (ctrl_data_mux)
+			3'd0: begin
+				data_to_tx = 1'sb0;
+				data_to_tx_valid = 1'b0;
+				spi_ctrl_data_tx_ready = 1'b0;
+			end
+			3'd1: begin
+				data_to_tx = 1'sb0;
+				data_to_tx_valid = 1'b1;
+			end
+			3'd2: begin
+				data_to_tx = spi_cmd;
+				data_to_tx_valid = ctrl_data_valid;
+				spi_ctrl_data_tx_ready = 1'b0;
+			end
+			3'd3: begin
+				data_to_tx = spi_addr;
+				data_to_tx_valid = ctrl_data_valid;
+				spi_ctrl_data_tx_ready = 1'b0;
+			end
+			3'd4: begin
+				data_to_tx = spi_ctrl_data_tx;
+				data_to_tx_valid = spi_ctrl_data_tx_valid;
+				spi_ctrl_data_tx_ready = data_to_tx_ready;
+			end
+		endcase
+	end
+	always @(*) begin
+		spi_cs = 1'b1;
+		spi_clock_en = 1'b0;
+		counter_tx = 1'sb0;
+		counter_tx_valid = 1'b0;
+		counter_rx = 1'sb0;
+		counter_rx_valid = 1'b0;
+		state_next = state;
+		ctrl_data_mux = 3'd0;
+		ctrl_data_valid = 1'b0;
+		spi_en_rx = 1'b0;
+		spi_en_tx = 1'b0;
+		spi_status = 1'sb0;
+		s_spi_mode = 2'b10;
+		eot = 1'b0;
+		case (state)
+			5'd0: begin
+				spi_status[0] = 1'b1;
+				s_spi_mode = 2'b10;
+				if (((spi_rd || spi_wr) || spi_qrd) || spi_qwr) begin
+					spi_cs = 1'b0;
+					spi_clock_en = 1'b1;
+					if (spi_cmd_len != 0) begin
+						s_spi_mode = (spi_qrd | spi_qwr ? 2'b01 : 2'b00);
+						counter_tx = {8'h00, spi_cmd_len};
+						counter_tx_valid = 1'b1;
+						ctrl_data_mux = 3'd2;
+						ctrl_data_valid = 1'b1;
+						spi_en_tx = 1'b1;
+						state_next = 5'd1;
+					end
+					else if (spi_addr_len != 0) begin
+						s_spi_mode = (spi_qrd | spi_qwr ? 2'b01 : 2'b00);
+						counter_tx = {8'h00, spi_addr_len};
+						counter_tx_valid = 1'b1;
+						ctrl_data_mux = 3'd3;
+						ctrl_data_valid = 1'b1;
+						spi_en_tx = 1'b1;
+						state_next = 5'd2;
+					end
+					else if (spi_data_len != 0)
+						if (spi_rd || spi_qrd) begin
+							s_spi_mode = (spi_qrd ? 2'b10 : 2'b00);
+							if (spi_dummy_rd != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_rd[13:0], 2'b00} : spi_dummy_rd);
+								counter_tx_valid = 1'b1;
+								spi_en_tx = 1'b1;
+								ctrl_data_mux = 3'd1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_rx = spi_data_len;
+								counter_rx_valid = 1'b1;
+								spi_en_rx = 1'b1;
+								state_next = 5'd6;
+							end
+						end
+						else begin
+							s_spi_mode = (spi_qwr ? 2'b01 : 2'b00);
+							if (spi_dummy_wr != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_wr[13:0], 2'b00} : spi_dummy_wr);
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd1;
+								spi_en_tx = 1'b1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_tx = spi_data_len;
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd4;
+								ctrl_data_valid = 1'b0;
+								spi_en_tx = 1'b1;
+								state_next = 5'd5;
+							end
+						end
+				end
+				else begin
+					spi_cs = 1'b1;
+					state_next = 5'd0;
+				end
+			end
+			5'd1: begin
+				spi_status[1] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = 1'b1;
+				s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+				if (tx_done) begin
+					if (spi_addr_len != 0) begin
+						s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+						counter_tx = {8'h00, spi_addr_len};
+						counter_tx_valid = 1'b1;
+						ctrl_data_mux = 3'd3;
+						ctrl_data_valid = 1'b1;
+						spi_en_tx = 1'b1;
+						state_next = 5'd2;
+					end
+					else if (spi_data_len != 0) begin
+						if (do_rx) begin
+							s_spi_mode = (en_quad ? 2'b10 : 2'b00);
+							if (spi_dummy_rd != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_rd[13:0], 2'b00} : spi_dummy_rd);
+								counter_tx_valid = 1'b1;
+								spi_en_tx = 1'b1;
+								ctrl_data_mux = 3'd1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_rx = spi_data_len;
+								counter_rx_valid = 1'b1;
+								spi_en_rx = 1'b1;
+								state_next = 5'd6;
+							end
+						end
+						else begin
+							s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+							if (spi_dummy_wr != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_wr[13:0], 2'b00} : spi_dummy_wr);
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd1;
+								spi_en_tx = 1'b1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_tx = spi_data_len;
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd4;
+								ctrl_data_valid = 1'b1;
+								spi_en_tx = 1'b1;
+								state_next = 5'd5;
+							end
+						end
+					end
+					else
+						state_next = 5'd0;
+				end
+				else begin
+					spi_en_tx = 1'b1;
+					state_next = 5'd1;
+				end
+			end
+			5'd2: begin
+				spi_en_tx = 1'b1;
+				spi_status[2] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = 1'b1;
+				s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+				if (tx_done)
+					if (spi_data_len != 0) begin
+						if (do_rx) begin
+							s_spi_mode = (en_quad ? 2'b10 : 2'b00);
+							if (spi_dummy_rd != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_rd[13:0], 2'b00} : spi_dummy_rd);
+								counter_tx_valid = 1'b1;
+								spi_en_tx = 1'b1;
+								ctrl_data_mux = 3'd1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_rx = spi_data_len;
+								counter_rx_valid = 1'b1;
+								spi_en_rx = 1'b1;
+								state_next = 5'd6;
+							end
+						end
+						else begin
+							s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+							spi_en_tx = 1'b1;
+							if (spi_dummy_wr != 0) begin
+								counter_tx = (en_quad ? {spi_dummy_wr[13:0], 2'b00} : spi_dummy_wr);
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd1;
+								state_next = 5'd4;
+							end
+							else begin
+								counter_tx = spi_data_len;
+								counter_tx_valid = 1'b1;
+								ctrl_data_mux = 3'd4;
+								ctrl_data_valid = 1'b1;
+								state_next = 5'd5;
+							end
+						end
+					end
+					else
+						state_next = 5'd0;
+			end
+			5'd3: begin
+				spi_status[3] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = 1'b1;
+				spi_en_tx = 1'b1;
+			end
+			5'd4: begin
+				spi_en_tx = 1'b1;
+				spi_status[4] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = 1'b1;
+				s_spi_mode = (en_quad ? 2'b10 : 2'b00);
+				if (tx_done) begin
+					if (spi_data_len != 0) begin
+						if (do_rx) begin
+							counter_rx = spi_data_len;
+							counter_rx_valid = 1'b1;
+							spi_en_rx = 1'b1;
+							state_next = 5'd6;
+						end
+						else begin
+							counter_tx = spi_data_len;
+							counter_tx_valid = 1'b1;
+							s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+							spi_clock_en = tx_clk_en;
+							spi_en_tx = 1'b1;
+							state_next = 5'd5;
+						end
+					end
+					else begin
+						eot = 1'b1;
+						state_next = 5'd0;
+					end
+				end
+				else begin
+					ctrl_data_mux = 3'd1;
+					spi_en_tx = 1'b1;
+					state_next = 5'd4;
+				end
+			end
+			5'd5: begin
+				spi_status[5] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = tx_clk_en;
+				ctrl_data_mux = 3'd4;
+				ctrl_data_valid = 1'b1;
+				spi_en_tx = 1'b1;
+				s_spi_mode = (en_quad ? 2'b01 : 2'b00);
+				if (tx_done) begin
+					eot = 1'b1;
+					state_next = 5'd0;
+					spi_clock_en = 1'b0;
+				end
+				else
+					state_next = 5'd5;
+			end
+			5'd6: begin
+				spi_status[6] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = rx_clk_en;
+				s_spi_mode = (en_quad ? 2'b10 : 2'b00);
+				if (rx_done)
+					state_next = 5'd7;
+				else begin
+					spi_en_rx = 1'b1;
+					state_next = 5'd6;
+				end
+			end
+			5'd7: begin
+				spi_status[6] = 1'b1;
+				spi_cs = 1'b0;
+				spi_clock_en = 1'b0;
+				s_spi_mode = (en_quad ? 2'b10 : 2'b00);
+				if (spi_fall) begin
+					eot = 1'b1;
+					state_next = 5'd0;
+				end
+				else
+					state_next = 5'd7;
+			end
+		endcase
+	end
+	always @(posedge clk or negedge rstn)
+		if (rstn == 1'b0) begin
+			state <= 5'd0;
+			en_quad_int <= 1'b0;
+			do_rx <= 1'b0;
+			do_tx <= 1'b0;
+			spi_mode <= 2'b10;
+		end
+		else begin
+			state <= state_next;
+			spi_mode <= s_spi_mode;
+			if (spi_qrd || spi_qwr)
+				en_quad_int <= 1'b1;
+			else if (state_next == 5'd0)
+				en_quad_int <= 1'b0;
+			if (spi_rd || spi_qrd) begin
+				do_rx <= 1'b1;
+				do_tx <= 1'b0;
+			end
+			else if (spi_wr || spi_qwr) begin
+				do_rx <= 1'b0;
+				do_tx <= 1'b1;
+			end
+			else if (state_next == 5'd0) begin
+				do_rx <= 1'b0;
+				do_tx <= 1'b0;
+			end
+		end
+	assign spi_csn0 = ~spi_csreg[0] | spi_cs;
+	assign spi_csn1 = ~spi_csreg[1] | spi_cs;
+	assign spi_csn2 = ~spi_csreg[2] | spi_cs;
+	assign spi_csn3 = ~spi_csreg[3] | spi_cs;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_fifo.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_fifo.v
new file mode 100644
index 0000000..08e20a1
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_fifo.v
@@ -0,0 +1,85 @@
+`define log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 )  ? 4 : (VALUE) < ( 32 )  ? 5 : (VALUE) < ( 64 )  ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25)
+
+module spi_master_fifo 
+#(
+    parameter DATA_WIDTH = 32,
+    parameter BUFFER_DEPTH = 2,
+    parameter LOG_BUFFER_DEPTH = `log2(BUFFER_DEPTH)
+)
+(
+	clk_i,
+	rst_ni,
+	clr_i,
+	elements_o,
+	data_o,
+	valid_o,
+	ready_i,
+	valid_i,
+	data_i,
+	ready_o
+);
+	//parameter DATA_WIDTH = 32;
+	//parameter BUFFER_DEPTH = 2;
+	//parameter LOG_BUFFER_DEPTH = (BUFFER_DEPTH < 1 ? 0 : (BUFFER_DEPTH < 2 ? 1 : (BUFFER_DEPTH < 4 ? 2 : (BUFFER_DEPTH < 8 ? 3 : (BUFFER_DEPTH < 16 ? 4 : (BUFFER_DEPTH < 32 ? 5 : (BUFFER_DEPTH < 64 ? 6 : (BUFFER_DEPTH < 128 ? 7 : (BUFFER_DEPTH < 256 ? 8 : (BUFFER_DEPTH < 512 ? 9 : (BUFFER_DEPTH < 1024 ? 10 : (BUFFER_DEPTH < 2048 ? 11 : (BUFFER_DEPTH < 4096 ? 12 : (BUFFER_DEPTH < 8192 ? 13 : (BUFFER_DEPTH < 16384 ? 14 : (BUFFER_DEPTH < 32768 ? 15 : (BUFFER_DEPTH < 65536 ? 16 : (BUFFER_DEPTH < 131072 ? 17 : (BUFFER_DEPTH < 262144 ? 18 : (BUFFER_DEPTH < 524288 ? 19 : (BUFFER_DEPTH < 1048576 ? 20 : (BUFFER_DEPTH < 2097152 ? 21 : (BUFFER_DEPTH < 4194304 ? 22 : (BUFFER_DEPTH < 8388608 ? 23 : (BUFFER_DEPTH < 16777216 ? 24 : 25)))))))))))))))))))))))));
+	input wire clk_i;
+	input wire rst_ni;
+	input wire clr_i;
+	output wire [LOG_BUFFER_DEPTH:0] elements_o;
+	output wire [DATA_WIDTH - 1:0] data_o;
+	output wire valid_o;
+	input wire ready_i;
+	input wire valid_i;
+	input wire [DATA_WIDTH - 1:0] data_i;
+	output wire ready_o;
+	reg [LOG_BUFFER_DEPTH - 1:0] pointer_in;
+	reg [LOG_BUFFER_DEPTH - 1:0] pointer_out;
+	reg [LOG_BUFFER_DEPTH:0] elements;
+	reg [DATA_WIDTH - 1:0] buffer [BUFFER_DEPTH - 1:0];
+	wire full;
+	integer loop1;
+	assign full = elements == BUFFER_DEPTH;
+	assign elements_o = elements;
+	always @(posedge clk_i or negedge rst_ni) begin : elements_sequential
+		if (rst_ni == 1'b0)
+			elements <= 0;
+		else if (clr_i)
+			elements <= 0;
+		else if ((ready_i && valid_o) && (!valid_i || full))
+			elements <= elements - 1;
+		else if (((!valid_o || !ready_i) && valid_i) && !full)
+			elements <= elements + 1;
+	end
+	always @(posedge clk_i or negedge rst_ni) begin : buffers_sequential
+		if (rst_ni == 1'b0) begin
+			for (loop1 = 0; loop1 < BUFFER_DEPTH; loop1 = loop1 + 1)
+				buffer[loop1] <= 0;
+		end
+		else if (valid_i && !full)
+			buffer[pointer_in] <= data_i;
+	end
+	always @(posedge clk_i or negedge rst_ni) begin : sequential
+		if (rst_ni == 1'b0) begin
+			pointer_out <= 0;
+			pointer_in <= 0;
+		end
+		else if (clr_i) begin
+			pointer_out <= 0;
+			pointer_in <= 0;
+		end
+		else begin
+			if (valid_i && !full)
+				if (pointer_in == $unsigned(BUFFER_DEPTH - 1))
+					pointer_in <= 0;
+				else
+					pointer_in <= pointer_in + 1;
+			if (ready_i && valid_o)
+				if (pointer_out == $unsigned(BUFFER_DEPTH - 1))
+					pointer_out <= 0;
+				else
+					pointer_out <= pointer_out + 1;
+		end
+	end
+	assign data_o = buffer[pointer_out];
+	assign valid_o = elements != 0;
+	assign ready_o = ~full;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_rx.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_rx.v
new file mode 100644
index 0000000..8885afc
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_rx.v
@@ -0,0 +1,116 @@
+module spi_master_rx (
+	clk,
+	rstn,
+	en,
+	rx_edge,
+	rx_done,
+	sdi0,
+	sdi1,
+	sdi2,
+	sdi3,
+	en_quad_in,
+	counter_in,
+	counter_in_upd,
+	data,
+	data_ready,
+	data_valid,
+	clk_en_o
+);
+	input wire clk;
+	input wire rstn;
+	input wire en;
+	input wire rx_edge;
+	output wire rx_done;
+	input wire sdi0;
+	input wire sdi1;
+	input wire sdi2;
+	input wire sdi3;
+	input wire en_quad_in;
+	input wire [15:0] counter_in;
+	input wire counter_in_upd;
+	output wire [31:0] data;
+	input wire data_ready;
+	output reg data_valid;
+	output reg clk_en_o;
+	reg [31:0] data_int;
+	reg [31:0] data_int_next;
+	reg [15:0] counter;
+	reg [15:0] counter_trgt;
+	reg [15:0] counter_next;
+	reg [15:0] counter_trgt_next;
+	wire done;
+	wire reg_done;
+	reg [1:0] rx_CS;
+	reg [1:0] rx_NS;
+	assign reg_done = (!en_quad_in && (counter[4:0] == 5'b11111)) || (en_quad_in && (counter[2:0] == 3'b111));
+	assign data = data_int_next;
+	assign rx_done = done;
+	always @(*)
+		if (counter_in_upd)
+			counter_trgt_next = (en_quad_in ? {2'b00, counter_in[15:2]} : counter_in);
+		else
+			counter_trgt_next = counter_trgt;
+	assign done = (counter == (counter_trgt - 1)) && rx_edge;
+	always @(*) begin
+		rx_NS = rx_CS;
+		clk_en_o = 1'b0;
+		data_int_next = data_int;
+		data_valid = 1'b0;
+		counter_next = counter;
+		case (rx_CS)
+			2'd0: begin
+				clk_en_o = 1'b0;
+				if (en)
+					rx_NS = 2'd1;
+			end
+			2'd1: begin
+				clk_en_o = 1'b1;
+				if (rx_edge) begin
+					counter_next = counter + 1;
+					if (en_quad_in)
+						data_int_next = {data_int[27:0], sdi3, sdi2, sdi1, sdi0};
+					else
+						data_int_next = {data_int[30:0], sdi0};
+					if (rx_done) begin
+						counter_next = 0;
+						data_valid = 1'b1;
+						if (data_ready)
+							rx_NS = 2'd0;
+						else
+							rx_NS = 2'd3;
+					end
+					else if (reg_done) begin
+						data_valid = 1'b1;
+						if (~data_ready) begin
+							clk_en_o = 1'b0;
+							rx_NS = 2'd2;
+						end
+					end
+				end
+			end
+			2'd3: begin
+				data_valid = 1'b1;
+				if (data_ready)
+					rx_NS = 2'd0;
+			end
+			2'd2: begin
+				data_valid = 1'b1;
+				if (data_ready)
+					rx_NS = 2'd1;
+			end
+		endcase
+	end
+	always @(posedge clk or negedge rstn)
+		if (rstn == 0) begin
+			counter <= 0;
+			counter_trgt <= 'h8;
+			data_int <= 1'sb0;
+			rx_CS <= 2'd0;
+		end
+		else begin
+			counter <= counter_next;
+			counter_trgt <= counter_trgt_next;
+			data_int <= data_int_next;
+			rx_CS <= rx_NS;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_spi_master/spi_master_tx.v b/verilog/rtl/ips/apb/apb_spi_master/spi_master_tx.v
new file mode 100644
index 0000000..ede9996
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_spi_master/spi_master_tx.v
@@ -0,0 +1,115 @@
+module spi_master_tx (
+	clk,
+	rstn,
+	en,
+	tx_edge,
+	tx_done,
+	sdo0,
+	sdo1,
+	sdo2,
+	sdo3,
+	en_quad_in,
+	counter_in,
+	counter_in_upd,
+	data,
+	data_valid,
+	data_ready,
+	clk_en_o
+);
+	input wire clk;
+	input wire rstn;
+	input wire en;
+	input wire tx_edge;
+	output wire tx_done;
+	output wire sdo0;
+	output wire sdo1;
+	output wire sdo2;
+	output wire sdo3;
+	input wire en_quad_in;
+	input wire [15:0] counter_in;
+	input wire counter_in_upd;
+	input wire [31:0] data;
+	input wire data_valid;
+	output reg data_ready;
+	output reg clk_en_o;
+	reg [31:0] data_int;
+	reg [31:0] data_int_next;
+	reg [15:0] counter;
+	reg [15:0] counter_trgt;
+	reg [15:0] counter_next;
+	reg [15:0] counter_trgt_next;
+	wire done;
+	wire reg_done;
+	reg [0:0] tx_CS;
+	reg [0:0] tx_NS;
+	assign sdo0 = (en_quad_in ? data_int[28] : data_int[31]);
+	assign sdo1 = data_int[29];
+	assign sdo2 = data_int[30];
+	assign sdo3 = data_int[31];
+	assign tx_done = done;
+	assign reg_done = (!en_quad_in && (counter[4:0] == 5'b11111)) || (en_quad_in && (counter[2:0] == 3'b111));
+	always @(*)
+		if (counter_in_upd)
+			counter_trgt_next = (en_quad_in ? {2'b00, counter_in[15:2]} : counter_in);
+		else
+			counter_trgt_next = counter_trgt;
+	assign done = (counter == (counter_trgt - 1)) && tx_edge;
+	always @(*) begin
+		tx_NS = tx_CS;
+		clk_en_o = 1'b0;
+		data_int_next = data_int;
+		data_ready = 1'b0;
+		counter_next = counter;
+		case (tx_CS)
+			1'd0: begin
+				clk_en_o = 1'b0;
+				if (en && data_valid) begin
+					data_int_next = data;
+					data_ready = 1'b1;
+					tx_NS = 1'd1;
+				end
+			end
+			1'd1: begin
+				clk_en_o = 1'b1;
+				if (tx_edge) begin
+					counter_next = counter + 1;
+					data_int_next = (en_quad_in ? {data_int[27:0], 4'b0000} : {data_int[30:0], 1'b0});
+					if (tx_done) begin
+						counter_next = 0;
+						if (en && data_valid) begin
+							data_int_next = data;
+							data_ready = 1'b1;
+							tx_NS = 1'd1;
+						end
+						else begin
+							clk_en_o = 1'b0;
+							tx_NS = 1'd0;
+						end
+					end
+					else if (reg_done)
+						if (data_valid) begin
+							data_int_next = data;
+							data_ready = 1'b1;
+						end
+						else begin
+							clk_en_o = 1'b0;
+							tx_NS = 1'd0;
+						end
+				end
+			end
+		endcase
+	end
+	always @(posedge clk or negedge rstn)
+		if (~rstn) begin
+			counter <= 0;
+			counter_trgt <= 'h8;
+			data_int <= 'h0;
+			tx_CS <= 1'd0;
+		end
+		else begin
+			counter <= counter_next;
+			counter_trgt <= counter_trgt_next;
+			data_int <= data_int_next;
+			tx_CS <= tx_NS;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_timer b/verilog/rtl/ips/apb/apb_timer
deleted file mode 160000
index c8b851b..0000000
--- a/verilog/rtl/ips/apb/apb_timer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c8b851bc43a22cb47f490e819b04fd0687b901f8
diff --git a/verilog/rtl/ips/apb/apb_timer/apb_timer.v b/verilog/rtl/ips/apb/apb_timer/apb_timer.v
new file mode 100644
index 0000000..a51a47d
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_timer/apb_timer.v
@@ -0,0 +1,71 @@
+module apb_timer 
+#(
+    parameter APB_ADDR_WIDTH = 12,  //APB slaves are 4KB by default
+    parameter TIMER_CNT = 2 // how many timers should be instantiated
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	irq_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	//parameter TIMER_CNT = 2;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output reg PREADY;
+	output reg PSLVERR;
+	output wire [(TIMER_CNT * 2) - 1:0] irq_o;
+	reg [TIMER_CNT - 1:0] psel_int;
+	wire [TIMER_CNT - 1:0] pready;
+	wire [TIMER_CNT - 1:0] pslverr;
+	wire [$clog2(TIMER_CNT) - 1:0] slave_address_int;
+	wire [(TIMER_CNT * 32) - 1:0] prdata;
+	assign slave_address_int = PADDR[$clog2(TIMER_CNT) + (2'd2 + 1):2'd2 + 2];
+	always @(*) begin
+		psel_int = 1'sb0;
+		psel_int[slave_address_int] = PSEL;
+	end
+	always @(*)
+		if (psel_int != {TIMER_CNT {1'sb0}}) begin
+			PRDATA = prdata[slave_address_int * 32+:32];
+			PREADY = pready[slave_address_int];
+			PSLVERR = pslverr[slave_address_int];
+		end
+		else begin
+			PRDATA = 1'sb0;
+			PREADY = 1'b1;
+			PSLVERR = 1'b0;
+		end
+	genvar k;
+	generate
+		for (k = 0; k < TIMER_CNT; k = k + 1) begin : TIMER_GEN
+			timer timer_i(
+				.HCLK(HCLK),
+				.HRESETn(HRESETn),
+				.PADDR(PADDR),
+				.PWDATA(PWDATA),
+				.PWRITE(PWRITE),
+				.PSEL(psel_int[k]),
+				.PENABLE(PENABLE),
+				.PRDATA(prdata[k * 32+:32]),
+				.PREADY(pready[k]),
+				.PSLVERR(pslverr[k]),
+				.irq_o(irq_o[(2 * k) + 1:2 * k])
+			);
+		end
+	endgenerate
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_timer/timer.v b/verilog/rtl/ips/apb/apb_timer/timer.v
new file mode 100644
index 0000000..7448cc8
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_timer/timer.v
@@ -0,0 +1,86 @@
+module timer 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	HCLK,
+	HRESETn,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	irq_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire HCLK;
+	input wire HRESETn;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	output reg [1:0] irq_o;
+	wire [1:0] register_adr;
+	assign register_adr = PADDR[4:2];
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+	reg [95:0] regs_q;
+	reg [95:0] regs_n;
+	reg [31:0] cycle_counter_n;
+	reg [31:0] cycle_counter_q;
+	wire [2:0] prescaler_int;
+	always @(*) begin
+		irq_o = 2'b00;
+		if (regs_q[64+:32] == 32'hffffffff)
+			irq_o[0] = 1'b1;
+		if ((regs_q[0+:32] != 'b0) && (regs_q[64+:32] == regs_q[0+:32]))
+			irq_o[1] = 1'b1;
+	end
+	assign prescaler_int = regs_q[37-:3];
+	always @(*) begin
+		regs_n = regs_q;
+		cycle_counter_n = cycle_counter_q + 1;
+		if ((irq_o[0] == 1'b1) || (irq_o[1] == 1'b1))
+			regs_n[64+:32] = 1'b0;
+		else if ((regs_q[32] && (prescaler_int != 'b0)) && (prescaler_int == cycle_counter_q))
+			regs_n[64+:32] = regs_q[64+:32] + 1;
+		else if (regs_q[32] && (regs_q[37-:3] == 'b0))
+			regs_n[64+:32] = regs_q[64+:32] + 1;
+		if (cycle_counter_q >= regs_q[32+:32])
+			cycle_counter_n = 32'b00000000000000000000000000000000;
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				2'b00: regs_n[64+:32] = PWDATA;
+				2'b01: regs_n[32+:32] = PWDATA;
+				2'b10: begin
+					regs_n[0+:32] = PWDATA;
+					regs_n[64+:32] = 32'b00000000000000000000000000000000;
+				end
+			endcase
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				2'b00: PRDATA = regs_q[64+:32];
+				2'b01: PRDATA = regs_q[32+:32];
+				2'b10: PRDATA = regs_q[0+:32];
+			endcase
+	end
+	always @(posedge HCLK or negedge HRESETn)
+		if (~HRESETn) begin
+			regs_q <= {3 {32'b00000000000000000000000000000000}};
+			cycle_counter_q <= 32'b00000000000000000000000000000000;
+		end
+		else begin
+			regs_q <= regs_n;
+			cycle_counter_q <= cycle_counter_n;
+		end
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv b/verilog/rtl/ips/apb/apb_uart_sv
deleted file mode 160000
index dfad6e0..0000000
--- a/verilog/rtl/ips/apb/apb_uart_sv
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit dfad6e04d19cc9481d3cd2750b45b970dc61271b
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/apb_uart.v b/verilog/rtl/ips/apb/apb_uart_sv/apb_uart.v
new file mode 100644
index 0000000..9969081
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/apb_uart.v
@@ -0,0 +1,241 @@
+module apb_uart_sv 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	CLK,
+	RSTN,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	rx_i,
+	tx_o,
+	event_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire CLK;
+	input wire RSTN;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	input wire rx_i;
+	output wire tx_o;
+	output wire event_o;
+	parameter RBR = 3'h0;
+	parameter THR = 3'h0;
+	parameter DLL = 3'h0;
+	parameter IER = 3'h1;
+	parameter DLM = 3'h1;
+	parameter IIR = 3'h2;
+	parameter FCR = 3'h2;
+	parameter LCR = 3'h3;
+	parameter MCR = 3'h4;
+	parameter LSR = 3'h5;
+	parameter MSR = 3'h6;
+	parameter SCR = 3'h7;
+	parameter TX_FIFO_DEPTH = 16;
+	parameter RX_FIFO_DEPTH = 16;
+	wire [2:0] register_adr;
+	reg [79:0] regs_q;
+	reg [79:0] regs_n;
+	reg [1:0] trigger_level_n;
+	reg [1:0] trigger_level_q;
+	wire [7:0] rx_data;
+	wire parity_error;
+	wire [3:0] IIR_o;
+	reg [3:0] clr_int;
+	wire tx_ready;
+	reg apb_rx_ready;
+	wire rx_valid;
+	reg tx_fifo_clr_n;
+	reg tx_fifo_clr_q;
+	reg rx_fifo_clr_n;
+	reg rx_fifo_clr_q;
+	reg fifo_tx_valid;
+	wire tx_valid;
+	wire fifo_rx_valid;
+	reg fifo_rx_ready;
+	wire rx_ready;
+	reg [7:0] fifo_tx_data;
+	wire [8:0] fifo_rx_data;
+	wire [7:0] tx_data;
+	wire [$clog2(TX_FIFO_DEPTH):0] tx_elements;
+	wire [$clog2(RX_FIFO_DEPTH):0] rx_elements;
+	uart_rx uart_rx_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.rx_i(rx_i),
+		.cfg_en_i(1'b1),
+		.cfg_div_i({regs_q[(DLM + 'd8) * 8+:8], regs_q[(DLL + 'd8) * 8+:8]}),
+		.cfg_parity_en_i(regs_q[(LCR * 8) + 3]),
+		.cfg_bits_i(regs_q[(LCR * 8) + 1-:2]),
+		.busy_o(),
+		.err_o(parity_error),
+		.err_clr_i(1'b1),
+		.rx_data_o(rx_data),
+		.rx_valid_o(rx_valid),
+		.rx_ready_i(rx_ready)
+	);
+	uart_tx uart_tx_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.tx_o(tx_o),
+		.busy_o(),
+		.cfg_en_i(1'b1),
+		.cfg_div_i({regs_q[(DLM + 'd8) * 8+:8], regs_q[(DLL + 'd8) * 8+:8]}),
+		.cfg_parity_en_i(regs_q[(LCR * 8) + 3]),
+		.cfg_bits_i(regs_q[(LCR * 8) + 1-:2]),
+		.cfg_stop_bits_i(regs_q[(LCR * 8) + 2]),
+		.tx_data_i(tx_data),
+		.tx_valid_i(tx_valid),
+		.tx_ready_o(tx_ready)
+	);
+	io_generic_fifo #(
+		.DATA_WIDTH(9),
+		.BUFFER_DEPTH(RX_FIFO_DEPTH)
+	) uart_rx_fifo_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.clr_i(rx_fifo_clr_q),
+		.elements_o(rx_elements),
+		.data_o(fifo_rx_data),
+		.valid_o(fifo_rx_valid),
+		.ready_i(fifo_rx_ready),
+		.valid_i(rx_valid),
+		.data_i({parity_error, rx_data}),
+		.ready_o(rx_ready)
+	);
+	io_generic_fifo #(
+		.DATA_WIDTH(8),
+		.BUFFER_DEPTH(TX_FIFO_DEPTH)
+	) uart_tx_fifo_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.clr_i(tx_fifo_clr_q),
+		.elements_o(tx_elements),
+		.data_o(tx_data),
+		.valid_o(tx_valid),
+		.ready_i(tx_ready),
+		.valid_i(fifo_tx_valid),
+		.data_i(fifo_tx_data),
+		.ready_o()
+	);
+	uart_interrupt #(
+		.TX_FIFO_DEPTH(TX_FIFO_DEPTH),
+		.RX_FIFO_DEPTH(RX_FIFO_DEPTH)
+	) uart_interrupt_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.IER_i(regs_q[(IER * 8) + 2-:3]),
+		.RDA_i(regs_n[(LSR * 8) + 5]),
+		.CTI_i(1'b0),
+		.error_i(regs_n[(LSR * 8) + 2]),
+		.rx_elements_i(rx_elements),
+		.tx_elements_i(tx_elements),
+		.trigger_level_i(trigger_level_q),
+		.clr_int_i(clr_int),
+		.interrupt_o(event_o),
+		.IIR_o(IIR_o)
+	);
+	always @(*) begin
+		regs_n = regs_q;
+		trigger_level_n = trigger_level_q;
+		fifo_tx_valid = 1'b0;
+		tx_fifo_clr_n = 1'b0;
+		rx_fifo_clr_n = 1'b0;
+		regs_n[LSR * 8] = fifo_rx_valid;
+		regs_n[(LSR * 8) + 2] = fifo_rx_data[8];
+		regs_n[(LSR * 8) + 5] = ~(|tx_elements);
+		regs_n[(LSR * 8) + 6] = tx_ready & ~(|tx_elements);
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				THR:
+					if (regs_q[(LCR * 8) + 7])
+						regs_n[(DLL + 'd8) * 8+:8] = PWDATA[7:0];
+					else begin
+						fifo_tx_data = PWDATA[7:0];
+						fifo_tx_valid = 1'b1;
+					end
+				IER:
+					if (regs_q[(LCR * 8) + 7])
+						regs_n[(DLM + 'd8) * 8+:8] = PWDATA[7:0];
+					else
+						regs_n[IER * 8+:8] = PWDATA[7:0];
+				LCR: regs_n[LCR * 8+:8] = PWDATA[7:0];
+				FCR: begin
+					rx_fifo_clr_n = PWDATA[1];
+					tx_fifo_clr_n = PWDATA[2];
+					trigger_level_n = PWDATA[7:6];
+				end
+				default:
+					;
+			endcase
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		apb_rx_ready = 1'b0;
+		fifo_rx_ready = 1'b0;
+		clr_int = 4'b0000;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				RBR:
+					if (regs_q[(LCR * 8) + 7])
+						PRDATA = {24'b000000000000000000000000, regs_q[(DLL + 'd8) * 8+:8]};
+					else begin
+						fifo_rx_ready = 1'b1;
+						PRDATA = {24'b000000000000000000000000, fifo_rx_data[7:0]};
+						clr_int = 4'b1000;
+					end
+				LSR: begin
+					PRDATA = {24'b000000000000000000000000, regs_q[LSR * 8+:8]};
+					clr_int = 4'b1100;
+				end
+				LCR: PRDATA = {24'b000000000000000000000000, regs_q[LCR * 8+:8]};
+				IER:
+					if (regs_q[(LCR * 8) + 7])
+						PRDATA = {24'b000000000000000000000000, regs_q[(DLM + 'd8) * 8+:8]};
+					else
+						PRDATA = {24'b000000000000000000000000, regs_q[IER * 8+:8]};
+				IIR: begin
+					PRDATA = {28'b0000000000000000000000001100, IIR_o};
+					clr_int = 4'b0100;
+				end
+				default:
+					;
+			endcase
+	end
+	always @(posedge CLK or negedge RSTN)
+		if (~RSTN) begin
+			regs_q[IER * 8+:8] <= 8'h00;
+			regs_q[IIR * 8+:8] <= 8'h01;
+			regs_q[LCR * 8+:8] <= 8'h00;
+			regs_q[MCR * 8+:8] <= 8'h00;
+			regs_q[LSR * 8+:8] <= 8'h60;
+			regs_q[MSR * 8+:8] <= 8'h00;
+			regs_q[SCR * 8+:8] <= 8'h00;
+			regs_q[(DLM + 'd8) * 8+:8] <= 8'h00;
+			regs_q[(DLL + 'd8) * 8+:8] <= 8'h00;
+			trigger_level_q <= 2'b00;
+			tx_fifo_clr_q <= 1'b0;
+			rx_fifo_clr_q <= 1'b0;
+		end
+		else begin
+			regs_q <= regs_n;
+			trigger_level_q <= trigger_level_n;
+			tx_fifo_clr_q <= tx_fifo_clr_n;
+			rx_fifo_clr_q <= rx_fifo_clr_n;
+		end
+	assign register_adr = {PADDR[2:0]};
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/apb_uart_sv.v b/verilog/rtl/ips/apb/apb_uart_sv/apb_uart_sv.v
new file mode 100644
index 0000000..9969081
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/apb_uart_sv.v
@@ -0,0 +1,241 @@
+module apb_uart_sv 
+#(
+    parameter APB_ADDR_WIDTH = 12  //APB slaves are 4KB by default
+)
+(
+	CLK,
+	RSTN,
+	PADDR,
+	PWDATA,
+	PWRITE,
+	PSEL,
+	PENABLE,
+	PRDATA,
+	PREADY,
+	PSLVERR,
+	rx_i,
+	tx_o,
+	event_o
+);
+	//parameter APB_ADDR_WIDTH = 12;
+	input wire CLK;
+	input wire RSTN;
+	input wire [APB_ADDR_WIDTH - 1:0] PADDR;
+	input wire [31:0] PWDATA;
+	input wire PWRITE;
+	input wire PSEL;
+	input wire PENABLE;
+	output reg [31:0] PRDATA;
+	output wire PREADY;
+	output wire PSLVERR;
+	input wire rx_i;
+	output wire tx_o;
+	output wire event_o;
+	parameter RBR = 3'h0;
+	parameter THR = 3'h0;
+	parameter DLL = 3'h0;
+	parameter IER = 3'h1;
+	parameter DLM = 3'h1;
+	parameter IIR = 3'h2;
+	parameter FCR = 3'h2;
+	parameter LCR = 3'h3;
+	parameter MCR = 3'h4;
+	parameter LSR = 3'h5;
+	parameter MSR = 3'h6;
+	parameter SCR = 3'h7;
+	parameter TX_FIFO_DEPTH = 16;
+	parameter RX_FIFO_DEPTH = 16;
+	wire [2:0] register_adr;
+	reg [79:0] regs_q;
+	reg [79:0] regs_n;
+	reg [1:0] trigger_level_n;
+	reg [1:0] trigger_level_q;
+	wire [7:0] rx_data;
+	wire parity_error;
+	wire [3:0] IIR_o;
+	reg [3:0] clr_int;
+	wire tx_ready;
+	reg apb_rx_ready;
+	wire rx_valid;
+	reg tx_fifo_clr_n;
+	reg tx_fifo_clr_q;
+	reg rx_fifo_clr_n;
+	reg rx_fifo_clr_q;
+	reg fifo_tx_valid;
+	wire tx_valid;
+	wire fifo_rx_valid;
+	reg fifo_rx_ready;
+	wire rx_ready;
+	reg [7:0] fifo_tx_data;
+	wire [8:0] fifo_rx_data;
+	wire [7:0] tx_data;
+	wire [$clog2(TX_FIFO_DEPTH):0] tx_elements;
+	wire [$clog2(RX_FIFO_DEPTH):0] rx_elements;
+	uart_rx uart_rx_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.rx_i(rx_i),
+		.cfg_en_i(1'b1),
+		.cfg_div_i({regs_q[(DLM + 'd8) * 8+:8], regs_q[(DLL + 'd8) * 8+:8]}),
+		.cfg_parity_en_i(regs_q[(LCR * 8) + 3]),
+		.cfg_bits_i(regs_q[(LCR * 8) + 1-:2]),
+		.busy_o(),
+		.err_o(parity_error),
+		.err_clr_i(1'b1),
+		.rx_data_o(rx_data),
+		.rx_valid_o(rx_valid),
+		.rx_ready_i(rx_ready)
+	);
+	uart_tx uart_tx_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.tx_o(tx_o),
+		.busy_o(),
+		.cfg_en_i(1'b1),
+		.cfg_div_i({regs_q[(DLM + 'd8) * 8+:8], regs_q[(DLL + 'd8) * 8+:8]}),
+		.cfg_parity_en_i(regs_q[(LCR * 8) + 3]),
+		.cfg_bits_i(regs_q[(LCR * 8) + 1-:2]),
+		.cfg_stop_bits_i(regs_q[(LCR * 8) + 2]),
+		.tx_data_i(tx_data),
+		.tx_valid_i(tx_valid),
+		.tx_ready_o(tx_ready)
+	);
+	io_generic_fifo #(
+		.DATA_WIDTH(9),
+		.BUFFER_DEPTH(RX_FIFO_DEPTH)
+	) uart_rx_fifo_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.clr_i(rx_fifo_clr_q),
+		.elements_o(rx_elements),
+		.data_o(fifo_rx_data),
+		.valid_o(fifo_rx_valid),
+		.ready_i(fifo_rx_ready),
+		.valid_i(rx_valid),
+		.data_i({parity_error, rx_data}),
+		.ready_o(rx_ready)
+	);
+	io_generic_fifo #(
+		.DATA_WIDTH(8),
+		.BUFFER_DEPTH(TX_FIFO_DEPTH)
+	) uart_tx_fifo_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.clr_i(tx_fifo_clr_q),
+		.elements_o(tx_elements),
+		.data_o(tx_data),
+		.valid_o(tx_valid),
+		.ready_i(tx_ready),
+		.valid_i(fifo_tx_valid),
+		.data_i(fifo_tx_data),
+		.ready_o()
+	);
+	uart_interrupt #(
+		.TX_FIFO_DEPTH(TX_FIFO_DEPTH),
+		.RX_FIFO_DEPTH(RX_FIFO_DEPTH)
+	) uart_interrupt_i(
+		.clk_i(CLK),
+		.rstn_i(RSTN),
+		.IER_i(regs_q[(IER * 8) + 2-:3]),
+		.RDA_i(regs_n[(LSR * 8) + 5]),
+		.CTI_i(1'b0),
+		.error_i(regs_n[(LSR * 8) + 2]),
+		.rx_elements_i(rx_elements),
+		.tx_elements_i(tx_elements),
+		.trigger_level_i(trigger_level_q),
+		.clr_int_i(clr_int),
+		.interrupt_o(event_o),
+		.IIR_o(IIR_o)
+	);
+	always @(*) begin
+		regs_n = regs_q;
+		trigger_level_n = trigger_level_q;
+		fifo_tx_valid = 1'b0;
+		tx_fifo_clr_n = 1'b0;
+		rx_fifo_clr_n = 1'b0;
+		regs_n[LSR * 8] = fifo_rx_valid;
+		regs_n[(LSR * 8) + 2] = fifo_rx_data[8];
+		regs_n[(LSR * 8) + 5] = ~(|tx_elements);
+		regs_n[(LSR * 8) + 6] = tx_ready & ~(|tx_elements);
+		if ((PSEL && PENABLE) && PWRITE)
+			case (register_adr)
+				THR:
+					if (regs_q[(LCR * 8) + 7])
+						regs_n[(DLL + 'd8) * 8+:8] = PWDATA[7:0];
+					else begin
+						fifo_tx_data = PWDATA[7:0];
+						fifo_tx_valid = 1'b1;
+					end
+				IER:
+					if (regs_q[(LCR * 8) + 7])
+						regs_n[(DLM + 'd8) * 8+:8] = PWDATA[7:0];
+					else
+						regs_n[IER * 8+:8] = PWDATA[7:0];
+				LCR: regs_n[LCR * 8+:8] = PWDATA[7:0];
+				FCR: begin
+					rx_fifo_clr_n = PWDATA[1];
+					tx_fifo_clr_n = PWDATA[2];
+					trigger_level_n = PWDATA[7:6];
+				end
+				default:
+					;
+			endcase
+	end
+	always @(*) begin
+		PRDATA = 'b0;
+		apb_rx_ready = 1'b0;
+		fifo_rx_ready = 1'b0;
+		clr_int = 4'b0000;
+		if ((PSEL && PENABLE) && !PWRITE)
+			case (register_adr)
+				RBR:
+					if (regs_q[(LCR * 8) + 7])
+						PRDATA = {24'b000000000000000000000000, regs_q[(DLL + 'd8) * 8+:8]};
+					else begin
+						fifo_rx_ready = 1'b1;
+						PRDATA = {24'b000000000000000000000000, fifo_rx_data[7:0]};
+						clr_int = 4'b1000;
+					end
+				LSR: begin
+					PRDATA = {24'b000000000000000000000000, regs_q[LSR * 8+:8]};
+					clr_int = 4'b1100;
+				end
+				LCR: PRDATA = {24'b000000000000000000000000, regs_q[LCR * 8+:8]};
+				IER:
+					if (regs_q[(LCR * 8) + 7])
+						PRDATA = {24'b000000000000000000000000, regs_q[(DLM + 'd8) * 8+:8]};
+					else
+						PRDATA = {24'b000000000000000000000000, regs_q[IER * 8+:8]};
+				IIR: begin
+					PRDATA = {28'b0000000000000000000000001100, IIR_o};
+					clr_int = 4'b0100;
+				end
+				default:
+					;
+			endcase
+	end
+	always @(posedge CLK or negedge RSTN)
+		if (~RSTN) begin
+			regs_q[IER * 8+:8] <= 8'h00;
+			regs_q[IIR * 8+:8] <= 8'h01;
+			regs_q[LCR * 8+:8] <= 8'h00;
+			regs_q[MCR * 8+:8] <= 8'h00;
+			regs_q[LSR * 8+:8] <= 8'h60;
+			regs_q[MSR * 8+:8] <= 8'h00;
+			regs_q[SCR * 8+:8] <= 8'h00;
+			regs_q[(DLM + 'd8) * 8+:8] <= 8'h00;
+			regs_q[(DLL + 'd8) * 8+:8] <= 8'h00;
+			trigger_level_q <= 2'b00;
+			tx_fifo_clr_q <= 1'b0;
+			rx_fifo_clr_q <= 1'b0;
+		end
+		else begin
+			regs_q <= regs_n;
+			trigger_level_q <= trigger_level_n;
+			tx_fifo_clr_q <= tx_fifo_clr_n;
+			rx_fifo_clr_q <= rx_fifo_clr_n;
+		end
+	assign register_adr = {PADDR[2:0]};
+	assign PREADY = 1'b1;
+	assign PSLVERR = 1'b0;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/io_generic_fifo.v b/verilog/rtl/ips/apb/apb_uart_sv/io_generic_fifo.v
new file mode 100644
index 0000000..f9c1329
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/io_generic_fifo.v
@@ -0,0 +1,83 @@
+module io_generic_fifo 
+#(
+    parameter DATA_WIDTH = 32,
+    parameter BUFFER_DEPTH = 2,
+    parameter LOG_BUFFER_DEPTH = $clog2(BUFFER_DEPTH)
+)
+(
+	clk_i,
+	rstn_i,
+	clr_i,
+	elements_o,
+	data_o,
+	valid_o,
+	ready_i,
+	valid_i,
+	data_i,
+	ready_o
+);
+	//parameter DATA_WIDTH = 32;
+	//parameter BUFFER_DEPTH = 2;
+	//parameter LOG_BUFFER_DEPTH = $clog2(BUFFER_DEPTH);
+	input wire clk_i;
+	input wire rstn_i;
+	input wire clr_i;
+	output wire [LOG_BUFFER_DEPTH:0] elements_o;
+	output wire [DATA_WIDTH - 1:0] data_o;
+	output wire valid_o;
+	input wire ready_i;
+	input wire valid_i;
+	input wire [DATA_WIDTH - 1:0] data_i;
+	output wire ready_o;
+	reg [LOG_BUFFER_DEPTH - 1:0] pointer_in;
+	reg [LOG_BUFFER_DEPTH - 1:0] pointer_out;
+	reg [LOG_BUFFER_DEPTH:0] elements;
+	reg [DATA_WIDTH - 1:0] buffer [BUFFER_DEPTH - 1:0];
+	wire full;
+	reg [31:0] loop1;
+	assign full = elements == BUFFER_DEPTH;
+	assign elements_o = elements;
+	always @(posedge clk_i or negedge rstn_i) begin : elements_sequential
+		if (rstn_i == 1'b0)
+			elements <= 0;
+		else if (clr_i)
+			elements <= 0;
+		else if ((ready_i && valid_o) && (!valid_i || full))
+			elements <= elements - 1;
+		else if (((!valid_o || !ready_i) && valid_i) && !full)
+			elements <= elements + 1;
+	end
+	always @(posedge clk_i or negedge rstn_i) begin : buffers_sequential
+		if (rstn_i == 1'b0) begin
+			for (loop1 = 0; loop1 < BUFFER_DEPTH; loop1 = loop1 + 1)
+				buffer[loop1] <= 0;
+		end
+		else if (valid_i && !full)
+			buffer[pointer_in] <= data_i;
+	end
+	always @(posedge clk_i or negedge rstn_i) begin : sequential
+		if (rstn_i == 1'b0) begin
+			pointer_out <= 0;
+			pointer_in <= 0;
+		end
+		else if (clr_i) begin
+			pointer_out <= 0;
+			pointer_in <= 0;
+		end
+		else begin
+			if (valid_i && !full)
+				if (pointer_in == $unsigned(BUFFER_DEPTH - 1))
+					pointer_in <= 0;
+				else
+					pointer_in <= pointer_in + 1;
+			if (ready_i && valid_o)
+				if (pointer_out == $unsigned(BUFFER_DEPTH - 1))
+					pointer_out <= 0;
+				else
+					pointer_out <= pointer_out + 1;
+		end
+	end
+	assign data_o = buffer[pointer_out];
+	assign valid_o = elements != 0;
+	assign ready_o = ~full;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/uart_interrupt.v b/verilog/rtl/ips/apb/apb_uart_sv/uart_interrupt.v
new file mode 100644
index 0000000..3a89ff6
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/uart_interrupt.v
@@ -0,0 +1,77 @@
+module uart_interrupt 
+#(
+    parameter TX_FIFO_DEPTH = 32,
+    parameter RX_FIFO_DEPTH = 32
+)
+(
+	clk_i,
+	rstn_i,
+	IER_i,
+	RDA_i,
+	CTI_i,
+	error_i,
+	rx_elements_i,
+	tx_elements_i,
+	trigger_level_i,
+	clr_int_i,
+	interrupt_o,
+	IIR_o
+);
+	//parameter TX_FIFO_DEPTH = 32;
+	//parameter RX_FIFO_DEPTH = 32;
+	input wire clk_i;
+	input wire rstn_i;
+	input wire [2:0] IER_i;
+	input wire RDA_i;
+	input wire CTI_i;
+	input wire error_i;
+	input wire [$clog2(RX_FIFO_DEPTH):0] rx_elements_i;
+	input wire [$clog2(TX_FIFO_DEPTH):0] tx_elements_i;
+	input wire [1:0] trigger_level_i;
+	input wire [3:0] clr_int_i;
+	output wire interrupt_o;
+	output wire [3:0] IIR_o;
+	reg [3:0] iir_n;
+	reg [3:0] iir_q;
+	reg trigger_level_reached;
+	always @(*) begin
+		trigger_level_reached = 1'b0;
+		case (trigger_level_i)
+			2'b00:
+				if ($unsigned(rx_elements_i) == 1)
+					trigger_level_reached = 1'b1;
+			2'b01:
+				if ($unsigned(rx_elements_i) == 4)
+					trigger_level_reached = 1'b1;
+			2'b10:
+				if ($unsigned(rx_elements_i) == 8)
+					trigger_level_reached = 1'b1;
+			2'b11:
+				if ($unsigned(rx_elements_i) == 14)
+					trigger_level_reached = 1'b1;
+			default:
+				;
+		endcase
+	end
+	always @(*) begin
+		if (clr_int_i == 4'b0000)
+			iir_n = iir_q;
+		else
+			iir_n = iir_q & ~clr_int_i;
+		if (IER_i[2] & error_i)
+			iir_n = 4'b1100;
+		else if (IER_i[0] & (trigger_level_reached | RDA_i))
+			iir_n = 4'b1000;
+		else if (IER_i[0] & CTI_i)
+			iir_n = 4'b1000;
+		else if (IER_i[1] & (tx_elements_i == 0))
+			iir_n = 4'b0100;
+	end
+	always @(posedge clk_i or negedge rstn_i)
+		if (~rstn_i)
+			iir_q <= 4'b0001;
+		else
+			iir_q <= iir_n;
+	assign IIR_o = iir_q;
+	assign interrupt_o = ~iir_q[0];
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/uart_rx.v b/verilog/rtl/ips/apb/apb_uart_sv/uart_rx.v
new file mode 100644
index 0000000..c25ded6
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/uart_rx.v
@@ -0,0 +1,179 @@
+module uart_rx (
+	clk_i,
+	rstn_i,
+	rx_i,
+	cfg_div_i,
+	cfg_en_i,
+	cfg_parity_en_i,
+	cfg_bits_i,
+	busy_o,
+	err_o,
+	err_clr_i,
+	rx_data_o,
+	rx_valid_o,
+	rx_ready_i
+);
+	input wire clk_i;
+	input wire rstn_i;
+	input wire rx_i;
+	input wire [15:0] cfg_div_i;
+	input wire cfg_en_i;
+	input wire cfg_parity_en_i;
+	input wire [1:0] cfg_bits_i;
+	output wire busy_o;
+	output reg err_o;
+	input wire err_clr_i;
+	output wire [7:0] rx_data_o;
+	output reg rx_valid_o;
+	input wire rx_ready_i;
+	reg [2:0] CS;
+	reg [2:0] NS;
+	reg [7:0] reg_data;
+	reg [7:0] reg_data_next;
+	reg [2:0] reg_rx_sync;
+	reg [2:0] reg_bit_count;
+	reg [2:0] reg_bit_count_next;
+	reg [2:0] s_target_bits;
+	reg parity_bit;
+	reg parity_bit_next;
+	reg sampleData;
+	reg [15:0] baud_cnt;
+	reg baudgen_en;
+	reg bit_done;
+	reg start_bit;
+	reg set_error;
+	wire s_rx_fall;
+	assign busy_o = CS != 3'd0;
+	always @(*)
+		case (cfg_bits_i)
+			2'b00: s_target_bits = 3'h4;
+			2'b01: s_target_bits = 3'h5;
+			2'b10: s_target_bits = 3'h6;
+			2'b11: s_target_bits = 3'h7;
+		endcase
+	always @(*) begin
+		NS = CS;
+		sampleData = 1'b0;
+		reg_bit_count_next = reg_bit_count;
+		reg_data_next = reg_data;
+		rx_valid_o = 1'b0;
+		baudgen_en = 1'b0;
+		start_bit = 1'b0;
+		parity_bit_next = parity_bit;
+		set_error = 1'b0;
+		case (CS)
+			3'd0:
+				if (s_rx_fall) begin
+					NS = 3'd1;
+					baudgen_en = 1'b1;
+					start_bit = 1'b1;
+				end
+			3'd1: begin
+				parity_bit_next = 1'b0;
+				baudgen_en = 1'b1;
+				start_bit = 1'b1;
+				if (bit_done)
+					NS = 3'd2;
+			end
+			3'd2: begin
+				baudgen_en = 1'b1;
+				parity_bit_next = parity_bit ^ reg_rx_sync[2];
+				case (cfg_bits_i)
+					2'b00: reg_data_next = {3'b000, reg_rx_sync[2], reg_data[4:1]};
+					2'b01: reg_data_next = {2'b00, reg_rx_sync[2], reg_data[5:1]};
+					2'b10: reg_data_next = {1'b0, reg_rx_sync[2], reg_data[6:1]};
+					2'b11: reg_data_next = {reg_rx_sync[2], reg_data[7:1]};
+				endcase
+				if (bit_done) begin
+					sampleData = 1'b1;
+					if (reg_bit_count == s_target_bits) begin
+						reg_bit_count_next = 'h0;
+						NS = 3'd3;
+					end
+					else
+						reg_bit_count_next = reg_bit_count + 1;
+				end
+			end
+			3'd3: begin
+				baudgen_en = 1'b1;
+				rx_valid_o = 1'b1;
+				if (rx_ready_i)
+					if (cfg_parity_en_i)
+						NS = 3'd4;
+					else
+						NS = 3'd5;
+			end
+			3'd4: begin
+				baudgen_en = 1'b1;
+				if (bit_done) begin
+					if (parity_bit != reg_rx_sync[2])
+						set_error = 1'b1;
+					NS = 3'd5;
+				end
+			end
+			3'd5: begin
+				baudgen_en = 1'b1;
+				if (bit_done)
+					NS = 3'd0;
+			end
+			default: NS = 3'd0;
+		endcase
+	end
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0) begin
+			CS <= 3'd0;
+			reg_data <= 8'hff;
+			reg_bit_count <= 'h0;
+			parity_bit <= 1'b0;
+		end
+		else begin
+			if (bit_done)
+				parity_bit <= parity_bit_next;
+			if (sampleData)
+				reg_data <= reg_data_next;
+			reg_bit_count <= reg_bit_count_next;
+			if (cfg_en_i)
+				CS <= NS;
+			else
+				CS <= 3'd0;
+		end
+	assign s_rx_fall = ~reg_rx_sync[1] & reg_rx_sync[2];
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0)
+			reg_rx_sync <= 3'b111;
+		else if (cfg_en_i)
+			reg_rx_sync <= {reg_rx_sync[1:0], rx_i};
+		else
+			reg_rx_sync <= 3'b111;
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0) begin
+			baud_cnt <= 'h0;
+			bit_done <= 1'b0;
+		end
+		else if (baudgen_en) begin
+			if (!start_bit && (baud_cnt == cfg_div_i)) begin
+				baud_cnt <= 'h0;
+				bit_done <= 1'b1;
+			end
+			else if (start_bit && (baud_cnt == {1'b0, cfg_div_i[15:1]})) begin
+				baud_cnt <= 'h0;
+				bit_done <= 1'b1;
+			end
+			else begin
+				baud_cnt <= baud_cnt + 1;
+				bit_done <= 1'b0;
+			end
+		end
+		else begin
+			baud_cnt <= 'h0;
+			bit_done <= 1'b0;
+		end
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0)
+			err_o <= 1'b0;
+		else if (err_clr_i)
+			err_o <= 1'b0;
+		else if (set_error)
+			err_o <= 1'b1;
+	assign rx_data_o = reg_data;
+endmodule
diff --git a/verilog/rtl/ips/apb/apb_uart_sv/uart_tx.v b/verilog/rtl/ips/apb/apb_uart_sv/uart_tx.v
new file mode 100644
index 0000000..e84068a
--- /dev/null
+++ b/verilog/rtl/ips/apb/apb_uart_sv/uart_tx.v
@@ -0,0 +1,152 @@
+module uart_tx (
+	clk_i,
+	rstn_i,
+	tx_o,
+	busy_o,
+	cfg_en_i,
+	cfg_div_i,
+	cfg_parity_en_i,
+	cfg_bits_i,
+	cfg_stop_bits_i,
+	tx_data_i,
+	tx_valid_i,
+	tx_ready_o
+);
+	input wire clk_i;
+	input wire rstn_i;
+	output reg tx_o;
+	output wire busy_o;
+	input wire cfg_en_i;
+	input wire [15:0] cfg_div_i;
+	input wire cfg_parity_en_i;
+	input wire [1:0] cfg_bits_i;
+	input wire cfg_stop_bits_i;
+	input wire [7:0] tx_data_i;
+	input wire tx_valid_i;
+	output reg tx_ready_o;
+	reg [2:0] CS;
+	reg [2:0] NS;
+	reg [7:0] reg_data;
+	reg [7:0] reg_data_next;
+	reg [2:0] reg_bit_count;
+	reg [2:0] reg_bit_count_next;
+	reg [2:0] s_target_bits;
+	reg parity_bit;
+	reg parity_bit_next;
+	reg sampleData;
+	reg [15:0] baud_cnt;
+	reg baudgen_en;
+	reg bit_done;
+	assign busy_o = CS != 3'd0;
+	always @(*)
+		case (cfg_bits_i)
+			2'b00: s_target_bits = 3'h4;
+			2'b01: s_target_bits = 3'h5;
+			2'b10: s_target_bits = 3'h6;
+			2'b11: s_target_bits = 3'h7;
+		endcase
+	always @(*) begin
+		NS = CS;
+		tx_o = 1'b1;
+		sampleData = 1'b0;
+		reg_bit_count_next = reg_bit_count;
+		reg_data_next = {1'b1, reg_data[7:1]};
+		tx_ready_o = 1'b0;
+		baudgen_en = 1'b0;
+		parity_bit_next = parity_bit;
+		case (CS)
+			3'd0: begin
+				if (cfg_en_i)
+					tx_ready_o = 1'b1;
+				if (tx_valid_i) begin
+					NS = 3'd1;
+					sampleData = 1'b1;
+					reg_data_next = tx_data_i;
+				end
+			end
+			3'd1: begin
+				tx_o = 1'b0;
+				parity_bit_next = 1'b0;
+				baudgen_en = 1'b1;
+				if (bit_done)
+					NS = 3'd2;
+			end
+			3'd2: begin
+				tx_o = reg_data[0];
+				baudgen_en = 1'b1;
+				parity_bit_next = parity_bit ^ reg_data[0];
+				if (bit_done)
+					if (reg_bit_count == s_target_bits) begin
+						reg_bit_count_next = 'h0;
+						if (cfg_parity_en_i)
+							NS = 3'd3;
+						else
+							NS = 3'd4;
+					end
+					else begin
+						reg_bit_count_next = reg_bit_count + 1;
+						sampleData = 1'b1;
+					end
+			end
+			3'd3: begin
+				tx_o = parity_bit;
+				baudgen_en = 1'b1;
+				if (bit_done)
+					NS = 3'd4;
+			end
+			3'd4: begin
+				tx_o = 1'b1;
+				baudgen_en = 1'b1;
+				if (bit_done)
+					if (cfg_stop_bits_i)
+						NS = 3'd5;
+					else
+						NS = 3'd0;
+			end
+			3'd5: begin
+				tx_o = 1'b1;
+				baudgen_en = 1'b1;
+				if (bit_done)
+					NS = 3'd0;
+			end
+			default: NS = 3'd0;
+		endcase
+	end
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0) begin
+			CS <= 3'd0;
+			reg_data <= 8'hff;
+			reg_bit_count <= 'h0;
+			parity_bit <= 1'b0;
+		end
+		else begin
+			if (bit_done)
+				parity_bit <= parity_bit_next;
+			if (sampleData)
+				reg_data <= reg_data_next;
+			reg_bit_count <= reg_bit_count_next;
+			if (cfg_en_i)
+				CS <= NS;
+			else
+				CS <= 3'd0;
+		end
+	always @(posedge clk_i or negedge rstn_i)
+		if (rstn_i == 1'b0) begin
+			baud_cnt <= 'h0;
+			bit_done <= 1'b0;
+		end
+		else if (baudgen_en) begin
+			if (baud_cnt == cfg_div_i) begin
+				baud_cnt <= 'h0;
+				bit_done <= 1'b1;
+			end
+			else begin
+				baud_cnt <= baud_cnt + 1;
+				bit_done <= 1'b0;
+			end
+		end
+		else begin
+			baud_cnt <= 'h0;
+			bit_done <= 1'b0;
+		end
+endmodule