//   ==================================================================
//   >>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
//   ------------------------------------------------------------------
//   Copyright (c) 2006-2011 by Lattice Semiconductor Corporation
//   ALL RIGHTS RESERVED
//   ------------------------------------------------------------------
//
//   IMPORTANT: THIS FILE IS AUTO-GENERATED BY THE LATTICEMICO SYSTEM.
//
//   Permission:
//
//      Lattice Semiconductor grants permission to use this code
//      pursuant to the terms of the Lattice Semiconductor Corporation
//      Open Source License Agreement.
//
//   Disclaimer:
//
//      Lattice Semiconductor provides no warranty regarding the use or
//      functionality of this code. It is the user's responsibility to
//      verify the user's design for consistency and functionality through
//      the use of formal verification methods.
//
//   --------------------------------------------------------------------
//
//                  Lattice Semiconductor Corporation
//                  5555 NE Moore Court
//                  Hillsboro, OR 97214
//                  U.S.A
//
//                  TEL: 1-800-Lattice (USA and Canada)
//                         503-286-8001 (other locations)
//
//                  web: http://www.latticesemi.com/
//                  email: techsupport@latticesemi.com
//
//   --------------------------------------------------------------------
//                         FILE DETAILS
// Project          : LatticeMico32
// File             : lm_mc_arithmetic.v
// Title            : Multi-cycle arithmetic unit.
// Dependencies     : lm32_include.v
// Version          : 6.1.17
//                  : Initial Release
// Version          : 7.0SP2, 3.0
//                  : No Change
// Version          : 3.1
//                  : No Change
// =============================================================================

`include "lm32_include.v"

`define LM32_MC_STATE_RNG         2:0
`define LM32_MC_STATE_IDLE        3'b000
`define LM32_MC_STATE_MULTIPLY    3'b001
`define LM32_MC_STATE_MODULUS     3'b010
`define LM32_MC_STATE_DIVIDE      3'b011
`define LM32_MC_STATE_SHIFT_LEFT  3'b100
`define LM32_MC_STATE_SHIFT_RIGHT 3'b101

/////////////////////////////////////////////////////
// Module interface
/////////////////////////////////////////////////////

module lm32_mc_arithmetic (
    // ----- Inputs -----
    clk_i,
    rst_i,
    stall_d,
    kill_x,
`ifdef CFG_MC_DIVIDE_ENABLED
    divide_d,
    modulus_d,
`endif
`ifdef CFG_MC_MULTIPLY_ENABLED
    multiply_d,
`endif
`ifdef CFG_MC_BARREL_SHIFT_ENABLED
    shift_left_d,
    shift_right_d,
    sign_extend_d,
`endif
    operand_0_d,
    operand_1_d,
    // ----- Ouputs -----
    result_x,
`ifdef CFG_MC_DIVIDE_ENABLED
    divide_by_zero_x,
`endif
    stall_request_x
    );

/////////////////////////////////////////////////////
// Inputs
/////////////////////////////////////////////////////

input clk_i;                                    // Clock
input rst_i;                                    // Reset
input stall_d;                                  // Stall instruction in D stage
input kill_x;                                   // Kill instruction in X stage
`ifdef CFG_MC_DIVIDE_ENABLED
input divide_d;                                 // Perform divide
input modulus_d;                                // Perform modulus
`endif
`ifdef CFG_MC_MULTIPLY_ENABLED
input multiply_d;                               // Perform multiply
`endif
`ifdef CFG_MC_BARREL_SHIFT_ENABLED
input shift_left_d;                             // Perform left shift
input shift_right_d;                            // Perform right shift
input sign_extend_d;                            // Whether to sign-extend (arithmetic) or zero-extend (logical)
`endif
input [`LM32_WORD_RNG] operand_0_d;
input [`LM32_WORD_RNG] operand_1_d;

/////////////////////////////////////////////////////
// Outputs
/////////////////////////////////////////////////////

output [`LM32_WORD_RNG] result_x;               // Result of operation
reg    [`LM32_WORD_RNG] result_x;
`ifdef CFG_MC_DIVIDE_ENABLED
output divide_by_zero_x;                        // A divide by zero was attempted
reg    divide_by_zero_x;
`endif
output stall_request_x;                         // Request to stall pipeline from X stage back
wire   stall_request_x;

/////////////////////////////////////////////////////
// Internal nets and registers
/////////////////////////////////////////////////////

reg [`LM32_WORD_RNG] p;                         // Temporary registers
reg [`LM32_WORD_RNG] a;
reg [`LM32_WORD_RNG] b;
`ifdef CFG_MC_DIVIDE_ENABLED
wire [32:0] t;
`endif

reg [`LM32_MC_STATE_RNG] state;                 // Current state of FSM
reg [5:0] cycles;                               // Number of cycles remaining in the operation

`ifdef CFG_MC_BARREL_SHIFT_ENABLED
reg sign_extend_x;                              // Whether to sign extend of zero extend right shifts
wire fill_value;                                // Value to fill with for right barrel-shifts
`endif

/////////////////////////////////////////////////////
// Combinational logic
/////////////////////////////////////////////////////

// Stall pipeline while any operation is being performed
assign stall_request_x = state != `LM32_MC_STATE_IDLE;

`ifdef CFG_MC_DIVIDE_ENABLED
// Subtraction
assign t = {p[`LM32_WORD_WIDTH-2:0], a[`LM32_WORD_WIDTH-1]} - b;
`endif

`ifdef CFG_MC_BARREL_SHIFT_ENABLED
// Determine fill value for right shift - Sign bit for arithmetic shift, or zero for logical shift
assign fill_value = (sign_extend_x == `TRUE) & b[`LM32_WORD_WIDTH-1];
`endif

/////////////////////////////////////////////////////
// Sequential logic
/////////////////////////////////////////////////////

// Perform right shift
always @(posedge clk_i `CFG_RESET_SENSITIVITY)
begin
    if (rst_i == `TRUE)
    begin
        cycles <= {6{1'b0}};
        p <= {`LM32_WORD_WIDTH{1'b0}};
        a <= {`LM32_WORD_WIDTH{1'b0}};
        b <= {`LM32_WORD_WIDTH{1'b0}};
`ifdef CFG_MC_BARREL_SHIFT_ENABLED
        sign_extend_x <= 1'b0;
`endif
`ifdef CFG_MC_DIVIDE_ENABLED
        divide_by_zero_x <= `FALSE;
`endif
        result_x <= {`LM32_WORD_WIDTH{1'b0}};
        state <= `LM32_MC_STATE_IDLE;
    end
    else
    begin
`ifdef CFG_MC_DIVIDE_ENABLED
        divide_by_zero_x <= `FALSE;
`endif
        case (state)
        `LM32_MC_STATE_IDLE:
        begin
            if (stall_d == `FALSE)
            begin
                cycles <= `LM32_WORD_WIDTH;
                p <= 32'b0;
                a <= operand_0_d;
                b <= operand_1_d;
`ifdef CFG_MC_DIVIDE_ENABLED
                if (divide_d == `TRUE)
                    state <= `LM32_MC_STATE_DIVIDE;
                if (modulus_d == `TRUE)
                    state <= `LM32_MC_STATE_MODULUS;
`endif
`ifdef CFG_MC_MULTIPLY_ENABLED
                if (multiply_d == `TRUE)
                    state <= `LM32_MC_STATE_MULTIPLY;
`endif
`ifdef CFG_MC_BARREL_SHIFT_ENABLED
                if (shift_left_d == `TRUE)
                begin
                    state <= `LM32_MC_STATE_SHIFT_LEFT;
                    sign_extend_x <= sign_extend_d;
                    cycles <= operand_1_d[4:0];
                    a <= operand_0_d;
                    b <= operand_0_d;
                end
                if (shift_right_d == `TRUE)
                begin
                    state <= `LM32_MC_STATE_SHIFT_RIGHT;
                    sign_extend_x <= sign_extend_d;
                    cycles <= operand_1_d[4:0];
                    a <= operand_0_d;
                    b <= operand_0_d;
                end
`endif
            end
        end
`ifdef CFG_MC_DIVIDE_ENABLED
        `LM32_MC_STATE_DIVIDE:
        begin
            if (t[32] == 1'b0)
            begin
                p <= t[31:0];
                a <= {a[`LM32_WORD_WIDTH-2:0], 1'b1};
            end
            else
            begin
                p <= {p[`LM32_WORD_WIDTH-2:0], a[`LM32_WORD_WIDTH-1]};
                a <= {a[`LM32_WORD_WIDTH-2:0], 1'b0};
            end
            result_x <= a;
            if ((cycles == `LM32_WORD_WIDTH'd0) || (kill_x == `TRUE))
            begin
                // Check for divide by zero
                divide_by_zero_x <= b == {`LM32_WORD_WIDTH{1'b0}};
                state <= `LM32_MC_STATE_IDLE;
            end
            cycles <= cycles - 1'b1;
        end
        `LM32_MC_STATE_MODULUS:
        begin
            if (t[32] == 1'b0)
            begin
                p <= t[31:0];
                a <= {a[`LM32_WORD_WIDTH-2:0], 1'b1};
            end
            else
            begin
                p <= {p[`LM32_WORD_WIDTH-2:0], a[`LM32_WORD_WIDTH-1]};
                a <= {a[`LM32_WORD_WIDTH-2:0], 1'b0};
            end
            result_x <= p;
            if ((cycles == `LM32_WORD_WIDTH'd0) || (kill_x == `TRUE))
            begin
                // Check for divide by zero
                divide_by_zero_x <= b == {`LM32_WORD_WIDTH{1'b0}};
                state <= `LM32_MC_STATE_IDLE;
            end
            cycles <= cycles - 1'b1;
        end
`endif
`ifdef CFG_MC_MULTIPLY_ENABLED
        `LM32_MC_STATE_MULTIPLY:
        begin
            if (b[0] == 1'b1)
                p <= p + a;
            b <= {1'b0, b[`LM32_WORD_WIDTH-1:1]};
            a <= {a[`LM32_WORD_WIDTH-2:0], 1'b0};
            result_x <= p;
            if ((cycles == `LM32_WORD_WIDTH'd0) || (kill_x == `TRUE))
                state <= `LM32_MC_STATE_IDLE;
            cycles <= cycles - 1'b1;
        end
`endif
`ifdef CFG_MC_BARREL_SHIFT_ENABLED
        `LM32_MC_STATE_SHIFT_LEFT:
        begin
            a <= {a[`LM32_WORD_WIDTH-2:0], 1'b0};
            result_x <= a;
            if ((cycles == `LM32_WORD_WIDTH'd0) || (kill_x == `TRUE))
                state <= `LM32_MC_STATE_IDLE;
            cycles <= cycles - 1'b1;
        end
        `LM32_MC_STATE_SHIFT_RIGHT:
        begin
            b <= {fill_value, b[`LM32_WORD_WIDTH-1:1]};
            result_x <= b;
            if ((cycles == `LM32_WORD_WIDTH'd0) || (kill_x == `TRUE))
                state <= `LM32_MC_STATE_IDLE;
            cycles <= cycles - 1'b1;
        end
`endif
        endcase
    end
end

endmodule
