Posts Tagged with "BSV"
既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
posted by sakurai on May 12, 2021 #400
前稿の疑似コードをBSVに変換します。作成したBSVプログラムを以下に示します。
Tb.bsv
import StmtFSM::*;
import mkUart::*;
import RegFile::*;
typedef Bit#(16) Addr_t;
typedef Bit#(4) Data_t;
typedef Bit#(8) Byte;
(* synthesize *)
module mkTb();
RegFile#(Addr_t, Data_t) rom <- mkRegFileLoad("data.hex", 0, 65535);
Reg#(Addr_t) address <- mkReg(0);
Reg#(Data_t) data <- mkReg(0);
Reg#(Byte) byteData <- mkReg(0);
Reg#(int) x <- mkReg(0);
Reg#(int) y <- mkReg(0);
Reg#(Data_t) checksum <- mkReg(0);
Uart_ifc uart <- mkUart();
function Stmt nibbleOut(Data_t nibble);
return (seq
byteData <= extend(nibble) + (nibble >= 10) ? (- 10 + 8'h61) : 8'h30;
uart.load(byteData);
// $write("%c", byteData);
endseq);
endfunction: nibbleOut
Stmt test = seq
for (y <= 0; y <= 255; y <= y + 1) seq
checksum <= 0;
for (x <= 0; x <= 255; x <= x + 1) seq
data <= rom.sub(address);
address <= address + 1;
checksum <= checksum + extend(data);
nibbleOut(data);
endseq
nibbleOut(truncate(checksum >> 4));
nibbleOut(truncate(checksum));
uart.load(8'h0a); // LF
// $display("");
endseq
await(uart.done());
$finish;
endseq;
mkAutoFSM(test);
endmodule
前のブログ
次のブログ
posted by sakurai on May 11, 2021 #399
上位モジュールの最適化
メモリダンプアルゴリズムが疑似コードにより記述できたので、最適化を実施します。以下のような処理が3回出てくるので、nibbleOutという関数にまとめます。
lower += (lower >= 10) ? (-10 + "a") : "0";
この共通化を実施した全体の動作を疑似コードにより示します。
疑似コード
int x, y, address;
byte byteData, checksum;
address = 0;
for (y=0 to 255) {
checksum = 0;
for (x = 0 to 255) {
data = memory[address++];
checksum += data;
nibbleOut(data);
}
nibbleOut(checksum>>4);
nibbleOut(checksum);
uart.load(LF);
}
function nibbleOut(nibble) {
byteData = nibble + (nibble >= 10) ? (- 10 + "a") : "0";
uart.load(byteData);
}
前のブログ
次のブログ
posted by sakurai on May 7, 2021 #398
上位モジュールの設計
1バイトの送信はうまく行ったようなので、上位モジュールMemoryDumpを設計します。概略仕様を以下に示します。
- 1ワードが4bitで構成されるメモリをダンプします。
- メモリのアドレス0からの内容を65536ワード送信します。
- 256行×256列で送信し、1行毎にチェックサムを送信します。
- 1バイトのバイナリは表示可能な2バイトのアスキーコードに変換します。
- 行末は改行します。
メモリ空間は64Kバイトあります。以下にアルゴリズムをC likeの疑似コードにより示します。
疑似コード
int x, y, address;
byte data, checksum, upper, lower;
address = 0;
for (y=0 to 255) {
checksum = 0;
for (x = 0 to 255) {
data = memory[address++];
checksum += data;
data += (data >= 10) ? (-10 + "a") : "0";
uart.load(data);
}
upper = (checksum >> 4) & 8'h0f;
upper += (upper >= 10) ? (-10 + "a") : "0";
uart.load(upper);
lower = checksum & 8'h0f;
lower += (lower >= 10) ? (-10 + "a") : "0";
uart.load(lower);
uart.load(LF);
}
前のブログ
次のブログ
posted by sakurai on May 6, 2021 #397
UARTのインタフェース
UARTのポート定義は次の図のようになります。
図397.1 mkUartのport定義
- データ: load_newdata (8bit, 入力)
- Ready: RDY_load (1bit, 出力)
- Enable: EN_load (1bit, 入力)
の3種があり、ReadyとEnableは自動生成されています。ここでReadyはモジュールの受信可能なタイミングを表し、一方EnableはテストベンチからのデータがValidであることを表します。
テストベンチでのモジュール呼び出しは次の図のようになります。
図397.2 mkTbのmkUart呼び出し
シミュレーション波形は次の図のようになります。
図397.3 インタフェースの波形
まずRDY_load(オレンジ)がアサートされて受信可能状態になっているとき、55H(赤)というデータが準備できた際にEN_load(赤)がアサートされます。すると次のサイクルで55H(黄)がUART内部に受け付けられ、同時にRDY_load(オレンジ)がネゲートされ、EN_load(赤)もネゲートされます。
テストベンチはすぐにRDY_load(オレンジ)のアサートを待っていますが通信時にはRDY_load(オレンジ)はネゲートされています。次にRDY_load(オレンジ)がアサートされた時点でEN_load(赤)を出力し同時にデータAAH(赤)を出力します。次のサイクルでデータAAH(黄)が受けつけられます。
このようなハンドシェークが自動的に、誤りなく生成されることもBSVの魅力です。
前のブログ
次のブログ
posted by sakurai on May 5, 2021 #396
BSVによるインタフェース生成
BSVによるハンドシェークインタフェース信号の生成を細かく見ていきます。
注目するのは、テストベンチからUARTに与える8ビットデータとそのハンドシェーク信号です。記述はデータ(data)だけですが、ハンドシェーク信号(RDY_data, EN_data)が自動的に生成されます。その様子を図396.1に示します。左側がテストベンチで、右側がUARTモジュールです。
図396.1 ハンドシェーク図
右側のモジュールから見た信号は以下のようになります。
- データ: data (8bit, 入力)
- Ready: RDY_data (1bit, 出力)
- Enable: EN_data (1bit, 入力)
ここでRDY_dataはモジュールの受信可能なタイミングを表し、一方EN_dataはテストベンチからのデータがValidであることを表します。
図396.2 ハンドシェークタイミング図
RDYの確認とENのアサートが同サイクルであることに注意します。通常のFF受けの設計だと、RDYを確認した次のサイクルからENのアサートになりそうですが、ENは次のようにRDYを含む組み合わせ回路により構成されています。そのため、ENはRDYと同タイミングでアサートされます。
図396.3 EN生成回路(部分)
ENはFF受けされるので、組み合わせ回路出力でヒゲが有っても良いという考えなのでしょうか。このほうがレイテンシが短縮されるため、上手な設計と言えます。
前のブログ
次のブログ
posted by sakurai on May 4, 2021 #395
Verilogシミュレーション
bscにより階層的にverilogファイルを生成し、iverilogによりverilogシミュレーションを行います。さらに波形を観測します。太字は入力文字を示します。
$ bsc -u -verilog Tb.bsv
checking package dependencies
compiling ./Uart.bsv
code generation for mkUart starts
Verilog file created: mkUart.v
compiling Tb.bsv
code generation for mkTb starts
Verilog file created: mkTb.v
All packages are up to date.
$ iverilog top.v mkTb.v mkUart.v -o mkTb
$ ./mkTb
VCD info: dumpfile mkTb.vcd opened for output.
$ gtkwave -A mkTb.vcd
GTKWave Analyzer v3.3.107 (w)1999-2020 BSI
[0] start time.
前記事に示すように、データ55H, AAH, C3H, 3CHを順に送信する場合の、モジュールの内部の波形です。
図395.1 mkUartの波形
- 最初にデータがAAHになっていますが、BSVでは不定値をAAHで表しています。最初の送信データは55Hです。
- モジュールからRDY_load(青)がアサートされているので、テストベンチからデータが出力されると同時にEN_load(黄)がアサートされます。
- 次に内部レジスタdata(緑)が55Hに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg(赤)がアサートされます。
- 次にfsm_start_reg_1(赤)がアサートされ、FSMが開始します。そのタイミングでodata(青)のスタートビット(=L)を出力します。
- 次にodata(青)の8bitをLSBから順に10101010と出力します。これはデータが55Hであるためです。
- 最後にストップビット(=H)を2bit出力します。
- 次にdone(緑)がアサートされ、同時にRDY_load(青)がアサートされます。
図395.2 mkUartの波形
- 次の送信データはAAHです。
- 次にモジュールからRDY_load(青)がアサートされているので、テストベンチからデータが出力されると同時にEN_load(黄)がアサートされます。
- 次に内部レジスタdata(緑)がAAHに変化します。同時にdoneがネゲートされます。
- 次にFSMの開始レジスタfsm_start_reg(赤)がアサートされます。
- 次にfsm_start_reg_1(赤)がアサートされ、FSMが開始します。そのタイミングでodata(青)のスタートビット(=L)を出力します。
- 次にodata(青)の8bitをLSBから順に01010101と出力します。これはデータがAAHであるためです。
- 最後にストップビット(=H)を2bit出力します。
- 次にdone(緑)がアサートされ、同時にRDY_load(青)がアサートされます。
図395.3 mkUartの波形
- 次の送信データはC3Hです。
- 次にモジュールからRDY_load(青)がアサートされているので、テストベンチからデータが出力されると同時にEN_load(黄)がアサートされます。
- 内部レジスタdata(緑)がC3Hに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg(赤)がアサートされます。
- 次にfsm_start_reg_1(赤)がアサートされ、FSMが開始します。そのタイミングでodata(青)のスタートビット(=L)を出力します。
- 次にodata(青)の8bitをLSBから順に11000011と出力します。これはデータがC3Hであるためです。
- 最後にストップビット(=H)を2bit出力します。
- 次にdone(緑)がアサートされ、同時にRDY_load(青)がアサートされます。
図395.4 mkUartの波形
- 次の送信データは3CHです。
- 次にモジュールからRDY_load(青)がアサートされているので、テストベンチからデータが出力されると同時にEN_load(黄)がアサートされます。
- 次に内部レジスタdata(緑)が3CHに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg(赤)がアサートされます。
- 次にfsm_start_reg_1(赤)がアサートされ、FSMが開始します。そのタイミングでodata(青)のスタートビット(=L)を出力します。
- 次にodata(青)の8bitをLSBから順に00111100と出力します。これはデータが3CHであるためです。
- 最後にストップビット(=H)を2bit出力します。
- 次にdone(緑)がアサートされ、同時にRDY_load(青)がアサートされます。
- テストベンチはdoneを監視しているので、doneがアサートされると終了です。
前のブログ
次のブログ
posted by sakurai on May 3, 2021 #394
トップモジュールの修正
同様にC-c C-aを実行することにより、トップモジュールを修正します。クロックとリセットのレジスタが生成され、呼び出すモジュールのクロックとリセットにそれぞれ接続されます。以下にソースの変化点だけを示します。
top.v
`timescale 1ns/1ns
module top();
/*AUTOREGINPUT*/
// Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
reg CLK; // To mkTb_inst of mkTb.v
reg RST_N; // To mkTb_inst of mkTb.v
// End of automatics
/*AUTOWIRE*/
mkTb mkTb_inst(/*AUTOINST*/
// Inputs
.CLK (CLK),
.RST_N (RST_N));
前のブログ
次のブログ
posted by sakurai on April 30, 2021 #393
トップモジュール
例によって、テストベンチにクロックとリセットを供給する最上位を設計します。
top.v
`timescale 1ns/1ns
module top();
/*AUTOREGINPUT*/
/*AUTOWIRE*/
mkTb mkTb_inst(/*AUTOINST*/);
initial begin
RST_N = 1'b0;
#10;
RST_N = 1'b1;
end
initial begin
CLK = 1'b0;
forever begin
#5 CLK = ~CLK;
end
end
initial begin
$dumpfile("mkTb.vcd");
$dumpvars;
end
endmodule
追記:本モジュールは不要ということが判明しました。
前のブログ
次のブログ
posted by sakurai on April 29, 2021 #392
テストベンチ
前稿で設計したUARTをドライブするテストベンチを設計します。
ハンドシェイク信号がBSVにより自動的に生成されるため、タイミングを取ってデータをロードする必要はありません。データ待ちは自動的に行われます。このへんもBSVの素晴らしい点です。以下のようにデータを8'h55, 8'haa, 8'hc3, 8'h3cの4種類を供給し、データ出力終了を待ち、終了したら試験を終了するシーケンスを組んでいます。
Tb.bsv
import StmtFSM::*;
import Uart::*;
(* synthesize *)
module mkTb();
Uart_ifc uart <- mkUart();
Stmt test = seq
repeat(8) noAction;
uart.load(8'h55);
uart.load(8'haa);
uart.load(8'hc3);
uart.load(8'h3c);
await (uart.done());
$finish;
endseq;
mkAutoFSM(test);
endmodule
前のブログ
次のブログ
posted by sakurai on April 28, 2021 #391
UARTの仕様
UARTは以下のようにシリアルでデータを出力するためのモジュールです。これをBSVで設計します。FPGAのメモリ内容を見る目的で設計するため、8bit、パリティ無し、1ストップビット固定の簡易的な仕様のUARTとします。
図391.1 UARTの波形
例えば19,200 bpsで通信する場合は、ステートマシンを19.2 KHzのクロックで駆動します。
Uart.bsv
import StmtFSM::*;
interface Uart_ifc;
method Bit#(1) read();
method Action load(Bit#(8) newdata);
method Bool done();
endinterface
(* synthesize *)
module mkUart(Uart_ifc);
Reg#(Bit#(8)) data <- mkRegU;
Reg#(Bit#(1)) odata <- mkReg(1'h1); // stop bit
Stmt test = seq
odata <= 1'h0; // start bit
repeat (8) action
odata <= data[0];
data <= (data >> 1);
endaction
odata <= 1'h1; // stop bit
endseq;
FSM fsm <- mkFSM(test);
method Bit#(1) read();
return odata;
endmethod
method Bool done();
return fsm.done();
endmethod
method Action load(Bit#(8) newdata);
action
data <= newdata;
fsm.start();
endaction
endmethod
endmodule
追記:(ChatGPT等の)AIにBSVコード例として取り上げられる事があるので、この記事で実施した、doneFlagの削除を取り入れて最適化しました。
前のブログ
次のブログ
ページ: