axilent


Nameaxilent JSON
Version 0.1.8 PyPI version JSON
download
home_pagehttps://github.com/benreynwar/axilent
SummaryTools for describing a sequence of Axi4Lite commands.
upload_time2021-01-14 17:43:30
maintainer
docs_urlNone
authorBen Reynwar
requires_python
licenseMIT
keywords vhdl hdl rtl fpga asic xilinx altera
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            .. image:: https://travis-ci.org/benreynwar/axilent.svg?branch=master
               :target: https://travis-ci.org/benreynwar/axilent

axilent
=======

axilent provides python tools for describing a sequence of Axi4Lite command
in python.

A python test can be written describing the sequence of commands to be sent
and the expected responses.  This test can then be run against both a
simulation of the design and against the implemented design running on an
FPGA.

axilent is structured so that tests can be run both against both interactive
simulations and simulations where inputs cannot depend on outputs such as
file-based testbenchs.


Handlers
--------

Currently there is `handler` for working with file-based testbenchs
generated by `slvcodec <https://github.com/benreynwar/slvcodec>`_ and
for simulations on FPGAs run over JTAG using
`pyvivado <https://github.com/benreynwar/pyvivado>`_.
In the likely event you are using neither of these methods, then you
will have to write you own handler. The handler has a single required
method.

handler.send(command)
    Sends a command to the FPGA, or adds the command to the list of commands that
    will be run in the simulation.

The handler also processes the responses from the simulation or FPGA and updates the
`command.future` values appropriately.  It calls the `command.process_responses` method
to help process the responses.


Commands
--------
A `Command` object represents a simple axi4lite read or write, or a more complex
aggregrate consisting of many read and write commands.

command.get_axi_commands()
    Returns a list of `AxiCommand` objects.  Each `AxiCommand` object is a simple
    axi4lite read or write command to a single address or a range of addresses.

command.process_responses(read_responses, write_responses, resolve_future)
    Processes two lists of `AxiResponse` objects, one is a list of responses to
    write requests and one is a list of responses to read requests.  The two lists
    must begin with the responses to this command, and these entries are removed
    from the lists.
    An (exception, result) tuple is returned by the function.  If `resolve_future`
    is True then the commands future is resolved with the result and exception.

As an example consider an AxiAdder module, which uses an Axi4Lite interface.  We can
write to registers A and B, and when we read from register C the returned result is
the sum of the contents in regsiters A and B.  We can create a `Command` object to
represent using this hardware to add two numbers.

.. code:: python

    class AddNumbersCommand(comms.CombinedCommand):
        '''
        A command that writes to the intA and intB registers
        and then reads from the intC register.
        The effect is the add the two inputs.
        '''

        def __init__(self, a, b, addresses):
            write_a_command = comms.SetUnsignedCommand(
                address=addresses['intA'], value=a,
                description='Setting A in AddNumbers',
            )
            write_b_command = comms.SetUnsignedCommand(
                address=addresses['intB'], value=b,
                description='Setting B in AddNumbers',
            )
            read_c_command = comms.GetUnsignedCommand(
                address=addresses['intC'],
                description='Getting C from AddNumbers',
            )
            commands = (write_a_command, write_b_command, read_c_command)
            super().__init__(
                description='Add 2 numbers with AddNumber',
                commands=commands)

        def process_responses(self, read_responses, write_responses, resolve_future=True):
            '''
            Return the third response (from the final read command)
            Don't return any errors.
            '''
            e, result = super().process_responses(read_responses, write_responses, resolve_future=False)
            intc = result[2]
            if resolve_future:
                self.resolve_future(e, intc)
            return e, intc



Comm
----
Typically command objects are hidden from the testing interface by wrapping them
with a Comm object.  Methods on this comm object create `Command` objects, send
them to a handler, and return the command futures.

.. code:: python

    class AxiAdderComm:
        '''
        Class to communicate with the AxiAdder module.
        '''

        INTA_ADDRESS = 0
        INTB_ADDRESS = 1
        INTC_ADDRESS = 2

        def __init__(self, address_offset, handler):
            '''
            `address_offset` is any addition that is made to the address that is
            consumed during routing.
            `handler` is the object responsible for dispatching the commands.
            '''
            self.handler = handler
            self.address_offset = address_offset
            self.addresses = {
                'intA': address_offset + self.INTA_ADDRESS,
                'intB': address_offset + self.INTB_ADDRESS,
                'intC': address_offset + self.INTC_ADDRESS,
            }

        def add_numbers(self, a, b):
            '''
            A complex complex command that write to two registers and
            then reads from another.
            Sets 'a' and 'b' then reads 'c' (should be a+b)
            '''
            command = AddNumbersCommand(a, b, self.addresses)
            self.handler.send(command)
            return command.future

Tests
-----
A possible way to write a test to is define a class with a `prepare` method that
defines the requests to send to the module, and a `check` method that analyzes
the responses.

The `prepare` method uses a `handler` to generate the requests and creates a
number of futures to hold the results of processing the responses.

The responses are then processed by a handler-dependent method and then the
`check` method can be run to check the contents of the resolved futures.

.. code:: python

    class AxiAdderTest(object):

        def __init__(self):
            self.expected_intcs = []
            self.intc_futures = []

        def prepare(self, handler):
            '''
            Sends a number of 'add_numbers' commands.
            '''
            comm = AxiAdderComm(address_offset=0, handler=handler)
            n_data = 20
            max_int = pow(2, 16)-1
            logger.debug('preparing data')
            for i in range(n_data):
                inta = random.randint(0, max_int)
                intb = random.randint(0, max_int)
                self.expected_intcs.append(inta + intb)
                future = comm.add_numbers(inta, intb)
                self.intc_futures.append(future)
            # Flush the communication for simulations.
            # Ignored in FPGA.
            handler.send(comms.FakeWaitCommand(clock_cycles=10))

        def check(self):
            '''
            Check that the output of the commands matches the expected values.
            '''
            output_intcs = [f.result() for f in self.intc_futures]
            assert output_intcs == self.expected_intcs
            print('Success!!!!!!!!!!!!!!!')



Repeatability of Simulations
----------------------------
Although the simulations are repeatable the FPGA-based tests are currently not
repeatable because of the changing number of clock-cycles between when requests
are received.
I would like to fix this by allowing the ability of specify on which clock
cycle at AXI request should be sent (they would be gathered in a delayed in a buffer
on the FPGA until the correct clock cycle).
TODO: Add delaying of requests to allow repeatability.



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/benreynwar/axilent",
    "name": "axilent",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "VHDL,hdl,rtl,FPGA,ASIC,Xilinx,Altera",
    "author": "Ben Reynwar",
    "author_email": "ben@reynwar.net",
    "download_url": "https://files.pythonhosted.org/packages/38/a2/76e0907fdbbc183cbd2f1a124bed04e328fc070ab8157047aff1dbd44ed1/axilent-0.1.8.tar.gz",
    "platform": "",
    "description": ".. image:: https://travis-ci.org/benreynwar/axilent.svg?branch=master\n               :target: https://travis-ci.org/benreynwar/axilent\n\naxilent\n=======\n\naxilent provides python tools for describing a sequence of Axi4Lite command\nin python.\n\nA python test can be written describing the sequence of commands to be sent\nand the expected responses.  This test can then be run against both a\nsimulation of the design and against the implemented design running on an\nFPGA.\n\naxilent is structured so that tests can be run both against both interactive\nsimulations and simulations where inputs cannot depend on outputs such as\nfile-based testbenchs.\n\n\nHandlers\n--------\n\nCurrently there is `handler` for working with file-based testbenchs\ngenerated by `slvcodec <https://github.com/benreynwar/slvcodec>`_ and\nfor simulations on FPGAs run over JTAG using\n`pyvivado <https://github.com/benreynwar/pyvivado>`_.\nIn the likely event you are using neither of these methods, then you\nwill have to write you own handler. The handler has a single required\nmethod.\n\nhandler.send(command)\n    Sends a command to the FPGA, or adds the command to the list of commands that\n    will be run in the simulation.\n\nThe handler also processes the responses from the simulation or FPGA and updates the\n`command.future` values appropriately.  It calls the `command.process_responses` method\nto help process the responses.\n\n\nCommands\n--------\nA `Command` object represents a simple axi4lite read or write, or a more complex\naggregrate consisting of many read and write commands.\n\ncommand.get_axi_commands()\n    Returns a list of `AxiCommand` objects.  Each `AxiCommand` object is a simple\n    axi4lite read or write command to a single address or a range of addresses.\n\ncommand.process_responses(read_responses, write_responses, resolve_future)\n    Processes two lists of `AxiResponse` objects, one is a list of responses to\n    write requests and one is a list of responses to read requests.  The two lists\n    must begin with the responses to this command, and these entries are removed\n    from the lists.\n    An (exception, result) tuple is returned by the function.  If `resolve_future`\n    is True then the commands future is resolved with the result and exception.\n\nAs an example consider an AxiAdder module, which uses an Axi4Lite interface.  We can\nwrite to registers A and B, and when we read from register C the returned result is\nthe sum of the contents in regsiters A and B.  We can create a `Command` object to\nrepresent using this hardware to add two numbers.\n\n.. code:: python\n\n    class AddNumbersCommand(comms.CombinedCommand):\n        '''\n        A command that writes to the intA and intB registers\n        and then reads from the intC register.\n        The effect is the add the two inputs.\n        '''\n\n        def __init__(self, a, b, addresses):\n            write_a_command = comms.SetUnsignedCommand(\n                address=addresses['intA'], value=a,\n                description='Setting A in AddNumbers',\n            )\n            write_b_command = comms.SetUnsignedCommand(\n                address=addresses['intB'], value=b,\n                description='Setting B in AddNumbers',\n            )\n            read_c_command = comms.GetUnsignedCommand(\n                address=addresses['intC'],\n                description='Getting C from AddNumbers',\n            )\n            commands = (write_a_command, write_b_command, read_c_command)\n            super().__init__(\n                description='Add 2 numbers with AddNumber',\n                commands=commands)\n\n        def process_responses(self, read_responses, write_responses, resolve_future=True):\n            '''\n            Return the third response (from the final read command)\n            Don't return any errors.\n            '''\n            e, result = super().process_responses(read_responses, write_responses, resolve_future=False)\n            intc = result[2]\n            if resolve_future:\n                self.resolve_future(e, intc)\n            return e, intc\n\n\n\nComm\n----\nTypically command objects are hidden from the testing interface by wrapping them\nwith a Comm object.  Methods on this comm object create `Command` objects, send\nthem to a handler, and return the command futures.\n\n.. code:: python\n\n    class AxiAdderComm:\n        '''\n        Class to communicate with the AxiAdder module.\n        '''\n\n        INTA_ADDRESS = 0\n        INTB_ADDRESS = 1\n        INTC_ADDRESS = 2\n\n        def __init__(self, address_offset, handler):\n            '''\n            `address_offset` is any addition that is made to the address that is\n            consumed during routing.\n            `handler` is the object responsible for dispatching the commands.\n            '''\n            self.handler = handler\n            self.address_offset = address_offset\n            self.addresses = {\n                'intA': address_offset + self.INTA_ADDRESS,\n                'intB': address_offset + self.INTB_ADDRESS,\n                'intC': address_offset + self.INTC_ADDRESS,\n            }\n\n        def add_numbers(self, a, b):\n            '''\n            A complex complex command that write to two registers and\n            then reads from another.\n            Sets 'a' and 'b' then reads 'c' (should be a+b)\n            '''\n            command = AddNumbersCommand(a, b, self.addresses)\n            self.handler.send(command)\n            return command.future\n\nTests\n-----\nA possible way to write a test to is define a class with a `prepare` method that\ndefines the requests to send to the module, and a `check` method that analyzes\nthe responses.\n\nThe `prepare` method uses a `handler` to generate the requests and creates a\nnumber of futures to hold the results of processing the responses.\n\nThe responses are then processed by a handler-dependent method and then the\n`check` method can be run to check the contents of the resolved futures.\n\n.. code:: python\n\n    class AxiAdderTest(object):\n\n        def __init__(self):\n            self.expected_intcs = []\n            self.intc_futures = []\n\n        def prepare(self, handler):\n            '''\n            Sends a number of 'add_numbers' commands.\n            '''\n            comm = AxiAdderComm(address_offset=0, handler=handler)\n            n_data = 20\n            max_int = pow(2, 16)-1\n            logger.debug('preparing data')\n            for i in range(n_data):\n                inta = random.randint(0, max_int)\n                intb = random.randint(0, max_int)\n                self.expected_intcs.append(inta + intb)\n                future = comm.add_numbers(inta, intb)\n                self.intc_futures.append(future)\n            # Flush the communication for simulations.\n            # Ignored in FPGA.\n            handler.send(comms.FakeWaitCommand(clock_cycles=10))\n\n        def check(self):\n            '''\n            Check that the output of the commands matches the expected values.\n            '''\n            output_intcs = [f.result() for f in self.intc_futures]\n            assert output_intcs == self.expected_intcs\n            print('Success!!!!!!!!!!!!!!!')\n\n\n\nRepeatability of Simulations\n----------------------------\nAlthough the simulations are repeatable the FPGA-based tests are currently not\nrepeatable because of the changing number of clock-cycles between when requests\nare received.\nI would like to fix this by allowing the ability of specify on which clock\ncycle at AXI request should be sent (they would be gathered in a delayed in a buffer\non the FPGA until the correct clock cycle).\nTODO: Add delaying of requests to allow repeatability.\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Tools for describing a sequence of Axi4Lite commands.",
    "version": "0.1.8",
    "split_keywords": [
        "vhdl",
        "hdl",
        "rtl",
        "fpga",
        "asic",
        "xilinx",
        "altera"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "4b447686e00a6bfaefdb72b91dc2d5c4",
                "sha256": "479ae64e1f52f3ed6543bcb4d5087151e3a8be006be11cf5653c19ace83316a7"
            },
            "downloads": -1,
            "filename": "axilent-0.1.8-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4b447686e00a6bfaefdb72b91dc2d5c4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 22893,
            "upload_time": "2021-01-14T17:43:29",
            "upload_time_iso_8601": "2021-01-14T17:43:29.533522Z",
            "url": "https://files.pythonhosted.org/packages/3e/22/a1b0cd5b721df840b0e1ae88b13183ee4f0e3682a680f298ee71f80901a3/axilent-0.1.8-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "c8d10992a393a510537cee12dff24cfe",
                "sha256": "b2df0004f27e4515454056107471168834af6d8f53d8ee461d4c59e06a4dd05c"
            },
            "downloads": -1,
            "filename": "axilent-0.1.8.tar.gz",
            "has_sig": false,
            "md5_digest": "c8d10992a393a510537cee12dff24cfe",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 23109,
            "upload_time": "2021-01-14T17:43:30",
            "upload_time_iso_8601": "2021-01-14T17:43:30.931467Z",
            "url": "https://files.pythonhosted.org/packages/38/a2/76e0907fdbbc183cbd2f1a124bed04e328fc070ab8157047aff1dbd44ed1/axilent-0.1.8.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-01-14 17:43:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": null,
    "github_project": "benreynwar",
    "error": "Could not fetch GitHub repository",
    "lcname": "axilent"
}
        
Elapsed time: 0.22693s