A compact RTL and Python framework for simulating Arbiter PUF variants, extracting CRPs, measuring core PUF metrics, and benchmarking simple modeling attacks.
- Basic Arbiter PUF RTL in
rtl/arbiter_puf.sv - XOR Arbiter PUF wrapper in
rtl/xor_arbiter_puf.sv - Multi-bit Arbiter PUF wrapper in
rtl/multi_bit_arbiter_puf.sv - Self-checking multi-PUF testbench in
sim/tb_arbiter_puf.sv - Assertion bundle in
sim/puf_assert.sv - Metrics suite: uniformity, uniqueness, reliability, bit aliasing, avalanche
- Modeling attack benchmark with logistic regression
- Figure generation for README images and metric dashboards
- GitHub Actions CI with RTL lint, simulation, metrics, and attack checks
An Arbiter PUF sends a challenge-controlled race through a chain of mux stages. The final race outcome is sampled by an arbiter latch. In this repository, SEED-driven mux bias is used to model repeatable simulation variation; that is useful for verification, but it is still a deterministic simulation model, not physical silicon randomness.
| Port | Direction | Description |
|---|---|---|
clk |
input | Clock |
rst_n |
input | Active-low reset |
valid_in |
input | Input strobe |
challenge[N-1:0] |
input | Challenge vector |
response |
output | 1-bit response |
valid_out |
output | Response valid strobe |
| Parameter | Default | Description |
|---|---|---|
N |
64 |
Number of stages |
SEED |
64'h9E3779B97F4A7C15 |
Deterministic stage-bias seed |
| Port | Direction | Description |
|---|---|---|
clk |
input | Clock |
rst_n |
input | Active-low reset |
valid_in |
input | Input strobe |
challenge[N-1:0] |
input | Shared challenge vector |
response |
output | XOR of K internal Arbiter responses |
valid_out |
output | Response valid strobe |
| Parameter | Default | Description |
|---|---|---|
N |
64 |
Number of stages per Arbiter |
K |
4 |
Number of parallel arbiters |
SEED_BASE |
64'h9E3779B97F4A7C15 |
Base seed for internal instances |
| Port | Direction | Description |
|---|---|---|
clk |
input | Clock |
rst_n |
input | Active-low reset |
valid_in |
input | Input strobe |
challenge[N-1:0] |
input | Shared challenge vector |
response[M-1:0] |
output | Multi-bit response vector |
valid_out |
output | Response valid strobe |
| Parameter | Default | Description |
|---|---|---|
N |
64 |
Number of stages per Arbiter |
M |
64 |
Number of response bits |
SEED_BASE |
64'hD1B5_4A32_9F3C_6E21 |
Base seed for bit lanes |
arbiter_puf #(
.N(64),
.SEED(64'h0123_4567_89AB_CDEF)
) u_basic (
.clk(clk),
.rst_n(rst_n),
.valid_in(valid_in),
.challenge(challenge),
.response(response),
.valid_out(valid_out)
);xor_arbiter_puf #(
.N(64),
.K(4),
.SEED_BASE(64'hA5A5_5A5A_0123_4567)
) u_xor (
.clk(clk),
.rst_n(rst_n),
.valid_in(valid_in),
.challenge(challenge),
.response(response),
.valid_out(valid_out)
);multi_bit_arbiter_puf #(
.N(64),
.M(64),
.SEED_BASE(64'h0F0F_F0F0_1357_9BDF)
) u_multi (
.clk(clk),
.rst_n(rst_n),
.valid_in(valid_in),
.challenge(challenge),
.response(response),
.valid_out(valid_out)
);The main testbench exercises all three RTL modules, emits CRPs to sim/crp_data.csv, injects response noise for reliability measurement, and prints a PASS/FAIL summary.
Run it with Icarus Verilog:
make -f scripts/Makefile simThat command compiles:
rtl/arbiter_puf.svrtl/mux_stage.svrtl/xor_arbiter_puf.svrtl/multi_bit_arbiter_puf.svsim/puf_assert.svsim/tb_arbiter_puf.sv
Run the full metrics suite on the generated CSV:
python scripts/analyze_puf.py --input sim/crp_data.csv --output-dir sim/analysisOutputs:
sim/analysis/metrics.jsonsim/analysis/metrics.txtsim/analysis/metrics_dashboard.pngsim/analysis/metrics_dashboard.pdf
Metrics covered:
- Uniformity: percentage of ones in the response stream
- Uniqueness: inter-chip Hamming distance
- Reliability: identical-response rate under injected noise
- Bit aliasing: per-bit bias across multi-bit responses
- Avalanche: response distance after one-bit challenge flips
Benchmark a simple logistic-regression attacker against the basic and XOR PUFs:
python scripts/ml_attack.py --input sim/crp_data.csv --output-dir sim/attackOutputs:
sim/attack/ml_attack.jsonsim/attack/ml_attack.txt
The expected trend is that the basic Arbiter PUF is easier to model than the XOR variant.
Generate the README figures:
python scripts/generate_puf_figures.py --output-dir docsThe dashboard should land near these targets:
- Uniformity: about 50%
- Uniqueness: about 50%
- Reliability: near 100%
- Avalanche: near 50% response distance for one-bit challenge changes
- The base RTL uses deterministic seed-driven path bias for repeatable simulation.
- That is useful for verification, but it is not a physical PUF measurement model.
- XOR Arbiter PUFs increase modeling resistance by combining multiple internal responses.
- Multi-bit wrappers enable key-generation workflows, but each bit lane still needs an independent evaluation of bias and stability.
- The included attack script is intentionally simple; it is enough to show the basic Arbiter PUF is easier to model than the XOR wrapper.
GitHub Actions runs:
- Icarus simulation for all PUF variants
- Python metrics generation
- Modeling attack benchmark
- Verilator lint on RTL and assertion files
- Artifact upload for generated figures and reports
git clone https://github.com/a0ax/ArbiterPUF.git
cd ArbiterPUF
make -f scripts/Makefile sim
python scripts/analyze_puf.py --input sim/crp_data.csv --output-dir sim/analysis
python scripts/ml_attack.py --input sim/crp_data.csv --output-dir sim/attack
python scripts/generate_puf_figures.py --output-dir docsrtl/- synthesizable SystemVerilog modulessim/- testbench and assertion logicscripts/- analysis, figures, and attack toolingmodels/- behavioral Python model for fast prototypingdocs/- generated figures for the README

