logic-circuit-design / practice / 06_Caluculator / rtl / CALUCULATOR_TOP.v
CALUCULATOR_TOP.v
Raw
// --------------------------------------------------------------------
//
// Major Functions:	CALUCULATOR_TOP
//
// --------------------------------------------------------------------
//
// Revision History :
// --------------------------------------------------------------------
//   Ver  :| Author                    :| Mod. Date :| Changes Made:
//   V1.0 :| Koh Johguchi              :| 12/17/19  :| Shinshu Univ.
// --------------------------------------------------------------------

module CALUCULATOR_TOP(
   iSTART,   //press the button for transmitting instrucions to device;
   iRST_n,   //FSM reset signal;
   iCLK_50,  //clock source;
   PS2_CLK,  //ps2_clock signal inout;
   PS2_DAT,  //ps2_data  signal inout;
   oLEFBUT,  //left button press display;
   oRIGBUT,  //right button press display;
   oMIDBUT,  //middle button press display;
   oX_MOV1,  //lower  SEG of mouse displacement display for X axis.
   oX_MOV2,  //higher SEG of mouse displacement display for X axis.
   oY_MOV1,  //lower  SEG of mouse displacement display for Y axis.
   oY_MOV2,  //higher SEG of mouse displacement display for Y axis.
   oZ_MOV1,  //lower  SEG of mouse displacement display for Z axis.
   oZ_MOV2   //higher SEG of mouse displacement display for Z axis.
);

//=======================================================
//  PORT declarations
//=======================================================

input iSTART;
input iRST_n;
input iCLK_50;

inout PS2_CLK;
inout PS2_DAT;

output oLEFBUT;
output oRIGBUT;
output oMIDBUT;
output [6:0]  oX_MOV1;
output [6:0]  oX_MOV2;
output [6:0]  oY_MOV1;
output [6:0]  oY_MOV2;
output [6:0]  oZ_MOV1;
output [6:0]  oZ_MOV2;

wire          PS2_READY;
wire   [15:0] SCANCODE;

// すべてのlatchを統合し扱いやすくする
// そのため[xyz]_latchをwireに変更
reg    [23:0] latches;
wire   [7:0]  x_latch, y_latch, z_latch;
assign        x_latch = latches[7:0];
assign        y_latch = latches[15:8];
assign        z_latch = latches[23:16];

wire          clk;

// 計算機で使用する変数定義
// 計算機のステータス 0=operand0, 1=operand1, 2=response
reg    [1:0]  status;
// 演算対象とその符号
reg    [19:0] operand_0, operand_1;
reg           operand_sign_0, operand_sign_1;
// 演算子
reg    [3:0]  operator;
// 演算子入力可能, イコール入力可能, エラー発生のフラグ
reg           operator_allow, enter_allow, error_occurred;
// 演算結果の関数の戻り値用変数(sign,value)
reg    [24:0] result_recv;
// 演算結果と割り算時の余り, その符号
reg    [23:0] result, remainder;
reg           result_sign;
// キーコード変換関数の戻り値用変数
reg    [3:0]  oNUM;

// キーコードを数値に置き換える関数
// 使用していない領域で記号もincrementな数値に変換
function [3:0] CODE2NUM;
   input [15:0] CODE;
   case(CODE)
      16'hf070: CODE2NUM = 4'd0;
      16'hf069: CODE2NUM = 4'd1;
      16'hf072: CODE2NUM = 4'd2;
      16'hf07a: CODE2NUM = 4'd3;
      16'hf06b: CODE2NUM = 4'd4;
      16'hf073: CODE2NUM = 4'd5;
      16'hf074: CODE2NUM = 4'd6;
      16'hf06c: CODE2NUM = 4'd7;
      16'hf075: CODE2NUM = 4'd8;
      16'hf07d: CODE2NUM = 4'd9;
      16'hf079: CODE2NUM = 4'd10; // +
      16'hf07b: CODE2NUM = 4'd11; // -
      16'hf07c: CODE2NUM = 4'd12; // *
      16'hf04a: CODE2NUM = 4'd13; // /
      16'hf05a: CODE2NUM = 4'd14; // enter
      default: CODE2NUM = 4'd15;
   endcase
endfunction

// 数値をlatchに変換する関数
function [23:0] NUM2LATCHES;
   input [23:0] NUM;
   input        SIGN;
   // オーバーフロー確認
   if (NUM > 24'd999999) begin
      NUM2LATCHES = 24'hfedd00;
   // マイナス時のオーバーフロー確認
   end else if (SIGN == 1 && NUM > 24'd99999) begin
      NUM2LATCHES = 24'hfedd00;
   end else begin
      // 数値を変換する
      NUM2LATCHES[3:0] = NUM % 4'd10;
      NUM2LATCHES[7:4] = (NUM / 4'd10) % 4'd10;
      NUM2LATCHES[11:8] = (NUM / 8'd100) % 4'd10;
      NUM2LATCHES[15:12] = (NUM / 12'd1000) % 4'd10;
      NUM2LATCHES[19:16] = (NUM / 16'd10000) % 4'd10;
      NUM2LATCHES[23:20] = (NUM / 20'd100000) % 4'd10;
      // 先頭の0をblankに置き換え
      if (NUM2LATCHES[23:20] == 0) NUM2LATCHES[23:20] = 4'hf;
      if (NUM2LATCHES[19:16] == 0 && NUM2LATCHES[23:20] == 4'hf) NUM2LATCHES[19:16] = 4'hf;
      if (NUM2LATCHES[15:12] == 0 && NUM2LATCHES[19:16] == 4'hf) NUM2LATCHES[15:12] = 4'hf;
      if (NUM2LATCHES[11:8] == 0 && NUM2LATCHES[15:12] == 4'hf) NUM2LATCHES[11:8] = 4'hf;
      if (NUM2LATCHES[7:4] == 0 && NUM2LATCHES[11:8] == 4'hf) NUM2LATCHES[7:4] = 4'hf;
      // マイナスの場合頭にハイフンをつける
      if (SIGN == 1) begin
         if (NUM2LATCHES[7:4] == 4'hf) NUM2LATCHES[7:4] = 4'ha;
         else if (NUM2LATCHES[11:8] == 4'hf) NUM2LATCHES[11:8] = 4'ha;
         else if (NUM2LATCHES[15:12] == 4'hf) NUM2LATCHES[15:12] = 4'ha;
         else if (NUM2LATCHES[19:16] == 4'hf) NUM2LATCHES[19:16] = 4'ha;
         else if (NUM2LATCHES[23:20] == 4'hf) NUM2LATCHES[23:20] = 4'ha;
      end
   end
endfunction

// 複数の数値をlatchに変換する関数
// 割り算の商と余りを表示する用 xxx_yyyのようにアンダーバーでsepする
function [23:0] MNUM2LATCHES;
   input [23:0] NUM0;
   input        SIGN;
   input [23:0] NUM1;
   reg [23:0] num0_latches, num1_latches;
   reg [51:0] combined_latches;
   // それぞれの数値をlatchに変換
   num0_latches = NUM2LATCHES(NUM0, SIGN);
   num1_latches = NUM2LATCHES(NUM1, 0);
   // 余りの先端blankの数によって結合する場所を場合分け
   if (num1_latches[23:20] != 4'hf) begin
      combined_latches = {num0_latches, 4'hb, num1_latches[23:0]};
   end else if (num1_latches[19:16] != 4'hf) begin
      combined_latches = {4'hf, num0_latches, 4'hb, num1_latches[19:0]};
   end else if (num1_latches[15:12] != 4'hf) begin
      combined_latches = {8'hff, num0_latches, 4'hb, num1_latches[15:0]};
   end else if (num1_latches[11:8] != 4'hf) begin
      combined_latches = {12'hfff, num0_latches, 4'hb, num1_latches[11:0]};
   end else if (num1_latches[7:4] != 4'hf) begin
      combined_latches = {16'hffff, num0_latches, 4'hb, num1_latches[7:0]};
   end else begin
      combined_latches = {20'hfffff, num0_latches, 4'hb, num1_latches[3:0]};
   end
   // オーバーフロー確認
   if (combined_latches < 52'hfffffff000000) combined_latches = 52'hfedd00;
   MNUM2LATCHES = combined_latches[23:0];
endfunction

// 符号に対応した加算関数
function [24:0] ADDITION;
   input [19:0] OP0;
   input        OPS0;
   input [19:0] OP1;
   input        OPS1;
   reg   [23:0] RES;
   // 符号によって場合分け
   if (OPS0 == OPS1) begin
      RES = OP0 + OP1;
      ADDITION = {OPS0, RES};
   end else if (OP0 > OP1) begin
      RES = OP0 - OP1;
      ADDITION = {OPS0, RES};
   end else if (OP0 == OP1) begin
      RES = OP0 - OP1;
      ADDITION = {1'b0, RES};
   end else begin
      RES = OP1 - OP0;
      ADDITION = {OPS1, RES};
   end
endfunction

// 符号に対応した乗算関数
function [24:0] MULTIPLICATION;
   input [19:0] OP0;
   input        OPS0;
   input [19:0] OP1;
   input        OPS1;
   reg [39:0]   RES;
   RES = OP0 * OP1;
   if (RES > 40'hffffff) RES = 40'hffffff;
   if (RES == 0) begin
      MULTIPLICATION = {1'b0, 24'h000000};
   end else begin
      MULTIPLICATION = {OPS0 ^ OPS1, RES[23:0]};
   end
endfunction

always @ (posedge clk or negedge iRST_n)
begin
   if ( !iRST_n ) begin
      // 初期化
      latches <= 24'hffffff;
      {
         status, operand_0, operand_1, operand_sign_0, operand_sign_1,
         operator, operator_allow, enter_allow, error_occurred,
         result, remainder, result_sign
      } <= 0;
   end else if ( !PS2_READY ) begin
      // キーコードを数値に変換
      oNUM = CODE2NUM(SCANCODE);
      // キーによって処理の場合分け
      // 押されたキーが数値のとき
      if (oNUM <= 4'd9) begin
         case(status)
            2'd0: begin
               if ((operand_sign_0 == 0 && operand_0 * 4'd10 + oNUM <= 24'd999999) || (operand_sign_0 == 1 && operand_0 * 4'd10 + oNUM <= 24'd99999)) begin
                  operand_0 = operand_0 * 4'd10 + oNUM;
                  operator_allow = 1;
                  latches = NUM2LATCHES(operand_0, operand_sign_0);
               end
            end
            2'd1: begin
               if ((operand_sign_1 == 0 && operand_1 * 4'd10 + oNUM <= 24'd999999) || (operand_sign_1 == 1 && operand_1 * 4'd10 + oNUM <= 24'd99999)) begin
                  operand_1 = operand_1 * 4'd10 + oNUM;
                  latches = NUM2LATCHES(operand_1, operand_sign_1);
                  enter_allow = 1;
               end
            end
         endcase
      end else begin
         // ハイフン(マイナス)かつ演算子入力でないとき
         if (!operator_allow && !enter_allow && oNUM == 4'd11) begin
            case(status)
               2'd0: begin
                  operand_sign_0 = ~operand_sign_0;
                  latches = {20'hfffff, (operand_sign_0 == 1) ? 4'ha : 4'hf};
               end
               2'd1: begin
                  operand_sign_1 = ~operand_sign_1;
                  latches = {20'hfffff, (operand_sign_1 == 1) ? 4'ha : 4'hf};
               end
            endcase
         // operator_allow時の演算子入力
         end else if (status == 2'd0 && operator_allow && oNUM <= 4'd13) begin
            operator = oNUM;
            operator_allow = 0;
            status = 2'd1;
            latches = 24'hffffff;
         // enter_allow時の演算子入力イコール入力(演算実行)
         end else if (status == 2'd1 && enter_allow && oNUM <= 4'd14) begin
            // 演算子によって場合分け
            case(operator)
               4'd10: begin // +
                  result_recv = ADDITION(operand_0, operand_sign_0, operand_1, operand_sign_1);
                  result = result_recv[23:0];
                  result_sign = result_recv[24];
               end
               4'd11: begin // -
                  result_recv = ADDITION(operand_0, operand_sign_0, operand_1, operand_sign_1 ^ 1);
                  result = result_recv[23:0];
                  result_sign = result_recv[24];
               end
               4'd12: begin // *
                  result_recv = MULTIPLICATION(operand_0, operand_sign_0, operand_1, operand_sign_1);
                  result = result_recv[23:0];
                  result_sign = result_recv[24];
               end
               4'd13: begin // /
                  // ゼロ除算エラーを検知
                  if (operand_1 == 0) begin
                     error_occurred = 1;
                  end else begin
                     result = operand_0 / operand_1;
                     result_sign = operand_sign_0 ^ operand_sign_1;
                     remainder = operand_0 % operand_1;
                     if (operand_sign_0 == 1 && remainder != 0) begin
                        result = result + 1'b1;
                        remainder = operand_1 - remainder;
                     end
                     if (result == 24'd0) begin
                        result_sign = 0;
                     end
                  end
               end
            endcase
            // ゼロ除算エラーが発生した場合
            if (error_occurred == 1) begin
               latches = 24'hfedd01;
            // 割り算の場合
            // 余りが0のときのみcontinuous-calcを可能に
            end else if (operator == 4'd13) begin
               latches = MNUM2LATCHES(result, result_sign, remainder);
               if (remainder != 0) begin
                  status = 2'd2;
               end
            end else begin
               latches = NUM2LATCHES(result, result_sign);
            end
            // エラーコードのときリザルト画面に
            if (latches[23:8] == 16'hfedd) status = 2'd2;
            // continuous-calcへの対応
            else if (status == 2'd1) begin
               // enter
               if (oNUM == 4'd14) begin
                  status = 2'd0;
                  operator = 0;
                  operator_allow = 1;
               // 演算子
               end else begin
                  status = 2'd1;
                  operator = oNUM;
                  operator_allow = 0;
               end
               operand_0 = result[19:0];
               operand_sign_0 = result_sign;
               operand_1 = 0;
               operand_sign_1 = 0;
            end
            enter_allow = 0;
         // テンキーのDel入力(クリア)
         end else if (SCANCODE == 16'hf071) begin
            case(status)
               2'd0: begin
                  operand_0 = 0;
                  operand_sign_0 = 0;
                  latches = NUM2LATCHES(operand_0, operand_sign_0);
                  operator_allow = 0;
                  latches = 24'hffffff;
               end
               2'd1: begin
                  operand_1 = 0;
                  operand_sign_1 = 0;
                  latches = NUM2LATCHES(operand_1, operand_sign_1);
                  enter_allow = 0;
                  latches = 24'hffffff;
               end
            endcase
         // esc/テンキーのnumlock入力(オールクリア)
         end else if (SCANCODE == 16'hf076 || SCANCODE == 16'hf077) begin
            latches <= 24'hffffff;
            {
               status, operand_0, operand_1, operand_sign_0, operand_sign_1,
               operator, operator_allow, enter_allow, error_occurred,
               result, remainder, result_sign
            } <= 0;
         end
      end
   end else begin
      latches <= latches;
   end
end

PS2 U0(
   .iSTART(iSTART),       //press the button for transmitting instrucions to device;
   .iRST_n(iRST_n),       //FSM reset signal;
   .iCLK(clk),            //clock source;
   .PS2_CLK(PS2_CLK),     //ps2_clock signal inout;
   .PS2_DAT(PS2_DAT),     //ps2_data  signal inout;
   .oLEFBUT(oLEFBUT),     //left button press display;
   .oRIGBUT(oRIGBUT),     //right button press display;
   .oMIDBUT(oMIDBUT),     //middle button press display;
   .PS2_READY(PS2_READY), //ready signal for PS2;
   .SCANCODE(SCANCODE)    //scan-code for PS2;
);

CLKDIV U1(
   .iCLK(iCLK_50),        //clock source;
   .RSTB(iRST_n),         //reset signal;
   .oCLK(clk)             //clock signal out;
);

//instantiation
SEG7_LUT U2(.oSEG(oX_MOV1),.iDIG(x_latch[3:0]));
SEG7_LUT U3(.oSEG(oX_MOV2),.iDIG(x_latch[7:4]));
SEG7_LUT U4(.oSEG(oY_MOV1),.iDIG(y_latch[3:0]));
SEG7_LUT U5(.oSEG(oY_MOV2),.iDIG(y_latch[7:4]));
SEG7_LUT U6(.oSEG(oZ_MOV1),.iDIG(z_latch[3:0]));
SEG7_LUT U7(.oSEG(oZ_MOV2),.iDIG(z_latch[7:4]));

endmodule