Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AES-128 Encryption Core — Layered UVM Verification

A complete UVM 1.2 verification environment for a combinational AES-128 encryption core, implementing the FIPS 197 standard. The testbench achieves 88.89% functional coverage across 9 constrained-random test categories using a DPI-C golden reference model for scoreboard checking.


Table of contents


Overview

This project implements and verifies AES-128 (Advanced Encryption Standard, 128-bit key) as specified in NIST FIPS 197.

The RTL is a fully combinational 10-round encryption datapath — no pipeline registers, output is available after combinational propagation delay. The UVM environment stress-tests the design using constrained-random stimulus, cross-coverage, and bit-accurate comparison against an independent C reference model via DPI-C.


Repository structure

aes128-uvm/
├── rtl/
│   └── AES_Encrypt.v        # AES-128 encryption core (all modules)
├── tb/
│   └── tb_aes128_uvm.sv     # UVM testbench (interface + package + top module)
├── sim/
│   ├── run_sim.sh           # Xcelium simulation script
│   └── aes_ref_model.c      # DPI-C golden reference model (stub — see note)
└── docs/
    └── EDAPlayground_output.png  # Screenshot of final coverage results

Architecture

RTL design

The encryption core is structured as a cascade of purely combinational submodules:

AES_Encrypt (top)
├── keyExpansion       -- derives 11 round keys (44 x 32-bit words) from 128-bit input key
├── addRoundKey        -- initial whitening XOR (Round 0)
└── encryptRound x9   -- Rounds 1–9: SubBytes → ShiftRows → MixColumns → AddRoundKey
    ├── subBytes       -- 16 parallel S-box lookups
    ├── shiftRows      -- cyclic row shifts (0/1/2/3 bytes)
    ├── mixColumns     -- GF(2^8) column mixing
    └── addRoundKey
    Final round (Round 10): SubBytes → ShiftRows → AddRoundKey (no MixColumns)

The key schedule implements the FIPS 197 key expansion algorithm: every 4th word uses SubWord(RotWord(W[i-1])) XOR Rcon[i/4]; all other words use W[i-4] XOR W[i-1].

MixColumns performs polynomial multiplication over GF(2^8) with irreducible polynomial x^8 + x^4 + x^3 + x + 1. The mb2 (multiply by {02}) and mb3 (multiply by {03}) helper functions implement the field arithmetic using shift-and-XOR reduction.

Testbench hierarchy

Top (module)
└── Enc_Test              [uvm_test]
    └── Enc_Env           [uvm_env]
        ├── Enc_Agent     [uvm_agent]
        │   ├── uvm_sequencer #(Enc_Sequence_Item)
        │   ├── Enc_Driver    [uvm_driver]   -- drives Message/Key via clocking block
        │   └── Enc_Monitor   [uvm_monitor]  -- samples DUT output, owns covergroup
        └── Enc_ScoreBoard    [uvm_scoreboard] -- DPI-C comparison, pass/fail tally

TLM connections:

  • Driverseq_item_port / seq_item_exportSequencer
  • Monitoranalysis_portAgent.analysis_portScoreboard.analysis_export

Verification methodology

Test categories

The sequence generates one instance of each of the 9 directed categories (in shuffled order), followed by 100 back-to-back fully random transactions:

Category Message / Key pattern Purpose
ALL_ZEROS 128'h0 / 128'h0 FIPS 197 known-answer baseline
RANDOM_FULL Fully random 128-bit General-purpose random coverage
SPARSE_DATA 1–12 bits set (constrained) Boundary: near-zero Hamming weight
RANDOM_SPARSE 1–16 bits, random positions Hamming weight variation
ALTERNATING_0xAA 0xAAAA... / 0xAAAA... Checkerboard: tests alternating bit propagation
ALTERNATING_0x55 0x5555... / 0x5555... Inverse checkerboard
LSB_ONES 0x...0FF / 0x...0FF Boundary: only LSB byte active
MSB_ONES 0xFF0...0 / 0xFF0...0 Boundary: only MSB byte active
BYTE7_ONES Byte 7 = 0xFF, rest zero Middle-byte isolation

post_randomize() in the sequence item applies deterministic patterns for the non-random categories after the rand solver completes, ensuring the patterns are exact regardless of randomization order.

Coverage model

The covergroup is owned by the monitor and sampled on every valid transaction:

Coverpoint What it measures
msg_lsb_cp Message LSB byte: zero / ones / other
msg_byte7_cp Message byte 7: zero / ones / other
msg_msb_cp Message MSB byte: zero / ones / other
key_lsb_cp Key LSB byte: zero / ones / other
key_byte7_cp Key byte 7: zero / ones / other
key_msb_cp Key MSB byte: zero / ones / other
msg_allzero_cp All-zero plaintext
key_allzero_cp All-zero key
msg_key_lsb_cross Cross: all combinations of msg LSB × key LSB bins

Scoreboard and DPI-C reference model

On every transaction broadcast by the monitor, the scoreboard calls c_ref_model_encrypt() via DPI-C. This C function computes the expected AES-128 ciphertext independently of the RTL, using a known-good software implementation (OpenSSL or tiny-AES-c).

The scoreboard performs a bit-exact === comparison between the expected and actual ciphertext, reporting pass/fail per transaction and a summary at end-of-simulation.


Coverage results

Simulation over 109 transactions (9 directed + 100 random):

Coverpoint Coverage
Message LSB 100.00%
Message Byte7 50.00%
Message MSB 100.00%
Key LSB 100.00%
Key Byte7 100.00%
Key MSB 100.00%
Message All-Zero 100.00%
Key All-Zero 100.00%
Msg-Key LSB Cross 50.00%
Overall 88.89%

Two coverpoints fall short of 100%:

msg_byte7_cp (Message Byte7, 50.00%): The BYTE7_ONES test case sets byte 7 of both Message and Key to 0xFF, exercising the ones bin. However the zeros bin for Message byte 7 is never explicitly isolated — the all-zeros transaction sets every byte to zero simultaneously, which the monitor captures under msg_allzero_cp rather than as a dedicated byte-7 observation. Adding a directed test case with a non-zero Message where only byte 7 is 0x00 would close this bin.

msg_key_lsb_cross (50.00%): The cross product between msg_lsb_cp and key_lsb_cp has 9 bins (3 × 3). The bins that require Message LSB and Key LSB to both be in the zeros state simultaneously are only exercised by the ALL_ZEROS transaction. Since the monitor uses change-detection with a 2 ns settling delay, and the DUT is purely combinational, the all-zero vector is sometimes missed at the sampling edge. This is a known monitor timing limitation — not a DUT functional issue. All 109 scoreboard comparisons passed with zero errors.

Simulation output


How to run

Option A — Local (Cadence Xcelium):

Prerequisites: Cadence Xcelium with UVM 1.2, and a real AES-128 C implementation in sim/aes_ref_model.c (see the stub file for instructions).

cd sim/
chmod +x run_sim.sh
./run_sim.sh

Option B — EDA Playground (no local install):

  1. Go to https://www.edaplayground.com
  2. Create a new playground, set simulator to Cadence Xcelium (or Aldec Riviera-PRO)
  3. Paste rtl/AES_Encrypt.v into the Design tab and tb/tb_aes128_uvm.sv into the Testbench tab
  4. Under Libraries, enable UVM 1.2
  5. Note: EDA Playground does not support DPI-C external shared libraries. To run there, replace the c_ref_model_encrypt DPI-C call in the scoreboard with a known-answer SystemVerilog task, or disable scoreboard comparison and observe coverage only.
  6. Click Run

View waveforms:

gtkwave dump.vcd

Check simulation log:

cat sim.log | grep -E "PASS|FAIL|COV|SCB"

Tools and dependencies

Tool Version used
Cadence Xcelium 23.x
UVM 1.2
GTKWave 3.3.x
C compiler gcc 9+
Reference model OpenSSL 1.1 / tiny-AES-c

About

FFVDD - Team 15

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages