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 7.3.1-dev.
3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
4// Generated 2025-04-26 09:24 from file regs_basic.toml at Git commit e16bd7741c27.
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
18
19 typedef struct {
20 logic [1:0] next;
21 } xtern__basic__status__state__in_t__in_t;
22
23 typedef struct {
24 logic next;
25 } xtern__basic__status__overflow__in_t__in_t;
26
27 typedef struct {
28 xtern__basic__status__state__in_t__in_t state;
29 xtern__basic__status__overflow__in_t__in_t overflow;
30 } xtern__basic__status__in_t__in_t;
31
32 typedef struct {
33 xtern__basic__status__in_t__in_t status;
34 } basic__in_t;
35
36 typedef struct {
37 logic value;
38 } xtern__basic__conf__enable__out_t__out_t;
39
40 typedef struct {
41 logic [7:0] value;
42 } xtern__basic__conf__tuser__out_t__out_t;
43
44 typedef struct {
45 xtern__basic__conf__enable__out_t__out_t enable;
46 xtern__basic__conf__tuser__out_t__out_t tuser;
47 } xtern__basic__conf__out_t__out_t;
48
49 typedef struct {
50 logic [27:0] value;
51 } xtern__basic__address__value__out_t__out_t;
52
53 typedef struct {
54 xtern__basic__address__value__out_t__out_t value;
55 } xtern__basic__address__out_t__out_t;
56
57 typedef struct {
58 xtern__basic__conf__out_t__out_t conf;
59 xtern__basic__address__out_t__out_t address;
60 } basic__out_t;
61
62 typedef enum logic [1:0] {
63 basic_status_state__idle = 'h0,
64 basic_status_state__processing = 'h1,
65 basic_status_state__sending_out = 'h2
66 } basic_status_state_e;
67endpackage
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 7.3.1-dev.
3// Code generator SystemVerilogAxiLiteGenerator version 0.0.1.
4// Generated 2025-04-26 09:24 from file regs_basic.toml at Git commit e16bd7741c27.
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($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($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 struct {
152 logic is_wr;
153 logic err;
154 logic [31:0] rdata;
155 } axil_resp_buffer[2];
156
157 logic [1:0] axil_resp_wptr;
158 logic [1:0] axil_resp_rptr;
159
160 always_ff @(posedge clk) begin
161 if(rst) begin
162 for(int i=0; i<2; i++) begin
163 axil_resp_buffer[i].is_wr <= '0;
164 axil_resp_buffer[i].err <= '0;
165 axil_resp_buffer[i].rdata <= '0;
166 end
167 axil_resp_wptr <= '0;
168 axil_resp_rptr <= '0;
169 end else begin
170 // Store responses in buffer until AXI response channel accepts them
171 if(cpuif_rd_ack || cpuif_wr_ack) begin
172 if(cpuif_rd_ack) begin
173 axil_resp_buffer[axil_resp_wptr[0:0]].is_wr <= '0;
174 axil_resp_buffer[axil_resp_wptr[0:0]].err <= cpuif_rd_err;
175 axil_resp_buffer[axil_resp_wptr[0:0]].rdata <= cpuif_rd_data;
176
177 end else if(cpuif_wr_ack) begin
178 axil_resp_buffer[axil_resp_wptr[0:0]].is_wr <= '1;
179 axil_resp_buffer[axil_resp_wptr[0:0]].err <= cpuif_wr_err;
180 end
181 axil_resp_wptr <= axil_resp_wptr + 1'b1;
182 end
183
184 // Advance read pointer when acknowledged
185 if(axil_resp_acked) begin
186 axil_resp_rptr <= axil_resp_rptr + 1'b1;
187 end
188 end
189 end
190
191 always_comb begin
192 axil_resp_acked = '0;
193 s_axil.BVALID = '0;
194 s_axil.RVALID = '0;
195 if(axil_resp_rptr != axil_resp_wptr) begin
196 if(axil_resp_buffer[axil_resp_rptr[0:0]].is_wr) begin
197 s_axil.BVALID = '1;
198 if(s_axil.BREADY) axil_resp_acked = '1;
199 end else begin
200 s_axil.RVALID = '1;
201 if(s_axil.RREADY) axil_resp_acked = '1;
202 end
203 end
204
205 s_axil.RDATA = axil_resp_buffer[axil_resp_rptr[0:0]].rdata;
206 if(axil_resp_buffer[axil_resp_rptr[0:0]].err) begin
207 s_axil.BRESP = 2'b10;
208 s_axil.RRESP = 2'b10;
209 end else begin
210 s_axil.BRESP = 2'b00;
211 s_axil.RRESP = 2'b00;
212 end
213 end
214
215 logic cpuif_req_masked;
216
217 // Read & write latencies are balanced. Stalls not required
218 assign cpuif_req_stall_rd = '0;
219 assign cpuif_req_stall_wr = '0;
220 assign cpuif_req_masked = cpuif_req
221 & !(!cpuif_req_is_wr & cpuif_req_stall_rd)
222 & !(cpuif_req_is_wr & cpuif_req_stall_wr);
223
224 //--------------------------------------------------------------------------
225 // Address Decode
226 //--------------------------------------------------------------------------
227 typedef struct {
228 logic conf;
229 logic address;
230 logic status;
231 } decoded_reg_strb_t;
232 decoded_reg_strb_t decoded_reg_strb;
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 decoded_reg_strb.conf = cpuif_req_masked & (cpuif_addr == 4'h0);
240 decoded_reg_strb.address = cpuif_req_masked & (cpuif_addr == 4'h4);
241 decoded_reg_strb.status = cpuif_req_masked & (cpuif_addr == 4'h8);
242 end
243
244 // Pass down signals to next stage
245 assign decoded_req = cpuif_req_masked;
246 assign decoded_req_is_wr = cpuif_req_is_wr;
247 assign decoded_wr_data = cpuif_wr_data;
248 assign decoded_wr_biten = cpuif_wr_biten;
249
250 //--------------------------------------------------------------------------
251 // Field logic
252 //--------------------------------------------------------------------------
253 typedef struct {
254 struct {
255 struct {
256 logic next;
257 logic load_next;
258 } enable;
259 struct {
260 logic [7:0] next;
261 logic load_next;
262 } tuser;
263 } conf;
264 struct {
265 struct {
266 logic [27:0] next;
267 logic load_next;
268 } value;
269 } address;
270 } field_combo_t;
271 field_combo_t field_combo;
272
273 typedef struct {
274 struct {
275 struct {
276 logic value;
277 } enable;
278 struct {
279 logic [7:0] value;
280 } tuser;
281 } conf;
282 struct {
283 struct {
284 logic [27:0] value;
285 } value;
286 } address;
287 } field_storage_t;
288 field_storage_t field_storage;
289
290 // Field: basic.conf.enable
291 always_comb begin
292 automatic logic [0:0] next_c;
293 automatic logic load_next_c;
294 next_c = field_storage.conf.enable.value;
295 load_next_c = '0;
296 if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
297 next_c = (field_storage.conf.enable.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]);
298 load_next_c = '1;
299 end
300 field_combo.conf.enable.next = next_c;
301 field_combo.conf.enable.load_next = load_next_c;
302 end
303 always_ff @(posedge clk) begin
304 if(rst) begin
305 field_storage.conf.enable.value <= 1'h0;
306 end else begin
307 if(field_combo.conf.enable.load_next) begin
308 field_storage.conf.enable.value <= field_combo.conf.enable.next;
309 end
310 end
311 end
312 assign hwif_out.conf.enable.value = field_storage.conf.enable.value;
313 // Field: basic.conf.tuser
314 always_comb begin
315 automatic logic [7:0] next_c;
316 automatic logic load_next_c;
317 next_c = field_storage.conf.tuser.value;
318 load_next_c = '0;
319 if(decoded_reg_strb.conf && decoded_req_is_wr) begin // SW write
320 next_c = (field_storage.conf.tuser.value & ~decoded_wr_biten[8:1]) | (decoded_wr_data[8:1] & decoded_wr_biten[8:1]);
321 load_next_c = '1;
322 end
323 field_combo.conf.tuser.next = next_c;
324 field_combo.conf.tuser.load_next = load_next_c;
325 end
326 always_ff @(posedge clk) begin
327 if(rst) begin
328 field_storage.conf.tuser.value <= 8'h0;
329 end else begin
330 if(field_combo.conf.tuser.load_next) begin
331 field_storage.conf.tuser.value <= field_combo.conf.tuser.next;
332 end
333 end
334 end
335 assign hwif_out.conf.tuser.value = field_storage.conf.tuser.value;
336 // Field: basic.address.value
337 always_comb begin
338 automatic logic [27:0] next_c;
339 automatic logic load_next_c;
340 next_c = field_storage.address.value.value;
341 load_next_c = '0;
342 if(decoded_reg_strb.address && decoded_req_is_wr) begin // SW write
343 next_c = (field_storage.address.value.value & ~decoded_wr_biten[27:0]) | (decoded_wr_data[27:0] & decoded_wr_biten[27:0]);
344 load_next_c = '1;
345 end
346 field_combo.address.value.next = next_c;
347 field_combo.address.value.load_next = load_next_c;
348 end
349 always_ff @(posedge clk) begin
350 if(rst) begin
351 field_storage.address.value.value <= 28'h0;
352 end else begin
353 if(field_combo.address.value.load_next) begin
354 field_storage.address.value.value <= field_combo.address.value.next;
355 end
356 end
357 end
358 assign hwif_out.address.value.value = field_storage.address.value.value;
359
360 //--------------------------------------------------------------------------
361 // Write response
362 //--------------------------------------------------------------------------
363 assign cpuif_wr_ack = decoded_req & decoded_req_is_wr;
364 // Writes are always granted with no error response
365 assign cpuif_wr_err = '0;
366
367 //--------------------------------------------------------------------------
368 // Readback
369 //--------------------------------------------------------------------------
370
371 logic readback_err;
372 logic readback_done;
373 logic [31:0] readback_data;
374
375 // Assign readback values to a flattened array
376 logic [31:0] readback_array[3];
377 assign readback_array[0][0:0] = (decoded_reg_strb.conf && !decoded_req_is_wr) ? field_storage.conf.enable.value : '0;
378 assign readback_array[0][8:1] = (decoded_reg_strb.conf && !decoded_req_is_wr) ? field_storage.conf.tuser.value : '0;
379 assign readback_array[0][31:9] = '0;
380 assign readback_array[1][27:0] = (decoded_reg_strb.address && !decoded_req_is_wr) ? field_storage.address.value.value : '0;
381 assign readback_array[1][31:28] = '0;
382 assign readback_array[2][1:0] = (decoded_reg_strb.status && !decoded_req_is_wr) ? hwif_in.status.state.next : '0;
383 assign readback_array[2][2:2] = (decoded_reg_strb.status && !decoded_req_is_wr) ? hwif_in.status.overflow.next : '0;
384 assign readback_array[2][31:3] = '0;
385
386 // Reduce the array
387 always_comb begin
388 automatic logic [31:0] readback_data_var;
389 readback_done = decoded_req & ~decoded_req_is_wr;
390 readback_err = '0;
391 readback_data_var = '0;
392 for(int i=0; i<3; i++) readback_data_var |= readback_array[i];
393 readback_data = readback_data_var;
394 end
395
396 assign cpuif_rd_ack = readback_done;
397 assign cpuif_rd_data = readback_data;
398 assign cpuif_rd_err = readback_err;
399endmodule