3.3.3 Modeling errors using exceptions

In the previous section, we used Python data types for modeling. If such a type is used inappropriately, Python's run time error system will come into play. For example, if we access an address in the sparseMemory model that was not initialized before, we will get a traceback similar to the following (some lines omitted for clarity):

Traceback (most recent call last):
...
  File "sparseMemory.py", line 31, in access
    dout.next = memory[addr.val]
KeyError: Signal(51)

Similarly, if the fifo is empty, and we attempt to read from it, we get:

Traceback (most recent call last):
...
  File "fifo.py", line 34, in fifo
    dout.next = memory.pop()
IndexError: pop from empty list

Instead of these low level errors, it may be preferable to define errors at the functional level. In Python, this is typically done by defining a custom Error exception, by subclassing the standard Exception class. This exception is then raised explicitly when an error condition occurs.

For example, we can change the sparseMemory function as follows (with the doc string is omitted for brevity):

class Error(Exception):
    pass

def sparseMemory2(dout, din, addr, we, en, clk):
   
    memory = {}

    @always(clk.posedge)
    def access():
        if en:
            if we:
                memory[addr.val] = din.val
            else:
                try:
                    dout.next = memory[addr.val]
                except KeyError:
                    raise Error, "Uninitialized address %s" % hex(addr)

    return access

This works by catching the low level data type exception, and raising the custom exception with an appropriate error message instead. If the sparseMemory function is defined in a module with the same name, an access error is now reported as follows:

Traceback (most recent call last):
...
  File "sparseMemory.py", line 61, in access
    raise Error, "Uninitialized address %s" % hex(addr)
Error: Uninitialized address 0x33

Likewise, the fifo function can be adapted as follows, to report underflow and overflow errors:

class Error(Exception):
    pass


def fifo2(dout, din, re, we, empty, full, clk, maxFilling=sys.maxint):
 
    memory = []

    @always(clk.posedge)
    def access():
        if we:
            memory.insert(0, din.val)
        if re:
            try:
                dout.next = memory.pop()
            except IndexError:
                raise Error, "Underflow -- Read from empty fifo"
        filling = len(memory)
        empty.next = (filling == 0)
        full.next = (filling == maxFilling)
        if filling > maxFilling:
            raise Error, "Overflow -- Max filling %s exceeded" % maxFilling

    return access

In this case, the underflow error is detected as before, by catching a low level exception on the list data type. On the other hand, the overflow error is detected by a regular check on the length of the list.

About this document