aboutsummaryrefslogblamecommitdiff
path: root/src/rtl/modexp_core.v
blob: 97f5488b4b6e4e92615678fbc79257eeeccfa68c (plain) (tree)
























                                                                        
                                   

                       
























                                                                           



                                                                        

                                                           



















































































































































































                                                                              

                                   







                                                                    

                                                     




                                                                    



























































































































                                                          























































































































                                                                    
                                                 

                               
                                                 
















































































































































                                                                    
                                                             
































                                                                    
                                                                      































































































































































































                                                                                                              
//======================================================================
//
// modexp_core.v
// -------------
// Modular exponentiation core for implementing public key algorithms
// such as RSA, DH, ElGamal etc.
//
// The core calculates the following function:
//
//   C = M ** e mod N
//
//   M is a message with a length of n bits
//   e is the exponent with a length of m bits
//   N is the modulus  with a length of n bits
//
//   n can be 32 and up to and including 8192 bits in steps
//   of 32 bits.
//   m can be one and up to and including 8192 bits in steps
//   of 32 bits.
//
// The core has access ports for the exponent, modulus, message and
// result memories.
//
//
// Author: Joachim Strombergson, Peter Magnusson
// Copyright (c) 2015, NORDUnet A/S
// All rights reserved.
//
// 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 modexp_core #(parameter OPW = 32, parameter ADW = 8)
                  (
                   input wire           clk,
                   input wire           reset_n,

                   input wire           start,
                   output wire          ready,

                   input wire [07 : 0]  exponent_length,
                   input wire [07 : 0]  modulus_length,

                   output wire [63 : 0] cycles,

                   input wire           exponent_mem_api_cs,
                   input wire           exponent_mem_api_wr,
                   input wire           exponent_mem_api_rst,
                   input wire [31 : 0]  exponent_mem_api_write_data,
                   output wire [31 : 0] exponent_mem_api_read_data,

                   input wire           modulus_mem_api_cs,
                   input wire           modulus_mem_api_wr,
                   input wire           modulus_mem_api_rst,
                   input wire [31 : 0]  modulus_mem_api_write_data,
                   output wire [31 : 0] modulus_mem_api_read_data,

                   input wire           message_mem_api_cs,
                   input wire           message_mem_api_wr,
                   input wire           message_mem_api_rst,
                   input wire [31 : 0]  message_mem_api_write_data,
                   output wire [31 : 0] message_mem_api_read_data,

                   input wire           result_mem_api_cs,
                   input wire           result_mem_api_rst,
                   output wire [31 : 0] result_mem_api_read_data
                  );


  //----------------------------------------------------------------
  // Internal constant and parameter definitions.
  //----------------------------------------------------------------
  localparam MONTPROD_SELECT_ONE_NR = 3'h0;
  localparam MONTPROD_SELECT_X_NR   = 3'h1;
  localparam MONTPROD_SELECT_Z_P    = 3'h2;
  localparam MONTPROD_SELECT_P_P    = 3'h3;
  localparam MONTPROD_SELECT_Z_ONE  = 3'h4;

  localparam MONTPROD_DEST_Z        = 2'b00;
  localparam MONTPROD_DEST_P        = 2'b01;
  localparam MONTPROD_DEST_NOWHERE  = 2'b10;

  localparam CTRL_IDLE              = 4'h0;
  localparam CTRL_RESIDUE           = 4'h1;
  localparam CTRL_CALCULATE_Z0      = 4'h2;
  localparam CTRL_CALCULATE_P0      = 4'h3;
  localparam CTRL_ITERATE           = 4'h4;
  localparam CTRL_ITERATE_Z_P       = 4'h5;
  localparam CTRL_ITERATE_P_P       = 4'h6;
  localparam CTRL_ITERATE_END       = 4'h7;
  localparam CTRL_CALCULATE_ZN      = 4'h8;
  localparam CTRL_DONE              = 4'h9;

  //for rsa, c=M^65537 etc, there is no need to slow down to hide the exponent
  localparam EXPONATION_MODE_SECRET_SECURE = 1'b0;
  localparam EXPONATION_MODE_PUBLIC_FAST   = 1'b1;


  //----------------------------------------------------------------
  // Registers including update variables and write enable.
  //----------------------------------------------------------------
  reg          ready_reg;
  reg          ready_new;
  reg          ready_we;

  reg [2 : 0]  montprod_select_reg;
  reg [2 : 0]  montprod_select_new;
  reg          montprod_select_we;
  reg [1 : 0]  montprod_dest_reg;
  reg [1 : 0]  montprod_dest_new;
  reg          montprod_dest_we;

  reg [3 : 0]  modexp_ctrl_reg;
  reg [3 : 0]  modexp_ctrl_new;
  reg          modexp_ctrl_we;

  reg [31 : 0] one_reg;
  reg [31 : 0] one_new;
  reg [31 : 0] b_one_reg;
  reg [31 : 0] b_one_new;

  reg [12 : 0] loop_counter_reg;
  reg [12 : 0] loop_counter_new;
  reg          loop_counter_we;

  reg [07 : 0] E_word_index;
  reg [04 : 0] E_bit_index;
  reg          last_iteration;

  reg          ei_reg;
  reg          ei_new;
  reg          ei_we;

  reg          exponation_mode_reg;
  reg          exponation_mode_new;
  reg          exponation_mode_we;

  reg [31 : 0] cycle_ctr_low_reg;
  reg [31 : 0] cycle_ctr_low_new;
  reg          cycle_ctr_low_we;
  reg [31 : 0] cycle_ctr_high_reg;
  reg [31 : 0] cycle_ctr_high_new;
  reg          cycle_ctr_high_we;
  reg          cycle_ctr_state_reg;
  reg          cycle_ctr_state_new;
  reg          cycle_ctr_state_we;
  reg          cycle_ctr_start;
  reg          cycle_ctr_stop;


  //----------------------------------------------------------------
  // Wires.
  //----------------------------------------------------------------
  reg [07 : 0]  modulus_mem_int_rd_addr;
  wire [31 : 0] modulus_mem_int_rd_data;

  reg [07 : 0]  message_mem_int_rd_addr;
  wire [31 : 0] message_mem_int_rd_data;

  reg [07 : 0]  exponent_mem_int_rd_addr;
  wire [31 : 0] exponent_mem_int_rd_data;

  reg  [07 : 0] result_mem_int_rd_addr;
  wire [31 : 0] result_mem_int_rd_data;
  reg  [07 : 0] result_mem_int_wr_addr;
  reg  [31 : 0] result_mem_int_wr_data;
  reg           result_mem_int_we;

  reg  [07 : 0] p_mem_rd0_addr;
  wire [31 : 0] p_mem_rd0_data;
  reg  [07 : 0] p_mem_rd1_addr;
  wire [31 : 0] p_mem_rd1_data;
  reg  [07 : 0] p_mem_wr_addr;
  reg  [31 : 0] p_mem_wr_data;
  reg           p_mem_we;

  reg [31 : 0]  tmp_read_data;

  reg           montprod_calc;
  wire          montprod_ready;
  reg [07 : 0]  montprod_length;

  wire [07 : 0] montprod_opa_addr;
  reg [31 : 0]  montprod_opa_data;

  wire [07 : 0] montprod_opb_addr;
  reg [31 : 0]  montprod_opb_data;

  wire [07 : 0] montprod_opm_addr;
  reg [31 : 0]  montprod_opm_data;

  wire [07 : 0] montprod_result_addr;
  wire [31 : 0] montprod_result_data;
  wire          montprod_result_we;

  reg           residue_calculate;
  wire          residue_ready;
  reg [14 : 0]  residue_nn;
  reg  [07 : 0] residue_length;
  wire [07 : 0] residue_opa_rd_addr;
  wire [31 : 0] residue_opa_rd_data;
  wire [07 : 0] residue_opa_wr_addr;
  wire [31 : 0] residue_opa_wr_data;
  wire          residue_opa_wr_we;
  wire [07 : 0] residue_opm_addr;
  reg [31 : 0]  residue_opm_data;

  reg  [07 : 0] residue_mem_montprod_read_addr;
  wire [31 : 0] residue_mem_montprod_read_data;

  reg           residue_valid_reg;
  reg           residue_valid_new;
  reg           residue_valid_int_validated;

  wire [7 : 0]  modulus_length_m1;
  wire [7 : 0]  exponent_length_m1;


  //----------------------------------------------------------------
  // Concurrent connectivity for ports etc.
  //----------------------------------------------------------------
  assign ready  = ready_reg;
  assign cycles = {cycle_ctr_high_reg, cycle_ctr_low_reg};

  assign modulus_length_m1  = modulus_length - 8'h1;
  assign exponent_length_m1 = exponent_length - 8'h1;


  //----------------------------------------------------------------
  // core instantiations.
  //----------------------------------------------------------------
  montprod #(.OPW(OPW), .ADW(ADW))
  montprod_inst(
                .clk(clk),
                .reset_n(reset_n),

                .calculate(montprod_calc),
                .ready(montprod_ready),

                .length(montprod_length),

                .opa_addr(montprod_opa_addr),
                .opa_data(montprod_opa_data),

                .opb_addr(montprod_opb_addr),
                .opb_data(montprod_opb_data),

                .opm_addr(montprod_opm_addr),
                .opm_data(montprod_opm_data),

                .result_addr(montprod_result_addr),
                .result_data(montprod_result_data),
                .result_we(montprod_result_we)
               );


  residue #(.OPW(OPW), .ADW(ADW))
  residue_inst(
               .clk(clk),
               .reset_n(reset_n),
               .calculate(residue_calculate),
               .ready(residue_ready),
               .nn(residue_nn),
               .length(residue_length),
               .opa_rd_addr(residue_opa_rd_addr),
               .opa_rd_data(residue_opa_rd_data),
               .opa_wr_addr(residue_opa_wr_addr),
               .opa_wr_data(residue_opa_wr_data),
               .opa_wr_we(residue_opa_wr_we),
               .opm_addr(residue_opm_addr),
               .opm_data(residue_opm_data)
              );


  blockmem2r1w #(.OPW(OPW), .ADW(ADW))
  residue_mem(
              .clk(clk),
              .read_addr0(residue_opa_rd_addr),
              .read_data0(residue_opa_rd_data),
              .read_addr1(residue_mem_montprod_read_addr),
              .read_data1(residue_mem_montprod_read_data),
              .wr(residue_opa_wr_we),
              .write_addr(residue_opa_wr_addr),
              .write_data(residue_opa_wr_data)
             );


  blockmem2r1w #(.OPW(OPW), .ADW(ADW))
  p_mem(
        .clk(clk),
        .read_addr0(p_mem_rd0_addr),
        .read_data0(p_mem_rd0_data),
        .read_addr1(p_mem_rd1_addr),
        .read_data1(p_mem_rd1_data),
        .wr(p_mem_we),
        .write_addr(p_mem_wr_addr),
        .write_data(p_mem_wr_data)
       );


  blockmem2r1wptr #(.OPW(OPW), .ADW(ADW))
  exponent_mem(
               .clk(clk),
               .reset_n(reset_n),
               .read_addr0(exponent_mem_int_rd_addr),
               .read_data0(exponent_mem_int_rd_data),
               .read_data1(exponent_mem_api_read_data),
               .rst(exponent_mem_api_rst),
               .cs(exponent_mem_api_cs),
               .wr(exponent_mem_api_wr),
               .write_data(exponent_mem_api_write_data)
              );


  blockmem2r1wptr #(.OPW(OPW), .ADW(ADW))
  modulus_mem(
              .clk(clk),
              .reset_n(reset_n),
              .read_addr0(modulus_mem_int_rd_addr),
              .read_data0(modulus_mem_int_rd_data),
              .read_data1(modulus_mem_api_read_data),
              .rst(modulus_mem_api_rst),
              .cs(modulus_mem_api_cs),
              .wr(modulus_mem_api_wr),
              .write_data(modulus_mem_api_write_data)
             );


  blockmem2r1wptr #(.OPW(OPW), .ADW(ADW))
  message_mem(
              .clk(clk),
              .reset_n(reset_n),
              .read_addr0(message_mem_int_rd_addr),
              .read_data0(message_mem_int_rd_data),
              .read_data1(message_mem_api_read_data),
              .rst(message_mem_api_rst),
              .cs(message_mem_api_cs),
              .wr(message_mem_api_wr),
              .write_data(message_mem_api_write_data)
             );


  blockmem2rptr1w #(.OPW(OPW), .ADW(ADW))
  result_mem(
             .clk(clk),
             .reset_n(reset_n),
             .read_addr0(result_mem_int_rd_addr[7 : 0]),
             .read_data0(result_mem_int_rd_data),
             .read_data1(result_mem_api_read_data),
             .rst(result_mem_api_rst),
             .cs(result_mem_api_cs),
             .wr(result_mem_int_we),
             .write_addr(result_mem_int_wr_addr),
             .write_data(result_mem_int_wr_data)
            );


  //----------------------------------------------------------------
  // reg_update
  //
  // Update functionality for all registers in the core.
  // All registers are positive edge triggered with asynchronous
  // active low reset. All registers have write enable.
  //----------------------------------------------------------------
  always @ (posedge clk or negedge reset_n)
    begin
      if (!reset_n)
        begin
          ready_reg           <= 1'b1;
          montprod_select_reg <= MONTPROD_SELECT_ONE_NR;
          montprod_dest_reg   <= MONTPROD_DEST_NOWHERE;
          modexp_ctrl_reg     <= CTRL_IDLE;
          one_reg             <= 32'h0;
          b_one_reg           <= 32'h0;
          loop_counter_reg    <= 13'b0;
          ei_reg              <= 1'b0;
          residue_valid_reg   <= 1'b0;
          exponation_mode_reg <= EXPONATION_MODE_SECRET_SECURE;
          cycle_ctr_low_reg   <= 32'h00000000;
          cycle_ctr_high_reg  <= 32'h00000000;
          cycle_ctr_state_reg <= 1'b0;
        end
      else
        begin
          one_reg             <= one_new;
          b_one_reg           <= b_one_new;
          residue_valid_reg   <= residue_valid_new;

          if (ready_we)
            ready_reg <= ready_new;

          if (montprod_select_we)
            montprod_select_reg <= montprod_select_new;

          if (montprod_dest_we)
            montprod_dest_reg <= montprod_dest_new;

          if (loop_counter_we)
            loop_counter_reg <= loop_counter_new;

          if (ei_we)
            ei_reg <= ei_new;

          if (exponation_mode_we)
            exponation_mode_reg <= exponation_mode_new;

          if (cycle_ctr_low_we)
            cycle_ctr_low_reg <= cycle_ctr_low_new;

          if (cycle_ctr_high_we)
            cycle_ctr_high_reg <= cycle_ctr_high_new;

          if (cycle_ctr_state_we)
            cycle_ctr_state_reg <= cycle_ctr_state_new;

          if (modexp_ctrl_we)
            modexp_ctrl_reg <= modexp_ctrl_new;
        end
    end // reg_update


  //----------------------------------------------------------------
  // cycle_ctr
  //
  // Implementation of the cycle counter
  //----------------------------------------------------------------
  always @*
    begin : cycle_ctr
      cycle_ctr_low_new   = 32'h00000000;
      cycle_ctr_low_we    = 1'b0;
      cycle_ctr_high_new  = 32'h00000000;
      cycle_ctr_high_we   = 1'b0;
      cycle_ctr_state_new = 1'b0;
      cycle_ctr_state_we  = 1'b0;

      if (cycle_ctr_start)
        begin
          cycle_ctr_low_new   = 32'h00000000;
          cycle_ctr_low_we    = 1'b1;
          cycle_ctr_high_new  = 32'h00000000;
          cycle_ctr_high_we   = 1'b1;
          cycle_ctr_state_new = 1'b1;
          cycle_ctr_state_we  = 1'b1;
        end

      if (cycle_ctr_stop)
        begin
          cycle_ctr_state_new = 1'b0;
          cycle_ctr_state_we  = 1'b1;
        end

      if (cycle_ctr_state_reg)
        begin
          cycle_ctr_low_new = cycle_ctr_low_reg + 1'b1;
          cycle_ctr_low_we  = 1'b1;

          if (cycle_ctr_low_new == 32'h00000000)
            begin
              cycle_ctr_high_new  = cycle_ctr_high_reg + 1'b1;
              cycle_ctr_high_we   = 1'b1;
            end
        end
    end // cycle_ctr


  //----------------------------------------------------------------
  // one
  //
  // generates the big integer one ( 00... 01 )
  //----------------------------------------------------------------
  always @*
    begin : one_process
      one_new   = 32'h00000000;
      b_one_new = 32'h00000000;

      if (montprod_opa_addr == modulus_length_m1)
        one_new = 32'h00000001;

      if (montprod_opb_addr == modulus_length_m1)
        b_one_new = 32'h00000001;
    end


  //----------------------------------------------------------------
  // Read mux for modulus. Needed since it is being
  // addressed by two sources.
  //----------------------------------------------------------------
  always @*
    begin : modulus_mem_reader_process
      if (modexp_ctrl_reg == CTRL_RESIDUE)
        modulus_mem_int_rd_addr = residue_opm_addr;
      else
        modulus_mem_int_rd_addr = montprod_opm_addr;
    end


  //----------------------------------------------------------------
  // Feeds residue calculator.
  //----------------------------------------------------------------
  always @*
    begin : residue_process
      //N*2, N=length*32, *32 = shl5, *64 = shl6
      residue_nn = { 1'b0, modulus_length, 6'h0 };
      residue_length = modulus_length;
      residue_opm_data = modulus_mem_int_rd_data;
    end


  //----------------------------------------------------------------
  // Detects if modulus has been updated and we need to
  // recalculate the residue
  // and we need residue is valid or not.
  //----------------------------------------------------------------
  always @*
    begin : residue_valid_process
      residue_valid_new = residue_valid_reg;

      if (modulus_mem_api_cs & modulus_mem_api_wr)
        residue_valid_new = 1'b0;
      else if ( residue_valid_int_validated == 1'b1)
        residue_valid_new = 1'b1;
    end


  //----------------------------------------------------------------
  // montprod_op_select
  //
  // Select operands used during montprod calculations depending
  // on what operation we want to do.
  //----------------------------------------------------------------
  always @*
    begin : montprod_op_select

      montprod_length          = modulus_length;

      result_mem_int_rd_addr   = montprod_opa_addr;
      message_mem_int_rd_addr  = montprod_opa_addr;
      p_mem_rd0_addr           = montprod_opa_addr;

      residue_mem_montprod_read_addr = montprod_opb_addr;
      p_mem_rd1_addr                 = montprod_opb_addr;

      montprod_opm_data = modulus_mem_int_rd_data;
      case (montprod_select_reg)
        MONTPROD_SELECT_ONE_NR:
          begin
            montprod_opa_data  = one_reg;
            montprod_opb_data  = residue_mem_montprod_read_data;
          end

        MONTPROD_SELECT_X_NR:
          begin
            montprod_opa_data  = message_mem_int_rd_data;
            montprod_opb_data  = residue_mem_montprod_read_data;
          end

        MONTPROD_SELECT_Z_P:
          begin
            montprod_opa_data  = result_mem_int_rd_data;
            montprod_opb_data  = p_mem_rd1_data;
          end

        MONTPROD_SELECT_P_P:
          begin
            montprod_opa_data  = p_mem_rd0_data;
            montprod_opb_data  = p_mem_rd1_data;
          end

        MONTPROD_SELECT_Z_ONE:
          begin
            montprod_opa_data  = result_mem_int_rd_data;
            montprod_opb_data  = b_one_reg;
          end

        default:
          begin
            montprod_opa_data  = 32'h00000000;
            montprod_opb_data  = 32'h00000000;
          end
      endcase // case (montprod_selcect_reg)
    end


  //----------------------------------------------------------------
  // memory write mux
  //
  // Direct memory write signals to correct memory.
  //----------------------------------------------------------------
  always @*
    begin : memory_write_process
      result_mem_int_wr_addr = montprod_result_addr;
      result_mem_int_wr_data = montprod_result_data;
      result_mem_int_we      = 1'b0;

      p_mem_wr_addr = montprod_result_addr;
      p_mem_wr_data = montprod_result_data;
      p_mem_we      = 1'b0;

      case (montprod_dest_reg)
        MONTPROD_DEST_Z:
          result_mem_int_we = montprod_result_we;
        MONTPROD_DEST_P:
          p_mem_we = montprod_result_we;
        default:
          begin
          end
      endcase

      // inhibit Z=Z*P when ei = 0
      if (modexp_ctrl_reg == CTRL_ITERATE_Z_P)
        result_mem_int_we = result_mem_int_we & ei_reg;
    end


  //----------------------------------------------------------------
  // loop_counter
  //
  // Calculate the loop counter and related variables.
  //----------------------------------------------------------------
  always @*
    begin : loop_counters_process
      loop_counter_new = 13'b0;
      loop_counter_we  = 1'b0;

      if (loop_counter_reg == {exponent_length_m1, 5'b11111})
        last_iteration = 1'b1;
      else
        last_iteration = 1'b0;

      case (modexp_ctrl_reg)
        CTRL_CALCULATE_P0:
          begin
            loop_counter_new = 13'b0;
            loop_counter_we  = 1'b1;
          end

        CTRL_ITERATE_END:
          begin
            loop_counter_new = loop_counter_reg + 1'b1;
            loop_counter_we  = 1'b1;
          end

        default:
          begin
          end
      endcase
    end


  //----------------------------------------------------------------
  // exponent
  //
  // Reads the exponent.
  //----------------------------------------------------------------
  always @*
    begin : exponent_process
      // Accessing new instead of reg - pick up update at
      // CTRL_ITERATE_NEW to remove a pipeline stall.
      E_word_index  = exponent_length_m1 - loop_counter_new[ 12 : 5 ];

      E_bit_index   = loop_counter_reg[ 04 : 0 ];

      exponent_mem_int_rd_addr = E_word_index;

      ei_new = exponent_mem_int_rd_data[ E_bit_index ];

      if (modexp_ctrl_reg == CTRL_ITERATE)
        ei_we = 1'b1;
      else
        ei_we = 1'b0;
    end


  //----------------------------------------------------------------
  // modexp_ctrl
  //
  // Control FSM logic needed to perform the modexp operation.
  //----------------------------------------------------------------
  always @*
    begin
      ready_new           = 1'b0;
      ready_we            = 1'b0;
      montprod_select_new = MONTPROD_SELECT_ONE_NR;
      montprod_select_we  = 0;
      montprod_dest_new   = MONTPROD_DEST_NOWHERE;
      montprod_dest_we    = 0;
      montprod_calc       = 0;
      modexp_ctrl_new     = CTRL_IDLE;
      modexp_ctrl_we      = 1'b0;
      cycle_ctr_start     = 1'b0;
      cycle_ctr_stop      = 1'b0;

      residue_calculate = 1'b0;

      residue_valid_int_validated = 1'b0;

      case (modexp_ctrl_reg)
        CTRL_IDLE:
          begin
            if (start)
              begin
                ready_new       = 1'b0;
                ready_we        = 1'b1;
                cycle_ctr_start = 1'b1;

                if (residue_valid_reg)
                  begin
                    //residue has alrady been calculated, start with MONTPROD( 1, Nr, MODULUS )
                    montprod_select_new = MONTPROD_SELECT_ONE_NR;
                    montprod_select_we  = 1;
                    montprod_dest_new   = MONTPROD_DEST_Z;
                    montprod_dest_we    = 1;
                    montprod_calc       = 1;
                    modexp_ctrl_new     = CTRL_CALCULATE_Z0;
                    modexp_ctrl_we      = 1;
                  end
                else
                  begin
                    //modulus has been written and residue (Nr) must be calculated
                    modexp_ctrl_new = CTRL_RESIDUE;
                    modexp_ctrl_we  = 1;
                    residue_calculate = 1'b1;
                  end
              end
          end

        CTRL_RESIDUE:
          begin
            if (residue_ready)
              begin
                montprod_select_new = MONTPROD_SELECT_ONE_NR;
                montprod_select_we  = 1;
                montprod_dest_new   = MONTPROD_DEST_Z;
                montprod_dest_we    = 1;
                montprod_calc       = 1;
                modexp_ctrl_new     = CTRL_CALCULATE_Z0;
                modexp_ctrl_we      = 1;
                residue_valid_int_validated = 1'b1; //update registers telling residue is valid
              end
          end

        CTRL_CALCULATE_Z0:
          begin
            if (montprod_ready)
              begin
                montprod_select_new = MONTPROD_SELECT_X_NR;
                montprod_select_we  = 1;
                montprod_dest_new   = MONTPROD_DEST_P;
                montprod_dest_we    = 1;
                montprod_calc       = 1;
                modexp_ctrl_new = CTRL_CALCULATE_P0;
                modexp_ctrl_we  = 1;
              end
          end

        CTRL_CALCULATE_P0:
          begin
            if (montprod_ready == 1'b1)
              begin
                modexp_ctrl_new = CTRL_ITERATE;
                modexp_ctrl_we  = 1;
              end
          end

        CTRL_ITERATE:
          begin
            montprod_select_new = MONTPROD_SELECT_Z_P;
            montprod_select_we  = 1;
            montprod_dest_new   = MONTPROD_DEST_Z;
            montprod_dest_we    = 1;
            montprod_calc       = 1;
            modexp_ctrl_new     = CTRL_ITERATE_Z_P;
            modexp_ctrl_we      = 1;

            if (ei_new == 1'b0 && exponation_mode_reg == EXPONATION_MODE_PUBLIC_FAST)
              begin
                //Skip the fake montgomery calculation, exponation_mode_reg optimizing for speed not blinding.
                montprod_select_new = MONTPROD_SELECT_P_P;
                montprod_dest_new   = MONTPROD_DEST_P;
                modexp_ctrl_new     = CTRL_ITERATE_P_P;
              end
          end

        CTRL_ITERATE_Z_P:
            if (montprod_ready)
              begin
                montprod_select_new = MONTPROD_SELECT_P_P;
                montprod_select_we  = 1;
                montprod_dest_new   = MONTPROD_DEST_P;
                montprod_dest_we    = 1;
                montprod_calc       = 1;
                modexp_ctrl_new     = CTRL_ITERATE_P_P;
                modexp_ctrl_we      = 1;
              end

        CTRL_ITERATE_P_P:
            if (montprod_ready == 1'b1)
              begin
                modexp_ctrl_new = CTRL_ITERATE_END;
                modexp_ctrl_we  = 1;
              end

        CTRL_ITERATE_END:
          begin
            if (!last_iteration)
              begin
                modexp_ctrl_new = CTRL_ITERATE;
                modexp_ctrl_we  = 1;
              end
            else
              begin
                montprod_select_new = MONTPROD_SELECT_Z_ONE;
                montprod_select_we  = 1;
                montprod_dest_new   = MONTPROD_DEST_Z;
                montprod_dest_we    = 1;
                montprod_calc       = 1;
                modexp_ctrl_new     = CTRL_CALCULATE_ZN;
                modexp_ctrl_we      = 1;
              end
          end

        CTRL_CALCULATE_ZN:
          begin
            if (montprod_ready)
              begin
                modexp_ctrl_new = CTRL_DONE;
                modexp_ctrl_we  = 1;
              end
          end

        CTRL_DONE:
          begin
            cycle_ctr_stop  = 1'b1;
            ready_new       = 1'b1;
            ready_we        = 1'b1;
            modexp_ctrl_new = CTRL_IDLE;
            modexp_ctrl_we  = 1;
          end

        default:
          begin
          end

      endcase // case (modexp_ctrl_reg)
    end

endmodule // modexp_core

//======================================================================
// EOF modexp_core.v
//======================================================================