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

module verilog6502_io_regs (
        input wire clk,
        input wire rst,

        taxi_apb_if.slv s_apb,

        output verilog6502_io_regs_pkg::verilog6502_io_regs__out_t hwif_out
    );

    //--------------------------------------------------------------------------
    // CPU Bus interface logic
    //--------------------------------------------------------------------------
    logic cpuif_req;
    logic cpuif_req_is_wr;
    logic [11:0] cpuif_addr;
    logic [31:0] cpuif_wr_data;
    logic [31:0] cpuif_wr_biten;
    logic cpuif_req_stall_wr;
    logic cpuif_req_stall_rd;

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

    logic cpuif_wr_ack;
    logic cpuif_wr_err;

    `ifndef SYNTHESIS
        initial begin
            assert_bad_addr_width: assert($bits(s_apb.paddr) >= verilog6502_io_regs_pkg::VERILOG6502_IO_REGS_MIN_ADDR_WIDTH)
                else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits(s_apb.paddr), verilog6502_io_regs_pkg::VERILOG6502_IO_REGS_MIN_ADDR_WIDTH);
            assert_bad_data_width: assert($bits(s_apb.pwdata) == verilog6502_io_regs_pkg::VERILOG6502_IO_REGS_DATA_WIDTH)
                else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits(s_apb.pwdata), verilog6502_io_regs_pkg::VERILOG6502_IO_REGS_DATA_WIDTH);
        end
    `endif

    // 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;
            cpuif_wr_biten <= '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[11:2], 2'b0};
                    cpuif_wr_data <= s_apb.pwdata;
                    for(int i=0; i<4; i++) begin
                        cpuif_wr_biten[i*8 +: 8] <= {8{s_apb.pstrb[i]}};
                    end
                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
                            & !(!cpuif_req_is_wr & cpuif_req_stall_rd)
                            & !(cpuif_req_is_wr & cpuif_req_stall_wr);

    //--------------------------------------------------------------------------
    // Address Decode
    //--------------------------------------------------------------------------
    typedef struct {
        logic core_ctrl;
        logic axi_base_address;
        logic nmi;
        logic reset_brq;
    } decoded_reg_strb_t;
    decoded_reg_strb_t decoded_reg_strb;
    logic decoded_err;
    logic [11:0] decoded_addr;
    logic decoded_req;
    logic decoded_req_is_wr;
    logic [31:0] decoded_wr_data;
    logic [31:0] decoded_wr_biten;

    always_comb begin
        automatic logic is_valid_addr;
        automatic logic is_valid_rw;
        is_valid_addr = '1; // No valid address check
        is_valid_rw = '1; // No valid RW check
        decoded_reg_strb.core_ctrl = cpuif_req_masked & (cpuif_addr == 12'h0);
        decoded_reg_strb.axi_base_address = cpuif_req_masked & (cpuif_addr == 12'h10);
        decoded_reg_strb.nmi = cpuif_req_masked & (cpuif_addr == 12'hff8);
        decoded_reg_strb.reset_brq = cpuif_req_masked & (cpuif_addr == 12'hffc);
        decoded_err = '0;
    end

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

    //--------------------------------------------------------------------------
    // Field logic
    //--------------------------------------------------------------------------
    typedef struct {
        struct {
            struct {
                logic next;
                logic load_next;
            } reset;
        } core_ctrl;
        struct {
            struct {
                logic [31:0] next;
                logic load_next;
            } val;
        } axi_base_address;
        struct {
            struct {
                logic [15:0] next;
                logic load_next;
            } nmi;
        } nmi;
        struct {
            struct {
                logic [15:0] next;
                logic load_next;
            } reset;
            struct {
                logic [15:0] next;
                logic load_next;
            } brk;
        } reset_brq;
    } field_combo_t;
    field_combo_t field_combo;

    typedef struct {
        struct {
            struct {
                logic value;
            } reset;
        } core_ctrl;
        struct {
            struct {
                logic [31:0] value;
            } val;
        } axi_base_address;
        struct {
            struct {
                logic [15:0] value;
            } nmi;
        } nmi;
        struct {
            struct {
                logic [15:0] value;
            } reset;
            struct {
                logic [15:0] value;
            } brk;
        } reset_brq;
    } field_storage_t;
    field_storage_t field_storage;

    // Field: verilog6502_io_regs.core_ctrl.reset
    always_comb begin
        automatic logic [0:0] next_c;
        automatic logic load_next_c;
        next_c = field_storage.core_ctrl.reset.value;
        load_next_c = '0;
        if(decoded_reg_strb.core_ctrl && decoded_req_is_wr) begin // SW write
            next_c = (field_storage.core_ctrl.reset.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]);
            load_next_c = '1;
        end
        field_combo.core_ctrl.reset.next = next_c;
        field_combo.core_ctrl.reset.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.core_ctrl.reset.value <= 1'h1;
        end else begin
            if(field_combo.core_ctrl.reset.load_next) begin
                field_storage.core_ctrl.reset.value <= field_combo.core_ctrl.reset.next;
            end
        end
    end
    assign hwif_out.core_ctrl.reset.value = field_storage.core_ctrl.reset.value;
    // Field: verilog6502_io_regs.axi_base_address.val
    always_comb begin
        automatic logic [31:0] next_c;
        automatic logic load_next_c;
        next_c = field_storage.axi_base_address.val.value;
        load_next_c = '0;
        if(decoded_reg_strb.axi_base_address && decoded_req_is_wr) begin // SW write
            next_c = (field_storage.axi_base_address.val.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]);
            load_next_c = '1;
        end
        field_combo.axi_base_address.val.next = next_c;
        field_combo.axi_base_address.val.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.axi_base_address.val.value <= 32'h0;
        end else begin
            if(field_combo.axi_base_address.val.load_next) begin
                field_storage.axi_base_address.val.value <= field_combo.axi_base_address.val.next;
            end
        end
    end
    assign hwif_out.axi_base_address.val.value = field_storage.axi_base_address.val.value;
    // Field: verilog6502_io_regs.nmi.nmi
    always_comb begin
        automatic logic [15:0] next_c;
        automatic logic load_next_c;
        next_c = field_storage.nmi.nmi.value;
        load_next_c = '0;
        if(decoded_reg_strb.nmi && decoded_req_is_wr) begin // SW write
            next_c = (field_storage.nmi.nmi.value & ~decoded_wr_biten[31:16]) | (decoded_wr_data[31:16] & decoded_wr_biten[31:16]);
            load_next_c = '1;
        end
        field_combo.nmi.nmi.next = next_c;
        field_combo.nmi.nmi.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.nmi.nmi.value <= 16'h200;
        end else begin
            if(field_combo.nmi.nmi.load_next) begin
                field_storage.nmi.nmi.value <= field_combo.nmi.nmi.next;
            end
        end
    end
    assign hwif_out.nmi.nmi.value = field_storage.nmi.nmi.value;
    // Field: verilog6502_io_regs.reset_brq.reset
    always_comb begin
        automatic logic [15:0] next_c;
        automatic logic load_next_c;
        next_c = field_storage.reset_brq.reset.value;
        load_next_c = '0;
        if(decoded_reg_strb.reset_brq && decoded_req_is_wr) begin // SW write
            next_c = (field_storage.reset_brq.reset.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]);
            load_next_c = '1;
        end
        field_combo.reset_brq.reset.next = next_c;
        field_combo.reset_brq.reset.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.reset_brq.reset.value <= 16'h200;
        end else begin
            if(field_combo.reset_brq.reset.load_next) begin
                field_storage.reset_brq.reset.value <= field_combo.reset_brq.reset.next;
            end
        end
    end
    assign hwif_out.reset_brq.reset.value = field_storage.reset_brq.reset.value;
    // Field: verilog6502_io_regs.reset_brq.brk
    always_comb begin
        automatic logic [15:0] next_c;
        automatic logic load_next_c;
        next_c = field_storage.reset_brq.brk.value;
        load_next_c = '0;
        if(decoded_reg_strb.reset_brq && decoded_req_is_wr) begin // SW write
            next_c = (field_storage.reset_brq.brk.value & ~decoded_wr_biten[31:16]) | (decoded_wr_data[31:16] & decoded_wr_biten[31:16]);
            load_next_c = '1;
        end
        field_combo.reset_brq.brk.next = next_c;
        field_combo.reset_brq.brk.load_next = load_next_c;
    end
    always_ff @(posedge clk) begin
        if(rst) begin
            field_storage.reset_brq.brk.value <= 16'h200;
        end else begin
            if(field_combo.reset_brq.brk.load_next) begin
                field_storage.reset_brq.brk.value <= field_combo.reset_brq.brk.next;
            end
        end
    end
    assign hwif_out.reset_brq.brk.value = field_storage.reset_brq.brk.value;

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

    //--------------------------------------------------------------------------
    // Readback
    //--------------------------------------------------------------------------

    logic [11:0] rd_mux_addr;
    assign rd_mux_addr = decoded_addr;

    logic readback_err;
    logic readback_done;
    logic [31:0] readback_data;
    always_comb begin
        automatic logic [31:0] readback_data_var;
        readback_data_var = '0;
        if(rd_mux_addr == 12'h0) begin
            readback_data_var[0] = field_storage.core_ctrl.reset.value;
        end
        if(rd_mux_addr == 12'h10) begin
            readback_data_var[31:0] = field_storage.axi_base_address.val.value;
        end
        if(rd_mux_addr == 12'hff8) begin
            readback_data_var[31:16] = field_storage.nmi.nmi.value;
        end
        if(rd_mux_addr == 12'hffc) begin
            readback_data_var[15:0] = field_storage.reset_brq.reset.value;
            readback_data_var[31:16] = field_storage.reset_brq.brk.value;
        end
        readback_data = readback_data_var;
        readback_done = decoded_req & ~decoded_req_is_wr;
        readback_err = '0;
    end

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