Register Map / CSR Generator

Generate a synthesizable AXI4-Lite register block from a simple text description: Verilog RTL with per-field ports, a C header with offsets/masks/shifts, and a Markdown register-map document. RW, RO, W1C and WO registers with per-register write strobes.

Results

Registers
4
Address span
16 bytes (ADDR_WIDTH = 4)
Hardware field ports
8
Module name
mycore_regs

Register map

OffsetRegisterAccessResetFields
0x000CTRLRW0x00000000enable, irq_en, mode
0x004STATUSRO0x00000000busy, err_code
0x008IRQW1C0x00000000done, error
0x00CDATAWO0x00000000data

Notes

Verilog (mycore_regs.v)
// mycore register block - AXI4-Lite slave
// 4 registers, 16 bytes, generated by libfpga.com/tools/register-map
// Single outstanding transaction; SLVERR on unmapped addresses.
module mycore_regs (
    input  wire        clk,
    input  wire        rst_n,
    // AXI4-Lite slave
    input  wire [3:0]  s_axil_awaddr,
    input  wire        s_axil_awvalid,
    output reg         s_axil_awready,
    input  wire [31:0] s_axil_wdata,
    input  wire [3:0]  s_axil_wstrb,
    input  wire        s_axil_wvalid,
    output reg         s_axil_wready,
    output reg  [1:0]  s_axil_bresp,
    output reg         s_axil_bvalid,
    input  wire        s_axil_bready,
    input  wire [3:0]  s_axil_araddr,
    input  wire        s_axil_arvalid,
    output reg         s_axil_arready,
    output reg  [31:0] s_axil_rdata,
    output reg  [1:0]  s_axil_rresp,
    output reg         s_axil_rvalid,
    input  wire        s_axil_rready,
    // hardware interface
    output wire o_ctrl_enable,
    output wire o_ctrl_irq_en,
    output wire [1:0] o_ctrl_mode,
    output reg         o_ctrl_wr,
    input  wire i_status_busy,
    input  wire [3:0] i_status_err_code,
    input  wire i_irq_done_set,
    output wire o_irq_done,
    input  wire i_irq_error_set,
    output wire o_irq_error,
    output wire [31:0] o_data,
    output reg         o_data_wr
);

    // word-address indices
    localparam [1:0] ADR_CTRL = 0;
    localparam [1:0] ADR_STATUS = 1;
    localparam [1:0] ADR_IRQ = 2;
    localparam [1:0] ADR_DATA = 3;

    function [31:0] wmask(input [31:0] o, input [31:0] n, input [3:0] s);
        wmask = {s[3] ? n[31:24] : o[31:24], s[2] ? n[23:16] : o[23:16],
                 s[1] ? n[15:8]  : o[15:8],  s[0] ? n[7:0]   : o[7:0]};
    endfunction

    wire wr_en = s_axil_awvalid & s_axil_wvalid & ~s_axil_bvalid & ~s_axil_awready;
    wire rd_en = s_axil_arvalid & ~s_axil_rvalid & ~s_axil_arready;
    wire [1:0] wr_word = s_axil_awaddr[3:2];
    wire [1:0] rd_word = s_axil_araddr[3:2];

    reg [31:0] r_ctrl;
    assign o_ctrl_enable = r_ctrl[0];
    assign o_ctrl_irq_en = r_ctrl[1];
    assign o_ctrl_mode = r_ctrl[3:2];

    always @(posedge clk) begin  // CTRL (RW)
        if (!rst_n) begin
            r_ctrl <= 32'h00000000;
            o_ctrl_wr <= 1'b0;
        end else begin
            o_ctrl_wr <= 1'b0;
            if (wr_en && wr_word == ADR_CTRL) begin
                r_ctrl <= wmask(r_ctrl, s_axil_wdata, s_axil_wstrb) & 32'h0000000F;
                o_ctrl_wr <= 1'b1;
            end
        end
    end

    wire [31:0] rv_status = {24'd0, i_status_err_code, 3'd0, i_status_busy};

    reg [31:0] r_irq;
    assign o_irq_done = r_irq[0];
    assign o_irq_error = r_irq[1];

    wire [31:0] set_irq = {30'd0, i_irq_error_set, i_irq_done_set};
    always @(posedge clk) begin  // IRQ (W1C)
        if (!rst_n)
            r_irq <= 32'h00000000;
        else
            r_irq <= (r_irq | set_irq) & ~((wr_en && wr_word == ADR_IRQ) ? s_axil_wdata : 32'd0) & 32'h00000003;
    end

    reg [31:0] r_data;
    assign o_data = r_data[31:0];

    always @(posedge clk) begin  // DATA (WO)
        if (!rst_n) begin
            r_data <= 32'h00000000;
            o_data_wr <= 1'b0;
        end else begin
            o_data_wr <= 1'b0;
            if (wr_en && wr_word == ADR_DATA) begin
                r_data <= wmask(r_data, s_axil_wdata, s_axil_wstrb) & 32'hFFFFFFFF;
                o_data_wr <= 1'b1;
            end
        end
    end

    always @(posedge clk) begin  // write handshake
        if (!rst_n) begin
            s_axil_awready <= 1'b0;
            s_axil_wready  <= 1'b0;
            s_axil_bvalid  <= 1'b0;
            s_axil_bresp   <= 2'b00;
        end else begin
            s_axil_awready <= 1'b0;
            s_axil_wready  <= 1'b0;
            if (wr_en) begin
                s_axil_awready <= 1'b1;
                s_axil_wready  <= 1'b1;
                s_axil_bvalid  <= 1'b1;
                case (wr_word)
                    ADR_CTRL: s_axil_bresp <= 2'b00;
                    ADR_IRQ: s_axil_bresp <= 2'b00;
                    ADR_DATA: s_axil_bresp <= 2'b00;
                    default: s_axil_bresp <= 2'b10;  // SLVERR
                endcase
            end
            if (s_axil_bvalid && s_axil_bready) s_axil_bvalid <= 1'b0;
        end
    end

    always @(posedge clk) begin  // read handshake
        if (!rst_n) begin
            s_axil_arready <= 1'b0;
            s_axil_rvalid  <= 1'b0;
            s_axil_rresp   <= 2'b00;
            s_axil_rdata   <= 32'd0;
        end else begin
            s_axil_arready <= 1'b0;
            if (rd_en) begin
                s_axil_arready <= 1'b1;
                s_axil_rvalid  <= 1'b1;
                s_axil_rresp   <= 2'b00;
                case (rd_word)
                    ADR_CTRL: s_axil_rdata <= r_ctrl;
                    ADR_STATUS: s_axil_rdata <= rv_status;
                    ADR_IRQ: s_axil_rdata <= r_irq;
                    ADR_DATA: s_axil_rdata <= 32'd0;
                    default: begin
                        s_axil_rdata <= 32'd0;
                        s_axil_rresp <= 2'b10;  // SLVERR
                    end
                endcase
            end
            if (s_axil_rvalid && s_axil_rready) s_axil_rvalid <= 1'b0;
        end
    end
endmodule
C header (mycore_regs.h)
/* mycore register map - generated by libfpga.com/tools/register-map */
#ifndef MYCORE_REGS_H
#define MYCORE_REGS_H

#define MYCORE_CTRL_OFFS 0x000u  /* RW, reset 0x00000000 */
#define MYCORE_CTRL_ENABLE_MASK 0x00000001u
#define MYCORE_CTRL_ENABLE_SHIFT 0u
#define MYCORE_CTRL_IRQ_EN_MASK 0x00000002u
#define MYCORE_CTRL_IRQ_EN_SHIFT 1u
#define MYCORE_CTRL_MODE_MASK 0x0000000Cu
#define MYCORE_CTRL_MODE_SHIFT 2u

#define MYCORE_STATUS_OFFS 0x004u  /* RO, reset 0x00000000 */
#define MYCORE_STATUS_BUSY_MASK 0x00000001u
#define MYCORE_STATUS_BUSY_SHIFT 0u
#define MYCORE_STATUS_ERR_CODE_MASK 0x000000F0u
#define MYCORE_STATUS_ERR_CODE_SHIFT 4u

#define MYCORE_IRQ_OFFS 0x008u  /* W1C, reset 0x00000000 */
#define MYCORE_IRQ_DONE_MASK 0x00000001u
#define MYCORE_IRQ_DONE_SHIFT 0u
#define MYCORE_IRQ_ERROR_MASK 0x00000002u
#define MYCORE_IRQ_ERROR_SHIFT 1u

#define MYCORE_DATA_OFFS 0x00Cu  /* WO, reset 0x00000000 */

#endif /* MYCORE_REGS_H */
Markdown documentation
# mycore register map

## CTRL - 0x000 (RW, reset 0x00000000)

| Bits | Field | Description |
|---|---|---|
| 3:2 | mode | 0=idle 1=run 2=loopback |
| 1 | irq_en | Interrupt enable |
| 0 | enable | Core enable |

## STATUS - 0x004 (RO, reset 0x00000000)

| Bits | Field | Description |
|---|---|---|
| 7:4 | err_code | Last error code |
| 0 | busy | Transfer in progress |

## IRQ - 0x008 (W1C, reset 0x00000000)

| Bits | Field | Description |
|---|---|---|
| 1 | error | Transfer error |
| 0 | done | Transfer complete |

## DATA - 0x00C (WO, reset 0x00000000)

| Bits | Field | Description |
|---|---|---|
| 31:0 | data | (whole register) |

This exact result is bookmarkable — the URL contains all your inputs. Need it in a script? Append &format=json (API docs).

About this tool

Every IP block needs the same three artifacts kept in sync: the RTL that implements its registers, the C header the driver uses, and the documentation humans read. Writing them by hand means they drift — the classic bug where the driver's mask disagrees with the RTL by one bit. This generator makes the text description the single source of truth and emits all three. The Verilog is a standard single-outstanding AXI4-Lite slave: wstrb honored on RW/WO writes, SLVERR on unmapped addresses, a write strobe output per writable register (handy for command/FIFO registers), and W1C semantics where hardware sets bits and software clears them — the standard interrupt-status pattern. Keep the definition file in your repo and regenerate via the JSON API in CI.

Related tools