Changes between Initial Version and Version 1 of Ex01_CA_2022


Ignore:
Timestamp:
Apr 5, 2022 4:05:28 PM (4 years ago)
Author:
nakasato
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Ex01_CA_2022

    v1 v1  
     1トップ:http://galaxy.u-aizu.ac.jp/note/wiki/CAEX2021 
     2 
     3= 演習の目的 = 
     4 * 命令レベルシミュレータに慣れること 
     5 * MIPSのアセンブリ命令の使い方を理解すること 
     6 * 今後の課題で使用するプログラムを作成すること 
     7  
     8今回の演習ではMIPSプロセッサのシミュレータをを用いてプログラミングを行います。 
     9ここで作成したプログラムはあとで設計するMIPSプロセッサで実行させるテストプログラムになります。 
     10 
     11= SPIMのドキュメント = 
     12http://galaxy.u-aizu.ac.jp/comparch2021/spim.pdf 
     13 
     14= 演習を始める前に =  
     15以下のページの留意点をよく読んでから演習を始めてください:[wiki:"Ex01 演習の留意点2021"] 
     16 
     17= 単純な代入文 =  
     18定数の代入文はor immediate命令とstore word命令の組み合わせで実現できます。 
     19 
     20例えばC言語のコード 
     21{{{ 
     22A = 15; 
     23}}} 
     24 
     25は、擬似的なプログラム 
     26{{{ 
     27$8 = 15 
     28A = $8 
     29}}} 
     30に置き換えることができます。 
     31 
     32各行をMIPSの命令に置き換えると: 
     33{{{ 
     34ori $8, $0, 15 
     35sw $8, A 
     36}}} 
     37となります。 
     38 
     39== 考察 ==  
     40 * この2つの命令で、なぜ「A=15」が正しく実現できているか考えなさい。 
     41 * なぜ$0が使われるのか? 
     42 * この方法で扱える代入文の右辺の定数の範囲を考えなさい。 
     43 * 符号付き32bitの範囲の定数の代入文を実装する方法を考えなさい。 
     44 
     45= メモリの初期化 = 
     46代入文「A = 15」は、Aへの代入としてプログラム開始から最初に実行され、一度しか実行されない場合 
     47{{{ 
     48    .data 
     49A:  .word 15  
     50}}} 
     51とすることで、アセンブリ言語の疑似命令による、メモリ領域の初期化によっても実現できます。 
     52 
     53「A:」の部分はラベルと呼び、この場合にはこのメモリ領域のアドレスを表す定数としてプログラムの中で利用できます。 
     54 
     55しかし、この初期化を含んだプログラムをサブルーチンに用いる場合、 
     561回目の実行でAの値が変更されてしまうと、2回目以降の呼び出しにおいてAは15以外の値を持つ可能性があります。 
     57この様な場合、上記のようにori命令とsw命令の組み合わせで代入文を実現します。 
     58 
     59= 例題1:変数の代入 =  
     60変数A, B, Cがある時に「C = A + B」の演算を実現するプログラムについて考えます。 
     61 
     62変数A, B, Cは、メモリ上に以下のように配置されているとします。 
     63それぞれは32ビットワードの変数であり、1変数につき4バイトのアドレス空間を占めます。  
     64  
     65|| 変数 || アドレス(16進数) ||  初期値(10進数) || 演算結果(10進数) || 
     66||     A|| 0x0|| 19|| 19|| 
     67||     B|| 0x4|| 75|| 75|| 
     68||     C|| 0x8||  0|| 94|| 
     69 
     70MIPSプロセッサでの演算はレジスタ間でのみ行うことができます。 
     71したがって、メモリの中のデータを演算するためには一度レジスタの中にデータをロードし、 
     72演算を行った後に演算結果をメモリにストアしなければなりません。 
     73そのようなプログラムを下に示します。  
     74 
     75{{{ 
     76         .data  
     77A:       .word 19  
     78B:       .word 75  
     79C:       .word 0  
     80         .text  
     81main:    lw   $8, A  
     82         lw   $9, B  
     83         add  $10, $8, $9  
     84         sw   $10, C  
     85exit: j  exit 
     86}}} 
     87 
     88このプログラムを「sum1.s」としてファイルに保存し、xspimでシミュレーションを行ってください。 
     89 
     90アセンブリプログラムで「.data」から始まる部分を、データセグメントと呼びます。 
     91各行は「32ビット数値で内容は「19」の変数を定義しそのアドレスを「A」で参照できるようにする」という意味です。 
     92 
     93「.text」で定義される部分をテキストセグメントと呼び、これがプログラムの本体になります。 
     94各命令の概要を以下のテーブルで説明します。 
     95 
     96このプログラムでは、レジスタ$8, $9, $10を利用しています。 
     97各行が実行される毎にその内容は変化する場合があります。 
     98 
     99|| 命令         || 動作 || $8の内容 || $9の内容 || $10の内容 || 
     100||lw  $8, A     ||アドレスAのデータをレジスタ$8にロード|| 19 || 不定 || 不定 || 
     101||lw  $9, B     ||アドレスBのデータをレジスタ$9にロード|| 19 || 75 || 不定 || 
     102||add  $10, $8, $9  ||レジスタ$8と$9を加算して、結果を$10に格納|| 19 || 75 || 94 || 
     103||sw   $10, C   ||レジスタ$10の内容をアドレスCにストア|| 19 || 75 || 94 || 
     104||j  exit       ||ラベルexitにジャンプ || 19 || 75 || 94 || 
     105  
     106補足:「lw $8, A」 は 「lw $8, A($0)」 の省略形であり、レジスタ$8には「A + $0」の値がロードされます。 
     107 
     108xspimを立ち上げると、以下のような画面となります。 
     109 
     110[[Image(http://galaxy.u-aizu.ac.jp/note/raw-attachment/wiki/Ex01%20MIPS%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E5%9F%BA%E7%A4%8E2017/xpsim.png)]] 
     111 
     112画面の上段には各レジスタの内容が示されています。 
     113 
     114中段には各ボタンがあり、ソースファイルの読み込みにはloadボタン、プログラムの実行にはstepボタンをクリックします。 
     115 
     116下段にはテキストセグメント(Text Segments)と、データセグメント(Data Segments)が示され、 
     117一番下の部分には実行時のメッセージが表示されます。 
     118 
     119プログラムの実行には二つの方法があり、stepボタンによる実行では、テキストセグメントの命令が一命令ずつ実行されます。 
     120一方runボタンを使うと、プログラムを一度に実行できます。 
     121 
     122「sum1.s」をstep実行し、一命令ずつレジスタやデータセグメントの値が変化するのを確かめること。 
     123 
     124= 複雑な式の評価 = 
     125代入文の右辺が複雑な場合は式を変換して考えます。 
     126{{{ 
     127A= B * C - 5 
     128}}} 
     129 
     130という演算をする場合、右辺は一時的な変数Tempを用いて、 
     131 
     132{{{ 
     133Temp = B * C 
     134A = Temp - 5 
     135}}} 
     136と変換できます。 
     137 
     138より複雑な式も、このように一時的な変数を導入して分解して、右辺の演算が実装する命令で実行できるようにします。 
     139if文の条件判定の式が複雑な場合も同様に分解します。 
     140 
     141アセンブリプログラムでは、一時的な変数の役割はレジスタが担います。 
     142しかし、すべての一時的なレジスタを使いきるような場合、一時変数を保持するのにメモリを利用する必要があります。 
     143メモリを利用する場合、swおよびlw命令が必要となり、 
     144実行命令数が増えるので、可能な限りレジスタを効率的に使用するようにします。 
     145 
     146= 課題1 = 
     1473つの変数と定数を含んだ式「S = (A + B ー C) | 3」の計算を行うプログラムを作成し、xspimでシミュレーションを行ってください。 
     148"|" はビットごとの論理和演算です。 
     149 
     150各変数の初期値は以下のように設定してください。 
     151 
     152||アドレス    ||データ(10進数) || 
     153||A ||  19 || 
     154||B ||  75 || 
     155||C ||  10 || 
     156||S ||  結果の格納 || 
     157 
     158作成ファイル名は「ex01_p1.s」としてください。 
     159 
     160= 例題2 レジスタによるメモリの参照 = 
     161この例題ではメモリを間接的に参照するためにレジスタ相対アドレスを用いる方法を示します。 
     162以下のような配列変数の和を計算することを考えます。 
     163 
     164{{{ 
     165S=A[0]+A[1]+A[2]+A[3] 
     166}}} 
     167 
     168配列のように連続するデータのアドレスが規則的に並んでいる場合、 
     169レジスタを使ってアドレスを加算しながらメモリにアクセスするとプログラムが簡単に記述できます。 
     170たとえば次のようにデータが並んでいるとします。   
     171  
     172||アドレス    ||データ(10進数) || 
     173||A      || 19 || 
     174||A+4 || 75 || 
     175||A+8 || 10 || 
     176||A+12 || 15 || 
     177||S ||  結果の格納場所 || 
     178 
     179このような場合、最初のアドレスAがわかれば、次のアドレスはAに4を足すことによって求めることができます。 
     180したがって、4つの要素の和を求める場合、次のようなプログラムを記述すればよいことになります。  
     181   
     182{{{ 
     183        .data  
     184A:      .word 19  
     185        .word 75  
     186        .word 10  
     187        .word 15  
     188S:      .word 0 
     189        .text  
     190main:   or   $8, $0, $0   # 和($8レジスタ)を 0 に初期化  
     191        la   $9, A        # la命令によりAのアドレスを $9 に入れる。実際は ori 命令に置き換わる。 
     192 
     193        lw   $10, 0($9)  
     194        add  $8, $8, $10  
     195 
     196        lw   $10, 4($9)  
     197        add  $8, $8, $10 
     198 
     199        lw   $10, 8($9)  
     200        add  $8, $8, $10 
     201 
     202        lw   $10, 12($9)  
     203        add  $8, $8, $10 
     204 
     205        sw   $8, S  
     206exit:   j exit 
     207}}}  
     208 
     209また、上のプログラムは下のように書き直すこともできます。 
     210この形は各データに対する処理が全く同じ命令列になるため、後で述べるループを用いて和を求める場合に適しています。  
     211 
     212{{{ 
     213        .data  
     214A:      .word 19  
     215        .word 75  
     216        .word 10  
     217        .word 15  
     218S:      .word 0 
     219        .text  
     220main:   or   $8, $0, $0  
     221        la   $9, A      
     222 
     223        lw   $10, 0($9)  
     224        add  $8, $8, $10   
     225        addi $9, $9, 4    # アドレスを次へすすめる 
     226 
     227        lw   $10, 0($9)  
     228        add  $8, $8, $10  
     229        addi $9, $9, 4 
     230 
     231        lw   $10, 0($9)  
     232        add  $8, $8, $10  
     233        addi $9, $9, 4 
     234 
     235        lw   $10, 0($9)  
     236        add  $8, $8, $10 
     237 
     238        sw   $8, S  
     239exit:   j exit 
     240}}}  
     241 
     242上の2番目のプログラムをエディタにコピー&ペーストして、xspimで動作を確認してください。 
     243レジスタを使って配列アドレスををインクリメントしながらメモリにアクセスする方法を理解してください。  
     244 
     245= 条件分岐 = 
     246本演習で利用可能な命令の中で、条件分岐に利用できるのはbranch on equal命令(比較結果が等しい場合に指定したアドレスにジャンプする)だけです。 
     247例えば、C言語での以下の条件分岐は 
     248 
     249{{{ 
     250if(i == j) goto label 
     251 
     252... 
     253 
     254label: 
     255}}} 
     256 
     257以下のbeq命令と同等です。 
     258 
     259{{{ 
     260beq $i,$j,label 
     261}}} 
     262 
     263beqなどのjump系の命令では、「ラベル名:」で指定したラベルを使って、jump先を指定できます。 
     264 
     265== 大小比較 == 
     266大小比較をするには、set on less than命令、あるいはset less than imm命令と、beq命令を組み合わせて実現します。 
     267 
     268=== C言語 === 
     269{{{ 
     270if(a > b) {サブプログラム1}else {サブプログラム2} 
     271}}} 
     272 
     273=== MIPSアセンブリ === 
     274{{{ 
     275lw $8, a 
     276lw $9, b 
     277slt $8,$9,$8 
     278beq $8,$0,label1 
     279 
     280サブプログラム1に対応する部分 
     281 
     282goto label2 
     283label1: 
     284 
     285サブプログラム2に対応する部分 
     286 
     287label2: 
     288}}} 
     289 
     290「<=」による条件分岐をどのように実現するか、また、 
     291「if (条件1 || 条件2)」や「if (条件1 && 条件2)」をどのように実現するかは各自検討してください。 
     292 
     293== while文 == 
     294=== C言語 === 
     295{{{ 
     296while(条件){ サブプログラム } 
     297}}} 
     298 
     299=== 疑似的なアセンブリプログラム === 
     300{{{ 
     301label1: 
     302$a ← if (条件) 1 else 0 
     303beq $a,$0,label2 
     304 
     305サブプログラムに対応する部分 
     306 
     307goto label1 
     308label2: 
     309}}} 
     310 
     311なお、条件が等式の場合は$aを用いず、直接beq命令で等式判定をします。 
     312 
     313== for文 == 
     314{{{ 
     315for( i = init_value; i < terminate_value ;更新式){サブプログラム} 
     316}}} 
     317 
     318のようなfor文は、while文を使って 
     319 
     320{{{ 
     321i = init_value; 
     322while(i < terminate_value){ 
     323サブプログラム; 
     324更新式; 
     325} 
     326}}} 
     327 
     328と置き換えることができるので、while文のアセンブリプログラムを応用することで変換可能です。 
     329 
     330このとき、iの値をレジスタに保持しておくと、iへのswやlw命令を省略でき、 
     331高速化が行えますが、繰り返しの中で自由に使えるレジスタが減ってしまいます。 
     332多重ループの場合は、内部の繰り返しに優先してfor文の制御変数(今の場合i)をレジスタに割り付けるといいでしょう。 
     333この場合繰り返しの最後に、レジスタの値をメモリに書き戻さないと、正しい最適化とはなりません. 
     334 
     335= 例題3 ループによる演算 = 
     336この例題では、1からnまでを単純に加算するプログラムを扱います。 
     337 
     338== C言語 == 
     339{{{ 
     340main()  
     341{  
     342  int i = 0;  
     343  int n = 15;  
     344  int s = 0; 
     345  while( i != n ) {  
     346     i++;  
     347     s += i;  
     348  }  
     349} 
     350}}} 
     351 
     352総和演算のような処理は処理はwhile文を用いることによって記述できます。 
     353 
     354=== MIPSアセンブリプログラム === 
     355アセンブリプログラムでは、分岐命令と比較命令を用いてループを実現します。 
     356メモリの初期状態を以下のように仮定します。  
     357  
     358|| アドレス || データ || 
     359|| n || 15 || 
     360|| s ||         結果の格納 || 
     361 
     362このコードをアセンブリプログラムに変換します。 
     363 
     364ループ変数 i を$8、n の値が入っているレジスタを$9とすると、ループの部分は次のように書けます。  
     365{{{ 
     366      or  $8, $0, $0           # i = 0  
     367loop: beq  $8, $9, loopend      # i == n なら loopend へ飛ぶ  
     368      addi $8, $8, 1            # i++  
     369   
     370        ・・・・ループの中身・・・・・  
     371   
     372      j loop  
     373loopend: 
     374}}} 
     375 
     376例題のソースファイル全体は次のようになります。  
     377{{{  
     378         .data  
     379n:       .word 15  
     380s:       .word 0 
     381         .text  
     382main:    or   $8, $0, $0       # i = 0  
     383         lw   $9, n            # nの値をロード  
     384         or   $10, $0, $0      # s = 0 
     385 
     386loop:    beq  $8, $9, loopend  # i == n なら loopend へ  
     387         addi $8, $8, 1        # i++  
     388         add  $10, $10, $8     # s += i  
     389         j    loop 
     390 
     391loopend: sw   $10, s           # sの値をストア 
     392 
     393exit:    j    exit 
     394}}} 
     395 
     396= 課題2 配列の総和計算 = 
     397メモリ上に並んだN要素からなる配列の総和を求めるプログラムを作成し、SPIMシミュレータで動作確認を行ってください。 
     398プログラムの先頭部分(配列データ)には下のコードを使用してください。  
     399 {{{ 
     400       .data  
     401N:     .word 10    # The length of Array  
     402A:     .word 8     # A[0] = 8  
     403       .word 4     # A[1] = 4  
     404       .word 7  
     405       .word 12  
     406       .word 13  
     407       .word 19  
     408       .word 23  
     409       .word 43  
     410       .word 56    # A[8] = 56  
     411       .word 32    # A[9] = 32  
     412S:     .word 0 
     413       .text  
     414main:  
     415}}} 
     416 
     417作成ファイル名は「ex01_p2.s」としてください。 
     418 
     419= 課題3 配列のコピー = 
     420メモリ上に並んだN要素からなる配列Aの内容を別の配列Bにコピーするプログラムを作成し、シミュレータで動作確認を行ってください。 
     421プログラムの先頭部分には、下のコードを使用して下さい。  
     422{{{  
     423      .data  
     424N:    .word 10    # The length of Array  
     425A:    .word 8     # A[0] = 8  
     426      .word 4     # A[1] = 4  
     427      .word 7  
     428      .word 12  
     429      .word 13  
     430      .word 19  
     431      .word 23  
     432      .word 43  
     433      .word 56    # A[8] = 56  
     434      .word 32    # A[9] = 32  
     435B:    .space 40   # 配列B の格納先を確保する。大きさは40バイト(10ワード分) 
     436      .text  
     437main:  
     438}}} 
     439 
     440作成ファイル名は「ex01_p3.s」としてください。 
     441 
     442= 課題4 バブルソート = 
     443バブルソートのアルゴリズムを使って、配列Aに格納されたN個の整数を昇順にソートするプログラムをアセンブラで作成し、シミュレータで動作確認を行ってください。 Nの値および配列Aの初期値は、課題1ー3と同じものを使って下さい。 参考までに、C言語プログラムの一部を下に示します。 
     444 
     445{{{ 
     446    for( i = 0; i < N-1; i++ ) { 
     447        for( j = N-2; j >= i; j-- ) { 
     448            if( A[j] > A[j+1] ) { 
     449               tmp = A[j]; 
     450               A[j] = A[j+1]; 
     451               A[j+1] = tmp; 
     452            } 
     453        } 
     454    } 
     455}}} 
     456 
     457= Ex01のレポート =  
     458課題1〜4について、プログラムの説明を書き、アセンブラソースファイルを添付してレポートを提出してください。例題についてはレポートでは特に触れなくて結構です。 
     459 
     460 * プログラムの細かな説明は、ソースファイルにコメントの形で埋め込んでも構いません。 
     461 * 課題1、2については、プログラムの実行結果(計算結果)をレポートに書いてください。 
     462 * 課題3、4では、xspimに表示される実行後のメモリの値をウィンドウ画像キャプチャで取得し、レポートに含めてください。 
     463 
     464