diff --git a/ip_cores/common/src/rtl/common_if.sv b/ip_cores/common/src/rtl/common_if.sv index e08aab5..3e41373 100644 --- a/ip_cores/common/src/rtl/common_if.sv +++ b/ip_cores/common/src/rtl/common_if.sv @@ -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 diff --git a/ip_cores/util/src/rtl/barret_mod.sv b/ip_cores/util/src/rtl/barret_mod.sv new file mode 100644 index 0000000..ed0eb2c --- /dev/null +++ b/ip_cores/util/src/rtl/barret_mod.sv @@ -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 . + */ + +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 \ No newline at end of file diff --git a/ip_cores/util/src/tb/barret_mod_tb.sv b/ip_cores/util/src/tb/barret_mod_tb.sv new file mode 100644 index 0000000..cee169e --- /dev/null +++ b/ip_cores/util/src/tb/barret_mod_tb.sv @@ -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 . +*/ +`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 \ No newline at end of file