module wb_reg #
(
    parameter DATA_WIDTH = 32,                  // width of data bus in bits (8, 16, 32, or 64)
    parameter ADDR_WIDTH = 32,                  // width of address bus in bits
    parameter SELECT_WIDTH = (DATA_WIDTH/8)     // width of word select bus (1, 2, 4, or 8)
)
(
    input  wire                    clk,
    input  wire                    rst,

    // master side
    input  wire [ADDR_WIDTH-1:0]   wbm_adr_i,   // ADR_I() address
    input  wire [DATA_WIDTH-1:0]   wbm_dat_i,   // DAT_I() data in
    output wire [DATA_WIDTH-1:0]   wbm_dat_o,   // DAT_O() data out
    input  wire                    wbm_we_i,    // WE_I write enable input
    input  wire [SELECT_WIDTH-1:0] wbm_sel_i,   // SEL_I() select input
    input  wire                    wbm_stb_i,   // STB_I strobe input
    output wire                    wbm_ack_o,   // ACK_O acknowledge output
    output wire                    wbm_err_o,   // ERR_O error output
    output wire                    wbm_rty_o,   // RTY_O retry output
    input  wire                    wbm_cyc_i,   // CYC_I cycle input

    // slave side
    output wire [ADDR_WIDTH-1:0]   wbs_adr_o,   // ADR_O() address
    input  wire [DATA_WIDTH-1:0]   wbs_dat_i,   // DAT_I() data in
    output wire [DATA_WIDTH-1:0]   wbs_dat_o,   // DAT_O() data out
    output wire                    wbs_we_o,    // WE_O write enable output
    output wire [SELECT_WIDTH-1:0] wbs_sel_o,   // SEL_O() select output
    output wire                    wbs_stb_o,   // STB_O strobe output
    input  wire                    wbs_ack_i,   // ACK_I acknowledge input
    input  wire                    wbs_err_i,   // ERR_I error input
    input  wire                    wbs_rty_i,   // RTY_I retry input
    output wire                    wbs_cyc_o    // CYC_O cycle output
);

reg [DATA_WIDTH-1:0] wbm_dat_o_reg = 0;
reg wbm_ack_o_reg = 0;
reg wbm_err_o_reg = 0;
reg wbm_rty_o_reg = 0;

reg [ADDR_WIDTH-1:0] wbs_adr_o_reg = 0;
reg [DATA_WIDTH-1:0] wbs_dat_o_reg = 0;
reg wbs_we_o_reg = 0;
reg [SELECT_WIDTH-1:0] wbs_sel_o_reg = 0;
reg wbs_stb_o_reg = 0;
reg wbs_cyc_o_reg = 0;

assign wbm_dat_o = wbm_dat_o_reg;
assign wbm_ack_o = wbm_ack_o_reg;
assign wbm_err_o = wbm_err_o_reg;
assign wbm_rty_o = wbm_rty_o_reg;

assign wbs_adr_o = wbs_adr_o_reg;
assign wbs_dat_o = wbs_dat_o_reg;
assign wbs_we_o = wbs_we_o_reg;
assign wbs_sel_o = wbs_sel_o_reg;
assign wbs_stb_o = wbs_stb_o_reg;
assign wbs_cyc_o = wbs_cyc_o_reg;

always @(posedge clk) begin
    if (rst) begin
        wbm_dat_o_reg <= 0;
        wbm_ack_o_reg <= 0;
        wbm_err_o_reg <= 0;
        wbm_rty_o_reg <= 0;
        wbs_adr_o_reg <= 0;
        wbs_dat_o_reg <= 0;
        wbs_we_o_reg <= 0;
        wbs_sel_o_reg <= 0;
        wbs_stb_o_reg <= 0;
        wbs_cyc_o_reg <= 0;
    end else begin
        if (wbs_cyc_o_reg & wbs_stb_o_reg) begin
            // cycle - hold values
            if (wbs_ack_i | wbs_err_i | wbs_rty_i) begin
                // end of cycle - pass through slave to master
                wbm_dat_o_reg <= wbs_dat_i;
                wbm_ack_o_reg <= wbs_ack_i;
                wbm_err_o_reg <= wbs_err_i;
                wbm_rty_o_reg <= wbs_rty_i;
                wbs_we_o_reg <= 0;
                wbs_stb_o_reg <= 0;
            end
        end else begin
            // idle - pass through master to slave
            wbm_dat_o_reg <= 0;
            wbm_ack_o_reg <= 0;
            wbm_err_o_reg <= 0;
            wbm_rty_o_reg <= 0;
            wbs_adr_o_reg <= wbm_adr_i;
            wbs_dat_o_reg <= wbm_dat_i;
            wbs_we_o_reg <= wbm_we_i & ~(wbm_ack_o | wbm_err_o | wbm_rty_o);
            wbs_sel_o_reg <= wbm_sel_i;
            wbs_stb_o_reg <= wbm_stb_i & ~(wbm_ack_o | wbm_err_o | wbm_rty_o);
            wbs_cyc_o_reg <= wbm_cyc_i;
        end
    end
end

endmodule