mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-10-01 01:45:38 -04:00
hw/tool: UDI/UDS storage
Describe how the UDI and UDS are actually stored in the FPGA, how they are accessed, and how they are initialled by the patch_uds_udi.py script. Co-authored-by: Joachim Strömbergson <joachim@assured.se>
This commit is contained in:
parent
1c90b1aa3d
commit
3cf218469c
@ -110,9 +110,27 @@ secret for any secrets it needs to perform its intended use case.
|
||||
ADDR_UDI_LAST: 0x31
|
||||
```
|
||||
|
||||
These registers provide read access to the 64-bit unique device
|
||||
identity. The UDI is stored as ROM within the FPGA configuration. The
|
||||
registers can't be written to.
|
||||
These read-only registers provide access to the 64-bit Unique Device
|
||||
Identity (UDI).
|
||||
|
||||
The two UDI words are stored using 32 named SB\_LUT4 FPGA multiplexer
|
||||
(MUX) instances, identified in the source code as "udi\_rom\_idx". One
|
||||
instance for each bit in core read_data output bus.
|
||||
|
||||
Each SB\_LUT4 MUX is able to store 16 bits of data, in total 512 bits.
|
||||
But since the UDI is 64 bits, we only use the two LSBs in each MUX.
|
||||
Note that only the LSB address of the SB_LUT4 instances are connected
|
||||
to the CPU address. This means that only the two LSBs in each MUX can
|
||||
be addressed.
|
||||
|
||||
During build of the FPGA design, the UDI is set to a known bit
|
||||
pattern, which means that the SB_LUT4 instantiations are initialized
|
||||
to a fixed bit pattern.
|
||||
|
||||
The tool 'patch\_uds\_udi.py' is used to replace the fixed bit pattern
|
||||
with a unique bit pattern before generating the per device unique FPGA
|
||||
bitstream. This allows us to generate these device unique FPGA
|
||||
bitstreams without having to do a full FPGA build.
|
||||
|
||||
|
||||
### RAM memory protecion
|
||||
|
@ -5,16 +5,21 @@ Unique Device Secret core
|
||||
## Introduction
|
||||
|
||||
This core store and protect the Unique Device Secret (UDS) asset. The
|
||||
UDS can be accessed as eight separate 32-bit words. The words can be
|
||||
accessed in any order, but a given word can only be accessed once
|
||||
between reset cycles. The words can only be accessed as long as the
|
||||
fw_app_mode input is low, implying that the CPU is executing the FW.
|
||||
UDS can be accessed as eight separate 32-bit words. The words can only
|
||||
be accessed as long as the fw_app_mode input is low, implying that the
|
||||
CPU is executing the FW.
|
||||
|
||||
Each UDS words has a companion read bit that is set when the word is
|
||||
accessed. This means that the even if the chip select (cs) control
|
||||
The UDS words can be accessed in any order, but a given word can only
|
||||
be accessed once between reset cycles. This read once functionality is
|
||||
implemented with a companion read bit for each word. The read bit is
|
||||
set when the word is first accessed. The read bit controls if the real
|
||||
UDS word is returned or not.
|
||||
|
||||
This means that the even if the chip select (cs) control
|
||||
input is forced high, the content will become all zero when the read
|
||||
bit has been set after one cycle.
|
||||
|
||||
|
||||
## API
|
||||
There are eight addresses in the API. These are defined by the
|
||||
two values ADDR_UDS_FIRST and ADDR_UDS_LAST:
|
||||
@ -31,4 +36,27 @@ Any access to another address will be ignored by the core.
|
||||
|
||||
## Implementation
|
||||
|
||||
The UDS words are implemented using discrete registers.
|
||||
These read-only registers provide read access to the 256-bit UDS.
|
||||
|
||||
The eight UDS words are stored using 32 named SB\_LUT4 FPGA
|
||||
multiplexer (MUX) instances, identified in the source code as
|
||||
"uds\_rom\_idx". One instance for each bit in the core read\_data
|
||||
output bus.
|
||||
|
||||
During build of the FPGA design, the UDS is set to a known bit
|
||||
pattern, which means that the SB\_LUT4 instantiations are initialized
|
||||
to a fixed bit pattern.
|
||||
|
||||
The tool 'patch\_uds\_udi.py' is used to replace the fixed bit pattern
|
||||
with a unique bit pattern before generating the per device unique FPGA
|
||||
bitstream. This allows us to generate these device unique FPGA
|
||||
bitstreams without haveing to do a full FPGA build.
|
||||
|
||||
Each SB\_LUT4 MUX is able to store 16 bits of data, in total 512 bits.
|
||||
But since the UDS is 256 bits, we only use the eight LSBs in each MUX.
|
||||
|
||||
The eighth MSBs in each MUX will be initialized to zero. The read
|
||||
access bit (se description above) for a given word is used as the
|
||||
highest address bit to the MUXes. This forces any subsequent accesses
|
||||
to a UDS word to read from the MUX MSBs, not the LSBs where the UDS is
|
||||
stored.
|
||||
|
@ -5,32 +5,40 @@
|
||||
# Written by Myrtle Shah <gatecat@ds0.me>
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# patch_uds_udi.py
|
||||
# --------------
|
||||
# Python program that patches the UDS and UDI implemented using
|
||||
# named LUT4 instances to have unique initial values, not the generic
|
||||
# values used during synthesis, p&r and mapping. This allows us to
|
||||
# generate device unique bitstreams without running the complete flow.
|
||||
# Script to patch in a Unique Device Secret (UDS) and a Unique Device
|
||||
# Identifier (UDI) from files into a bitstream.
|
||||
#
|
||||
# Both the UDI and UDS are using bit indexing from 32 LUTs for each
|
||||
# word, i.e., the first word consists of bit 0 from each 32 LUTs and
|
||||
# so on.
|
||||
# It's supposed to be run like this:
|
||||
#
|
||||
# The size requirements for the UDI and UDS are specified as 1 bit (8
|
||||
# bytes of data) and 3 bits (32 bytes of data), respectively. The UDI
|
||||
# does not occupy the entire LUT4 instance, and to conserve resources,
|
||||
# the pattern of the UDI is repeated over the unused portion of the
|
||||
# LUT4 instance. This eliminates the need to drive the three MSB pins
|
||||
# while still achieving the correct output.
|
||||
# nextpnr-ice40 --up5k --package sg48 --ignore-loops \
|
||||
# --json application_fpga_par.json --run patch_uds_udi.py
|
||||
#
|
||||
# In the case of UDS, a read-enable signal is present, and the most
|
||||
# significant bit serves as the read-enable input. This requires the
|
||||
# lower half of initialization bits to be forced to zero, ensuring
|
||||
# that the memory outputs zero when the read-enable signal is
|
||||
# inactive.
|
||||
# with this environment:
|
||||
#
|
||||
# - UDS_HEX: path to the UDS file, typically the path to
|
||||
# ../data/uds.hex
|
||||
# - UDI_HEX: path to the UDI file, typically the path to ../data/udi.hex
|
||||
# - OUT_ASC: path to the ASC output that is then used by icebram and icepack.
|
||||
#
|
||||
#=======================================================================
|
||||
# The script changes the UDS and UDI that are stored in named 4-bit
|
||||
# LUT instances in the JSON file so we can generate device
|
||||
# unique bitstreams without running the complete flow just to change
|
||||
# UDS and UDI. Then we can just run the final bitstream generation
|
||||
# from the ASC file.
|
||||
#
|
||||
# We represent our UDI and UDS values as a number of 32 bit words:
|
||||
#
|
||||
# - UDI: 2 words.
|
||||
# - UDS: 8 words.
|
||||
#
|
||||
# We reserve 32 named 4-bit LUTs *each* to store the data: UDS in
|
||||
# "uds_rom_idx" and UDI in "udi_rom_idx".
|
||||
#
|
||||
# The script repeats the value in the LUTs so we don't have to care
|
||||
# about the value of the unused address bits.
|
||||
#
|
||||
# See documentation in their implementation in ../core/uds/README.md
|
||||
# and ../core/tk1/README.md
|
||||
|
||||
import os
|
||||
|
||||
@ -49,7 +57,8 @@ def rewrite_lut(lut, idx, data, has_re=False):
|
||||
new_init = 0
|
||||
for i, word in enumerate(data):
|
||||
if (word >> idx) & 0x1:
|
||||
# repeat so inputs above address have a don't care value
|
||||
# repeat so we don't have to care about inputs above
|
||||
# address
|
||||
repeat = (16 // len(data))
|
||||
for k in range(repeat):
|
||||
# UDS also has a read enable
|
||||
|
Loading…
Reference in New Issue
Block a user