開発日記

Erlangをダラダラ書きます。

Chisel, Scala メモ

val フィールド名: 型 = 値の式

初期化後に二度と代入できないフィールド ... val

再代入できるフィールド ... var

クラスの外部に公開したくない ... private

クラスのメンバーにはフィールドとメソッド

def 関数名(引数): 戻り型 = {}

UInt という戻りの型は,他の言語でのvoid,意味のある値を返さないことを宣言

引数を取らず値を返すだけで,値の変更や画面への表示のような副作用を持たないメソッドは,括弧を省略する習慣

引数を受け取らないコンストラクタの場合は,new 型名()の括弧は省略する習慣

scalaの関数はreturn文を省略できる(最後に計算された文の結果を返す)

文の区切りのセミコロンは,文の終了であることが明確なら省略可能

Scalaはstaticメンバが存在しない-> シングルトンオブジェクト

シングルトンオブジェクトはobjectで宣言.classにも同じ名前があるならコンパニオンオブジェクトと呼ばれる

こめんとは// // / */

Scalaのプログラムはmainメソッドを持つシングルトンオブジェクトから開始する

sbt "runMain オブジェクト名"で実行

シングルトンオブジェクトのmainメソッドは,Scala組み込みのAppトレイから継承することで代用可能

import chisel3._ でchjiselの基本のパッケージをインポート

Chiselでは,ハードウェアをModuleを継承したクラスで宣言

Moduleを継承したクラスは,valで定義されたioとう名称の入出力ポートフィールドを持つ必要がある

Chiselではループ処理の一時変数にはvarを使えるが,ハードウェアの回路に変換される部分の変数はvalである必要がある.

val io = IO(new Bundle {})

applyメソッドのようにインスタンス生成用のメソッドをファクトリーメソッドと呼ぶ

ファクトリーメソッドを持つ必シングルトンオブジェクトはファクトリーオブジェクトと呼ぶ

applyメソッドの記述は省略できる

Chiselではtrueの値が割り当てられると,そのポートの電位が高くなり電流が流れる

Chiselでの論理演算はビット演算子で行う.NOT, AND, OR, XOR

ベクトル可(配列)はvecオブジェクトを使用する

Vec(数, T)

ベクトルの要素のアクセスは()

Scalaでは,すべての演算はメソッドの呼び出し

Scalaでは,整数や配列すべてがメソッドを持つオブジェクト

ChiselはScalaの言語内のDSL

Scalaではすべてのメソッドが中置演算子として書ける

ChiselではtrueとfalseはBool型

Chiselで符号なし整数はUInt, 符号あり整数はSInt

Scalaではtrue,falseはBoolean型

Scalaでは整数値型はByte, Short, Int, Long, Char, 数値型はFloat, Double

2.U ... 符号なしの2ビット幅の2 ... ビット幅はその数値を表現できる最小のビット幅になる

Chiselのパッケージをインポートすることで,.U とか .Bとかscalaの値を変換できる

整数を書くときにビット幅を指定したい場合は,UやSメソッドの引数にビット幅を指定する

4ビットをニブルと呼ぶ

Chisel独自の等値演算子,===, =/=

Scalaは==, !=

下記はリダクション演算子(縮退演算子

.orR ... どれかのビットが1ならtrue

.andR ... すべてのビットが1ならtrue

.xorR ... すべてのビットでxorの結果

ビット演算の組み合わせでマルチプレクサを実現

定義したハードウェアモジュールを使うときは,Module(new モジュール名())

他のモジュールを内部に含み,最終的にFPGAチップ外部と接続するモジュールのことをトップモジュールと呼ぶ

モジュールを使うときは,同一のパッケージで定義する必要がある.もしくは他のパッケージをインポートしてくるか

Scalaではファイルの切り替えには頭を使うから悪であるという思想

モジュールの関数化 ... objectを作って,applyメソッドに関数の記述

ChiselのMuxはプログラムのif文と同じ順番,一般に販売されているチップのマルチプレクサと違う

ビット連接はCatオブジェクト(chisel3.util._)

ChiselではMuxCaseオブジェクトは,switch, case文の役割を果たす

Muxcase(デフォルト, Seq( 条件 -> 結果, 条件 -> 結果, ...))

タプルはscalaのデータ型の一つで,Seq, Listと異なり異なる型のデータ型を保存できる

タプルで2つの関係のときは ->

7segはアノードで場所を選択肢(Highの電位),カソードで数値を表現(Lowの電位)

1ビットの加算機...半加算器(真理値表を合計とcarry outに分けて考える)

下からの繰り上がりの桁を入力の持つ加算器を全加算器と呼ぶ

全加算器を複数並べれば,複数ビットの加算器ができる

Scalaのfor式... for (要素 <- コレクション フィルター) ... コレクションには0 until 4とか0 to 4とかSeqとか

Seq.fillメソッド ... Seq.fill(4) { "foo" } ... List{foo, foo, foo, foo}

VecInitは,Seqのインスタンスを引数にしてその値で初期化する.

モジュールの中で一時的な接続処理をしたいときは,Wireオブジェクトを使う.

UIntの型は,ビット単位での読み取りは可能だが,ビット単位での割当ができない -> VecのWireを生成してからasUIntで型変換して代入するのがいい処理

例:val tmp = Wire(Vec(4, UInt(4.W))); io.tmp := tmp.asUInt;

.Uは定数の変換,.asUIntは変数の変換推奨

コンストラクタに引数を渡せる(クラスの引数)...基本コンストラク

def this(...)のコンストラクタを補助コンストラク

Scalaの型とChiselの型の使い分け ... 入出力ポートの最終的にハードウェアに流れる信号になるものはUInt型,一時的な変数やビット幅などはScala

2進数の世界では,負の数を表現するのに2の補数表現を使う

01111..111 ... 最大値

10000..000 ... 最小値

1111...1111 ... マイナス1

10000000 ... 11111111, 00000000, ... 01111111

-232, ..., -1, 0, ... 232-1

正の数から負の数 ... 全部のビットのNOT演算をして1を足す

比較演算は組み合わせ !(< | ==) ... >

シフト演算はマルチプレクサで実現

乗算器 ... 筆算の考え方(掛ける数の各ビットのANDをとってから,その結果のビットをずらしながら足し算していく)

for式は基本的には値を返さないが,{}の前にyieldをつけるとコレクションを返す

算術演算の結果のビット幅は自動的に調整される.(代入先のビット幅が指定されていたら,上位のビットは切り捨てられる)

メソッドの先頭文字で優先順位が決まる

算術演算や論理演算を行うモジュールをALUという

パターンを使った定義 ... val (.. , ..) = (.., ..)

信号がループしている組み合わせ回路の合成は禁止されている.

SRラッチ ... resetとsetがfalseで保持,resetとsetがtrueで出力が変になる

--no-check-comb-loops

Dラッチ ... SRラッチを組み合わせて作成

Dラッチ ... ボタンを押しているときのスイッチの値を記憶

Dフリップフロップ .... Dラッチは値を覚えるときにしんごうをへんかさせてはいけない. ... Dラッチを2つよういしてクロック信号がLowからHighに立ち上がる瞬間の値を覚える記憶するようにする

フリップフロップは他にもTフリップフロップや,JKフリップフロップがある

リセット機能付きフリップフロップ ... 電源投入直後は,いつも決まった値にしておきたい

くろっくにあわせてりせっとされる同期式リセット

くろっくにかんけいなくリセットされる非同期式リセット

Chiselでは同期式リセット

CPU Resetボタンはボタンの片方がグランドに接続されている.そのためボタンを押すとポート側からグランドに電流が流れて,ポートはLowになる.CPU Resetのポートはボタンを押していない状態でHigh, 押している状態でLowってこと.

reset 信号は反転させて入力する(負論理だから)

フリップフロップのような順序回路を挟んだループはおっけい(出力がクロックごとにしかきりかわらないから)

フリップフロップを複数まとめたものをレジスタ

Reg(リセット機能なし)

RegInit(リセット機能つきレジスタ,初期化できる)

RegNext(リセット,入力指定)... 初期化して,入力信号の値を保持して出力し続ける.もちろんクロックの立ち上がりで入力信号によって出力をかえる.

RegEnable(リセット,入力指定,いネーブル付き)... enable信号がtrueのときに入力信号の値を保持して出力し続ける.

CPU reset ボタンにこだわらないなら,他のボタンをリセットに割り当てて,そのまま正論理で使用できる.

レジスタの変数が右辺にある場合,クロックの立ち上がりごとに変化する

OutputやWire等の変数が右辺にある場合,右辺の変数が変わったら即座に左辺の変数も変化する

couter := counter + 1.U はクロックごとにしか1増えていく

when で条件指定

.otherwise でそれ以外

chisel の Counterモジュール ... Counter(true.B, 1000000000) ... 100Mクロックカウントごとに(カウント数,true.B)を返す(つまり100MHzでうごく回路だったら,1秒に一回trueを返す)

バウンス,チャタリング(金属同士の接触でバウンドしてるやつ)

時計もストップウォッチもキッチンタイマーもできる

イベントごとにステートを変えて回路の動作をかえるものをステートマシンという

デジタル回路は状態の数は有限,だからFSM, 有限状態機械とよばれる

ステートマシンの中で複雑なもの,究極系がCPU

ステートマシンの中でUMLとかくときは,管理するクラスと制御するクラスをかんがえる

MVCデザインパターンにのっとって作ったりする(状態を管理Model, 表示を管理View,イベント管理Control)

状態の列挙はEnum (UIntを返す)

Scalaでは:: でListを生成,リストの末尾にはNilをつける

Chiselではswitch() is()

Scalaではmatch()

複数のビットのやり取りは,VecやUIntをつかってふくすうのしんごうせんを並列に処理.パラレル通信

もしくは,一本の信号線で連続的にシリアルに複数のクロックを使ってそうしんもできる.シリアル通信

シリアルパラレル変換(直列並列変換器)

クロックを使って同期して通信するのを同期式通信

シリアルからパラレルへの変換はシフトレジスタで実現

クロックの立ち上がりで値を取り込むが,取り込む信号が変化の途中のとき,レジスタの出力はメタステーブル状態(HighとLowの中間の不安定な状態)になる

最終的には0,1になるが,どちらになるかわからない

レジスタを二段重ねると回避できたりする(2つのレジスタで入力を受け取る)

キーボードのスキャンコード(ASCIIではない)

USBのパケットをシリアルデータに変換するUSB-UARTブリッジがFPGAボードにはだいたいある

送信用 TXD

受信用 RXD

送信と受信が同時にできるから全二重通信

UARTでクロック信号はない.信号のデータに同期するための信号を追加して,同期を釣る

この同期の仕方を,調歩同期式(非同期式)という

Decoupled ... データにvalidとreadyを付加する

Chiselのバルク接続 <>

ChiselでBlackBoxクラスを継承したクラスをつくると,Verilogで作成してあるIPを利用できる