Welcome to Our Company
Home
Tutorial
Download
Opencores
F.A.Q.
Support
Purchase
Links

Information

このページでは、当社で開発したIPやトランスレータを使ってVHDLコアをVerilogに翻訳したもの、OpencoresのVerilog等を紹介していきます。

とりあえず、既存の設計例を通して、CPUアーキテクチャ、HDLコーディング技法の研究にお役になれば幸いです。


1.ベンチマークテスト
  同じCソースプログラムをシミュレータ上で動かして総クロック数を評価してみます。Cソースプログラムは、リードソロモン(5インターリーブ、3重訂正)です。下記オープンソースコアについて行いました。
駆動周波数の比較ではなくCPIの比較であることと、オリジナルとの比較ではなく、Opencores上での比較であることに注意してください。


注意:コンパチブルコアは、オリジナルと命令の実行ステート数が異なります。あくまでオープンソースコア上の実行時間比較であることに注意してください

この評価の観点としては、同じクロック周波数で駆動していますので(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で画期的だったのは、まがりなりにも単一の言語で全てを記述できることです。

この、
  • システム(アルゴリズム・アーキテクチャ)設計
  • RTL
  • ゲートレベル
という段階を踏むやり方は、いわゆるトップダウン手法でもあります。実体は、一つなのですが、それは、抽象から具体へ展開され、置き換えられていく過程でもあります。
コンテストという共通の題材を借りて、一つの(実用的な)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.690000
seed は、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
y2 y3 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)だけになります。
同様にして以下のようにまとめることができます。
1個のエラー パリティエラーとなる式 訂正
y0 (1),(3) y0^=1;
y1 (1),(4) y1^=1;
y2 (2),(3) y2^=1;
y3 (2),(4) y3^=1;
r0 (1) 必要ない
r1 (2) 必要ない
c0 (3) 必要ない
c1 (4) 必要ない
 データの訂正は、データビットを反転させてやれば、よいです。回路にするのも、簡単ですね。
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----------


課題の取り組み方針
コンテストの取り組み方針は、上の検討で決まりました。懲りずにまた、参戦してみたいと思います。狙いは、
  • スループット 4Gbps (Iterative Decoderは使わない独自方式)
  • AWGN生成器搭載->1e8ビットでのBERを実測(スタータキット3)
に決めましたAWGNについてもサーベイしてみました。
課題を解くよりも、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理論値は、
S/N BER
5dB 3.75e-2
6dB 2.29e-2
7dB 1.25e-2
8dB 5.95e-3
9dB 2.39e-3
です。
さて、訂正の効果は、どうでしょうか?グラフにしてみないと良くわかりませんが、BERは確かに下がっているので、効果はあると
言えるでしょう。問題は、これをMaximum Likelihood Decodingした場合どうなるか?、です。

F:\regression_test\ldgm\raw_error_rate.v(2)::raw_error_rate
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------

10000個のランダムデータを生成中です。
パリティを生成中です。
-------------------------------------------------
ノイズパワー 0.000000[dB] 狙いで生成中です。
ノイズパワー 0.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 1607 エラーレート=1.607000e-001
ハード訂正回路結果
トータルエラー数= 1280 エラーレート=1.280000e-001


-------------------------------------------------
ノイズパワー 3.000000[dB] 狙いで生成中です。
ノイズパワー 3.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 810 エラーレート=8.100000e-002
ハード訂正回路結果
トータルエラー数= 402 エラーレート=4.020000e-002


-------------------------------------------------
ノイズパワー 5.000000[dB] 狙いで生成中です。
ノイズパワー 5.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 393 エラーレート=3.930000e-002
ハード訂正回路結果
トータルエラー数= 104 エラーレート=1.040000e-002


-------------------------------------------------
ノイズパワー 6.000000[dB] 狙いで生成中です。
ノイズパワー 6.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 238 エラーレート=2.380000e-002
ハード訂正回路結果
トータルエラー数= 41 エラーレート=4.100000e-003


-------------------------------------------------
ノイズパワー 7.000000[dB] 狙いで生成中です。
ノイズパワー 7.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 110 エラーレート=1.100000e-002
ハード訂正回路結果
トータルエラー数= 12 エラーレート=1.200000e-003


-------------------------------------------------
ノイズパワー 8.000000[dB] 狙いで生成中です。
ノイズパワー 8.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 52 エラーレート=5.200000e-003
ハード訂正回路結果
トータルエラー数= 2 エラーレート=2.000000e-004


-------------------------------------------------
ノイズパワー 9.000000[dB] 狙いで生成中です。
ノイズパワー 9.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 23 エラーレート=2.300000e-003
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000


-------------------------------------------------
ノイズパワー 10.000000[dB] 狙いで生成中です。
ノイズパワー 10.024707[dB]の生成をしました。
ノイズを加算中です。
訂正なしシステム結果
トータルエラー数= 8 エラーレート=8.000000e-004
ハード訂正回路結果
トータルエラー数= 0 エラーレート=0.000000e+000



---------- シミュレーションを終了します。time=0----------

MLによる復号
ユークリッド距離による復号です。上の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)を使うと速い結果になりました。
合成ソフト/デバイス 合成対象 LUT数 遅延(ns) 周波数(MHz) LUT数比 速度比 転送レート(Gbps)
Quartus5.1/ リファレンス回路 14 10.57 94.60 1 1
EP2S15F484C3 hardware.v 309 2.11 473.26 22.07 5.003 3.79
ISE7.1/ リファレンス回路 17 12.73 78.57 1 1
XC3S200-4 hardware.v 291 5.37 186.22 17.12 2.370 1.49


遅延シミュレーション
 設計したコアの遅延シミュレーションを行ってみました。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)

//参考にしたCソースは、
//http://www.fit.ac.jp/elec/7_online/lu/sample/bmp_image_proc.cpp
//です。

`define Y_SIZE 2048     // 処理できる最大画像
`define X_SIZE 2048

`define HIGH   255      // 画像の最大強度値
`define LOW      0      // 画像の最小強度値
`define LEVEL  256      // 画像の強度レベル値

module bmp_test;
parameter read_filename="3port.bmp";
parameter write_filename1="after_abc1.bmp";
parameter write_filename2="after_abc2.bmp";
parameter write_filename3="after_abc3.bmp";
parameter [7:0] INTENSITY=100;//0-255

integer  biCompression,       
      biSizeImage,
      biXPelsPerMeter,
      biYPelsPerMeter,
      biClrUsed, 
      biClrImportant;

reg [15:0]  bfType;
integer bfSize;
reg [15:0]   bfReserved1,      bfReserved2;
integer  bfOffBits;
integer  biSize,   biWidth, biHeight;
reg [15:0] biPlanes,    biBitCount;


reg [7:0] image_in [0:`Y_SIZE][0:`X_SIZE][0:2]; // 入力カラー画像配列
reg [7:0] image_out [0:`Y_SIZE][0:`X_SIZE][0:2];        //出力カラー画像配列
reg [7:0] image_bw [0:`Y_SIZE][0:`X_SIZE];              //濃淡画像配列


//********************************************
//   24Bitビットマップファイル読み込み       *
//********************************************


task readBMP(input [128*8:1] read_filename);
        integer fp;
        integer  i, j, k;
        reg [7:0] byte;
        begin   
                // ファイルオープン
                fp = $fopen(read_filename, "rb") ;//must be binary read mode
                if (!fp) begin
                        $display("readBmp: Open error!\n");
                        $finish;
                end
                $display("input file : %s\n", read_filename);

                // ヘッダー情報読み込む
                $fread(bfType,  fp);
                $fread(bfSize,  fp);
                $fread(bfReserved1, fp);
                $fread(bfReserved2, fp);
                $fread(bfOffBits,  fp);

                $fread(biSize,  fp);
                $fread(biWidth, fp);
                if (biWidth%4) begin
                        $display("Sorry, biWidth%4 must be zero in this program. Found =%d",biWidth);
                        $finish;
                end
                $fread(biHeight, fp);
                $fread(biPlanes, fp);
                $fread(biBitCount,  fp);
                if (biBitCount !=24) begin
                        $display("Sorry, biBitCount must be 24 in this program. Found=%d",biBitCount);
                        $finish;
                end
                $fread(biCompression,  fp);
                $fread(biSizeImage,  fp);
                $fread(biXPelsPerMeter, fp);
                $fread(biYPelsPerMeter, fp);
                $fread(biClrUsed,  fp);
                $fread(biClrImportant,  fp);

        // RGB画像データ読み込む
                for (i=0; i<  biHeight; i=i+1) begin
                        for (j=0; j<  biWidth; j=j+1) begin
                                for (k=0; k<3; k=k+1) begin
                                        $fread(byte,fp);
                                        image_in[biHeight-i][j][2-k]=byte;
                                end
                        end
                end
                $display("Current POS=%d",$ftell(fp));
                $fclose(fp);
        end
endtask


//******************************************************
//   24ビット-ビットマップデータをBMPファイルに出力    *
//******************************************************

task writeBMP(input [128*8:1] write_filename,input O);
        integer fp;
        integer  i, j, k;
        
        begin
        // ファイルオープン 
                fp = $fopen(write_filename, "wb");//must be binary read mode
                if (!fp) begin
                        $display("writeBmp: Open error!\n");
                        $finish;
                end
                $display("output file : %s\n", write_filename);

        // ヘッダー情報 
                $fwrite(fp,"%u",bfType);
                $fwrite(fp,"%u",bfSize);
                $fwrite(fp,"%u",bfReserved1);
                $fwrite(fp,"%u",bfReserved2);
                $fwrite(fp,"%u",bfOffBits);

                $fwrite(fp,"%u",biSize);
                $fwrite(fp,"%u",biWidth);
                $fwrite(fp,"%u",biHeight);
                $fwrite(fp,"%u",biPlanes);
                $fwrite(fp,"%u",biBitCount);
                $fwrite(fp,"%u",biCompression);
                $fwrite(fp,"%u",biSizeImage);
                $fwrite(fp,"%u",biXPelsPerMeter);
                $fwrite(fp,"%u",biYPelsPerMeter);
                $fwrite(fp,"%u",biClrUsed);
                $fwrite(fp,"%u",biClrImportant);

        // ビットマップデータ 
                for (i=0; i< biHeight; i=i+1) begin
                        for (j=0; j< biWidth; j=j+1) begin
                                for (k=0; k<3; k=k+1)  begin
                                        if (O) $fwrite(fp,"%u",image_out[biHeight-i][j][2-k]);
                                      else $fwrite(fp,"%u",image_in[biHeight-i][j][2-k]);
                                end
                        end
                end
                $display("Current WPOS=%d",$ftell(fp));
                $fclose(fp);
        end
endtask



//**********************************************
// RGBカラー画像を256諧調白黒濃淡画像へ変換 *
//**********************************************

task BMPto256BW;
        integer y, x, a;
        begin
                for (y=0; y<biHeight; y=y+1) begin
                        for (x=0; x<biWidth; x=x+1) begin
                                a =$rtoi(0.3*image_in[y][x][0] + 0.59*image_in[y][x][1] + 0.11*image_in[y][x][2]);
                                if (a<`LOW) a = `LOW;
                                if (a>`HIGH) a = `HIGH;
                                image_bw[y][x] = a;
                        end
                end
        end
endtask



//****************************************
// 白黒画像を24bitビットマップ形式に変換 *
//****************************************
task BWto24BMP;
        integer  y, x, a;
        begin
                for (y=0; y<biHeight; y=y+1) begin
                        for (x=0; x<biWidth; x=x+1) begin
                                a = image_bw[y][x];
                                image_out[y][x][0] = a;
                                image_out[y][x][1] = a;
                                image_out[y][x][2] = a;
                        end
                end
        end
        
endtask



//****************************************
// 白黒画像の2値化                      *
//****************************************
task toBinary( input [7:0] intensity);
        
        integer y, x;
        begin

                for (y=0; y<biHeight; y=y+1)begin
                        for (x=0; x<biWidth; x=x+1) begin
                                if(image_bw[y][x] >= intensity) image_bw[y][x]=`HIGH;
                                else image_bw[y][x] = `LOW;
                        end
                end
        end
endtask


initial begin

        
        //画像処理1:
        readBMP(read_filename);   // 画像の入力,RGB24ビットカラーBMP画像を配列に格納
        writeBMP(write_filename1,0);//無変換で書き込み

        //画像処理2:白黒変換
        BMPto256BW; // RGBカラー画像を白黒画像に変換
        BWto24BMP; // 単チャンネル白黒画像を3チャンネルBMP標準フォーマットへ変換
        writeBMP(write_filename2,1);            // 白黒画像出力

        //画像処理3: 2値化
        toBinary(INTENSITY);
        BWto24BMP;
        writeBMP(write_filename3,1);            // 白黒画像出力

end

endmodule
veritak Project File のダウンロード



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上でのドライバ特定機能が連動し理想的な解析環境になる筈です。

プロジェクトは、以下の検証可能なテストベンチ付のコアについて作成しました。
プロジェクトは複数作成している場合がありますが、その意味は、次の通りです。

除算器のプロジェクトを追加しました。
プロジェクトの名前 内容 備考
*_no_save.prj 波形Saveなしのプロジェクト。 完走を確認済み。このプロジェクトでは波形(WaveformView)をご覧になれませんのでご注意ください。
*_trace_mode.prj トレースモードでのプロジェクト
*_trace_mode2.prj トレースモード2でのプロジェクト トレースファイルの生成用



IP CORES Veritak プロジェクトファイル位置 ソース総行数 シミュレーション時間(Athlon64
3000+ 1GB Memory on W2K)
ダウンロード(ZIP) 備考
AC97 \ac97_ctrl\bench\verilog 11K 49min54sec AC97(0.3MB) ac97_no_save.prj
CAN \can 12K 0min CAN(0.2MB) can.prj
ATA \ata 4K 13min38sec ATA(1MB) Nov.22.2004 no_save_ata.prj
PCI \pci\bench\verilog 89K 2h PCI(14MB) pci_no_save.prj
USB1.1 \usb11 11K 2min37sec USB1.1(0.3MB) `includeを一箇所追加
usb11_no_save.prj
I2C \i2c 2K 2sec I2C(0.7MB) i2c.prj
ETHERNET \ethernet\ethernet 45K 4h ETHERNET(2.6MB) tb_ethernet_no_save.prj
AES \aes_core 2K 13sec AES(0.2MB) aes.prj
DES \des 2K 1sec DES(0.5MB) des_trace_mode.prj
GENERIC FIFO \generic_fifos\generic_fifos\bench\verilog 2K 2h GENERIC  
FIFO(0.1MB)
fifo_no_save.prj
GPIO \gpio\gpio 4K 41sec GPIO(0.6MB) gpio.prj
WB_DMA \wb_dma 15K 46h WB_DMA(1.5MB)
WB_CONBUS \wb_conbus 4K 6sec WB_CONBUS(0.4MB) conbus.prj
WB_CONMAX \wb_conmax 11K
10min6sec WB_CONMAX(0.2MB)
Divider dividers\bench\verilog divierders divider.vtakprj (Version 2.11以上 RunLength1000us)
 **秒数まで、明示してあるものは、Goから$finishまでの時間です。(コンパイラステータス画面に出ます。) これで、ご自分のPCとAthlon64 3000+ DUAL PC3200 2GB メモリとPCの性能比較ができます。
(たとえば、Pentium500MHzだと、6倍弱程度にはなっているようです。
Veritak Version は、上がっており、計測時間は、古いVersionのデータです。例えば、ダイレクトコンパイルドVersion 2.20で、AC97を見ると10分以下,ETHERNETは、15分以下になっています。

<Opencoresについて>

 大作が多いです。なかには、シミュレーション時間が異常にかかっているものがありますが、(上のCPUベンチマークテスト例では、長くても数分です) 
これは、Regression Testで、網羅的なテストをしているためです。中身は、task毎で分割記述され、それをFOR LOOPで回しているので、設計エッセンスのシミュレーションでは、それほど多くを見る必要はないでしょう。

ハードウェアの記述に、 たとえば、A<=#1Aを使っている(シミュレーションサイクルが余計にかかる以外に何の益もない記述)、テストベンチでのRace記述が多い、等、
必ずしもよい記述と言えないものも含まれていますが、全体のレベルとしては高く、大変参考になると思います。


 ところで、このOpencores はどうしてあるの?設計したプロセッサは、何の意味あるの?という質問がどこからか飛んできそうです。筆者の場合は、FAQSにVeritakの目指しているものとして書いてありますのそちらをご参照を。
Opencoresの人達の答えを引用します。

Mainly for fun.

Other reasons include:

  • to learn,
  • to teach,
  • to improve your resume,
  • to make the world a better place to live in.
This list goes on forever, as everyone has their own reasons.




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

ハードウェア本体の記述です。合成可能なソースに仕上がっていると思います。
/******************