Welcome to scaffold’s documentation!

_images/board-anim.gif

Getting started

Scaffold is a FPGA board made for security research on hardware embedded devices. It is capable of communicating with many circuits using standard protocols. It can be easily extended with new protocols by writing new transceivers in VHDL or Verilog language, and integrating it in the proposed scaffold global architecture. When exchanging messages with the devices under tests, Scaffold can generate triggers for chosen commands, spy data on a bus or intercept and traffic.

Scaffold board also embeds special electronics tailored for hardware attacks:

  • Sense resistor and analog amplifier for real-time current measurement
  • FPGA safety protections against voltage glitch attacks
  • Controllable power switches
  • Fast power tearing capability, for emergency shutdown of the device under test
  • Delay and pulse generators, allowing firing glitches or laser pulses.

An easy-to-use python API is provided to control the board.

Board tour

_images/board-640.svg
  • A: USB2 link with host computer. Also used to power the board.
  • B: Main board power switch.
  • C: Switch the jumper to the right to power the board from an external 5 V power supply and not from the USB.
  • D: External power supply for the platform socket.
  • E: External power supply for the DUT socket.
  • F: Adjustable voltage regulator for the platform socket. When the jumper is set on top position, this power source is not used and the platform socket is powered from the external power supply.
  • G: Adjustable voltage regulator for the DUT socket. When the jumper is set on top position, this power source is not used and the DUT socket is powered from the external power supply.
  • H: Adjustable voltage regulator for the I/O bank of the FPGA connected to the platform and DUT sockets. This allows setting the correct voltage depending on the connected device. Supported voltage range goes from 1.5 V up to 3.3 V.
  • I: FPGA active serial connector for bitstream programmation. An USB blaster can be used to update the bitstream.
  • J: FPGA reset button. Push if the board enters error state.
  • K: Switches to select Spy or Intercept mode for each I/O of the FPGA. In intercept mode, the signals of the platform and DUT sockets are not connected anymore and the FPGA can act as a man in the middle circuit.
  • L: Switches to enable 1 KOhm series protection resistors on the I/Os (at the cost of slew-rate). Shall be enabled when there is a risk to damage the FPGA, with high-voltage glitches for instance.
  • M: Tearing input. Any positive edge will power-off the DUT immediately and shunt all I/Os to ground.
  • N: A, B, C I/Os voltage standard selection between 3.3 V or 5 V.
  • O: A, B, C I/O groups. Each group can be configured to have 3.3 V or 5 V I/Os. 5 V is suited to drive Alphanov TTL 50 Ohm laser sources (other 3.3 V I/Os can’t). All the I/Os of a same group are either in output or input mode.
  • P: D I/O group. Maximum voltage is 3.3 V. Those I/Os are connected to the platform and DUT sockets and are usually used to communicate with the target device and generate triggers. SMA connectors are provided for D0 to D6.
  • Q: Platform socket and power state LED.
  • R: DUT socket and power state LED.
  • S: Adjustable shunt resistor for power trace measurement.
  • T: Output of the analog 11 X amplifier for power trace measurement.

Connecting the board

Connect the board with a micro-USB cable to your computer. Power-on the board using the power switch. The operating system shall detect the board as a USB-To-Serial device (COMx on Windows, /dev/ttyUSBx on linux). It may be necessary to install FTDI driver for some Windows versions.

Using the Python API

The file scaffold.py is the library which can be used to interact with Scaffold board.

from scaffold import Scaffold

# Connect to the board.
# This will open the serial device and check for hardware version
scaffold = Scaffold('/dev/ttyUSB0')

# Configure UART0 for operation
uart = scaff.uart0
uart.baudrate = 115200

# Connect UART0 signals to board pins
# << operator connects signals. Left operand is the destination, right operand
# is the source (the order is important)
# In this example, D0 and D1 are configured as outputs, D2 is configured as an
# input.
scaffold.d0 << uart.tx
scaffold.d1 << uart.trigger
uart.rx << scaffold.d2

# UART is now ready to use
uart.send('Hello world !')

Kits

Some kits for specific applications with Scaffold are available.

Breadboard kit

The Scaffold breadboard provides solderable test points for all the DUT socket pins. It can be used to mount a setup with any target.

_images/kit-breadboard.png

STM32F2 QFP64 kit

This kit allows communicating with the embedded ST bootloader of STM32F2 devices. It is possible to write the Flash memory to load code, and then execute it after reset.

_images/kit-stm32f2.png

The socket FLIPPED-QPF64 can be used to mount a STM32F2 device on its backside. All pins of the package must be returned before inserting the device in the socket. Pin 1 of the target device is as shown in the figure above. Make sure the device is correctly inserted and all the needles of the socket touch their corresponding PIN.

Python API example

The class scaffold.stm32.STM32 of the Python API provides methods to communicate with the circuit and setup tests very quickly.

from scaffold import Scaffold
from scaffold.stm32 import STM32

stm = STM32(Scaffold('/dev/ttyUSB0'))
# Load some code into Flash memory
stm.startup_bootloader()
stm.extended_erase()
stm.write_memory(0x08000000, open('program.bin', 'rb').read())
# Run the program
stm.startup_flash()

Example script

An example file in examples/stm32.py can be used to load and execute code onto a STM32F2 device.

$ python3 stm32.py -d /dev/ttyUSB0 --load program.bin --run
Communication initiated
Product ID: 0x0411
Possible device match: stm32f2xxxx
Get: 310001021121314463738292
Bootloader version: 3.1
Option bytes: ffaa0055ffaa0055ffff0000ffff0000
RDP: no protection
Erasing Flash memory...
Programming...
Verifying...
Flash memory written successfully!
Rebooting from Flash memory...

Smartcard kit

This kit allows communicating with any smartcard using 7816 protocol.

The class scaffold.iso7816.Smartcard of the Python API provides methods to communicate with an ISO7816 Smartcard and setup tests very quickly. Currently, only T=0 protocol is supported by the API and it has not been tested extensively yet.

_images/kit-smartcard.png

Pinout

D0 I/O. This signal is pulled up with a resistor on the daughter board
D1 nRST
D2 CLK
D3 Socket card presence contactor

Scaffold peripherals

I/Os

The Scaffold Python API allows controlling and reading the I/Os of the board. This can be useful, for instance, to control the reset signal of a device under test. The following example shows how easy this can be done.

# Toggle D0 every second.
for i in range(10):
    scaffold.d0 << 0
    time.sleep(1)
    scaffold.d0 << 1
    time.sleep(1)

# Put D0 in high impedance state
scaffold.d0 << None
time.sleep(1)

# Connect the output to the internal UART peripheral TX signal, and send a
# message !
scaffold.d0 << scaffold.uart0.tx
scaffold.uart0.send('Hello world!')

It is also possible to read the current electrical state of an input of the board:

if scaffold.d0.value == 1:
    print('Input is high!')

Finally, you can watch for events on an input. The event will be asserted if a 1 is detected. This works for short pulses (> 10 ns).

# Reset event flag
scaffold.d0.event = 0
# Wait some time
time.sleep(1)
# Lookup event register to know if a pulse has been received
if scaffold.d0.event == 1:
    print('Event detected!')
    scaffold.d0.clear_event()

Warning

I/Os internals have been refactored in 0.3. Registers are not the same as in 0.2. Current API supports both 0.2 and 0.3 versions.

Internal registers

a0 0xe000
a1 0xe010
b0 0xe020
b1 0xe030
c0 0xe040
c1 0xe050
dn 0xe060 + 0x10 * n
base + 0x0000 value R/W
base + 0x0001 config W

value register

7 6 5 4 3 2 1 0
reserved event value
value
Reading this bit will return current logical state on the I/O. Setting this bit has no effect.
event
This bit is set to 1 when the I/O logical state changes. It can be reset by writing 0 to it.

config register

7 6 5 4 3 2 1 0
reserved mode

This register allows customizing the output mode of an I/O.

mode Description
0

Auto

The pin is driven by the routed peripheral.

1

Open-drain

The pin is driven by the routed peripheral, but always acts as an open-collector.

2

Push-only

The pin is driven by the routed peripheral, but is active only when a one is outputed.

Power module

The power module controls two power supplies: the “platform” socket power supply and the “dut” socket power supply. Each power supply can be turned on and off.

A positive input pulse on the “tearing” input of the board (SMA connector on the left side) will automatically power-off both power supplies.

Python API example

# Turn DUT on
scaffold.power.dut = 1 # True is also valid
# Check current power supply status
# (it may be off due to external tearing)
if scaffold.power.dut: # Will return 0 or 1
    print('DUT is still ON')
# Turn DUT off
scaffold.power.dut = 0 # False is also valid

The following example controls both power supplies at the same time.

# Turn on all power supplies
scaffold.power.all = 0b11
# Check current power status of both power supplies
if (scaffold.power.all == 0b11):
    print('Both power supplies are ON')
# Turn off all power supplies
scaffold.power.all = 0b00

It is also possible to output the power enable signal to one of the IOs, for triggering or monitoring:

scaffold.d0 << scaffold.power.dut_trigger

For more API documentation, see scaffold.Power

Internal registers

control 0x0600
control register
7 6 5 4 3 2 1 0
reserved platform dut
dut
Write 1 to enable the DUT socket power supply. Write 0 to disable. This bit is cleared when the tearing input is high.
platform
Write 1 to enable the platform socket power supply. Write 0 to disable. This bit is cleared when the tearing input is high.

LEDs module

The LEDs module allows controlling the LEDs of the board.

Python API example

# Adjust LEDs brightness
# 0 is minimum
# 1 is maximum
scaffold.leds.brightness = 0.5

# Toggle a LED every second
scaffold.leds.d0.mode = LEDMode.VALUE
for i in range(10):
    time.sleep(1)
    scaffold.d0 << 1
    time.sleep(1)
    scaffold.d0 << 0

# Now flash the LED every second
scaffold.leds.d0.mode = LEDMode.EVENT
for i in range(10):
    time.sleep(1)
    scaffold.d0 << 1
    time.sleep(1)
    scaffold.d0 << 0

LEDs mode

A/B/C/D LEDs can be lit according to two different modes:

  • Event mode: the LED will flash when an rising or falling edge occurs on the monitored signal. This is the default mode. This mode allows seeing activity on the LED even when very short pulses happen.
  • Value mode: the LED is ON when the monitored signal is high. When using this mode, a user may not be able to catch very short pulses. This mode is more appropriate for watching slow signals.

Registers

0x0200 control W
0x0201 brightness W
0x0202 leds_0 W
0x0203 leds_1 W
0x0204 leds_2 W
0x0205 mode W
control register
7 6 5 4 3 2 1 0
reserved override disable
disable
Write 1 to disable TLC5927 drivers output. Default is 0 (enabled).
override
Sets the LEDs of the board to the values defined in led_n registers.
brightness registers
7 6 5 4 3 2 1 0
reserved brightness
brightness
7-bits word which controls the brightness of the LEDs. See TLC5952 datasheet for more details.
leds_n registers

leds_n registers define the state of the LEDs of the board when override bit is set. Set a bit to 1 to turn the corresponding LED on, 0 to turn it off. Value on reset is 0x00.

mode register
23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
reserved d5 d4 d3 d2 d1 d0 c1 c0 b1 b0 a1 a0 reserved

mode register is a 24-bit register. Write three times to update all the bits of this register.

Each bit of this register sets the lighting mode of a LED. When bit is 0, the corresponding LED will blink on a falling or rising edge of the monitored signal. When the bit is 1, the LED will be lit as long as the monitored signal is high. Default mode is 0 (event mode) for all LEDs.

Version module

This module helps the software identifying the board. When scaffold.Scaffold connects to the board, the version string is automatically queried and cached in the version attribute.

Python API example

# Read cached version string
# Shall return something like 'scaffold-1.0'
print(scaffold.version)

Internal registers

0x0100 data R
data register

Reading multiple times this register will return the version string of the board. A null character indicates the beginning (and end) of the version string. Latest version string is “scaffold-1.0”.

Python API

Main Scaffold API

This page documents the main classes and methods of the Scaffold Python API.

Manipulating the attributes of the different modules of a Scaffold instance will read or write in the FPGA registers. Some registers may be cached by the Python API, so reading them does not require any communication with the board and thus can be fast.

class scaffold.Scaffold(dev='/dev/scaffold')

This class connects to a Scaffold board and provides access to all the device parameters and peripherals.

Variables:
  • uarts – list of scaffold.UART instance managing UART peripherals.
  • i2cs – list of scaffold.I2C instance managing I2C peripherals.
  • iso7816scaffold.ISO7816 instance managing the ISO7816 peripheral.
  • pgens – list of four scaffold.PulseGenerator instance managing the FPGA pulse generators.
  • powerscaffold.Power instance, enabling control of the power supplies of DUT and platform sockets.
  • ledsscaffold.LEDs instance, managing LEDs brightness and lighting mode.
  • [a0,a1,b0,b1,c0,c1,d0,d1,d2,d3,d4,d5]scaffold.Signal instances for connecting and controlling the corresponding I/Os of the board.
__init__(dev='/dev/scaffold')

Create Scaffold API instance.

Parameters:dev – If specified, connect to the hardware Scaffold board using the given serial device. If None, call connect method later to establish the communication.
class scaffold.Signal(parent, path)

Base class for all connectable signals in Scaffold. Every Signal instance has a Scaffold board parent instance which is used to electrically configure the hardware board when two Signal are connected together.

__init__(parent, path)
Parameters:
  • parent – Scaffold instance which the signal belongs to.
  • path – Signal path string. Uniquely identifies a Scaffold board internal signal. For instance ‘/dev/uart0/tx’.
__lshift__(other)

Feed the current signal with another signal.

Parameters:other – Another Signal instance. The other signal must belong to the same Scaffold instance.
__str__()
Returns:Signal path. For instance ‘/dev/uart0/tx’.
name

Signal name (last element of the path). For instance ‘tx’. Read-only.

parent

Parent Scaffold board instance. Read-only.

path

Signal path. For instance ‘/dev/uart0/tx’. Read-only.

class scaffold.IO(parent, path, index)

Board I/O.

clear_event()

Clear event register.

Warning:If an event is received during this call, it may be cleared without being took into account.
event

I/O event register.

Getter:Returns 1 if an event has been detected on this input, 0 otherwise.
Setter:Writing 0 to clears the event flag. Writing 1 has no effect.
mode

I/O mode. Default is AUTO, but this can be overriden for special applications.

Type:IOMode
value

Current IO logical state.

Getter:Senses the input pin of the board and return either 0 or 1.
Setter:Sets the output to 0, 1 or high-impedance state (None). This will disconnect the I/O from any already connected internal peripheral. Same effect can be achieved using << operator.
class scaffold.GroupIO(parent, path, index)

Board I/O in group A, B or C. Those I/Os are special since their operating voltage can be configured to either 3.3 V or 5.0 V by switching an on-board jumper. Voltage configuration applies to groups of I/Os, and as a side effect, all I/Os of a same group are in input only mode, or output only mode. This class allows setting the direction of the I/O and will check for conflicting configurations in a same group of I/Os.

dir

Current I/O direction.

Getter:Returns IODir.INPUT if the I/O is in input mode, IODir.OUTPUT if the I/O is in output mode, or None if it is undecided (thus the mode will be the same as the other I/O of the same group).
Setter:Changes the direction of the I/O. The API will verify that the new configuration does not conflict with the other I/O of the same group. Accepted values are IODir.INPUT, IODir.OUTPUT or None.
class scaffold.UART(parent, index)

UART module of Scaffold.

baudrate

Target UART baudrate.

Getter:Returns current baudrate, or None if no baudrate has been previously set during current session.
Setter:Set target baudrate. If baudrate cannot be reached within 1% accuracy, a RuntimeError is thrown. Reading the baudrate attribute after setting it will return the real effective baudrate.
flush()

Discard all the received bytes in the FIFO.

receive(n=1)

Receive n bytes from the UART. This function blocks until all bytes have been received or the timeout expires and a TimeoutError is thrown.

reset()

Reset the UART to a default configuration: 9600 bps, no parity, one stop bit, trigger disabled.

transmit(data, trigger=False)

Transmit data using the UART.

Parameters:
  • data – Data to be transmitted. bytes or bytearray.
  • trigger – True or 1 to enable trigger on last byte, False or 0 to disable trigger.
class scaffold.ISO7816(parent)

ISO7816 peripheral of Scaffold. Does not provide convention or protocol management. See scaffold.iso7816.Smartcard for more features.

clock_frequency

Target ISO7816 clock frequency. According to ISO7816-3 specification, minimum frequency is 1 Mhz and maximum frequency is 5 MHz. Scaffold hardware allows going up to 50 Mhz and down to 195312.5 Hz (although this may not work with the smartcard).

Getter:Returns current clock frequency, or None if it has not been set previously.
Setter:Set clock frequency. If requested frequency cannot be reached within 1% accuracy, a RuntimeError is thrown. Reading this attribute after setting it will return the real effective clock frequency.
empty

True if reception FIFO is empty.

etu

ISO7816 ETU parameter. Value must be in range [1, 2^11-1]. Default ETU is 372.

flush()

Discard all the received bytes in the FIFO.

parity_mode

Parity mode. Standard is Even parity, but it can be changed to odd or forced to a fixed value for testing purposes. :type: ISO7816ParityMode

receive(n=1)

Receive bytes. This function blocks until all bytes have been received or the timeout expires and a TimeoutError is thrown.

Parameters:n – Number of bytes to be read.
reset_config()

Reset ISO7816 peripheral to its default configuration.

transmit(data)

Transmit data.

Parameters:data (bytes) – Data to be transmitted.
trigger_long

Enable or disable long trigger (set on transmission, cleared on reception). When changing this value, wait until transmission buffer is empty.

Type:bool
trigger_rx

Enable or disable trigger upon reception. :type: bool

trigger_tx

Enable or disable trigger upon transmission. :type: bool

class scaffold.I2C(parent, index)

I2C module of Scaffold.

clock_stretching

Enable or disable clock stretching support. When clock stretching is enabled, the I2C slave may hold SCL low during a transaction. In this mode, an external pull-up resistor on SCL is required. When clock stretching is disabled, SCL is always controlled by the master and the pull-up resistor is not required.

Type:bool or int.
flush()

Discards all bytes in the transmission/reception FIFO.

frequency

Target I2C clock frequency.

Getter:Returns current frequency.
Setter:Set target frequency. Effective frequency may be different if target cannot be reached accurately.
raw_transaction(data, read_size, trigger=None)

Executes an I2C transaction. This is a low-level function which does not manage I2C addressing nor read/write mode (those shall already be defined in data parameter).

Parameters:
  • data (bytes) – Transmitted bytes. First byte is usually the address of the slave and the R/W bit. If the R/W bit is 0 (write), this parameter shall then contain the bytes to be transmitted, and read_size shall be zero.
  • read_size (int) – Number of bytes to be expected from the slave. 0 in case of a write transaction.
  • trigger (int or str.) – Trigger configuration. If int and value is 1, trigger is asserted when the transaction starts. If str, it may contain the letter ‘a’ and/or ‘b’, where ‘a’ asserts trigger on transaction start and ‘b’ on transaction end.
Raises:

I2CNackError – If a NACK is received during the transaction.

read(size, address=None, trigger=None)

Perform an I2C read transaction.

Parameters:address (int or None) – Slave device address. If None, self.address is used by default. If defined and addressing mode is 7 bits, LSB must be 0 (this is the R/W bit). If defined and addressing mode is 10 bits, bit 8 must be 0.
Returns:Bytes from the slave.
Raises:I2CNackError – If a NACK is received during the transaction.
reset_config()

Reset the I2C peripheral to a default configuration.

write(data, address=None, trigger=None)

Perform an I2C write transaction.

Parameters:address (int or None) – Slave device address. If None, self.address is used by default. If defined and addressing mode is 7 bits, LSB must be 0 (this is the R/W bit). If defined and addressing mode is 10 bits, bit 8 must be 0.
Raises:I2CNackError – If a NACK is received during the transaction.
class scaffold.PulseGenerator(parent, index)

Pulse generator module of Scaffold. Usually abreviated as pgen.

count

Number of pulses to be generated. Minimum value is 1. Maximum value is 2^16.

delay

Delay before pulse, in seconds. float.

fire()

Manually trigger the pulse generation.

interval

Delay between pulses, in seconds. float.

width

Pulse width, in seconds. float.

class scaffold.LEDs(parent)

LEDs module of Scaffold.

brightness

LEDs brightness. 0 is the minimum. 1 is the maximum.

Type:float
disabled

If set to True, LEDs driver outputs are all disabled.

override

If set to True, LEDs state is the value of the leds_n registers.

reset()

Set module registers to default values.

class scaffold.Power(parent)

Controls the platform and DUT sockets power supplies.

all

All power-supplies state. int. Bit 0 corresponds to the DUT power supply. Bit 1 corresponds to the platform power-supply. When a bit is set to 1, the corresponding power supply is enabled. This attribute can be used to control both power supplies simultaneously.

dut

DUT power-supply state. int.

platform

Platform power-supply state. int.

STM32 API

This API is for STM32 experimental daughter boards.

class scaffold.stm32.STM32(scaffold)

Class for instrumenting STM32 devices using Scaffold board and API. The following Scaffold IOs are used:

  • D0: STM32 UART MCU RX pin, Scaffold UART TX
  • D1: STM32 UART MCU TX pin, Scaffold UART RX
  • D2: STM32 NRST pin for reset
  • D6: STM32 BOOT0 pin
  • D7: STM32 BOOT1 pin

The uart0 peripheral of Scaffold board is used for serial communication with the ST bootloader.

This class can communicate with the ST bootloader via USART1. This allows programming the Flash memory and then execute the loaded code.

__init__(scaffold)
Parameters:scaffold – An instance of scaffold.Scaffold which will be configured and used to communicate with STM32 daughter board.
assert_device()

Raise a RuntimeError is device is unknown (None).

checksum(data)

Calculate the checksum of some data, according to the STM32 bootloader protocol.

Parameters:data – Input bytes.
Returns:Checksum byte. This is the XOR of all input bytes.
command(index)

Send a command and return the response.

Parameters:index – Command index.
Returns:Response bytes.
extended_erase()

Execute the Extended Erase command to erase all the Flash memory of the device.

get()

Execute the Get command of the bootloader, which returns the version and the supported commands.

get_id()

Execute the Get ID command. The result is interpreted and the class will try to find information if the ID matches a known device.

go(address, trigger=0)

Execute the Go command.

Parameters:
  • address – Jump address.
  • trigger – 1 to enable trigger on command transmission.
read_memory(address, length, trigger=0)

Tries to read some memory from the device. If requested size is larger than 256 bytes, many Read Memory commands are sent.

Parameters:
  • address – Memory address to be read.
  • size – Number of bytes to be read.
  • trigger – 1 to enable trigger on command transmission.
read_option_bytes()

Read the option bytes of the device. The method get_id must have been called previously for device identification.

Returns:Memory content of ‘option_bytes’ section.
readout_protect()

Execute the Readout Unprotect command.

readout_unprotect()

Execute the Readout Unprotect command. If the device is locked, it will perform mass flash erase, which can be very very long.

startup_bootloader()

Power-cycle and reset target device in bootloader mode (boot on System Memory) and initiate serial communication. The byte 0x7f is sent and the response byte 0x79 (ACK) is expected. If the device does not respond, a Timeout exception is thrown by Scaffold. The device will not respond if it is locked in RDP2 state (Readout Protection level 2).

startup_flash()

Power-cycle and reset target device and boot from user Flash memory.

wait_ack(tag=None)

Wait for ACK byte.

Parameters:tag – Tag which is set when NACKError are thrown. Useful for error diagnostic.
wait_ack_or_nack()

Wait for ACK or NACK byte.

Returns:True if ACK has been received, False if NACK has been received.
write_memory(address, data, trigger=0)

Write data to device memory. If target address is Flash memory, this function DOES NOT erase Flash memory prior to writing. If data size is larger than 256 bytes, many Write Memory commands are sent.

Parameters:
  • address – Address.
  • data – Data to be written. bytes or bytearray.
  • trigger – 1 to enable trigger on each command transmission.

ISO7816 API

This API provides support for ISO7816 support with Scaffold.

class scaffold.iso7816.Smartcard(scaffold)

Class for smartcard testing with Scaffold board and API. The following IOs are used:

  • D0: ISO7816 IO
  • D1: ISO7816 nRST
  • D2: ISO7816 CLK
  • D3: Socket card contactor sense

scaffold.Scaffold class has ISO7816 peripheral support, but it is very limited. This class adds full support to ISO7816 by managing ATR, convention convertion, etc.

Variables:
  • atr (bytes) – ATR received from card after reset.
  • convention (Convention) – Communication convention between card and terminal. Updated when first byte TS of ATR is received.
  • protocols (set) – Communication protocols found in ATR. This set contains integers, for instance 0 if T=0 is supported, 1 if T=1 is supported…
__init__(scaffold)

Configure a Scaffold board for use with smartcards. :param scaffold: scaffold.Scaffold instance.

apdu(the_apdu, trigger='')

Send an APDU to the smartcard and retrieve the response.

Parameters:
  • the_apdu (bytes or str) – APDU to be sent. str hexadecimal strings are allowed, but use should consider using the apdu_str() method instead.
  • trigger (str) – If ‘a’ is in this string, trigger is raised after ISO-7816 header is sent, and cleared when the following response byte arrives. If ‘b’ is in this string, trigger is raised after data field has been transmitted, and cleared when the next response byte is received.
Raises:

ValueError – if APDU data is invalid.

Return bytes:

Response data, with status word.

apdu_str(the_apdu)

Same as apdu() function, with str argument and return type for convenience.

Parameters:the_apdu (str) – APDU to be sent, as an hexadecimal string.
Return str:Response from the card, as a lowercase hexadecimal string without spaces.
find_info()

Parse the smartcard ATR list database available at http://ludovic.rousseau.free.fr/softwares/pcsc-tools/smartcard_list.txt and try to match the current ATR to retrieve more info about the card.

The database file cannot be embedded in the library because it uses GPL and not LGPL license. On debian systems, this file is provided in the pcsc-tools package.

Returns:A list of str, where each item is an information line about the card. Return None if the ATR did not match any entry in the database.
inverse_byte(byte)

Inverse order and polarity of bits in a byte. Used for ISO7816 inverse convention decoding.

receive(n)

Use the ISO7816 peripheral to receive bytes from the smartcard, and apply direct or inverse convention depending on what has been read in the ATR.

Parameters:n – Number of bytes to be read.
reset()

Reset the smartcard and retrieve the ATR. If the ATR is retrieved successfully, the attributes atr convention and protocols are updated.

Returns:ATR from the card.
Raises:ProtocolError – if the ATR is not valid.
class scaffold.iso7816.Convention

Possible ISO7816 communication convention. This is given by the first byte of the ATR returned by the card.

DIRECT = 59
INVERSE = 63
class scaffold.iso7816.ProtocolError(message)

Exception raised when a protocol error between the terminal and the smartcard occurs.

Architecture

The following documentation describes in details the architecture of Scaffold. The targeted audience is the developer who wish fixing bugs in the architecture or extend its functionalities. Reading this documentation may help understanding what’s under the hood.

Communication protocol

General architecture

The FPGA has many embedded peripherals. Each peripheral has registers which can be read/written to receive/send data and perform actions. Each register is assigned a unique 16-bits address and is connected to the system data bus.

A bridge controller, inside the FPGA, controls the read and write operations on the system data bus. This controller can be driven by a host computer using the USB link, allowing the host computer to manipulate the registers connected to the system data bus.

Protocol

When connected to a host computer using a USB cable, the board is recognized as a USB-to-Serial device from FTDI manufacturer. Baudrate is 2 Mbits/s, with one stop bit and no parity check.

A very simple protocol is defined to perform read and write operations through the serial device. To perform an operation, the following data must be sent to Scaffold:

Command 1 byte Mandatory
Address 2 bytes Mandatory
Polling address 2 bytes When polling enabled
Polling mask 1 byte When polling enabled
Polling value 1 byte When polling enabled
Size 1 byte When size enabled
Data N bytes For write commands

Bit 0 of command byte indicates if this is a read (0) or write (1) command. Bit 1 indicates if size parameter is present (1 to enable size). Bit 2 indicates if polling is requested for this command (1 to enable polling). All other bits must be set to zero, otherwise the command is considered invalid and Scaffold will enter error mode.

For write and read commands, a response is transmitted by the Scaffold board. This response starts by the data bytes (for register read commands) and terminates with a status byte. The status byte shall be equal to the size of the processed data. If a command times out during polling, the returned status byte will be lower than the size of the data.

Register polling

Read or write operations on registers can be conditioned to a given register expected state. When polling is enabled, each read or write operation is performed when the monitored register reaches a given value for some chosen bits. Polling is enabled when bit 2 of command byte is 1. Read or write operation is performed when (Register and Mask) = (Value and Mask).

Polling can be used for flow control when using a peripheral to process multiple bytes. For instance, when using a SPI peripheral, polling can be used to wait for incoming bytes.

The polled register can be different from the read or written register (two different addresses can be passed in the command parameters: address and polling address).

Polling timeout

Optionally, a timeout can be configured for polling operations. This is particularly useful if receiving bytes from a peripheral is not guaranteed. When the polling times out in a read operation, the remaining expected bytes are sent by the board as zero. When the polling times out in a write operation, the remaining bytes sent to the board are discarded by the bus bridge. The returned acknowledge byte indicates the number of successfully processed bytes and will be lower than the requested size in the command.

The timeout delay can be configured with a special command:

Command 0x08 1 byte Mandatory
Polling delay value 4 bytes MSB first

No response is expected after this command. The new delay value will be applied for all the following commands. If the delay is set to zero, then the timeout is disabled (which is the default).

FPGA bus

The internal global bus connects all the peripherals together. This bus is controlled by the serial bridge which is connected to the host computer with the USB link. The bus can perform only two simple operations:

  • Read a byte from a register,
  • Write a byte to a register.

The bus works using many signals:

  • 16-bits address: bus_address,
  • Write assertion signal: bus_write
  • 8-bits write data: bus_write_data,
  • Read assertion signal: bus_read
  • 8-bits read data: bus_read_data

Using different data wires for read and write operations makes conflicts between modules impossible. Also, some FPGA devices may not allow internal bidirectional wires.

All the signals of the bus are synchronized to the system clock, on rising edges.

Register read cycle

_images/wavedrom-bb53ba3e-b0a2-45b7-a3f1-e381c6097fc6.svg

Register write cycle

_images/wavedrom-ffa81278-5ebf-433c-b720-80d41d922167.svg

FPGA bus bridge

The bus bridge allows reading and writing bytes on the internal FPGA bus using commands sent in serial. This bridge is implemented in VHDL using a Finite State Machine. The state machine manages the commands parsing and execution.

The state machine has a lot of states and is a bit complex. However, the parsing and execution of a command is faster than the time required to receive an incoming new command - making it hard to saturate the commands queue. The only cases a command queue can be overflowed is when using polling commands - in such cases the application may wait for the response before sending further commands.

Overflowing the commands queue will usually lead to entering the error state as bytes will be discarded, resulting in data bytes being interpreted as invalid command codes. When in error state, the error LED on the board is lit. The only way to recover from the error state is pressing the reset button.

State machine

The following graph is the bus bridge finite state machine implemented in the FPGA. This document is an help for understanding FPGA internals.

digraph {
  graph [dpi = 55, bgcolor = "#ffffff00", fontname = "helvetica", fontsize = 16];
  node [fontname = "helvetica", fontsize = 16, fixedsize = true, width = 0.8];
  edge [fontname = "helvetica", fontsize = 12];

  node [shape = doublecircle] CMD; ERR;
  node [shape = circle];

  CMD[group = g5];
  CMD -> ADH [label = " data & rw_cmd"];
  CMD -> ERR [label = " invalid command"];
  ADH[group = g5];
  ADH -> ADL [label = " data"];
  ADL[group = g5];
  ADL -> PADH [label = " data &and;\n has_polling"];
  ADL -> SIZE [label = " data &and;\n ! has_polling"];
  PADH[group = g6];
  PADH -> PADL [label = " data"];
  PADL[group = g6];
  PADL -> PMSK [label = " data"];
  PMSK[group = g6];
  PMSK -> PVAL [label = " data"];
  PVAL[group = g6];
  PVAL -> SIZE [label = " data"];
  SIZE[group = g5];
  SIZE -> LOOP [label = " data &or; ! has_size"];

  RD[group = g1];
  RD -> WAIT1 [label = " 1"];
  WAIT1[label = "WAIT", group = g1];
  WAIT1 -> SEND [label = " ready"];
  SEND[group = g1];
  SEND -> LOOP [label = " 1"];

  VAL[group = g2];
  VAL -> WR [label = " data"];
  WR[group = g2];
  WR -> LOOP [label = " 1"];

  LOOP[group = g5];
  LOOP -> P1 [label = " size &ne; 0"];

  P1[group = g3];
  P1 -> P2 [label = " 1"];
  P2[group = g3];
  P2 -> P3 [label = " 1"]
  P3[group = g3];
  P3 -> P4 [label = " poll_ok"]
  P3 -> P1 [label = " ! poll_ok"]
  P4[group = g3];
  P4 -> RD [label = " read_command"];
  P4 -> VAL [label = " write_command"];

  LOOP -> WAIT2 [label = " size = 0"];
  WAIT2[label = "WAIT", group = g4];
  WAIT2 -> SEND2 [label = " ready"];
  SEND2[label = "SEND", group = g4];
  SEND2 -> CMD [label = " 1"];

  TOC1 [group = g7]
  CMD -> TOC1 [label = " data & timeout_config"]
  TOC2 [group = g7]
  TOC1 -> TOC2 [label = " data"]
  TOC3 [group = g7]
  TOC2 -> TOC3 [label = " data"]
  TOC4 [group = g7]
  TOC3 -> TOC4 [label = " data"]
  TOC4 -> CMD

  TO [group=g8]
  P3 -> TO [label = " timeout"]
  WAIT3[label="WAIT", group=g8]
  WAIT3 [label="WAIT", group=g8]
  TO -> WAIT3 [label = " read_command"]
  SEND3 [label="SEND", group=g8]
  WAIT3 -> SEND3 [label = " ready"]
  SEND3 -> TO [label=" 1"]

  POP4 [label="POP", group=g9]
  TO -> POP4 [label=" write_command"]
  POP4 -> TO [label=" data"]

  TO -> WAIT2 [label=" size = 0"]

}

Details on states:

  • CMD: Idle state, awaiting for command byte.
  • ADH: Awaiting for read/write address high nibble.
  • ADL: Awaiting for read/write address low nibble.
  • PADH: Awaiting for polling address high nibble.
  • PADL: Awaiting for polling address low nibble.
  • PMSK: Awaiting for polling mask.
  • PVAL: Awaiting for polling value.
  • SIZE: Read/write size fetch. If the command does not require size parameter, use 1. Otherwise, wait and store next byte received on UART.
  • LOOP: Preload polling address on the bus for P1 and P2 states. Return to initial state if all bytes have been read or written.
  • WAIT: Wait for UART to be ready to transmit a byte. This also acts as a delay cycle required for data bus to be available (due to pipelining).
  • SEND: Send result byte on UART (command acknowledge or register value).
  • P1: First polling state. Assert bus read signal. Due to pipelining, the data read from the bus is available to the rest of the logic at P2.
  • P2: Second polling state. Used for pipelining (registers the data read from the bus).
  • P3: Polling test. If polling value matches, leave polling by going to P4. Otherwise, return to P1 for a new polling read cycle.
  • P4: End of polling. Load register address on the bus.
  • RD: Register read cycle.
  • VAL: Read from UART the byte to be written in register.
  • WR: Register write cycle.
  • TO: Timeout state. Loops until all unprocessed bytes have been discarded (write operation) or returned as zeros (read operation).
  • POP: Cycle used to discard a byte from the input FIFO during a write operation which has timed out.

Details on transition conditions:

  • 1: Unconditionally pass to the next state at the next clock cycle.
  • data: True when the UART FIFO has a byte available.
  • size: Number of remaining bytes to be read or written.
  • ready: True when the UART of the bus bridge is ready to transmit a byte.
  • read_command: True when the fetched command byte corresponds to a bus read cycle command.
  • write_command: True when the fetched command byte corresponds to a bus write cycle command.

Polling timeout configuration

A special command bit allows reconfiguring the polling timeout delay. The delay parameter is stored in an internal 32 bits register and encodes a number of clock cycles to wait during polling state (1 unit equals to 3 clock cycles). When this register is set to 0, the timeout is disabled. Note that disabling the timeout is not recommended since the FSM may stuck in a polling operation forever or until general reset.

The maximum possible timeout duration is approximately 128 seconds.

Output module

The available I/Os of the scaffold board can be internally connected to any module output. The “right matrix” controls which module output signals are connected to which I/Os. Each I/O has a register storing its multiplexer selecting the source signal.

Multiplexers selection table

Index Signal name
0 z
1 0
2 1
3 /power/dut_trigger
4 /power/platform_trigger
5 /uart0/tx
6 /uart0/trigger
7 /uart1/tx
8 /uart1/trigger
9 /iso7816/io_out
10 /iso7816/clk
11 /iso7816/trigger
12 /pgen0/out
13 /pgen1/out
14 /pgen2/out
15 /pgen3/out

Building and flashing the FPGA bitstream

Advanced users may want to modify the architecture of the FPGA of Scaffold to support more peripherals, implement new features or even fix bugs. This section briefly describe how to build the FPGA bitstream with Intel (Altera) tools, and flash the board.

Prerequisites

Quartus II software from Intel (Altera) must be used to build the FPGA bitstream. The version of Quartus must have support for Cyclone IV devices; version 14.1 can be used. Although Quartus is a proprietary tool, the free Quartus Web Edition can be used and shall not require any license. Quartus can run on linux and Windows (but we did not give a try on Windows).

For flashing the FPGA, a programmer supporting Active Serial mode must be used. Altera USB Blaster is a good one.

Building the bitstream

Under Quartus software, load the fpga-arch/scaffold.qpf project file with the File > Open Project menu. Build the design with Start context-menu of Compile Design task as highlighted below.

_images/screenshot-quartus-project.jpg

Hopefully the compilation should succeed. If you modified the original design, we recommand you to check in the compilation report that the max frequency of the system clock of the compiled design is at least 110 MHz (100 MHz plus some security margin). If this constraint is not respected, then your design may not work properly and need to be optimized.

_images/screenshot-quartus-fmax.jpg

Flashing the FPGA

Power-on the board and connect the programmer as shown below:

_images/flashing.jpg

In Quartus, open the programmer window with the Tools > Programmer menu.

  • Setup your programmer with the Hardware Setup button.
  • Switch to Active Serial Programming mode.
  • Click on Add File… and select the file confware.pof. The setup shall represent an EPCS16 device, which is the on-board Flash memory storing the bitstream and read by the FPGA when powering-up Scaffold.
  • Check the Program/Configure and Verify boxes.
  • Click on the Start button.

Remove the programmer cable to test your new design!

_images/screenshot-quartus-programmer.jpg

Indices and tables