本章节为my_bit8_mixcolum.v代码解析(完整代码见文章最后)
在上一篇文章中,我们深入分析了bit1_mixcolum——一个仅处理4个输入字节、输出2个字节的轻量级线性变换单元。这种“比特片”设计展示了如何在有限域上仅用异或和移位实现高效运算。然而,在实际的AES或类AES算法中,我们需要处理32位字甚至128位状态块。如何将基础单元扩展为实用的模块?本文介绍的bit8_mixcolum正是答案:通过模块化复用和循环输入重排,将四个bit1_mixcolum并行组合,一次性完成32位数据的双路列混合。
1. 模块功能概览
| 端口名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
| in | input | 32 | 输入数据,按大端字节序包含四个字节 (a,b,c,d) |
| outx | output | 32 | 第一个输出字,由四组独立变换的结果拼接而成 |
| outy | output | 32 | 第二个输出字,对应另一种线性组合的结果 |
核心功能:将输入的32位视为4个独立的字节,通过四个并行的bit1_mixcolum实例(每个实例的输入顺序经过循环移位),产生两个32位输出。这两个输出分别对应同一输入列混合后的两组不同线性关系。
2. 内部结构与工作原理
2.1 字节拆分
首先将32位输入按大端序拆分为四个字节:
a = in[31:24](最高字节)b = in[23:16]c = in[15:8]d = in[7:0](最低字节)
这种拆分方式与AES官方文档及大多数处理器内存布局一致。
2.2 并行实例化的连接方式
模块内部例化了四个bit1_mixcolum子模块,分别记为u1、u2、u3、u4。每个子模块的输入顺序不是固定的(a,b,c,d),而是按照循环右移(或左移)方式重排:
| 实例 | in1 | in2 | in3 | in4 | 输出Out1 | 输出Out2 |
|---|---|---|---|---|---|---|
| u1 | a | b | c | d | x1 | y1 |
| u2 | b | c | d | a | x2 | y2 |
| u3 | c | d | a | b | x3 | y3 |
| u4 | d | a | b | c | x4 | y4 |
这种旋转方式的直观理解:
- 如果把输入四个字节看作一个循环队列,那么每个子模块依次从队列的不同起点开始取四个连续元素。
- 当所有子模块的
Out1按顺序(x1,x2,x3,x4)拼接时,得到的结果相当于对四组不同旋转的输入应用同一个变换F1。同理,(y1,y2,y3,y4)拼接得到F2变换的结果。
2.3 输出拼接
最终输出为:
outx = {x1, x2, x3, x4} outy = {y1, y2, y3, y4}保持大端字节序,与输入格式一致。
3. 数学形式化描述
设bit1_mixcolum定义的变换为:
F1(a,b,c,d) = b ⊕ c ⊕ d ⊕ xtime(a ⊕ b) F2(a,b,c,d) = xtime(xtime( (a⊕c) ⊕ xtime(a⊕b) ⊕ xtime(c⊕d) )) ⊕ F1(a,b,c,d)则bit8_mixcolum的输出为:
outx = { F1(a,b,c,d), F1(b,c,d,a), F1(c,d,a,b), F1(d,a,b,c) } outy = { F2(a,b,c,d), F2(b,c,d,a), F2(c,d,a,b), F2(d,a,b,c) }可以看出,整个模块实际上对输入向量的四个循环移位版本分别应用相同的非线性‑线性组合,然后将结果向量化输出。这是一种典型的**SIMD(单指令多数据)**风格的硬件设计。
4. 创新点分析
| 创新维度 | 具体描述 |
|---|---|
| 模块化分层设计 | 直接复用bit1_mixcolum作为构建块,避免了在更高层次上重新设计有限域乘法逻辑。这种“乐高式”搭建使得代码简洁、易于验证和维护。 |
| 循环输入重排技巧 | 通过简单的接线旋转(无需额外逻辑),实现了对输入字节所有循环排列的覆盖。这比硬编码四个不同的变换函数要优雅得多,且面积几乎为零。 |
| 双输出通道并行 | 同时产生outx和outy两个32位结果,满足了后续数据处理(例如AES的列混合需要两行同时计算)的需求,提升了吞吐率。 |
| 纯组合逻辑,无流水线冲突 | 整个模块没有任何寄存器,所有子模块并行计算,输出延迟等于单个bit1_mixcolum的传播延迟(约4级xtime加若干异或门)。非常适合整合到高性能流水线中。 |
| 字节序友好 | 明确采用大端字节序,与AES标准、网络字节序以及多数RISC指令集的内存视图一致,减少了集成的字节交换开销。 |
| 可扩展性强 | 以此模块为基础,可以很容易地扩展到完整的128位列混合:只需例化4个bit8_mixcolum(分别处理四列),即可一次完成整个状态矩阵的变换。 |
5. 比喻
想象你有一个4人小组,每个人都要完成两道计算题(F1和F2),但每个人拿到的题目顺序不同:
- 第1人:按顺序使用
(a,b,c,d)作为输入 - 第2人:把题目向前滚动一位,使用
(b,c,d,a) - 第3人:再滚动一位,使用
(c,d,a,b) - 第4人:再滚动一位,使用
(d,a,b,c)
四个人同时独立计算,最后把所有人算出的F1结果按编号收起来就是outx,F2结果收起来就是outy。
这个比喻说明了并行(四人同时)和循环移位(每人看到的顺序不同)两个核心思想。
6. 应用场景与扩展
- AES-128/256的列混合层:将128位状态划分为4个32位列,每个列送入一个
bit8_mixcolum,即可在单个时钟周期内完成整个MixColumns操作。 - 自定义加密算法的混淆层:如果读者需要设计轻量级分组密码,可以调整
bit1_mixcolum内部的xtime系数,然后同样用这种“旋转并行”结构实现任意4字节的线性变换。 - FPGA中作为组合逻辑加速器:由于无寄存器,可放在逻辑路径中而不增加流水线深度,适合对延迟敏感的协议处理。
- ASIC中的标准单元库模块:面积小、扇出规整,易于布局布线。
7. 总结
bit8_mixcolum是一个教科书级的模块化并行设计范例。它没有创造新的运算,而是通过巧妙的例化方式和输入重排,将基础单元的能力放大了四倍。读者可以从中学习到:
- 如何从底层模块构建上层功能
- 如何利用循环移位覆盖所有需要的组合
- 如何保持接口简洁而功能完整
理解了这个模块,也就掌握了构建更大规模密码硬件的基本思路。下一篇我们将继续向上组合,展示完整的128位列混合模块,敬请期待。
my_bit8_mixcolum.v完整代码
`timescale 1ns / 10ps module bit8_mixcolum ( input [31:0] in, output [31:0] outx, output [31:0] outy ); // ==================== 内部线网(w_ 前缀) ==================== wire [7:0] w_a, w_b, w_c, w_d; // 输入字节拆分 wire [7:0] w_x1, w_x2, w_x3, w_x4; // 子模块 outx 输出 wire [7:0] w_y1, w_y2, w_y3, w_y4; // 子模块 outy 输出 // ==================== 输入字节拆分 ==================== assign w_a = in[31:24]; assign w_b = in[23:16]; assign w_c = in[15:8]; assign w_d = in[7:0]; // ==================== 四个 bit1_mixcolum 实例化 ==================== bit1_mixcolum u1 ( .in1 (w_a), .in2 (w_b), .in3 (w_c), .in4 (w_d), .Out1 (w_x1), .Out2 (w_y1) ); bit1_mixcolum u2 ( .in1 (w_b), .in2 (w_c), .in3 (w_d), .in4 (w_a), .Out1 (w_x2), .Out2 (w_y2) ); bit1_mixcolum u3 ( .in1 (w_c), .in2 (w_d), .in3 (w_a), .in4 (w_b), .Out1 (w_x3), .Out2 (w_y3) ); bit1_mixcolum u4 ( .in1 (w_d), .in2 (w_a), .in3 (w_b), .in4 (w_c), .Out1 (w_x4), .Out2 (w_y4) ); // ==================== 输出拼接 ==================== assign outx = {w_x1, w_x2, w_x3, w_x4}; assign outy = {w_y1, w_y2, w_y3, w_y4}; endmoduletb_my_bit8_mixcolum.v完整代码
`timescale 1ns/10ps module tb_bit8_mixcolum; // ============================================================ // 信号声明(全部放在模块头部,符合 Verilog 2001 要求) // ============================================================ reg [31:0] in; wire [31:0] outx, outy; reg [31:0] expected_outx; reg [31:0] expected_outy; reg [7:0] a, b, c, d; // 输入字节拆分(辅助) integer test_id; reg all_pass; // ============================================================ // 实例化待测模块 // ============================================================ bit8_mixcolum uut ( .in (in), .outx (outx), .outy (outy) ); // ============================================================ // 参考模型函数(GF(2^8) 运算,不可约多项式 x^8+x^4+x^3+x+1) // ============================================================ // xtime(x) = x * {02} in GF(2^8) function [7:0] xtime; input [7:0] x; reg [7:0] tmp; begin tmp = {x[6:0], 1'b0}; if (x[7]) tmp = tmp ^ 8'h1b; xtime = tmp; end endfunction // F1 函数:对应 bit1_mixcolum 的 Out1 function [7:0] F1; input [7:0] a, b, c, d; reg [7:0] t1; begin t1 = a ^ b; F1 = b ^ c ^ d ^ xtime(t1); end endfunction // F2 函数:对应 bit1_mixcolum 的 Out2 function [7:0] F2; input [7:0] a, b, c, d; reg [7:0] t1, t2, t3, t4, t5, t6; begin t1 = a ^ b; t2 = a ^ c; t3 = c ^ d; t4 = xtime(t1); t5 = xtime(t3); t6 = t2 ^ t4 ^ t5; F2 = xtime(xtime(t6)) ^ F1(a, b, c, d); end endfunction // 期望 outx 的计算 function [31:0] exp_outx; input [7:0] a, b, c, d; begin exp_outx = { F1(a,b,c,d), F1(b,c,d,a), F1(c,d,a,b), F1(d,a,b,c) }; end endfunction // 期望 outy 的计算 function [31:0] exp_outy; input [7:0] a, b, c, d; begin exp_outy = { F2(a,b,c,d), F2(b,c,d,a), F2(c,d,a,b), F2(d,a,b,c) }; end endfunction // ============================================================ // 测试主流程 // ============================================================ initial begin $display("=========================================================="); $display("Starting auto-checking testbench for bit8_mixcolum"); $display("==========================================================\n"); all_pass = 1'b1; test_id = 0; // -------------------------------------------------------- // Test 1: 全零输入 // -------------------------------------------------------- test_id = test_id + 1; in = 32'h00000000; a = 8'h00; b = 8'h00; c = 8'h00; d = 8'h00; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 2: 全 0x01 // -------------------------------------------------------- test_id = test_id + 1; in = 32'h01010101; a = 8'h01; b = 8'h01; c = 8'h01; d = 8'h01; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 3: 全 0xFF // -------------------------------------------------------- test_id = test_id + 1; in = 32'hFFFFFFFF; a = 8'hFF; b = 8'hFF; c = 8'hFF; d = 8'hFF; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 4: 递增模式 0x01020304 // -------------------------------------------------------- test_id = test_id + 1; in = 32'h01020304; a = 8'h01; b = 8'h02; c = 8'h03; d = 8'h04; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 5: 随机模式 1 // -------------------------------------------------------- test_id = test_id + 1; in = 32'h12345678; a = 8'h12; b = 8'h34; c = 8'h56; d = 8'h78; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 6: 随机模式 2 (边界含 0x80) // -------------------------------------------------------- test_id = test_id + 1; in = 32'h80A5C3F0; a = 8'h80; b = 8'hA5; c = 8'hC3; d = 8'hF0; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // Test 7: 交替 0x55 / 0xAA // -------------------------------------------------------- test_id = test_id + 1; in = 32'h55AA55AA; a = 8'h55; b = 8'hAA; c = 8'h55; d = 8'hAA; expected_outx = exp_outx(a, b, c, d); expected_outy = exp_outy(a, b, c, d); #10; run_check(test_id, in, outx, outy, expected_outx, expected_outy); // -------------------------------------------------------- // 最终报告 // -------------------------------------------------------- $display("\n=========================================================="); if (all_pass) $display("RESULT: ALL TESTS PASSED"); else $display("RESULT: SOME TESTS FAILED (see above)"); $display("=========================================================="); $finish; end // ============================================================ // 比较任务(自动判断 PASS/FAIL) // ============================================================ task run_check; input integer id; input [31:0] i, ox, oy, exx, exy; reg okx, oky; begin okx = (ox === exx); oky = (oy === exy); $display("Test %0d: in = %h", id, i); $display(" outx = %h (%s) expected = %h", ox, okx ? "OK" : "FAIL", exx); $display(" outy = %h (%s) expected = %h", oy, oky ? "OK" : "FAIL", exy); if (okx && oky) begin $display(" => PASS\n"); end else begin $display(" => FAIL\n"); all_pass = 1'b0; end end endtask endmodule
AES加密解密硬件实现详解:从算法到Verilog设计
AES加密解密硬件实现详解-完整代码(1):my_sbox_tops.v
AES加密解密硬件实现详解-完整代码(2):my_subbytes.v
AES加密解密硬件实现详解-完整代码(3):my_keysecret.v
AES加密解密硬件实现详解-完整代码(4):my_mixcolum.v
AES加密解密硬件实现详解-完整代码(5):my_bit1_mixcolum.v
AES加密解密硬件实现详解-完整代码(6):my_bit8_mixcolum.v
AES加密解密硬件实现详解-完整代码(7):my_aes_top.v
(如果您对模块的数学推导或硬件实现有疑问,欢迎留言讨论!)