OTP的工作原理
物理特性
OTP存储器通常是基于半导体制造工艺实现的,在编程时通过烧毁熔丝或调整存储单元的电荷状态来记录数据。OTP最常见的实现方式是在晶体管级别修改存储单元的物理属性,达到永久保存信息的效果。因为这种物理变化是不可逆的,存储器数据一旦写入后就不能再修改或擦除。
OTP的主要特点
一次性编程:OTP只能编程一次,数据无法修改或删除,这使得它非常适合存储永久性的数据。
高可靠性:编程后的数据非常稳定,能够在极端环境条件下保持长时间的可靠性。
低成本:与其他可重复编程的存储器相比,OTP的制造成本较低,因为它不需要支持反复写入或擦除的复杂电路。
适用于安全应用:由于数据一旦写入就不能更改,OTP非常适合用于存储安全密钥、加密信息、唯一标识符等,防止数据篡改。
OTP的应用场景
微控制器(MCU)中的固件存储:
在一些嵌入式设备中,OTP可用于存储微控制器的初始固件代码。
芯片配置:
一些集成电路在生产过程中需要配置特定功能参数,OTP可以用来一次性设置这些参数。
安全性应用:
OTP常用于存储加密密钥、设备唯一标识符(ID)、安全启动代码等需要防止篡改的数据。
校准数据:
在传感器或其他精密设备中,OTP可以存储制造过程中产生的校准数据,确保设备在整个生命周期内的准确性。
产品序列号或身份识别:
OTP可以用来存储产品的序列号、批次号或其他身份识别信息,便于追溯和认证。
HPM OTP熔丝阵列
对于HPM SOC的片上OTP,共有4种类型
– 识别(IDENTITY):芯片的出厂信息,UUID 等信息归为识别类型,此类信息由原厂烧写,安全启动的公钥 HASH 也归为识别类型,可由客户烧写
– 安全(SECURE):芯片的生命周期,OTP 硬件锁定位等 OTP 字归为安全类型
– 密钥(SECRET):各类密钥,如 Debug Key, EXIP 的 KEK,密钥管理器的 FMK 等归为密钥类型
– 通用(GENERAL):用户可自由烧录通用数据,归为通用类型。保留给 BOOT ROM 使用的控制 OTP 字,也归为通用类型
如何读取熔丝阵列:
通过FUSE寄存器直接读写
-共128个FUSE寄存器,对应128个字(8位,32bit)
-共8个FUSELOCK寄存器,每2个bit对应一个FUSE寄存器
-00 不锁定,FUSELOCK可修改
-01 写锁定,只能读,FUSELOCK不可修改
-10 不锁定,FUSELOCK不可修改
-11 读写锁定,不能读写,FUSELOCK不可修改
-LOCK寄存器
-写特定值(密钥见手册)可以解锁,写其他任何值都会打开写锁定
通过操作引擎读写
有什么用?
-读取熔丝比较慢,如果阻塞读会妨碍程序运行
-通过引擎读可以触发中断,从而不影响主程序运行
读取步骤:
-ADDR内通过6bit寻址指定OTP字
-CMD指定读/写
-读写完成后产生中断
-从DATA中读取数据
影子寄存器
有什么用?
-在程序复位时,会将FUSE寄存器的值保存至对应的SHADOW寄存器中
-FUSE寄存器中的值与OTP熔丝中的值是完全一致的,这就意味着他是不可更改的
-与程序实际运行的很多OTP内的值是加载进shadow后生效的,方便后期更改,软件控制
-直接读取SHADOW获取OTP值的速度是最快的,也是最方便的
SHADOW_LOCK
与FUSE_LOCK完全一致,用于锁定shadow寄存器
读写保护
除了FUSE和SHADOW各有各的LOCK寄存器外,在OTP中还有一个32bit的硬件LOCK
1bit对应4个字的熔丝,硬件锁定对不同类型的熔丝以及FUSE/SHADOW影响如下图所示
结合MFG Tool讲一下硬件锁保护
每位bit对应4个字的硬件锁
对于前四个bit 0110就意味着 word0-word3没有锁 ,word4-word11上锁了,Word12-word15没有锁
对于密钥类,如果上锁不仅有写保护,也有读保护,可以看到word56-word59是密文
查看他们对应word0的第14位也是1
如果尝试写入被锁定的字,会失败
注意:word0的修改需要在SOC复位后才能生效,可见他是load进影子寄存器后才会生效的
OTP的软件api
Otp的驱动由ROM API管理,共提供以下接口
typedef struct {
/**< OTP driver interface version */
uint32_t version;
/**< OTP driver interface: init */
void (*init)(void);
/**< OTP driver interface: deinit */
void (*deinit)(void);
/**< OTP driver interface: read from shadow */
uint32_t (*read_from_shadow)(uint32_t addr);
/**< OTP driver interface: read from ip */
uint32_t (*read_from_ip)(uint32_t addr);
/**< OTP driver interface: program */
hpm_stat_t (*program)(uint32_t addr, const uint32_t *src, uint32_t num_of_words);
/**< OTP driver interface: reload */
hpm_stat_t (*reload)(otp_region_t region);
/**< OTP driver interface: lock */
hpm_stat_t (*lock)(uint32_t addr, otp_lock_option_t lock_option);
/**< OTP driver interface: lock_shadow */
hpm_stat_t (*lock_shadow)(uint32_t addr, otp_lock_option_t lock_option);
/**< OTP driver interface: set_configurable_region */
hpm_stat_t (*set_configurable_region)(uint32_t start, uint32_t num_of_words);
/**< OTP driver interface: write_shadow_register */
hpm_stat_t (*write_shadow_register)(uint32_t addr, uint32_t data);
} otp_driver_interface_t;
如何使用?
ROM_API_TABLE_ROOT在ROM内已经完成了初始化,直接通过他指向otp_driver_if即可,例如:
ROM_API_TABLE_ROOT->otp_driver_if->read_from_shadow(i);
不需要做任何的配置