summaryrefslogblamecommitdiff
path: root/rtl/ed25519_multiplier.v
blob: d6960ecd0a5ed33941318633a70e3d78eff6a639 (plain) (tree)

























































































































































































































































                                                                                                                                       
//------------------------------------------------------------------------------
//
// ed25519_multiplier.v
// -----------------------------------------------------------------------------
// Ed25519 base point scalar multiplier.
//
// Authors: Pavel Shatov
//
// Copyright (c) 2018, NORDUnet A/S
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// - Neither the name of the NORDUnet nor the names of its contributors may be
//   used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//------------------------------------------------------------------------------

module ed25519_multiplier
(
    clk, rst_n,
    ena, rdy,
    k_addr, qy_addr,
    qy_wren,
    k_din,
    qy_dout
);


    //
    // Microcode Header
    //
`include "ed25519_uop.v"
    

    //
    // Ports
    //
    input           clk;        // system clock
    input           rst_n;      // active-low async reset

    input           ena;        // enable input
    output          rdy;        // ready output

    output  [ 2:0]  k_addr;     //
    output  [ 2:0]  qy_addr;    //
    output          qy_wren;    //
    input   [31:0]  k_din;      //
    output  [31:0]  qy_dout;    //


    //
    // FSM
    //
    localparam [3:0] FSM_STATE_IDLE                 = 4'd00;
    localparam [3:0] FSM_STATE_PREPARE_TRIG         = 4'd01;
    localparam [3:0] FSM_STATE_PREPARE_WAIT         = 4'd02;
    localparam [3:0] FSM_STATE_BEFORE_ROUND_TRIG    = 4'd03;
    localparam [3:0] FSM_STATE_BEFORE_ROUND_WAIT    = 4'd04;
    localparam [3:0] FSM_STATE_DURING_ROUND_TRIG    = 4'd05;
    localparam [3:0] FSM_STATE_DURING_ROUND_WAIT    = 4'd06;
    localparam [3:0] FSM_STATE_AFTER_ROUND_TRIG     = 4'd07;
    localparam [3:0] FSM_STATE_AFTER_ROUND_WAIT     = 4'd08;
    localparam [3:0] FSM_STATE_DONE                 = 4'd15;

    reg [3:0] fsm_state = FSM_STATE_IDLE;
    reg [3:0] fsm_state_next;


    //
    // Round Counter
    //
    reg  [7:0] bit_counter;
    wire [7:0] bit_counter_max  = 8'hFF;    // 255
    wire [7:0] bit_counter_zero = 8'h00;    // 0
    wire [7:0] bit_counter_next =
        (bit_counter < bit_counter_max) ? bit_counter + 1'b1 : bit_counter_zero;

    assign k_addr = bit_counter[7:5];


    //
    // Worker Trigger Logic
    //
    reg  worker_trig = 1'b0;
    wire worker_done;

    wire fsm_wait_done = !worker_trig && worker_done;

    always @(posedge clk or negedge rst_n)
        //
        if (rst_n == 1'b0)                  worker_trig <= 1'b0;
        else case (fsm_state)
            FSM_STATE_PREPARE_TRIG,
            FSM_STATE_BEFORE_ROUND_TRIG,
            FSM_STATE_DURING_ROUND_TRIG,
            FSM_STATE_AFTER_ROUND_TRIG:     worker_trig <= 1'b1;
            default:                        worker_trig <= 1'b0;
        endcase
        
        
    //
    // Round Counter Increment Logic
    //
    always @(posedge clk)
        //
        case (fsm_state_next)
            FSM_STATE_PREPARE_TRIG:         bit_counter <= bit_counter_zero;
            FSM_STATE_AFTER_ROUND_TRIG:     bit_counter <= bit_counter_next;
        endcase


    //
    // Final Round Detection Logic
    //
    wire [ 3: 0] fsm_state_after_round = (bit_counter != bit_counter_zero) ?
        FSM_STATE_BEFORE_ROUND_TRIG : FSM_STATE_DONE;


  

    //
    // K Latch
    //
    reg [31:0] k_din_shreg;
    
    wire [4:0] k_bit_index = bit_counter[4:0];
    
    always @(posedge clk)
        //
        if (fsm_state_next == FSM_STATE_BEFORE_ROUND_TRIG)
            //
            if (k_bit_index == 5'd0)
                //
                case (k_addr)
                    3'd0:       k_din_shreg <= {k_din[31:3], 3'b000};
                    3'd7:       k_din_shreg <= {2'b01, k_din[29:0]};
                    default:    k_din_shreg <= k_din;
                endcase
                //
            else                k_din_shreg <= {k_din_shreg[0], k_din_shreg[31:1]};
    

    //
    // Worker Offset Logic
    //
    reg [UOP_ADDR_WIDTH-1:0] worker_offset;
    
    always @(posedge clk)
        //
        case (fsm_state)
            FSM_STATE_PREPARE_TRIG:         worker_offset <= UOP_OFFSET_PREPARE;
            FSM_STATE_BEFORE_ROUND_TRIG:    worker_offset <= k_din_shreg[0] ? UOP_OFFSET_BEFORE_ROUND_K1 : UOP_OFFSET_BEFORE_ROUND_K0;
            default:                        worker_offset <= {UOP_ADDR_WIDTH{1'bX}};
        endcase
        
    
    //
    // FSM Process
    //
    always @(posedge clk or negedge rst_n)
        //
        if (rst_n == 1'b0)  fsm_state <= FSM_STATE_IDLE;
        else                fsm_state <= fsm_state_next;


    //
    // FSM Transition Logic
    //
    always @* begin
        //
        fsm_state_next = FSM_STATE_IDLE;
        //
        case (fsm_state)

            FSM_STATE_IDLE:                 fsm_state_next = ena ? FSM_STATE_PREPARE_TRIG : FSM_STATE_IDLE;

            FSM_STATE_PREPARE_TRIG:         fsm_state_next = FSM_STATE_PREPARE_WAIT;
            FSM_STATE_PREPARE_WAIT:         fsm_state_next = fsm_wait_done ? FSM_STATE_BEFORE_ROUND_TRIG : FSM_STATE_PREPARE_WAIT;

            FSM_STATE_BEFORE_ROUND_TRIG:    fsm_state_next = FSM_STATE_BEFORE_ROUND_WAIT;
            FSM_STATE_BEFORE_ROUND_WAIT:    fsm_state_next = fsm_wait_done ? FSM_STATE_DURING_ROUND_TRIG : FSM_STATE_BEFORE_ROUND_WAIT;

            FSM_STATE_DURING_ROUND_TRIG:    fsm_state_next = FSM_STATE_DURING_ROUND_WAIT;
            FSM_STATE_DURING_ROUND_WAIT:    fsm_state_next = fsm_wait_done ? FSM_STATE_AFTER_ROUND_TRIG : FSM_STATE_DURING_ROUND_WAIT;

            FSM_STATE_AFTER_ROUND_TRIG:     fsm_state_next = FSM_STATE_AFTER_ROUND_WAIT;
            FSM_STATE_AFTER_ROUND_WAIT:     fsm_state_next = fsm_wait_done ? fsm_state_after_round : FSM_STATE_AFTER_ROUND_WAIT;

            FSM_STATE_DONE:                 fsm_state_next = FSM_STATE_IDLE;

        endcase
        //
    end


    //
    // Worker
    //
    ed25519_worker uop_worker
    (
        .clk        (clk),
        .rst_n      (rst_n),
        .ena        (worker_trig),
        .rdy        (worker_done),
        .uop_offset (worker_offset)
    );


    //
    // Ready Flag Logic
    //
    reg rdy_reg = 1'b1;
    assign rdy = rdy_reg;

    always @(posedge clk or negedge rst_n)
        //
        if (rst_n == 1'b0)              rdy_reg <= 1'b1;
        else case (fsm_state)
            FSM_STATE_IDLE: if (ena)    rdy_reg <= 1'b0;
            FSM_STATE_DONE:             rdy_reg <= 1'b1;
        endcase


endmodule


//------------------------------------------------------------------------------
// End-of-File
//------------------------------------------------------------------------------