diff --git a/Makefile b/Makefile index 617c23b..86ecedf 100644 --- a/Makefile +++ b/Makefile @@ -15,23 +15,26 @@ gen: ipxact2vhdl --srcFile example/input/test.xml --destDir example/output ipxact2md --srcFile example/input/test.xml --destDir example/output ipxact2c --srcFile example/input/test.xml --destDir example/output + ipxact2py --srcFile example/input/test.xml --destDir example/output pandoc -s example/output/example.rst -o example/output/example.html pandoc -s example/output/example.rst -o example/output/example.rtf pandoc -s example/output/example.rst -o example/output/example.pdf - # default config - ipxact2systemverilog --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini - ipxact2rst --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini - ipxact2md --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini - ipxact2vhdl --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini - ipxact2c --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini - - # no default config - ipxact2systemverilog --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini - ipxact2rst --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini - ipxact2md --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini - ipxact2vhdl --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini - ipxact2c --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + # default config + ipxact2systemverilog --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini + ipxact2rst --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini + ipxact2md --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini + ipxact2vhdl --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini + ipxact2c --srcFile example/input/test.xml --destDir example/output_default --config example/input/default.ini + ipxact2py --srcFile example/input/test.xml --destDir example/output_default -- config example/input/default.ini + + # no default config + ipxact2systemverilog --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + ipxact2rst --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + ipxact2md --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + ipxact2vhdl --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + ipxact2c --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini + ipxact2py --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini # RestructuredText and Sphinx with Wavedrom ipxact2rst --srcFile example/input/test.xml --destDir example/output_sphinx --config example/input/sphinx.ini @@ -46,13 +49,7 @@ gen: ipxact2vhdl --srcFile example/input/test2.xml --destDir example/output ipxact2md --srcFile example/input/test2.xml --destDir example/output ipxact2c --srcFile example/input/test2.xml --destDir example/output - pandoc -s example/output/example2.rst -o example/output/example2.pdf - - cp example/output/example2.rst example/output_sphinx2 - ipxact2rst --srcFile example/input/test2.xml --destDir example/output_sphinx2 --config example/input/sphinx.ini - sphinx-build example/output_sphinx2 example/output_sphinx2/build -q -b latex - make -C example/output_sphinx2/build - cp example/output_sphinx2/build/example2.pdf example/output_sphinx2/example2.pdf + ipxact2py --srcFile example/input/test.xml --destDir example/output compile: @@ -105,6 +102,11 @@ test_rst: rst-lint example/output_default/*.rst rst-lint example/output_no_default/*.rst +test_py: + pylint example/output/*.py + pylint example/output_default/*.py + pylint example/output_no_default/*.py + venv: requirements.txt python3 -m venv ./venv pip install wheel diff --git a/README.md b/README.md index d909f5a..7f8476a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ipxact2systemverilog ipxact2rst ipxact2md ipxact2vhdl ipxact2c +# ipxact2systemverilog ipxact2rst ipxact2md ipxact2vhdl ipxact2c ipxact2py [![CI](https://github.com/oddball/ipxact2systemverilog/actions/workflows/ci.yml/badge.svg)](https://github.com/oddball/ipxact2systemverilog/actions/workflows/ci.yml) @@ -21,6 +21,7 @@ ipxact2rst --srcFile FILE --destDir DIR ipxact2md --srcFile FILE --destDir DIR ipxact2vhdl --srcFile FILE --destDir DIR ipxact2c --srcFile FILE --destDir DIR +ipxact2py --srcFile FILE --destDir DIR ``` ## Development diff --git a/example/input/default.ini b/example/input/default.ini index 52cca29..8ad3e7e 100644 --- a/example/input/default.ini +++ b/example/input/default.ini @@ -43,3 +43,14 @@ std = unresolved # - https://github.com/bavovanachte/sphinx-wavedrom # - https://github.com/wavedrom/bitfield wavedrom = no + +# Python +[py] +# Defines how Python modules generated by this tool will import other modules. +# This setting directly influences the generated 'from ... import ...' statements +# and the necessity of '__init__.py' files for package recognition. +# +# Options: +# - 'absolute': default option +# - 'relative' +imports = absolute \ No newline at end of file diff --git a/example/input/no_default.ini b/example/input/no_default.ini index ba1cc71..86c09ed 100644 --- a/example/input/no_default.ini +++ b/example/input/no_default.ini @@ -5,3 +5,6 @@ OneBitEnum = yes [vhdl] PublicConvFunct = yes std = resolved + +[py] +imports = relative diff --git a/example/output/acces_layer.py b/example/output/acces_layer.py new file mode 100644 index 0000000..f51035f --- /dev/null +++ b/example/output/acces_layer.py @@ -0,0 +1,264 @@ +""" +Base types for the generated code are declared here, as well as exceptions and other supporting classes. +Functionality to read, write and modify registers is also provided here. Several methods are provided, all sharing the same +interface. +""" + + +class OutOfRangeReading(Exception): + """Exception thrown when an out of range value is read from the register.""" + pass + + +class OutOfRangeWriting(Exception): + """Exception thrown when an out of range value is attempted to be written to the register.""" + pass + + +class OutOfRangeSpecifying(Exception): + """Exception thrown when an a boundary is specified that is incoherent (e.g. with the number of bits in a field).""" + pass + + +class FieldNotWritable(Exception): + """Exception thrown when attempting to write to a read-only field.""" + pass + + +class accesLayer: + """This interface contains method declarations that should be used by classes that can implement any type of memory + access the registers (including simulated). """ + def read_register(self, addr, offset=0, width=32): + """ + Reads a register + :param addr: address + :param offset: bit offset + :param width: number of bits after the offset to read + :return: the requested bits from the register + """ + raise NotImplementedError() + + def modify_register(self, addr, data, offset=0, width=32): + """ + Partially writes a register + :param addr: address + :param addr: data to be written + :param offset: bit offset + :param width: number of bits after the offset to write + """ + raise NotImplementedError() + + def write_register(self, addr, data): + """ + writes a complete register + :param addr: address + :param addr: data to be written + """ + raise NotImplementedError() + + +class IP(): + def __init__(self, parent, base_address, acces_layer=accesLayer): + self._base_address = base_address + self._parent = parent + self._acces_layer = acces_layer + + def get_base_address(self): + return self._base_address + + def get_acces_layer(self): + return self._acces_layer + + +class Register(): + def __init__(self, parent_ip, address_offset): + """ + Constructor for a register of an IP + :param parent_ip: an IP object to which this register belongs + :param address_offset: the address offset (from the IP's base address) + """ + self._parent_ip = parent_ip + self._address_offset = address_offset + self._acces_layer = parent_ip.get_acces_layer() + + def get_address_offset(self): + """ + Returns the address offset + :return: the address offset + """ + return self._address_offset + + def get_parent_ip(self): + """ + Returns the parent IP object + :return: the parent IP object + """ + return self._parent_ip + + def get_acces_layer(self): + """ + Returns the acces layer object currently used. Usually it's the parent IP's access layer + :return: the acces layer object currently used + """ + return self._acces_layer + + def get(self): + """ + returns the value of this register (as unsigned int) + :return: the value of this register + """ + self.get_acces_layer().read_register(self.get_parent_ip().get_base_address() + + self.get_address_offset()) + + def set(self, value): + """ + Sets the value of this register (as unsigned int) + :value: the value to set this register + """ + self.get_acces_layer().write_register(self.get_parent_ip().get_base_address() + + self.get_address_offset(), value) + + +class Field(): + """This base class represents a field in the register. It will work like an unsigned integer field and is the prefered + way of obtaining 'raw values' from the registers but should be derived from and not instanced. """ + def __init__(self, parent_register, bit_width, bit_offset, access): + """ + Constructor for a Field of a register + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + """ + self._parent_register = parent_register + self._bit_width = bit_width + self._bit_offset = bit_offset + self._access = access + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + return self._parent_register.get_acces_layer().read_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + self._bit_offset, + self._bit_width) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if self._access == "read-only": + raise FieldNotWritable() + + self._parent_register.get_acces_layer().modify_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + value, + self._bit_offset, + self._bit_width) + + +class EnumField(Field): + """Represents an enumerated type.""" + def __init__(self, parent_register, bit_width, bit_offset, access, enum_type): + """ + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param enum_type: a Python type derived from IntEnum which we want to represent + """ + super().__init__(parent_register, bit_width, bit_offset, access) + self._enum_type = enum_type + self._int_values = list(map(int, enum_type)) + + def get(self): + """ + Returns the value of the field + :return: the current enumeration represented by the field + """ + val = super().get() + if val not in self._int_values: + return OutOfRangeReading() + return self._enum_type(val) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set. Either an integer or an enumeration of 'our' enumerated type + """ + if value in self._int_values: + super().set(value) + else: + raise OutOfRangeWriting() + + +class IntegerField(Field): + """This class represents an (unsigned) integer. It derives from Field and relies on it for abstracting reads and + writes. Therefore, this class is basically concerned with boundary checking.""" + def __init__(self, parent_register, bit_width, bit_offset, access, minimum, maximum): + """ + Constructor for a Field of a register + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param minimum: minimum value that this field can have (None to assume the minimum representable) + :param maximum: maximum value that this field can have (None to assume the maximum representable) + """ + super().__init__(parent_register, bit_width, bit_offset, access) + type_minimum = 0 + type_maximum = (2 ** bit_width) - 1 + if minimum is not None: + if minimum < type_minimum: + raise OutOfRangeSpecifying() + else: + self._minimum = minimum + else: + self._minimum = type_minimum + + if maximum is not None: + if maximum > type_maximum: + raise OutOfRangeSpecifying() + else: + self._maximum = maximum + else: + self._maximum = type_maximum + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + val = super().get() + if val < self._minimum or val > self._maximum: + raise OutOfRangeReading() + return val + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if value < self._minimum or value > self._maximum: + raise OutOfRangeWriting() + else: + super().set(value) + + def get_minimum(self): + """ + Gets the minimum value that can be defined for the field + :return: minimum + """ + return self._minimum + + def get_maximum(self): + """ + Gets the maximum value that can be defined for the field + :return: maximum + """ + return self._maximum diff --git a/example/output/example.html b/example/output/example.html index 57d60af..a715d82 100644 --- a/example/output/example.html +++ b/example/output/example.html @@ -60,10 +60,6 @@ img { max-width: 100%; } - svg { - height: auto; - max-width: 100%; - } h1, h2, h3, h4, h5, h6 { margin-top: 1.4em; } diff --git a/example/output/example.pdf b/example/output/example.pdf index f729f58..865a16f 100644 Binary files a/example/output/example.pdf and b/example/output/example.pdf differ diff --git a/example/output/example.py b/example/output/example.py new file mode 100644 index 0000000..fcc2892 --- /dev/null +++ b/example/output/example.py @@ -0,0 +1,650 @@ +# Automatically generated +# with the command '/home/labgrid/Thomas_vreys/ipxact2systemverilog/.venv/bin/ipxact2py --srcFile example/input/test.xml --destDir example/output' +# +# Do not manually edit! +# + +from enum import IntEnum + +from acces_layer import * + + +class reg0_type(Register): + """ + write something useful for reg0 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [7:0] + # write something useful for field0 + self._byte0 = IntegerField( + self, + bit_width=8, + bit_offset=0, + access="read-write", + minimum=0, + maximum=7, + ) + # [15:8] + # write something useful for field1 + self._byte1 = IntegerField( + self, + bit_width=8, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [23:16] + # write something useful for field2 + self._byte2 = IntegerField( + self, + bit_width=8, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:24] + # write something useful for field3 + self._byte3 = IntegerField( + self, + bit_width=8, + bit_offset=24, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def byte0(self): + return self._byte0.get() + + @byte0.setter + def byte0(self, value: int): + self._byte0.set(value) + + @property + def byte1(self): + return self._byte1.get() + + @byte1.setter + def byte1(self, value: int): + self._byte1.set(value) + + @property + def byte2(self): + return self._byte2.get() + + @byte2.setter + def byte2(self, value: int): + self._byte2.set(value) + + @property + def byte3(self): + return self._byte3.get() + + @byte3.setter + def byte3(self, value: int): + self._byte3.set(value) + + +class reg1_type(Register): + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=4, + maximum=20, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +# which monkey +class monkey_enum(IntEnum): + chimp = 0x0 # a monkey + gorilla = 0x1 + phb = 0x2 # and another monkey + + +# which monkey +class monkey2_enum(IntEnum): + chimp = 0 + gorilla = 1 + phb = 2 + + +# which monkey +class monkey3_enum(IntEnum): + phb = 0 + gorilla = 1 + chimp = 2 + + +# which monkey +class monkey4_enum(IntEnum): + chimp = 0 + gorilla = 1 + bonobo = 2 + + +class reg2_type(Register): + """ + write something useful for reg2 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [0] + # write something useful for field power + self._power = IntegerField( + self, + bit_width=1, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [1] + # write something useful for field power2 + self._power2 = IntegerField( + self, + bit_width=1, + bit_offset=1, + access="read-write", + minimum=None, + maximum=None, + ) + # [3:2] + # which monkey + self._monkey = EnumField( + self, + bit_width=2, + bit_offset=2, + access="read-write", + enum_type=monkey_enum, + ) + # [5:4] + # which monkey + self._monkey2 = EnumField( + self, + bit_width=2, + bit_offset=4, + access="read-write", + enum_type=monkey2_enum, + ) + # [7:6] + # which monkey + self._monkey3 = EnumField( + self, + bit_width=2, + bit_offset=6, + access="read-write", + enum_type=monkey3_enum, + ) + # [9:8] + # which monkey + self._monkey4 = EnumField( + self, + bit_width=2, + bit_offset=8, + access="read-write", + enum_type=monkey4_enum, + ) + # [31:10] + # unused + self._unused0 = IntegerField( + self, + bit_width=22, + bit_offset=10, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def power(self): + return self._power.get() + + @power.setter + def power(self, value: int): + self._power.set(value) + + @property + def power2(self): + return self._power2.get() + + @power2.setter + def power2(self, value: int): + self._power2.set(value) + + @property + def monkey(self): + return self._monkey.get() + + @monkey.setter + def monkey(self, value: monkey_enum): + self._monkey.set(value) + + @property + def monkey2(self): + return self._monkey2.get() + + @monkey2.setter + def monkey2(self, value: monkey2_enum): + self._monkey2.set(value) + + @property + def monkey3(self): + return self._monkey3.get() + + @monkey3.setter + def monkey3(self, value: monkey3_enum): + self._monkey3.set(value) + + @property + def monkey4(self): + return self._monkey4.get() + + @monkey4.setter + def monkey4(self, value: monkey4_enum): + self._monkey4.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + +class reg3_type(Register): + """ + write something useful for reg3 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +class reg4_type(Register): + """ + reg4 is a very useful register. It can take down the moon when configured correctly. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg4 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg4(self): + return self._reg4.get() + + @reg4.setter + def reg4(self, value: int): + self._reg4.set(value) + + +class reg5_type(Register): + """ + reg5 is as useful as reg4 but without a reset value defined. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg5 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg5(self): + return self._reg5.get() + + @reg5.setter + def reg5(self, value: int): + self._reg5.set(value) + + +class reg6_type(Register): + """ + reg6 is a read only register. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg6 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-only", + minimum=None, + maximum=None, + ) + + @property + def reg6(self): + return self._reg6.get() + + @reg6.setter + def reg6(self, value: int): + self._reg6.set(value) + + +class reg7_type(Register): + """ + write something useful for reg7 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + # write something useful for nibble0 + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [7:4] + # unused + self._unused0 = IntegerField( + self, + bit_width=4, + bit_offset=4, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [15:12] + # unused + self._unused1 = IntegerField( + self, + bit_width=4, + bit_offset=12, + access="read-write", + minimum=None, + maximum=None, + ) + # [19:16] + # write something useful for nibble2 + self._nibble2 = IntegerField( + self, + bit_width=4, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:20] + # unused + self._unused2 = IntegerField( + self, + bit_width=12, + bit_offset=20, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + @property + def unused1(self): + return self._unused1.get() + + @unused1.setter + def unused1(self, value: int): + self._unused1.set(value) + + @property + def nibble2(self): + return self._nibble2.get() + + @nibble2.setter + def nibble2(self, value: int): + self._nibble2.set(value) + + @property + def unused2(self): + return self._unused2.get() + + @unused2.setter + def unused2(self, value: int): + self._unused2.set(value) + + +class reg8_type(Register): + """ + register with empty and no descriptions of the fields + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [7:4] + # unused + self._unused0 = IntegerField( + self, + bit_width=4, + bit_offset=4, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:12] + # unused + self._unused1 = IntegerField( + self, + bit_width=20, + bit_offset=12, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + @property + def unused1(self): + return self._unused1.get() + + @unused1.setter + def unused1(self, value: int): + self._unused1.set(value) + + +class example_type(IP): + def __init__(self, parent: IP, base_address=0, access_layer=accesLayer): + super().__init__(parent, base_address, access_layer) + + self.reg0 = reg0_type(self, address_offset=0x0) + self.reg1 = reg1_type(self, address_offset=0x1) + self.reg2 = reg2_type(self, address_offset=0x2) + self.reg3 = reg3_type(self, address_offset=0x3) + self.reg4 = reg4_type(self, address_offset=0x4) + self.reg5 = reg5_type(self, address_offset=0x5) + self.reg6 = reg6_type(self, address_offset=0x6) + self.reg7 = reg7_type(self, address_offset=0x7) + self.reg8 = reg8_type(self, address_offset=0x8) diff --git a/example/output_default/acces_layer.py b/example/output_default/acces_layer.py new file mode 100644 index 0000000..f51035f --- /dev/null +++ b/example/output_default/acces_layer.py @@ -0,0 +1,264 @@ +""" +Base types for the generated code are declared here, as well as exceptions and other supporting classes. +Functionality to read, write and modify registers is also provided here. Several methods are provided, all sharing the same +interface. +""" + + +class OutOfRangeReading(Exception): + """Exception thrown when an out of range value is read from the register.""" + pass + + +class OutOfRangeWriting(Exception): + """Exception thrown when an out of range value is attempted to be written to the register.""" + pass + + +class OutOfRangeSpecifying(Exception): + """Exception thrown when an a boundary is specified that is incoherent (e.g. with the number of bits in a field).""" + pass + + +class FieldNotWritable(Exception): + """Exception thrown when attempting to write to a read-only field.""" + pass + + +class accesLayer: + """This interface contains method declarations that should be used by classes that can implement any type of memory + access the registers (including simulated). """ + def read_register(self, addr, offset=0, width=32): + """ + Reads a register + :param addr: address + :param offset: bit offset + :param width: number of bits after the offset to read + :return: the requested bits from the register + """ + raise NotImplementedError() + + def modify_register(self, addr, data, offset=0, width=32): + """ + Partially writes a register + :param addr: address + :param addr: data to be written + :param offset: bit offset + :param width: number of bits after the offset to write + """ + raise NotImplementedError() + + def write_register(self, addr, data): + """ + writes a complete register + :param addr: address + :param addr: data to be written + """ + raise NotImplementedError() + + +class IP(): + def __init__(self, parent, base_address, acces_layer=accesLayer): + self._base_address = base_address + self._parent = parent + self._acces_layer = acces_layer + + def get_base_address(self): + return self._base_address + + def get_acces_layer(self): + return self._acces_layer + + +class Register(): + def __init__(self, parent_ip, address_offset): + """ + Constructor for a register of an IP + :param parent_ip: an IP object to which this register belongs + :param address_offset: the address offset (from the IP's base address) + """ + self._parent_ip = parent_ip + self._address_offset = address_offset + self._acces_layer = parent_ip.get_acces_layer() + + def get_address_offset(self): + """ + Returns the address offset + :return: the address offset + """ + return self._address_offset + + def get_parent_ip(self): + """ + Returns the parent IP object + :return: the parent IP object + """ + return self._parent_ip + + def get_acces_layer(self): + """ + Returns the acces layer object currently used. Usually it's the parent IP's access layer + :return: the acces layer object currently used + """ + return self._acces_layer + + def get(self): + """ + returns the value of this register (as unsigned int) + :return: the value of this register + """ + self.get_acces_layer().read_register(self.get_parent_ip().get_base_address() + + self.get_address_offset()) + + def set(self, value): + """ + Sets the value of this register (as unsigned int) + :value: the value to set this register + """ + self.get_acces_layer().write_register(self.get_parent_ip().get_base_address() + + self.get_address_offset(), value) + + +class Field(): + """This base class represents a field in the register. It will work like an unsigned integer field and is the prefered + way of obtaining 'raw values' from the registers but should be derived from and not instanced. """ + def __init__(self, parent_register, bit_width, bit_offset, access): + """ + Constructor for a Field of a register + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + """ + self._parent_register = parent_register + self._bit_width = bit_width + self._bit_offset = bit_offset + self._access = access + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + return self._parent_register.get_acces_layer().read_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + self._bit_offset, + self._bit_width) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if self._access == "read-only": + raise FieldNotWritable() + + self._parent_register.get_acces_layer().modify_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + value, + self._bit_offset, + self._bit_width) + + +class EnumField(Field): + """Represents an enumerated type.""" + def __init__(self, parent_register, bit_width, bit_offset, access, enum_type): + """ + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param enum_type: a Python type derived from IntEnum which we want to represent + """ + super().__init__(parent_register, bit_width, bit_offset, access) + self._enum_type = enum_type + self._int_values = list(map(int, enum_type)) + + def get(self): + """ + Returns the value of the field + :return: the current enumeration represented by the field + """ + val = super().get() + if val not in self._int_values: + return OutOfRangeReading() + return self._enum_type(val) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set. Either an integer or an enumeration of 'our' enumerated type + """ + if value in self._int_values: + super().set(value) + else: + raise OutOfRangeWriting() + + +class IntegerField(Field): + """This class represents an (unsigned) integer. It derives from Field and relies on it for abstracting reads and + writes. Therefore, this class is basically concerned with boundary checking.""" + def __init__(self, parent_register, bit_width, bit_offset, access, minimum, maximum): + """ + Constructor for a Field of a register + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param minimum: minimum value that this field can have (None to assume the minimum representable) + :param maximum: maximum value that this field can have (None to assume the maximum representable) + """ + super().__init__(parent_register, bit_width, bit_offset, access) + type_minimum = 0 + type_maximum = (2 ** bit_width) - 1 + if minimum is not None: + if minimum < type_minimum: + raise OutOfRangeSpecifying() + else: + self._minimum = minimum + else: + self._minimum = type_minimum + + if maximum is not None: + if maximum > type_maximum: + raise OutOfRangeSpecifying() + else: + self._maximum = maximum + else: + self._maximum = type_maximum + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + val = super().get() + if val < self._minimum or val > self._maximum: + raise OutOfRangeReading() + return val + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if value < self._minimum or value > self._maximum: + raise OutOfRangeWriting() + else: + super().set(value) + + def get_minimum(self): + """ + Gets the minimum value that can be defined for the field + :return: minimum + """ + return self._minimum + + def get_maximum(self): + """ + Gets the maximum value that can be defined for the field + :return: maximum + """ + return self._maximum diff --git a/example/output_default/example.py b/example/output_default/example.py new file mode 100644 index 0000000..fe102e4 --- /dev/null +++ b/example/output_default/example.py @@ -0,0 +1,650 @@ +# Automatically generated +# with the command '/home/labgrid/Thomas_vreys/ipxact2systemverilog/.venv/bin/ipxact2py --srcFile example/input/test.xml --destDir example/output_default -- config example/input/default.ini' +# +# Do not manually edit! +# + +from enum import IntEnum + +from acces_layer import * + + +class reg0_type(Register): + """ + write something useful for reg0 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [7:0] + # write something useful for field0 + self._byte0 = IntegerField( + self, + bit_width=8, + bit_offset=0, + access="read-write", + minimum=0, + maximum=7, + ) + # [15:8] + # write something useful for field1 + self._byte1 = IntegerField( + self, + bit_width=8, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [23:16] + # write something useful for field2 + self._byte2 = IntegerField( + self, + bit_width=8, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:24] + # write something useful for field3 + self._byte3 = IntegerField( + self, + bit_width=8, + bit_offset=24, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def byte0(self): + return self._byte0.get() + + @byte0.setter + def byte0(self, value: int): + self._byte0.set(value) + + @property + def byte1(self): + return self._byte1.get() + + @byte1.setter + def byte1(self, value: int): + self._byte1.set(value) + + @property + def byte2(self): + return self._byte2.get() + + @byte2.setter + def byte2(self, value: int): + self._byte2.set(value) + + @property + def byte3(self): + return self._byte3.get() + + @byte3.setter + def byte3(self, value: int): + self._byte3.set(value) + + +class reg1_type(Register): + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=4, + maximum=20, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +# which monkey +class monkey_enum(IntEnum): + chimp = 0x0 # a monkey + gorilla = 0x1 + phb = 0x2 # and another monkey + + +# which monkey +class monkey2_enum(IntEnum): + chimp = 0 + gorilla = 1 + phb = 2 + + +# which monkey +class monkey3_enum(IntEnum): + phb = 0 + gorilla = 1 + chimp = 2 + + +# which monkey +class monkey4_enum(IntEnum): + chimp = 0 + gorilla = 1 + bonobo = 2 + + +class reg2_type(Register): + """ + write something useful for reg2 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [0] + # write something useful for field power + self._power = IntegerField( + self, + bit_width=1, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [1] + # write something useful for field power2 + self._power2 = IntegerField( + self, + bit_width=1, + bit_offset=1, + access="read-write", + minimum=None, + maximum=None, + ) + # [3:2] + # which monkey + self._monkey = EnumField( + self, + bit_width=2, + bit_offset=2, + access="read-write", + enum_type=monkey_enum, + ) + # [5:4] + # which monkey + self._monkey2 = EnumField( + self, + bit_width=2, + bit_offset=4, + access="read-write", + enum_type=monkey2_enum, + ) + # [7:6] + # which monkey + self._monkey3 = EnumField( + self, + bit_width=2, + bit_offset=6, + access="read-write", + enum_type=monkey3_enum, + ) + # [9:8] + # which monkey + self._monkey4 = EnumField( + self, + bit_width=2, + bit_offset=8, + access="read-write", + enum_type=monkey4_enum, + ) + # [31:10] + # unused + self._unused0 = IntegerField( + self, + bit_width=22, + bit_offset=10, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def power(self): + return self._power.get() + + @power.setter + def power(self, value: int): + self._power.set(value) + + @property + def power2(self): + return self._power2.get() + + @power2.setter + def power2(self, value: int): + self._power2.set(value) + + @property + def monkey(self): + return self._monkey.get() + + @monkey.setter + def monkey(self, value: monkey_enum): + self._monkey.set(value) + + @property + def monkey2(self): + return self._monkey2.get() + + @monkey2.setter + def monkey2(self, value: monkey2_enum): + self._monkey2.set(value) + + @property + def monkey3(self): + return self._monkey3.get() + + @monkey3.setter + def monkey3(self, value: monkey3_enum): + self._monkey3.set(value) + + @property + def monkey4(self): + return self._monkey4.get() + + @monkey4.setter + def monkey4(self, value: monkey4_enum): + self._monkey4.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + +class reg3_type(Register): + """ + write something useful for reg3 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +class reg4_type(Register): + """ + reg4 is a very useful register. It can take down the moon when configured correctly. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg4 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg4(self): + return self._reg4.get() + + @reg4.setter + def reg4(self, value: int): + self._reg4.set(value) + + +class reg5_type(Register): + """ + reg5 is as useful as reg4 but without a reset value defined. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg5 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg5(self): + return self._reg5.get() + + @reg5.setter + def reg5(self, value: int): + self._reg5.set(value) + + +class reg6_type(Register): + """ + reg6 is a read only register. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg6 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-only", + minimum=None, + maximum=None, + ) + + @property + def reg6(self): + return self._reg6.get() + + @reg6.setter + def reg6(self, value: int): + self._reg6.set(value) + + +class reg7_type(Register): + """ + write something useful for reg7 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + # write something useful for nibble0 + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [7:4] + # unused + self._unused0 = IntegerField( + self, + bit_width=4, + bit_offset=4, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [15:12] + # unused + self._unused1 = IntegerField( + self, + bit_width=4, + bit_offset=12, + access="read-write", + minimum=None, + maximum=None, + ) + # [19:16] + # write something useful for nibble2 + self._nibble2 = IntegerField( + self, + bit_width=4, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:20] + # unused + self._unused2 = IntegerField( + self, + bit_width=12, + bit_offset=20, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + @property + def unused1(self): + return self._unused1.get() + + @unused1.setter + def unused1(self, value: int): + self._unused1.set(value) + + @property + def nibble2(self): + return self._nibble2.get() + + @nibble2.setter + def nibble2(self, value: int): + self._nibble2.set(value) + + @property + def unused2(self): + return self._unused2.get() + + @unused2.setter + def unused2(self, value: int): + self._unused2.set(value) + + +class reg8_type(Register): + """ + register with empty and no descriptions of the fields + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [7:4] + # unused + self._unused0 = IntegerField( + self, + bit_width=4, + bit_offset=4, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:12] + # unused + self._unused1 = IntegerField( + self, + bit_width=20, + bit_offset=12, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def unused0(self): + return self._unused0.get() + + @unused0.setter + def unused0(self, value: int): + self._unused0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + @property + def unused1(self): + return self._unused1.get() + + @unused1.setter + def unused1(self, value: int): + self._unused1.set(value) + + +class example_type(IP): + def __init__(self, parent: IP, base_address=0, access_layer=accesLayer): + super().__init__(parent, base_address, access_layer) + + self.reg0 = reg0_type(self, address_offset=0x0) + self.reg1 = reg1_type(self, address_offset=0x1) + self.reg2 = reg2_type(self, address_offset=0x2) + self.reg3 = reg3_type(self, address_offset=0x3) + self.reg4 = reg4_type(self, address_offset=0x4) + self.reg5 = reg5_type(self, address_offset=0x5) + self.reg6 = reg6_type(self, address_offset=0x6) + self.reg7 = reg7_type(self, address_offset=0x7) + self.reg8 = reg8_type(self, address_offset=0x8) diff --git a/example/output_no_default/__init__.py b/example/output_no_default/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example/output_no_default/acces_layer.py b/example/output_no_default/acces_layer.py new file mode 100644 index 0000000..f51035f --- /dev/null +++ b/example/output_no_default/acces_layer.py @@ -0,0 +1,264 @@ +""" +Base types for the generated code are declared here, as well as exceptions and other supporting classes. +Functionality to read, write and modify registers is also provided here. Several methods are provided, all sharing the same +interface. +""" + + +class OutOfRangeReading(Exception): + """Exception thrown when an out of range value is read from the register.""" + pass + + +class OutOfRangeWriting(Exception): + """Exception thrown when an out of range value is attempted to be written to the register.""" + pass + + +class OutOfRangeSpecifying(Exception): + """Exception thrown when an a boundary is specified that is incoherent (e.g. with the number of bits in a field).""" + pass + + +class FieldNotWritable(Exception): + """Exception thrown when attempting to write to a read-only field.""" + pass + + +class accesLayer: + """This interface contains method declarations that should be used by classes that can implement any type of memory + access the registers (including simulated). """ + def read_register(self, addr, offset=0, width=32): + """ + Reads a register + :param addr: address + :param offset: bit offset + :param width: number of bits after the offset to read + :return: the requested bits from the register + """ + raise NotImplementedError() + + def modify_register(self, addr, data, offset=0, width=32): + """ + Partially writes a register + :param addr: address + :param addr: data to be written + :param offset: bit offset + :param width: number of bits after the offset to write + """ + raise NotImplementedError() + + def write_register(self, addr, data): + """ + writes a complete register + :param addr: address + :param addr: data to be written + """ + raise NotImplementedError() + + +class IP(): + def __init__(self, parent, base_address, acces_layer=accesLayer): + self._base_address = base_address + self._parent = parent + self._acces_layer = acces_layer + + def get_base_address(self): + return self._base_address + + def get_acces_layer(self): + return self._acces_layer + + +class Register(): + def __init__(self, parent_ip, address_offset): + """ + Constructor for a register of an IP + :param parent_ip: an IP object to which this register belongs + :param address_offset: the address offset (from the IP's base address) + """ + self._parent_ip = parent_ip + self._address_offset = address_offset + self._acces_layer = parent_ip.get_acces_layer() + + def get_address_offset(self): + """ + Returns the address offset + :return: the address offset + """ + return self._address_offset + + def get_parent_ip(self): + """ + Returns the parent IP object + :return: the parent IP object + """ + return self._parent_ip + + def get_acces_layer(self): + """ + Returns the acces layer object currently used. Usually it's the parent IP's access layer + :return: the acces layer object currently used + """ + return self._acces_layer + + def get(self): + """ + returns the value of this register (as unsigned int) + :return: the value of this register + """ + self.get_acces_layer().read_register(self.get_parent_ip().get_base_address() + + self.get_address_offset()) + + def set(self, value): + """ + Sets the value of this register (as unsigned int) + :value: the value to set this register + """ + self.get_acces_layer().write_register(self.get_parent_ip().get_base_address() + + self.get_address_offset(), value) + + +class Field(): + """This base class represents a field in the register. It will work like an unsigned integer field and is the prefered + way of obtaining 'raw values' from the registers but should be derived from and not instanced. """ + def __init__(self, parent_register, bit_width, bit_offset, access): + """ + Constructor for a Field of a register + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + """ + self._parent_register = parent_register + self._bit_width = bit_width + self._bit_offset = bit_offset + self._access = access + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + return self._parent_register.get_acces_layer().read_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + self._bit_offset, + self._bit_width) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if self._access == "read-only": + raise FieldNotWritable() + + self._parent_register.get_acces_layer().modify_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + value, + self._bit_offset, + self._bit_width) + + +class EnumField(Field): + """Represents an enumerated type.""" + def __init__(self, parent_register, bit_width, bit_offset, access, enum_type): + """ + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param enum_type: a Python type derived from IntEnum which we want to represent + """ + super().__init__(parent_register, bit_width, bit_offset, access) + self._enum_type = enum_type + self._int_values = list(map(int, enum_type)) + + def get(self): + """ + Returns the value of the field + :return: the current enumeration represented by the field + """ + val = super().get() + if val not in self._int_values: + return OutOfRangeReading() + return self._enum_type(val) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set. Either an integer or an enumeration of 'our' enumerated type + """ + if value in self._int_values: + super().set(value) + else: + raise OutOfRangeWriting() + + +class IntegerField(Field): + """This class represents an (unsigned) integer. It derives from Field and relies on it for abstracting reads and + writes. Therefore, this class is basically concerned with boundary checking.""" + def __init__(self, parent_register, bit_width, bit_offset, access, minimum, maximum): + """ + Constructor for a Field of a register + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param minimum: minimum value that this field can have (None to assume the minimum representable) + :param maximum: maximum value that this field can have (None to assume the maximum representable) + """ + super().__init__(parent_register, bit_width, bit_offset, access) + type_minimum = 0 + type_maximum = (2 ** bit_width) - 1 + if minimum is not None: + if minimum < type_minimum: + raise OutOfRangeSpecifying() + else: + self._minimum = minimum + else: + self._minimum = type_minimum + + if maximum is not None: + if maximum > type_maximum: + raise OutOfRangeSpecifying() + else: + self._maximum = maximum + else: + self._maximum = type_maximum + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + val = super().get() + if val < self._minimum or val > self._maximum: + raise OutOfRangeReading() + return val + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if value < self._minimum or value > self._maximum: + raise OutOfRangeWriting() + else: + super().set(value) + + def get_minimum(self): + """ + Gets the minimum value that can be defined for the field + :return: minimum + """ + return self._minimum + + def get_maximum(self): + """ + Gets the maximum value that can be defined for the field + :return: maximum + """ + return self._maximum diff --git a/example/output_no_default/example.py b/example/output_no_default/example.py new file mode 100644 index 0000000..9df5fc3 --- /dev/null +++ b/example/output_no_default/example.py @@ -0,0 +1,552 @@ +# Automatically generated +# with the command '/home/labgrid/Thomas_vreys/ipxact2systemverilog/.venv/bin/ipxact2py --srcFile example/input/test.xml --destDir example/output_no_default --config example/input/no_default.ini' +# +# Do not manually edit! +# + +from enum import IntEnum + +from .acces_layer import * + + +class reg0_type(Register): + """ + write something useful for reg0 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [7:0] + # write something useful for field0 + self._byte0 = IntegerField( + self, + bit_width=8, + bit_offset=0, + access="read-write", + minimum=0, + maximum=7, + ) + # [15:8] + # write something useful for field1 + self._byte1 = IntegerField( + self, + bit_width=8, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [23:16] + # write something useful for field2 + self._byte2 = IntegerField( + self, + bit_width=8, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + # [31:24] + # write something useful for field3 + self._byte3 = IntegerField( + self, + bit_width=8, + bit_offset=24, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def byte0(self): + return self._byte0.get() + + @byte0.setter + def byte0(self, value: int): + self._byte0.set(value) + + @property + def byte1(self): + return self._byte1.get() + + @byte1.setter + def byte1(self, value: int): + self._byte1.set(value) + + @property + def byte2(self): + return self._byte2.get() + + @byte2.setter + def byte2(self, value: int): + self._byte2.set(value) + + @property + def byte3(self): + return self._byte3.get() + + @byte3.setter + def byte3(self, value: int): + self._byte3.set(value) + + +class reg1_type(Register): + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=4, + maximum=20, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +# write something useful for field power +class power_enum(IntEnum): + false = 0 # disable + true = 1 # enable + + +# write something useful for field power2 +class power2_enum(IntEnum): + false = 0 + true = 1 + + +# which monkey +class monkey_enum(IntEnum): + chimp = 0x0 # a monkey + gorilla = 0x1 + phb = 0x2 # and another monkey + + +# which monkey +class monkey2_enum(IntEnum): + chimp = 0 + gorilla = 1 + phb = 2 + + +# which monkey +class monkey3_enum(IntEnum): + phb = 0 + gorilla = 1 + chimp = 2 + + +# which monkey +class monkey4_enum(IntEnum): + chimp = 0 + gorilla = 1 + bonobo = 2 + + +class reg2_type(Register): + """ + write something useful for reg2 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [0] + # write something useful for field power + self._power = EnumField( + self, + bit_width=1, + bit_offset=0, + access="read-write", + enum_type=power_enum, + ) + # [1] + # write something useful for field power2 + self._power2 = EnumField( + self, + bit_width=1, + bit_offset=1, + access="read-write", + enum_type=power2_enum, + ) + # [3:2] + # which monkey + self._monkey = EnumField( + self, + bit_width=2, + bit_offset=2, + access="read-write", + enum_type=monkey_enum, + ) + # [5:4] + # which monkey + self._monkey2 = EnumField( + self, + bit_width=2, + bit_offset=4, + access="read-write", + enum_type=monkey2_enum, + ) + # [7:6] + # which monkey + self._monkey3 = EnumField( + self, + bit_width=2, + bit_offset=6, + access="read-write", + enum_type=monkey3_enum, + ) + # [9:8] + # which monkey + self._monkey4 = EnumField( + self, + bit_width=2, + bit_offset=8, + access="read-write", + enum_type=monkey4_enum, + ) + + @property + def power(self): + return self._power.get() + + @power.setter + def power(self, value: power_enum): + self._power.set(value) + + @property + def power2(self): + return self._power2.get() + + @power2.setter + def power2(self, value: power2_enum): + self._power2.set(value) + + @property + def monkey(self): + return self._monkey.get() + + @monkey.setter + def monkey(self, value: monkey_enum): + self._monkey.set(value) + + @property + def monkey2(self): + return self._monkey2.get() + + @monkey2.setter + def monkey2(self, value: monkey2_enum): + self._monkey2.set(value) + + @property + def monkey3(self): + return self._monkey3.get() + + @monkey3.setter + def monkey3(self, value: monkey3_enum): + self._monkey3.set(value) + + @property + def monkey4(self): + return self._monkey4.get() + + @monkey4.setter + def monkey4(self, value: monkey4_enum): + self._monkey4.set(value) + + +class reg3_type(Register): + """ + write something useful for reg3 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + # write something useful for field0 + self._field0 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def field0(self): + return self._field0.get() + + @field0.setter + def field0(self, value: int): + self._field0.set(value) + + +class reg4_type(Register): + """ + reg4 is a very useful register. It can take down the moon when configured correctly. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg4 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg4(self): + return self._reg4.get() + + @reg4.setter + def reg4(self, value: int): + self._reg4.set(value) + + +class reg5_type(Register): + """ + reg5 is as useful as reg4 but without a reset value defined. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg5 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def reg5(self): + return self._reg5.get() + + @reg5.setter + def reg5(self, value: int): + self._reg5.set(value) + + +class reg6_type(Register): + """ + reg6 is a read only register. + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [31:0] + self._reg6 = IntegerField( + self, + bit_width=32, + bit_offset=0, + access="read-only", + minimum=None, + maximum=None, + ) + + @property + def reg6(self): + return self._reg6.get() + + @reg6.setter + def reg6(self, value: int): + self._reg6.set(value) + + +class reg7_type(Register): + """ + write something useful for reg7 + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + # write something useful for nibble0 + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + # [19:16] + # write something useful for nibble2 + self._nibble2 = IntegerField( + self, + bit_width=4, + bit_offset=16, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + @property + def nibble2(self): + return self._nibble2.get() + + @nibble2.setter + def nibble2(self, value: int): + self._nibble2.set(value) + + +class reg8_type(Register): + """ + register with empty and no descriptions of the fields + """ + + def __init__( + self, + parent_ip: IP, + address_offset: int, + ): + super().__init__( + parent_ip, + address_offset, + ) + # [3:0] + self._nibble0 = IntegerField( + self, + bit_width=4, + bit_offset=0, + access="read-write", + minimum=None, + maximum=None, + ) + # [11:8] + self._nibble1 = IntegerField( + self, + bit_width=4, + bit_offset=8, + access="read-write", + minimum=None, + maximum=None, + ) + + @property + def nibble0(self): + return self._nibble0.get() + + @nibble0.setter + def nibble0(self, value: int): + self._nibble0.set(value) + + @property + def nibble1(self): + return self._nibble1.get() + + @nibble1.setter + def nibble1(self, value: int): + self._nibble1.set(value) + + +class example_type(IP): + def __init__(self, parent: IP, base_address=0, access_layer=accesLayer): + super().__init__(parent, base_address, access_layer) + + self.reg0 = reg0_type(self, address_offset=0x0) + self.reg1 = reg1_type(self, address_offset=0x1) + self.reg2 = reg2_type(self, address_offset=0x2) + self.reg3 = reg3_type(self, address_offset=0x3) + self.reg4 = reg4_type(self, address_offset=0x4) + self.reg5 = reg5_type(self, address_offset=0x5) + self.reg6 = reg6_type(self, address_offset=0x6) + self.reg7 = reg7_type(self, address_offset=0x7) + self.reg8 = reg8_type(self, address_offset=0x8) diff --git a/example/output_sphinx/example.pdf b/example/output_sphinx/example.pdf index 481c9cc..28e0201 100644 Binary files a/example/output_sphinx/example.pdf and b/example/output_sphinx/example.pdf differ diff --git a/ipxact2systemverilog/cli.py b/ipxact2systemverilog/cli.py index f8a7c77..d2ef0d1 100644 --- a/ipxact2systemverilog/cli.py +++ b/ipxact2systemverilog/cli.py @@ -9,6 +9,7 @@ from .ipxact2hdlCommon import rstAddressBlock from .ipxact2hdlCommon import systemVerilogAddressBlock from .ipxact2hdlCommon import vhdlAddressBlock +from ipxact2systemverilog.ipxact2hdlCommon import pyAddressBlock from .ipxact2hdlCommon import DEFAULT_INI from .validate import validate @@ -18,7 +19,7 @@ def main_c(): parser.add_argument('-d', '--destDir', help="write generated file to dir", required=True) parser.add_argument('-c', '--config', help="configuration ini file") - args, unknown_args = parser.parse_known_args() + args, _ = parser.parse_known_args() if not validate(args.srcFile): print(f"{args.srcFile} doesn't validate") @@ -132,3 +133,27 @@ def main_vhdl(): generator = ipxact2otherGenerator(args.destDir, config) generator.generate(vhdlAddressBlock, document) +def main_py(): + parser = argparse.ArgumentParser(description='ipxact2python') + parser.add_argument('-s', '--srcFile', help='ipxact xml input file', required=True) + parser.add_argument('-d', '--destDir', help="write generated file to dir", required=True) + parser.add_argument('-c', '--config', help="configuration ini file") + + args, _ = parser.parse_known_args() + + if not validate(args.srcFile): + print("%s doesn't validate" % args.srcFile) + sys.exit(1) + + config = configparser.ConfigParser() + if args.config: + config.read_dict(DEFAULT_INI) + config.read(args.config) + else: + config.read_dict(DEFAULT_INI) + + e = ipxactParser(args.srcFile, config) + document = e.returnDocument() + generator = ipxact2otherGenerator(args.destDir, config) + generator.generate(pyAddressBlock, document) + diff --git a/ipxact2systemverilog/ipxact2hdlCommon.py b/ipxact2systemverilog/ipxact2hdlCommon.py index df4836c..8dcde80 100644 --- a/ipxact2systemverilog/ipxact2hdlCommon.py +++ b/ipxact2systemverilog/ipxact2hdlCommon.py @@ -30,10 +30,11 @@ DEFAULT_INI = {'global': {'unusedholes': 'yes', 'onebitenum': 'no'}, - 'vhdl': {'PublicConvFunct': 'no', + 'vhdl': {'PublicConvFunct': 'no', 'std': 'unresolved'}, - 'rst': {'sphinx': 'no', - 'wavedrom': 'no'}} + 'rst': {'sphinx': 'no', + 'wavedrom': 'no'}, + 'py': {'imports': 'absolute'}} def sortRegisterAndFillHoles(regName, @@ -189,6 +190,159 @@ def compare(self, other): def compareLists(self, list1, list2): return list1 == list2 + + +class pyAddressBlock(addressBlockClass): + """Generates a Python file from a IP-XACT register description""" + + def __init__(self, name, description, baseAddress, addrWidth, dataWidth, config): + self.name = name + self.description = description + self.baseAddress = baseAddress + self.addrWidth = addrWidth + self.dataWidth = dataWidth + self.registerList = [] + self.suffix = ".py" + self.library = "" + self.config = config + if self.config['py']['imports'] == "absolute": + self.imports = "absolute" + else: + self.imports = "relative" + + + def returnAsString(self): + r = '' + r += self.returnPkgHeaderString() + r += self.returnObjectClass() + r += self.returnRegistersClass() + r += self.returnIPClass() + return r + + def returnIncludeString(self): + lib_file = os.path.join(os.path.dirname(__file__), "ipxact2pyCommon" + self.suffix) + f = open(lib_file, "r", encoding='utf-8') + return f.read() + + def returnPkgHeaderString(self): + r = '' + r += "# Automatically generated\n" + r += f"# with the command '{' '.join(sys.argv)}'\n" + r += "#\n" + r += "# Do not manually edit!\n" + r += "#\n" + r += "\n" + return r + + def returnObjectClass(self): + r = '' + r += "from enum import IntEnum\n\n" + + if self.imports == "absolute": + r += "from acces_layer import *\n" + else: + r += "from .acces_layer import *\n" + + r += "\n\n" + return r + + def returnRegistersClass(self): + r = '' + + # Do for all registers + for reg in self.registerList: + + for i, enum in enumerate(reg.enumTypeList): + if enum: + r += f"# {reg.fieldDescList[i]}\n" + r += f"class {enum.name}_enum(IntEnum):\n" + for i in range(len(enum.keyList)): + r += f" {enum.keyList[i]} = {enum.valueList[i]}" + if enum.descrList[i]: + r += f" # {enum.descrList[i]}" + + r += "\n" + r += "\n\n" + + r += f"class {reg.name}_type(Register):\n" + if reg.desc: + r += ' """\n' + r += f' {reg.desc}\n' + r += ' """\n\n' + + # r += " def __init__(self, parent_ip, address_offset):\n" + r += " def __init__(\n" + r += " self,\n" + r += " parent_ip: IP,\n" + r += " address_offset: int,\n" + r += " ):\n" + # r += " super().__init__(parent_ip, address_offset)\n" + r += " super().__init__(\n" + r += " parent_ip,\n" + r += " address_offset,\n" + r += " )" + r += "\n" + + for i in list(range(len(reg.fieldNameList))): + bits = f"[{reg.bitOffsetList[i] + reg.bitWidthList[i] - 1}:{reg.bitOffsetList[i]}]" + bit = f"[{reg.bitOffsetList[i]}]" + if reg.bitWidthList[i] == 1: # field with only one bit + r += f" # {bit}\n" + else: + r += f" # {bits}\n" + if reg.fieldDescList[i]: + desc_lines = reg.fieldDescList[i].split("\n") + for line in desc_lines: + r += f' # {line}\n' + if reg.enumTypeList[i]: + r += f' self._{reg.fieldNameList[i]} = EnumField(\n' + r += f' self,\n' + r += f' bit_width={reg.bitWidthList[i]},\n' + r += f' bit_offset={reg.bitOffsetList[i]},\n' + r += f' access="{reg.access}",\n' + r += f' enum_type={reg.enumTypeList[i].name}_enum,\n' + r += f' )' + else: + r += f' self._{reg.fieldNameList[i]} = IntegerField(\n' + r += f' self,\n' + r += f' bit_width={reg.bitWidthList[i]},\n' + r += f' bit_offset={reg.bitOffsetList[i]},\n' + r += f' access="{reg.access}",\n' + r += f' minimum={reg.fieldMinimumConstraintsList[i]},\n' + r += f' maximum={reg.fieldMaximumConstraintsList[i]},\n' + r += f' )' + r += "\n" + + r += "\n" + + for i in list(range(len(reg.fieldNameList))): + r += " @property\n" + r += f" def {reg.fieldNameList[i]}(self):\n" + r += f" return self._{reg.fieldNameList[i]}.get()\n" + r += "\n" + r += f" @{reg.fieldNameList[i]}.setter\n" + if reg.enumTypeList[i]: + r += f" def {reg.fieldNameList[i]}(self, value: {reg.enumTypeList[i].name}_enum):\n" + else: + r += f" def {reg.fieldNameList[i]}(self, value: int):\n" + r += f" self._{reg.fieldNameList[i]}.set(value)\n" + r += "\n" + + r += "\n" + + return r + + def returnIPClass(self): + r = '' + r += f"class {self.name}_type(IP):\n" + r += " def __init__(self, parent: IP, base_address=0, access_layer=accesLayer):\n" + r += " super().__init__(parent, base_address, access_layer)\n\n" + # Do for all registers + _width = math.ceil(self.addrWidth / 4) + 2 # +2 for the '0x' + for reg in self.registerList: + r += f" self.{reg.name} = {reg.name}_type(self, address_offset={reg.address:#0{_width}x})\n" + + return r class rstAddressBlock(addressBlockClass): @@ -1131,8 +1285,17 @@ def __init__(self, srcFile, config): self.config = config self.enumTypeClassRegistry = enumTypeClassRegistry() + def getXmlSchema(self): + tree = ETree.parse(self.srcFile) + root = tree.getroot() + namespace_uri = root.tag.split('}', 1)[0][1:] + version = namespace_uri.split('/')[-1] + schema = 'http://www.spiritconsortium.org/XMLSchema/SPIRIT/'+version + return schema + + def returnDocument(self): - spirit_ns = 'http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.5' + spirit_ns = self.getXmlSchema() tree = ETree.parse(self.srcFile) ETree.register_namespace('spirit', spirit_ns) namespace = tree.getroot().tag[1:].split("}")[0] @@ -1308,3 +1471,9 @@ def generate(self, generatorClass, document): includeFileName = fileName + "h" includeString = block.returnIncludeString() self.write(includeFileName, includeString) + elif generatorClass == pyAddressBlock: + includeFileName = "acces_layer" + ".py" + includeString = block.returnIncludeString() + self.write(includeFileName, includeString) + if block.imports == "relative": + self.write("__init__.py", "") diff --git a/ipxact2systemverilog/ipxact2pyCommon.py b/ipxact2systemverilog/ipxact2pyCommon.py new file mode 100644 index 0000000..f51035f --- /dev/null +++ b/ipxact2systemverilog/ipxact2pyCommon.py @@ -0,0 +1,264 @@ +""" +Base types for the generated code are declared here, as well as exceptions and other supporting classes. +Functionality to read, write and modify registers is also provided here. Several methods are provided, all sharing the same +interface. +""" + + +class OutOfRangeReading(Exception): + """Exception thrown when an out of range value is read from the register.""" + pass + + +class OutOfRangeWriting(Exception): + """Exception thrown when an out of range value is attempted to be written to the register.""" + pass + + +class OutOfRangeSpecifying(Exception): + """Exception thrown when an a boundary is specified that is incoherent (e.g. with the number of bits in a field).""" + pass + + +class FieldNotWritable(Exception): + """Exception thrown when attempting to write to a read-only field.""" + pass + + +class accesLayer: + """This interface contains method declarations that should be used by classes that can implement any type of memory + access the registers (including simulated). """ + def read_register(self, addr, offset=0, width=32): + """ + Reads a register + :param addr: address + :param offset: bit offset + :param width: number of bits after the offset to read + :return: the requested bits from the register + """ + raise NotImplementedError() + + def modify_register(self, addr, data, offset=0, width=32): + """ + Partially writes a register + :param addr: address + :param addr: data to be written + :param offset: bit offset + :param width: number of bits after the offset to write + """ + raise NotImplementedError() + + def write_register(self, addr, data): + """ + writes a complete register + :param addr: address + :param addr: data to be written + """ + raise NotImplementedError() + + +class IP(): + def __init__(self, parent, base_address, acces_layer=accesLayer): + self._base_address = base_address + self._parent = parent + self._acces_layer = acces_layer + + def get_base_address(self): + return self._base_address + + def get_acces_layer(self): + return self._acces_layer + + +class Register(): + def __init__(self, parent_ip, address_offset): + """ + Constructor for a register of an IP + :param parent_ip: an IP object to which this register belongs + :param address_offset: the address offset (from the IP's base address) + """ + self._parent_ip = parent_ip + self._address_offset = address_offset + self._acces_layer = parent_ip.get_acces_layer() + + def get_address_offset(self): + """ + Returns the address offset + :return: the address offset + """ + return self._address_offset + + def get_parent_ip(self): + """ + Returns the parent IP object + :return: the parent IP object + """ + return self._parent_ip + + def get_acces_layer(self): + """ + Returns the acces layer object currently used. Usually it's the parent IP's access layer + :return: the acces layer object currently used + """ + return self._acces_layer + + def get(self): + """ + returns the value of this register (as unsigned int) + :return: the value of this register + """ + self.get_acces_layer().read_register(self.get_parent_ip().get_base_address() + + self.get_address_offset()) + + def set(self, value): + """ + Sets the value of this register (as unsigned int) + :value: the value to set this register + """ + self.get_acces_layer().write_register(self.get_parent_ip().get_base_address() + + self.get_address_offset(), value) + + +class Field(): + """This base class represents a field in the register. It will work like an unsigned integer field and is the prefered + way of obtaining 'raw values' from the registers but should be derived from and not instanced. """ + def __init__(self, parent_register, bit_width, bit_offset, access): + """ + Constructor for a Field of a register + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + """ + self._parent_register = parent_register + self._bit_width = bit_width + self._bit_offset = bit_offset + self._access = access + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + return self._parent_register.get_acces_layer().read_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + self._bit_offset, + self._bit_width) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if self._access == "read-only": + raise FieldNotWritable() + + self._parent_register.get_acces_layer().modify_register(self._parent_register.get_parent_ip().get_base_address() + + self._parent_register.get_address_offset(), + value, + self._bit_offset, + self._bit_width) + + +class EnumField(Field): + """Represents an enumerated type.""" + def __init__(self, parent_register, bit_width, bit_offset, access, enum_type): + """ + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param enum_type: a Python type derived from IntEnum which we want to represent + """ + super().__init__(parent_register, bit_width, bit_offset, access) + self._enum_type = enum_type + self._int_values = list(map(int, enum_type)) + + def get(self): + """ + Returns the value of the field + :return: the current enumeration represented by the field + """ + val = super().get() + if val not in self._int_values: + return OutOfRangeReading() + return self._enum_type(val) + + def set(self, value): + """ + Sets the value of the field + :value: value to be set. Either an integer or an enumeration of 'our' enumerated type + """ + if value in self._int_values: + super().set(value) + else: + raise OutOfRangeWriting() + + +class IntegerField(Field): + """This class represents an (unsigned) integer. It derives from Field and relies on it for abstracting reads and + writes. Therefore, this class is basically concerned with boundary checking.""" + def __init__(self, parent_register, bit_width, bit_offset, access, minimum, maximum): + """ + Constructor for a Field of a register + :param name: name + :param parent_register: parent register + :param bit_width: number of bits used by the field + :param bit_offset: bit offset from the beginning of the register + :param acces: a string for accessibility ('read-write' / 'read-only') + :param minimum: minimum value that this field can have (None to assume the minimum representable) + :param maximum: maximum value that this field can have (None to assume the maximum representable) + """ + super().__init__(parent_register, bit_width, bit_offset, access) + type_minimum = 0 + type_maximum = (2 ** bit_width) - 1 + if minimum is not None: + if minimum < type_minimum: + raise OutOfRangeSpecifying() + else: + self._minimum = minimum + else: + self._minimum = type_minimum + + if maximum is not None: + if maximum > type_maximum: + raise OutOfRangeSpecifying() + else: + self._maximum = maximum + else: + self._maximum = type_maximum + + def get(self): + """ + Returns the value of the field + :return: value of the field + """ + val = super().get() + if val < self._minimum or val > self._maximum: + raise OutOfRangeReading() + return val + + def set(self, value): + """ + Sets the value of the field + :value: value to be set + """ + if value < self._minimum or value > self._maximum: + raise OutOfRangeWriting() + else: + super().set(value) + + def get_minimum(self): + """ + Gets the minimum value that can be defined for the field + :return: minimum + """ + return self._minimum + + def get_maximum(self): + """ + Gets the maximum value that can be defined for the field + :return: maximum + """ + return self._maximum diff --git a/pyproject.toml b/pyproject.toml index 481470f..a97111c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ name = "ipxact2systemverilog" authors = [ {name = "oddball"}, ] -keywords = ["ipxact2systemverilog", "ipxact2vhdl", "VHDL", "SystemVerilog", "html", "rst", "md", "pdf", "IPXACT"] +keywords = ["ipxact2systemverilog", "ipxact2vhdl", "ipxact2c", "ipxact2py", "VHDL", "SystemVerilog", "html", "rst", "md", "pdf", "IPXACT", "python"] requires-python = ">= 3" dependencies = [ "docutils", @@ -39,6 +39,7 @@ ipxact2md = "ipxact2systemverilog.cli:main_md" ipxact2rst = "ipxact2systemverilog.cli:main_rst" ipxact2systemverilog = "ipxact2systemverilog.cli:main_systemverilog" ipxact2vhdl = "ipxact2systemverilog.cli:main_vhdl" +ipxact2py = "ipxact2systemverilog.cli:main_py" [project.urls]