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
|
ADDR_UDI_LAST: 0x31
|
||||||
```
|
```
|
||||||
|
|
||||||
These registers provide read access to the 64-bit unique device
|
These read-only registers provide access to the 64-bit Unique Device
|
||||||
identity. The UDI is stored as ROM within the FPGA configuration. The
|
Identity (UDI).
|
||||||
registers can't be written to.
|
|
||||||
|
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
|
### RAM memory protecion
|
||||||
|
@ -5,16 +5,21 @@ Unique Device Secret core
|
|||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This core store and protect the Unique Device Secret (UDS) asset. The
|
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
|
UDS can be accessed as eight separate 32-bit words. The words can only
|
||||||
accessed in any order, but a given word can only be accessed once
|
be accessed as long as the fw_app_mode input is low, implying that the
|
||||||
between reset cycles. The words can only be accessed as long as the
|
CPU is executing the FW.
|
||||||
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
|
The UDS words can be accessed in any order, but a given word can only
|
||||||
accessed. This means that the even if the chip select (cs) control
|
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
|
input is forced high, the content will become all zero when the read
|
||||||
bit has been set after one cycle.
|
bit has been set after one cycle.
|
||||||
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
There are eight addresses in the API. These are defined by the
|
There are eight addresses in the API. These are defined by the
|
||||||
two values ADDR_UDS_FIRST and ADDR_UDS_LAST:
|
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
|
## 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>
|
# Written by Myrtle Shah <gatecat@ds0.me>
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
#
|
#
|
||||||
# patch_uds_udi.py
|
# Script to patch in a Unique Device Secret (UDS) and a Unique Device
|
||||||
# --------------
|
# Identifier (UDI) from files into a bitstream.
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# Both the UDI and UDS are using bit indexing from 32 LUTs for each
|
# It's supposed to be run like this:
|
||||||
# word, i.e., the first word consists of bit 0 from each 32 LUTs and
|
|
||||||
# so on.
|
|
||||||
#
|
#
|
||||||
# The size requirements for the UDI and UDS are specified as 1 bit (8
|
# nextpnr-ice40 --up5k --package sg48 --ignore-loops \
|
||||||
# bytes of data) and 3 bits (32 bytes of data), respectively. The UDI
|
# --json application_fpga_par.json --run patch_uds_udi.py
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# In the case of UDS, a read-enable signal is present, and the most
|
# with this environment:
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
# - 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
|
import os
|
||||||
|
|
||||||
@ -49,7 +57,8 @@ def rewrite_lut(lut, idx, data, has_re=False):
|
|||||||
new_init = 0
|
new_init = 0
|
||||||
for i, word in enumerate(data):
|
for i, word in enumerate(data):
|
||||||
if (word >> idx) & 0x1:
|
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))
|
repeat = (16 // len(data))
|
||||||
for k in range(repeat):
|
for k in range(repeat):
|
||||||
# UDS also has a read enable
|
# UDS also has a read enable
|
||||||
|
Loading…
Reference in New Issue
Block a user