Coverage for hdl_registers/field/register_field.py: 94%

33 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 11:11 +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 __future__ import annotations 

11 

12from abc import ABC, abstractmethod 

13from typing import TYPE_CHECKING 

14 

15if TYPE_CHECKING: 

16 from .enumeration import EnumerationElement 

17 

18 

19class RegisterField(ABC): 

20 """ 

21 Meta class for all register fields (bits, bit vectors, integers, ...). 

22 Lists a few methods that must be implemented. 

23 """ 

24 

25 # Must set as class members in subclasses. 

26 name: str 

27 _base_index: int 

28 _width: int 

29 description: str 

30 default_value: str | int | EnumerationElement 

31 

32 @property 

33 def base_index(self) -> int: 

34 """ 

35 The index within the register for the lowest bit of this field. 

36 Note that this (along with :attr:`.width`) decides the base index of upcoming fields, and 

37 thus it may not be changed. 

38 Hence this read-only property which is a getter for a private member. 

39 """ 

40 return self._base_index 

41 

42 @property 

43 def width(self) -> int: 

44 """ 

45 The width, in number of bits, that this field occupies. 

46 Note that this (along with :attr:`.base_index`) decides the base index of upcoming fields, 

47 and thus it may not be changed. 

48 Hence this read-only property which is a getter for a private member. 

49 """ 

50 return self._width 

51 

52 @property 

53 @abstractmethod 

54 def default_value_uint(self) -> int: 

55 """ 

56 Return a the default value of this field as an unsigned integer. 

57 """ 

58 

59 def get_value(self, register_value: int) -> int: 

60 """ 

61 Get the value of this field, given the supplied register value. 

62 

63 Arguments: 

64 register_value: Value of the register that this field belongs to, 

65 as an unsigned integer. 

66 

67 Return: 

68 The value of the field as an unsigned integer. 

69 

70 Note that a subclass might have a different type for the resulting value. 

71 Subclasses should call this super method, and then convert the numeric value to whatever 

72 type is applicable for that field. 

73 Subclasses might also implement sanity checks of the value given the constraints 

74 of that field. 

75 """ 

76 shift_count = self.base_index 

77 

78 mask_at_base = (1 << self.width) - 1 

79 mask_shifted = mask_at_base << shift_count 

80 

81 return (register_value & mask_shifted) >> shift_count 

82 

83 def set_value(self, field_value: int) -> int: 

84 """ 

85 Convert the supplied value into the bit-shifted unsigned integer ready 

86 to be written to the register. 

87 The bits of the other fields in the register are masked out and will be set to zero. 

88 

89 Arguments: 

90 field_value: Desired unsigned integer value to set the field to. 

91 

92 Note that a subclass might have a different type for this argument. 

93 Subclasses should convert their argument value to an integer and call 

94 this super method. 

95 

96 Return: 

97 The register value as an unsigned integer. 

98 """ 

99 max_value = 2**self.width - 1 

100 if not 0 <= field_value <= max_value: 

101 raise ValueError(f"Value: {field_value} is invalid for unsigned of width {self.width}") 

102 

103 return field_value << self.base_index 

104 

105 @abstractmethod 

106 def __repr__(self) -> str: 

107 pass