Coverage for hdl_registers/generator/vhdl/simulation/read_write_package.py: 97%
141 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl-registers project, an HDL register generator fast enough to run
5# in real time.
6# https://hdl-registers.com
7# https://github.com/hdl-registers/hdl-registers
8# --------------------------------------------------------------------------------------------------
10# Standard libraries
11from pathlib import Path
12from typing import TYPE_CHECKING, Any, Optional
14# First party libraries
15from hdl_registers.field.bit_vector import BitVector
16from hdl_registers.field.numerical_interpretation import Signed, Unsigned
18# Local folder libraries
19from .vhdl_simulation_generator_common import VhdlSimulationGeneratorCommon
21if TYPE_CHECKING:
22 # First party libraries
23 from hdl_registers.field.register_field import RegisterField
24 from hdl_registers.register import Register
25 from hdl_registers.register_array import RegisterArray
28class VhdlSimulationReadWritePackageGenerator(VhdlSimulationGeneratorCommon):
29 """
30 Generate VHDL code with register read/write procedures that simplify simulation.
31 See the :ref:`generator_vhdl` article for usage details.
33 * For each readable register, procedures that read the register value.
34 Value can be read as:
36 1. bit vector,
38 2. integer, or
40 3. native VHDL record type as given by :class:`.VhdlRecordPackageGenerator`.
42 * For each field in each readable register, a procedure that reads the natively-typed value of
43 the field.
45 * For each writeable register, a procedure that writes the register value.
46 Value can be written as:
48 1. bit vector, or
50 2. native VHDL record type as given by :class:`.VhdlRecordPackageGenerator`.
52 * For each field in each writeable register, a procedure that writes a given
53 natively-typed field value.
55 Uses VUnit Verification Component calls to create bus read/write operations.
57 The generated VHDL file needs also the generated packages from
58 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
59 """
61 __version__ = "1.1.0"
63 SHORT_DESCRIPTION = "VHDL simulation read/write package"
65 @property
66 def output_file(self) -> Path:
67 """
68 Result will be placed in this file.
69 """
70 return self.output_folder / f"{self.name}_register_read_write_pkg.vhd"
72 def create(self, **kwargs: Any) -> Path:
73 """
74 See super class for API details.
76 Overloaded here because this package file shall only be created if the register list
77 actually has any registers.
78 """
79 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
81 def get_code(self, **kwargs: Any) -> str:
82 """
83 Get a package with methods for reading/writing registers.
84 """
85 package_name = self.output_file.stem
87 vhdl = f"""\
88library ieee;
89use ieee.numeric_std.all;
90use ieee.std_logic_1164.all;
92library vunit_lib;
93use vunit_lib.bus_master_pkg.bus_master_t;
94use vunit_lib.bus_master_pkg.read_bus;
95use vunit_lib.bus_master_pkg.write_bus;
96use vunit_lib.com_types_pkg.network_t;
98library common;
99use common.addr_pkg.addr_t;
100use common.addr_pkg.addr_width;
102library register_file;
103use register_file.register_file_pkg.register_t;
104use register_file.register_file_pkg.register_width;
105use register_file.register_operations_pkg.register_bus_master;
107use work.{self.name}_regs_pkg.all;
108use work.{self.name}_register_record_pkg.all;
111package {package_name} is
113{self._declarations()}\
114end package;
116package body {package_name} is
118{self._implementations()}\
119end package body;
120"""
122 return vhdl
124 def _declarations(self) -> str:
125 """
126 Get procedure declarations for all procedures.
127 """
128 separator = self.get_separator_line(indent=2)
129 vhdl = ""
131 for register, register_array in self.iterate_registers():
132 register_name = self.qualified_register_name(
133 register=register, register_array=register_array
134 )
135 declarations = []
137 if register.mode.software_can_read:
138 # Read the register as a plain SLV.
139 signature = self._register_read_write_signature(
140 is_read_not_write=True,
141 register=register,
142 register_array=register_array,
143 value_type="register_t",
144 )
145 declarations.append(f"{signature};\n")
147 # Read the register as a plain SLV casted to integer.
148 signature = self._register_read_write_signature(
149 is_read_not_write=True,
150 register=register,
151 register_array=register_array,
152 value_type="integer",
153 )
154 declarations.append(f"{signature};\n")
156 if register.fields:
157 # Read the register as a record.
158 signature = self._register_read_write_signature(
159 is_read_not_write=True,
160 register=register,
161 register_array=register_array,
162 value_type=f"{register_name}_t",
163 )
164 declarations.append(f"{signature};\n")
166 for field in register.fields:
167 # Read the field as its native type.
168 value_type = self.field_type_name(
169 register=register, register_array=register_array, field=field
170 )
171 signature = self._field_read_write_signature(
172 is_read_not_write=True,
173 register=register,
174 register_array=register_array,
175 field=field,
176 value_type=value_type,
177 )
178 declarations.append(f"{signature};\n")
180 if self._should_be_able_to_access_field_as_integer(field=field):
181 # Read the field casted to an integer.
182 signature = self._field_read_write_signature(
183 is_read_not_write=True,
184 register=register,
185 register_array=register_array,
186 field=field,
187 value_type="integer",
188 )
189 declarations.append(f"{signature};\n")
191 if register.mode.software_can_write:
192 # Write the register as an integer.
193 signature = self._register_read_write_signature(
194 is_read_not_write=False,
195 register=register,
196 register_array=register_array,
197 value_type="integer",
198 )
199 declarations.append(f"{signature};\n")
201 if register.fields:
202 # Write the register as a record.
203 signature = self._register_read_write_signature(
204 is_read_not_write=False,
205 register=register,
206 register_array=register_array,
207 value_type=f"{register_name}_t",
208 )
209 declarations.append(f"{signature};\n")
210 else:
211 # Write the register as a plain SLV.
212 # This one is made available only if there are no fields.
213 # This is because there can be a signature ambiguity if both are available
214 # that some compilers can not resolve.
215 # Namely e.g. value=>(field_name => '1').
216 # Where the field is a std_logic.
217 # GHDL gets confused in this case between using the signature with the record
218 # or the one with SLV.
219 signature = self._register_read_write_signature(
220 is_read_not_write=False,
221 register=register,
222 register_array=register_array,
223 value_type="register_t",
224 )
225 declarations.append(f"{signature};\n")
227 for field in register.fields:
228 # Write the field as its native type.
229 value_type = self.field_type_name(
230 register=register, register_array=register_array, field=field
231 )
232 signature = self._field_read_write_signature(
233 is_read_not_write=False,
234 register=register,
235 register_array=register_array,
236 field=field,
237 value_type=value_type,
238 )
239 declarations.append(f"{signature};\n")
241 if self._should_be_able_to_access_field_as_integer(field=field):
242 # Write the field casted to an integer.
243 signature = self._field_read_write_signature(
244 is_read_not_write=False,
245 register=register,
246 register_array=register_array,
247 field=field,
248 value_type="integer",
249 )
250 declarations.append(f"{signature};\n")
252 vhdl += separator
253 vhdl += "\n".join(declarations)
254 vhdl += separator
255 vhdl += "\n"
257 return vhdl
259 def _register_read_write_signature(
260 self,
261 is_read_not_write: bool,
262 register: "Register",
263 register_array: Optional["RegisterArray"],
264 value_type: str,
265 ) -> str:
266 """
267 Get signature for a 'read_reg'/'write_reg' procedure.
268 """
269 direction = "read" if is_read_not_write else "write"
270 value_direction = "out" if is_read_not_write else "in"
272 register_name = self.qualified_register_name(
273 register=register, register_array=register_array
274 )
275 register_description = self.register_description(
276 register=register, register_array=register_array
277 )
278 # If it is not either of these, then it is the native type which shall not have a comment
279 # since it is the default.
280 type_comment = (
281 " as a plain 'register_t'"
282 if value_type == "register_t"
283 else " as an 'integer'" if value_type == "integer" else ""
284 )
286 return f"""\
287 -- {direction.capitalize()} the {register_description}{type_comment}.
288 procedure {direction}_{register_name}(
289 signal net : inout network_t;
290{self.get_array_index_port(register_array=register_array)}\
291 value : {value_direction} {value_type};
292 base_address : in addr_t := (others => '0');
293 bus_handle : in bus_master_t := register_bus_master
294 )\
295"""
297 def _field_read_write_signature(
298 self,
299 is_read_not_write: bool,
300 register: "Register",
301 register_array: Optional["RegisterArray"],
302 field: "RegisterField",
303 value_type: str,
304 ) -> str:
305 """
306 Get signature for a 'read_field'/'write_field' procedure.
307 """
308 direction = "read" if is_read_not_write else "write"
310 field_name = self.qualified_field_name(
311 register=register, register_array=register_array, field=field
312 )
313 field_description = self.field_description(
314 register=register, field=field, register_array=register_array
315 )
316 # If its not integer, then it is the native type which shall shall not have a comment
317 # since it is the default.
318 type_comment = " as an 'integer'" if value_type == "integer" else ""
320 comment = ""
321 if not is_read_not_write:
322 if self.field_setter_should_read_modify_write(register=register):
323 comment = (
324 " -- Will read-modify-write the register to set the field to the "
325 "supplied 'value'.\n"
326 )
327 else:
328 comment = (
329 " -- Will write the whole register, with the field set to the \n"
330 " -- supplied 'value' and everything else set to default.\n"
331 )
333 value_direction = "out" if is_read_not_write else "in"
335 return f"""\
336 -- {direction.capitalize()} the {field_description}{type_comment}.
337{comment}\
338 procedure {direction}_{field_name}(
339 signal net : inout network_t;
340{self.get_array_index_port(register_array=register_array)}\
341 value : {value_direction} {value_type};
342 base_address : in addr_t := (others => '0');
343 bus_handle : in bus_master_t := register_bus_master
344 )\
345"""
347 @staticmethod
348 def _should_be_able_to_access_field_as_integer(field: "RegisterField") -> bool:
349 """
350 Return True if the field is of a type where there should be procedures to read/write it
351 casted as an integer.
352 """
353 return isinstance(field, BitVector) and isinstance(
354 field.numerical_interpretation, (Signed, Unsigned)
355 )
357 def _implementations(self) -> str:
358 """
359 Get implementations of all procedures.
360 """
361 separator = self.get_separator_line(indent=2)
362 vhdl = ""
364 for register, register_array in self.iterate_registers():
365 register_name = self.qualified_register_name(
366 register=register, register_array=register_array
367 )
368 implementations = []
370 if register.mode.software_can_read:
371 # Read the register as a plain SLV.
372 implementations.append(
373 self._register_read_implementation(
374 register=register,
375 register_array=register_array,
376 value_type="register_t",
377 value_conversion="reg_value",
378 )
379 )
381 # Read the register as a plain SLV casted to integer.
382 implementations.append(
383 self._register_read_implementation(
384 register=register,
385 register_array=register_array,
386 value_type="integer",
387 value_conversion="to_integer(unsigned(reg_value))",
388 )
389 )
391 if register.fields:
392 # Read the register as a record.
393 implementations.append(
394 self._register_read_implementation(
395 register=register,
396 register_array=register_array,
397 value_type=f"{register_name}_t",
398 value_conversion=f"to_{register_name}(reg_value)",
399 )
400 )
402 for field in register.fields:
403 # Read the field as its native type.
404 value_type = self.field_type_name(
405 register=register, register_array=register_array, field=field
406 )
407 implementations.append(
408 self._field_read_implementation(
409 register=register,
410 register_array=register_array,
411 field=field,
412 value_type=value_type,
413 value_conversion=f"reg_value.{field.name}",
414 )
415 )
417 if self._should_be_able_to_access_field_as_integer(field=field):
418 # Read the field casted to an integer.
419 implementations.append(
420 self._field_read_implementation(
421 register=register,
422 register_array=register_array,
423 field=field,
424 value_type="integer",
425 value_conversion=f"to_integer(reg_value.{field.name})",
426 )
427 )
429 if register.mode.software_can_write:
430 # Write the register as an integer.
431 implementations.append(
432 self._register_write_implementation(
433 register=register,
434 register_array=register_array,
435 value_type="integer",
436 value_conversion="std_ulogic_vector(to_unsigned(value, register_width))",
437 )
438 )
440 if register.fields:
441 # Write the register as a record.
442 implementations.append(
443 self._register_write_implementation(
444 register=register,
445 register_array=register_array,
446 value_type=f"{register_name}_t",
447 value_conversion="to_slv(value)",
448 )
449 )
450 else:
451 # Write the register as a plain SLV.
452 # Only if there are no fields.
453 # See the signatures method for more info.
454 implementations.append(
455 self._register_write_implementation(
456 register=register,
457 register_array=register_array,
458 value_type="register_t",
459 value_conversion="value",
460 )
461 )
463 for field in register.fields:
464 # Write the field as its native type.
465 value_type = self.field_type_name(
466 register=register, register_array=register_array, field=field
467 )
468 implementations.append(
469 self._field_write_implementation(
470 register=register,
471 register_array=register_array,
472 field=field,
473 value_type=value_type,
474 )
475 )
477 if self._should_be_able_to_access_field_as_integer(field=field):
478 # Read the field casted to an integer.
479 implementations.append(
480 self._field_write_implementation(
481 register=register,
482 register_array=register_array,
483 field=field,
484 value_type="integer",
485 )
486 )
488 vhdl += separator
489 vhdl += "\n".join(implementations)
490 vhdl += separator
491 vhdl += "\n"
493 return vhdl
495 def _register_read_implementation(
496 self,
497 register: "Register",
498 register_array: Optional["RegisterArray"],
499 value_type: str,
500 value_conversion: str,
501 ) -> str:
502 """
503 Get implementation for a 'read_reg' procedure.
504 """
505 signature = self._register_read_write_signature(
506 is_read_not_write=True,
507 register=register,
508 register_array=register_array,
509 value_type=value_type,
510 )
512 return f"""\
513{signature} is
514{self.reg_index_constant(register=register, register_array=register_array)}\
515{self.reg_address_constant()}\
516 variable reg_value : register_t := (others => '0');
517 begin
518 read_bus(
519 net => net,
520 bus_handle => bus_handle,
521 address => std_logic_vector(reg_address),
522 data => reg_value
523 );
524 value := {value_conversion};
525 end procedure;
526"""
528 def _register_write_implementation(
529 self,
530 register: "Register",
531 register_array: Optional["RegisterArray"],
532 value_type: str,
533 value_conversion: str,
534 ) -> str:
535 """
536 Get implementation for a 'write_reg' procedure.
537 """
538 signature = self._register_read_write_signature(
539 is_read_not_write=False,
540 register=register,
541 register_array=register_array,
542 value_type=value_type,
543 )
545 return f"""\
546{signature} is
547{self.reg_index_constant(register=register, register_array=register_array)}\
548{self.reg_address_constant()}\
549 constant reg_value : register_t := {value_conversion};
550 begin
551 write_bus(
552 net => net,
553 bus_handle => bus_handle,
554 address => std_logic_vector(reg_address),
555 data => reg_value
556 );
557 end procedure;
558"""
560 def _field_read_implementation(
561 self,
562 register: "Register",
563 register_array: Optional["RegisterArray"],
564 field: "RegisterField",
565 value_type: str,
566 value_conversion: str,
567 ) -> str:
568 """
569 Get implementation for a 'read_field' procedure.
570 """
571 signature = self._field_read_write_signature(
572 is_read_not_write=True,
573 register=register,
574 register_array=register_array,
575 field=field,
576 value_type=value_type,
577 )
579 register_name = self.qualified_register_name(
580 register=register, register_array=register_array
581 )
583 return f"""\
584{signature} is
585 variable reg_value : {register_name}_t := {register_name}_init;
586 begin
587 read_{register_name}(
588 net => net,
589{self.get_array_index_association(register_array=register_array)}\
590 value => reg_value,
591 base_address => base_address,
592 bus_handle => bus_handle
593 );
594 value := {value_conversion};
595 end procedure;
596"""
598 def _field_write_implementation(
599 self,
600 register: "Register",
601 register_array: Optional["RegisterArray"],
602 field: "RegisterField",
603 value_type: str,
604 ) -> str:
605 """
606 Get implementation for a 'write_field' procedure.
607 """
608 signature = self._field_read_write_signature(
609 is_read_not_write=False,
610 register=register,
611 register_array=register_array,
612 field=field,
613 value_type=value_type,
614 )
615 register_name = self.qualified_register_name(
616 register=register, register_array=register_array
617 )
619 if self.field_setter_should_read_modify_write(register=register):
620 set_base_value = f"""\
621 read_{register_name}(
622 net => net,
623{self.get_array_index_association(register_array=register_array)}\
624 value => reg_value,
625 base_address => base_address,
626 bus_handle => bus_handle
627 );
628"""
629 else:
630 set_base_value = ""
632 if value_type == "integer":
633 field_name = self.qualified_field_name(
634 register=register, register_array=register_array, field=field
635 )
636 field_width = f"{field_name}_width"
638 if isinstance(field, BitVector) and isinstance(
639 field.numerical_interpretation, Unsigned
640 ):
641 field_conversion = f"to_unsigned(value, {field_width})"
642 elif isinstance(field, BitVector) and isinstance(
643 field.numerical_interpretation, Signed
644 ):
645 field_conversion = f"to_signed(value, {field_width})"
646 else:
647 raise ValueError(f"Should not end up here for field: {field}")
648 else:
649 field_conversion = "value"
651 return f"""\
652{signature} is
653 variable reg_value : {register_name}_t := {register_name}_init;
654 begin
655{set_base_value}\
656 reg_value.{field.name} := {field_conversion};
658 write_{register_name}(
659 net => net,
660{self.get_array_index_association(register_array=register_array)}\
661 value => reg_value,
662 base_address => base_address,
663 bus_handle => bus_handle
664 );
665 end procedure;
666"""