资讯动态

硬件加密协处理器设计:APB接口与SM4算法硬件实现

发布时间:2026/10/26 6:38:40 来源:尧图企业网站定制
简介这是一份面向数字IC设计与密码学硬件实现学习者的SM4密码协处理器Verilog工程文档聚焦APB总线接口与国密算法硬件加速的融合设计。资源以单个docx文档交付压缩包约37KB内容涵盖APB_slave从机模块、top_level顶层模块以及SM4基本模块的完整代码并附有密钥扩展、轮函数、S盒非线性变换与线性变换等原理说明。文档中详细定义了密钥寄存器、明文寄存器与密文寄存器通过PSELx、PENABLE、PWRITE等信号完成APB读写时序控制实现明文与密钥分四次写入、加密完成后分四拍读回密文的数据通路。读者可据此理解总线协议与密码算法模块的对接方式掌握寄存器地址映射、valid握手信号及复位逻辑的编写技巧适合具备一定Verilog基础、希望深入总线外设与安全芯片设计的中高级开发者参考。目前已有523人学习下载。1. 为什么硬件加密比软件快从一次总线写操作说起很多人第一次接触密码协处理器时都会有一个直觉性的疑问CPU 跑软件加密库明明也能出结果为什么还要专门用 Verilog 写一个硬件模块答案藏在一次 APB 总线写操作里。当 CPU 通过总线把 128 位明文分四个 32 位字写入协处理器时每个时钟周期只搬运 32 位但协处理器内部的 32 轮非线性迭代可以在同一时间段内并行展开。软件实现受限于指令流水线每一轮都需要取指、译码、执行、写回硬件实现则把轮函数直接铺成组合逻辑一个时钟推进一轮。这就是硬件加密协处理器存在的根本理由——不是算得更聪明而是把“串行执行”变成了“空间换时间”。SM4 作为分组长度和密钥长度均为 128 位的分组密码算法其轮函数结构天然适合硬件流水化。而 APB 总线作为低速外设总线恰好提供了足够的寄存器映射空间来承载密钥、明文和密文的搬运。理解这两者如何咬合是看懂整个协处理器设计的关键。2. APB 从机接口寄存器映射与握手时序的底层逻辑2.1 地址译码与寄存器分区APB 从机模块的核心任务只有一件事把总线上的地址翻译成内部寄存器的读写操作。在给出的apb_slave模块中地址的低 8 位PADDR[7:0]被用作译码依据映射关系非常直接0x00到0x0c对应四个 32 位明文寄存器data1到data40x10到0x1c对应四个 32 位密钥寄存器key1到key4。读通道则把密文寄存器DATA_ENC的四个 32 位切片映射回相同的地址区间。这种地址分配方式有一个容易被忽略的设计动机读写地址重叠。写0x00写入的是明文读0x00读出的是密文。从硬件资源角度看这节省了地址译码逻辑从软件角度看CPU 可以用同一套地址偏移量完成写入和读取不需要维护两套地址表。常见的实现思路是让读写共用地址空间通过PWRITE信号区分方向这在资源受限的嵌入式场景中很常见。// 写通道根据地址低8位将PWDATA分发到对应寄存器 case(PADDR[7:0]) 8h00: data1 PWDATA; 8h04: data2 PWDATA; 8h08: data3 PWDATA; 8h0c: begin data4 PWDATA; text_isvalid_in 1b1; // 第四个明文写入完成拉高有效标志 end 8h10: key1 PWDATA; 8h14: key2 PWDATA; 8h18: key3 PWDATA; 8h1c: begin key4 PWDATA; mk_isvalid_in 1b1; // 第四个密钥写入完成拉高有效标志 end endcase这段代码里最关键的设计点是text_isvalid_in和mk_isvalid_in的置位时机。它们不是在第一个字写入时就拉高而是在第四个 32 位字写入完成的那个时钟周期才置位。这保证了 SM4 核心模块看到的DATA_i和KEY一定是完整的 128 位数据不会出现半截数据触发加密的情况。参数PADDR[7:0]取低 8 位而非全部 32 位是因为 APB 外设的寄存器空间通常很小高位地址在系统级译码时已经被片选信号PSELx过滤掉了。2.2 写有效与读有效的时序窗口APB 协议的传输分为两个阶段Setup 阶段和 Access 阶段。PSELx在 Setup 阶段拉高PENABLE在 Access 阶段拉高。只有PSELx PENABLE PWRITE同时为真时写操作才真正生效。这个条件在代码中体现为else if(PSELx PENABLE PWRITE) // 写数据读操作的条件则是PSELx PENABLE !PWRITE。这里有一个常见的误解认为PSELx拉高就可以读写。实际上 APB 协议要求PENABLE必须与PSELx同时有效才能完成传输这是 APB 区别于 AHB 的一个重要特征。APB 没有流水线每次传输至少两个时钟周期这降低了协议复杂度但也意味着带宽有限。对于 SM4 这种 128 位分组加密来说搬运 256 位数据明文加密钥需要 8 次写传输每次至少两个时钟周期总共至少 16 个时钟周期。而 SM4 核心完成一次加密需要 32 轮每轮一个时钟周期。总线搬运时间和加密计算时间在同一量级这也是为什么 APB 足够用的原因——瓶颈不在总线。2.3 组合逻辑输出与数据对齐always (*)块负责把四个 32 位寄存器拼接成 128 位输出always (*) begin if(!PRESETN) begin KEY 128h0; DATA_i 128h0; end else if(text_isvalid_in mk_isvalid_in) begin KEY {key1, key2, key3, key4}; DATA_i {data1, data2, data3, data4}; end end拼接顺序是{key1, key2, key3, key4}其中key1对应地址0x10是密钥的最高 32 位。这意味着 CPU 写入时先写0x10的是密钥的高位字。如果软件端的字节序处理不当就会出现密钥高低位颠倒的问题。常见的实现思路是在软件驱动层统一按小端序处理硬件端保持固定的拼接顺序双方约定一致即可。DATA_ENC的更新则更简单always (SM4_DATA) DATA_ENC SM4_DATA;这是一个电平敏感的锁存逻辑只要SM4_DATA变化就更新密文寄存器。这里没有用时钟同步是因为SM4_DATA本身来自 SM4 核心的寄存器输出已经是被时钟同步过的信号。这种写法在综合时可能会产生锁存器警告但功能上没有问题。3. SM4 核心32 轮迭代的硬件展开与密钥扩展3.1 轮函数 F 的硬件映射SM4 的轮函数定义为F(X0, X1, X2, X3, rk) X0 ^ T(X1 ^ X2 ^ X3 ^ rk)其中 T 是合成置换由非线性变换和线性变换组成。在硬件中这个函数的实现被拆解为几个并行步骤// 线性变换部分 {b0, b1, b2, b3} X[cnt1] ^ X[cnt2] ^ X[cnt3] ^ rk[cnt]; A {Sbox[b0], Sbox[b1], Sbox[b2], Sbox[b3]}; X[cnt4] X[cnt] ^ A[31:0] ^ ({A[29:0], A[31:30]}) // 循环左移2位 ^ ({A[21:0], A[31:22]}) // 循环左移10位 ^ ({A[13:0], A[31:14]}) // 循环左移18位 ^ ({A[7:0], A[31:8]}); // 循环左移24位这里用位拼接实现了循环左移避免了移位操作符在综合时可能产生的额外逻辑。{A[29:0], A[31:30]}表示把 A 的低 30 位和高 2 位拼接等效于循环左移 2 位。四个移位结果异或在一起完成了线性变换 L(B) B ^ (B2) ^ (B10) ^ (B18) ^ (B24)。这种写法在综合后通常映射为纯组合逻辑延迟取决于工艺库中异或门和布线延迟。S 盒的实现是一个 256 字节的查找表用寄存器数组存储reg [7:0] Sbox [0:255]; // 初始化时逐项赋值 Sbox[0] 8hd6; Sbox[1] 8h90; // ... 共256项在 FPGA 中这个查找表会被映射到分布式 RAM 或块 RAM 中。如果综合工具识别出这是一个只读查找表可能会用 LUT 实现。256 字节的查找表在 FPGA 中资源占用很小但在 ASIC 中需要评估面积和功耗。常见的优化思路是用组合逻辑实现 S 盒的代数表达式但会增加关键路径延迟。这里选择查找表是因为 SM4 的 S 盒没有已知的简单代数结构查找表是最直接的实现方式。3.2 密钥扩展与轮密钥生成密钥扩展的逻辑与加密逻辑结构相似但常数不同// 密钥扩展 {a0, a1, a2, a3} K[cnt1] ^ K[cnt2] ^ K[cnt3] ^ CK[cnt]; B {Sbox[a0], Sbox[a1], Sbox[a2], Sbox[a3]}; K[cnt4] K[cnt] ^ B[31:0] ^ ({B[18:0], B[31:19]}) // 循环左移13位 ^ ({B[8:0], B[31:9]}); // 循环左移23位 rk[cnt] K[cnt4];密钥扩展的线性变换参数是 13 和 23而加密的线性变换参数是 2、10、18、24。这是 SM4 算法设计中的一个关键区分点。密钥扩展的初始状态由系统参数 FK 和密钥 MK 异或得到K[3] MK3 ^ FK[3]; K[2] MK2 ^ FK[2]; K[1] MK1 ^ FK[1]; K[0] MK0 ^ FK[0];FK 的四个常数A3B1BAC6、56AA3350、677D9197、B27022DC是固定的CK 的 32 个常数则按规律递增。这些常数在代码中逐项初始化综合时会变成常量不会消耗寄存器资源。3.3 计数器与就绪信号的状态机整个 SM4 核心由一个 6 位计数器cnt驱动从 0 计数到 31对应 32 轮迭代if(ready_out 0) begin if(cnt 6d31) begin cnt 6b000000; carry 1b1; ready_out 1b1; end else begin cnt cnt 6b1; carry 0; ready_out 1b0; end endready_out为 0 表示正在计算为 1 表示空闲。当mk_isvalid_in或text_isvalid_in有效时计数器清零ready_out拉低开始计算。第 31 轮结束后carry拉高ready_out恢复为 1同时输出密文if(carry 1b1) begin dat_o {X[35], X[34], X[33], X[32]}; end这里输出的是X[35]到X[32]而不是X[31]到X[28]。这是因为 SM4 的加密结果需要经过一次反序变换(Y0, Y1, Y2, Y3) (X35, X34, X33, X32)。硬件实现中直接取最后四个状态值即可不需要额外的反序逻辑。4. 避坑与排查仿真通过但上板失败的那些事4.1 现象仿真波形正确综合后时序违例原因always (*)块中的组合逻辑路径过长。KEY和DATA_i的拼接逻辑虽然简单但如果综合工具把 S 盒查找表和线性变换都映射到组合逻辑中关键路径可能超过时钟周期。解决在DATA_i和KEY输出后插入一级寄存器把组合逻辑路径切断。或者在综合约束中降低时钟频率给组合逻辑留出足够的建立时间。4.2 现象写入密钥后加密结果不稳定原因mk_isvalid_in和text_isvalid_in是电平信号在always (*)块中被读取。如果这两个信号在 SM4 核心的时钟域中没有被正确同步可能产生亚稳态。解决在 SM4 核心内部对这两个信号做两级寄存器同步或者确保 APB 时钟和 SM4 核心时钟是同一个时钟源。在给出的代码中PCLK同时驱动 APB 从机和 SM4 核心属于同步设计但跨模块信号仍建议加同步器。4.3 现象读出的密文与软件计算结果不一致原因字节序处理错误。APB 总线写入的PWDATA是 32 位字但 SM4 算法按字节操作。如果软件端把 128 位数据按大端序拆分成四个 32 位字而硬件端按小端序拼接结果就会错位。解决在软件驱动层统一字节序。通常的做法是软件端把 128 位数据按小端序拆成四个 32 位字依次写入0x00到0x0c。硬件端按{data1, data2, data3, data4}拼接其中data1是第一个写入的字。双方约定一致后结果自然匹配。4.4 现象text_isvalid_in和mk_isvalid_in同时拉高时加密不启动原因在always (*)块中KEY和DATA_i的赋值条件是text_isvalid_in mk_isvalid_in。如果两个信号不是在同一时钟周期拉高组合逻辑的输出会等待最后一个信号有效。解决这是设计意图不是 bug。但需要注意如果 CPU 先写完密钥再写明文mk_isvalid_in会先拉高此时KEY已经有效但DATA_i还在等待text_isvalid_in。当明文写完时两个信号同时有效DATA_i和KEY同时更新SM4 核心开始计算。如果 CPU 写密钥和明文的顺序不确定建议在软件端保证先写密钥再写明文或者用独立的 valid 信号分别触发。4.5 现象ready_out信号在复位后为高但第一次加密不输出结果原因复位时ready_out 1b1cnt 0。当mk_isvalid_in或text_isvalid_in有效时ready_out被拉低cnt清零。但如果 valid 信号只持续一个时钟周期而ready_out的拉低逻辑在下一个时钟周期才生效可能会错过。解决确保 valid 信号至少持续到ready_out拉低。在给出的测试代码中mk_isvalid_in和text_isvalid_in在delayx(2)后被拉低即持续了两个时钟周期足够ready_out响应。如果实际使用中 valid 信号更短需要在 APB 从机中增加一个握手信号确认 SM4 核心已经接收。5. 设计取舍为什么用查找表而不是组合逻辑实现 S 盒SM4 的 S 盒是一个 16×16 的查找表输入 8 位输出 8 位。在硬件实现中有两种主流方案查找表法和组合逻辑法。查找表法把 256 个输出值存储在寄存器或 RAM 中输入作为地址索引。组合逻辑法则用布尔表达式直接计算输出通常基于 S 盒的代数结构进行优化。查找表法的优势在于延迟固定且可预测。无论输入是什么输出都在一个查找表访问延迟内到达。在 FPGA 中256 字节的查找表可以映射到分布式 RAM访问延迟通常为 1 到 2 纳秒。组合逻辑法的优势在于面积小不需要存储 256 个字节但延迟取决于输入值的不同路径可能存在毛刺和竞争冒险。在给出的代码中S 盒用寄存器数组实现初始化时逐项赋值。综合工具会识别出这是一个只读存储器在 FPGA 中映射为 LUT 或块 RAM。这种实现方式的资源占用在 FPGA 中完全可以接受但在 ASIC 中需要评估面积。如果面积敏感可以考虑用组合逻辑实现 S 盒但需要仔细验证时序。另一个设计取舍是轮密钥的生成方式。代码中密钥扩展和加密共用同一个计数器cnt在ready_out 0期间同时进行。这意味着密钥扩展和加密是并行执行的每一轮加密使用的轮密钥在同一时钟周期内生成。这种设计的优点是节省了存储 32 个轮密钥的寄存器缺点是密钥扩展的逻辑必须和加密逻辑一样快否则会成为关键路径。如果时序紧张可以把密钥扩展提前完成把 32 个轮密钥存储在寄存器中加密时直接读取。这会增加 32×32 位的寄存器开销但可以缩短关键路径。从那以后我每次写密码协处理器都会先把 S 盒的访问延迟和轮函数的组合逻辑延迟分别约束确保关键路径不超过时钟周期的一半。这个习惯帮我省去了很多次上板后才发现时序违例的返工。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑