Update 2017-08-19: Updated Links, linked to follow on parts

So first of all, let's pick up where we left off in Part 1 (also remember to checkout Part 3). If you haven't yet read and worked through, Part 1 you should start there. Last week I shared a working passthrough configuration for the Digilent Arty board on Github. This logic implemented in this design essentially bypasses the FPGA, exposing the TX/RX of the second channel of the FTDI 2232H on pins IO[26] and IO[27]. Using this configuration, you should be able to connect using the python script included in the project.

Communicating with python

First things first, we'll need to be able to read data from the device. The problem we'll eventually run into (when glitching) is that either the data be corrupted because we'll genuinely get garbage data back or because the voltage will drop so low, that the FPGA will fail to decode the bytes properly. No matter the reason, what we'll need is a non-blocking read function. We'll log the errors to terminal, but make sure not to match resulting strings in such a way that they block the predicates within your python logic.

Non-blocking Read

This code will allow you to check if the response matches the expected string. If it does, it will return None, otherwise it will return the result that was read and output the error to stdout. Remember, you can use the repr function to print non-printable characters as a python unicode string which you can use in your python code.

def expect_read(expected):
    result = ""
    # Don't attempt to read more than 10 times
    for i in range(0,10):
        result += dev.read(len(expected))
        if expected in result:
            return None;

    print "\tExpected = " + repr(expected) + " got " + repr(result)
    return result

Synchronizing with the Bootloader

Now, we'll need a function to synchronize the device. The bootloader is stateful and requires a couple of things, as described in Part 1. We'll need to send a ?, send Sychronized\r\n, set the clock rate and check the responses. One thing that can be tricky is that the NXP LPC bootloader does local echo by default, so you will see some of your data getting echoed back. Most notably however, the LPC will not echo back the \n character that you send it, but you'll be able to verify this with some trial and error. You can simply write a function like my expect_read function, to do "best effort" reads and return whether or not the data was as expected:

def synchronize():
    # Detect baud rate
    dev.write('?')

    # Wait for 'Synchronized\r\n'
    expect_read('Synchronized\r\n')

    # Reply 'Synchronized\r\n'
    dev.write('Synchronized\r\n')

    # Verify 'Synchronized\rOK\r\n'
    expect_read('Synchronized\rOK\r\n')

    # Set a clock rate (value doesn't matter)
    dev.write("12000\r\n")

    # Verify OK
    expect_read('12000\rOK\r\n')

Reading an address

Eventually, we'll simply read arbitrary addresses from the LPC1343 target board, but first we need to check the CRP value. CRP, or Code Read Protection, disables some of the debug functionality of the device. Most notably, setting a CRP value of CRP1 or higher will disable the read command. Hence, we can simply try to read the first address of memory and if this fails CRP is set.

The NXP UM10375 (page 332) describes the read command and states that the first character of the response determines whether or not the command succeeded or not. If the command succeeded the response will be 0, so a response like R 0 4\r0\r\n$________\r\n1020\r\n succeeded, while a response like R 0 4\r19\r\n failed. If the command succeeded, the host most confirm it with OK\r\n. The NXP UM10375 (page 332) also states, "The length of any UU-encoded line should not exceed 61 characters (bytes) ". Hence, we should read up to 61 characters of data from the device. Remember the pylibftdi read is non-blocking. Hence, even though we'll be reading less in most cases, this won't be a problem. This is also why we append the result string.

So what we need is something like this:

def read_address(address, length):
    cmd = 'R {:d} {:d}\r\n'.format(address, length)
    dev.write(cmd)

    result = ""
    # Don't attempt to read more than 10 times
    for i in range(0,10):
        result += dev.read(61)
        if '\r\n' in result:
            break

    # Check if command succeeded.
    if '\r0' in result:
        dev.write('OK\r\n')
        expect_read('OK\r\n')
        return result
    else:
        return None

Checking the CRP Value

Finally even though read_address will do all the heavy lifting, let's create a test_crp function for good measure. To test the CRP value, we can simply read 4 bytes from address 0, i.e. call read_address(0,4) . Most firmwares of course won't set the CRP value, so even the default firmware on the board does not set CRP. I will provide binaries that do set the CRP value as part of Part 3.

def test_crp():
    result = read_address(0,4)
    if result:
        print "DEVICE UNLOCKED"
        print repr(result)
        return result
    
    print "device is locked."
    return None

Building the Glitcher

The ultimate goal for Part 2 is to create the logic necessary for configuring and executing the glitch after reseting the LPC1343 target board. Since the LPC1343 does not randomize it's clock at boot in any way, simply counting FPGA clock cycles after reset will be a reliable enough way to place the glitch. In the next part, we'll use one of the output pins of the FPGA to control a select line of a Maxim Max4619 to rapidly switch between two voltages that we will configure. So we'll use the FPGA to reset the target board, count clock cycles and eventually glitch the target by changing the signal on the select line of the multiplexer.

Ultimately, we'll be able to control the glitch pulse width and delay from python and implement all the control logic required for moving the pulse position and configuring the duration of the pulse from python.

On an oscilloscope (I recommend and use the Rigol DS2072A myself) the result will look something like this:

Glitcher Design

Let's go over the high level design first.

  1. cmd module - Since we're sending UART data to the target board as well as the FPGA, it's easiest to design a simple protocol to be able to differentiate between the two. This module is responsible for decoding, which data goes to board and which data is destined for the FPGA. this module is also responsible for driving the inputs of all the other modules.
  2. resetter module - This module simply holds the line low for enough clock cycles for the LPC1343 target board to fully reset. It is triggered by the board_rst from the cmd module.
  3. delay module - This module is responsible for delaying by a certain amount of clock cycles before the glitch takes place. It recieves width and delay from the cmd module and has a rdy signal, which is high whenever the module is not running.
  4. trigger moudle - This module consists of a simple 2-state state machine that checks the rdy of the delay module after a board_rst has occurred. Once the delay module has finished, i.e. rdy is high, it set it's output valid.
  5. pulse module - This is the actual module that outputs a pulse. We will eventually connect this signal to the select line of the Maxim Max4619. It receives width and cnt from the cmd module. The width signal determines the width of the pulse in clock cycles, cnt determines how many times this should be repeated.

Block Diagram

All in all, you get a block diagram like this: top_assignment5

Simple Communications Protocol

One of the simplest and most reliable protocols one can implement is to send variable length data to the target and fixed length data to the FPGA. The easiest realization of this is simply to send a binary length byte, followed by the serial data to the target board. Hence, anything that begins with a null byte, i.e. \x00, is a 'special' byte for the FPGA.

So when we want to communicate with the board, we can do something along the lines of:

def board_write(msg):
    length = struct.pack('B',len(msg))
    dev.write(length + msg)

And the 'special' messages to the FPGA, i.e. the messages starting with \x00, can look like this:

def reset_fpga():
    dev.write('\x00\xff')

def reset_board():
    dev.write('\x00\xfe')

def glitch():
    dev.write('\x00\x00')

In this example \x00\xff resets the FPGA, \x00\xfe resets the board and \x00\x00 enables the glitch.

One thing worth mentioning is that if you're decoding data in the FPGA, you'll need a FIFO because UART receivers usually require fewer clock cycles to decode a byte of data than UART transmitters need to send a byte of data. The simple solution to this is to buffer the data using a FIFO, which then feeds the data to the transmitter, see the block diagram. Fortunately, Vivado has the IP catalog that will let you synthesize a FIFO with the flags that you'll want.

What's next?

Now we've covered all the modules we'll need to build a glitcher, as well as what python code will be necessary. In total you'll need to write 4 modules, as well as instantiate a UART transmitter, a UART receiver and FIFO. The cmd module will likely consist of a large state machine to differential between a special byte and data to the target board. For the delay and pulse modules, it's sufficient to use two state state machines (with state idle and running) with counters that count up every clock cycle (default assignments). In the next part we'll load up firmware with CRP1 set and begin to put it all together. Until then you should build and test all the pieces, verifying that you can set all of the paramters you'll need to glitch the device.

Can't wait for the solution?

Sign up for our mailing list and we'll send you the full solution for all four parts right away, so you won't have to wait until we publish all the materials.

Consider taking a training

If you're new to all of this, you should consider taking one of our trainings. This specific assignment is part of our five day course, which we currently only offer in Berlin. We also offer onsite trainings for companies, starting at just 5 participants. If you're building out a lab or need to teach your engineers on the proper use of lab equipment, we can help.

Questions? Comments?

You can always DM me on twitter or email me at dmitry [at] toothless.co.