SystemVerilog generator

The SystemVerilogAxiLiteGenerator class creates a register file with AXI-Lite interface. See below for an example usage and a showcase of the code that can be generated.

Details and limitations

The SystemVerilog code generator in hdl-registers is a wrapper around PeakRDL-regblock. The hdl-registers representation of register data is translated internally to a PeakRDL representation before the PeakRDL exporter is called. All the features of hdl-registers are supported, except for the following:

  1. Register constants (of any type).

  2. Register arrays.

  3. Integer fields with signed range.

  4. Bit vector fields with a numeric interpretation other than unsigned.

  5. Pulse-on-write register modes.

It is quite likely that some or even all of these could be supported in the future. Some of the missing features are due to limitations in translation layer, while others stem from the PeakRDL tool.

Configuration

The RegisterCodeGenerator.create() and RegisterCodeGenerator.create_if_needed() methods of SystemVerilogAxiLiteGenerator can be supplied with a flatten_axi_lite argument. If this is set to True, the generated SystemVerilog module will have an input/output port for each individual AXI-Lite signal. If left as False, the AXI-Lite signals will be grouped into a single SystemVerilog interface.

If using the non-flattened interface, this interface file must be added to your simulation/build project.

Example

An example is used to illustrate the generator API and to showcase the code that can be generated.

Register definition TOML file

The TOML file used in this example sets up some very basic registers with a few fields.

Click to expand/collapse code.
Example TOML file.
 1################################################################################
 2[conf]
 3
 4mode = "r_w"
 5
 6enable.type = "bit"
 7enable.description = "Write as '1' to enable the module."
 8
 9tuser.type = "bit_vector"
10tuser.width = 8
11tuser.description = "Tag all output data with this."
12
13
14################################################################################
15[address]
16
17mode = "r_w"
18
19value.type = "bit_vector"
20value.width = 28
21value.description = "Data will be read from this base address."
22
23
24################################################################################
25[status]
26
27mode = "r"
28
29state.type = "enumeration"
30state.description = "Current state of the module."
31state.element.idle = "Is ready to receive new data."
32state.element.processing = "Is processing a chunk."
33state.element.sending_out = "Is waiting to send out data."
34
35overflow.type = "bit"
36overflow.description = "Is asserted if an overflow has occurred at any point."

Python file to generate register artifacts

The Python code below is used to parse the above TOML file and generate the SystemVerilog code.

Python code that parses the example TOML file and generates SystemVerilog code.
 1import sys
 2from pathlib import Path
 3
 4from hdl_registers.generator.systemverilog.axi_lite.register_file import (
 5    SystemVerilogAxiLiteGenerator,
 6)
 7from hdl_registers.parser.toml import from_toml
 8
 9THIS_DIR = Path(__file__).parent
10
11
12def main(output_folder: Path) -> None:
13    """
14    Create SystemVerilog register artifacts for an example module.
15    """
16    register_list = from_toml(
17        name="basic", toml_file=THIS_DIR.parent / "example_basic" / "regs_basic.toml"
18    )
19
20    SystemVerilogAxiLiteGenerator(
21        register_list=register_list, output_folder=output_folder
22    ).create_if_needed()
23
24
25if __name__ == "__main__":
26    main(output_folder=Path(sys.argv[1]))

Generated SystemVerilog register package

Below is the generated register package, which is used by the Generated SystemVerilog register file module. It must also be used wherever the register file is instantiated in order to get the correct types for the register and field values.

Example register package.
 1// -----------------------------------------------------------------------------
 2// This file is automatically generated by hdl-registers version 7.3.1-dev.
 3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
 4// Generated 2025-04-01 11:34 from file regs_basic.toml at Git commit 228a22928e9a.
 5// Register hash 2a2b3d670f42402eff5a314c1dde6eec58243b5c.
 6// -----------------------------------------------------------------------------
 7
 8// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
 9//  https://github.com/SystemRDL/PeakRDL-regblock
10
11package basic_register_file_axi_lite_pkg;
12
13    localparam BASIC_REGISTER_FILE_AXI_LITE_DATA_WIDTH = 32;
14    localparam BASIC_REGISTER_FILE_AXI_LITE_MIN_ADDR_WIDTH = 4;
15
16    typedef struct {
17        logic [1:0] next;
18    } xtern__basic__status__state__in_t__in_t;
19
20    typedef struct {
21        logic next;
22    } xtern__basic__status__overflow__in_t__in_t;
23
24    typedef struct {
25        xtern__basic__status__state__in_t__in_t state;
26        xtern__basic__status__overflow__in_t__in_t overflow;
27    } xtern__basic__status__in_t__in_t;
28
29    typedef struct {
30        xtern__basic__status__in_t__in_t status;
31    } basic__in_t;
32
33    typedef struct {
34        logic value;
35    } xtern__basic__conf__enable__out_t__out_t;
36
37    typedef struct {
38        logic [7:0] value;
39    } xtern__basic__conf__tuser__out_t__out_t;
40
41    typedef struct {
42        xtern__basic__conf__enable__out_t__out_t enable;
43        xtern__basic__conf__tuser__out_t__out_t tuser;
44    } xtern__basic__conf__out_t__out_t;
45
46    typedef struct {
47        logic [27:0] value;
48    } xtern__basic__address__value__out_t__out_t;
49
50    typedef struct {
51        xtern__basic__address__value__out_t__out_t value;
52    } xtern__basic__address__out_t__out_t;
53
54    typedef struct {
55        xtern__basic__conf__out_t__out_t conf;
56        xtern__basic__address__out_t__out_t address;
57    } basic__out_t;
58
59    typedef enum logic [1:0] {
60        basic_status_state__idle = 'h0,
61        basic_status_state__processing = 'h1,
62        basic_status_state__sending_out = 'h2
63    } basic_status_state_e;
64endpackage

Generated SystemVerilog register file module

Below is the generated SystemVerilog AXI-Lite register file module.

Example register file module.
  1// -----------------------------------------------------------------------------
  2// This file is automatically generated by hdl-registers version 7.3.1-dev.
  3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
  4// Generated 2025-04-01 11:34 from file regs_basic.toml at Git commit 228a22928e9a.
  5// Register hash 2a2b3d670f42402eff5a314c1dde6eec58243b5c.
  6// -----------------------------------------------------------------------------
  7
  8// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
  9//  https://github.com/SystemRDL/PeakRDL-regblock
 10
 11module basic_register_file_axi_lite (
 12        input wire clk,
 13        input wire rst,
 14
 15        axi4lite_intf.slave s_axil,
 16
 17        input basic_register_file_axi_lite_pkg::basic__in_t hwif_in,
 18        output basic_register_file_axi_lite_pkg::basic__out_t hwif_out
 19    );
 20
 21    //--------------------------------------------------------------------------
 22    // CPU Bus interface logic
 23    //--------------------------------------------------------------------------
 24    logic cpuif_req;
 25    logic cpuif_req_is_wr;
 26    logic [3:0] cpuif_addr;
 27    logic [31:0] cpuif_wr_data;
 28    logic [31:0] cpuif_wr_biten;
 29    logic cpuif_req_stall_wr;
 30    logic cpuif_req_stall_rd;
 31
 32    logic cpuif_rd_ack;
 33    logic cpuif_rd_err;
 34    logic [31:0] cpuif_rd_data;
 35
 36    logic cpuif_wr_ack;
 37    logic cpuif_wr_err;
 38
 39    // Max Outstanding Transactions: 2
 40    logic [1:0] axil_n_in_flight;
 41    logic axil_prev_was_rd;
 42    logic axil_arvalid;
 43    logic [3:0] axil_araddr;
 44    logic axil_ar_accept;
 45    logic axil_awvalid;
 46    logic [3:0] axil_awaddr;
 47    logic axil_wvalid;
 48    logic [31:0] axil_wdata;
 49    logic [3:0] axil_wstrb;
 50    logic axil_aw_accept;
 51    logic axil_resp_acked;
 52
 53    // Transaction request acceptance
 54    always_ff @(posedge clk) begin
 55        if(rst) begin
 56            axil_prev_was_rd <= '0;
 57            axil_arvalid <= '0;
 58            axil_araddr <= '0;
 59            axil_awvalid <= '0;
 60            axil_awaddr <= '0;
 61            axil_wvalid <= '0;
 62            axil_wdata <= '0;
 63            axil_wstrb <= '0;
 64            axil_n_in_flight <= '0;
 65        end else begin
 66            // AR* acceptance register
 67            if(axil_ar_accept) begin
 68                axil_prev_was_rd <= '1;
 69                axil_arvalid <= '0;
 70            end
 71            if(s_axil.ARVALID && s_axil.ARREADY) begin
 72                axil_arvalid <= '1;
 73                axil_araddr <= s_axil.ARADDR;
 74            end
 75
 76            // AW* & W* acceptance registers
 77            if(axil_aw_accept) begin
 78                axil_prev_was_rd <= '0;
 79                axil_awvalid <= '0;
 80                axil_wvalid <= '0;
 81            end
 82            if(s_axil.AWVALID && s_axil.AWREADY) begin
 83                axil_awvalid <= '1;
 84                axil_awaddr <= s_axil.AWADDR;
 85            end
 86            if(s_axil.WVALID && s_axil.WREADY) begin
 87                axil_wvalid <= '1;
 88                axil_wdata <= s_axil.WDATA;
 89                axil_wstrb <= s_axil.WSTRB;
 90            end
 91
 92            // Keep track of in-flight transactions
 93            if((axil_ar_accept || axil_aw_accept) && !axil_resp_acked) begin
 94                axil_n_in_flight <= axil_n_in_flight + 1'b1;
 95            end else if(!(axil_ar_accept || axil_aw_accept) && axil_resp_acked) begin
 96                axil_n_in_flight <= axil_n_in_flight - 1'b1;
 97            end
 98        end
 99    end
100
101    always_comb begin
102        s_axil.ARREADY = (!axil_arvalid || axil_ar_accept);
103        s_axil.AWREADY = (!axil_awvalid || axil_aw_accept);
104        s_axil.WREADY = (!axil_wvalid || axil_aw_accept);
105    end
106
107    // Request dispatch
108    always_comb begin
109        cpuif_wr_data = axil_wdata;
110        for(int i=0; i<4; i++) begin
111            cpuif_wr_biten[i*8 +: 8] = {8{axil_wstrb[i]}};
112        end
113        cpuif_req = '0;
114        cpuif_req_is_wr = '0;
115        cpuif_addr = '0;
116        axil_ar_accept = '0;
117        axil_aw_accept = '0;
118
119        if(axil_n_in_flight < 2'd2) begin
120            // Can safely issue more transactions without overwhelming response buffer
121            if(axil_arvalid && !axil_prev_was_rd) begin
122                cpuif_req = '1;
123                cpuif_req_is_wr = '0;
124                cpuif_addr = {axil_araddr[3:2], 2'b0};
125                if(!cpuif_req_stall_rd) axil_ar_accept = '1;
126            end else if(axil_awvalid && axil_wvalid) begin
127                cpuif_req = '1;
128                cpuif_req_is_wr = '1;
129                cpuif_addr = {axil_awaddr[3:2], 2'b0};
130                if(!cpuif_req_stall_wr) axil_aw_accept = '1;
131            end else if(axil_arvalid) begin
132                cpuif_req = '1;
133                cpuif_req_is_wr = '0;
134                cpuif_addr = {axil_araddr[3:2], 2'b0};
135                if(!cpuif_req_stall_rd) axil_ar_accept = '1;
136            end
137        end
138    end
139
140
141    // AXI4-Lite Response Logic
142    struct {
143        logic is_wr;
144        logic err;
145        logic [31:0] rdata;
146    } axil_resp_buffer[2];
147
148    logic [1:0] axil_resp_wptr;
149    logic [1:0] axil_resp_rptr;
150
151    always_ff @(posedge clk) begin
152        if(rst) begin
153            for(int i=0; i<2; i++) begin
154                axil_resp_buffer[i].is_wr <= '0;
155                axil_resp_buffer[i].err <= '0;
156                axil_resp_buffer[i].rdata <= '0;
157            end
158            axil_resp_wptr <= '0;
159            axil_resp_rptr <= '0;
160        end else begin
161            // Store responses in buffer until AXI response channel accepts them
162            if(cpuif_rd_ack || cpuif_wr_ack) begin
163                if(cpuif_rd_ack) begin
164                    axil_resp_buffer[axil_resp_wptr[0:0]].is_wr <= '0;
165                    axil_resp_buffer[axil_resp_wptr[0:0]].err <= cpuif_rd_err;
166                    axil_resp_buffer[axil_resp_wptr[0:0]].rdata <= cpuif_rd_data;
167
168                end else if(cpuif_wr_ack) begin
169                    axil_resp_buffer[axil_resp_wptr[0:0]].is_wr <= '1;
170                    axil_resp_buffer[axil_resp_wptr[0:0]].err <= cpuif_wr_err;
171                end
172                axil_resp_wptr <= axil_resp_wptr + 1'b1;
173            end
174
175            // Advance read pointer when acknowledged
176            if(axil_resp_acked) begin
177                axil_resp_rptr <= axil_resp_rptr + 1'b1;
178            end
179        end
180    end
181
182    always_comb begin
183        axil_resp_acked = '0;
184        s_axil.BVALID = '0;
185        s_axil.RVALID = '0;
186        if(axil_resp_rptr != axil_resp_wptr) begin
187            if(axil_resp_buffer[axil_resp_rptr[0:0]].is_wr) begin
188                s_axil.BVALID = '1;
189                if(s_axil.BREADY) axil_resp_acked = '1;
190            end else begin
191                s_axil.RVALID = '1;
192                if(s_axil.RREADY) axil_resp_acked = '1;
193            end
194        end
195
196        s_axil.RDATA = axil_resp_buffer[axil_resp_rptr[0:0]].rdata;
197        if(axil_resp_buffer[axil_resp_rptr[0:0]].err) begin
198            s_axil.BRESP = 2'b10;
199            s_axil.RRESP = 2'b10;
200        end else begin
201            s_axil.BRESP = 2'b00;
202            s_axil.RRESP = 2'b00;
203        end
204    end
205
206    logic cpuif_req_masked;
207
208    // Read & write latencies are balanced. Stalls not required
209    assign cpuif_req_stall_rd = '0;
210    assign cpuif_req_stall_wr = '0;
211    assign cpuif_req_masked = cpuif_req
212                            & !(!cpuif_req_is_wr & cpuif_req_stall_rd)
213                            & !(cpuif_req_is_wr & cpuif_req_stall_wr);
214
215    //--------------------------------------------------------------------------
216    // Address Decode
217    //--------------------------------------------------------------------------
218    typedef struct {
219        logic conf;
220        logic address;
221        logic status;
222    } decoded_reg_strb_t;
223    decoded_reg_strb_t decoded_reg_strb;
224    logic decoded_req;
225    logic decoded_req_is_wr;
226    logic [31:0] decoded_wr_data;
227    logic [31:0] decoded_wr_biten;
228
229    always_comb begin
230        decoded_reg_strb.conf = cpuif_req_masked & (cpuif_addr == 4'h0);
231        decoded_reg_strb.address = cpuif_req_masked & (cpuif_addr == 4'h4);
232        decoded_reg_strb.status = cpuif_req_masked & (cpuif_addr == 4'h8);
233    end
234
235    // Pass down signals to next stage
236    assign decoded_req = cpuif_req_masked;
237    assign decoded_req_is_wr = cpuif_req_is_wr;
238    assign decoded_wr_data = cpuif_wr_data;
239    assign decoded_wr_biten = cpuif_wr_biten;
240
241    //--------------------------------------------------------------------------
242    // Field logic
243    //--------------------------------------------------------------------------
244    typedef struct {
245        struct {
246            struct {
247                logic next;
248                logic load_next;
249            } enable;
250            struct {
251                logic [7:0] next;
252                logic load_next;
253            } tuser;
254        } conf;
255        struct {
256            struct {
257                logic [27:0] next;
258                logic load_next;
259            } value;
260        } address;
261    } field_combo_t;
262    field_combo_t field_combo;
263
264    typedef struct {
265        struct {
266            struct {
267                logic value;
268            } enable;
269            struct {
270                logic [7:0] value;
271            } tuser;
272        } conf;
273        struct {
274            struct {
275                logic [27:0] value;
276            } value;
277        } address;
278    } field_storage_t;
279    field_storage_t field_storage;
280
281    // Field: basic.conf.enable
282    always_comb begin
283        automatic logic [0:0] next_c;
284        automatic logic load_next_c;
285        next_c = field_storage.conf.enable.value;
286        load_next_c = '0;
287        if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
288            next_c = (field_storage.conf.enable.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]);
289            load_next_c = '1;
290        end
291        field_combo.conf.enable.next = next_c;
292        field_combo.conf.enable.load_next = load_next_c;
293    end
294    always_ff @(posedge clk) begin
295        if(rst) begin
296            field_storage.conf.enable.value <= 1'h0;
297        end else begin
298            if(field_combo.conf.enable.load_next) begin
299                field_storage.conf.enable.value <= field_combo.conf.enable.next;
300            end
301        end
302    end
303    assign hwif_out.conf.enable.value = field_storage.conf.enable.value;
304    // Field: basic.conf.tuser
305    always_comb begin
306        automatic logic [7:0] next_c;
307        automatic logic load_next_c;
308        next_c = field_storage.conf.tuser.value;
309        load_next_c = '0;
310        if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
311            next_c = (field_storage.conf.tuser.value & ~decoded_wr_biten[8:1]) | (decoded_wr_data[8:1] & decoded_wr_biten[8:1]);
312            load_next_c = '1;
313        end
314        field_combo.conf.tuser.next = next_c;
315        field_combo.conf.tuser.load_next = load_next_c;
316    end
317    always_ff @(posedge clk) begin
318        if(rst) begin
319            field_storage.conf.tuser.value <= 8'h0;
320        end else begin
321            if(field_combo.conf.tuser.load_next) begin
322                field_storage.conf.tuser.value <= field_combo.conf.tuser.next;
323            end
324        end
325    end
326    assign hwif_out.conf.tuser.value = field_storage.conf.tuser.value;
327    // Field: basic.address.value
328    always_comb begin
329        automatic logic [27:0] next_c;
330        automatic logic load_next_c;
331        next_c = field_storage.address.value.value;
332        load_next_c = '0;
333        if(decoded_reg_strb.address && decoded_req_is_wr) begin // SW write
334            next_c = (field_storage.address.value.value & ~decoded_wr_biten[27:0]) | (decoded_wr_data[27:0] & decoded_wr_biten[27:0]);
335            load_next_c = '1;
336        end
337        field_combo.address.value.next = next_c;
338        field_combo.address.value.load_next = load_next_c;
339    end
340    always_ff @(posedge clk) begin
341        if(rst) begin
342            field_storage.address.value.value <= 28'h0;
343        end else begin
344            if(field_combo.address.value.load_next) begin
345                field_storage.address.value.value <= field_combo.address.value.next;
346            end
347        end
348    end
349    assign hwif_out.address.value.value = field_storage.address.value.value;
350
351    //--------------------------------------------------------------------------
352    // Write response
353    //--------------------------------------------------------------------------
354    assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
355    // Writes are always granted with no error response
356    assign cpuif_wr_err = '0;
357
358    //--------------------------------------------------------------------------
359    // Readback
360    //--------------------------------------------------------------------------
361
362    logic readback_err;
363    logic readback_done;
364    logic [31:0] readback_data;
365
366    // Assign readback values to a flattened array
367    logic [31:0] readback_array[3];
368    assign readback_array[0][0:0] = (decoded_reg_strb.conf && !decoded_req_is_wr) ? field_storage.conf.enable.value : '0;
369    assign readback_array[0][8:1] = (decoded_reg_strb.conf && !decoded_req_is_wr) ? field_storage.conf.tuser.value : '0;
370    assign readback_array[0][31:9] = '0;
371    assign readback_array[1][27:0] = (decoded_reg_strb.address && !decoded_req_is_wr) ? field_storage.address.value.value : '0;
372    assign readback_array[1][31:28] = '0;
373    assign readback_array[2][1:0] = (decoded_reg_strb.status && !decoded_req_is_wr) ? hwif_in.status.state.next : '0;
374    assign readback_array[2][2:2] = (decoded_reg_strb.status && !decoded_req_is_wr) ? hwif_in.status.overflow.next : '0;
375    assign readback_array[2][31:3] = '0;
376
377    // Reduce the array
378    always_comb begin
379        automatic logic [31:0] readback_data_var;
380        readback_done = decoded_req & ~decoded_req_is_wr;
381        readback_err = '0;
382        readback_data_var = '0;
383        for(int i=0; i<3; i++) readback_data_var |= readback_array[i];
384        readback_data = readback_data_var;
385    end
386
387    assign cpuif_rd_ack = readback_done;
388    assign cpuif_rd_data = readback_data;
389    assign cpuif_rd_err = readback_err;
390endmodule