前文讲解了SM3加密算法的计算过程,本文使用FPGA来实现加密算法,注意本文是参考奇哥的视频写的,因此与他的代码基本一致。 奇哥在B站有相关视频讲解,可以自行查看,下一篇文章会对这个工程进行优化。学习就是这样吧,先模仿别人理清思路,在用自己的方法去实现,才能掌握。 当然在此之前,我也使用FPGA实现过DES和AES加解密算法,后续也会在加密算法这个模块更新。 该工程主要包含3个模块,sm3_encrypt是算法的整体实现模块,data_extend用于实现消息扩展,iterator用于完成一次迭代运算。为了与前文的加密流程对应,本文以sm3_encrypt模块为主体,穿插其余两个模块进行讲解。 0 1 模块代码分析
首先是sm3_encrypt的端口信号,可以通过参数设置每个时钟完成迭代运算的轮数,在后续工程的时序约束发现,zynq7030ffg676-2每个时钟最多只支持完成两轮迭代运算。因此需要32+个时钟才能计算得到结果。
*
*
*
*
*
*
*
*
*
*
*
*
*
module sm3_encrypt #( parameter PARALLEL_NUM = 4 //并行线程:1,2,4,8,16,32,64)( input clk ,//系统时钟信号; input rst_n ,//系统复位信号,低电平有效;
input [255 : 0] din ,//输入待加密数据; input din_vld ,//输入待加密数据有效指示信号,高电平有效; output reg [255 : 0] dout ,//输出加密结果数据信号; output reg dout_vld //输出加密结果数据有效指示信号,高电平有效;); localparam P_ITERATOR_CYCLE = 64/PARALLEL_NUM ;//需要计算多少轮。
PART
1.1 消息填充 由于输入数据固定为256比特,因此需要加191个0,对应的填充代码如下所示,填充后数据为512比特。
*
*
*
*
*
*
*
*
*
*
always@(posedge clk)begin if(~rst_n)begin padding_data <= 'd0; padding_valid <= 'd0; end else begin padding_data <= {din[255:0],1'b1,191'd0,64'd256};//进行填充,在数据后面加1'b1,然后将数据补足448位,最后64位存储数据长度; padding_valid <= din_vld; end end
PART
1.2 消息扩展
将消息填充后的数据作为消息扩展模块的输入数据,该模块数据即为消息扩展的结果。消息扩展后为132个字(1个字为32比特),因此消息扩展模块输出数据为4224比特。消息扩展模块的端口信号如下所示: * * * * * * * * *
module data_extend ( input clk ,//系统时钟信号; input rst_n ,//系统复位信号,低电平有效;
input [511 : 0] padding_data ,//需要扩展的512位数据; input padding_valid ,//输入数据有效指示信号,高电平有效; output [4223 : 0] extend_data ,//扩展后的4224位数据; output reg extend_valid //输出数据有效指示信号,高电平有效;);
下面是置换函数P1的实现方式,X与X循环左移15位和循环左移23位的结果异或。 * * * * * * *
//生成置换函数P1 function [31 : 0] P1; input [31 : 0] X; begin//X异或X循环左移15位,在异或X循环左移23位; P1 = X ^ {X[16:0],X[31:17]} ^ {X[8:0],X[31:9]}; end endfunction
由于加密过程的数据都是以大端的方式存储,因此在进行后续运算之前,需要先把小端的输入数据转换为大端数据,对应代码如下。 * * * * * * * * * * * * * * *
//使用移位寄存器暂存输入数据; always@(posedge clk)begin padding_valid_r <= padding_valid; extend_valid <= padding_valid_r; end
genvar g_i; generate for(g_i = 0 ; g_i < 16 ; g_i = g_i + 1)begin : Encode always@(posedge clk)begin if(padding_valid)//将输入的小端数据转换为大端数据; padding_data_big[g_i*32+31 : g_i*32] = padding_data[511-g_i*32 : 480-g_i*32]; end end endgenerate
先把输入的16个字节数据存储在扩展数据存储体(前文的w~(0~15)~)的低16个地址中,对应代码如下所示。 * * * * * * * * * * *
genvar g_exi; generate for(g_exi = 0 ; g_exi < 16 ; g_exi = g_exi + 1)begin : Extend_Word1 always@(*)begin if(~rst_n) extend_data_m[g_exi] <= 'd0; else //将输入的低16个字存入扩展结果中; extend_data_m[g_exi] <= padding_data_big[(g_exi * 32) + 31 : g_exi * 32]; end end endgenerate
然后再通过下面的公式计算w~(16~67)~的数据,然后存入存储体的16~67地址中。
图1 w(16~67)计算公式 对应代码如下所示,首先计算P1函数的参数w_P1_X,然后得到P1函数的计算结果w_P1,同时计算W(i-13)循环左移七位的结果w_word 2_mod1,最后上述结果通过异或得到W(i)的运算结果,存入存储体对应位置。 * * * * * * * * * * * * * *
genvar g_exj; generate//计算第16个字到第68个字中间的数据; for(g_exj = 16 ; g_exj < 68 ; g_exj = g_exj + 1)begin : Extend_Word2 assign w_P1_X[g_exj] = extend_data_m[g_exj-16] ^ extend_data_m[g_exj-9] ^ {extend_data_m[g_exj-3][16:0],extend_data_m[g_exj-3][31:17]}; assign w_P1[g_exj] = P1(w_P1_X[g_exj]);//计算P1(w(i-16) ^ w(i-9) ^ (w(i-3) <<< 15)) assign w_word2_mid1[g_exj] = {extend_data_m[g_exj - 13][24:0],extend_data_m[g_exj - 13][31:25]}; always@(*)begin if(~rst_n) extend_data_m[g_exj] <= 'd0; else //计算公式wi=P1(w(i-16) ^ w(i-9) ^ (w(i-3) <<< 15)) ^ (w(i-13) <<< 7) ^ w(i-6) extend_data_m[g_exj] <= w_P1[g_exj] ^ w_word2_mid1[g_exj] ^ extend_data_m[g_exj - 6]; end end endgenerate
还需要计算W'~(0~63)~的值,该值的计算公式如下所示,比较简单,对应的代码如下,计算的结果存储的起始地址为68。
图2 W'~(0~63)~计算公式 * * * * * * * * * * *
genvar g_exk; generate//计算第68个字到132个字的扩数据; for(g_exk = 0 ; g_exk < 64 ; g_exk = g_exk + 1)begin : Extend_Word3 always@(*)begin if(~rst_n) extend_data_m[g_exk + 68] <= 'd0; else//计算公式wi'=wi ^ w(i+4) extend_data_m[g_exk + 68] <= extend_data_m[g_exk] ^ extend_data_m[g_exk + 4]; end end endgenerate
最终存储体extend_data_m的前68个地址存储w~(0~67)~的数据,后64个地址存储W'~(0~63)~的数据。 然后将上述存储体的数据通过触发器暂存一个时钟,防止组合逻辑过长,导致时序违例。 * * * * * * * * * * *
genvar g_ddi; generate//将上述扩展的计算结果暂存一个时钟周期; for(g_ddi = 0 ; g_ddi < 132 ; g_ddi = g_ddi + 1)begin : reg_1d always@(posedge clk)begin if(~rst_n) extend_data_r[g_ddi] <= 'd0; else if(padding_valid_r) extend_data_r[g_ddi] <= extend_data_m[g_ddi]; end end endgenerate
最后将上述存储体中的数据拼接后数据,表示扩展后的132个数据,用于后续的迭代运算。 * * * * * *
genvar g_outi; generate//将扩展后的数据拼接后输出; for(g_outi = 0 ; g_outi < 132 ; g_outi = g_outi + 1)begin : Word_Out assign extend_data[(g_outi*32) + 31 : g_outi*32] = extend_data_r[g_outi]; end endgenerate
然后回到sm3_encrypt模块,得到消息扩展的数据后,将数据打一拍,减小组合逻辑延时。 * * * * * * * * * * * * * * * * * * * *
data_extend u_data_extend( .clk (clk ), .rst_n (rst_n ), .padding_data (padding_data ), .padding_valid (padding_valid ), .extend_data (extend_data ), .extend_valid (extend_valid ) );
//将扩展后的数据暂存; always@(posedge clk)begin if(~rst_n)begin extend_valid_r <= 'd0; extend_data_r <= 'd0; end else begin extend_valid_r <= extend_valid; extend_data_r <= extend_data; end end
PART
1.3 迭代运算 当消息扩展后,开始进行迭代运算,cmp_valid信号用来表示迭代的时钟个数,辅助生成加密数据有效指示信号,最低位等于消息扩展输出数据有效指示信号。
*
*
*
*
*
*
always@(posedge clk)begin if(~rst_n) cmp_valid[0] <= 'd0; else //当消息扩展完成后开始迭代运算; cmp_valid[0] <= extend_valid_r; end
而iterator_V[0]用来存储加密数据的初始数据,对应代码如下所示。 * * * * *
//迭代的初始值; always@(posedge clk)begin if(~rst_n) iterator_V[0] <= 256'h7380166f4914b2b9172442d7da8a0600_a96f30bc163138aae38dee4db0fb0e4e; end
下面通过两个for循环实现64轮迭代运算,第一重for循环用来指示多少个时钟能够完成64轮迭代运算,每经过一个时钟,cmp_valid左移一位。将上个时钟最后一次迭代运算的结果作为下个时钟第一次迭代运算的初始值。 第二重for循环用来表示每个时钟需要完成的迭代次数,同一时钟内可能会进行多次迭代运算(取决于参数PARALLEL_NUM的值),上一次的迭代运算结果作为下次迭代运算的初始值。 需要将当前时钟最后一次迭代运算结果使用触发器暂存,作为下个时钟第一次迭代运算的初始值。 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
genvar g_iteri; genvar g_iterj; generate//第一FOR用来确定需要多少个时钟完成64次迭代运算; for(g_iteri = 0 ; g_iteri < P_ITERATOR_CYCLE ; g_iteri = g_iteri + 1)begin : iterator_cycle //将上个时钟的迭代运算结果V(n+1)作为本次迭代运算的初始值v(n); assign iterator_in_V[g_iteri*PARALLEL_NUM] = iterator_V[g_iteri*PARALLEL_NUM]; if(g_iteri != 0)begin always@(posedge clk)begin if(~rst_n) cmp_valid[g_iteri] <= 'd0; else cmp_valid[g_iteri] <= cmp_valid[g_iteri - 1]; end end //FOR循环内包含每个时钟需要完成的迭代次数; for(g_iterj = 0 ; g_iterj < PARALLEL_NUM ; g_iterj = g_iterj + 1)begin : iterator_parallel iterator u_iterator( .i_V ( iterator_in_V[g_iterj + g_iteri * PARALLEL_NUM] ),//迭代运算的v(n) .i_B ( extend_data_r ),//消息扩展数据; .i_j ( g_iterj + g_iteri * PARALLEL_NUM ),//迭代运算的轮数-1 .o_V1 ( iteratot_out_V[g_iterj + g_iteri * PARALLEL_NUM] ) //每轮迭代运算的计算结果V(n) ); if(g_iterj < PARALLEL_NUM - 1) //将上轮迭代运算的结果作为本轮迭代运算的初始值,注意这个语句只有在一个时钟内多次运算的迭代过程才有效; assign iterator_in_V[g_iterj + 1 + g_iteri * PARALLEL_NUM] = iteratot_out_V[g_iterj + g_iteri * PARALLEL_NUM]; always@(posedge clk)begin //将一个时钟的最后一轮迭代运算结果暂存,作为下个时钟第一次迭代运算的初始值。 iterator_V[g_iterj + 1 + g_iteri * PARALLEL_NUM] <= iteratot_out_V[g_iterj + g_iteri * PARALLEL_NUM]; end end end endgenerate
迭代运算的具体过程由iterator模块完成,端口信号如下所示。 * * * * * *
module iterator ( input [255 : 0] i_V ,//迭代运算的v(n); input [4223 : 0] i_B ,//消息扩展数据;; input [31 : 0] i_j ,//迭代运算的轮数-1; output [255 : 0] o_V1 //每轮迭代运算的计算结果V(n););
首先使用函数实现几个迭代运算会用到的公式,如下所示,包含置换函数P0、布尔函数FFj(X,Y, Z)、GGj(X,Y,Z),常量Tj,这些函数的功能最终会通过组合逻辑实现。 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//计算置换函数P0 function [31 : 0] P0; input [31 : 0] X; begin//计算公式:P0=X ^ (X <<< 9) ^ (X <<< 17) P0 = X ^ {X[22:0],X[31:23]} ^ {X[14:0],X[31:15]}; end endfunction
//计算布尔函数FFj(x,y,z); function [31 : 0] FFj; input [31 : 0] X; input [31 : 0] Y; input [31 : 0] Z; input [31 : 0] j; begin FFj = (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | (X & Z) | (Y & Z)); end endfunction
//计算布尔函数GGj(x,y,z); function [31:0] GGj; input [31 :0] X; input [31 :0] Y; input [31 :0] Z; input [31 :0] j; begin GGj = (j < 16) ? (X ^ Y ^ Z) : ((X & Y) | (~X & Z)); end endfunction
//常量Tj; function [31:0] Tj; input [31:0] j; begin Tj = (j < 16) ? 32'h79cc4519 : 32'h7a879d8a; end endfunction
然后需要将输入的消息扩展数据分离、存储,便于后续计算使用,其中存储体w_Wi用来存储W~(0~67)~的数据,存储体w_Wj_用来存储W'~(0~63)~的数据,对应代码如下所示。 * * * * * * * * * * * * * *
genvar g_Wi; generate//将低68个字暂存; for(g_Wi = 0 ; g_Wi < 68 ; g_Wi = g_Wi + 1)begin : Gen_Wj assign w_Wj[g_Wi] = i_B[g_Wi*32+31 : g_Wi*32]; end endgenerate
genvar g_Wj; generate //将高64个字暂存,并且计算(Ti<<<(jmod32)) for(g_Wj = 0 ; g_Wj < 64 ; g_Wj = g_Wj + 1)begin : Gen_Wj_ assign w_Wj_[g_Wj] = i_B[2176 + g_Wj *32 + 31:2176 + g_Wj *32];//将高64个字暂存; assign w_Tj_y[g_Wj] = ((g_Wj != 0) && (g_Wj != 32)) ? ({w_Tj[31 - g_Wj[4:0] : 0],w_Tj[31:32 - g_Wj[4:0]]}) : w_Tj; end endgenerate
注意下面这部分代码用来计算Ti <<< ( j mod 32),其中(j mod 32)表示j除以32的余数。由于32比较特殊,取余运算的结果为j[4:0],即Ti <<< (j mod 32)表示Ti的计算结果循环左移j[4:0]位。(奇哥的代码这部分没这么写,这部分是自己简化的)。 *
assign w_Tj_y[g_Wj] = (g_Wj[4:0]) ? ({w_Tj[31 - g_Wj[4:0] : 0],w_Tj[31:32 - g_Wj[4:0]]}) : w_Tj;
下面位迭代运算做准备,首先将输入的256比特V(n)赋值给A、B、C、D、E、F、G、H八个32比特信号。然后调用常函数Tj,之后计算出(A <<< 12) + E + (Ti <<< (j mod 32))。 * * * *
//将输入数据存储为W_A~W_H; assign {w_A[31:0],w_B[31:0],w_C[31:0],w_D[31:0],w_E[31:0],w_F[31:0],w_G[31:0],w_H[31:0]} = i_V[255:0]; assign w_Tj = Tj(i_j); assign w_ss1_mid0 = {w_A[19:0],w_A[31:20]} + w_E + w_Tj_y[i_j];//计算(A<<<12)+E+(Ti<<<(jmod32));
然后正式进入迭代运算的代码,首先计算SS1、SS2、TT1、TT2,这几部分都是异或运算、循环左移、调用布尔函数相关的东西,对照代码查看即可。 * * * *
assign w_ss1 = {w_ss1_mid0[24:0],w_ss1_mid0[31:25]};//计算SS1=((A<<<12)+E+(Ti<<<(jmod32)))<<<7; assign w_ss2 = w_ss1 ^ {w_A[19:0],w_A[31:20]};//计算SS2=SS1^(A<<<12); assign w_tt1 = FFj(w_A,w_B,w_C,i_j) + w_D + w_ss2 + w_Wj_[i_j];//计算TT1=FFi(A,B,C)+D+SS2+W'; assign w_tt2 = GGj(w_E,w_F,w_G,i_j) + w_H + w_ss1 + w_Wj[i_j];//计算TT2=GGi(E,F,G)+H+SS1+Wi;
然后对A、B、C、D、E、F、G、H这8个数据赋值,最后将这8个数据拼接得到本轮迭代结果。 * * * * * * * * * *
assign w_D_ = w_C;//计算D=C; assign w_C_ = {w_B[22:0],w_B[31:23]};//C=B<<<9; assign w_B_ = w_A;//B=A; assign w_A_ = w_tt1;//A=TT1; assign w_H_ = w_G;//H=G; assign w_G_ = {w_F[12:0],w_F[31:13]};// G=F<<<19; assign w_F_ = w_E;//F=E; assign w_E_ = P0(w_tt2);//E=P0(TT2);
assign o_V1 = {w_A_,w_B_,w_C_,w_D_,w_E_,w_F_,w_G_,w_H_};//
经过以上分析,整个迭代过程的代码其实比较简单,难一点的是模块外部初始数据V(n)的控制。PART
1.4 输出结果
经过64轮迭代后,将最后一轮的迭代结果和初始迭代内容异或,得到最终的加密数据,对应代码如下。 * * * * * * * *
always@(posedge clk)begin if(~rst_n)begin dout <= 'd0; end else begin//将迭代64轮的数据和迭代运算的初始值相与得到加密结果; dout <= iteratot_out_V[63] ^ iterator_V[0]; end end
然后根据每个时钟加密的轮数,输出对应的加密数据有效指示信号。 * * * * * * * * * * * * * *
always@(posedge clk)begin if(~rst_n)begin dout_vld <= 'd0; end else case(P_ITERATOR_CYCLE) 1 : dout_vld <= extend_valid_r; 2 : dout_vld <= cmp_valid[0]; 4 : dout_vld <= cmp_valid[2]; 8 : dout_vld <= cmp_valid[6]; 16 : dout_vld <= cmp_valid[14]; 32 : dout_vld <= cmp_valid[30]; 64 : dout_vld <= cmp_valid[62]; endcase end
02 模块仿真
上述设计了加密模块,以下是顶层模块,每隔64个时钟周期生成一个测试数据,对应的顶层模块信号如下所示。
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
//--###############################################################################################//--#//--# File Name : top//--# Designer : 数字站//--# Tool : Vivado 2021.1//--# Design Date : 2024.6.2//--# Description : sm3加密顶层模块//--# Version : 0.0//--# Coding scheme : GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)//--#//--###############################################################################################module top ( input clk ,//系统时钟信号,默认100MHz; input rst_n ,//系统复位信号,低电平有效; output reg led ); (* MARK_DEBUG = "TRUE" *)reg [255 : 0] Original_Data ; (* MARK_DEBUG = "TRUE" *)reg Original_Valid ; reg [5 : 0] cnt ;
(* MARK_DEBUG = "TRUE" *)wire [255 : 0] Encrypt_Data ; (* MARK_DEBUG = "TRUE" *)wire Encrypt_Valid ; wire clk_50m ;
//例化锁相环模块; clk_wiz_0 u_clk_wiz_0( .clk_out1 ( clk_50m ),//output clk_out1; .resetn ( rst_n ),//input resetn; .clk_in1 ( clk ) //input clk_in1; );
sm3_encrypt #( .PARALLEL_NUM ( 2 )//并行线程:1,2,4,8,16,32,64 ) u_sm3_encrypt ( .clk ( clk_50m ),//系统时钟信号; .rst_n ( rst_n ),//系统复位信号,低电平有效; .din ( Original_Data ),//输入待加密数据; .din_vld ( Original_Valid),//输入待加密数据有效指示信号,高电平有效; .dout ( Encrypt_Data ),//输出加密结果数据信号; .dout_vld ( Encrypt_Valid ) //输出加密结果数据有效指示信号,高电平有效; );
//循环产生测试数据的时钟信号; always@(posedge clk_50m)begin if(~rst_n) cnt <= 'd0; else cnt <= cnt + 'd1; end
//每间隔128个时钟生成一个测试数据; always@(posedge clk_50m)begin if(~rst_n)begin Original_Data <= 256'h0001020304050607_08090a0b0c0d0e0f_0001020304050607_08090a0b0c0d0e0f; Original_Valid <= 'd0; end else if(&cnt)begin Original_Data <= Original_Data + 3; Original_Valid <= 'd1; end else begin Original_Valid <= 'd0; end end
//防止Encrypt_Data被优化掉; always@(posedge clk_50m)begin if(~rst_n)begin//初始值为0; led <= 'd0; end else if(Encrypt_Valid)begin led <= (Encrypt_Data > 10000); end end
endmodule
顶层模块对应的TestBench如下所示: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//--###############################################################################################//--#//--# File Name : test//--# Designer : 数字站//--# Tool : Vivado 2021.1//--# Design Date : 2024/6/2//--# Description : 顶层测试模块//--# Version : 0.0//--# Coding scheme : GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)//--#//--###############################################################################################`timescale 1ns / 1psmodule test (); localparam CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns; localparam RST_TIME = 10 ;//系统复位持续时间,默认10个系统时钟周期; localparam STOP_TIME = 1000 ;//仿真运行时间,复位完成后运行1000个系统时钟后停止;
reg clk ;//系统时钟,默认100MHz; reg rst_n ;//系统复位,默认低电平有效;
wire led ;
//例化待测试模块 top u_top( .clk ( clk ),//系统时钟信号,默认100MHz; .rst_n ( rst_n ),//系统复位信号,低电平有效; .led ( led ) );
//生成周期为CYCLE数值的系统时钟; initial begin clk = 0; forever #(CYCLE/2) clk = ~clk; end
//生成复位信号; initial begin rst_n = 1; #2; rst_n = 0;//开始时复位10个时钟; repeat(RST_TIME) @(posedge clk); rst_n = 1; repeat(STOP_TIME) @(posedge clk); $stop;//停止仿真; end
endmodule
仿真结果如下,输入256'h0001020304050607 _08090a0b0c0d0e0f_0001020304050607_08090a0b0c0d0e12,经过加密后得到的数据为256'h3C6F866ABC77AF25_F3EE32C4864BD AE5_06F4A946197D774D_45ACD127797360A6。
图3 仿真结果 如何验证上述计算结果是否正确?可以通过下面这个网址(https://lzltool.cn/SM3)进行验证,如下所示。加密的结果与上述运算保持一致,证明上述设计正常。
图4 软件加密验证 下面是输入的第二个有效数据进行加密,加密结果为256'h5FBF2FB7401F95C7_6EF055A1845 B9C80_ECA7BEB9F5E4845F_F7D70A688899DDAF。
图5 加密仿真 上述数据使用加密工具的计算结果如下,与上述仿真结果一致。
图6 软件加密结果 03 上板测试
综合工程,然后添加时序约束、分配管脚、添加ILA信号,最后工程布局布线,生成bit流。Zynq7030ffg676-2在50MHz的时钟频率下,将并行线程数量设置为2的时序约束结果如下所示。
图7 时序约束结果 将程序下载到开发板,使用ILA抓取待加密数据和加密后的数据信号,如下图所示。待加密的数据为256'h0001020304050607_08090a0b0c0d 0e0f_0001020304050607_08090a0b0c86468d,加密后的结果为256'h76CFC9E080089DE 3_3B2DF20A1484BF16_0B8D5E4221D356EE_3EF8F428F4E168FE。
图8 ILA抓取加密时序(一) 使用网页对上述加密进行验证,结果与上述一致,表示加密正确。
图9 加密工具加密验证 然后随机抓取一次加密结果,如下所示。待加密数据为256'h0001020304050607_08090a0b0 c0d0e0f_0001020304050607_08090a0b18f8a1a4,加密后的结果为256'h3C9E577C13175 098_F62AD0E84C53D5F6_CB8ED75069B1D972_88A2E5C938C9FBF9。
图10 ILA抓取加密时序(二) 使用软件工具的加密结果如下所示,与FPGA加密结果一致,证明上述设计正确。
图11 加密工具加密验证 经过上述验证后,该设计的功能没有什么问题,当并行度设置为2时,每2个周期可以输入一个加密数据,效率其实是比较高的。
但是时钟频率也只能跑50MHz,因此其余更高的并行度其实是没必要的。后文将并行度设置固定为1,通过更改设计流程,提升最大时钟频率。