蒙哥马利模乘算法在FPGA中如何优化实现

一、搞要

        Montgomery大整数乘法 在fpga中实现资源占用高,特别是DSP资源,而且一个算法包含好多个Montgomery大整数乘法,fpga中的dsp资源总是很快就耗尽。本文讲述了如何改进算法,减少dsp使用量,并优化逻辑,提高实现的最终频率。

二、原理简述

        2.1、蒙哥马利(Montgomery)模乘算法      

        Montgomery乘法是一种用于加速模运算的算法,‌其数学表达式为A*B*R^{-1} mod M,‌其中A和B是与M同位长的大数,‌R是2^{m}(‌M为位长)‌,‌R^{-1}是R相对于M的模逆,‌即满足R*R^{-1} mod M =1。‌这种算法特别适用于对奇数求模的情况,‌因为它通过使用蒙哥马利表示法,‌可以在计算过程中避免直接的除法运算,‌从而提高计算效率。‌Montgomery乘法通常用于加密算法中,‌如椭圆曲线密码学,‌以加速模乘和模逆运算

        下面涉汲到的Montgomery模乘算法都是指密码学中的大整数模乘算法,所谓的大整数得指位数达几百位的大数,例如256位的大整数。

        2.2、在fpga中如何实现Montgomery模乘法算法

        公式推导及数学原理本文不再详述。我这里就直接给出算法的实现公式。因在fpga要实现除法,耗资源,速度也慢,因而要把模乘中的除法转换成模域中的乘法,如何转换不是本文的重点。现直接给出Montgomery乘法公式。下面讲述的模乘运算基于Bn128曲线的模及模逆

模P:P=256'h73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001

模逆Q:Q=256'h3d443ab0d7bf2839181b2c170004ec0653ba5bfffffe5bfdfffffffeffffffff

       Montgomery模乘算法 C=A*B mod P在FPGA中分以下4步计算(公式推导不再详述):

1、计算中间值Ma=A*B;

2、计算中间值Mb=Ma*Q;

3、计算中间值Mc=Mb*P;

4、计算结果C=(Ma+Mc)modP;

         Montgomery模乘算法完美的去除了除法,只剩下3个大整数乘法和一个大整数模加,完全可以在fpga中实现全流水线计算。

        然而要实现这3个大整数乘法,单个模乘就要要消耗200多个DSP48资源,因而想办法改进算法,还是要进一步减少DSP48资源的使用量。

三、Montgomery模乘算法在fpga中优化方法

        Montgomery模乘算法中的模加完全是用fpga中逻辑资源实现,在这里不再详细讲述优化方法。

        三个大正数乘法的在算法方面优化改进方法如下:

        1、采用karatsuba乘法减少乘法次数;

        2、采用CSD编码乘法器算法把乘法改用加法减法逻辑实现;

        3、当A=B时,可以进行单独优化,减少逻辑资源的使用;

        4、当B=常数时,也可以进一优化,

 光采用karatsuba乘法优化,要实现三个大整数乘法要消耗DSP48资源210个左右,当采用前两种方法时,即采用karatsuba乘法+CSD编码乘法,则可以降低到180个DSP48资源左右。

        注意:以上数据都是基于xilinx fpga Virtex ultraScale+ VU19P上综合结果。

四、karatsuba乘法原理

        Karatsuba乘法是一种快速的大数乘法算法,它的原理是利用分治法将两个大数相乘。

设要计算的两个大数为A和B,分别表示为A=a*10^{n/2}+bB=c*10^{n/2}+d,其中a、b、c、d是相应位数的小数。根据乘法的分配律,可以将A * B表示为:

A*B=(a*10^{n/2}+b)*(c*10^{n/2}+d)=a*c*10^{n}+(a*d+b*c)*10^{n/2}+b*d

在上式中,a * c、b * d和(a * d + b * c)是三个乘法运算,其中(a * d + b * c)是两个小数相乘,需要计算两次。为了减少乘法的次数,Karatsuba乘法采用如下的递归思想:

  1. 将A和B都分成两部分:A=a*10^{n/2}+bB=c*10^{n/2}+d
  2. 计算a * c,b * d和(a * d + b * c)
  3. 通过减少两次大数乘法的次数来计算A * B:
    • 计算a * c的乘积,记为P1
    • 计算b * d的乘积,记为P2
    • 计算(a * d + b * c)的乘积,记为P3
    • 计算A*B=P1*10^{n}+(P3-P1-P2)*10^{n/2}+P2

通过递归地应用这个过程,可以快速计算出大数的乘积。

Karatsuba乘法的关键思想是通过分解大数,减少乘法的次数。相较于传统的乘法算法,它具有更高的效率。然而,在实际应用中,Karatsuba乘法的效率在乘数长度较小时可能并不明显,因此通常在处理大数乘法时使用。

// 输入:n位长度的正整数x和y
// 输出:x*y的结果
函数 karatsuba (x, y) {
     a和b为x的前半部分和后半部分;
     c和d是y的前半部分和后半部分;
    递归调用函数karatsuba解决q=a*c, 递归karatsuba解决p=b*d,递归karatsuba解决k=(a+b)(c+d)
    返回10的n次方*q + 10的n/2次方 * (k - q - p) + p

}

 4.1、karatsuba算法实现的verilogHDL代码分为三级嵌套

       256位乘法器分为256位,128位,64位等三级嵌套。如下图

        y_mult_64:64位乘法器采用karatsuba算法实现的代码;

        y_mult_128:128位乘法器,它包含三个y_mult_64模块;

        y_mult_256:256位乘法器,它包含三个y_mult_128模块。

        所以一个256位乘法器包含了3*3=9个64位乘法器。

4.2、 64位大整数乘法采用karatsuba算法实现的verilogHDL代码

        64位大整数乘法,采用karatsuba算法在Xilinx fpga Virtex ultraScale+ VU19P 上的实现的最后一级64位乘法代码如下:

//
// Company: 杭州幻智星科技有限公司
// Engineer: 俞工
// email: yupc123@qq.com
// Create Date: 01/25/2021 03:45:32 PM
// Design Name: Karatsuba 64位乘法器
// Module Name: y_mult_64
// Revision 0.01
//
module y_mult_64#(
            parameter AB_WIDTH = 64,
            parameter DSP_STEP = 6,
            parameter RESULT_WIDTH = AB_WIDTH*2          
        )(
                input clk,
                input      [AB_WIDTH-1:0]    a,
                input      [AB_WIDTH-1:0]    b,
                output reg [RESULT_WIDTH-1:0]   result // delay 9 clk
                );
    wire [AB_WIDTH-1:0] z2;
    wire [AB_WIDTH-1:0] z0;
    wire [AB_WIDTH+1:0] z1;
    reg [RESULT_WIDTH-1:0] tmp1;
    reg [RESULT_WIDTH:0] tmp1_;
    reg [AB_WIDTH/2:0] z1_tmp3,z1_tmp4;
    reg [AB_WIDTH+1:0] z2_z0_tmp[1:0];
    generate
        if(AB_WIDTH==64)begin
            mult32 mult32_u0 (
              .CLK(clk),  // input wire CLK
              .A(a[AB_WIDTH-1:AB_WIDTH/2]),      // input wire [31 : 0] A
              .B(b[AB_WIDTH-1:AB_WIDTH/2]),      // input wire [31 : 0] B
              .P(z2)      // output wire [63 : 0] P     // delay 6 clk
            );
            mult32 mult32_u1 (
              .CLK(clk),  // input wire CLK
              .A(a[(AB_WIDTH/2) -1:0]),      // input wire [31 : 0] A
              .B(b[(AB_WIDTH/2) -1:0]),      // input wire [31 : 0] B
              .P(z0)      // output wire [63 : 0] P
            );
            mult33 mult33_u0 (
              .CLK(clk),  // input wire CLK
              .A(z1_tmp3),      // input wire [31 : 0] A
              .B(z1_tmp4),      // input wire [31 : 0] B
              .P(z1)      // output wire [63 : 0] P
            );
        end else begin
            reg [AB_WIDTH-1:0] z2_[DSP_STEP-1:0];
            reg [AB_WIDTH-1:0] z0_[DSP_STEP-1:0];
            reg [AB_WIDTH+1:0] z1_[DSP_STEP-1:0];
            always @ (posedge clk)begin 
                (*use_dsp48="yes"*) z2_[0]<=a[AB_WIDTH-1:AB_WIDTH/2]*b[AB_WIDTH-1:AB_WIDTH/2];
                (*use_dsp48="yes"*) z0_[0]<=a[(AB_WIDTH/2) -1:0]*b[(AB_WIDTH/2) -1:0];
                (*use_dsp48="yes"*) z1_[0]<=z1_tmp3*z1_tmp4;
                if(DSP_STEP>1) begin
                    z2_[DSP_STEP-1:1]<=z2_[DSP_STEP-2:0];
                    z0_[DSP_STEP-1:1]<=z0_[DSP_STEP-2:0];
                    z1_[DSP_STEP-1:1]<=z1_[DSP_STEP-2:0];
                end
            end
            assign z2=z2_[DSP_STEP-1];               // delay 6 clk
            assign z1=z1_[DSP_STEP-1];
            assign z0=z0_[DSP_STEP-1];
        end
    endgenerate
    // delay 1 clk
    always @ (posedge clk)begin 
        z1_tmp3 <= a[AB_WIDTH-1:AB_WIDTH/2] + a[(AB_WIDTH/2) -1:0];  // delay 1 clk
        z1_tmp4 <= b[AB_WIDTH-1:AB_WIDTH/2] + b[(AB_WIDTH/2) -1:0];        
    end
    // delay 2 clk
    always @ (posedge clk)begin 
        z2_z0_tmp[0] <= z2+z0;                                                  // delay 7 clk
        tmp1 <= {z2,z0};
        tmp1_<={(tmp1[RESULT_WIDTH-1 :AB_WIDTH/2]+z1),tmp1[(AB_WIDTH/2) -1:0]}; // delay 8 clk
        z2_z0_tmp[1]<=z2_z0_tmp[0];
    end
    // delay 4 clk
    always @ (posedge clk)begin
        result <= {tmp1_[RESULT_WIDTH :AB_WIDTH/2] - z2_z0_tmp[1],tmp1_[(AB_WIDTH/2) -1:0]}; // delay 9 clk
    end
endmodule

  

五、CSD编码乘法器原理

        CSD编码(Canonic Signed Digit)是一种用于表示和计算数字的编码方法,特别适用于乘法器的设计。CSD编码将每个数字表示为一系列正负1的权重值之和,其中正数表示1,负数表示-1。

乘法器的设计可以采用CSD编码来实现高效的乘法运算。具体步骤如下:

  1. 将输入的乘数和被乘数转换为CSD编码。这涉及到将二进制表示的数字转换为CSD形式。
  2. 实现部分乘法器来计算每个位上的部分乘积。这可以通过位与运算和位移运算来实现。
  3. 对于每个部分乘积,根据CSD编码的规则,将其转换为CSD形式。
  4. 将所有部分乘积相加,得到最终结果。在这一步中,可以使用加法器和位移运算来实现。

通过使用CSD编码,可以减少乘法器中的部分乘积和加法器的数量,从而提高乘法器的效率。然而,CSD编码的实现相对复杂,需要额外的逻辑电路。因此,在实际中,需要综合考虑CSD编码的优点和实现的复杂性来选择乘法器的设计方法。

5.1、CSD编码乘法器的verilogHDL代码例子

        此代码是Montgomery的乘法器的第二个乘法即:计算中间值Mb=Ma*Q;

//
// Company: 杭州幻智星科技有限公司
// Engineer: 俞工
// email: yupc123@qq.com
// Create Date: 01/25/2021 03:45:32 PM
// Design Name: CSD编码256位乘法器
// Module Name: y_CSD_mul2_256
// Revision 0.01
// Description: 计算中间值Mb=Ma*Q
//    Q=256'h3d443ab0d7bf2839181b2c170004ec0653ba5bfffffe5bfdfffffffeffffffff
//
module y_CSD_mul2_256(
                    input clk,
                    input in_val,
                    input[255:0] a,
                    output reg out_val=0,
                    output reg[255:0] result=0 //delay 4 clk
                    );
    wire[255:0] SL_254;
    wire[255:0] SL_247;
    wire[255:0] SL_245;
    wire[255:0] SL_241;
    wire[255:0] SL_237;
    wire[255:0] SL_223; //30
    wire[255:0] SL_204;
    wire[255:0] SL_202;
    wire[255:0] SL_197;
    wire[255:0] SL_191;
    wire[255:0] SL_187;
    wire[255:0] SL_186;
    wire[255:0] SL_180;
    wire[255:0] SL_172;
    wire[255:0] SL_170;
    wire[255:0] SL_169;
    wire[255:0] SL_163;
    wire[255:0] SL_162;
    wire[255:0] SL_145;
    wire[255:0] SL_143;
    wire[255:0] SL_129;
    wire[255:0] SL_128;
         
    wire[255:0] SL_125;
    wire[255:0] SL_123;
    wire[255:0] SL_121;
    wire[255:0] SL_112;
    wire[255:0] SL_110;
    wire[255:0] SL_78;

    //-1 383
    wire[255:0] SL_249;
    wire[255:0] SL_233;
    wire[255:0] SL_231;
    wire[255:0] SL_229;
    wire[255:0] SL_227;
    wire[255:0] SL_220;
    wire[255:0] SL_218;
    wire[255:0] SL_213;
    wire[255:0] SL_207;
    wire[255:0] SL_194;
    wire[255:0] SL_177;
    wire[255:0] SL_175;
    wire[255:0] SL_159;
    wire[255:0] SL_139;
    wire[255:0] SL_137;
         
    wire[255:0] SL_117;
    wire[255:0] SL_114;
    wire[255:0] SL_108;
    wire[255:0] SL_105;
    wire[255:0] SL_80;
    wire[255:0] SL_76;
    wire[255:0] SL_73;
    wire[255:0] SL_64;
    wire[255:0] SL_31;
    wire[255:0] SL_0;
         
    reg [255:0] r_tmp1,r_tmp2,r_tmp3,r_tmp4,r_tmp5,r_tmp6,r_tmp7,r_tmp8,r_tmp9;
    reg [255:0] r1_tmp1,r1_tmp2,r1_tmp3,r1_tmp4,r1_tmp5,r1_tmp6,r1_tmp7,r1_tmp8,r1_tmp9;           
    reg [2:0] shift_csdval=0;
    //1
    //assign SL_384 = a<<384;
    assign SL_254= a<<254;
    assign SL_247= a<<248;
    assign SL_245= a<<246;
    assign SL_241= a<<242;
    assign SL_237= a<<238;
    assign SL_223= a<<224;
    assign SL_204= a<<205;
    assign SL_202= a<<203;
    assign SL_197= a<<198;
    assign SL_191= a<<192;
    assign SL_187= a<<188;
    assign SL_186= a<<187;
    assign SL_180= a<<181;
    assign SL_172= a<<173;
    assign SL_170= a<<171;
    assign SL_169= a<<170;
    assign SL_163= a<<164;
    assign SL_162= a<<163;
    assign SL_145= a<<146;
    assign SL_143= a<<144;
    assign SL_129= a<<130;
    assign SL_128= a<<129;
    assign SL_125= a<<126;
    assign SL_123= a<<124;
    assign SL_121= a<<122;
    assign SL_112= a<<113;
    assign SL_110= a<<111;
    assign SL_78 = a<<79;
   
    //-1          = a<<   
    assign SL_249 = a<<250;
    assign SL_233 = a<<234;
    assign SL_231 = a<<232;
    assign SL_229 = a<<230;
    assign SL_227 = a<<228;
    assign SL_220 = a<<221;
    assign SL_218 = a<<219;
    assign SL_213 = a<<214;
    assign SL_207 = a<<208;
    assign SL_194 = a<<195;
    assign SL_177 = a<<178;
    assign SL_175 = a<<176;
    assign SL_159 = a<<160;
    assign SL_139 = a<<140;
    assign SL_137 = a<<138;

    assign SL_117 = a<<118;
    assign SL_114 = a<<115;
    assign SL_108 = a<<109;
    assign SL_105 = a<<106;
    assign SL_80  = a<<81;
    assign SL_76  = a<<77;
    assign SL_73  = a<<74;
    assign SL_64  = a<<65;
    assign SL_31  = a<<32;
    assign SL_0   = a;

    always @ (posedge clk)begin
            //add tree level-1
            //r_tmp0 <= SL_384 + SL_380 + SL_367;
            r_tmp1 <= SL_125 + SL_163 + SL_187 + SL_237 + SL_241;
            r_tmp2 <= SL_123 + SL_162 + SL_186 + SL_223 + SL_245;
            r_tmp3 <= SL_121 + SL_145 + SL_180 + SL_204 + SL_247;
            r_tmp4 <= SL_112 + SL_143 + SL_172 + SL_202 + SL_254;
            r_tmp5 <= SL_110 + SL_129 + SL_170 + SL_197;
            r_tmp6 <= SL_78  + SL_128 + SL_169 + SL_191;
            
            //r1_tmp0 <= SL_382 + SL_376 + SL_374;
            r1_tmp1 <= SL_80 + SL_139 + SL_213 + SL_233 + SL_249;
            r1_tmp2 <= SL_76 + SL_137 + SL_207 + SL_231;
            r1_tmp3 <= SL_73 + SL_117 + SL_194 + SL_229;
            r1_tmp4 <= SL_64 + SL_114 + SL_177 + SL_227;
            r1_tmp5 <= SL_31 + SL_108 + SL_175 + SL_220;
            r1_tmp6 <= SL_0  + SL_105 + SL_159 + SL_218;

            
            //add tree level-2
            r_tmp7 <= r_tmp1 + r_tmp2 + r_tmp3;
            r_tmp8 <= r_tmp4 + r_tmp5 + r_tmp6;
            
            r1_tmp7 <= r1_tmp1 + r1_tmp3 + r1_tmp5;
            r1_tmp8 <= r1_tmp2 + r1_tmp4 + r1_tmp6;
            
            //add tree level-3
            r_tmp9<=r_tmp7+r_tmp8;            
            r1_tmp9<=r1_tmp7+r1_tmp8;
            
            //add tree level-4
            result<=(r_tmp9)-(r1_tmp9);
    end
    always @ (posedge clk)begin
        {out_val,shift_csdval}<={shift_csdval,in_val};
    end
endmodule

         Montgomery的乘法器的第三步计算中间值Mc=Mb*P,也适合用CSD编码实现,但常数模P中连续的1太少,使得用CSD编码实现的话,消耗过多的逻辑资源,最终得不偿失,最终放弃。

六、 karatsuba算法同CSD编码其同实现256位乘法

        上一节所述的 Montgomery的乘法器的第三步计算中间值Mc=Mb*P不适合单独用CSD编码实现,但是可以用karatsuba算法结合CSD编码一起实现,即达到了不消耗过多的逻辑资源,也能减少DSP48的资源。

6.1、karatsuba算法的三级嵌套

 

6.2、karatsuba算法和CSD编码混合编程代码

//
// Company: 杭州幻智星科技有限公司
// Engineer: 俞工
// email: yupc123@qq.com
// Create Date: 01/25/2021 03:45:32 PM
// Design Name: Karatsuba算法同CSD编码一起实现的64位乘法器
// Module Name: y_mult_c64
// Revision 0.01
// Description: 计算中间值Mc=Mb*P
//
module y_mult_c64#(
            parameter AB_WIDTH = 96,
            parameter DSP_STEP = 6,
            parameter DSP_EN = 0,
            parameter RESULT_WIDTH = AB_WIDTH*2,
            parameter [AB_WIDTH-1:0] CONST_B = -1,
            parameter [AB_WIDTH/2:0] z1_tmp4 = CONST_B[AB_WIDTH-1:AB_WIDTH/2] + CONST_B[(AB_WIDTH/2) -1:0]           
        )(
                input clk,
                input      [AB_WIDTH-1:0]    a,
                output reg [RESULT_WIDTH-1:0]   result   //delay 4clk
                );
    
        reg [RESULT_WIDTH-1:0] result_delay[DSP_STEP-2:0];    
    generate
        
        if(AB_WIDTH==64&&CONST_B==64'b1111111111111111111111111111111100000000000000000000000000000001)begin
            reg [AB_WIDTH-1:0] tmp1,tmp2;
            reg [RESULT_WIDTH-1:0]   result_;             
            always @ (posedge clk)begin 
                tmp1<=a;
                tmp2<=tmp1;
                result_<={tmp2,tmp2}-{tmp2,32'd0};
                if(DSP_STEP==1) begin
                    result<=result_;
                end else begin
                    if(DSP_STEP>2) begin
                        result_delay[DSP_STEP-2:0]<={result_delay[DSP_STEP-3:0],result_};
                    end
                    result<=result_delay[DSP_STEP-2];
                end
            end
        end  else if(DSP_EN==0&&AB_WIDTH==64&&CONST_B==64'b0101001110111101101001000000001011111111111111100101101111111110)begin
             reg [AB_WIDTH+48:0] tmp1;
             reg [AB_WIDTH+44:0] tmp2;
             reg [AB_WIDTH+45:0] tmp3;
             reg [AB_WIDTH+20:0] tmp4;  
             reg [AB_WIDTH-1:0] tmp; 
             reg [RESULT_WIDTH-1:0]   result_;         
            always @ (posedge clk)begin 
                tmp<=a;
                tmp1<={tmp}+{tmp,12'd0}+{tmp,31'd0}+{tmp,48'd0};
                tmp2<={tmp}+{tmp,7'd0}+{tmp,37'd0}+{tmp,44'd0};
                tmp3<={tmp}+{tmp,19'd0}+{tmp,30'd0}+{tmp,45'd0};
                tmp4<={tmp}+{tmp,16'd0}+{tmp,20'd0};
                result_<=({tmp4,42'd0}+{tmp3,15'd0})-({tmp1,1'd0}+{tmp2,10'd0});           
                if(DSP_STEP==1) begin
                    result<=result_;
                end else begin
                    if(DSP_STEP>2) begin
                        result_delay[DSP_STEP-2:0]<={result_delay[DSP_STEP-3:0],result_};
                    end
                    result<=result_delay[DSP_STEP-2];
                end
            end
         end else if(DSP_EN==0&&AB_WIDTH==64&&CONST_B==64'b1010100111011110110100100000000011111111111111110010110111111111)begin
// 1010101000h0000h0h01001000000001000000000000000h010h00h00000000h
             reg [AB_WIDTH+48:0] tmp1;
             reg [AB_WIDTH+53:0] tmp2;
             reg [AB_WIDTH+61:0] tmp3;
             reg [AB_WIDTH+63:0] tmp4;  
             reg [AB_WIDTH-1:0] tmp;   
             reg [RESULT_WIDTH-1:0]   result_;       
            always @ (posedge clk)begin 
                tmp<=a;
                tmp1<={tmp}+{tmp,9'd0}+{tmp,16'd0}+{tmp,48'd0};
                tmp2<={tmp,12'd0}+{tmp,46'd0}+{tmp,53'd0};
                tmp3<={tmp,14'd0}+{tmp,41'd0}+{tmp,57'd0}+{tmp,61'd0};
                tmp4<={tmp,32'd0}+{tmp,44'd0}+{tmp,59'd0}+{tmp,63'd0};
                result_<=(tmp4+tmp3)-(tmp1+tmp2);  
                if(DSP_STEP==1) begin
                    result<=result_;
                end else begin
                    if(DSP_STEP>2) begin
                        result_delay[DSP_STEP-2:0]<={result_delay[DSP_STEP-3:0],result_};
                    end
                    result<=result_delay[DSP_STEP-2];
                end
            end
        end else  
        begin
            wire [AB_WIDTH-1:0] z2;
            wire [AB_WIDTH-1:0] z0;
            wire [AB_WIDTH+1:0] z1;
            reg [AB_WIDTH+1:0] z1_tmp;
            reg [RESULT_WIDTH-1:0] tmp1;
            reg [RESULT_WIDTH:0] tmp1_;
            reg [AB_WIDTH/2:0] z1_tmp3;
            reg [AB_WIDTH+1:0] z2_z0_tmp[1:0];
            if(AB_WIDTH==64)begin
                mult32 mult32_u0 (
                  .CLK(clk),  // input wire CLK
                  .A(a[AB_WIDTH-1:AB_WIDTH/2]),      // input wire [31 : 0] A
                  .B(CONST_B[AB_WIDTH-1:AB_WIDTH/2]),      // input wire [31 : 0] B
                  .P(z2)      // output wire [63 : 0] P
                );
                mult32 mult32_u1 (
                  .CLK(clk),  // input wire CLK
                  .A(a[(AB_WIDTH/2) -1:0]),      // input wire [31 : 0] A
                  .B(CONST_B[(AB_WIDTH/2) -1:0]),      // input wire [31 : 0] B
                  .P(z0)      // output wire [63 : 0] P
                );
                mult33 mult33_u0 (
                  .CLK(clk),  // input wire CLK
                  .A(z1_tmp3),      // input wire [31 : 0] A
                  .B(z1_tmp4),      // input wire [31 : 0] B
                  .P(z1)      // output wire [63 : 0] P
                );
            end else begin
                reg [AB_WIDTH-1:0] z2_[DSP_STEP-1:0];
                reg [AB_WIDTH-1:0] z0_[DSP_STEP-1:0];
                reg [AB_WIDTH+1:0] z1_[DSP_STEP-1:0];
                always @ (posedge clk)begin 
                    (*use_dsp48="yes"*) z2_[0]<=a[AB_WIDTH-1:AB_WIDTH/2]*CONST_B[AB_WIDTH-1:AB_WIDTH/2];
                    (*use_dsp48="yes"*) z0_[0]<=a[(AB_WIDTH/2) -1:0]*CONST_B[(AB_WIDTH/2) -1:0];
                    (*use_dsp48="yes"*) z1_[0]<=z1_tmp3*z1_tmp4;
                    if(DSP_STEP>1) begin
                        z2_[DSP_STEP-1:1]<=z2_[DSP_STEP-2:0];
                        z0_[DSP_STEP-1:1]<=z0_[DSP_STEP-2:0];
                        z1_[DSP_STEP-1:1]<=z1_[DSP_STEP-2:0];
                    end
                end
                assign z2=z2_[DSP_STEP-1];               // delay 6 clk
                assign z1=z1_[DSP_STEP-1];
                assign z0=z0_[DSP_STEP-1];
            end
             always @ (posedge clk)begin 
                z1_tmp3 <= a[AB_WIDTH-1:AB_WIDTH/2] + a[(AB_WIDTH/2) -1:0];        
            end
            // delay 2 clk
            always @ (posedge clk)begin 
                z2_z0_tmp[0] <= z2+z0;
                tmp1 <= {z2,z0};
                tmp1_<={(tmp1[RESULT_WIDTH-1 :AB_WIDTH/2]+z1),tmp1[(AB_WIDTH/2) -1:0]}; 
                z2_z0_tmp[1]<=z2_z0_tmp[0];// delay 3 clk
            end
            // delay 3 clk
            always @ (posedge clk)begin
                result <= {tmp1_[RESULT_WIDTH :AB_WIDTH/2] - z2_z0_tmp[1],tmp1_[(AB_WIDTH/2) -1:0]}; // delay 4 clk
            end 
        end
    endgenerate
endmodule

七、Montgomery模乘算法的设计与实现

        下图为Montgomery模乘算法层级结构,y_mul_mont2、y_CSD_mul2_256、y_mult_c256是三个256位的大整数乘法器,用上面所述的三种方法实现。

        

7.1、Montgomery模乘算法顶层代码

        全流水算法设计实现,流水级数为40级。

//
// Company: 杭州幻智星科技有限公司
// Engineer: 俞工
// email: yupc123@qq.com
// Create Date: 01/25/2021 03:45:32 PM
// Design Name: Montgomery算法实现的256位乘法器top层代码
// Module Name: y_mul_mont
// Revision 0.01
//
module y_mul_mont#(
            parameter  P  =256'h73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001,
            //parameter  P  =  256'd21888242871839275222246405745257275088696311157297823662689037894645226208583,
            parameter INV = bn128_pkg::MONT_FACTOR,//256'd2613481120475770576337551280382610583496597584173309950404576975523838984063,
            parameter CTL_WIDTH = 1,
            parameter DSP_EN=0,
            parameter DSP_STEP = 6,
            parameter DAT_BITS = 256
        )
        (
                    input                   clk,
                    input                   in_val,
                    input     wire [CTL_WIDTH-1:0]  in_ctl,
                    input     [DAT_BITS-1:0]       a,
                    input     [DAT_BITS-1:0]       b,
                    output    reg           out_val=0,
                    output    wire [CTL_WIDTH-1:0]  out_ctl,
                    output    reg [DAT_BITS-1:0]   result=0    //delay DSP_STEP*2+28 clk
                    );
    wire    [DAT_BITS*2-1:0]    m;
    reg     [DAT_BITS-1:0]       r_a=0;
    reg     [DAT_BITS-1:0]       r_b=0;
    reg     in_valid=0;
    wire m_val;
    logic     [CTL_WIDTH-1:0]     shift_ctl[DSP_STEP*2+28:0];
    assign out_ctl=shift_ctl[DSP_STEP*2+28];
   always @ (posedge clk)begin
        r_a<=a;
        r_b<=b;       
   end
   initial begin
        for(int i=0;i<(DSP_STEP*2+29);i=i+1)
                shift_ctl[i]=0;
    end
   always @ (posedge clk)begin
         in_valid<=in_val;
         shift_ctl<={shift_ctl[DSP_STEP*2+27:0],in_ctl};
   end  
   y_mult_256 #(
                    .DSP_STEP(DSP_STEP),
                    .AB_WIDTH(DAT_BITS)
                ) U0_m(
                    .clk(clk),
                    .in_val(in_valid),
                    .a(r_a),
                    .b(r_b),
                    .out_val(m_val),
                    .result(m)      //delay 16 clk
                 );
   wire [DAT_BITS-1:0] m_f;
   wire m_f_val;
    y_CSD_mul2_256 U1_m_f(
                   .clk(clk),
                    .in_val(m_val),
                    .a(m[DAT_BITS-1:0]),
                    .out_val(m_f_val),
                    .result(m_f)         //delay 20 clk
                    );
   wire[DAT_BITS*2-1:0]  m_f_P;
   wire m_f_p_val;
    y_mult_c256 #(  .DSP_EN(DSP_EN),
                    .DSP_STEP(DSP_STEP),
                    .AB_WIDTH(DAT_BITS),
                    .CONST_B(P)
                )U2_m_f_P(
                        .clk(clk),
                        .in_val(m_f_val),
                        .a(m_f[DAT_BITS-1:0]),
                        .out_val(m_f_p_val),
                        .result(m_f_P)   //delay 36 clk
                      ); 
    wire[DAT_BITS*2-1:0] result_tmp1;

    fifo_shift512 fifo_shift_u (
      .clk(clk),      // input wire clk
      .din(m),      // input wire [767 : 0] din
      .wr_en(m_val),  // input wire wr_en
      .rd_en(m_f_p_val),  // input wire rd_en
      .dout(result_tmp1),    // output wire [767 : 0] dout
      .full(),    // output wire full
      .empty()  // output wire empty
    ); 
    
    //assign w_result_tmp=(m_f_P+result_tmp1);  //513=512+512
    reg [256:0]add_result_low=0;
    reg [256:0]add_result_high=0;
    always@(posedge clk)begin
        add_result_low<=m_f_P[255:0]+result_tmp1[255:0];          //delay 37 clk
        add_result_high<=m_f_P[511:256]+result_tmp1[511:256];
    end
    reg [256:0]add_result_high_full=0;
    always@(posedge clk)add_result_high_full<=add_result_high+add_result_low[256];  //delay 38 clk  

    reg[DAT_BITS:0] result_tmp=0;
    always @ (posedge clk) result_tmp<=add_result_high_full;  //delay 39 clk
    always @ (posedge clk)begin
        if(result_tmp>=P)begin
            result <= result_tmp - P;        //delay 40 clk
        end
        else begin
            result <= result_tmp;
        end
    end
    reg [2:0] valid_out_reg=0;
    always@(posedge clk) begin
            {out_val,valid_out_reg} <= {valid_out_reg,m_f_p_val};
    end
endmodule

八、在区块链filecoin P2阶段中fpga加速中的应用

        上述的Montgomery模乘算法算法用在filecon矿机的 P2阶段,用Xilinx Alveo U200加速卡实现。本项目难点在于如何简化降低单个256位模乘算子的DSP资源使用量,如何将算法逻辑布局约束在不同区域,优化矩阵运算并减少模乘算子的使用量等,最终经过6个月的不断努力,把单个模乘算子的DSP使用量减少到180个,模乘算子使用量减少到25个。在Alveo U200上主要资源使用情况如下。

1、逻辑资源使用率:70%;

2、DSP使用率:91%;

3、运行频率:324M;

        工程布线综合结果如下图:

九、bln128模乘源代码链接 

        下载地址:https://download.csdn.net/download/weixin_46897017/89584817

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值