Skip to main content

Tools for describing a sequence of Axi4Lite commands.

Project description

https://travis-ci.org/benreynwar/axilent.svg?branch=master

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 and for simulations on FPGAs run over JTAG using 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.

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.

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.

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

axilent-0.1.8.tar.gz (23.1 kB view details)

Uploaded Source

Built Distribution

axilent-0.1.8-py3-none-any.whl (22.9 kB view details)

Uploaded Python 3

File details

Details for the file axilent-0.1.8.tar.gz.

File metadata

  • Download URL: axilent-0.1.8.tar.gz
  • Upload date:
  • Size: 23.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.5

File hashes

Hashes for axilent-0.1.8.tar.gz
Algorithm Hash digest
SHA256 b2df0004f27e4515454056107471168834af6d8f53d8ee461d4c59e06a4dd05c
MD5 c8d10992a393a510537cee12dff24cfe
BLAKE2b-256 38a276e0907fdbbc183cbd2f1a124bed04e328fc070ab8157047aff1dbd44ed1

See more details on using hashes here.

File details

Details for the file axilent-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: axilent-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 22.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.5

File hashes

Hashes for axilent-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 479ae64e1f52f3ed6543bcb4d5087151e3a8be006be11cf5653c19ace83316a7
MD5 4b447686e00a6bfaefdb72b91dc2d5c4
BLAKE2b-256 3e22a1b0cd5b721df840b0e1ae88b13183ee4f0e3682a680f298ee71f80901a3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page