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:
Register constants (of any type).
Integer fields with signed range.
Bit vector fields with a numeric interpretation other than unsigned.
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.
Python file to generate register artifacts
The Python code below is used to parse the above TOML file and generate the 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.
1// -----------------------------------------------------------------------------
2// This file is automatically generated by hdl-registers version 8.1.1-dev.
3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
4// Generated 2026-04-03 21:07 from file regs_basic.toml at Git commit dc68c4e79956.
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 localparam BASIC_REGISTER_FILE_AXI_LITE_SIZE = 'hc;
16
17 typedef struct {
18 logic [1:0] next;
19 } xtern__basic__status__state__in_t__in_t;
20
21 typedef struct {
22 logic next;
23 } xtern__basic__status__overflow__in_t__in_t;
24
25 typedef struct {
26 xtern__basic__status__state__in_t__in_t state;
27 xtern__basic__status__overflow__in_t__in_t overflow;
28 } xtern__basic__status__in_t__in_t;
29
30 typedef struct {
31 xtern__basic__status__in_t__in_t status;
32 } basic__in_t;
33
34 typedef struct {
35 logic value;
36 } xtern__basic__conf__enable__out_t__out_t;
37
38 typedef struct {
39 logic [7:0] value;
40 } xtern__basic__conf__tuser__out_t__out_t;
41
42 typedef struct {
43 xtern__basic__conf__enable__out_t__out_t enable;
44 xtern__basic__conf__tuser__out_t__out_t tuser;
45 } xtern__basic__conf__out_t__out_t;
46
47 typedef struct {
48 logic [27:0] value;
49 } xtern__basic__address__value__out_t__out_t;
50
51 typedef struct {
52 xtern__basic__address__value__out_t__out_t value;
53 } xtern__basic__address__out_t__out_t;
54
55 typedef struct {
56 xtern__basic__conf__out_t__out_t conf;
57 xtern__basic__address__out_t__out_t address;
58 } basic__out_t;
59
60 typedef enum logic [1:0] {
61 basic_status_state__idle = 'h0,
62 basic_status_state__processing = 'h1,
63 basic_status_state__sending_out = 'h2
64 } basic_status_state_e;
65endpackage
Generated SystemVerilog register file module
Below is the generated SystemVerilog AXI-Lite register file module.
1// -----------------------------------------------------------------------------
2// This file is automatically generated by hdl-registers version 8.1.1-dev.
3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
4// Generated 2026-04-03 21:07 from file regs_basic.toml at Git commit dc68c4e79956.
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 `ifndef SYNTHESIS
40 initial begin
41 assert_bad_addr_width: assert($bits(s_axil.ARADDR) >= basic_register_file_axi_lite_pkg::BASIC_REGISTER_FILE_AXI_LITE_MIN_ADDR_WIDTH)
42 else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits(s_axil.ARADDR), basic_register_file_axi_lite_pkg::BASIC_REGISTER_FILE_AXI_LITE_MIN_ADDR_WIDTH);
43 assert_bad_data_width: assert($bits(s_axil.WDATA) == basic_register_file_axi_lite_pkg::BASIC_REGISTER_FILE_AXI_LITE_DATA_WIDTH)
44 else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits(s_axil.WDATA), basic_register_file_axi_lite_pkg::BASIC_REGISTER_FILE_AXI_LITE_DATA_WIDTH);
45 end
46 `endif
47
48 // Max Outstanding Transactions: 2
49 logic [1:0] axil_n_in_flight;
50 logic axil_prev_was_rd;
51 logic axil_arvalid;
52 logic [3:0] axil_araddr;
53 logic axil_ar_accept;
54 logic axil_awvalid;
55 logic [3:0] axil_awaddr;
56 logic axil_wvalid;
57 logic [31:0] axil_wdata;
58 logic [3:0] axil_wstrb;
59 logic axil_aw_accept;
60 logic axil_resp_acked;
61
62 // Transaction request acceptance
63 always_ff @(posedge clk) begin
64 if(rst) begin
65 axil_prev_was_rd <= '0;
66 axil_arvalid <= '0;
67 axil_araddr <= '0;
68 axil_awvalid <= '0;
69 axil_awaddr <= '0;
70 axil_wvalid <= '0;
71 axil_wdata <= '0;
72 axil_wstrb <= '0;
73 axil_n_in_flight <= '0;
74 end else begin
75 // AR* acceptance register
76 if(axil_ar_accept) begin
77 axil_prev_was_rd <= '1;
78 axil_arvalid <= '0;
79 end
80 if(s_axil.ARVALID && s_axil.ARREADY) begin
81 axil_arvalid <= '1;
82 axil_araddr <= s_axil.ARADDR;
83 end
84
85 // AW* & W* acceptance registers
86 if(axil_aw_accept) begin
87 axil_prev_was_rd <= '0;
88 axil_awvalid <= '0;
89 axil_wvalid <= '0;
90 end
91 if(s_axil.AWVALID && s_axil.AWREADY) begin
92 axil_awvalid <= '1;
93 axil_awaddr <= s_axil.AWADDR;
94 end
95 if(s_axil.WVALID && s_axil.WREADY) begin
96 axil_wvalid <= '1;
97 axil_wdata <= s_axil.WDATA;
98 axil_wstrb <= s_axil.WSTRB;
99 end
100
101 // Keep track of in-flight transactions
102 if((axil_ar_accept || axil_aw_accept) && !axil_resp_acked) begin
103 axil_n_in_flight <= axil_n_in_flight + 1'b1;
104 end else if(!(axil_ar_accept || axil_aw_accept) && axil_resp_acked) begin
105 axil_n_in_flight <= axil_n_in_flight - 1'b1;
106 end
107 end
108 end
109
110 always_comb begin
111 s_axil.ARREADY = (!axil_arvalid || axil_ar_accept);
112 s_axil.AWREADY = (!axil_awvalid || axil_aw_accept);
113 s_axil.WREADY = (!axil_wvalid || axil_aw_accept);
114 end
115
116 // Request dispatch
117 always_comb begin
118 cpuif_wr_data = axil_wdata;
119 for(int i=0; i<4; i++) begin
120 cpuif_wr_biten[i*8 +: 8] = {8{axil_wstrb[i]}};
121 end
122 cpuif_req = '0;
123 cpuif_req_is_wr = '0;
124 cpuif_addr = '0;
125 axil_ar_accept = '0;
126 axil_aw_accept = '0;
127
128 if(axil_n_in_flight < 2'd2) begin
129 // Can safely issue more transactions without overwhelming response buffer
130 if(axil_arvalid && !axil_prev_was_rd) begin
131 cpuif_req = '1;
132 cpuif_req_is_wr = '0;
133 cpuif_addr = {axil_araddr[3:2], 2'b0};
134 if(!cpuif_req_stall_rd) axil_ar_accept = '1;
135 end else if(axil_awvalid && axil_wvalid) begin
136 cpuif_req = '1;
137 cpuif_req_is_wr = '1;
138 cpuif_addr = {axil_awaddr[3:2], 2'b0};
139 if(!cpuif_req_stall_wr) axil_aw_accept = '1;
140 end else if(axil_arvalid) begin
141 cpuif_req = '1;
142 cpuif_req_is_wr = '0;
143 cpuif_addr = {axil_araddr[3:2], 2'b0};
144 if(!cpuif_req_stall_rd) axil_ar_accept = '1;
145 end
146 end
147 end
148
149
150 // AXI4-Lite Response Logic
151 logic axil_resp_buffer_is_wr[2];
152 logic axil_resp_buffer_err[2];
153 logic [31:0] axil_resp_buffer_rdata[2];
154
155 logic [1:0] axil_resp_wptr;
156 logic [1:0] axil_resp_rptr;
157
158 always_ff @(posedge clk) begin
159 if(rst) begin
160 for(int i=0; i<2; i++) begin
161 axil_resp_buffer_is_wr[i] <= '0;
162 axil_resp_buffer_err[i] <= '0;
163 axil_resp_buffer_rdata[i] <= '0;
164 end
165 axil_resp_wptr <= '0;
166 axil_resp_rptr <= '0;
167 end else begin
168 // Store responses in buffer until AXI response channel accepts them
169 if(cpuif_rd_ack || cpuif_wr_ack) begin
170 if(cpuif_rd_ack) begin
171 axil_resp_buffer_is_wr[axil_resp_wptr[0:0]] <= '0;
172 axil_resp_buffer_err[axil_resp_wptr[0:0]] <= cpuif_rd_err;
173 axil_resp_buffer_rdata[axil_resp_wptr[0:0]] <= cpuif_rd_data;
174
175 end else if(cpuif_wr_ack) begin
176 axil_resp_buffer_is_wr[axil_resp_wptr[0:0]] <= '1;
177 axil_resp_buffer_err[axil_resp_wptr[0:0]] <= cpuif_wr_err;
178 end
179 axil_resp_wptr <= axil_resp_wptr + 1'b1;
180 end
181
182 // Advance read pointer when acknowledged
183 if(axil_resp_acked) begin
184 axil_resp_rptr <= axil_resp_rptr + 1'b1;
185 end
186 end
187 end
188
189 always_comb begin
190 axil_resp_acked = '0;
191 s_axil.BVALID = '0;
192 s_axil.RVALID = '0;
193 if(axil_resp_rptr != axil_resp_wptr) begin
194 if(axil_resp_buffer_is_wr[axil_resp_rptr[0:0]]) begin
195 s_axil.BVALID = '1;
196 if(s_axil.BREADY) axil_resp_acked = '1;
197 end else begin
198 s_axil.RVALID = '1;
199 if(s_axil.RREADY) axil_resp_acked = '1;
200 end
201 end
202
203 s_axil.RDATA = axil_resp_buffer_rdata[axil_resp_rptr[0:0]];
204 if(axil_resp_buffer_err[axil_resp_rptr[0:0]]) begin
205 s_axil.BRESP = 2'b10;
206 s_axil.RRESP = 2'b10;
207 end else begin
208 s_axil.BRESP = 2'b00;
209 s_axil.RRESP = 2'b00;
210 end
211 end
212
213 logic cpuif_req_masked;
214
215 // Read & write latencies are balanced. Stalls not required
216 assign cpuif_req_stall_rd = '0;
217 assign cpuif_req_stall_wr = '0;
218 assign cpuif_req_masked = cpuif_req
219 & !(!cpuif_req_is_wr & cpuif_req_stall_rd)
220 & !(cpuif_req_is_wr & cpuif_req_stall_wr);
221
222 //--------------------------------------------------------------------------
223 // Address Decode
224 //--------------------------------------------------------------------------
225 typedef struct {
226 logic conf;
227 logic address;
228 logic status;
229 } decoded_reg_strb_t;
230 decoded_reg_strb_t decoded_reg_strb;
231 logic decoded_err;
232 logic [3:0] decoded_addr;
233 logic decoded_req;
234 logic decoded_req_is_wr;
235 logic [31:0] decoded_wr_data;
236 logic [31:0] decoded_wr_biten;
237
238 always_comb begin
239 automatic logic is_valid_addr;
240 automatic logic is_valid_rw;
241 is_valid_addr = '1; // No valid address check
242 is_valid_rw = '1; // No valid RW check
243 decoded_reg_strb.conf = cpuif_req_masked & (cpuif_addr == 4'h0);
244 decoded_reg_strb.address = cpuif_req_masked & (cpuif_addr == 4'h4);
245 decoded_reg_strb.status = cpuif_req_masked & (cpuif_addr == 4'h8) & !cpuif_req_is_wr;
246 decoded_err = '0;
247 end
248
249 // Pass down signals to next stage
250 assign decoded_addr = cpuif_addr;
251 assign decoded_req = cpuif_req_masked;
252 assign decoded_req_is_wr = cpuif_req_is_wr;
253 assign decoded_wr_data = cpuif_wr_data;
254 assign decoded_wr_biten = cpuif_wr_biten;
255
256 //--------------------------------------------------------------------------
257 // Field logic
258 //--------------------------------------------------------------------------
259 typedef struct {
260 struct {
261 struct {
262 logic next;
263 logic load_next;
264 } enable;
265 struct {
266 logic [7:0] next;
267 logic load_next;
268 } tuser;
269 } conf;
270 struct {
271 struct {
272 logic [27:0] next;
273 logic load_next;
274 } value;
275 } address;
276 } field_combo_t;
277 field_combo_t field_combo;
278
279 typedef struct {
280 struct {
281 struct {
282 logic value;
283 } enable;
284 struct {
285 logic [7:0] value;
286 } tuser;
287 } conf;
288 struct {
289 struct {
290 logic [27:0] value;
291 } value;
292 } address;
293 } field_storage_t;
294 field_storage_t field_storage;
295
296 // Field: basic.conf.enable
297 always_comb begin
298 automatic logic [0:0] next_c;
299 automatic logic load_next_c;
300 next_c = field_storage.conf.enable.value;
301 load_next_c = '0;
302 if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
303 next_c = (field_storage.conf.enable.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]);
304 load_next_c = '1;
305 end
306 field_combo.conf.enable.next = next_c;
307 field_combo.conf.enable.load_next = load_next_c;
308 end
309 always_ff @(posedge clk) begin
310 if(rst) begin
311 field_storage.conf.enable.value <= 1'h0;
312 end else begin
313 if(field_combo.conf.enable.load_next) begin
314 field_storage.conf.enable.value <= field_combo.conf.enable.next;
315 end
316 end
317 end
318 assign hwif_out.conf.enable.value = field_storage.conf.enable.value;
319 // Field: basic.conf.tuser
320 always_comb begin
321 automatic logic [7:0] next_c;
322 automatic logic load_next_c;
323 next_c = field_storage.conf.tuser.value;
324 load_next_c = '0;
325 if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
326 next_c = (field_storage.conf.tuser.value & ~decoded_wr_biten[8:1]) | (decoded_wr_data[8:1] & decoded_wr_biten[8:1]);
327 load_next_c = '1;
328 end
329 field_combo.conf.tuser.next = next_c;
330 field_combo.conf.tuser.load_next = load_next_c;
331 end
332 always_ff @(posedge clk) begin
333 if(rst) begin
334 field_storage.conf.tuser.value <= 8'h0;
335 end else begin
336 if(field_combo.conf.tuser.load_next) begin
337 field_storage.conf.tuser.value <= field_combo.conf.tuser.next;
338 end
339 end
340 end
341 assign hwif_out.conf.tuser.value = field_storage.conf.tuser.value;
342 // Field: basic.address.value
343 always_comb begin
344 automatic logic [27:0] next_c;
345 automatic logic load_next_c;
346 next_c = field_storage.address.value.value;
347 load_next_c = '0;
348 if(decoded_reg_strb.address && decoded_req_is_wr) begin // SW write
349 next_c = (field_storage.address.value.value & ~decoded_wr_biten[27:0]) | (decoded_wr_data[27:0] & decoded_wr_biten[27:0]);
350 load_next_c = '1;
351 end
352 field_combo.address.value.next = next_c;
353 field_combo.address.value.load_next = load_next_c;
354 end
355 always_ff @(posedge clk) begin
356 if(rst) begin
357 field_storage.address.value.value <= 28'h0;
358 end else begin
359 if(field_combo.address.value.load_next) begin
360 field_storage.address.value.value <= field_combo.address.value.next;
361 end
362 end
363 end
364 assign hwif_out.address.value.value = field_storage.address.value.value;
365
366 //--------------------------------------------------------------------------
367 // Write response
368 //--------------------------------------------------------------------------
369 assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
370 // Writes are always granted with no error response
371 assign cpuif_wr_err = '0;
372
373 //--------------------------------------------------------------------------
374 // Readback
375 //--------------------------------------------------------------------------
376
377 logic [3:0] rd_mux_addr;
378 assign rd_mux_addr = decoded_addr;
379
380 logic readback_err;
381 logic readback_done;
382 logic [31:0] readback_data;
383 always_comb begin
384 automatic logic [31:0] readback_data_var;
385 readback_data_var = '0;
386 if(rd_mux_addr == 4'h0) begin
387 readback_data_var[0] = field_storage.conf.enable.value;
388 readback_data_var[8:1] = field_storage.conf.tuser.value;
389 end
390 if(rd_mux_addr == 4'h4) begin
391 readback_data_var[27:0] = field_storage.address.value.value;
392 end
393 if(rd_mux_addr == 4'h8) begin
394 readback_data_var[1:0] = hwif_in.status.state.next;
395 readback_data_var[2] = hwif_in.status.overflow.next;
396 end
397 readback_data = readback_data_var;
398 readback_done = decoded_req & ~decoded_req_is_wr;
399 readback_err = '0;
400 end
401
402 assign cpuif_rd_ack = readback_done;
403 assign cpuif_rd_data = readback_data;
404 assign cpuif_rd_err = readback_err;
405endmodule