Barret modulus block
This commit is contained in:
parent
5555a71810
commit
c558b391dd
|
@ -147,6 +147,28 @@ interface if_axi_stream # (
|
|||
|
||||
endinterface
|
||||
|
||||
interface if_axi_mm # (
|
||||
parameter D_BITS = 64,
|
||||
parameter A_BITS = 8
|
||||
)(
|
||||
input i_clk
|
||||
);
|
||||
|
||||
logic [A_BITS-1:0] raddr;
|
||||
logic [A_BITS-1:0] waddr;
|
||||
logic [D_BITS-1:0] rdat;
|
||||
logic [D_BITS-1:0] wdat;
|
||||
logic rval;
|
||||
logic wval;
|
||||
logic rrdy;
|
||||
logic wrdy;
|
||||
|
||||
modport sink (input raddr, waddr, wdat, wval, rrdy, i_clk, output rdat, rval, wrdy);
|
||||
modport source (input rdat, rval, wrdy , i_clk, output raddr, waddr, wdat, wval, rrdy);
|
||||
|
||||
|
||||
endinterface
|
||||
|
||||
interface if_ram # (
|
||||
parameter RAM_WIDTH = 32,
|
||||
parameter RAM_DEPTH = 128
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
Calculates a mod n, using barret reduction.
|
||||
|
||||
We provide an external interface to be hooked up to a multiplier.
|
||||
|
||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module barret_mod #(
|
||||
parameter OUT_BITS = 256,
|
||||
parameter IN_BITS = 512,
|
||||
parameter [OUT_BITS-1:0] P = 256'hFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141,
|
||||
parameter K = $clog2(P) + 1
|
||||
)(
|
||||
input i_clk,
|
||||
input i_rst,
|
||||
input [IN_BITS-1:0] i_dat,
|
||||
input i_val,
|
||||
output logic o_rdy,
|
||||
output logic [OUT_BITS-1:0] o_dat,
|
||||
output logic o_val,
|
||||
input i_rdy,
|
||||
|
||||
// Multiplier interface
|
||||
if_axi_stream.source o_mult,
|
||||
if_axi_stream.sink i_mult_res
|
||||
);
|
||||
|
||||
|
||||
localparam MAX_IN_BITS = 2*K;
|
||||
localparam [MAX_IN_BITS:0] U = (1 << (2*K)) / P;
|
||||
localparam [MAX_IN_BITS-1:0] P_ = P;
|
||||
logic [MAX_IN_BITS-1:0] c1, c2, c3, c4, c2_;
|
||||
|
||||
|
||||
typedef enum {IDLE, S0, S1, S2, FINISHED, WAIT_MULT} state_t;
|
||||
state_t state, prev_state;
|
||||
|
||||
always_ff @ (posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
o_rdy <= 0;
|
||||
o_dat <= 0;
|
||||
o_val <= 0;
|
||||
state <= IDLE;
|
||||
prev_state <= IDLE;
|
||||
c1 <= 0;
|
||||
c2 <= 0;
|
||||
c3 <= 0;
|
||||
c4 <= 0;
|
||||
o_mult.reset_source();
|
||||
i_mult_res.rdy <= 1;
|
||||
end else begin
|
||||
i_mult_res.rdy <= 1;
|
||||
case (state)
|
||||
{IDLE}: begin
|
||||
o_rdy <= 1;
|
||||
o_val <= 0;
|
||||
c2 <= (i_dat >> (K-1))*U; // Using multiplier interface TODO
|
||||
c4 <= i_dat;
|
||||
if (i_val && o_rdy) begin
|
||||
o_rdy <= 0;
|
||||
state <= S0;// WAIT_MULT;
|
||||
o_mult.val <= 1;
|
||||
o_mult.dat[0 +: OUT_BITS + 8] <= i_dat >> (K-1);
|
||||
o_mult.dat[OUT_BITS + 8 +: OUT_BITS + 8] <= U;
|
||||
prev_state <= S0;
|
||||
c2_ <= (i_dat >> (K-1))*U; // Using multiplier interface
|
||||
end
|
||||
end
|
||||
{S0}: begin
|
||||
c3 <= c2 >> (K + 1);
|
||||
state <= S1;
|
||||
end
|
||||
{S1}: begin
|
||||
c4 <= c4 - c3*P; // Using multiplier interface TODO
|
||||
o_mult.val <= 1;
|
||||
o_mult.dat[0 +: OUT_BITS] <= c3;
|
||||
o_mult.dat[OUT_BITS +: OUT_BITS] <= P;
|
||||
state <= S2; //WAIT_MULT;
|
||||
prev_state <= S2;
|
||||
end
|
||||
{S2}: begin
|
||||
if (c4 >= P_) begin
|
||||
c4 <= c4 - P_;
|
||||
end else begin
|
||||
state <= FINISHED;
|
||||
o_dat <= c4;
|
||||
o_val <= 1;
|
||||
end
|
||||
end
|
||||
{FINISHED}: begin
|
||||
if (o_val && i_rdy) begin
|
||||
o_val <= 0;
|
||||
state <= IDLE;
|
||||
end
|
||||
end
|
||||
// In this state we are waiting for a multiply to be finished
|
||||
{WAIT_MULT}: begin
|
||||
if (o_mult.val && o_mult.rdy) o_mult.val <= 0;
|
||||
if (i_mult_res.rdy && i_mult_res.val) begin
|
||||
state <= prev_state;
|
||||
case(prev_state)
|
||||
S0: c2 <= i_mult_res.dat;
|
||||
S2: c4 <= c4 - i_mult_res.dat;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
initial assert (IN_BITS <= MAX_IN_BITS) else $fatal(1, "%m ERROR: IN_BITS[%d] > MAX_IN_BITS[%d] in barret_mod", IN_BITS, MAX_IN_BITS);
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
`timescale 1ps/1ps
|
||||
|
||||
module barret_mod_tb ();
|
||||
import common_pkg::*;
|
||||
import secp256k1_pkg::*;
|
||||
|
||||
localparam CLK_PERIOD = 100;
|
||||
|
||||
logic clk, rst;
|
||||
|
||||
localparam IN_BITS = 512;
|
||||
localparam OUT_BITS = 256;
|
||||
localparam [OUT_BITS-1:0] P = secp256k1_pkg::n;
|
||||
localparam USE_MULT = 0;
|
||||
|
||||
if_axi_stream #(.DAT_BYTS(IN_BITS/8)) in_if(clk);
|
||||
if_axi_stream #(.DAT_BYTS(OUT_BITS/8)) out_if(clk);
|
||||
if_axi_stream #(.DAT_BYTS(2*((OUT_BITS+16))/8)) mult_in_if(clk);
|
||||
if_axi_stream #(.DAT_BYTS((OUT_BITS+8)/8)) mult_out_if(clk);
|
||||
|
||||
initial begin
|
||||
rst = 0;
|
||||
repeat(2) #(20*CLK_PERIOD) rst = ~rst;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
forever #CLK_PERIOD clk = ~clk;
|
||||
end
|
||||
|
||||
generate
|
||||
if (USE_MULT == 0) begin: MULT_GEN
|
||||
always_ff @ (posedge clk) begin
|
||||
if (rst) begin
|
||||
mult_in_if.rdy <= 0;
|
||||
mult_out_if.reset_source();
|
||||
end else begin
|
||||
mult_in_if.rdy <= 1;
|
||||
if (mult_in_if.rdy && mult_in_if.val) begin
|
||||
mult_out_if.dat <= mult_in_if.dat[0 +: OUT_BITS] * mult_in_if.dat[OUT_BITS +: OUT_BITS];
|
||||
mult_out_if.val <= 1;
|
||||
end
|
||||
if (mult_out_if.val && mult_out_if.rdy) mult_out_if.val <= 0;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always_comb begin
|
||||
out_if.sop = 1;
|
||||
out_if.eop = 1;
|
||||
out_if.ctl = 0;
|
||||
out_if.mod = 0;
|
||||
end
|
||||
|
||||
// Check for errors
|
||||
always_ff @ (posedge clk)
|
||||
if (out_if.val && out_if.err)
|
||||
$error(1, "%m %t ERROR: output .err asserted", $time);
|
||||
|
||||
barret_mod #(
|
||||
.IN_BITS ( IN_BITS ),
|
||||
.OUT_BITS ( OUT_BITS ),
|
||||
.P ( P )
|
||||
)
|
||||
barret_mod (
|
||||
.i_clk ( clk ),
|
||||
.i_rst ( rst ),
|
||||
.i_dat ( in_if.dat ),
|
||||
.i_val ( in_if.val ),
|
||||
.o_rdy ( in_if.rdy ),
|
||||
.o_dat ( out_if.dat ),
|
||||
.o_val ( out_if.val ),
|
||||
.i_rdy ( out_if.rdy ),
|
||||
|
||||
.o_mult ( mult_in_if ),
|
||||
.i_mult_res ( mult_out_if )
|
||||
);
|
||||
|
||||
|
||||
task test_loop();
|
||||
begin
|
||||
integer signed get_len;
|
||||
logic [common_pkg::MAX_SIM_BYTS*8-1:0] expected, get_dat;
|
||||
logic [512*8-1:0] in;
|
||||
integer i, max;
|
||||
|
||||
$display("Running test_loop...");
|
||||
i = 0;
|
||||
max = 10000;
|
||||
|
||||
while (i < max) begin
|
||||
in = random_vector(IN_BITS/8);
|
||||
expected = (in % P);
|
||||
|
||||
fork
|
||||
in_if.put_stream(in, IN_BITS/8);
|
||||
out_if.get_stream(get_dat, get_len);
|
||||
join
|
||||
common_pkg::compare_and_print(get_dat, expected);
|
||||
$display("test_loop PASSED loop %d/%d", i, max);
|
||||
i = i + 1;
|
||||
end
|
||||
|
||||
$display("test_loop PASSED");
|
||||
end
|
||||
endtask;
|
||||
|
||||
initial begin
|
||||
out_if.rdy = 0;
|
||||
in_if.val = 0;
|
||||
#(40*CLK_PERIOD);
|
||||
|
||||
test_loop();
|
||||
|
||||
#1us $finish();
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue