Coverage for hdl_registers/register_mode.py: 98%
41 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-16 20:50 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-16 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# --------------------------------------------------------------------------------------------------
10from dataclasses import dataclass
11from enum import Enum, auto
14@dataclass
15class _SoftwareAccessDirection:
16 name_past: str
17 name_adjective: str
20class SoftwareAccessDirection(Enum):
21 """
22 The possible directions software can access registers.
23 """
25 # The type of the enum values might change in the future.
26 # Should not make a difference for any user since the values are unique either way.
27 READ = _SoftwareAccessDirection(name_past="read", name_adjective="readable")
28 WRITE = _SoftwareAccessDirection(name_past="written", name_adjective="writeable")
31class HardwareAccessDirection(Enum):
32 """
33 The possible directions hardware can provide/read register values.
34 """
36 # The type of the enum values might change in the future.
37 # Should not make a difference for any user since the values are unique either way.
38 UP = auto()
39 DOWN = auto()
42class RegisterMode:
43 """
44 Represents a mode of a register, which defines how the register can be accessed.
45 The terms "software"/"hardware", along with "register bus", "register file", "read", "write",
46 "up" and "down" are used in this class.
47 These terms are explained by the following diagram:
49 .. code-block:: none
51 ______________________
52 | "Software" |
53 | E.g. CPU, PCIe, etc. |
54 |______________________|
55 ||
56 ||
57 || "Register bus"
58 || E.g. AXI-Lite.
59 || "read" or "write" transactions.
60 ||
61 ||
62 _______________________ "down" ______________________________
63 | "Register file" |--------------->| "Hardware" |
64 | E.g. generic AXI-Lite | | Meaning, your application. |
65 | register file. | "up" | In e.g. FPGA fabric or ASIC. |
66 |_______________________|<---------------|______________________________|
67 """
69 def __init__(
70 self,
71 shorthand: str,
72 name: str,
73 description: str,
74 software_can_read: bool,
75 software_can_write: bool,
76 hardware_has_up: bool,
77 ) -> None:
78 """
79 Arguments:
80 shorthand: A short string that can be used to refer to this mode.
81 E.g. "r".
82 name: A short but human-readable representation of this mode.
83 E.g. "Read".
84 description: Textual description and explanation of this mode.
85 software_can_read: True if register is readable by software on the register bus.
86 I.e. if software accessors shall have a 'read' method for registers of this mode.
87 False otherwise.
89 Analogous the ``register_file.register_file_pkg.is_read_mode`` VHDL function.
90 software_can_write: True if register is writeable by software on the register bus.
91 I.e. if software accessors shall have a 'write' method for registers of this mode.
92 False otherwise.
94 Analogous the ``register_file.register_file_pkg.is_write_mode`` VHDL function.
95 hardware_has_up: True if register gets its software-read value from hardware.
96 I.e. if register file shall have an 'up' input port for registers of this mode.
98 False otherwise, which can be due to either
100 * mode is not software-readable, or
101 * mode loopbacks a software-written value to the software-read value.
102 """
103 if hardware_has_up and not software_can_read:
104 raise ValueError(
105 f'Register mode "{shorthand}"" has hardware "up", but is not software readable. '
106 "This does not make sense."
107 )
109 self.shorthand = shorthand
110 self.name = name
111 self.description = description
112 self.software_can_read = software_can_read
113 self.software_can_write = software_can_write
114 self.hardware_has_up = hardware_has_up
116 @property
117 def hardware_has_down(self) -> bool:
118 """
119 True if register provides a value from software to hardware.
120 I.e. if register file shall have a 'down' output port for registers of this mode.
122 False otherwise, which is most likely due to the register being read-only.
123 """
124 # At the moment this is the same being software-writeable.
125 # Might change in the future if we implement some exotic cool mode.
126 return self.software_can_write
128 def is_software_accessible(self, direction: SoftwareAccessDirection) -> bool:
129 """
130 Test if this mode is software-accessible in the given ``direction``.
131 Method is just a simple wrapper around the already-existing attributes.
132 """
133 if direction == SoftwareAccessDirection.READ:
134 return self.software_can_read
136 return self.software_can_write
138 def is_hardware_accessible(self, direction: HardwareAccessDirection) -> bool:
139 """
140 Test if this mode is hardware-accessible in the given ``direction``.
141 Method is just a simple wrapper around the already-existing attributes.
142 """
143 if direction == HardwareAccessDirection.UP:
144 return self.hardware_has_up
146 return self.hardware_has_down
148 def __repr__(self) -> str:
149 """
150 There should never be different modes with the same shorthand.
151 Hence, in order to make a unique representation, it is enough to use the shorthand.
152 None of the other attributes are needed.
153 """
154 return f"{self.__class__.__name__}(shorthand={self.shorthand})"
156 def __str__(self) -> str:
157 return repr(self)
159 def __eq__(self, other: object) -> bool:
160 # Same logic as in __repr__.
161 return isinstance(other, self.__class__) and other.shorthand == self.shorthand
163 def __hash__(self) -> int:
164 # Same logic as in __repr__.
165 return hash(self.shorthand)