// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
//  https://github.com/SystemRDL/PeakRDL-regblock

module atxmega_spi (
        input wire clk,
        input wire rst,

        input wire s_apb_psel,
        input wire s_apb_penable,
        input wire s_apb_pwrite,
        input wire [2:0] s_apb_paddr,
        input wire [7:0] s_apb_pwdata,
        output logic s_apb_pready,
        output logic [7:0] s_apb_prdata,
        output logic s_apb_pslverr,

        input atxmega_spi_pkg::atxmega_spi__in_t hwif_in,
        output atxmega_spi_pkg::atxmega_spi__out_t hwif_out
    );

    //--------------------------------------------------------------------------
    // CPU Bus interface logic
    //--------------------------------------------------------------------------
    logic cpuif_req;
    logic cpuif_req_is_wr;
    logic [2:0] cpuif_addr;
    logic [7:0] cpuif_wr_data;
    logic cpuif_req_stall_wr;
    logic cpuif_req_stall_rd;

    logic cpuif_rd_ack;
    logic cpuif_rd_err;
    logic [7:0] cpuif_rd_data;

    logic cpuif_wr_ack;
    logic cpuif_wr_err;

    // Request
    logic is_active;
    always_ff @(posedge clk) begin
        if(rst) begin
            is_active <= '0;
            cpuif_req <= '0;
            cpuif_req_is_wr <= '0;
            cpuif_addr <= '0;
            cpuif_wr_data <= '0;
        end else begin
            if(~is_active) begin
                if(s_apb_psel) begin
                    is_active <= '1;
                    cpuif_req <= '1;
                    cpuif_req_is_wr <= s_apb_pwrite;
                    cpuif_addr <= s_apb_paddr[2:0];
                    cpuif_wr_data <= s_apb_pwdata;
                end
            end else begin
                cpuif_req <= '0;
                if(cpuif_rd_ack || cpuif_wr_ack) begin
                    is_active <= '0;
                end
            end
        end
    end

    // Response
    assign s_apb_pready = cpuif_rd_ack | cpuif_wr_ack;
    assign s_apb_prdata = cpuif_rd_data;
    assign s_apb_pslverr = cpuif_rd_err | cpuif_wr_err;

    logic cpuif_req_masked;

    // Read & write latencies are balanced. Stalls not required
    assign cpuif_req_stall_rd = '0;
    assign cpuif_req_stall_wr = '0;
    assign cpuif_req_masked = cpuif_req;

    //--------------------------------------------------------------------------
    // Address Decode
    //--------------------------------------------------------------------------
    typedef struct {
        logic CTRL;
        logic INTCTRL;
        logic STATUS;
        logic DATA;
    } decoded_reg_strb_t;
    decoded_reg_strb_t decoded_reg_strb;
    logic decoded_req;
    logic decoded_req_is_wr;
    logic [7:0] decoded_wr_data;

    always_comb begin
        decoded_reg_strb.CTRL = cpuif_req_masked & (cpuif_addr == 'h0);
        decoded_reg_strb.INTCTRL = cpuif_req_masked & (cpuif_addr == 'h1);
        decoded_reg_strb.STATUS = cpuif_req_masked & (cpuif_addr == 'h2);
        decoded_reg_strb.DATA = cpuif_req_masked & (cpuif_addr == 'h3);
    end

    // Pass down signals to next stage
    assign decoded_req = cpuif_req_masked;
    assign decoded_req_is_wr = cpuif_req_is_wr;
    assign decoded_wr_data = cpuif_wr_data;

    // Writes are always granted with no error response
    assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
    assign cpuif_wr_err = '0;

    //--------------------------------------------------------------------------
    // Field logic
    //--------------------------------------------------------------------------
    typedef struct {
        struct {
            struct {
                logic [1:0] next;
                logic load_next;
            } PRESCALER;
            struct {
                logic [1:0] next;
                logic load_next;
            } MODE;
            struct {
                logic next;
                logic load_next;
            } MASTER;
            struct {
                logic next;
                logic load_next;
            } DORD;
            struct {
                logic next;
                logic load_next;
            } ENABLE;
            struct {
                logic next;
                logic load_next;
            } CLK2X;
        } CTRL;
        struct {
            struct {
                logic [1:0] next;
                logic load_next;
            } INTLVL;
        } INTCTRL;
        struct {
            struct {
                logic next;
                logic load_next;
            } WRCOL;
            struct {
                logic next;
                logic load_next;
            } IF;
        } STATUS;
        struct {
            struct {
                logic [7:0] next;
                logic load_next;
            } WDATA;
        } DATA;
    } field_combo_t;
    field_combo_t field_combo;

    typedef struct {
        struct {
            struct {
                logic [1:0] value;
            } PRESCALER;
            struct {
                logic [1:0] value;
            } MODE;
            struct {
                logic value;
            } MASTER;
            struct {
                logic value;
            } DORD;
            struct {
                logic value;
            } ENABLE;
            struct {
                logic value;
            } CLK2X;
        } CTRL;
        struct {
            struct {
                logic [1:0] value;
            } INTLVL;
        } INTCTRL;
        struct {
            struct {
                logic value;
            } WRCOL;
            struct {
                logic value;
            } IF;
        } STATUS;
        struct {
            struct {
                logic [7:0] value;
            } WDATA;
        } DATA;
    } field_storage_t;
    field_storage_t field_storage;

    // Field: atxmega_spi.CTRL.PRESCALER
    always_comb begin
        automatic logic [1:0] next_c = field_storage.CTRL.PRESCALER.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[1:0];
            load_next_c = '1;
        end
        field_combo.CTRL.PRESCALER.next = next_c;
        field_combo.CTRL.PRESCALER.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.PRESCALER.value <= 'h0;
        end else if(field_combo.CTRL.PRESCALER.load_next) begin
            field_storage.CTRL.PRESCALER.value <= field_combo.CTRL.PRESCALER.next;
        end
    end
    assign hwif_out.CTRL.PRESCALER.value = field_storage.CTRL.PRESCALER.value;
    // Field: atxmega_spi.CTRL.MODE
    always_comb begin
        automatic logic [1:0] next_c = field_storage.CTRL.MODE.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[3:2];
            load_next_c = '1;
        end
        field_combo.CTRL.MODE.next = next_c;
        field_combo.CTRL.MODE.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.MODE.value <= 'h0;
        end else if(field_combo.CTRL.MODE.load_next) begin
            field_storage.CTRL.MODE.value <= field_combo.CTRL.MODE.next;
        end
    end
    assign hwif_out.CTRL.MODE.value = field_storage.CTRL.MODE.value;
    // Field: atxmega_spi.CTRL.MASTER
    always_comb begin
        automatic logic [0:0] next_c = field_storage.CTRL.MASTER.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[4:4];
            load_next_c = '1;
        end else if(hwif_in.CTRL.MASTER.we) begin // HW Write - we
            next_c = hwif_in.CTRL.MASTER.next;
            load_next_c = '1;
        end
        field_combo.CTRL.MASTER.next = next_c;
        field_combo.CTRL.MASTER.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.MASTER.value <= 'h0;
        end else if(field_combo.CTRL.MASTER.load_next) begin
            field_storage.CTRL.MASTER.value <= field_combo.CTRL.MASTER.next;
        end
    end
    assign hwif_out.CTRL.MASTER.value = field_storage.CTRL.MASTER.value;
    // Field: atxmega_spi.CTRL.DORD
    always_comb begin
        automatic logic [0:0] next_c = field_storage.CTRL.DORD.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[5:5];
            load_next_c = '1;
        end
        field_combo.CTRL.DORD.next = next_c;
        field_combo.CTRL.DORD.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.DORD.value <= 'h0;
        end else if(field_combo.CTRL.DORD.load_next) begin
            field_storage.CTRL.DORD.value <= field_combo.CTRL.DORD.next;
        end
    end
    assign hwif_out.CTRL.DORD.value = field_storage.CTRL.DORD.value;
    // Field: atxmega_spi.CTRL.ENABLE
    always_comb begin
        automatic logic [0:0] next_c = field_storage.CTRL.ENABLE.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[6:6];
            load_next_c = '1;
        end
        field_combo.CTRL.ENABLE.next = next_c;
        field_combo.CTRL.ENABLE.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.ENABLE.value <= 'h0;
        end else if(field_combo.CTRL.ENABLE.load_next) begin
            field_storage.CTRL.ENABLE.value <= field_combo.CTRL.ENABLE.next;
        end
    end
    assign hwif_out.CTRL.ENABLE.value = field_storage.CTRL.ENABLE.value;
    // Field: atxmega_spi.CTRL.CLK2X
    always_comb begin
        automatic logic [0:0] next_c = field_storage.CTRL.CLK2X.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.CTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[7:7];
            load_next_c = '1;
        end
        field_combo.CTRL.CLK2X.next = next_c;
        field_combo.CTRL.CLK2X.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.CTRL.CLK2X.value <= 'h0;
        end else if(field_combo.CTRL.CLK2X.load_next) begin
            field_storage.CTRL.CLK2X.value <= field_combo.CTRL.CLK2X.next;
        end
    end
    assign hwif_out.CTRL.CLK2X.value = field_storage.CTRL.CLK2X.value;
    // Field: atxmega_spi.INTCTRL.INTLVL
    always_comb begin
        automatic logic [1:0] next_c = field_storage.INTCTRL.INTLVL.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.INTCTRL && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[1:0];
            load_next_c = '1;
        end
        field_combo.INTCTRL.INTLVL.next = next_c;
        field_combo.INTCTRL.INTLVL.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.INTCTRL.INTLVL.value <= 'h0;
        end else if(field_combo.INTCTRL.INTLVL.load_next) begin
            field_storage.INTCTRL.INTLVL.value <= field_combo.INTCTRL.INTLVL.next;
        end
    end
    assign hwif_out.INTCTRL.INTLVL.value = field_storage.INTCTRL.INTLVL.value;
    // Field: atxmega_spi.STATUS.WRCOL
    always_comb begin
        automatic logic [0:0] next_c = field_storage.STATUS.WRCOL.value;
        automatic logic load_next_c = '0;
        if(hwif_in.STATUS.WRCOL.we) begin // HW Write - we
            next_c = hwif_in.STATUS.WRCOL.next;
            load_next_c = '1;
        end
        field_combo.STATUS.WRCOL.next = next_c;
        field_combo.STATUS.WRCOL.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.STATUS.WRCOL.value <= 'h0;
        end else if(field_combo.STATUS.WRCOL.load_next) begin
            field_storage.STATUS.WRCOL.value <= field_combo.STATUS.WRCOL.next;
        end
    end
    assign hwif_out.STATUS.WRCOL.value = field_storage.STATUS.WRCOL.value;
    // Field: atxmega_spi.STATUS.IF
    always_comb begin
        automatic logic [0:0] next_c = field_storage.STATUS.IF.value;
        automatic logic load_next_c = '0;
        if(hwif_in.STATUS.IF.we) begin // HW Write - we
            next_c = hwif_in.STATUS.IF.next;
            load_next_c = '1;
        end
        field_combo.STATUS.IF.next = next_c;
        field_combo.STATUS.IF.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.STATUS.IF.value <= 'h0;
        end else if(field_combo.STATUS.IF.load_next) begin
            field_storage.STATUS.IF.value <= field_combo.STATUS.IF.next;
        end
    end
    assign hwif_out.STATUS.IF.value = field_storage.STATUS.IF.value;
    // Field: atxmega_spi.DATA.WDATA
    always_comb begin
        automatic logic [7:0] next_c = field_storage.DATA.WDATA.value;
        automatic logic load_next_c = '0;
        if(decoded_reg_strb.DATA && decoded_req_is_wr) begin // SW write
            next_c = decoded_wr_data[7:0];
            load_next_c = '1;
        end
        field_combo.DATA.WDATA.next = next_c;
        field_combo.DATA.WDATA.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(field_combo.DATA.WDATA.load_next) begin
            field_storage.DATA.WDATA.value <= field_combo.DATA.WDATA.next;
        end
    end
    assign hwif_out.DATA.WDATA.value = field_storage.DATA.WDATA.value;

    //--------------------------------------------------------------------------
    // Readback
    //--------------------------------------------------------------------------
    logic readback_err;
    logic readback_done;
    logic [7:0] readback_data;
    
    // Assign readback values to a flattened array
    logic [7:0] readback_array[4];
    assign readback_array[0][1:0] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.PRESCALER.value : '0;
    assign readback_array[0][3:2] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.MODE.value : '0;
    assign readback_array[0][4:4] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.MASTER.value : '0;
    assign readback_array[0][5:5] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.DORD.value : '0;
    assign readback_array[0][6:6] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.ENABLE.value : '0;
    assign readback_array[0][7:7] = (decoded_reg_strb.CTRL && !decoded_req_is_wr) ? field_storage.CTRL.CLK2X.value : '0;
    assign readback_array[1][1:0] = (decoded_reg_strb.INTCTRL && !decoded_req_is_wr) ? field_storage.INTCTRL.INTLVL.value : '0;
    assign readback_array[1][7:2] = '0;
    assign readback_array[2][5:0] = '0;
    assign readback_array[2][6:6] = (decoded_reg_strb.STATUS && !decoded_req_is_wr) ? field_storage.STATUS.WRCOL.value : '0;
    assign readback_array[2][7:7] = (decoded_reg_strb.STATUS && !decoded_req_is_wr) ? field_storage.STATUS.IF.value : '0;
    assign readback_array[3][7:0] = (decoded_reg_strb.DATA && !decoded_req_is_wr) ? hwif_in.DATA.RDATA.next : '0;


    // Reduce the array
    always_comb begin
        automatic logic [7:0] readback_data_var;
        readback_done = decoded_req & ~decoded_req_is_wr;
        readback_err = '0;
        readback_data_var = '0;
        for(int i=0; i<4; i++) readback_data_var |= readback_array[i];
        readback_data = readback_data_var;
    end


    assign cpuif_rd_ack = readback_done;
    assign cpuif_rd_data = readback_data;
    assign cpuif_rd_err = readback_err;


endmodule