Coverage for hdl_registers/generator/vhdl/simulation/read_write_package.py: 97%
141 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +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"""\
88{self.header}
89library ieee;
90use ieee.numeric_std.all;
91use ieee.std_logic_1164.all;
93library vunit_lib;
94use vunit_lib.bus_master_pkg.bus_master_t;
95use vunit_lib.bus_master_pkg.read_bus;
96use vunit_lib.bus_master_pkg.write_bus;
97use vunit_lib.com_types_pkg.network_t;
99library common;
100use common.addr_pkg.addr_t;
101use common.addr_pkg.addr_width;
103library reg_file;
104use reg_file.reg_file_pkg.reg_t;
105use reg_file.reg_file_pkg.reg_width;
106use reg_file.reg_operations_pkg.regs_bus_master;
108use work.{self.name}_regs_pkg.all;
109use work.{self.name}_register_record_pkg.all;
112package {package_name} is
114{self._declarations()}\
115end package;
117package body {package_name} is
119{self._implementations()}\
120end package body;
121"""
123 return vhdl
125 def _declarations(self) -> str:
126 """
127 Get procedure declarations for all procedures.
128 """
129 separator = self.get_separator_line(indent=2)
130 vhdl = ""
132 for register, register_array in self.iterate_registers():
133 register_name = self.qualified_register_name(
134 register=register, register_array=register_array
135 )
136 declarations = []
138 if register.mode.software_can_read:
139 # Read the register as a plain SLV.
140 signature = self._register_read_write_signature(
141 is_read_not_write=True,
142 register=register,
143 register_array=register_array,
144 value_type="reg_t",
145 )
146 declarations.append(f"{signature};\n")
148 # Read the register as a plain SLV casted to integer.
149 signature = self._register_read_write_signature(
150 is_read_not_write=True,
151 register=register,
152 register_array=register_array,
153 value_type="integer",
154 )
155 declarations.append(f"{signature};\n")
157 if register.fields:
158 # Read the register as a record.
159 signature = self._register_read_write_signature(
160 is_read_not_write=True,
161 register=register,
162 register_array=register_array,
163 value_type=f"{register_name}_t",
164 )
165 declarations.append(f"{signature};\n")
167 for field in register.fields:
168 # Read the field as its native type.
169 value_type = self.field_type_name(
170 register=register, register_array=register_array, field=field
171 )
172 signature = self._field_read_write_signature(
173 is_read_not_write=True,
174 register=register,
175 register_array=register_array,
176 field=field,
177 value_type=value_type,
178 )
179 declarations.append(f"{signature};\n")
181 if self._should_be_able_to_access_field_as_integer(field=field):
182 # Read the field casted to an integer.
183 signature = self._field_read_write_signature(
184 is_read_not_write=True,
185 register=register,
186 register_array=register_array,
187 field=field,
188 value_type="integer",
189 )
190 declarations.append(f"{signature};\n")
192 if register.mode.software_can_write:
193 # Write the register as an integer.
194 signature = self._register_read_write_signature(
195 is_read_not_write=False,
196 register=register,
197 register_array=register_array,
198 value_type="integer",
199 )
200 declarations.append(f"{signature};\n")
202 if register.fields:
203 # Write the register as a record.
204 signature = self._register_read_write_signature(
205 is_read_not_write=False,
206 register=register,
207 register_array=register_array,
208 value_type=f"{register_name}_t",
209 )
210 declarations.append(f"{signature};\n")
211 else:
212 # Write the register as a plain SLV.
213 # This one is made available only if there are no fields.
214 # This is because there can be a signature ambiguity if both are available
215 # that some compilers can not resolve.
216 # Namely e.g. value=>(field_name => '1').
217 # Where the field is a std_logic.
218 # GHDL gets confused in this case between using the signature with the record
219 # or the one with SLV.
220 signature = self._register_read_write_signature(
221 is_read_not_write=False,
222 register=register,
223 register_array=register_array,
224 value_type="reg_t",
225 )
226 declarations.append(f"{signature};\n")
228 for field in register.fields:
229 # Write the field as its native type.
230 value_type = self.field_type_name(
231 register=register, register_array=register_array, field=field
232 )
233 signature = self._field_read_write_signature(
234 is_read_not_write=False,
235 register=register,
236 register_array=register_array,
237 field=field,
238 value_type=value_type,
239 )
240 declarations.append(f"{signature};\n")
242 if self._should_be_able_to_access_field_as_integer(field=field):
243 # Write the field casted to an integer.
244 signature = self._field_read_write_signature(
245 is_read_not_write=False,
246 register=register,
247 register_array=register_array,
248 field=field,
249 value_type="integer",
250 )
251 declarations.append(f"{signature};\n")
253 vhdl += separator
254 vhdl += "\n".join(declarations)
255 vhdl += separator
256 vhdl += "\n"
258 return vhdl
260 def _register_read_write_signature(
261 self,
262 is_read_not_write: bool,
263 register: "Register",
264 register_array: Optional["RegisterArray"],
265 value_type: str,
266 ) -> str:
267 """
268 Get signature for a 'read_reg'/'write_reg' procedure.
269 """
270 direction = "read" if is_read_not_write else "write"
271 value_direction = "out" if is_read_not_write else "in"
273 register_name = self.qualified_register_name(
274 register=register, register_array=register_array
275 )
276 register_description = self.register_description(
277 register=register, register_array=register_array
278 )
279 # If it is not either of these, then it is the native type which shall not have a comment
280 # since it is the default.
281 type_comment = (
282 " as a plain 'reg_t'"
283 if value_type == "reg_t"
284 else " as an 'integer'" if value_type == "integer" else ""
285 )
287 return f"""\
288 -- {direction.capitalize()} the {register_description}{type_comment}.
289 procedure {direction}_{register_name}(
290 signal net : inout network_t;
291{self.get_array_index_port(register_array=register_array)}\
292 value : {value_direction} {value_type};
293 base_address : in addr_t := (others => '0');
294 bus_handle : in bus_master_t := regs_bus_master
295 )\
296"""
298 def _field_read_write_signature(
299 self,
300 is_read_not_write: bool,
301 register: "Register",
302 register_array: Optional["RegisterArray"],
303 field: "RegisterField",
304 value_type: str,
305 ) -> str:
306 """
307 Get signature for a 'read_field'/'write_field' procedure.
308 """
309 direction = "read" if is_read_not_write else "write"
311 field_name = self.qualified_field_name(
312 register=register, register_array=register_array, field=field
313 )
314 field_description = self.field_description(
315 register=register, field=field, register_array=register_array
316 )
317 # If its not integer, then it is the native type which shall shall not have a comment
318 # since it is the default.
319 type_comment = " as an 'integer'" if value_type == "integer" else ""
321 comment = ""
322 if not is_read_not_write:
323 if self.field_setter_should_read_modify_write(register=register):
324 comment = (
325 " -- Will read-modify-write the register to set the field to the "
326 "supplied 'value'.\n"
327 )
328 else:
329 comment = (
330 " -- Will write the whole register, with the field set to the \n"
331 " -- supplied 'value' and everything else set to default.\n"
332 )
334 value_direction = "out" if is_read_not_write else "in"
336 return f"""\
337 -- {direction.capitalize()} the {field_description}{type_comment}.
338{comment}\
339 procedure {direction}_{field_name}(
340 signal net : inout network_t;
341{self.get_array_index_port(register_array=register_array)}\
342 value : {value_direction} {value_type};
343 base_address : in addr_t := (others => '0');
344 bus_handle : in bus_master_t := regs_bus_master
345 )\
346"""
348 @staticmethod
349 def _should_be_able_to_access_field_as_integer(field: "RegisterField") -> bool:
350 """
351 Return True if the field is of a type where there should be procedures to read/write it
352 casted as an integer.
353 """
354 return isinstance(field, BitVector) and isinstance(
355 field.numerical_interpretation, (Signed, Unsigned)
356 )
358 def _implementations(self) -> str:
359 """
360 Get implementations of all procedures.
361 """
362 separator = self.get_separator_line(indent=2)
363 vhdl = ""
365 for register, register_array in self.iterate_registers():
366 register_name = self.qualified_register_name(
367 register=register, register_array=register_array
368 )
369 implementations = []
371 if register.mode.software_can_read:
372 # Read the register as a plain SLV.
373 implementations.append(
374 self._register_read_implementation(
375 register=register,
376 register_array=register_array,
377 value_type="reg_t",
378 value_conversion="reg_value",
379 )
380 )
382 # Read the register as a plain SLV casted to integer.
383 implementations.append(
384 self._register_read_implementation(
385 register=register,
386 register_array=register_array,
387 value_type="integer",
388 value_conversion="to_integer(unsigned(reg_value))",
389 )
390 )
392 if register.fields:
393 # Read the register as a record.
394 implementations.append(
395 self._register_read_implementation(
396 register=register,
397 register_array=register_array,
398 value_type=f"{register_name}_t",
399 value_conversion=f"to_{register_name}(reg_value)",
400 )
401 )
403 for field in register.fields:
404 # Read the field as its native type.
405 value_type = self.field_type_name(
406 register=register, register_array=register_array, field=field
407 )
408 implementations.append(
409 self._field_read_implementation(
410 register=register,
411 register_array=register_array,
412 field=field,
413 value_type=value_type,
414 value_conversion=f"reg_value.{field.name}",
415 )
416 )
418 if self._should_be_able_to_access_field_as_integer(field=field):
419 # Read the field casted to an integer.
420 implementations.append(
421 self._field_read_implementation(
422 register=register,
423 register_array=register_array,
424 field=field,
425 value_type="integer",
426 value_conversion=f"to_integer(reg_value.{field.name})",
427 )
428 )
430 if register.mode.software_can_write:
431 # Write the register as an integer.
432 implementations.append(
433 self._register_write_implementation(
434 register=register,
435 register_array=register_array,
436 value_type="integer",
437 value_conversion="std_ulogic_vector(to_unsigned(value, reg_width))",
438 )
439 )
441 if register.fields:
442 # Write the register as a record.
443 implementations.append(
444 self._register_write_implementation(
445 register=register,
446 register_array=register_array,
447 value_type=f"{register_name}_t",
448 value_conversion="to_slv(value)",
449 )
450 )
451 else:
452 # Write the register as a plain SLV.
453 # Only if there are no fields.
454 # See the signatures method for more info.
455 implementations.append(
456 self._register_write_implementation(
457 register=register,
458 register_array=register_array,
459 value_type="reg_t",
460 value_conversion="value",
461 )
462 )
464 for field in register.fields:
465 # Write the field as its native type.
466 value_type = self.field_type_name(
467 register=register, register_array=register_array, field=field
468 )
469 implementations.append(
470 self._field_write_implementation(
471 register=register,
472 register_array=register_array,
473 field=field,
474 value_type=value_type,
475 )
476 )
478 if self._should_be_able_to_access_field_as_integer(field=field):
479 # Read the field casted to an integer.
480 implementations.append(
481 self._field_write_implementation(
482 register=register,
483 register_array=register_array,
484 field=field,
485 value_type="integer",
486 )
487 )
489 vhdl += separator
490 vhdl += "\n".join(implementations)
491 vhdl += separator
492 vhdl += "\n"
494 return vhdl
496 def _register_read_implementation(
497 self,
498 register: "Register",
499 register_array: Optional["RegisterArray"],
500 value_type: str,
501 value_conversion: str,
502 ) -> str:
503 """
504 Get implementation for a 'read_reg' procedure.
505 """
506 signature = self._register_read_write_signature(
507 is_read_not_write=True,
508 register=register,
509 register_array=register_array,
510 value_type=value_type,
511 )
513 return f"""\
514{signature} is
515{self.reg_index_constant(register=register, register_array=register_array)}\
516{self.reg_address_constant()}\
517 variable reg_value : reg_t := (others => '0');
518 begin
519 read_bus(
520 net => net,
521 bus_handle => bus_handle,
522 address => std_logic_vector(reg_address),
523 data => reg_value
524 );
525 value := {value_conversion};
526 end procedure;
527"""
529 def _register_write_implementation(
530 self,
531 register: "Register",
532 register_array: Optional["RegisterArray"],
533 value_type: str,
534 value_conversion: str,
535 ) -> str:
536 """
537 Get implementation for a 'write_reg' procedure.
538 """
539 signature = self._register_read_write_signature(
540 is_read_not_write=False,
541 register=register,
542 register_array=register_array,
543 value_type=value_type,
544 )
546 return f"""\
547{signature} is
548{self.reg_index_constant(register=register, register_array=register_array)}\
549{self.reg_address_constant()}\
550 constant reg_value : reg_t := {value_conversion};
551 begin
552 write_bus(
553 net => net,
554 bus_handle => bus_handle,
555 address => std_logic_vector(reg_address),
556 data => reg_value
557 );
558 end procedure;
559"""
561 def _field_read_implementation(
562 self,
563 register: "Register",
564 register_array: Optional["RegisterArray"],
565 field: "RegisterField",
566 value_type: str,
567 value_conversion: str,
568 ) -> str:
569 """
570 Get implementation for a 'read_field' procedure.
571 """
572 signature = self._field_read_write_signature(
573 is_read_not_write=True,
574 register=register,
575 register_array=register_array,
576 field=field,
577 value_type=value_type,
578 )
580 register_name = self.qualified_register_name(
581 register=register, register_array=register_array
582 )
584 return f"""\
585{signature} is
586 variable reg_value : {register_name}_t := {register_name}_init;
587 begin
588 read_{register_name}(
589 net => net,
590{self.get_array_index_association(register_array=register_array)}\
591 value => reg_value,
592 base_address => base_address,
593 bus_handle => bus_handle
594 );
595 value := {value_conversion};
596 end procedure;
597"""
599 def _field_write_implementation(
600 self,
601 register: "Register",
602 register_array: Optional["RegisterArray"],
603 field: "RegisterField",
604 value_type: str,
605 ) -> str:
606 """
607 Get implementation for a 'write_field' procedure.
608 """
609 signature = self._field_read_write_signature(
610 is_read_not_write=False,
611 register=register,
612 register_array=register_array,
613 field=field,
614 value_type=value_type,
615 )
616 register_name = self.qualified_register_name(
617 register=register, register_array=register_array
618 )
620 if self.field_setter_should_read_modify_write(register=register):
621 set_base_value = f"""\
622 read_{register_name}(
623 net => net,
624{self.get_array_index_association(register_array=register_array)}\
625 value => reg_value,
626 base_address => base_address,
627 bus_handle => bus_handle
628 );
629"""
630 else:
631 set_base_value = ""
633 if value_type == "integer":
634 field_name = self.qualified_field_name(
635 register=register, register_array=register_array, field=field
636 )
637 field_width = f"{field_name}_width"
639 if isinstance(field, BitVector) and isinstance(
640 field.numerical_interpretation, Unsigned
641 ):
642 field_conversion = f"to_unsigned(value, {field_width})"
643 elif isinstance(field, BitVector) and isinstance(
644 field.numerical_interpretation, Signed
645 ):
646 field_conversion = f"to_signed(value, {field_width})"
647 else:
648 raise ValueError(f"Should not end up here for field: {field}")
649 else:
650 field_conversion = "value"
652 return f"""\
653{signature} is
654 variable reg_value : {register_name}_t := {register_name}_init;
655 begin
656{set_base_value}\
657 reg_value.{field.name} := {field_conversion};
659 write_{register_name}(
660 net => net,
661{self.get_array_index_association(register_array=register_array)}\
662 value => reg_value,
663 base_address => base_address,
664 bus_handle => bus_handle
665 );
666 end procedure;
667"""