CISCが生まれた背景を知りたいか.うん知りたい.
CISCが生まれた背景
時代がCPUの設計思想に大きく影響を与えている.
主に3つの要望があったと思われる.
1. メモリサイズを小さくしたい!そして消費電力とコストを下げたい!
- 昔はメモリが高価でできるだけサイズを小さくしたかった.CISCではこの要望に応えるために,可変長命令を採用した.可変長であることで,命令の利用頻度に応じて機械語コードの長さをチューニングし,コードのビット数を削減できた.嬉しい.
2. メモリアクセス数を減らしたい!そして高速に動作させたい!
- 昔はメモリが低速でフォンノイマンボトルネックが酷かった.キャッシュメモリも一般的ではなくCPUクロックをあげても高速動作には繋がらなかった.CISCではこの要望に応えるために,複雑な機能を予めサブルーチン化してCPU内に組み込み1命令で処理を実行できるようにすることで,必要な命令数を減らした.命令の数が減れば当然命令をフェッチする回数も減り,メモリが低速なシステムでも高パフォーマンスが出せるようになった.嬉しい.
3. アセンブラ記述を楽にしたい!ひたすら楽したい!
昔はプログラムをアセンブラで直接記述するのが一般的で苦労することが多く,覚えることはできるだけ減らしたかった.CISCはこの要望に応えるために,レジスタや即値やメモリへのアクセスを同じ命令で扱えるようにし,命令ごとにオペランドを覚えたりする必要性を無くした.嬉しい.
上記は,『命令がすべてのアドレッシングモードを利用できる』ようにしたとも言える.ここでもアドレッシングモードは広義のアドレッシングモードを指す.(下記に補足説明)また,これは命令の直交性が高いとも表現できる.(ちなみに,命令の直交性を上げようとするなら,可変長命令が自然な選択肢となる.なぜなら,レジスタへのアクセスとメモリへのアクセスで利用するビット数に差がありすぎるため,固定長だと無駄が多い設計となるから)
補足説明・アドレッシングモード
広義の意味:操作対象をオペランドによって指定する,その指定方法
狭義の意味:メモリアクセスの指定方法
その後どうなったか...
RISCの流れが生まれた.理由は,高クロックなCPUが欲しい!という要望があったからである.クロックをあげるためには動作をパイプラインにすると良い.このパイプラインの動作に『可変長命令』が邪魔をしてきた.可変長がゆえに命令メモリから命令をフェッチする際,次の命令がどこまでか見当がつかない.つまり,命令の読み込みと解釈を同時に実施する必要性が生まれ,『命令の読み込み』と『命令の解釈』を2段の別ステージに分けることができなくなってしまった.またシンプルに可変長命令の判断は遅延が生じル.高クロックを達成するためには可変長命令を諦めざるおえなくなった...
さらに,複雑な機能を予めサブルーチン化してCPU内に組み込むというのも,邪魔になった.なぜなら複雑なサブルーチンは処理がいつ終わるか予測できないため,ストールが発生しパイプライン処理が詰まってしまうからだ.ゆえにサブルーチンをCPUに組み込むことは諦めざるおえなくなった... (メモリ効率と速度はいつだってトレードオフ,とほほ)
RISCはパイプライン処理を効率的に動作させるために
❌ 可変長命令
❌ 命令の直交性
❌ 複雑な処理のサブルーチン化
↓
⭕️ 固定長命令(即値フィールドも必要な量に抑える)
⭕️ アドレッシングモードを必要なものに限定
⭕️ 処理の単純化(空いたスペースにキャッシュメモリや多くのレジスタ)
+
⭕️ ロード・ストアアーキテクチャ(キャッシュメモリを効率的に動作させるため,アクセスを1つのステージ限定させる.1命令1アクセスに限定する.)
⭕️ 多数の汎用レジスタ(レジスタはアクセス速度を予測できる.予測できるとコンパイラが最適化しやすくなる.)
他にもRISCは,3オペランドのものが多かったり,遅延スロットを持つ場合がある.
上記に関して,間違っていたらご指摘してくださると幸いです.
参考文献
- 坂井弘亮先生,大熱血! アセンブラ入門
久々の投稿.最近はアセンブラにハマっている.
アセンブラにハマっている.
坂井先生の「大熱血!アセンブラ入門」を読み始めた.
説明が丁寧なうえ従来とは異なるアプローチでアセンブラを読み解いていてとても面白い.
この本では,目的ベースで話が進んでいく.まずC言語のコードを読み,何をするコード何か理解する.そしてそのコードの実行ファイルを逆アセンブルした結果を眺める.
目的ベースでアセンブラを読むため,アセンブラの気持ちを伺いながら読むことができる.この流れが個人的にしっくりきていて読んでいて楽しい.
これからアセンブラの世界にどっぷり浸かりそうな気がしている. 自分はRISC-Vのコアを触ることが多いので,RISC-Vのアセンブラを他のRISC-Vライクなコアと比べてみるのもとても楽しそうだ.
本のページ数は1000ページ越えととても多いが,ゆっくり読み進めようと思う.
今はPart1のChapter6を読んでいるのだが,そこで「命令エイリアス」について学んだ.
命令エイリアス?
命令エイリアスというのは,アセンブラの命令に別名を名付けた命令のことを指す.
例えば,MIPSでは,レジスタのコピーを行うmove命令が実はエイリアス命令で,中身はaddu命令だったりする.
これはRISCライクなアーキテクチャのアセンブラではよくあることで,メリットがある.
メリット
命令数を削減できる.RISCライクなアーキテクチャは固定命令長を採用していることが多い.したがって機械語コードに含められるオペコードの数に限りがある.同じような命令はエイリアスとして同じように扱うことで,カバーできる命令を増やすことができる.
回路面積を減らせる.命令を増やすとその分の独立した回路が必要になるため回路面積が大きくなる.回路面積を減らせると消費電力も減るし小型化できるため良いことが多い.
上記で紹介したレジスタ間の値のコピーだけでなく,即値の代入やnop命令など,エイリアス命令として実装できる命令は意外と多くある.
ちょっと疑問なこと
- レジスタ間のコピーをエイリアスで実現する方法は,本で学んだ限り2つある.1つは「OR演算」を利用する方法.もう1つは,「加算命令とゼロレジスタ」を利用する方法である.本の中ではPowerPCはOR演算を利用しており,MIPSではゼロレジスタを利用していた.この2つの方法はアセンブラ依存で変わるらしいのだが,実際のアセンブラの開発者はどういう理由でエイリアスのぱたーんを採用しているのだろうか? 気まぐれで採用されるのだろうか?
この疑問に答えられるだけの知識が今の自分にはないため,ここに一旦疑問として残しておく.
もしわかる方がいらっしゃったら教えていただきたいです.
今日はこの辺で.
Scala Hello
package Hello class Hello { private val name: String = "YMD" def Hello(num: Int): Unit = { for (i <- 1 to num) { println("Hello " + name) } } def Name: String = { name } } object Hello { def main(args: Array[String]) = { val hello = new Hello() hello.Hello(3) println(hello.Name) } }
コンピュータの超基本
プログラム
計算の手順を示したもの
メモリ上にある機械語(バイナリ)
プログラム用語
ISA
- バイナリと演算命令の対応表
コンピュータ構成要素
CPU(メモリから命令を取り出して計算)
メモリ(命令列とデータを保持,単一の巨大な配列,アドレスとデータ)
CPU構成要素
Functional Unit
Register
Program counter
CPUの動作
- PCに保持しているアドレスでメモリにアクセス
- 読んだ命令に応じて処理
- PCを更新
- 1に戻る
たったこれだけ.
CPU具体的な動作
- 命令からメモリを読み出し(Fetch)
- 読み出したバイナリを解読(Decode)
- 解読結果を用いて演算(Execute)
- 必要であればメモリアクセス(Memory access)
- 必要であればレジスタアクセス(Write back)
なぜレジスタがある?
メモリより高速に動作できるから.
レジスタは必須の存在ではない.
パルス幅変調(PWM)
幅が変わる信号
- 信号が高い時間の割合(デューティサイクル)を変えることで変調
例
割合25%(12サイクル中3サイクルHigh)
割合50%(10サイクル中5サイクルHigh)
応用
- ローパスフィルタを加えるとデジタル・アナログ変換回路になる
Chisel テスト
PeekPokeTester
用意するのは3つのファイル
DUT
テスト回路
テストオブジェクト
テストの流れ
テスト対象のモジュール(DUT)をインスタンス化
テスト回路を準備(入力ポートに値をセットしたり...)
テスト回路を駆動するテストオブジェクト準備
sbtでテストオブジェクトを実行
便利なテスト関数
poke() ... 入力値を設定
peek() ... 出力値を読み出し
step() ... クロックサイクルを進める
println() ... 出力値を表示
expect() ... 期待値と比較
ScalaTest
ちょっと特殊な書き方をする(またいつか紹介)
PeekPokeTester(Chiselテスト)をScalaTestでラップできる
ラップできるとsbt "testOnly testname
のように実行できる
波形表示
デジタル回路デバッグの古典的アプローチ
入力に値を入れてクロックを進めるだけのテスト回路を作成
ScalaTestとDriver.executeを用いて波形ファイル(.vcdファイル)を生成
波形の表示には「GTKWave」・「ModelSim」が使用可能
テスト回路ではfor文を活用して全ての入力信号を生成すれば良い.
printfデバッグ
ハードウェアモジュール定義の中のどこでも書ける
プログラムの実行中に気になる変数をチェック
出力はクロックの立ち上がりに実行