![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
![]() このページでは、当社で開発したIPやトランスレータを使ってVHDLコアをVerilogに翻訳したもの、OpencoresのVerilog等を紹介していきます。
![]() 注意:コンパチブルコアは、オリジナルと命令の実行ステート数が異なります。あくまでオープンソースコア上の実行時間比較であることに注意してください。 この評価の観点としては、同じクロック周波数で駆動していますので(RTLシミュレーション、仮想50MHz)、時間=クロック数でよいと思います。Cコンパイラも含めたCPIの評価に近いことになります。 また、8ビット演算が主体なので、8ビットCPUにとって不利にはならない(8ビットCPUが得意なはずの)ベンチマークになっています。 面白いのは、大体設計時代順に並んでいることです。 H8300Hは、筆者による設計Versionですが、オリジナルより30%以上CPIを高速化しているためAVRより速い結果になっていますが、オリジナルの実行ステート数ではAVRと同等ではないかと推察します。 さらに、AVRは比較的最近の石でかつゲート数もH8300Hよりはかなり少ないと見られます。CISCからRISCの流れ、アーキテクチャの進歩がこのグラフから読みとれると思います。 そんな流れの中で、RISCの草分け的存在であるMIPSは80年代の設計です。 MIPSの先見性を物語っていますね。オリジナルCPUを設計するにあたってどれを規範にするべきか参考になるのではないでしょうか? なお、YACCは筆者による設計でやはりPLASMAと同じMIPSアーキテクチャを規範にしています。 詳細は、YACCプロジェクトのページをご参照ください。 また、オリジナルCPUを作っている方を見つけました。YACCと比較していただいています。なんと200MHzオーバというから驚きです。しかも、DMIPS/MHzが1を超える事を狙っているというから驚き2倍です。 (シングルで1を超えるのは、かなりの最適化コンパイラがないと、きつい感じがします。) でも、アイデアが浮かんで、CPUを作ってしまう。。この乗りは、私も好きで見ていても楽しいです。アーキテクチャから自作するのは、本当に楽しいものです。 MAX2で動く、オリジナルCPUを製作してみました。僅か240セルで動作します。CPU設計の入門編という感じですが、16ビット演算でパターン生成もでき、実用性も追求しています。 。また、VPIで作成した強力なVisualデバッガも付属しており、CPUの動作原理の理解に役立つと思います。 <Z80について> PC8001で有名なFZ80コアを追加しました。 FZ80が、WB_Z80を抜いて最速のCPI、Z80コアとなりました。4本のZ80コアがそろいました。同じ機能を実現するものでありながら、そのCoding方法の違いを見ると面白いでしょう。T80やTV80は、Behaviorで書いています。 一方、WB_Z80/FZ80はゲートレベルに近い書き方をしています。合成器の差が出やすいのは、抽象度の高いBehaivorで、狙った性能とゲート数で見通しがよいのは、 ゲートレベルに近い(回路図に近い)書き方をしている方でしょう。FZ80は、コンパイラの貧弱性を補って8051に迫る凄いものがあります。しかし、そのコアも、Version1.04以前では、このベンチが走りませんでした。 公開=>実際に使われる=>Feedbackというサイクルの図式があるように思います。この流れにうまく乗ると自然と完成度が上がっていくのでは?と思いました。 このほかに6502を使ったコアもあるのですが、適当がCコンパイラが見つからず、留保状態になっています。 CPUアーキテクチャの研究には、MIPSアーキテクチャからはいることをお勧めしています。 Z80よりはるかにシンプルなデコーダは、入門として最も適していると思います。Simple is bestの設計思想が貫かれたアーキテクチャになっています。たとえば、H8のALU記述とYACCのALU記述、デコーダを比較してみてください。 デコーダの比較は、YACCが5段パイプラインかつ、合成後の速度最速を意識徹底した書き方になっているので単純な比較はできないのですが、ソースを見ていただければそのシンプルさがお分かりでしょう。 また、有名なヘネパタ本のモデルCPU DLXは、MIPSアーキテクチャの原点になっています。(DLXのコンパイラを探すより現役のGCCがあります。) 加えて、NiosU(アーキテクチャが変更されました。)、MicroBlaze共、MIPSアーキテクチャに酷似していることにお気づきでしょうか? ハードウェアがシンプル=>合成後の速度が上げやすい=>駆動周波数UP=>DMIPSの向上、という図式になっています。上図は、CPIでの比較ですが、合成後の速度(DMIPS)では、さらに差が開くことになります。 SH2もRISCはRISCなのですが、16ビットに命令を収める機構とCISC的な命令があったりして、MIPSアーキテクチャよりデコーダの負担は、重いはずです。 Openriscが振るわないのは、1ポートRAMで駆動していることもありますが、キャッシュやMMUを積むとその分のオーバヘッドが大きくなるせいでしょう。(PLASMAは、1ポートRAM駆動で、それとの比較です。 条件:DATA /Instruction共8KBキャッシュ)DIV演算をNativeでサポートしていないこともあって、コードサイズが肥大化するのも気になりました。それにしても、もう少し頑張れるはずなのですが。。 鳴り物入りで、登場しただけにちょっと寂しい気がします。 これを設計したのは、opencoresを主催する、Damjan Lampretという人で、当時スロベニア、リュブリャナ大学の一学生というのは、驚嘆に値します。今後、Opencoresがどのような位置を占めるのか興味深いです。 2. AESコア(終了) CQ出版社のデザインウェーブコンテスト2004に応募した設計レポートです(加筆修正していません)。 AESは次世代のDESに代わる暗号コアです。課題は、AESに使われるSUBBYTESという部品を設計するものでしたが、それだけでは、実用的ではないので、AESそのものを設計してしまいました。 結構気合を入れて設計したのですが、惜しくも努力賞という結果に終わりました。 とはいえ、LUT2500程度(LUTだけなら雑誌付録についてくるFPGAに入ります。)で、CLOCK200MHz超、スループット2.7Gbpsは、FPGAとして世界最速ではないかと思います。 本コアは、TOP階層でパイプライン部とFIFOをインスタンス化しているので、容易に拡張できるようにしています。理論的には、インスタンス数を10にすれば、10倍のスループットになる筈ですが、合成するとがっかりするような結果になります。 これはFPGAで、限界に近い使い方をすると配線遅延がボトルネックとなるためです。何でもない論理が5nsを超えてしまうことに唖然としてしまいます。FPGAでも大規模なロジックはきちんとフローアプランをしないといけないということでしょう。 3.H8互換CPUの設計(Under Development Jul.28.2004 Update) オープンコアで未だ誰も先鞭をつけていないもの、比較的メジャー、GCCが動くものということで、H8300Hを設計してみます。 とりあえず、仕様は、主要な命令を取り込んだサブセット仕様とします。 時代物のCPUなので、オリジナルCPU設計為のリファレンスにはなり得ませんが、基本的な設計手法は、マイクロプロセッサの仕組みを理解する上で参考になると思います。 シミュレータ上でドライストーンベンチを計測しました。 H8 BASICをなんとアセンブラで書いている方をみつけました。そのうち遊んでみたいと思います。 ご要望もいただいていますが、いかんせんYACCと比べるとDMIPS/リソース比が1/20くらいになってしまうので、モチベーションが低いです。 CPI重視を捨てて、完全なマイクロコード化を(ROM化)した方が、リソースを少なくでき、FPGA向きかな、と思います。 3Eのスタータでは、32MBのRAMが実装されているようなので、 UCLINUXのよいプラットフォームになる可能性があります。 なんと、H8で、TCP/IPを書いている方を見つけました。これは、面白いです。もし、H8プロジェクトで動くようにしたら、C言語と、FPGAと、 CPUとアセンブラとイーサネットとTCP/IPとVerilog-2001が一緒に勉強できる(遊べる)、ごった煮のような凄いプロジェクトになることでしょう?! (3Eは、LANのPHYもついているようです。) 開発Snapshot なにも変わっていませんが、RTLソースとVeritak Projectです。ツールのソースもどこから持ってきたか忘れてしまいました。 HEXファイルは、例の特製ベンチマークになっています。(メモリは、Alteraライブラリを使用していますが、アーカイブは、含んでいません。 別途、altera_mf.v をCOPY、set してください。) 4.YACCプロジェクト(Yet Another CPU CPUの略??です。 Opencoresのページです。) => 次のような質問がありましたので、少しまともな割り込みコントローラを記述してみたいと思います。 Hi Tak-san, Couple of months ago I successfully interfaced Yacc with a dsp hardware blocked. I was able to program hardware blocks configuration registers and share its RAMS. Now I want to interface interrupts generated by the hardware block to YACC. The main idea is that when once the hardware block has finished its job it raises the interrupt. This interrupt causes the yacc to jump to a interrupt service routine which inspects some registers, sets up the next task and returns to normal execution. また、マルチタスクスイッチャや、VCMによるデジタルサーボコントローラを記述し、サーボシミュレーションして遊んでみましょう。。 <マルチタスクスイッチャ> OSが必要なレベルではないけれどもタスクは分けたい、そんな組み込み用途にびったりです。各タスクは、 task_switch(); を呼ぶことで切り替わります。 例えば、UARTの出力をやりながら、コマンドを受け取って解析したり。。これらのタスクは、自分の仕事がひと段落したら、次の タスクにスイッチします。順番がやってきたら、また自分がすることして次に渡します。タスクが順番に回るだけですが、各タスクは、専用のスタックをもっているので、 自分の仕事に専心できます。MIPSの場合32個もレジスタがあるので、タスク切り替えのオーバヘッドが大きいのですが、ここはよしとしましょう。 サーボは、一定時間毎の処理が必要なので、上記とは別にTimer割り込みによる最優先処理になります。 とりあえず、 ・割り込みコントローラの仕様決め。 ・HDL記述 ・割り込みルーチンの雛形記述 ・タスクスイッチの記述 ・デモサーボの仕様決め ・サーボコントローラの記述 .. 結構ありますね。。 最終的には、シミュレータ上で、CPUが動いてサーボが外乱にめげずに目標に追従する様子を見てみたいと思います。 YACCについては、外部パスに接続可能なCPUの設計ということでこちらで再設計中です。 システム(アルゴリズム・アーキテクチャ)設計に始まり、RTL コーディング、ゲートシミュレーションと、落としていくまとまった記述というのは、 企業や・研究室の奥底にあるもので、あまり公開されていないように思います。Verilog HDLで画期的だったのは、まがりなりにも単一の言語で全てを記述できることです。 この、
コンテストという共通の題材を借りて、一つの(実用的な)IPを作成することを楽しんでいますが、少しでも参考になる記述が見つかれば幸いです。 DWMデザインコンテスト2006上級課題を解く 恒例のコンテストの季節になりました。符号理論がらみの出題が多いのですが、今年の問題は、難問ではないでしょうか? この積符号のデコーダは、ターボ符号やLDPCといった最近の符号理論の成果のエッセンスに迫るものです。 このように、受信値のアナログ値を使って訂正確率を向上させる方式を軟判定(Soft Decision)と呼んでいます。 1.準備 システムのシミュレーションをVerilog HDLで行おうとするとAWG(additive white Gaussian ) Noiseを生成する必要があります。Verilog 2001では、 $dist_normal関数で、AWGNを生成できるのですが、残念なことに仕様上、入力・出力パラメータ共、integerになっていて、量子化誤差が つまないところで入ってしまいます。(どうして、このような仕様になったか全く意味不明です。) 理想値との比較検証する上でも、最初の 検討は、量子化誤差の入らないreal(c言語でのdouble)精度で簡単に行いたいですね。そこで、Veritakでは、real版の$normal_vtakをVPIで作成してみました。(1.83からビルトインしています。) これで、Cで書かなくとも、最終のHard回路の検証まで、Verilog HDL上で自由に記述することができます。 1.1 $dist_normalの使い方 サンプルです。 //Oct.11.2005
//random_sn.v
module random_sn;
parameter integer No=10;
real sd,mean;
real a;
integer seed1;
integer i;
real sdf_array[0:No-1] ;
real sum,avg,sd2;
real expected_mean=-10;
real expected_sd=2.0;
integer result;
initial begin
#10;
seed1=0;
sum=0;
for (i=0;i<No;i=i+1) begin
`ifdef Veritak
a=$normal_vtak(seed1,expected_mean,expected_sd);
`else
result=$dist_normal(seed1,$rtoi(expected_mean),$rtoi(expected_sd));
a=$itor(result);
`endif
sdf_array[i]=a;
sum=sum+a;
$display(" a=%f seed=%d",a,seed1);
end
avg=sum/No;
sum=0;
for (i=0;i<No;i=i+1) begin
a=sdf_array[i]-avg;
sum=sum+a*a;
end
sd2=sum/No;
`ifdef Veritak
sd=$sqrt(sd2);
$display("Total %d ",No);
$display("Expected Avg=%f SD=%f ",expected_mean,expected_sd);
$display("Results Avg=%f SD=%f SD2=%f",avg,sd,sd2);
$display("Difference Avg=%f SD=%f",$fabs(expected_mean-avg),$fabs(expected_sd-sd));
`else
$display("Total %d ",No);
$display("Expected Avg=%f SD2=%f ",expected_mean,expected_sd*expected_sd);
$display("Results Avg=%f SD2=%f",avg,sd2);
`endif
end
endmodule
Veritakの実行結果です。このプログラムでは、平均(mean)=-10.0, 標準偏差=2.0狙いで生成しています。 F:\regression_test\complex\random_sn.v(3)::random_sn Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。 ------------- シミュレーションを開始します。-------------------- a=-9.107809 seed= 1082744015 a=-9.710610 seed= -958425333 a=-9.057617 seed=-2032678137 a=-7.126455 seed=-1634874387 a=-12.131116 seed= 1427361855 a=-9.270853 seed= 1735824513 a=-11.797993 seed= 1657425015 a=-7.087418 seed= 1586374365 a=-12.799056 seed=-2056026887 a=-13.158670 seed=-1872486481 Total 10 Expected Avg=-10.000000 SD=2.000000 Results Avg=-10.124760 SD=2.108805 SD2=4.447058 Difference Avg=0.124760 SD=0.108805 ModelSimの実行結果です。 # Loading work.random_sn run -all # a=-9.000000 seed= 1082744015 # a=-10.000000 seed= -958425333 # a=-9.000000 seed=-2032678137 # a=-7.000000 seed=-1634874387 # a=-12.000000 seed= 1427361855 # a=-9.000000 seed= 1735824513 # a=-12.000000 seed= 1657425015 # a=-7.000000 seed= 1586374365 # a=-13.000000 seed=-2056026887 # a=-13.000000 seed=-1872486481 # Total 10 # Expected Avg=-10.000000 SD2=4.000000 # Results Avg=-10.100000 SD2=4.690000seed は、randomの種、でこれがrandomのSEQを決めています。Verilog 2001では、生成互換なので、同じseedを与えれば、 シミュレータに依存せず、同じ生成SEQになります。$dist_normalを呼ぶと、seed&で呼んだみたいに次のseed値が返ってきます。 次回呼び出しでは、この返ってきた値で呼ぶことで、AWGNになります。$dist_normalでは、整数で返ってきてしまい狙いSDからの誤差も 大きくなってしまいます。 そこで、これ以降は、Veritak独自システムタスクを使うことにします。 少し、サーベイしてみました。課題は、水平垂直パリティコードをSum-Productアルゴリズムで解いています。パリティ方程式が違うので LDPCではありませんが復号法は、LDPCそのものです。 とりあえず、硬判定でも1ビットの誤り訂正はできます。Hamming Codeは、符号長7ビットで、1ビット訂正できますから、それだけを見ると、 効率はよくありません。BP(Belief Propagation)復号法が真価を発揮するのは、このような短い符号長ではなく ある程度長い符号長です。ただし、今回の符号は、LDGMというパリティの作り方になっていて、LDPCに比べて特性差が知られています。 硬判定のストラテジの検討 下表で、y0-y3がデータ、ro-r1が、行パリティ、c0-c1が列パリティで、y0-y3-r0-r1-c0-c1の順で送ります。
単純にパリティマトリクスを式に書き下すと、 y0+y1 +r0 =0 -(1) y2+y3+ r1 =0 -(2) y0+ y2+ +c0 =0 -(3) y1+ y3 +c1=0 -(4) になります。 ここで、一個だけエラーが発生したと仮定します。 <パリティビットに発生した場合> この場合は、各バリティは、(1)-(4)まで、独立した式になっています。したがって、(1)-(4)までのどれか、一式だけが、満たさないケースは、 パリティビットのエラーと考えることができ、データビット(y0-y3)の訂正は必要ありません。 <データビットに1個のデータエラーが発生した場合> 仮にy0にエラー量e0が生じたとします。そうすると、式を満たさないのは、(1)、(3)だけになります。 同様にして以下のようにまとめることができます。
2個エラーが発生した場合は、どうでしょうか?単純には、行かないですね。たとえば、y0,y1にエラーが生じたことと、c0,c1にエラーが生じたことを 区別できません。エラーが生じたことは、検出できそうですが、訂正までは、できません。さらに、3個エラーすると、1個のエラーと区別できなくなり、 誤訂正の可能性があります。4個エラーしたとすると、エラーがあったことすら検出できなる可能性があります。 (一般に、訂正と誤訂正は、確率の違いでしかありません。訂正するシステムは、必ず誤訂正確率について考察する必要があります。) 元のデータは、4ビットですから、符号の種類は、高々16個しかありません。しかしパリティをいれると8ビットすなわち256種類の Data受信の可能性があります。受信した、256種類のデータ(アナログで考えると無限にありますが)は、元の16個の どれに一番近いでしょうか?という問題です。 一番近いデータを復号すれば、最もエラー確率を小さくできるような気がしますね。 では、一番近いということをどう表現できるでしょうか?それが距離という概念です。 ハミング距離とユークリッド距離 今回の課題では、LDGMのBPという解答の他にユークリッド距離を比較する答案もあるだろうと思います。 それでは、ハミング距離について見ていきましょう。 次のプログラムでは、符号の全ワードを出力しています。 //課題の符号を全部列挙する
//Brute Forceに最小距離を調べる
module dwm1_test;
reg [3:0] data;//Encodeする元DataのWORK
reg [7:0] code_array[0:15];//全コードワードを格納する
reg [7:0] work,a,b,c,d;
wire [7:0] encoded_data;//encoder からの出力を受け取る
integer i,j,flag,m,k,n;
encoder en1(data,encoded_data);
initial begin
//符号の列挙
for (i=0;i<16;i=i+1) begin//
data=i[3:0];
#10;
code_array[i]=encoded_data;//配列に格納
$display("Code [%d]=%b",i,encoded_data);
end
//CODE Wordのビットの任意1ビットを変化させて別符号語に一致するかを見る。
flag=0;
$display("1ビットエラーをチェックしています。");
for (i=0;i<16;i=i+1) begin//
work=code_array[i];//元のDataに
for ( j=0;j<8;j=j+1) begin
a=work ^ (1 <<j);//1bit エラーを乗せて
for (m=0; m<16;m=m+1) begin//同じ符号語があるかどうかをチェック
if (a==code_array[m]) flag=1;//同じ符号語が見つかったら フラグON
end
end
end
if (!flag) $display( "1ビットエラーで、他の符号語になることはありませんでした。最小距離は、1より大きいです。\n");
//CODE Wordのビットの任意2ビットを変化させて別符号に一致するかを見る。
flag=0;
$display("2ビットエラーをチェックしています。");
for (i=0;i<16;i=i+1) begin//
work=code_array[i];//元のDataに
for ( j=0;j<8;j=j+1) begin
a=work ^ (1 <<j);//1bit エラーを乗せて
for (k=0;k<8;k=k+1) begin
b=a ^(1<< k);//もう1ビットエラーを乗せて
if (work !=b) begin//同じ符号は除いて
for (m=0; m<16;m=m+1) begin//同じ符号語があるかどうかをチェック
if (b==code_array[m]) flag=1;//同じ符号語が見つかったら フラグON
end
end
end
end
end
if (!flag) $display( "2ビットエラーで、他の符号語になることはありませんでした。最小距離は、2より大きいです。\n");
//CODE Wordのビットの任意3ビットを変化させて別符号に一致するかを見る。
flag=0;
$display("3ビットエラーをチェックしています。");
for (i=0;i<16;i=i+1) begin//
work=code_array[i];//元のDataに
for ( j=0;j<8;j=j+1) begin
a=work ^ (1 <<j);//1bit エラーを乗せて
for (k=0;k<8;k=k+1) begin
b=a ^(1<< k);//もう1ビットエラーを乗せて
for (n=0;n<8;n=n+1) begin
c=b^(1<<n);//もう1ビットエラーを乗せ
if (work !=c) begin//同じ符号語は除いて
for (m=0; m<16;m=m+1) begin
if (c==code_array[m]) begin//同じ符号語があるかどうかをチェック
//$display("%b %b",work,c);
flag=1;//同じ符号語が見つかったら フラグON
end
end
end
end
end
end
end
if (!flag) $display( "3ビットエラーで、他の符号語になることはありませんでした。最小距離は、2より大きいです。");
else $display("3ビットエラーで、他の符号語になることがあります。最小距離は、3以下です。");
end
endmodule
module encoder(input [3:0] in,
output wire [7:0] out);
reg r0,r1,c0,c1;
wire [3:0] y=in[3:0];
assign out[3:0]=in;
assign out[4]=r0;
assign out[5]=r1;
assign out[6]=c0;
assign out[7]=c1;
always @* begin
r0=y[0] ^ y[1];
r1=y[2] ^ y[3];
c0=y[0] ^ y[2];
c1=y[1] ^ y[3];
end
endmodule
この結果を見ると、次のようになります。コードワード0と1のハミング距離は、3になります。ハミング距離とは0と1が違うところの数です。全ての符号語でみたときに、 最小となるハミング距離が最小距離になります。プログラムでは、これを素直に全ワードについて試して最小距離を求めていて、 真の最小距離は、3であることを導いています。 他の符号語に化けるには、3ビットのエラーが必要です。訂正の立場から見ると、1ビットのエラーについては、どちらに近いか 硬判定でも容易に判断でき、2ビットエラーでは、誤訂正の可能性があり、3ビットでは、他の符号語に化けてしまってエラーがあったことすら、 分からない、ということになります。以上は、最小距離で見た場合で、全ての符号間の距離が3という訳でもなさそうなので、 軟判定では、2ビット訂正も可能な場面もでてくることが期待できるかもしれないということではないかと思います。 デジタル的に見たのが、ハミング距離ですが、これをアナログ的に見たのがユークリッド距離になります。今回のようにAWGNの系においては、受信ワードについて、 全符号語とのユークリッド距離を求め最小となる符号語を選び出せば、最も確からしい復号(Maximum likelihood Decoding)となることが知られています。 今回の場合、極端に符号長が短いので、歩はこちらにあるかもしれません。 F:\regression_test\ldgm\dwm_test1.v(3)::dwm1_test Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。 ------------- シミュレーションを開始します。-------------------- Code [ 0]=00000000 Code [ 1]=01010001 Code [ 2]=10010010 Code [ 3]=11000011 Code [ 4]=01100100 Code [ 5]=00110101 Code [ 6]=11110110 Code [ 7]=10100111 Code [ 8]=10101000 Code [ 9]=11111001 Code [ 10]=00111010 Code [ 11]=01101011 Code [ 12]=11001100 Code [ 13]=10011101 Code [ 14]=01011110 Code [ 15]=00001111 1ビットエラーをチェックしています。 1ビットエラーで、他の符号語になることはありませんでした。最小距離は、1より大きいです。 2ビットエラーをチェックしています。 2ビットエラーで、他の符号語になることはありませんでした。最小距離は、2より大きいです。 3ビットエラーをチェックしています。 3ビットエラーで、他の符号語になることがあります。最小距離は、3以下です。 ---------- シミュレーションを終了します。time=160---------- 課題の取り組み方針 コンテストの取り組み方針は、上の検討で決まりました。懲りずにまた、参戦してみたいと思います。狙いは、
課題を解くよりも、6σまで耐えるAWGNのIPを作る方が難しいです。 ハードで発生させるとなると、Box Muller、Ziggurat、Wallaceの方法のいずれをもってしても浮動小数演算をすることになるので、 面倒です。今回は、YACCに、zigguratの方法をソフトで実装することにしました。(YACCの項をご参照ください。) on the fly では、ありませんが、自律的にAWGNを生成できるので、一度走らせれば、後はχ自乗分布まで、プログラムで検証することが可能になります。 BPSKの生エラーレートと1ビット訂正回路 生エラーレートに対して、上の硬判定訂正回路の効果を見てみましょう。 課題通り、0dB,3dB,6dB,9dBを含んだAWGNを発生させ、生エラーレートに対する比較を行ってみます。 //Nov.2.2005
//Nov.3.2005
module raw_error_rate;
parameter integer RATE=2;
parameter integer No_Of_Data_Bits=10000;
parameter integer No_Of_Array=No_Of_Data_Bits/4;
parameter integer Total_Bits=No_Of_Data_Bits*RATE;
parameter integer SEED=1;
real expected_sigma;
real noise;
real noise_array[ 0 :Total_Bits-1];
real r_array [0 :Total_Bits-1];
reg [7:0] data_array [0 : No_Of_Array-1];
reg [7:0] decode_array [0 : No_Of_Array-1];
reg [3:0] hard_correction_array [0 : No_Of_Array-1];
initial begin
make_random_data;//10000個のランダムデータを作る 結果は=>data_array LSB側4bit
encode_data;//10000個のパイティを生成する 結果は=>data_array MSB側4bit
add_noise(0.0);//0dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(3.0);//3dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(5.0);//5dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(6.0);//6dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(7.0);//7dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(8.0);//8.0dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(9.0);//9.0dBノイズを加える
raw_decode;//デコードする
hard_correction;
add_noise(10.0);//10.0dBノイズを加える
raw_decode;//デコードする
hard_correction;
end
task make_random_data;
integer i;
integer work,counter;
begin
$display("%d個のランダムデータを生成中です。",No_Of_Data_Bits);
for (i=0;i<No_Of_Array-8; i=i+8) begin//4x8 ビットづつランダムデータを生成
work=$random;
{data_array[i+3][0 +:4],data_array[i+2][ 0 +:4],data_array[i+1][0 +:4],data_array[i+0][0 +:4]}=work[15:0];
{data_array[i+7][0 +:4],data_array[i+6][ 0 +:4],data_array[i+5][0 +:4],data_array[i+4][0 +:4]}=work[31:16];
end
work=$random;
counter=0;
for (i=No_Of_Array-8;i<No_Of_Array; i=i+1) begin//最後は、4ビットづつ
data_array[i][0 +:4]=work[counter*4 +:4];
counter=counter+1;
end
end
endtask
task encode_data;
integer i;
begin
$display("パリティを生成中です。");
if ( (Total_Bits/RATE)% No_Of_Array)begin//assert(0);
$display(" プログラムエラー");
$finish;
end
for (i=0;i<No_Of_Array; i=i+1) begin
data_array[i][4 +:4]=encoder(data_array[i][ 0 +:4]);//データを渡してパリティを受け取る
// $display("data=%h parity=%h",data_array[i][0 +:4],data_array[i][4 +:4]);
end
end
endtask
function [3:0] encoder (input [3:0] y);//パリティ生成器
reg r0,r1,c0,c1;
begin
r0=y[0] ^ y[1];
r1=y[2] ^ y[3];
c0=y[0] ^ y[2];
c1=y[1] ^ y[3];
encoder ={c1,c0,r1,r0};
end
endfunction
task make_noise( input real db);//所望のS/N[dB]を入力
real sigma2,sigma;
integer seed,i;
real noise;
real noise_sum;
begin
$display("-------------------------------------------------");
$display("ノイズパワー %f[dB] 狙いで生成中です。",db);
sigma2=$pow(10.0,-db/10.0);//$pow は、Veritak Unique
sigma=$sqrt(sigma2);//$sqrt は、Veritak Unique
seed=0;
noise_sum=0;
for (i=0;i<Total_Bits;i=i+1) begin
noise=$normal_vtak(seed,0.0,sigma);//$normal_vtakは、Veritak Unique 平均値0、σのAWGNを生成
noise_array[i]=noise;//ノイズ配列に収納する
noise_sum=noise_sum+noise*noise;
end
$display("ノイズパワー %f[dB]の生成をしました。",-10*$log10(noise_sum/Total_Bits));
end
endtask
task add_noise( input real db);//所望のS/N[dB]を入力
real sigma2,sigma;
integer counter,i,j;
real noise,RData;
begin
make_noise(db);//所望のS/N Noize をnoise_array に収納する
$display("ノイズを加算中です。");
counter=0;
for (i=0;i<No_Of_Array; i=i+1) begin
for (j=0;j<8;j=j+1) begin
if (data_array[i][j]==1'b1) RData=$itor(-1);//データ1=>−1.0にエンコード
else RData=$itor(1);//データ0=>1.0にエンコード
RData=RData+noise_array[counter];//計算しておいたAWGNノイズを加算する
r_array[counter]=RData;//r_array にノイズが重畳したreal data をSaveする。
counter=counter+1;
end
end
if ( counter !=Total_Bits) begin//assert(0);
$display(" プログラムエラー");
$finish;
end
end
endtask
task raw_decode();
integer i,j;
integer index;
integer error_count;
begin
error_count=0;
index=0;
for (i=0;i<Total_Bits;i=i+8) begin
for (j=0;j<8;j=j+1) begin
if (r_array[i+j]>0) decode_array[index][j]=0;//パリティも含めハードデコードする
else decode_array[index][j]=1;
if (j<4) begin//データ部はビットエラーをカウントする
if (decode_array[index][j] !=data_array[index][j]) error_count=error_count+1;
end
end
index=index+1;
end
$display("訂正なしシステム結果");
$display("トータルエラー数=%d エラーレート=%e",error_count, $itor(error_count)/No_Of_Data_Bits);
end
endtask
task hard_correction();
reg p0,p1,p2,p3;
reg [3:0] parities;
reg d0,d1,d2,d3;
integer index,error_count,i;
begin
error_count=0;
for (index=0;index<No_Of_Array; index=index+1) begin
{d3,d2,d1,d0}=decode_array[index][3:0];//work
p0=d0 ^ d1 ^ decode_array[index][4];//パリティを計算
p1=d2 ^ d3 ^ decode_array[index][5];//パリティを計算
p2=d0 ^ d2 ^ decode_array[index][6];//パリティを計算
p3=d1 ^ d3 ^ decode_array[index][7];//パリティを計算
parities={p3,p2,p1,p0};
hard_correction_array[index][3:0]={d3,d2,d1,d0};
if (parities==4'b0000);//No Error Nothing to Do
else if (parities==4'b0101) hard_correction_array[index][3:0]={d3,d2,d1,~d0};//1bit 訂正
else if (parities==4'b1001) hard_correction_array[index][3:0]={d3,d2,~d1,d0};//1bit 訂正
else if (parities==4'b0110) hard_correction_array[index][3:0]={d3,~d2,d1,d0};//1bit 訂正
else if (parities==4'b1010) hard_correction_array[index][3:0]={~d3,d2,d1,d0};//1bit 訂正
else ;//parity error or uncorrectable error.
//エラー数をカウント
if (data_array[index][3:0] !=hard_correction_array[index][3:0]) begin//ワード比較して
for (i=0;i<4;i=i+1) begin//違っていたらエラー数をカウント
if (data_array[index][i] !=hard_correction_array[index][i]) error_count=error_count+1;
end
end
end
$display("ハード訂正回路結果");
$display("トータルエラー数=%d エラーレート=%e",error_count, $itor(error_count)/No_Of_Data_Bits);
$display("\n");
end
endtask
endmodule
実行結果です。なお、BPSKのBER理論値は、
さて、訂正の効果は、どうでしょうか?グラフにしてみないと良くわかりませんが、BERは確かに下がっているので、効果はあると 言えるでしょう。問題は、これをMaximum Likelihood Decodingした場合どうなるか?、です。 F:\regression_test\ldgm\raw_error_rate.v(2)::raw_error_rate ユークリッド距離による復号です。上のhard_correctionタスクに対応しています。 task euclidean_decode();
integer i,j,k,m;
integer index;
integer error_count;
reg [3:0] min_code_word;
reg [7:0] data;
real distance,min_distance;
real bit_distance;
real data_r;
begin
error_count=0;
index=0;
for (i=0;i<Total_Bits;i=i+8) begin
//符号の列挙
min_distance=1e5;//Big Value
for (j=0;j<16;j=j+1) begin//全コードワードについて
data={encoder(j[3:0]),j[3:0]};//符号の列挙
//$display("Code [%d]=%b",j,data);
distance=0.0;
for (k=0;k<8;k=k+1) begin//ユークリッド距離を調べる
data_r=data[k]==1'b1 ? -1.0 : 1.0;//エンコード
bit_distance=r_array[i+k]- data_r;//ビット毎の距離
distance=distance+ bit_distance**2;//2乗して足す
end
if (distance <min_distance) begin//距離が最小か? 比較だけなのでルートは不要
min_distance=distance;//距離をSave
min_code_word=j[3:0];//コードワードを覚えておく
end
end
euclidean_correction_array[index]=min_code_word;
if (min_code_word !==data_array[index][3:0]) begin//コードワードを比較して
for (m=0;m<4;m=m+1) begin//違っていたらビットエラー数をカウント
if (data_array[index][m] !=euclidean_correction_array[index][m]) error_count=error_count+1;
end
end
index=index+1;
end
$display("MLシステム結果");
$display("トータルエラー数=%d エラーレート=%e",error_count, $itor(error_count)/No_Of_Data_Bits);
end
endtask
実行結果です。確かに上の硬判定訂正回路よりもエラーレートが向上しています。この結果は、この符号化方式の上界を与えます。 (これより良い結果はありません。) コンテストでは、いかにこの結果に、高速かつ最小ゲート数で近づけるか、がキーポイントになります。 F:\regression_test\ldgm\Euclidean_error_rate.v(3)::euclidean_error_rate
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------
10000個のランダムデータを生成中です。
パリティを生成中です。
-------------------------------------------------
ノイズパワー 0.000000[dB] 狙いで生成中です。
ノイズパワー 0.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 1607 エラーレート=1.607000e-001
ハード訂正回路結果
トータルエラー数= 1280 エラーレート=1.280000e-001
MLシステム結果
トータルエラー数= 755 エラーレート=7.550000e-002
-------------------------------------------------
ノイズパワー 3.000000[dB] 狙いで生成中です。
ノイズパワー 3.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 810 エラーレート=8.100000e-002
ハード訂正回路結果
トータルエラー数= 402 エラーレート=4.020000e-002
MLシステム結果
トータルエラー数= 128 エラーレート=1.280000e-002
-------------------------------------------------
ノイズパワー 5.000000[dB] 狙いで生成中です。
ノイズパワー 5.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 393 エラーレート=3.930000e-002
ハード訂正回路結果
トータルエラー数= 104 エラーレート=1.040000e-002
MLシステム結果
トータルエラー数= 13 エラーレート=1.300000e-003
-------------------------------------------------
ノイズパワー 6.000000[dB] 狙いで生成中です。
ノイズパワー 6.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 238 エラーレート=2.380000e-002
ハード訂正回路結果
トータルエラー数= 41 エラーレート=4.100000e-003
MLシステム結果
トータルエラー数= 4 エラーレート=4.000000e-004
-------------------------------------------------
ノイズパワー 7.000000[dB] 狙いで生成中です。
ノイズパワー 7.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 110 エラーレート=1.100000e-002
ハード訂正回路結果
トータルエラー数= 12 エラーレート=1.200000e-003
MLシステム結果
トータルエラー数= 3 エラーレート=3.000000e-004
-------------------------------------------------
ノイズパワー 8.000000[dB] 狙いで生成中です。
ノイズパワー 8.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 52 エラーレート=5.200000e-003
ハード訂正回路結果
トータルエラー数= 2 エラーレート=2.000000e-004
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
-------------------------------------------------
ノイズパワー 9.000000[dB] 狙いで生成中です。
ノイズパワー 9.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 23 エラーレート=2.300000e-003
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
-------------------------------------------------
ノイズパワー 10.000000[dB] 狙いで生成中です。
ノイズパワー 10.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 8 エラーレート=8.000000e-004
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
---------- シミュレーションを終了します。time=0----------
さて、次に課題提示の方法でも結果を比較しておきましょう。素直に、例示の方法をコーディングします。 task iterative_decoder(input integer iteration,input integer min_sum_flag);//min_sum を用いる場合は、min_sum=1とする
integer i,j,m;
integer index;
integer iter;
integer error_count;
real y0,y1,y2,y3,r0,r1,c0,c1;
real y0col,y1col,y2col,y3col;
real y0row,y1row,y2row,y3row;
reg d0,d1,d2,d3;
begin
error_count=0;
index=0;
for (i=0;i<Total_Bits;i=i+8) begin
//iterative decoder の初期化
y0=r_array[i+0];
y1=r_array[i+1];
y2=r_array[i+2];
y3=r_array[i+3];
r0=r_array[i+4];
r1=r_array[i+5];
c0=r_array[i+6];
c1=r_array[i+7];
y0col=0.0;
y1col=0.0;
y2col=0.0;
y3col=0.0;
for (iter=0;iter< iteration;iter=iter+1) begin//interative デコーディング
//行、外部値の計算
y0row=func(y1+y1col,r0,min_sum_flag);
y1row=func(y0+y0col,r0,min_sum_flag);
y2row=func(y3+y3col,r1,min_sum_flag);
y3row=func(y2+y2col,r1,min_sum_flag);
//列、外部値の計算
y0col=func(y2+y2row,c0,min_sum_flag);
y1col=func(y3+y3row,c1,min_sum_flag);
y2col=func(y0+y0row,c0,min_sum_flag);
y3col=func(y1+y1row,c1,min_sum_flag);
end
//繰り返しが終わったらデコード=チャネル値+事前値+事後値
d0=y0+y0row+y0col > 0 ? 0 :1;
d1=y1+y1row+y1col > 0 ? 0: 1;
d2=y2+y2row+y2col > 0 ? 0: 1;
d3=y3+y3row+y3col > 0 ? 0: 1;
iterative_correction_array[index]={d3,d2,d1,d0};
if (iterative_correction_array[index] !==data_array[index][3:0]) begin//コードワードを比較して
for (m=0;m<4;m=m+1) begin//違っていたらビットエラー数をカウント
if (data_array[index][m] !=iterative_correction_array[index][m]) error_count=error_count+1;
end
end
index=index+1;
end
if (min_sum_flag) $display("Min-Sum Iterative Decoding Logシステム結果 ");
else $display("Log-Sum Iterative Decoding Logシステム結果 ");
$display("トータルエラー数=%d エラーレート=%e",error_count, $itor(error_count)/No_Of_Data_Bits);
end
endtask
function real func (input real a, input real b,input integer min_sum_flag);//min_sum を用いる場合は min_sum_flag =1に設定する
real den,num;
real sign;
real min;
begin
if (min_sum_flag) begin
sign=a*b > 0 ? 1.0 : -1.0;
min=$fabs(a) > $fabs(b) ? $fabs(b) : $fabs(a);
func=sign* min;
end else begin
num=1.0 +$exp(a+b);
den=$exp(a)+$exp(b);
func=$log(num/den);
end
end
endfunction
結果です。iteration は、5回にしました。意外にも、殆どOptimumな結果になりました。実際のところ、2回でも、殆ど違いは、ないように思います。 どうして、このiterative algorithmが収束するかというのは、感覚的に言っても不思議ですね。 それにしても、これ以上簡単化しようのないLDPC復号例になっており、面白い課題になっています。。 グラフ化してみました。Min-Sum とユークリッドは、ピッタリ重なってしまっています。1e-3で見ると、 生エラーレートに対して、5dB位のゲインがあります。符号化による帯域ノイズの上昇は、2倍の3dBなので、符号化に よって見かけ上2dBの符号化利得が得られることになります。(2dB送信電力を落としても同じBERが得られる)
F:\regression_test\ldgm\iterative_decoding.v(3)::iterative_decoding
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------
10000個のランダムデータを生成中です。
パリティを生成中です。
-------------------------------------------------
ノイズパワー 0.000000[dB] 狙いで生成中です。
ノイズパワー 0.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 1607 エラーレート=1.607000e-001
ハード訂正回路結果
トータルエラー数= 1280 エラーレート=1.280000e-001
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 765 エラーレート=7.650000e-002
MLシステム結果
トータルエラー数= 755 エラーレート=7.550000e-002
-------------------------------------------------
ノイズパワー 1.000000[dB] 狙いで生成中です。
ノイズパワー 1.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 1345 エラーレート=1.345000e-001
ハード訂正回路結果
トータルエラー数= 969 エラーレート=9.690000e-002
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 494 エラーレート=4.940000e-002
MLシステム結果
トータルエラー数= 489 エラーレート=4.890000e-002
-------------------------------------------------
ノイズパワー 2.000000[dB] 狙いで生成中です。
ノイズパワー 2.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 1061 エラーレート=1.061000e-001
ハード訂正回路結果
トータルエラー数= 645 エラーレート=6.450000e-002
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 283 エラーレート=2.830000e-002
MLシステム結果
トータルエラー数= 282 エラーレート=2.820000e-002
-------------------------------------------------
ノイズパワー 3.000000[dB] 狙いで生成中です。
ノイズパワー 3.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 810 エラーレート=8.100000e-002
ハード訂正回路結果
トータルエラー数= 402 エラーレート=4.020000e-002
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 131 エラーレート=1.310000e-002
MLシステム結果
トータルエラー数= 128 エラーレート=1.280000e-002
-------------------------------------------------
ノイズパワー 4.000000[dB] 狙いで生成中です。
ノイズパワー 4.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 577 エラーレート=5.770000e-002
ハード訂正回路結果
トータルエラー数= 214 エラーレート=2.140000e-002
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 40 エラーレート=4.000000e-003
MLシステム結果
トータルエラー数= 40 エラーレート=4.000000e-003
-------------------------------------------------
ノイズパワー 5.000000[dB] 狙いで生成中です。
ノイズパワー 5.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 393 エラーレート=3.930000e-002
ハード訂正回路結果
トータルエラー数= 104 エラーレート=1.040000e-002
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 13 エラーレート=1.300000e-003
MLシステム結果
トータルエラー数= 13 エラーレート=1.300000e-003
-------------------------------------------------
ノイズパワー 6.000000[dB] 狙いで生成中です。
ノイズパワー 6.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 238 エラーレート=2.380000e-002
ハード訂正回路結果
トータルエラー数= 41 エラーレート=4.100000e-003
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 4 エラーレート=4.000000e-004
MLシステム結果
トータルエラー数= 4 エラーレート=4.000000e-004
-------------------------------------------------
ノイズパワー 7.000000[dB] 狙いで生成中です。
ノイズパワー 7.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 110 エラーレート=1.100000e-002
ハード訂正回路結果
トータルエラー数= 12 エラーレート=1.200000e-003
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 3 エラーレート=3.000000e-004
MLシステム結果
トータルエラー数= 3 エラーレート=3.000000e-004
-------------------------------------------------
ノイズパワー 8.000000[dB] 狙いで生成中です。
ノイズパワー 8.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 52 エラーレート=5.200000e-003
ハード訂正回路結果
トータルエラー数= 2 エラーレート=2.000000e-004
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
-------------------------------------------------
ノイズパワー 9.000000[dB] 狙いで生成中です。
ノイズパワー 9.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 23 エラーレート=2.300000e-003
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
-------------------------------------------------
ノイズパワー 10.000000[dB] 狙いで生成中です。
ノイズパワー 10.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 8 エラーレート=8.000000e-004
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000
Min-Sum Iterative Decoding Logシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
MLシステム結果
トータルエラー数= 0 エラーレート=0.000000e+000
---------- シミュレーションを終了します。time=0----------
さて、以上で理想化したシステムでのモデルの構築ができました。今までは、論理合成を意識しませんでした。当面の問題は、ビット数を幾つにするかという問題です。 それには、上のモデルを量子化ビット幅をパラメータにして、シミュレーションしてみればいいですね。 量子化誤差が入り込んでくるので、上のシミュレーションから劣化は避けられません。理想値を知った上での ゲート数/速度とのトレードオフになります。Version 1.83のregresiion_test\ldgmフォルダに上のコードは入っていますので、そこから、 個々のアイデアを展開してみるのもよいかもしれません。 ビット幅の検討 アーキテクチャが決まったので、RTLを書いて、ビット幅の検討を行いました。 この段階では、速度と、合成回路規模、性能のトレードオフになるので、ビット幅をパラメータにして、合成も行っています。 下の記述は、ハードウェアトップのポート記述ですが、ビット幅BWをパラメータにしています。XILINXでは、このパラメータも ちゃんと認識してくれて、BWの値を換えるだけで、全体のビット幅が変わってくれます。ビット幅のコストは、合成してみないと 予想がつかないことも多く、この手法はお勧めです。 module hardware # (parameter BW=8)
( input [BW*8-1:0] indata,
output reg [3:0] outdata,
input clock, Reset );
localparam integer FIFO_DEPTH=6;
reg [BW*8-1:0] indataR,indataR_D;
また、このHW記述では、verilog-2001のgenerate を使ってパイプラインレジスタを書いてみました。FIFO_DEPTHをパラメータとして、HW変更に対して柔軟にしています。(Verilog 2001 generateについては、こちらをご参照ください。) generate
genvar g;
for (g=0;g <FIFO_DEPTH; g=g+1) begin :fifo
reg [3:0] BinDD;
reg [3:0] word0_D;
reg [3:0] word1_D;
reg [3:0] word2_D;
if (g==0) begin :if_label //why xilinx needs label here?
always @(posedge clock) begin
BinDD<=BinD;//2nd
word0_D<=word0[3:0];//2nd
word1_D<=word1[3:0];
word2_D<=word2[3:0];
end
end else begin :else_label//why xilinx needs label here?
always @(posedge clock) begin
BinDD<=fifo[g-1].BinDD;//
word0_D<=fifo[g-1].word0_D;//
word1_D<=fifo[g-1].word1_D;
word2_D<=fifo[g-1].word2_D;
end
end //if
end //for
endgenerate
ところで、性能ですが、提案しているアーキテクチャでは、MLの理想値にたいして、劣化は避けられません。ここでは、0.5dB位にとどめたいと思っています。下のシステムシミュレーションのグラフから、3ビット以上が必要であることがわかります。なお、同じベンチで、合成に使ったRTLでもシミュレーション を行い、同一の結果であることを確認しました。このシミュレーションでは、各1e6ビット流しています。エラーレートが低いところで、バタついているのは、数個のエラーしかでない為です。 ![]() 合成後の規模・速度共、大体想定内でしたので、これで、コア部の設計終了です。 余談ですが、XILINXスパルタン3とStratix2とでは、ロジックの深さが違います。スパルタン3は、とても浅いので、深いロジックを 書く事ができません。溢れたロジックは、即速度低下という形で現れます。最初3段位で書いていましたが、70MHz位 しか出なくて、目を疑いました。結果、最終的に8段になってしまいました。試してはいませんが、ST2なら自身の最高速度を保ったまま、3-4段で収まるような気がします。 =>さすがに最速デバイス(ST2)を使うと速い結果になりました。
遅延シミュレーション 設計したコアの遅延シミュレーションを行ってみました。RTL上は、9クロック遅延なのですが、出力の遅延(約7ns)のため、10クロック遅延になってしまっています。 また、下で、1ビットないし、2ビットのエラーパターンが発生していますが、訂正が行われてノイズ加算前のデータが再現していることが分かります。 ![]() YACCとの結合 YACCのメモリ上にコアをマップし、実働回路を作成します。 3Eは、待っていられないので、スパルタン3のスタータキット上で作成しました。結果、Cプログラムのダイエットを余儀なくされました。 Area優先で、遅延約25nsが得られています。 ![]() ![]() 後は、下のような、Cプログラムをひたすら走らせ、UARTで、エラーレートの報告を(寝て)待てば、よいだけです。 double target_sds[]={ 1.0, //0db PASS0
0.891,//1db 1
0.794,//2db 2
0.708,//3db 3
0.631,//4db 4
0.562,//5dB 5
0.501,//6dB 6
0.447,//7 7
0.422,//7.5 8
0.398,//8.0 9
0.376,//8.5 10
0.355,//9 11
0.335,//9.5 12
0.315// 10.0 13
};
void main()
{
double sum,avg,a,target_sd;
double noise;
int i,j,index,I;
unsigned data_in,data_out;
int n=No;
#ifdef RTL_SIM
print_uart("V1.1\n");
#endif
total_bits=0;
sum=0;
// for (i=0;i< (STATISTICS_LEVELS*2);i++) counter_array[i]=0;
for(j=0;j< 14 ;j++) {
target_sd=target_sds[j];
bit_counter=0;
bit_error_counter=0;
raw_bit_error_counter=0;
do{
data_in=make_input_data(target_sd);
#ifndef DOS
data_write_and_compare(data_in);
#endif
bit_counter +=4;
total_bits +=4;
if (bit_error_counter> ERROR_THRESHOLD){
print_uart("Pass=");
print_num(j);
print_num(bit_counter);
print_num(raw_bit_error_counter);
print_num(bit_error_counter);
print_uart("EndPass\n");
break;
}
}while(1);
}
#ifdef DOS
#ifdef SD_DISPLAY
avg=sum/No;
sum=0;
for (i=0;i<No;i=i+1){
a=array[i]-avg;
sum=sum+a*a;
}
sd=sqrt(sum/No);
printf("Done %d samples. avg=%f target_sd=%f sd=%f",n,avg,target_sd,sd);
#endif
printf("Done %d samples. target_sd=%f\n",n,target_sd);
for (i=0;i< (STATISTICS_LEVELS*2);i++){
printf("%d,%d\n",i-STATISTICS_LEVELS,counter_array[i]);
}
#else
print_uart("Done All\n");
#endif
}
![]() <所感> AWGNの生成に時間がかかっているので、もくろみのビット数は、難しい結果となりましたが、それでも、H/Wを 作成する手間を考えると、現実的な妥協点ではないかと思います。手軽にCプログラムが走るCPUがあると、 周辺H/Wの検証が楽になることを、実感しました。 一方、CプログラムのデバッグをVeritak上で行いましたが、AWGNの生成は、時間がかかるので結構な時間がかかりました。 ソフトウェアシミュレータでは、高速に走らせることができますが、周辺H/Wは、独自なので、やはりRTLでの高速化が望ましいと思いました。 また、システムの検討では、RTLはそのままで、それ以外をVPIを使ったC呼び出しで作成してもよかったかもしれません。 少し、時間があるので、この点での検討をしてみたいと思います。 なお、今回は、中国のエンジニア陸偉良氏との共作です。 => <VPIを使った高速RTLシミュレーション> H/Wの記述以外の周辺をCで記述して、RTLシミュレーションを高速化しました。 これだと、総ビット数1e9のテストでも、それほど時間は、かかりません。上のスパルタン3でのテストよりも、 かなり速いです。実機CPUの方が情けなくなってきます。(なにせ、ソフトフロート25MHz駆動ですので。) H/W部は、合成に使用したソースですし、AWGNのプログラムも同じなので、実機と同じ結果になるはずです。 以下テストベンチです。 //RTL VPIを用いた高速版
`timescale 1ns/1ps
`define CYCLE 10
module euclidean_error_rate;
parameter integer ERROR_LIMIT=1000;//エラー数 これを超えたら次のDB STEPに行く
parameter integer BW=3;//ビット幅
parameter integer FIFO_DEPTH=9;//ハードウェアの遅延待ち
reg clock=0;
reg [31:0] VPI_Data;//VPIで返される値
wire [23:0] indata=VPI_Data[23:0];//ハードウェア入力3ビットx8ビット
wire [3:0] correct_data=VPI_Data[27:24];//期待値
wire [3:0] outdata;//ハードウェア出力
reg Reset=1;
reg [63:0] bit_counter=0;
integer bit_error_counter=0;
real DB;
real sigma;
initial begin
#100;
Reset=0;
DB=0.0;
set_sigma;
end
generate
genvar gen;
for (gen=0;gen<FIFO_DEPTH;gen=gen+1) begin :loop
reg [4:0] D;
if (gen==0) begin
always @(posedge clock) begin
D<={Reset,correct_data};
end
end else if(gen==FIFO_DEPTH-1) begin//ハードウェアの遅延クロック分、期待値も遅延させる
always @(posedge clock) begin
D<=loop[gen-1].D;
@(negedge clock);
if (D[4]==0 ) begin//RESET がLOWのときだけカウントする
bit_counter=bit_counter+4;
count_bit_error(D[3:0],outdata);//エラー数のカウント D[3:0]は、期待値
end
end
end else begin
always @(posedge clock) begin
D<=loop[gen-1].D;
end
end
end
endgenerate
task count_bit_error( input [3:0] in1,in2) ;
integer i;
real rate;
begin
if ( in1 !==in2) begin
for (i=0;i<4;i=i+1) begin
if (in1[i] !==in2[i]) bit_error_counter=bit_error_counter+1;
end
end
if (bit_error_counter >ERROR_LIMIT) begin//エラー数のLIMITを上回ったら次のDB STEP
rate=bit_error_counter;
rate=rate/bit_counter;
$display("DB=%f bits=%d error=%d error_rate=%e sigma=%f",DB,bit_counter,bit_error_counter,rate,sigma);
DB=DB+0.5;
bit_counter=0;
bit_error_counter=0;
set_sigma;
end
end
endtask
task set_sigma;
real sigma2;
begin
sigma2=$pow(10.0,-DB/10.0);//
sigma=$sqrt(sigma2);//$sqrt
end
endtask
always @(negedge clock) begin
#1;
if (Reset) ;
else begin
VPI_Data=$my_random_for_dwm(sigma);//DWM 用 VPI
end
end
always #(`CYCLE) clock=~clock;
hardware # ( .BW(BW)) hw ( .indata(indata), .outdata(outdata), .clock(clock), .Reset(Reset) );
endmodule
VPIソースです。
static int sys_my_random_for_dwm_size_tf()//Aug.42003
{
return 32;
}
/* position of right-most step */
#define PARAM_R 2.96454468156
/* level values */
static const double ytab[32] = {
1,
0.907475617638,
0.8387295158,
0.780775785072,
0.729446868113,
0.682776316768,
0.639647716015,
0.599353201849,
0.561409662765,
0.525469625896,
0.491273228111,
0.458620229136,
0.427352696597,
0.397343775955,
0.368490127285,
0.340706676394,
0.313922886649,
0.288080067698,
0.263129417761,
0.23903060658,
0.215750777731,
0.193263899438,
0.171550433862,
0.150597335281,
0.130398439093,
0.110955385052,
0.0922793705114,
0.0743943543072,
0.0573431209268,
0.0411998625242,
0.0261009770388,
0.0123479825627,
};
/* quick acceptance check */
static const unsigned long ktab[32] = {
0,
12465559,
14143380,
14859376,
15254596,
15503949,
15674743,
15798352,
15891342,
15963265,
16020003,
16065358,
16101882,
16131333,
16154939,
16173564,
16187797,
16198017,
16204425,
16207053,
16205755,
16200181,
16189707,
16173323,
16149413,
16115345,
16066638,
15995077,
15883832,
15691829,
15281721,
15063248
};
/* quick value conversion */
static const double wtab[32] = {
2.6265107239e-08,
3.5349827263e-08,
4.19328107856e-08,
4.73449074891e-08,
5.20705831605e-08,
5.63468970139e-08,
6.03100167664e-08,
6.40468149156e-08,
6.76171489147e-08,
7.10648776583e-08,
7.44238783956e-08,
7.77216053723e-08,
8.09813473119e-08,
8.42237615336e-08,
8.74679979284e-08,
9.0732597892e-08,
9.4036290297e-08,
9.73987784303e-08,
1.00841606117e-07,
1.04389204949e-07,
1.08070261837e-07,
1.11919620405e-07,
1.15981071511e-07,
1.20311664676e-07,
1.24988739931e-07,
1.30122131768e-07,
1.35877026736e-07,
1.42521234138e-07,
1.50537318078e-07,
1.60949814958e-07,
1.76700632665e-07,
1.96806467179e-07
};
unsigned long s1=0xffffffff,s2=0xffffffff,s3=0xffffffff;
unsigned long taus88_int ()
{/*Generates numbers between 0 and 1.*/
unsigned b;
b =(((s1 <<13)^s1)>>19);
s1 =(((s1 &4294967294)<<12)^b);
b =(((s2 <<2)^s2)>>25);
s2 =(((s2 &4294967288)<<4)^b);
b =(((s3 <<3)^s3)>>11);
s3 =(((s3 &4294967280)<<17)^b);
return ((s1 ^s2 ^s3));
}
double taus88 ()
{
return (taus88_int()*2.3283064365e-10);
}
double
gsl_ran_gaussian_ziggurat ()
{
unsigned long U, sign, i, j;
double x, y;
while (1) {
U = taus88_int();
i = U & 0x0000001F; /* 7 bit to choose the step */
sign = U & 0x00000020; /* 1 bit for the sign */
j = U>>6; /* 24 bit for the x-value */
x = j*wtab[i];
if (j < ktab[i]) break;
if (i<32) {
double y0, y1;
y0 = ytab[i];
y1 = ytab[i+1];
y = y1+(y0-y1)*taus88();
} else {
x = PARAM_R - log(1.0-taus88())/PARAM_R;
y = exp(-PARAM_R*(x-0.5*PARAM_R))*taus88();
}
if (y < exp(-0.5*x*x)) break;
}
return sign ? x : -x;
}
unsigned quantize(double rvalue)
{
double R;
int qdata;
R=rvalue*1;
if (R>=1.5) qdata=3;
else if (R>=1.0) qdata=2;
else if (R>=0.5) qdata=1;
else if (R>=0) qdata=0;
else if (R>=-0.5) qdata=-1;
else if (R>=-1.0) qdata=-2;
else if (R>=-1.5) qdata=-3;
else qdata=-4;
qdata &=0x7;//3bit 切り出し
return qdata;
}
unsigned get_random_data()
{
unsigned y;
unsigned r0,r1,c0,c1;
unsigned y0,y1,y2,y3;
unsigned outdata;
y=taus88_int();//ランダムデータを得る。下位4ビットしか使っていない
//ビット切り出し
y0=y & 0x01;
y1=(y >> 1) & 0x01;
y2=(y >> 2) & 0x01;
y3=(y >> 3) & 0x01;
//パリティ計算
r0=y0 ^ y1;
r1=y2 ^ y3;
c0=y0 ^ y2;
c1=y1 ^ y3;
outdata=y0;
outdata |=y1 <<1;
outdata |=y2 <<2;
outdata |=y3 <<3;
outdata |=r0 <<4;
outdata |=r1 <<5;
outdata |=c0 <<6;
outdata |=c1 <<7;
outdata <<=24;// 上位8ビットが訂正前ノイズ加算前エンコードデータ
return outdata;
}
unsigned make_input_data(double target_sd)
{
double rvalue;
unsigned temp;
unsigned i,j;
double noise;
unsigned outdata;
unsigned word;
outdata=get_random_data();//エンコーダ上位8ビットにエンコードデータ
//3ビットアナログ受信値の作成
for (i=0;i<8 ;i++) {
noise=gsl_ran_gaussian_ziggurat();//1.0 ガウスノイズ生成
#ifdef STATISTICS
statistics(noise);//ガウスノイズの度数分布処理
#endif
noise *=target_sd;//ノイズシグマ乗算
if ( (outdata >> (24+i)) & 0x01) {
rvalue=-1.0;//1-> -1に
}else rvalue=+1.0;//0-> +1にエンコード
rvalue +=noise;
temp=quantize(rvalue);//3ビットに量子化
temp <<=3*i;
outdata |=temp;
}
return outdata;
}
static int sys_my_random_for_dwm(char* name)
{
vpiHandle systfref,argsiter,argh;
t_vpi_value value;
double r;
systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */
argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of passed arguments */
argh = vpi_scan(argsiter);/* get the one argument - add loop for more args */
if(!argh){
vpi_printf("$VPI missing parameter.\n");
return 0;
}
value.format = vpiRealVal;
vpi_get_value(argh, &value);
r = value.value.real;
value.value.integer =make_input_data(r) ;
value.format = vpiIntVal;/* return the result */
vpi_put_value(systfref, &value, NULL, vpiNoDelay);
vpi_free_object(argsiter);
return(0);
}
extern "C" void sys_math_vpi_register()//VPI DLLロード時CALLされ、$Functionが登録される。
{
s_vpi_systf_data tf_data;
//Dec.30.2005 FOR DWM
tf_data.type = vpiSysFunc;//Define as function
tf_data.subtype =vpiIntFunc;//return by integer
tf_data.tfname = "$my_random_for_dwm";
tf_data.user_data = "$my_random_for_dwm";
tf_data.calltf = sys_my_random_for_dwm;
tf_data.compiletf = 0;
tf_data.sizetf = sys_my_random_for_dwm_size_tf;//func なので返す
vpi_register_systf(&tf_data);
}
RTLシミュレータで、Cのデバッグをするなんて、筆者くらいでしょうか。最終的な結果です。9/9.5dBの実測で、RTLシミュレーション値と一致していないのは、この2点だけ、エラー数を10で止めているためと思われます。 他のデータは、すべて1000個以上のエラー数ですが、RTLシミュレーション値とよく一致しています。どうして10で止めているかというと、時間がなかったからです。 1.3e8ビットをテストするのに、1週間以上回しました。次のデータ9.0dbでは、おそらく3週間はかかってしまうでしょう。 ![]() 生成したAWGNが本当にガウス分布しているのかも調べました。カイ自乗検定で合格です。 ![]() 提出したレポートです。 システムの検討からバックアノテーション後の遅延シミュレーションまで、単一の言語Verilog HDL上で検討を行いました。 足りない部分や、速度的にネックの部分は、C/C++で記述して、Verilog HDLで呼び出してしまえばよいのです。 、 テストベンチがシステムの検討から遅延シミュレーションまで、変わらなかったことにお気づきでしょうか?その事がHDLを使う もっとも大きな点だと筆者は思います。確かにH/Wは、抽象から具体へ、すなわち、モデル->RTL->ゲートー>遅延つきゲートに変化しましたが、テストベンチは、システムで 使ったものをそのまま使用しています。しかも、この記述は、SystemVerilogになっても変更の必要はありません。 20年も前に設計された言語が、その基本機能はそのままに、すくなくとも、今後10年間、生き続けるのは、すごいことだと思います。 LDPCのVerilog HDL記述 コンテスト課題は、LDGMの特殊例です。少し横道に逸れますが、LDPCのVerilogコーディング例は、Opencoresも含めてまとまったものは、 公開されていないようなので、ここで記述してみようかと思います。 幸い、MATLABやCでの例は、結構ありますので、それをポートしていきたいと思います。 T.B.C. Verilog で画像処理?(Version 1.74A/3.24A) Note:以下は、Veritak拡張オプションをEnableにして8ビット単位で書いています。これをEnableしないと、Verilog-2001の規格では、%uは、32ビット単位で行うという規定があり、C言語のイメージをそのまま移植した以下のソースは正しく動作しません。 3.24Aから、代わりに$fwriteの%cを使えますが、バイナリ0を書いても空白(20)にしてしまうシミュレータもありシミュレータ間で実装の差異があるようです。また、Veritakでは、1バイトライト用にfputcも使用できますが、これは、Standard(LRM)ではありません。 Verilog 2001でサポートされた、多次元配列とバイナリファイルを扱うシステムタスクの例です。 この例では、24ビットのBMPファイル(3port.bmp)をバイナリで読み込んで、 濃淡白黒画像(after_abc2.bmp)と、2値化画像(after_abc3.bmp)にしています。 速度を求める場合は、CでVPIを書いた方がよいのは、言うまでもありません。 バイナリのライトを行うには、$fwrite(”%u”、xx)、引数は%uは、強制バイナリ2進のフォーマット指示になります。通常、 Verilog HDLでは、4値なのでメモリ上では物理2ビットにアサインされていますが、ここでは、強制的に1ビットに対応します。 また、注意点として、バイナリでR/Wする場合は、 $fopenは、テキストモードではなく、バイナリモードで開かなくてはいけません。(筆者も最初テキストモードで開いて しまって、変な結果に悩んでしまいました。) また、バイナリリードを行うには、$freadを使います。 無変換(after_abc1.bmp) ![]() 濃淡白黒画像(after_abc2.bmp) ![]() 2値化画像(after_abc3.bmp) ![]()
Verilog でテキストファイル処理?(Version 1.74A) 上の例は、バイナリファイルを扱う例でしたが、テキストファイル処理を行うこともできます。 テストベンチで結果の妥当性をチェックする用途として機会は多いのではないでしょうか? テキストファイル処理には、$fseek,$fscanf,$fdisplay,$fell等を使います。殆どCそのままですね。 下は、256MBのテキストファイルを作成して、$fseekで$randomに飛び、書き込んであるDataの妥当性をチェックしています。 なお、$randomは、符号付で返すので、$unsignedもしくは、{}で、符号なしにします。 //Sep.21.2005
//$fseek/$fscanf/$fdisplay Test
module large_file;
parameter integer WORDS=1024*1024*8;//1024*1024*34 BYTES;
parameter integer STR_LEN=32+2;//CR+LF
integer i;
integer fp;
integer offset;
reg [255:0] R2;
initial begin
//Open
fp=$fopen("large_file.txt","w+");//Open By Write & Read Mode
if (!fp) begin
$display("File Can not be opend.!");
$finish;
end
//Write
$display("Writing Large File..");
for (i=0; i<WORDS;i=i+1) begin
R2={i+7,i+6,i+5,i+4,i+3,i+2,i+1,i};
if (i%10000==1) $display("%d",i);
$fdisplay(fp,"%32x",R2[127:0]);
end
$display("Large Text File genrated Words=%d Total %fMBytes",WORDS,(WORDS*STR_LEN)/1e6);
//Trival Check
trival_check(fp);
//Random Check
random_check(fp);
end
task disp_current_position(input integer fp);
integer position;
begin
position=$ftell(fp);
$display("Current Posion=%d",position);
end
endtask
task trival_check(input integer fp);
parameter OFFSET=10;
begin
$display("Trival Check Starts...");
offset=$ftell(fp);
disp_current_position(fp);
offset=offset-OFFSET*STR_LEN;
$fseek(fp, offset,0);
disp_current_position(fp);
for (i=0;i<OFFSET;i=i+1) begin
$fscanf(fp,"%h\n",R2);
$display("%h",R2);
end
$display("Trival Check Done.\n");
end
endtask
task random_check(input integer fp);
integer EOF;
parameter integer No_of_Checks=1000_000;
integer i,position,aligned_pos,word_pos;
reg [127:0] word;
begin
$display("Random Check.Starts.. It takes several minutes.");
$fseek(fp,0,2);//Go to EOF
EOF=$ftell(fp);//EOF position
disp_current_position(fp);
for (i=0;i< No_of_Checks;i=i+1) begin
position={$random} % EOF;//or $unsigned($random) %EOF
word_pos=(position/STR_LEN);
aligned_pos=word_pos*STR_LEN;
$fseek(fp,aligned_pos,0);//Goto aligned_pos
$fscanf(fp,"%h\n",R2);
word={word_pos+3,word_pos+2,word_pos+1,word_pos};
if (word !==R2[127:0] ) begin//Check result
$display("Fails. Error detected");
$stop;//assert(0);
end
end
$display("Random Test passed. Random %d seeks performed . Error Detected 0",No_of_Checks);
end
endtask
endmodule
Opencores Veritak プロジェクトファイル(Version1.41A) Opencoresには、プロフェッショナルが書いた様々なIPコアがあります。これを簡単に、評価/研究してみたい方も多いのではないでしょうか? ところが、Opencores流儀のINCLUDE ファイルや、Defineの設定等が必要な場合があり、 原作者のシミュレータ環境がないと簡単にいかないところがあります。シミュレータが走り出すまでに挫折してしまった人もいるかもしれません。 そこで、Veritakプロジェクトファイルを作成し便宜を図りました。 解凍して、「Verilogプロジェクト=>Load Verilog」でプロジェクトファイルをロード、「Go」だけでテストベンチが走りだします。 人のRTLソースは、理解しがたいものですが、トレースファイル(タグファイル)も添付していますので、解析の一助になるでしょう。 必要に応じてトレースモードでコンパイルすることで、階層View、VeriPad、トレースファイル(タグファイル)、WaveformView上でのドライバ特定機能が連動し理想的な解析環境になる筈です。 プロジェクトは、以下の検証可能なテストベンチ付のコアについて作成しました。 プロジェクトは複数作成している場合がありますが、その意味は、次の通りです。
(たとえば、Pentium500MHzだと、6倍弱程度にはなっているようです。 <Opencoresについて> 大作が多いです。なかには、シミュレーション時間が異常にかかっているものがありますが、(上のCPUベンチマークテスト例では、長くても数分です) これは、Regression Testで、網羅的なテストをしているためです。中身は、task毎で分割記述され、それをFOR LOOPで回しているので、設計エッセンスのシミュレーションでは、それほど多くを見る必要はないでしょう。 ハードウェアの記述に、 たとえば、A<=#1Aを使っている(シミュレーションサイクルが余計にかかる以外に何の益もない記述)、テストベンチでのRace記述が多い、等、 必ずしもよい記述と言えないものも含まれていますが、全体のレベルとしては高く、大変参考になると思います。 ところで、このOpencores はどうしてあるの?設計したプロセッサは、何の意味あるの?という質問がどこからか飛んできそうです。筆者の場合は、FAQSにVeritakの目指しているものとして書いてありますのそちらをご参照を。 Opencoresの人達の答えを引用します。 Mainly for fun. Other reasons include:
CRCのRTL自動生成プログラムの解析(May.5.2005 終了) <概略> 下記リンクで、WEB上で、任意生成多項式、任意ビット数のRTLソースが得られます。 http://www.easics.be/webtools/crctool 実際にやってみると確かに生成します。ところで、これは、どうやって生成しているのでしょうか?生成プログラムを書いて、同じコードを出力することを確認しました。 また、使い方の例として、Verilog-2001でテストベンチを書きました。(本文は、英文です。) なお、CRCについては以下が詳しいです。 インターフェース2004 12月号CRC回路の作り方とアレンジ 森岡澄夫氏 理論から学びたい方は、 符号理論 電子情報通信学会 今井秀樹 をお勧めします。 =>CRC32(RFC2083) のC プログラムと結果が合わない、というご質問をいただきました。 解析してみたところ、ビットのMSBとLSBが逆順になっておりました。上のH/W生成器は、MSBから入力する仕様になっています。 これを、LSBから入力する仕様のCプログラムと比較する場合は、ビットをリバースする必要がありますね。 そこで、ビットをリバースする記述を追加すると次のようになります。これによるH/Wリソース追加はありません。(配線だけです) `define CYCLE (10)
`timescale 1ns/1ns
module crc_module_test;
reg RES; // Reset
reg CLK; // clock
reg[31:0] DATA; // data
wire w_RESULT;//TAK
always #20 CLK =~CLK;
parameter RATE = 50;
wire [31:0] DReverse;//TAK
wire [31:0] ResultReverse;//TAK
generate //TAK
genvar g;
for (g=0; g<32;g=g+1) begin :loop
assign DReverse[g]=DATA[31-g];
assign ResultReverse[g]=_crc_module.crc_reg[31-g];
end
endgenerate
initial begin
#0
RES = 0;
CLK = 0;
DATA = 0;
#200
RES=1;
#(RATE*2)
@(negedge CLK);//NEG TAK
DATA = 32'h000_0001;
@(negedge CLK);//NEG TAK
DATA = 0;//32'h30303030;
#1000;
$finish;
end
crc_module _crc_module(
.RES(RES),
.CLK(CLK),
.DATA(DReverse),//TAK
.CRC_RESULT(w_RESULT)
);
endmodule
通常、Cプログラムでは、256Byteのテーブルで、1Byteづつ計算されますが、上のH/W Generatorでは、任意の並列度(任意の語長)で計算することができます。本例では、32ビット/CLOCKです。 =>その後、さらに、次のようなデータをファイルから読み出したい、というご質問がありました。 ; 最初の行コメント大丈夫かな? 12345678 aaaa5555 ; これは、コメント行です。冒頭に;が来ること。 1111aaaa 3456789a ; これも、コメント行です。 abcdef01 58d7e7e0 ;最後の行のコメント大丈夫かな?次の記述が、ユーザ様からご提供頂いたソースです。(筆者とやりとりしながら記述されました。=>がコメント部分です。) この方は、Cでの記述は、筆者より経験をお持ちです。が、Verilog HDLは、似て非なる言語です。Cの類推から間違えやすい 箇所、Verilog 2001のFILE IO/generate 、そして、for loop におけるbreak、continue処理に関する記述が参考になると思います。 テストベンチの記述です。 `define CYCLE (10)
`timescale 1ns/1ns
module crc_module_test;
reg RES; // Reset
reg CLK; // clock
reg START; // calc start
reg[31:0] DATA; // data
parameter integer ROW_LINES=16;//=>パラメタライズ
wire w_RESULT;
always #20 CLK =~CLK;
parameter RATE = 50;
//--------------------------------------------------------------------
//ビット入れ替えMSB<->LSB, D30<->D1,....D16<->D15の場合はこう書くべし
//--------------------------------------------------------------------
//=>次は、ちょっと技です。 assgin DReverse[0]=DATA[31];..... と延々書いてもOKです。
wire [31:0] DReverse;
wire [31:0] ResultReverse;
generate //
genvar g;
for (g=0; g<32;g=g+1) begin :loop
assign DReverse[g]=DATA[31-g];
assign ResultReverse[g]=_crc_module.crc_reg[31-g];
end
endgenerate
//-----------------------------------------------------------
//task readFDATAで読み込むためのバッファを確保
//-----------------------------------------------------------
reg[31:0] FDATA[0:ROW_LINES]; //実際には2048くらい使いたい。=>パラメタライズ
reg[31:0] td;
reg [8*100:1] str_buffer;//=>$fgets は、テキスト読み込みなのでStrバッファを用意します。
//=>8は、charの意味です。コメントもありますので、十分大きく(100)に変更しました。
//-----------------------------------------------------------
//読み込みを開始
//-----------------------------------------------------------
task readFDATA;
integer fp;
integer i, r,c;
begin
fp = $fopen("tp.txt", "r"); //ダメ元でやってみました。
//=>OKです。 "r" を指定しないとDefault"w"なので
//大切なファイルが壊れてしまいます。
if (!fp)
begin
$display(" Open error!\n"); //ファイルが存在しなければ終了
$finish;
end
begin : gloval_loop //=>break するためのラベル
for(i=0; i< ROW_LINES;i=i+1) // i<16 =>直接数値指定するよりROW_LINES パラメータで指定した方が保守性がよろしいかと思います。
begin :local_loop //=>continueするためのラベル
if( !$feof(fp) )
begin
c=$fgetc(fp);//=>一文字読み込みます。
if (c==";") begin //=>コメント処理は冒頭の1文字を読みます。
//=> str_buffer[1] では、一ビットのセレクトになってしまいます。
//=>また、";"のバッファ上の位置は、コメントの長さによってしまう為、上記の記述としました。
r = $fgets(str_buffer,fp);//; の次から1行読みます
$display("Comment %s",str_buffer);//コメント行発見
i=i-1;//かなり苦しいです。。
disable local_loop;//continue
end
$ungetc(c,fp);//読み込んだ1文字を戻します。
// r = $fgets(td, 10, fp); //=>ファイルから1行読み込む。読み込むデータは32ビット分
r = $fgets(str_buffer,fp); //=> Cとは違います。10を指定するパラメータはありません。
$sscanf(str_buffer,"%h\n",td); //=>sscanf で改行までバッファを解析して数値に直します。
$display("%d:read data= %h \n",i,td);
FDATA[i] = td; //32ビット分のデータを格納
end
else
begin
$fclose(fp);
//ファイルの最後に到達したら抜けたい
//Cでいうbreakしたいが、表記不明
disable gloval_loop; //=>でbreakに相当します。
//=>ちなみにcontinue は、disable local_loop としてください。
end
end //end local_loop
end //gloval_loop
$display("Exit Task ");
end //endtask
endtask
integer i;
initial begin
// integer i;=> initail processの外で宣言してください。
readFDATA; //定義したタスクをコール
RES = 0;
CLK = 0;
DATA = 0;
START = 0;
i=0;
#200
RES=1;
#(RATE*2)
//ファイルから読み込んだFDATAを使ってCRC入力したい。
for(i=0; i<ROW_LINES; i=i+1)//=>i++ は、Verilog2001ではありません。
begin
@(negedge CLK);//=>ハードがPosなので、neg で入力してやれば、レース問題を回避できます。
if(i==0)
START = 1;
DATA = FDATA[i] ; //わざわざ数値を指定するのではなくファイルから読んだ値を使いたい
end
#1000;
$finish;
end
crc_module _crc_module(//=>DUT ハードウェアをインスタンス化します
.RES(RES),
.CLK(CLK),
.START(START),
.DATA(DReverse),
.CRC_RESULT(w_RESULT)
);
endmodule
ハードウェア本体の記述です。合成可能なソースに仕上がっていると思います。 /****************** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||