Coverage for hdl_registers/generator/vhdl/simulation/read_write_package.py: 97%
141 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-07 20:51 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-07 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, a procedure that reads the register and converts the value to the
34 natively-typed record.
36 * For each field in each readable register, a procedure that reads the natively-typed value of
37 the field.
39 * For each writeable register, a procedure that writes a given natively-typed record value.
41 * For each field in each writeable register, a procedure that writes a given field value.
43 Uses VUnit Verification Component calls to create bus read/write operations.
45 The generated VHDL file needs also the generated packages from
46 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
47 """
49 __version__ = "1.0.0"
51 SHORT_DESCRIPTION = "VHDL simulation read/write package"
53 @property
54 def output_file(self) -> Path:
55 """
56 Result will be placed in this file.
57 """
58 return self.output_folder / f"{self.name}_register_read_write_pkg.vhd"
60 def create(self, **kwargs: Any) -> Path:
61 """
62 See super class for API details.
64 Overloaded here because this package file shall only be created if the register list
65 actually has any registers.
66 """
67 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
69 def get_code(self, **kwargs: Any) -> str:
70 """
71 Get a package with methods for reading/writing registers.
72 """
73 package_name = self.output_file.stem
75 vhdl = f"""\
76{self.header}
77library ieee;
78use ieee.numeric_std.all;
79use ieee.std_logic_1164.all;
81library vunit_lib;
82use vunit_lib.bus_master_pkg.bus_master_t;
83use vunit_lib.bus_master_pkg.read_bus;
84use vunit_lib.bus_master_pkg.write_bus;
85use vunit_lib.com_types_pkg.network_t;
87library common;
88use common.addr_pkg.addr_t;
89use common.addr_pkg.addr_width;
91library reg_file;
92use reg_file.reg_file_pkg.reg_t;
93use reg_file.reg_file_pkg.reg_width;
94use reg_file.reg_operations_pkg.regs_bus_master;
96use work.{self.name}_regs_pkg.all;
97use work.{self.name}_register_record_pkg.all;
100package {package_name} is
102{self._declarations()}\
103end package;
105package body {package_name} is
107{self._implementations()}\
108end package body;
109"""
111 return vhdl
113 def _declarations(self) -> str:
114 """
115 Get procedure declarations for all procedures.
116 """
117 separator = self.get_separator_line(indent=2)
118 vhdl = ""
120 for register, register_array in self.iterate_registers():
121 register_name = self.qualified_register_name(
122 register=register, register_array=register_array
123 )
124 declarations = []
126 if register.mode.software_can_read:
127 if register.fields:
128 # Read the register as a record.
129 signature = self._register_read_write_signature(
130 is_read_not_write=True,
131 register=register,
132 register_array=register_array,
133 value_type=f"{register_name}_t",
134 )
135 declarations.append(f"{signature};\n")
136 else:
137 # Read the register as a plain SLV, since it has no fields.
138 signature = self._register_read_write_signature(
139 is_read_not_write=True,
140 register=register,
141 register_array=register_array,
142 value_type="reg_t",
143 )
144 declarations.append(f"{signature};\n")
146 # Read the register as a plain SLV casted to integer.
147 signature = self._register_read_write_signature(
148 is_read_not_write=True,
149 register=register,
150 register_array=register_array,
151 value_type="integer",
152 )
153 declarations.append(f"{signature};\n")
155 for field in register.fields:
156 # Read the field as its native type.
157 value_type = self.field_type_name(
158 register=register, register_array=register_array, field=field
159 )
160 signature = self._field_read_write_signature(
161 is_read_not_write=True,
162 register=register,
163 register_array=register_array,
164 field=field,
165 value_type=value_type,
166 )
167 declarations.append(f"{signature};\n")
169 if self._should_be_able_to_access_field_as_integer(field=field):
170 # Read the field casted to an integer.
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="integer",
177 )
178 declarations.append(f"{signature};\n")
180 if register.mode.software_can_write:
181 if register.fields:
182 # Write the register as a record.
183 signature = self._register_read_write_signature(
184 is_read_not_write=False,
185 register=register,
186 register_array=register_array,
187 value_type=f"{register_name}_t",
188 )
189 declarations.append(f"{signature};\n")
190 else:
191 # Write the register as a plain SLV, since it has no fields.
192 signature = self._register_read_write_signature(
193 is_read_not_write=False,
194 register=register,
195 register_array=register_array,
196 value_type="reg_t",
197 )
198 declarations.append(f"{signature};\n")
200 # Write the register as an integer.
201 signature = self._register_read_write_signature(
202 is_read_not_write=False,
203 register=register,
204 register_array=register_array,
205 value_type="integer",
206 )
207 declarations.append(f"{signature};\n")
209 for field in register.fields:
210 # Write the field as its native type.
211 value_type = self.field_type_name(
212 register=register, register_array=register_array, field=field
213 )
214 signature = self._field_read_write_signature(
215 is_read_not_write=False,
216 register=register,
217 register_array=register_array,
218 field=field,
219 value_type=value_type,
220 )
221 declarations.append(f"{signature};\n")
223 if self._should_be_able_to_access_field_as_integer(field=field):
224 # Write the field casted to an integer.
225 signature = self._field_read_write_signature(
226 is_read_not_write=False,
227 register=register,
228 register_array=register_array,
229 field=field,
230 value_type="integer",
231 )
232 declarations.append(f"{signature};\n")
234 vhdl += separator
235 vhdl += "\n".join(declarations)
236 vhdl += separator
237 vhdl += "\n"
239 return vhdl
241 def _register_read_write_signature(
242 self,
243 is_read_not_write: bool,
244 register: "Register",
245 register_array: Optional["RegisterArray"],
246 value_type: str,
247 ) -> str:
248 """
249 Get signature for a 'read_reg'/'write_reg' procedure.
250 """
251 direction = "read" if is_read_not_write else "write"
252 value_direction = "out" if is_read_not_write else "in"
254 register_name = self.qualified_register_name(
255 register=register, register_array=register_array
256 )
257 register_description = self.register_description(
258 register=register, register_array=register_array
259 )
260 # If it is not either of these, then it is the native type which shall not have a comment
261 # since it is the default.
262 type_comment = (
263 " as a plain 'reg_t'"
264 if value_type == "reg_t"
265 else " as an 'integer'" if value_type == "integer" else ""
266 )
268 return f"""\
269 -- {direction.capitalize()} the {register_description}{type_comment}.
270 procedure {direction}_{register_name}(
271 signal net : inout network_t;
272{self.get_array_index_port(register_array=register_array)}\
273 value : {value_direction} {value_type};
274 base_address : in addr_t := (others => '0');
275 bus_handle : in bus_master_t := regs_bus_master
276 )\
277"""
279 def _field_read_write_signature(
280 self,
281 is_read_not_write: bool,
282 register: "Register",
283 register_array: Optional["RegisterArray"],
284 field: "RegisterField",
285 value_type: str,
286 ) -> str:
287 """
288 Get signature for a 'read_field'/'write_field' procedure.
289 """
290 direction = "read" if is_read_not_write else "write"
292 field_name = self.qualified_field_name(
293 register=register, register_array=register_array, field=field
294 )
295 field_description = self.field_description(
296 register=register, field=field, register_array=register_array
297 )
298 # If its not integer, then it is the native type which shall shall not have a comment
299 # since it is the default.
300 type_comment = " as an 'integer'" if value_type == "integer" else ""
302 comment = ""
303 if not is_read_not_write:
304 if self.field_setter_should_read_modify_write(register=register):
305 comment = (
306 " -- Will read-modify-write the register to set the field to the "
307 "supplied 'value'.\n"
308 )
309 else:
310 comment = (
311 " -- Will write the whole register, with the field set to the \n"
312 " -- supplied 'value' and everything else set to default.\n"
313 )
315 value_direction = "out" if is_read_not_write else "in"
317 return f"""\
318 -- {direction.capitalize()} the {field_description}{type_comment}.
319{comment}\
320 procedure {direction}_{field_name}(
321 signal net : inout network_t;
322{self.get_array_index_port(register_array=register_array)}\
323 value : {value_direction} {value_type};
324 base_address : in addr_t := (others => '0');
325 bus_handle : in bus_master_t := regs_bus_master
326 )\
327"""
329 @staticmethod
330 def _should_be_able_to_access_field_as_integer(field: "RegisterField") -> bool:
331 """
332 Return True if the field is of a type where there should be procedures to read/write it
333 casted as an integer.
334 """
335 return isinstance(field, BitVector) and isinstance(
336 field.numerical_interpretation, (Signed, Unsigned)
337 )
339 def _implementations(self) -> str:
340 """
341 Get implementations of all procedures.
342 """
343 separator = self.get_separator_line(indent=2)
344 vhdl = ""
346 for register, register_array in self.iterate_registers():
347 register_name = self.qualified_register_name(
348 register=register, register_array=register_array
349 )
350 implementations = []
352 if register.mode.software_can_read:
353 if register.fields:
354 # Read the register as a record.
355 implementations.append(
356 self._register_read_implementation(
357 register=register,
358 register_array=register_array,
359 value_type=f"{register_name}_t",
360 value_conversion=f"to_{register_name}(reg_value)",
361 )
362 )
363 else:
364 # Read the register as a plain SLV, since it has no fields.
365 implementations.append(
366 self._register_read_implementation(
367 register=register,
368 register_array=register_array,
369 value_type="reg_t",
370 value_conversion="reg_value",
371 )
372 )
374 # Read the register as a plain SLV casted to integer.
375 implementations.append(
376 self._register_read_implementation(
377 register=register,
378 register_array=register_array,
379 value_type="integer",
380 value_conversion="to_integer(unsigned(reg_value))",
381 )
382 )
384 for field in register.fields:
385 # Read the field as its native type.
386 value_type = self.field_type_name(
387 register=register, register_array=register_array, field=field
388 )
389 implementations.append(
390 self._field_read_implementation(
391 register=register,
392 register_array=register_array,
393 field=field,
394 value_type=value_type,
395 value_conversion=f"reg_value.{field.name}",
396 )
397 )
399 if self._should_be_able_to_access_field_as_integer(field=field):
400 # Read the field casted to an integer.
401 implementations.append(
402 self._field_read_implementation(
403 register=register,
404 register_array=register_array,
405 field=field,
406 value_type="integer",
407 value_conversion=f"to_integer(reg_value.{field.name})",
408 )
409 )
411 if register.mode.software_can_write:
412 if register.fields:
413 # Write the register as a record.
414 implementations.append(
415 self._register_write_implementation(
416 register=register,
417 register_array=register_array,
418 value_type=f"{register_name}_t",
419 value_conversion="to_slv(value)",
420 )
421 )
422 else:
423 # Write the register as a plain SLV, since it has no fields.
424 implementations.append(
425 self._register_write_implementation(
426 register=register,
427 register_array=register_array,
428 value_type="reg_t",
429 value_conversion="value",
430 )
431 )
433 # Write the register as an integer.
434 implementations.append(
435 self._register_write_implementation(
436 register=register,
437 register_array=register_array,
438 value_type="integer",
439 value_conversion="std_ulogic_vector(to_unsigned(value, reg_width))",
440 )
441 )
443 for field in register.fields:
444 # Write the field as its native type.
445 value_type = self.field_type_name(
446 register=register, register_array=register_array, field=field
447 )
448 implementations.append(
449 self._field_write_implementation(
450 register=register,
451 register_array=register_array,
452 field=field,
453 value_type=value_type,
454 )
455 )
457 if self._should_be_able_to_access_field_as_integer(field=field):
458 # Read the field casted to an integer.
459 implementations.append(
460 self._field_write_implementation(
461 register=register,
462 register_array=register_array,
463 field=field,
464 value_type="integer",
465 )
466 )
468 vhdl += separator
469 vhdl += "\n".join(implementations)
470 vhdl += separator
471 vhdl += "\n"
473 return vhdl
475 def _register_read_implementation(
476 self,
477 register: "Register",
478 register_array: Optional["RegisterArray"],
479 value_type: str,
480 value_conversion: str,
481 ) -> str:
482 """
483 Get implementation for a 'read_reg' procedure.
484 """
485 signature = self._register_read_write_signature(
486 is_read_not_write=True,
487 register=register,
488 register_array=register_array,
489 value_type=value_type,
490 )
492 return f"""\
493{signature} is
494{self.reg_index_constant(register=register, register_array=register_array)}\
495{self.reg_address_constant()}\
496 variable reg_value : reg_t := (others => '0');
497 begin
498 read_bus(
499 net => net,
500 bus_handle => bus_handle,
501 address => std_logic_vector(reg_address),
502 data => reg_value
503 );
504 value := {value_conversion};
505 end procedure;
506"""
508 def _register_write_implementation(
509 self,
510 register: "Register",
511 register_array: Optional["RegisterArray"],
512 value_type: str,
513 value_conversion: str,
514 ) -> str:
515 """
516 Get implementation for a 'write_reg' procedure.
517 """
518 signature = self._register_read_write_signature(
519 is_read_not_write=False,
520 register=register,
521 register_array=register_array,
522 value_type=value_type,
523 )
525 return f"""\
526{signature} is
527{self.reg_index_constant(register=register, register_array=register_array)}\
528{self.reg_address_constant()}\
529 constant reg_value : reg_t := {value_conversion};
530 begin
531 write_bus(
532 net => net,
533 bus_handle => bus_handle,
534 address => std_logic_vector(reg_address),
535 data => reg_value
536 );
537 end procedure;
538"""
540 def _field_read_implementation(
541 self,
542 register: "Register",
543 register_array: Optional["RegisterArray"],
544 field: "RegisterField",
545 value_type: str,
546 value_conversion: str,
547 ) -> str:
548 """
549 Get implementation for a 'read_field' procedure.
550 """
551 signature = self._field_read_write_signature(
552 is_read_not_write=True,
553 register=register,
554 register_array=register_array,
555 field=field,
556 value_type=value_type,
557 )
559 register_name = self.qualified_register_name(
560 register=register, register_array=register_array
561 )
563 return f"""\
564{signature} is
565 variable reg_value : {register_name}_t := {register_name}_init;
566 begin
567 read_{register_name}(
568 net => net,
569{self.get_array_index_association(register_array=register_array)}\
570 value => reg_value,
571 base_address => base_address,
572 bus_handle => bus_handle
573 );
574 value := {value_conversion};
575 end procedure;
576"""
578 def _field_write_implementation(
579 self,
580 register: "Register",
581 register_array: Optional["RegisterArray"],
582 field: "RegisterField",
583 value_type: str,
584 ) -> str:
585 """
586 Get implementation for a 'write_field' procedure.
587 """
588 signature = self._field_read_write_signature(
589 is_read_not_write=False,
590 register=register,
591 register_array=register_array,
592 field=field,
593 value_type=value_type,
594 )
595 register_name = self.qualified_register_name(
596 register=register, register_array=register_array
597 )
599 if self.field_setter_should_read_modify_write(register=register):
600 set_base_value = f"""\
601 read_{register_name}(
602 net => net,
603{self.get_array_index_association(register_array=register_array)}\
604 value => reg_value,
605 base_address => base_address,
606 bus_handle => bus_handle
607 );
608"""
609 else:
610 set_base_value = ""
612 if value_type == "integer":
613 field_name = self.qualified_field_name(
614 register=register, register_array=register_array, field=field
615 )
616 field_width = f"{field_name}_width"
618 if isinstance(field, BitVector) and isinstance(
619 field.numerical_interpretation, Unsigned
620 ):
621 field_conversion = f"to_unsigned(value, {field_width})"
622 elif isinstance(field, BitVector) and isinstance(
623 field.numerical_interpretation, Signed
624 ):
625 field_conversion = f"to_signed(value, {field_width})"
626 else:
627 raise ValueError(f"Should not end up here for field: {field}")
628 else:
629 field_conversion = "value"
631 return f"""\
632{signature} is
633 variable reg_value : {register_name}_t := {register_name}_init;
634 begin
635{set_base_value}\
636 reg_value.{field.name} := {field_conversion};
638 write_{register_name}(
639 net => net,
640{self.get_array_index_association(register_array=register_array)}\
641 value => reg_value,
642 base_address => base_address,
643 bus_handle => bus_handle
644 );
645 end procedure;
646"""