summaryrefslogblamecommitdiff
path: root/rtl/ed25519_uop_worker.v
blob: 244ff97109ce5c34330c30635db08a00d5100dae (plain) (tree)





































                                                                                
                         


               



                 
                          





                       
                         











                                                              








                                                               
































                                                                               


                                                      

                                                      
                                       












































                                                                                     

























































































































                                                                                     
    





                                                



                                                       
                      



                                    












                                                                                          


                                                                                         


















                                        
                                   

















                                                
                                       

      






                                              

















                                                  





































                                                  













                                      
 
    











                                                                          










































                                                                                                            

























                                                      





                                                                                
//------------------------------------------------------------------------------
//
// ed25519_uop_worker.v
// -----------------------------------------------------------------------------
// Ed25519 uOP Worker.
//
// 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_uop_worker
(
    clk, rst_n,
    ena, rdy,
    uop_offset,
    final_reduce,
    handle_sign,
    output_now,
    y_addr, y_dout, y_wren
);


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


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

    input   ena;    // enable input
    output  rdy;    // ready output
    
    input [UOP_ADDR_WIDTH-1:0] uop_offset;  // starting offset
    
    input   final_reduce;   // use regular (not double) modulus
    input   handle_sign;    // handle sign of x
    input   output_now;     // produce output    

    output  [ 2: 0] y_addr;
    output  [31: 0] y_dout;
    output          y_wren;
    
    
    //
    // Constants
    //
    localparam integer OPERAND_NUM_WORDS = 8;   // 256 bits -> 8 x 32-bit words
    localparam integer WORD_COUNTER_WIDTH = 3;  // 0..7 -> 3 bits
    

    //
    // FSM
    //
    localparam [1:0] FSM_STATE_IDLE     = 2'b00;
    localparam [1:0] FSM_STATE_FETCH    = 2'b01;
    localparam [1:0] FSM_STATE_DECODE   = 2'b10;
    localparam [1:0] FSM_STATE_BUSY     = 2'b11;

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

  
    //
    // Microcode
    //
    reg  [UOP_ADDR_WIDTH-1:0] uop_addr;
    wire [UOP_DATA_WIDTH-1:0] uop_data;
    
    wire [4:0] uop_data_opcode        = uop_data[1 + 3*6 +: 5];
    wire       uop_data_banks         = uop_data[0 + 3*6 +: 1];
    wire [5:0] uop_data_operand_src1  = uop_data[0 + 2*6 +: 6];
    wire [5:0] uop_data_operand_src2  = uop_data[0 + 1*6 +: 6];
    wire [5:0] uop_data_operand_dst   = uop_data[0 + 0*6 +: 6];
    
    wire uop_data_opcode_is_stop = uop_data_opcode[4];
    wire uop_data_opcode_is_mul  = uop_data_opcode[3];
    wire uop_data_opcode_is_sub  = uop_data_opcode[2];
    wire uop_data_opcode_is_add  = uop_data_opcode[1];
    wire uop_data_opcode_is_copy = uop_data_opcode[0];
    
    ed25519_microcode_rom microcode_rom
    (
        .clk    (clk),
        .addr   (uop_addr),
        .data   (uop_data)
    );
    
    
    //
    // Microcode Address Increment Logic
    //
    always @(posedge clk)
        //
        if (fsm_state_next == FSM_STATE_FETCH)
            uop_addr <= (fsm_state == FSM_STATE_IDLE) ? uop_offset : uop_addr + 1'b1;


    //
    // Multi-Word Mover
    //
    reg  mw_mover_ena = 1'b0;
    wire mw_mover_rdy;
    
    wire [WORD_COUNTER_WIDTH-1:0]   mw_mover_x_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mw_mover_y_addr;
    wire [                32-1:0]   mw_mover_x_din;
    wire [                32-1:0]   mw_mover_y_dout;
    wire                            mw_mover_y_wren;
    
    mw_mover #
    (
        .WORD_COUNTER_WIDTH     (WORD_COUNTER_WIDTH),
        .OPERAND_NUM_WORDS      (OPERAND_NUM_WORDS)
    )
    mw_mover_inst
    (
        .clk        (clk),
        .rst_n      (rst_n),
        .ena        (mw_mover_ena),
        .rdy        (mw_mover_rdy),
        .x_addr     (mw_mover_x_addr),
        .y_addr     (mw_mover_y_addr),
        .y_wren     (mw_mover_y_wren),
        .x_din      (mw_mover_x_din),
        .y_dout     (mw_mover_y_dout)
    );


    //
    // Modular Multiplier
    //
    reg  mod_mul_ena = 1'b0;
    wire mod_mul_rdy;
    
    wire [WORD_COUNTER_WIDTH-1:0]   mod_mul_a_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_mul_b_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_mul_p_addr;
    wire [                32-1:0]   mod_mul_a_din;
    wire [                32-1:0]   mod_mul_b_din;
    wire [                32-1:0]   mod_mul_p_dout;
    wire                            mod_mul_p_wren;
    
    ed25519_modular_multiplier mod_mul_inst
    (
        .clk        (clk),
        .rst_n      (rst_n),
        .ena        (mod_mul_ena),
        .rdy        (mod_mul_rdy),
        .a_addr     (mod_mul_a_addr),
        .b_addr     (mod_mul_b_addr),
        .p_addr     (mod_mul_p_addr),
        .p_wren     (mod_mul_p_wren),
        .a_din      (mod_mul_a_din),
        .b_din      (mod_mul_b_din),
        .p_dout     (mod_mul_p_dout)
    );
    
    
    //
    // Modular Adder
    //
    reg  mod_add_ena = 1'b0;
    wire mod_add_rdy;
    
    wire [WORD_COUNTER_WIDTH-1:0]   mod_add_ab_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_add_n_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_add_s_addr;
    wire [                32-1:0]   mod_add_a_din;
    wire [                32-1:0]   mod_add_b_din;
    reg  [                32-1:0]   mod_add_n_din;
    wire [                32-1:0]   mod_add_s_dout;
    wire                            mod_add_s_wren;
        
    mod_adder #
    (
        .OPERAND_NUM_WORDS(OPERAND_NUM_WORDS),
        .WORD_COUNTER_WIDTH(WORD_COUNTER_WIDTH)
    )
    mod_add_inst
    (
        .clk        (clk),
        .rst_n      (rst_n),
        .ena        (mod_add_ena),
        .rdy        (mod_add_rdy),
        .ab_addr    (mod_add_ab_addr),
        .n_addr     (mod_add_n_addr),
        .s_addr     (mod_add_s_addr),
        .s_wren     (mod_add_s_wren),
        .a_din      (mod_add_a_din),
        .b_din      (mod_add_b_din),
        .n_din      (mod_add_n_din),
        .s_dout     (mod_add_s_dout)
    );
    
    
    //
    // Modular Subtractor
    //
    reg  mod_sub_ena = 1'b0;
    wire mod_sub_rdy;
    
    wire [WORD_COUNTER_WIDTH-1:0]   mod_sub_ab_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_sub_n_addr;
    wire [WORD_COUNTER_WIDTH-1:0]   mod_sub_d_addr;
    wire [                32-1:0]   mod_sub_a_din;
    wire [                32-1:0]   mod_sub_b_din;
    reg  [                32-1:0]   mod_sub_n_din;
    wire [                32-1:0]   mod_sub_d_dout;
    wire                            mod_sub_d_wren;
        
    mod_subtractor #
    (
        .OPERAND_NUM_WORDS(OPERAND_NUM_WORDS),
        .WORD_COUNTER_WIDTH(WORD_COUNTER_WIDTH)
    )
    mod_sub_inst
    (
        .clk        (clk),
        .rst_n      (rst_n),
        .ena        (mod_sub_ena),
        .rdy        (mod_sub_rdy),
        .ab_addr    (mod_sub_ab_addr),
        .n_addr     (mod_sub_n_addr),
        .d_addr     (mod_sub_d_addr),
        .d_wren     (mod_sub_d_wren),
        .a_din      (mod_sub_a_din),
        .b_din      (mod_sub_b_din),
        .n_din      (mod_sub_n_din),
        .d_dout     (mod_sub_d_dout)
    );
    
    
    //
    // Double Modulus
    //
    always @(posedge clk) begin
        //
        case (mod_add_n_addr)
            3'd0:       mod_add_n_din <= !final_reduce ? 32'hFFFFFFDA : 32'hFFFFFFED;
            3'd7:       mod_add_n_din <= !final_reduce ? 32'hFFFFFFFF : 32'h7FFFFFFF;
            default:    mod_add_n_din <= 32'hFFFFFFFF;
        endcase
        //
        if (mod_sub_n_addr == 3'd0) mod_sub_n_din <= 32'hFFFFFFDA;
        else                        mod_sub_n_din <= 32'hFFFFFFFF;
        //
    end
    
    
    //
    // uOP Trigger Logic
    //
    always @(posedge clk)
        //
        if (fsm_state == FSM_STATE_DECODE) begin
            mw_mover_ena    <= uop_data_opcode_is_copy;
            mod_mul_ena     <= uop_data_opcode_is_mul;
            mod_add_ena     <= uop_data_opcode_is_add;
            mod_sub_ena     <= uop_data_opcode_is_sub;
        end else begin
            mw_mover_ena    <= 1'b0;
            mod_mul_ena     <= 1'b0;
            mod_add_ena     <= 1'b0;
            mod_sub_ena     <= 1'b0;
        end

    
    //
    // uOP Completion Detector
    //
    reg fsm_exit_from_busy;
    
    always @* begin
        //
        fsm_exit_from_busy = 0;
        //
        if (uop_data_opcode_is_copy)    fsm_exit_from_busy = ~mw_mover_ena & mw_mover_rdy;
        if (uop_data_opcode_is_mul)     fsm_exit_from_busy = ~mod_mul_ena  & mod_mul_rdy;
        if (uop_data_opcode_is_add)     fsm_exit_from_busy = ~mod_add_ena  & mod_add_rdy;
        if (uop_data_opcode_is_sub)     fsm_exit_from_busy = ~mod_sub_ena  & mod_sub_rdy;
        //
    end

    
        
        //
        // Banks
        //
    reg     [ 2:0]  banks_src1_addr;
    reg     [ 2:0]  banks_src2_addr;
    reg     [ 2:0]  banks_dst_addr;
    
    reg             banks_dst_wren;
    
    reg     [31:0]  banks_dst_din;

    wire    [31:0]  banks_src1_dout;
    wire    [31:0]  banks_src2_dout;    
    
    ed25519_banks_array banks_array
    (
        .clk            (clk),
    
        .banks          (uop_data_banks),
    
        .src1_operand   (uop_data_operand_src1),
        .src2_operand   (uop_data_operand_src2),
        .dst_operand    (uop_data_operand_dst),
    
        .src1_addr      (banks_src1_addr),
        .src2_addr      (banks_src2_addr),
        .dst_addr       (banks_dst_addr),
    
        .dst_wren       (banks_dst_wren),
    
        .src1_dout      (banks_src1_dout),
        .src2_dout      (banks_src2_dout),
    
        .dst_din        (banks_dst_din)
    );
    
    assign mw_mover_x_din   = banks_src1_dout;
    assign mod_mul_a_din    = banks_src1_dout;
    assign mod_mul_b_din    = banks_src2_dout;
    assign mod_add_a_din    = banks_src1_dout;
    assign mod_add_b_din    = banks_src2_dout;
    assign mod_sub_a_din    = banks_src1_dout;
    assign mod_sub_b_din    = banks_src2_dout;
    
    always @*
        //
        case (uop_data_opcode)
            //
            UOP_OPCODE_COPY: begin
                //
                banks_src1_addr = mw_mover_x_addr;
                banks_src2_addr = 'bX;
                //
                banks_dst_addr  = mw_mover_y_addr;
                //
                banks_dst_wren  = mw_mover_y_wren;
                //
                banks_dst_din   = mw_mover_y_dout;
                //
            end
            //
            UOP_OPCODE_ADD: begin
                //
                banks_src1_addr = mod_add_ab_addr;
                banks_src2_addr = mod_add_ab_addr;
                //
                banks_dst_addr  = mod_add_s_addr;
                //
                banks_dst_wren  = mod_add_s_wren;
                //
                banks_dst_din   = mod_add_s_dout;
                //                
            end
            //
            UOP_OPCODE_SUB: begin
                //
                banks_src1_addr = mod_sub_ab_addr;
                banks_src2_addr = mod_sub_ab_addr;
                //
                banks_dst_addr  = mod_sub_d_addr;
                //
                banks_dst_wren  = mod_sub_d_wren;
                //
                banks_dst_din   = mod_sub_d_dout;
                //                
            end
            //
            UOP_OPCODE_MUL: begin
                //
                banks_src1_addr = mod_mul_a_addr;
                banks_src2_addr = mod_mul_b_addr;
                //
                banks_dst_addr  = mod_mul_p_addr;
                //
                banks_dst_wren  = mod_mul_p_wren;
                //
                banks_dst_din   = mod_mul_p_dout;
                //                
            end
            //
            default: begin
                //
                banks_src1_addr = 'bX;
                banks_src2_addr = 'bX;
                //
                banks_dst_addr  = 'bX;
                //
                banks_dst_wren  = 'b0;
                //
                banks_dst_din   = 'bX;
            end
            //
        endcase

    
    //
    // Sign Handler
    //
    reg sign_x_int;

    wire [31:0] mw_mover_y_dout_with_x_sign = {(mw_mover_y_addr == 3'd7) ?
        sign_x_int : mw_mover_y_dout[31], mw_mover_y_dout[30:0]};
    
    always @(posedge clk)
        //
        if (handle_sign && mw_mover_y_wren && (mw_mover_y_addr == 0))
            sign_x_int <= mw_mover_y_dout[0];
    
    
    //
    // 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_FETCH : FSM_STATE_IDLE;
            FSM_STATE_FETCH:    fsm_state_next = FSM_STATE_DECODE;
            FSM_STATE_DECODE:   fsm_state_next = uop_data_opcode_is_stop ? FSM_STATE_IDLE  : FSM_STATE_BUSY;
            FSM_STATE_BUSY:     fsm_state_next = fsm_exit_from_busy      ? FSM_STATE_FETCH : FSM_STATE_BUSY;
        endcase
        //
    end


    //
    // 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:     rdy_reg <= ~ena;
            FSM_STATE_DECODE:   rdy_reg <= uop_data_opcode_is_stop;
        endcase



    //
    // Output Logic
    //
    reg [ 2: 0] y_addr_reg = 3'b000;
    reg [31: 0] y_dout_reg = 32'h00000000;
    reg         y_wren_reg = 1'b0;

    assign y_addr = y_addr_reg;
    assign y_dout = y_dout_reg;
    assign y_wren = y_wren_reg;

    always @(posedge clk)
        //
        if (output_now && mw_mover_y_wren) begin
            //
            y_addr_reg <= mw_mover_y_addr;
            y_dout_reg <= mw_mover_y_dout_with_x_sign;
            y_wren_reg <= 1'b1;
            //
        end else begin
            y_addr_reg <= 3'b000;
            y_dout_reg <= 32'h00000000;
            y_wren_reg <= 1'b0;
        end

endmodule


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