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

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

9 

10from dataclasses import dataclass 

11from enum import Enum, auto 

12 

13 

14@dataclass 

15class _SoftwareAccessDirection: 

16 name_past: str 

17 name_adjective: str 

18 

19 

20class SoftwareAccessDirection(Enum): 

21 """ 

22 The possible directions software can access registers. 

23 """ 

24 

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") 

29 

30 

31class HardwareAccessDirection(Enum): 

32 """ 

33 The possible directions hardware can provide/read register values. 

34 """ 

35 

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() 

40 

41 

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: 

48 

49 .. code-block:: none 

50 

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

68 

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. 

88 

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. 

93 

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. 

97 

98 False otherwise, which can be due to either 

99 

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 ) 

108 

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 

115 

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. 

121 

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 

127 

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 

135 

136 return self.software_can_write 

137 

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 

145 

146 return self.hardware_has_down 

147 

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})" 

155 

156 def __str__(self) -> str: 

157 return repr(self) 

158 

159 def __eq__(self, other: object) -> bool: 

160 # Same logic as in __repr__. 

161 return isinstance(other, self.__class__) and other.shorthand == self.shorthand 

162 

163 def __hash__(self) -> int: 

164 # Same logic as in __repr__. 

165 return hash(self.shorthand)