Barret modulus block

This commit is contained in:
bsdevlin 2019-03-19 14:00:06 -04:00
parent 5555a71810
commit c558b391dd
3 changed files with 285 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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