Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Commit

Permalink
typo fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting committed Nov 17, 2021
1 parent 5ccc36a commit 82dcee6
Showing 1 changed file with 25 additions and 21 deletions.
46 changes: 25 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# :lock: A Physical Unclonable Function for _any_ FPGA
# :closed_lock_with_key: A Physical Unclonable Function for _any_ FPGA

[![license](https://img.shields.io/github/license/stnolting/fpga_puf?longCache=true&style=flat-square)](https://github.com/stnolting/fpga_puf/blob/main/LICENSE)

Expand All @@ -20,7 +20,7 @@ security-related applications. The `fpga_puf` hardware module provides an unclon
that is defined by the target chip's semiconductor characteristics. It is implemented in a technology-independent
way that does not use any device-specific macros, primitives or attributes so it can be implemented on _any_ FPGA.

:lock: The PUF ID is _unique_ for the combination of bitstream and _one_ specific FPGA. The same bitstream
:key: The PUF ID is _unique_ for the combination of bitstream and _one_ specific FPGA. The same bitstream
will lead to a different PUF ID on a different FPGA of same type. If the bitstream of a specific FPGA is changed
(by changing design logic) the PUF ID of that specific FPGA will also change.

Expand All @@ -37,24 +37,25 @@ will lead to a different PUF ID on a different FPGA of same type. If the bitstre

## Theory of Operation

Each bit of the PUF ID is generated by an individual **PUF cell**. Each cell consists of an asynchronous element that is
based on a simple 1-bit ring oscillator. The oscillator is constructed from an inverter that negates it's own output signal.
Each bit of the 96-bit PUF ID is generated by an individual **PUF cell**. Each cell consists of an asynchronous element that is
based on a simple ring oscillator. The oscillator is constructed from a single inverter that negates it's own output signal.
This feedback loop is interrupted by a latch. If the latch is open (=transparent) the oscillator starts oscillating.
If the latch is closed, it stores the last state of the oscillator (high or low). The latch also provides a reset
to bring each cell into a defined state.

The frequency of each oscillator is defined by the mapping of the logic cell and the according routing. Both factors
are constant for a single bitstream. Furthermore, the frequency is also defined by the chip's semiconductor characteristics.
are constant for a specific bitstream. Furthermore, the frequency is also randomly (but constantly for a given setup)
"tuned" by the chip's semiconductor characteristics.
These are caused by tiny productions fluctuation (for example the capacitance / delay of a routing wire is affected
by variation in oxide thickness). The frequency of each oscillator is expected to be fixed. Hence, sampling several periods
by variation in oxide thickness). The frequency of each oscillator is considered to be fixed. Hence, sampling several periods
within a fixed time window will always end in the same state (oscillator output is high or low). Temperature drift
affects all oscillators in the (nearly) same way and has to be computationally eliminated/compensated by the
disturbs all oscillators in the (nearly) same way and has to be computationally eliminated/compensated by the
[post-processing](#Post-Processing).

Since `fpga_puf` does not use any kind of macros or primitives, there is a risk that the synthesis toolchain will
remove the asynchronous PUF cells or collapse the design to a single PUF cell ("optimize away"). Therefore, the design
used a shift register to control reset and the latch open/close phase of each cell individually. This concept is
based on the [NEORV32 TRNG](https://github.com/stnolting/neorv32) allowing a **platform-independent implementation**.
Since `fpga_puf` does not use any device-specific attributes or primitives, there is a risk that the synthesis toolchain will
remove the asynchronous PUF cells or collapse them into a single PUF cell ("optimize away"). Therefore, the design
uses a shift register to control reset and the latch open/close phase of each cell individually and distributed over time. This concept is
based on the [NEORV32 TRNG](https://github.com/stnolting/neorv32) and allows a **platform-independent implementation**.

Whenever a new sampling of the PUF ID is started, a 96+1 bit wide shift register. A single '1' is applied to the
least significant bit that travels throughout the whole shift register chain during operation. The PUF cell's control signals (reset and
Expand Down Expand Up @@ -82,15 +83,16 @@ change over time and over a broad range of operation conditions (e.g. temperatur
approaches can be found in literatures. One promising approach is the use of error-correction codes to "stabilizes"
an initially determined ID.

However, these concepts are out of (my) scope so I used a simple "averaging"
concept for this example. My approach samples the _raw_ PUF ID several times and checks how often each bit is set across
all ID samples. If an ID bit is set in more then half of the sample it is considered to be `1`, otherwise it is considered to
be`0`. Since some bits of the raw ID are quite noisy, a hysteresis is used to eliminate those bits from the final PUF (these
bits are masked to be always zero). If a bit is set or cleared more often than a certain _threshold_ it is considered "stable"
in the final ID.
However, these concepts are out of (my) scope so I used a simple "averaging" concept for this example. My approach samples
the _raw_ PUF ID several times (for example 4096 times) and checks how often each bit of the ID is set across
all sampled IDs. If an ID bit is set in more then half of the samples it is considered to be a static `1`, otherwise it is considered to
be a static `0`. Since a few bits of the raw ID might be quite noisy, a hysteresis is used to eliminate those bits from the final PUF:
if a bit is set or cleared more often than a certain _threshold_ it is considered "stable" in the final ID. These lower and
upper hysteresis threshold define an _uncertain_ band between them. For the final ID all bits tha fall
into this uncertainty band are masked to be always zero.

My post-processing concept can be found in [`sw/main.c`](https://github.com/stnolting/fpga_puf/blob/main/sw/main.c)
providing more-detailed comments.
My post-processing concept can be found in [`sw/main.c`](https://github.com/stnolting/fpga_puf/blob/main/sw/main.c). The
source file provids more-detailed comments.


## Top Entity
Expand All @@ -111,15 +113,17 @@ end fpga_puf;
);
```

:warning: Note that the PUF cannot be simulated due to it's combinatorial loops.

## Evaluation

To evaluate this concept and the quality/reliability of the PUF IDs I am using the [NEORV32](https://github.com/stnolting/neorv32)
as processor platform. The `fpga_puf` IP module is added to the processor's "Custom Functions Subsystem (CFS)",
which is a _blank_ template for implementing custom application-specific co-processors.

The setup is synthesized for different FPGAs using different toolchains (like Intel and Lattice). A specific
bitstream generated and programmed into _several_ FPGAs of the _same type_ to check for chip-specific
ID variations. On one chip the ID is generated several times to check if it is "stable" and thus, reliable.
bitstream generated and programmed into _several_ FPGAs of the _same_ type to check for chip-specific
ID variations. On one chip the ID is generated several times to check if it is "stable over time" and thus, reliable.

### Setup

Expand Down

0 comments on commit 82dcee6

Please sign in to comment.