シングルサイクルRISCプロセッサの設計「発展編」
(実験8まで早く終わった人にお勧めの実験)
発展編・実験9: 整数乗算命令multの追加

実験の概要

本実験では、整数乗算命令multならびにムーブ・フロム・Lo命令mfloのためのbin2vの拡張、プロセッサ(cpu.v,main_ctrl.v,alu_ctrler.v)の拡張、動作実験を行う。
  1. 整数乗算命令multならびにムーブ・フロム・Lo命令mfloのためのbin2vの拡張
  2. プロセッサ(cpu.v,main_ctrl.v,alu_ctrler.v)の拡張
  3. 動作実験


なお、本実験で使用するbin2vのソースコードbin2v.tar.gzは、下記のURLからダウンロードできる。

bin2vのソースファイル:
http://www.ice.nuie.nagoya-u.ac.jp/jikken/hard/j2hard-mips/k09_mult/bin2v.tar.gz

1. bin2vの拡張

本実験では、整数乗算命令multのためのbin2vの拡張を行う。

また、乗算結果が格納される64-bitレジスタ{Hi, Lo}の下位32-bit Loをレジスタに転送する、ムーブ・フロム・Lo命令mfloについての拡張も同時に行う。

本実験では、bin2vのC記述bin2v.tar.gz(ダウンロード)を使用する。

bin2v.tar.gzを図1のように展開し、bin2vのソースbin2v.cとMakefileを得る。

tar xvfz ./bin2v.tar.gz
図 1: tar を使った展開


整数乗算命令 mult とムーブ・フロム・Lo 命令 mflo に関する拡張を行う。 bin2v.c に図 2 に示すようななコメントを追加する(実験 9 のヒント(1)から(3))。

/*
実験 9 ヒント(1):整数乗算命令 mult, ムーブ・フロム・Lo 命令 mflo についてのコメント追加
`define      R  6'b000000    R 形式 (add, addu, sub, subu, and, or, slt, jalr, jr, mult, mflo)
*/

/*
実験 9 ヒント(2):整数乗算命令 mult についてのコメント追加
MULT(op = 000000, func = 011000)
MULT      {Hi, Lo} <= REG[rs] * REG[rt];                MULT rs,rt
*/

/*
実験 9 ヒント(3):ムーブ・フロム・Lo 命令 mflo についてのコメント追加
MFLO(op = 000000, func = 010010)
MFLO      REG[rd]  <= Lo;                               MFLO rd
*/
	    
図 2: bin2v.cに追加するコメント


追加したコメントを参考に、MFLO 命令の func コードに関する define を追加する(実験 9 のヒント(4))。

追加したコメントを参考に、rom8x1024_sim.v 生成用の MULT 命令に関する記述を変更する(実験 9 のヒント(5))。
追加したコメントを参考に、rom8x1024_sim.v 生成用の MFLO 命令に関する記述を追加する(実験 9 のヒント(6))。

追加したコメントを参考に、rom8x1024_DE2.mif 生成用の MULT 命令に関する記述を変更する(実験 9 のヒント(7))。
追加したコメントを参考に、rom8x1024_DE2.mif 生成用の MFLO 命令に関する記述を追加する(実験 9 のヒント(8))。

bin2v.c を図 2 のように make し、mult, mflo 命令に対応した bin2v を得る。

make
図 2: make を使った bin2v.c のコンパイル


2. 整数乗算命令 mult のためのプロセッサの拡張

本実験では、実験 6-2 で完成させたプロセッサの Verilog HDL 記述 cpu.v, alu.v, main_ctrl.v, alu_ctrler.v について追加設計を行う。

整数乗算命令 mult ならびにムーブ・フロム・Lo 命令 mflo に関する拡張を行う。
cpu.v に図 3 のようなコメントの変更を行う。

// 実験 9 のヒント(1):ALU モジュールの実体化に関する記述の変更
//      clock, reset 信号の追加、乗算結果を保持するレジスタ hi と lo 用
// alu
//                  +----+
//     alu_a[31:0]->|    |
//     alu_b[31:0]->|    |->alu_y[31:0]
//   alu_ctrl[3:0]->|    |->alu_comp[1:0]
//                  +----+
alu  alua(alu_a, alu_b, alu_ctrl, alu_y, alu_comp);
を
// 実験 9 のヒント(1):ALU モジュールの実体化に関する記述の変更
//      clock, reset 信号の追加、乗算結果を保持するレジスタ hi と lo 用
// alu
//                  +----+
//           clock->|    |
//           reset->|    |
//     alu_a[31:0]->|    |
//     alu_b[31:0]->|    |->alu_y[31:0]
//   alu_ctrl[3:0]->|    |->alu_comp[1:0]
//                  +----+
alu  alua(clock, reset, alu_a, alu_b, alu_ctrl, alu_y, alu_comp);
に変更	    
図 3: cpu.v の変更


追加したコメントを参考に、図 4 のような alu.v の変更を行う(実験 9 のヒント(2)から(7))。

// 実験 9 のヒント(2):コメントの追加(1)
// mult(multiply)
// mflo(move from Lo)

// 実験 9 のヒント(3):コメントの追加(2)
// 1011,           mult
// 1100,           mflo

// 実験 9 のヒント(4):mult, mflo 用の ALU 制御コードの define
`define     ALU_MULT 4'b1011
`define     ALU_MFLO 4'b1100

// 実験 9 のヒント(5):ALU モジュールの入力ポートの拡張
//              clock, reset 信号の追加、乗算結果を保持するレジスタ hi と lo 用
module alu (alu_a, alu_b, alu_ctrl, alu_y, alu_comp);  // 入出力ポート
を
module alu (clock, reset, alu_a, alu_b, alu_ctrl, alu_y, alu_comp);  // 入出力ポート
に変更

// 実験 9 のヒント(6):mult 命令実行時に alu_a * alu_b の結果を {hi, lo} に格納する記述の追加
input       clock, reset;   // 入力 クロック, リセット
reg    [31:0] hi;              //上位
reg    [31:0] lo;              //下位
always @(posedge clock or negedge reset) begin
  if (reset == 1'b0) begin
    hi <= 32'h00000000;
    lo <= 32'h00000000;
  end else begin
    {hi, lo} <= (alu_ctrl == `ALU_MULT) ? alu_a * alu_b : {hi, lo};
  end
end
の追加

// 実験 9 のヒント(7):mflo 命令実行時に {hi, lo} の lo を result に出力する記述の追加
      `ALU_MFLO: begin
        result <= lo;
      end
	    
図 4: alu.v の変更


main_ctrl.v に図 5 のようなコメントの追加を行う。

// 実験 9 のヒント(9):mult, mflo 命令に関するコメントの追加
/* R 形式
MULT(op = 000000, func = 011000)
MULT       {Hi, Lo} <= REG[rs] * REG[rt];               MULT rs,rt

MFLO(op = 000000, func = 010010)
MFLO       REG[rd] <= Lo;                               MFLO rd
*/
	    
図 5: main_ctrl.v へのコメントの追加


alu_ctrler.v に、次のような追加および変更を行う(実験 9 のヒント(10)から(12))。
mult, mflo 命令用の ALU 制御コードの define を追加する(実験 9 のヒント(10))。
mult, mflo 命令用の ALU 制御コードについてのコメントを追加する(実験 9 のヒント(11))。
実行する命令が mult, mflo 命令のとき、mult, mflo 命令用の ALU 制御コードを生成する処理を行う記述を追加する(実験 9 のヒント(12))。


3. 動作実験

本実験では、図5のmult.cを使用する(ダウンロード)。
このプログラムは、キーボードから入力された数の2乗を表示するプログラムである。
このプログラムを、拡張されたbin2vとプロセッサを使って動作させ正しく動くかどうかを確認せよ。
#define EXTIO_SCAN_ASCII (*(volatile unsigned int *)0x0310)
#define EXTIO_SCAN_REQ (*(volatile unsigned int *)0x030c)
#define EXTIO_SCAN_STROKE (*(volatile unsigned int *)0x0308)

#define SCAN_STRORING (unsigned int)0xffffffff

#define EXTIO_PRINT_STROKE (*(volatile unsigned int *) 0x0300)
#define EXTIO_PRINT_ASCII  (*(volatile unsigned int *) 0x0304)

#define TRUE 	0x1
#define FALSE	0x0

unsigned int sosuu_check(unsigned int kouho);
unsigned int my_a2i();
void my_i2a();
void my_print();
void my_scan();

main() {
  unsigned int k;
  unsigned int str1[16];
  unsigned int str2[16];

  /* "HELLO" を print */
  str1[0] = 'H';  str1[1] = 'E';
  str1[2] = 'L';  str1[3] = 'L';
  str1[4] = 'O';  str1[5] = '\n';
  str1[6] = '\0';
  my_print(str1);

  while (1) {	
    /* "NUM=" を print */
    str1[0] = 'N';  str1[1] = 'U';
    str1[2] = 'M';  str1[3] = '=';
    str1[4] = '\0';
    my_print(str1);

    /* キーボードから入力された文字列(数字)を str2[] に記憶 */
    my_scan(str2);

    /* "ECHO " を print */
    str1[0] = 'E';	str1[1] = 'C';
    str1[2] = 'H';	str1[3] = 'O';
    str1[4] = ' ';	str1[5] = '\0';
    my_print(str1);

    /* str2[] を print */
    my_print(str2);

    /* '\n' を print */
    str1[0] = '\n';  str1[1] = '\0';
    my_print(str1);
    
    /* 文字列(数字) srt2[] を unsigned int に変換 */
    k = my_a2i(str2);

    /* k × k を計算 */
    k = k*k;
    
    /* unsigned int k を文字列(数字)に変換して print */
    my_i2a(k);

    /* '\n' を print */
    str1[0] = '\n';  str1[1] = '\0';
    my_print(str1);
  }
}

/* 文字列(数字) srt[] を unsigned int に変換する関数 */
/* unsigned int result を返す */
unsigned int my_a2i(str)
     unsigned int *str;
{
  unsigned int *str_tmp;
  unsigned int k;
  unsigned int result;

  str_tmp = str;
  for (k = 0; *str_tmp != '\0'; k++) {
    str_tmp++;
  }

  result = 0;
  str_tmp = str;
  
  if (k == 1) {
    result = *str_tmp - '0';
  } else if (k == 2) {
    for (k = 0; k < (*str_tmp - '0'); k++) {
      result = result + 10;
    }
    str_tmp++;
    result = result + (*str_tmp - '0');
  } else if (k == 3) {
    for (k = 0; k < (*str_tmp - '0'); k++) {
      result = result + 100;
    }
    str_tmp++;
    for (k = 0; k < (*str_tmp - '0'); k++) {
      result = result + 10;
    }
    str_tmp++;
    result = result + (*str_tmp - '0');
  }

  return result;
}

/* unsigned int i を文字列(数字)に変換して print する関数 */
void my_i2a(unsigned int i) {
  unsigned int counter;
  unsigned int s[4];

    for (counter = 0; i >= 10; counter++) {
        i -= 10;
    }
    s[0] = counter + '0';
    s[1] = i + '0';
    s[2] = ' ';
    s[3] = '\0';

    my_print(s);
}

/* キーボードから入力された文字列を str[] に記憶する関数 */
void my_scan(str)
     unsigned int *str;
{
    EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
    EXTIO_SCAN_REQ = (unsigned int)0x00000001;
    EXTIO_SCAN_STROKE = (unsigned int)0x00000001;

    EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
    EXTIO_SCAN_STROKE = (unsigned int)0x00000001;
    while (EXTIO_SCAN_ASCII == SCAN_STRORING) {
      EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
      EXTIO_SCAN_STROKE = (unsigned int)0x00000001;
    }

    while ((*str = EXTIO_SCAN_ASCII) != (unsigned int)0x3e) {    // 0x3e=RETURN
      if ((*str >= 1) && (*str <= 26)) {
	*str = 'A' + *str - 1;
      } else if ((*str >= 48) && (*str <= 57)) {
	*str = '0' + *str - 48;
      } else {
	if (*str == 0) {
	  *str = '@';
	} else if (*str == 27) {
	  *str = '[';
	} else if (*str == 29) {
	  *str = ']';
	} else if ((*str >= 32) && (*str <= 47)) {
	  *str = ' ' + *str - 32;
	} else if (*str == 58) {
	  *str = '?';
	} else if (*str == 59) {
	  *str = '=';
	} else if (*str == 60) {
	  *str = ';';
	} else if (*str == 61) {
	  *str = ':';
	} else if (*str == 62) {
	  *str = '\n';
	} else {
	  *str = '@';
	}
      }
      EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
      EXTIO_SCAN_STROKE = (unsigned int)0x00000001;
      str++;
    }
    *str = '\0';

    EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
    EXTIO_SCAN_REQ = (unsigned int)0x00000000;
    EXTIO_SCAN_STROKE = (unsigned int)0x00000001;

    EXTIO_SCAN_STROKE = (unsigned int)0x00000000;
}

/* 文字列 str[] を表示する関数 */
void my_print(str)
     unsigned int *str;
{
  while (*str != '\0') {
    EXTIO_PRINT_STROKE = (unsigned int)0x00000000;

    if ((*str >= 'A') && (*str <= 'Z')) {
      EXTIO_PRINT_ASCII = *str - 'A' + 1;
    } else if ((*str >= 'a') && (*str <= 'z')) {
      EXTIO_PRINT_ASCII = *str - 'a' + 1;
    } else if ((*str >= '0') && (*str <= '9')) {
      EXTIO_PRINT_ASCII = *str - '0' + 48;
    } else {
      if (*str == '@') {
        EXTIO_PRINT_ASCII = (unsigned int)0;
      } else if (*str == '[') {
        EXTIO_PRINT_ASCII = (unsigned int)27;
      } else if (*str == ']') {
        EXTIO_PRINT_ASCII = (unsigned int)29;
      } else if ((*str >= ' ') && (*str <= '/')) {
        EXTIO_PRINT_ASCII = *str - ' ' + 32;
      } else if (*str == '?') {
        EXTIO_PRINT_ASCII = (unsigned int)58;
      } else if (*str == '=') {
        EXTIO_PRINT_ASCII = (unsigned int)59;
      } else if (*str == ';') {
        EXTIO_PRINT_ASCII = (unsigned int)60;
      } else if (*str == ':') {
        EXTIO_PRINT_ASCII = (unsigned int)61;
      } else if (*str == '\n') {
	EXTIO_PRINT_ASCII = (unsigned int)62;
      } else {
        EXTIO_PRINT_ASCII = (unsigned int)0x00000000;
      }
    }
   
    EXTIO_PRINT_STROKE = (unsigned int)0x00000001;
    str++;
  }
}
          
図5: mult.c

参考書


中村一博