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-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.
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