Binary inversion module and testbench

This commit is contained in:
bsdevlin 2019-03-19 00:18:41 -04:00
parent b94b2a7c5d
commit 5555a71810
4 changed files with 243 additions and 7 deletions

View File

@ -0,0 +1,128 @@
/*
Calculates inversion mod P using binary gcd algorithm.
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 bin_inv #(
parameter BITS,
parameter [BITS-1:0] P
)(
input i_clk,
input i_rst,
input [BITS-1:0] i_dat,
input i_val,
output logic o_rdy,
output logic [BITS-1:0] o_dat,
output logic o_val,
input i_rdy
);
logic [BITS:0] x1, x2, u, v;
enum {IDLE,
U_STATE,
V_STATE,
UPDATE,
FINISHED} state;
always_ff @ (posedge i_clk) begin
if (i_rst) begin
x1 <= 0;
x2 <= 0;
u <= 0;
v <= 0;
o_rdy <= 0;
o_val <= 0;
o_dat <= 0;
state <= IDLE;
end else begin
o_rdy <= 0;
case(state)
IDLE: begin
o_rdy <= 1;
o_val <= 0;
if (o_rdy && i_val) begin
o_rdy <= 0;
u <= i_dat;
v <= P;
x1 <= 1;
x2 <= 0;
state <= U_STATE;
end
end
U_STATE: begin
if (u % 2 == 1) begin
state <= V_STATE;
end else begin
u <= u/2;
if (x1 % 2 == 0) begin
x1 <= x1/2;
end else begin
x1 <= (x1 + P)/2;
end
if ((u/2) % 2 == 1) begin
state <= V_STATE;
end
end
end
V_STATE: begin
if (v % 2 == 1) begin
state <= UPDATE;
end else begin
v <= v/2;
if (x2 % 2 == 0) begin
x2 <= x2/2;
end else begin
x2 <= (x2 + P)/2;
end
if ((v/2 % 2) == 1) begin
state <= UPDATE;
end
end
end
UPDATE: begin
state <= U_STATE;
if (u >= v) begin
u <= u - v;
x1 <= x1 + (x1 >= x2 ? 0 : P) - x2;
if (u - v == 1 || v == 1) begin
state <= FINISHED;
end
end else begin
v <= v - u;
x2 <= x2 + (x2 >= x1 ? 0 : P) - x1;
if (v - u == 1 || u == 1) begin
state <= FINISHED;
end
end
end
FINISHED: begin
if (~o_val || (o_val && i_rdy)) begin
o_val <= 1;
o_dat <= (u == 1) ? x1 : x2;
if (o_val && i_rdy) begin
o_val <= 0;
o_rdy <= 1;
state <= IDLE;
end
end
end
endcase
end
end
endmodule

View File

@ -0,0 +1,106 @@
/*
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 bin_inv_tb ();
import common_pkg::*;
import secp256k1_pkg::*;
localparam CLK_PERIOD = 100;
logic clk, rst;
if_axi_stream #(.DAT_BYTS(256/8)) in_if(clk);
if_axi_stream #(.DAT_BYTS(256/8)) 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
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);
bin_inv #(
.P ( secp256k1_pkg::p_eq ),
.BITS ( 256 )
)
bin_inv (
.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 ),
.i_rdy( out_if.rdy ),
.o_val( out_if.val )
);
task test_loop();
begin
integer signed get_len;
logic [common_pkg::MAX_SIM_BYTS*8-1:0] expected, get_dat;
logic [255:0] in;
integer i, max;
$display("Running test_loop...");
i = 0;
max = 10000;
while (i < max) begin
in = random_vector(256/8) % p_eq;
fork
in_if.put_stream(in, 256/8);
out_if.get_stream(get_dat, get_len);
join
assert(get_dat < p_eq) else $fatal(1, "%m %t ERROR: test_loop value was >= p:\n%d", $time, get_dat);
assert((get_dat*in) % p_eq == 1) else $fatal(1, "%m %t ERROR: test_loop (get_dat*in) % p_eq != 1", $time);
$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

View File

@ -42,7 +42,7 @@ module secp256k1_mult_mod (
import secp256k1_pkg::*;
import common_pkg::*;
localparam KARATSUBA_LEVEL = 3;
localparam KARATSUBA_LEVEL = 2;
if_axi_stream #(.DAT_BYTS(512/8)) int_if(i_clk);
always_comb o_rdy = int_if.rdy;

View File

@ -1,10 +1,12 @@
module secp256k1_top ();
module secp256k1_top (
input i_clk,
input i_rst,
input i_val,
output logic o_rdy,
output logic o_val
// inversion
// multiplication
// addition
);
endmodule