![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
![]() このページでは、当社で開発した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の設計ということでこちらで再設計中です。 DWMデザインコンテスト2006上級課題を解く システム(アルゴリズム・アーキテクチャ)設計に始まり、RTL コーディング、ゲートシミュレーションと、落としていくまとまった記述というのは、 企業や・研究室の奥底にあるもので、あまり公開されていないように思います。Verilog HDLで画期的だったのは、まがりなりにも単一の言語で全てを記述できることです。 この、
コンテストという共通の題材を借りて、一つの(実用的な)IPを作成することを楽しんでいますが、少しでも参考になる記述が見つかれば幸いです。 恒例のコンテストの季節になりました。符号理論がらみの出題が多いのですが、今年の問題は、難問ではないでしょうか? この積符号のデコーダは、ターボ符号や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
ハードウェア本体の記述です。合成可能なソースに仕上がっていると思います。 /*******************************************************************************
** crc check
*******************************************************************************/
module crc_module(
RES, // input :Reset
CLK,
START, // crc calc start
DATA, // input :data
CRC_RESULT
);
/****************************************************************
モジュール入力、出力
****************************************************************/
input RES; // Reset
input CLK;
input START;
input[31:0] DATA; // data
output CRC_RESULT; //
/****************************************************************
reg [内部で使用]
****************************************************************/
reg [31:0] crc_reg;
reg crc_result;
wire[31:0] w_data;
wire[31:0] w_crc_temp;
/****************************************************************
出力設定
****************************************************************/
assign CRC_RESULT = crc_result;
assign w_data = DATA;
generate //=>Verilog-2001 generate Xilinx は、OKですが、Altera 5.1では、まだ対応できていないようです。
genvar g;
for (g=0; g<32;g=g+1)
begin
:loop
assign w_crc_temp[g] = crc_reg[31-g];
end
endgenerate
//---------------------------------------------------------
// crc check
//---------------------------------------------------------
always @ (posedge CLK)
if(RES == 0)
begin
crc_reg <= 32'hffffffff;
crc_result <= 0;
end
else
begin
if(START == 1)
begin
crc_reg = nextCRC32_D32(w_data, crc_reg);
if(crc_reg == 0)
begin
crc_result <= 1;
end
end
end
///////////////////////////////////////////////////////////////////////
// File: CRC32_D32.v
// Date: Sat Jan 7 09:33:48 2006
//
// Copyright (C) 1999-2003 Easics NV.
// This source file may be used and distributed without restriction
// provided that this copyright statement is not removed from the file
// and that any derivative work contains the original copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Purpose: Verilog module containing a synthesizable CRC function
// * polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
// * data width: 32
//
// Info: tools@easics.be
// http://www.easics.com
///////////////////////////////////////////////////////////////////////
// polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
// data width: 32
// convention: the first serial data bit is D[31]
function [31:0] nextCRC32_D32;
input [31:0] Data;
input [31:0] CRC;
reg [31:0] D;
reg [31:0] C;
reg [31:0] NewCRC;
begin
D = Data;
C = CRC;
NewCRC[0] = D[31] ^ D[30] ^ D[29] ^ D[28] ^ D[26] ^ D[25] ^ D[24] ^
D[16] ^ D[12] ^ D[10] ^ D[9] ^ D[6] ^ D[0] ^ C[0] ^
C[6] ^ C[9] ^ C[10] ^ C[12] ^ C[16] ^ C[24] ^ C[25] ^
C[26] ^ C[28] ^ C[29] ^ C[30] ^ C[31];
NewCRC[1] = D[28] ^ D[27] ^ D[24] ^ D[17] ^ D[16] ^ D[13] ^ D[12] ^
D[11] ^ D[9] ^ D[7] ^ D[6] ^ D[1] ^ D[0] ^ C[0] ^ C[1] ^
C[6] ^ C[7] ^ C[9] ^ C[11] ^ C[12] ^ C[13] ^ C[16] ^
C[17] ^ C[24] ^ C[27] ^ C[28];
NewCRC[2] = D[31] ^ D[30] ^ D[26] ^ D[24] ^ D[18] ^ D[17] ^ D[16] ^
D[14] ^ D[13] ^ D[9] ^ D[8] ^ D[7] ^ D[6] ^ D[2] ^
D[1] ^ D[0] ^ C[0] ^ C[1] ^ C[2] ^ C[6] ^ C[7] ^ C[8] ^
C[9] ^ C[13] ^ C[14] ^ C[16] ^ C[17] ^ C[18] ^ C[24] ^
C[26] ^ C[30] ^ C[31];
NewCRC[3] = D[31] ^ D[27] ^ D[25] ^ D[19] ^ D[18] ^ D[17] ^ D[15] ^
D[14] ^ D[10] ^ D[9] ^ D[8] ^ D[7] ^ D[3] ^ D[2] ^
D[1] ^ C[1] ^ C[2] ^ C[3] ^ C[7] ^ C[8] ^ C[9] ^ C[10] ^
C[14] ^ C[15] ^ C[17] ^ C[18] ^ C[19] ^ C[25] ^ C[27] ^
C[31];
NewCRC[4] = D[31] ^ D[30] ^ D[29] ^ D[25] ^ D[24] ^ D[20] ^ D[19] ^
D[18] ^ D[15] ^ D[12] ^ D[11] ^ D[8] ^ D[6] ^ D[4] ^
D[3] ^ D[2] ^ D[0] ^ C[0] ^ C[2] ^ C[3] ^ C[4] ^ C[6] ^
C[8] ^ C[11] ^ C[12] ^ C[15] ^ C[18] ^ C[19] ^ C[20] ^
C[24] ^ C[25] ^ C[29] ^ C[30] ^ C[31];
NewCRC[5] = D[29] ^ D[28] ^ D[24] ^ D[21] ^ D[20] ^ D[19] ^ D[13] ^
D[10] ^ D[7] ^ D[6] ^ D[5] ^ D[4] ^ D[3] ^ D[1] ^ D[0] ^
C[0] ^ C[1] ^ C[3] ^ C[4] ^ C[5] ^ C[6] ^ C[7] ^ C[10] ^
C[13] ^ C[19] ^ C[20] ^ C[21] ^ C[24] ^ C[28] ^ C[29];
NewCRC[6] = D[30] ^ D[29] ^ D[25] ^ D[22] ^ D[21] ^ D[20] ^ D[14] ^
D[11] ^ D[8] ^ D[7] ^ D[6] ^ D[5] ^ D[4] ^ D[2] ^ D[1] ^
C[1] ^ C[2] ^ C[4] ^ C[5] ^ C[6] ^ C[7] ^ C[8] ^ C[11] ^
C[14] ^ C[20] ^ C[21] ^ C[22] ^ C[25] ^ C[29] ^ C[30];
NewCRC[7] = D[29] ^ D[28] ^ D[25] ^ D[24] ^ D[23] ^ D[22] ^ D[21] ^
D[16] ^ D[15] ^ D[10] ^ D[8] ^ D[7] ^ D[5] ^ D[3] ^
D[2] ^ D[0] ^ C[0] ^ C[2] ^ C[3] ^ C[5] ^ C[7] ^ C[8] ^
C[10] ^ C[15] ^ C[16] ^ C[21] ^ C[22] ^ C[23] ^ C[24] ^
C[25] ^ C[28] ^ C[29];
NewCRC[8] = D[31] ^ D[28] ^ D[23] ^ D[22] ^ D[17] ^ D[12] ^ D[11] ^
D[10] ^ D[8] ^ D[4] ^ D[3] ^ D[1] ^ D[0] ^ C[0] ^ C[1] ^
C[3] ^ C[4] ^ C[8] ^ C[10] ^ C[11] ^ C[12] ^ C[17] ^
C[22] ^ C[23] ^ C[28] ^ C[31];
NewCRC[9] = D[29] ^ D[24] ^ D[23] ^ D[18] ^ D[13] ^ D[12] ^ D[11] ^
D[9] ^ D[5] ^ D[4] ^ D[2] ^ D[1] ^ C[1] ^ C[2] ^ C[4] ^
C[5] ^ C[9] ^ C[11] ^ C[12] ^ C[13] ^ C[18] ^ C[23] ^
C[24] ^ C[29];
NewCRC[10] = D[31] ^ D[29] ^ D[28] ^ D[26] ^ D[19] ^ D[16] ^ D[14] ^
D[13] ^ D[9] ^ D[5] ^ D[3] ^ D[2] ^ D[0] ^ C[0] ^ C[2] ^
C[3] ^ C[5] ^ C[9] ^ C[13] ^ C[14] ^ C[16] ^ C[19] ^
C[26] ^ C[28] ^ C[29] ^ C[31];
NewCRC[11] = D[31] ^ D[28] ^ D[27] ^ D[26] ^ D[25] ^ D[24] ^ D[20] ^
D[17] ^ D[16] ^ D[15] ^ D[14] ^ D[12] ^ D[9] ^ D[4] ^
D[3] ^ D[1] ^ D[0] ^ C[0] ^ C[1] ^ C[3] ^ C[4] ^ C[9] ^
C[12] ^ C[14] ^ C[15] ^ C[16] ^ C[17] ^ C[20] ^ C[24] ^
C[25] ^ C[26] ^ C[27] ^ C[28] ^ C[31];
NewCRC[12] = D[31] ^ D[30] ^ D[27] ^ D[24] ^ D[21] ^ D[18] ^ D[17] ^
D[15] ^ D[13] ^ D[12] ^ D[9] ^ D[6] ^ D[5] ^ D[4] ^
D[2] ^ D[1] ^ D[0] ^ C[0] ^ C[1] ^ C[2] ^ C[4] ^ C[5] ^
C[6] ^ C[9] ^ C[12] ^ C[13] ^ C[15] ^ C[17] ^ C[18] ^
C[21] ^ C[24] ^ C[27] ^ C[30] ^ C[31];
NewCRC[13] = D[31] ^ D[28] ^ D[25] ^ D[22] ^ D[19] ^ D[18] ^ D[16] ^
D[14] ^ D[13] ^ D[10] ^ D[7] ^ D[6] ^ D[5] ^ D[3] ^
D[2] ^ D[1] ^ C[1] ^ C[2] ^ C[3] ^ C[5] ^ C[6] ^ C[7] ^
C[10] ^ C[13] ^ C[14] ^ C[16] ^ C[18] ^ C[19] ^ C[22] ^
C[25] ^ C[28] ^ C[31];
NewCRC[14] = D[29] ^ D[26] ^ D[23] ^ D[20] ^ D[19] ^ D[17] ^ D[15] ^
D[14] ^ D[11] ^ D[8] ^ D[7] ^ D[6] ^ D[4] ^ D[3] ^
D[2] ^ C[2] ^ C[3] ^ C[4] ^ C[6] ^ C[7] ^ C[8] ^ C[11] ^
C[14] ^ C[15] ^ C[17] ^ C[19] ^ C[20] ^ C[23] ^ C[26] ^
C[29];
NewCRC[15] = D[30] ^ D[27] ^ D[24] ^ D[21] ^ D[20] ^ D[18] ^ D[16] ^
D[15] ^ D[12] ^ D[9] ^ D[8] ^ D[7] ^ D[5] ^ D[4] ^
D[3] ^ C[3] ^ C[4] ^ C[5] ^ C[7] ^ C[8] ^ C[9] ^ C[12] ^
C[15] ^ C[16] ^ C[18] ^ C[20] ^ C[21] ^ C[24] ^ C[27] ^
C[30];
NewCRC[16] = D[30] ^ D[29] ^ D[26] ^ D[24] ^ D[22] ^ D[21] ^ D[19] ^
D[17] ^ D[13] ^ D[12] ^ D[8] ^ D[5] ^ D[4] ^ D[0] ^
C[0] ^ C[4] ^ C[5] ^ C[8] ^ C[12] ^ C[13] ^ C[17] ^
C[19] ^ C[21] ^ C[22] ^ C[24] ^ C[26] ^ C[29] ^ C[30];
NewCRC[17] = D[31] ^ D[30] ^ D[27] ^ D[25] ^ D[23] ^ D[22] ^ D[20] ^
D[18] ^ D[14] ^ D[13] ^ D[9] ^ D[6] ^ D[5] ^ D[1] ^
C[1] ^ C[5] ^ C[6] ^ C[9] ^ C[13] ^ C[14] ^ C[18] ^
C[20] ^ C[22] ^ C[23] ^ C[25] ^ C[27] ^ C[30] ^ C[31];
NewCRC[18] = D[31] ^ D[28] ^ D[26] ^ D[24] ^ D[23] ^ D[21] ^ D[19] ^
D[15] ^ D[14] ^ D[10] ^ D[7] ^ D[6] ^ D[2] ^ C[2] ^
C[6] ^ C[7] ^ C[10] ^ C[14] ^ C[15] ^ C[19] ^ C[21] ^
C[23] ^ C[24] ^ C[26] ^ C[28] ^ C[31];
NewCRC[19] = D[29] ^ D[27] ^ D[25] ^ D[24] ^ D[22] ^ D[20] ^ D[16] ^
D[15] ^ D[11] ^ D[8] ^ D[7] ^ D[3] ^ C[3] ^ C[7] ^
C[8] ^ C[11] ^ C[15] ^ C[16] ^ C[20] ^ C[22] ^ C[24] ^
C[25] ^ C[27] ^ C[29];
NewCRC[20] = D[30] ^ D[28] ^ D[26] ^ D[25] ^ D[23] ^ D[21] ^ D[17] ^
D[16] ^ D[12] ^ D[9] ^ D[8] ^ D[4] ^ C[4] ^ C[8] ^
C[9] ^ C[12] ^ C[16] ^ C[17] ^ C[21] ^ C[23] ^ C[25] ^
C[26] ^ C[28] ^ C[30];
NewCRC[21] = D[31] ^ D[29] ^ D[27] ^ D[26] ^ D[24] ^ D[22] ^ D[18] ^
D[17] ^ D[13] ^ D[10] ^ D[9] ^ D[5] ^ C[5] ^ C[9] ^
C[10] ^ C[13] ^ C[17] ^ C[18] ^ C[22] ^ C[24] ^ C[26] ^
C[27] ^ C[29] ^ C[31];
NewCRC[22] = D[31] ^ D[29] ^ D[27] ^ D[26] ^ D[24] ^ D[23] ^ D[19] ^
D[18] ^ D[16] ^ D[14] ^ D[12] ^ D[11] ^ D[9] ^ D[0] ^
C[0] ^ C[9] ^ C[11] ^ C[12] ^ C[14] ^ C[16] ^ C[18] ^
C[19] ^ C[23] ^ C[24] ^ C[26] ^ C[27] ^ C[29] ^ C[31];
NewCRC[23] = D[31] ^ D[29] ^ D[27] ^ D[26] ^ D[20] ^ D[19] ^ D[17] ^
D[16] ^ D[15] ^ D[13] ^ D[9] ^ D[6] ^ D[1] ^ D[0] ^
C[0] ^ C[1] ^ C[6] ^ C[9] ^ C[13] ^ C[15] ^ C[16] ^
C[17] ^ C[19] ^ C[20] ^ C[26] ^ C[27] ^ C[29] ^ C[31];
NewCRC[24] = D[30] ^ D[28] ^ D[27] ^ D[21] ^ D[20] ^ D[18] ^ D[17] ^
D[16] ^ D[14] ^ D[10] ^ D[7] ^ D[2] ^ D[1] ^ C[1] ^
C[2] ^ C[7] ^ C[10] ^ C[14] ^ C[16] ^ C[17] ^ C[18] ^
C[20] ^ C[21] ^ C[27] ^ C[28] ^ C[30];
NewCRC[25] = D[31] ^ D[29] ^ D[28] ^ D[22] ^ D[21] ^ D[19] ^ D[18] ^
D[17] ^ D[15] ^ D[11] ^ D[8] ^ D[3] ^ D[2] ^ C[2] ^
C[3] ^ C[8] ^ C[11] ^ C[15] ^ C[17] ^ C[18] ^ C[19] ^
C[21] ^ C[22] ^ C[28] ^ C[29] ^ C[31];
NewCRC[26] = D[31] ^ D[28] ^ D[26] ^ D[25] ^ D[24] ^ D[23] ^ D[22] ^
D[20] ^ D[19] ^ D[18] ^ D[10] ^ D[6] ^ D[4] ^ D[3] ^
D[0] ^ C[0] ^ C[3] ^ C[4] ^ C[6] ^ C[10] ^ C[18] ^
C[19] ^ C[20] ^ C[22] ^ C[23] ^ C[24] ^ C[25] ^ C[26] ^
C[28] ^ C[31];
NewCRC[27] = D[29] ^ D[27] ^ D[26] ^ D[25] ^ D[24] ^ D[23] ^ D[21] ^
D[20] ^ D[19] ^ D[11] ^ D[7] ^ D[5] ^ D[4] ^ D[1] ^
C[1] ^ C[4] ^ C[5] ^ C[7] ^ C[11] ^ C[19] ^ C[20] ^
C[21] ^ C[23] ^ C[24] ^ C[25] ^ C[26] ^ C[27] ^ C[29];
NewCRC[28] = D[30] ^ D[28] ^ D[27] ^ D[26] ^ D[25] ^ D[24] ^ D[22] ^
D[21] ^ D[20] ^ D[12] ^ D[8] ^ D[6] ^ D[5] ^ D[2] ^
C[2] ^ C[5] ^ C[6] ^ C[8] ^ C[12] ^ C[20] ^ C[21] ^
C[22] ^ C[24] ^ C[25] ^ C[26] ^ C[27] ^ C[28] ^ C[30];
NewCRC[29] = D[31] ^ D[29] ^ D[28] ^ D[27] ^ D[26] ^ D[25] ^ D[23] ^
D[22] ^ D[21] ^ D[13] ^ D[9] ^ D[7] ^ D[6] ^ D[3] ^
C[3] ^ C[6] ^ C[7] ^ C[9] ^ C[13] ^ C[21] ^ C[22] ^
C[23] ^ C[25] ^ C[26] ^ C[27] ^ C[28] ^ C[29] ^ C[31];
NewCRC[30] = D[30] ^ D[29] ^ D[28] ^ D[27] ^ D[26] ^ D[24] ^ D[23] ^
D[22] ^ D[14] ^ D[10] ^ D[8] ^ D[7] ^ D[4] ^ C[4] ^
C[7] ^ C[8] ^ C[10] ^ C[14] ^ C[22] ^ C[23] ^ C[24] ^
C[26] ^ C[27] ^ C[28] ^ C[29] ^ C[30];
NewCRC[31] = D[31] ^ D[30] ^ D[29] ^ D[28] ^ D[27] ^ D[25] ^ D[24] ^
D[23] ^ D[15] ^ D[11] ^ D[9] ^ D[8] ^ D[5] ^ C[5] ^
C[8] ^ C[9] ^ C[11] ^ C[15] ^ C[23] ^ C[24] ^ C[25] ^
C[27] ^ C[28] ^ C[29] ^ C[30] ^ C[31];
nextCRC32_D32 = NewCRC;
end
endfunction
endmodule
結果です。![]() コンソールの出力です。 Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------
Comment 最初の行コメント大丈夫かな?
0:read data= 12345678
1:read data= aaaa5555
Comment これは、コメント行です。冒頭に;が来ること。
2:read data= 1111aaaa
3:read data= 3456789a
Comment これも、コメント行です。
4:read data= abcdef01
5:read data= 58d7e7e0
Comment 最後の行のコメント大丈夫かな?
Exit Task
Info: $finishコマンドを実行します。time=1920
---------- シミュレーションを終了します。time=1920----------
注意:iなお、上記は、RFC2083のそれでは、ありません。(出力にINVERTERが入っていません。) 32ビットCRCを8ビットDataで使用する例 こちらの方にあるベンチを少しだけ書き換えて32ビットCRC8ビットデータ版を作ってみました。 CRCのテストは、送信側と受信側で同じCRCを使います。ですので、同じモジュールでインスタンス化すればよいです。 CRC32_D8_Module #(.initial_value(initial_value),.poly_width(poly_width),.data_width(data_width)) crc_sender_module( clock,
data,sync_reset,hold,
crc_sender);
CRC32_D8_Module #(.initial_value(initial_value),.poly_width(poly_width),.data_width(data_width)) crc_receiver_module( clock,
receive_data,sync_reset,receiver_hold,
crc_receiver);
送信側では、CRCを送るときは、生成をホールドします。 for (i=0;i<data_length;i=i+1) begin
data=$random;//ランダムデータを生成
@(negedge clock);
end
hold=1;//送り側CRC生成ホールド
for (i=0;i<crc_bytes;i=i+1) begin//CRCバイト数分送る
data=crc_sender[poly_width-1-data_width*i -:data_width];
@(negedge clock);
end
受信側では、通常のデータとして受信します。上記例では、4ByteのCRC受信後、受け側CRCが0になっていることがわかります。 なお、後半のテストでは、故意にエラーを加えて、CRCが0にならないことを確認します。エラーがあるのにCRCが0になるケースは誤検出です。誤検出確率は、生成多項式に依存 しますので、生のエラーレート環境に応じて適切な生成多項式を選択する必要があります。 テストベンチです。 //Dec.18.2006
module CRC32D8_test_bench;
parameter integer initial_value=-1;
parameter integer poly_width=32;
parameter integer data_width=8;//;
parameter integer maximum_no_of_errors=6;
parameter integer data_length=10;
parameter integer crc_bytes=poly_width/data_width;
reg clock=0;
reg sync_reset=1;
reg hold=0, receiver_hold=0;
wire [poly_width-1:0] crc_sender,crc_receiver;
reg done=0;
integer i,test_loops,error_counter=0;
reg error=0;
reg [data_width-1:0] data=0;
wire [data_width-1:0] receive_data=data^ error;
always #10 clock=~clock;
initial begin
#5;
test_loops=0;
$display("Make sure no CRC error ocrrurs when we don't have error additions.");
//Test 1 Make sure no crc error occurs
repeat (1000) begin
test_loops=test_loops+1;
if (test_loops%100==0) $display("Test Loops=%d",test_loops);
sync_reset=1;
@(negedge clock);
@(negedge clock);
sync_reset=0; hold=0;
for (i=0;i<data_length;i=i+1) begin
data=$random;//ランダムデータを生成
@(negedge clock);
end
hold=1;//送り側CRC生成ホールド
for (i=0;i<crc_bytes;i=i+1) begin//CRCバイト数分送る
data=crc_sender[poly_width-1-data_width*i -:data_width];
@(negedge clock);
end
done=1;
if (crc_receiver !==0) $display("Miss Detection or Programming Error");//CRCが0でなかったらプログラムエラー
@(negedge clock);
done=0;
end
$display("Test Done.!");
//Make sure crc error occurs
$display("Make sure CRC occurs when we have some trivial error additions.");//故意にエラーを加えるテスト
test_loops=0;
repeat (1000) begin
test_loops=test_loops+1;
if (test_loops%1000==0) $display("Test Loops=%d",test_loops);
sync_reset=1;
@(negedge clock);
@(negedge clock);
sync_reset=0; hold=0;error_counter=0;
for (i=0;i<data_length;i=i+1) begin
data=$random;//ランダムデータを生成
if (i==0) begin
error=1;//We add at least one error.
error_counter=error_counter+1;
end else begin//Limit maxinum no of errors
if (error_counter <maximum_no_of_errors) begin
error=$random;//$random error addition
error_counter=error_counter+1;
end
end
@(negedge clock);
end
hold=1;//Stop CRC generation in sender//送り側CRC生成ホールド
for (i=0;i<crc_bytes;i=i+1) begin//CRCバイト数分送る
data=crc_sender[poly_width-1-data_width*i -:data_width];
@(negedge clock);
end
done=1;//CRCが0だったらプログラムエラーか誤検出
if (crc_receiver ==0) $display("Miss Error Detection or Programming Error Test Loops=%d",test_loops);
@(negedge clock);
done=0;
end
$display("Test Done.! .... see you again..");
$finish;
end
CRC32_D8_Module #(.initial_value(initial_value),.poly_width(poly_width),.data_width(data_width)) crc_sender_module( clock,
data,sync_reset,hold,
crc_sender);
CRC32_D8_Module #(.initial_value(initial_value),.poly_width(poly_width),.data_width(data_width)) crc_receiver_module( clock,
receive_data,sync_reset,receiver_hold,
crc_receiver);
endmodule
CRC生成モジュール(H/W)です。
//Dec.18.2006 Tak.S
module CRC32_D8_Module #(parameter integer initial_value=-1, poly_width=32,data_width=8) (input clock,
input [data_width-1:0] data,
input sync_reset,hold,
output reg [poly_width-1:0] crc);
always @(posedge clock) begin
if (sync_reset) crc <=initial_value;
else if (hold) crc <=crc;
else crc<=nextCRC32_D8(data,crc);
end
///////////////////////////////////////////////////////////////////////
// File: CRC32_D8.v
// Date: Mon Dec 18 10:47:11 2006
//
// Copyright (C) 1999-2003 Easics NV.
// This source file may be used and distributed without restriction
// provided that this copyright statement is not removed from the file
// and that any derivative work contains the original copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Purpose: Verilog module containing a synthesizable CRC function
// * polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
// * data width: 8
//
// Info: tools@easics.be
// http://www.easics.com
///////////////////////////////////////////////////////////////////////
// polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
// data width: 8
// convention: the first serial data bit is D[7]
function [31:0] nextCRC32_D8;
input [7:0] Data;
input [31:0] CRC;
reg [7:0] D;
reg [31:0] C;
reg [31:0] NewCRC;
begin
D = Data;
C = CRC;
NewCRC[0] = D[6] ^ D[0] ^ C[24] ^ C[30];
NewCRC[1] = D[7] ^ D[6] ^ D[1] ^ D[0] ^ C[24] ^ C[25] ^ C[30] ^
C[31];
NewCRC[2] = D[7] ^ D[6] ^ D[2] ^ D[1] ^ D[0] ^ C[24] ^ C[25] ^
C[26] ^ C[30] ^ C[31];
NewCRC[3] = D[7] ^ D[3] ^ D[2] ^ D[1] ^ C[25] ^ C[26] ^ C[27] ^
C[31];
NewCRC[4] = D[6] ^ D[4] ^ D[3] ^ D[2] ^ D[0] ^ C[24] ^ C[26] ^
C[27] ^ C[28] ^ C[30];
NewCRC[5] = D[7] ^ D[6] ^ D[5] ^ D[4] ^ D[3] ^ D[1] ^ D[0] ^ C[24] ^
C[25] ^ C[27] ^ C[28] ^ C[29] ^ C[30] ^ C[31];
NewCRC[6] = D[7] ^ D[6] ^ D[5] ^ D[4] ^ D[2] ^ D[1] ^ C[25] ^ C[26] ^
C[28] ^ C[29] ^ C[30] ^ C[31];
NewCRC[7] = D[7] ^ D[5] ^ D[3] ^ D[2] ^ D[0] ^ C[24] ^ C[26] ^
C[27] ^ C[29] ^ C[31];
NewCRC[8] = D[4] ^ D[3] ^ D[1] ^ D[0] ^ C[0] ^ C[24] ^ C[25] ^
C[27] ^ C[28];
NewCRC[9] = D[5] ^ D[4] ^ D[2] ^ D[1] ^ C[1] ^ C[25] ^ C[26] ^
C[28] ^ C[29];
NewCRC[10] = D[5] ^ D[3] ^ D[2] ^ D[0] ^ C[2] ^ C[24] ^ C[26] ^
C[27] ^ C[29];
NewCRC[11] = D[4] ^ D[3] ^ D[1] ^ D[0] ^ C[3] ^ C[24] ^ C[25] ^
C[27] ^ C[28];
NewCRC[12] = D[6] ^ D[5] ^ D[4] ^ D[2] ^ D[1] ^ D[0] ^ C[4] ^ C[24] ^
C[25] ^ C[26] ^ C[28] ^ C[29] ^ C[30];
NewCRC[13] = D[7] ^ D[6] ^ D[5] ^ D[3] ^ D[2] ^ D[1] ^ C[5] ^ C[25] ^
C[26] ^ C[27] ^ C[29] ^ C[30] ^ C[31];
NewCRC[14] = D[7] ^ D[6] ^ D[4] ^ D[3] ^ D[2] ^ C[6] ^ C[26] ^ C[27] ^
C[28] ^ C[30] ^ C[31];
NewCRC[15] = D[7] ^ D[5] ^ D[4] ^ D[3] ^ C[7] ^ C[27] ^ C[28] ^
C[29] ^ C[31];
NewCRC[16] = D[5] ^ D[4] ^ D[0] ^ C[8] ^ C[24] ^ C[28] ^ C[29];
NewCRC[17] = D[6] ^ D[5] ^ D[1] ^ C[9] ^ C[25] ^ C[29] ^ C[30];
NewCRC[18] = D[7] ^ D[6] ^ D[2] ^ C[10] ^ C[26] ^ C[30] ^ C[31];
NewCRC[19] = D[7] ^ D[3] ^ C[11] ^ C[27] ^ C[31];
NewCRC[20] = D[4] ^ C[12] ^ C[28];
NewCRC[21] = D[5] ^ C[13] ^ C[29];
NewCRC[22] = D[0] ^ C[14] ^ C[24];
NewCRC[23] = D[6] ^ D[1] ^ D[0] ^ C[15] ^ C[24] ^ C[25] ^ C[30];
NewCRC[24] = D[7] ^ D[2] ^ D[1] ^ C[16] ^ C[25] ^ C[26] ^ C[31];
NewCRC[25] = D[3] ^ D[2] ^ C[17] ^ C[26] ^ C[27];
NewCRC[26] = D[6] ^ D[4] ^ D[3] ^ D[0] ^ C[18] ^ C[24] ^ C[27] ^
C[28] ^ C[30];
NewCRC[27] = D[7] ^ D[5] ^ D[4] ^ D[1] ^ C[19] ^ C[25] ^ C[28] ^
C[29] ^ C[31];
NewCRC[28] = D[6] ^ D[5] ^ D[2] ^ C[20] ^ C[26] ^ C[29] ^ C[30];
NewCRC[29] = D[7] ^ D[6] ^ D[3] ^ C[21] ^ C[27] ^ C[30] ^ C[31];
NewCRC[30] = D[7] ^ D[4] ^ C[22] ^ C[28] ^ C[31];
NewCRC[31] = D[5] ^ C[23] ^ C[29];
nextCRC32_D8 = NewCRC;
end
endfunction
endmodule
F.A.Q.Q.MSBとLSBを入れ替え・ ビット極性について 頂いたソースとテストベンチでさっそく シュミレーションしてみました やはり 結果CRCのMSBとLSBの反転 と極性の反転が起きます 実行しましすと 最初のDATA 0x24に対して 波形上で出力されるCRCは ”C52F7F88"でした フリーのソフトのCRC32の計算ソフトで計算させると ”EE010B5C"となりました 2種類のフリーソフトで試してみましたが 同じ結果でした ”C52F7F88"を MSBとLSBを入れ替えて ビット極性を反転させると ”EE010B5C"となります なにか私が勘違いしているのでしょうか? A.それは、CRCの利用の定義に依存します 本質的なのは、生成多項式で、これだけで誤検出確率を決定します。 CRC初期値や、INVや順序を変えても、同じ生成多項式なら、誤検出確率は変わりません。 つまり、送信と受信がClosedした世界(上のCRCや、HDD内部等)では、CRC初期値/INV/順序を変えて定義してなんら誤検出確率は変わりません。 利用するときのプロトコルの問題です。 既に送信側受信側の処理が決まっている場合(Ethernet/CD/FDD/DVDのCRCとか)は、CRC初期値はいくらにするとか、INVするとかMSBから入れるかLSBから入れるか、 これらは、そのプロトコルSpec.のなかで定義しているはずですから、それにあわせこむ必要があります。(結果的にxx様がやられている作業です) でないと、まさにCRCエラーになってしまいますね。勿論テストベンチは、その場合、そのプロトコルに沿う形にするべきでしょう。 アナログPLLのシミュレーション(May.7.2005 終了) 一つの古典的なデザインを行ってみました。 リードソロモン自動プログラムの実験 (Under Development) リードソロモンは、CDやDVDで使われている誤り訂正(ECC)の一種です。5年位前のハードディスクのテクノロジーでは、5インターリーブのの3重訂正(CPUベンチマークで取り上げました。)が使われていました。(CDは2重訂正までです。) リードソロモンは、訂正次数が3次までなら、比較的簡単な代数方程式で高速に解くことができます。(なので、CD・DVD・HardDiskではインターリーブを使ってバーストエラー能力を上げてます。) 3次以下では、CPUベンチマークに示した代数手法例(3次方程式の一般解:カルダノの公式)で、それ以上では、探索による方法(5次以上の代数方程式の一般解が存在しないように)、以下の自動Verilogが一般的だと思います。 ガロア体上の乗除算は、CPUでは表引きになるので、高速化のためには、ハードウェアで演算するのが一般的でした。(2次元バーコードように低速でよい場合には、現在でもソフトウェアで可能です。) 今後、組み込みCPUが、リコンフィグアブルになってくると、ガロア体のALUを装備して、命令追加したCPUで行う場面も多くなってくるでしょう。 SourceForgeで、Verilogを自動出力してくれるプログラムを見つけました。簡単にできるようなら、実験してみたいと思います。 =>テストベンチを吐いてくれないので、自分で作らなければなりません。コードは、それなりに吐くみたいです。Erasureは、サポートされていませんし、更新も滞っているようです。 とりあえず、VC++でコンパイルしたバイナリを入れておきます。 CDで使われている生成多項式で、接続H/Wとテストベンチを書くつもりでしたが、下のコンテストの問題で時間がなくなってしまいました。 デザインウェーブ コンテスト2005を解く(終了) FMレシーバの問題です。まず、疑問に思うのは、設計仕様の曖昧なところです。送信波形の帯域制限(スペクトラム整形)は、どうなっているのでしょうか? 受信フィルタの最適化設計においては、必要だと思うのですが。 さらに、三角波は、無限帯域を持つ。。。 とりあえず、DPLLの実装例としては、面白い課題だと思うので、取り組んでみたいと思います。コンテストのHPを見ましたが、誌面で設計されているソース(Verilog/VHDL)はないようです。 そこで、誌面で説明されている設計の(例題としての)Veritakプロジェクトを作成し便宜を図りろうと思ったのですが、興味がDPLLからARCTANに移ってしまいました。) => 少し、検討してみました。これは、DPLLとして考えるよりFSKとして考えた方がよさそうです。 確かに、位相がロックすれば、周波数ずれはないので、確実に周波数変化を捉えることができます。しかし、要は、周波数変化=>位相の変化が分かればよい訳で、必ずしもPLLにこだわる必要はないでしょう。 ソフトウェア無線的な考えで、高機能を狙うよりも、ハードウェア(FPGA)にしかできないこと(そのうち、ソフトウェアで出来てしまうのがこの世界の常ですが。)、すなわちスピードで最速を狙う設計をしてみたいと思います。 それにしてもS/Nを無視してよいのかどうかで、まったく設計が異なってしまいます。実用性を無視ししたコンテスト課題と捉えるか、ある程度、実使用に耐えうる設計とするか悩みどころですね。 => かなり検討(休日のAM)してみました。1月号のDWM誌 西村芳一氏の解説にある通り、やはりPLLのアプローチは、直接的ではありません。 そこで、方式選定ですが、
(アナログでは、Quadratureによる方法が一般的で、実際、筆者も2.4GHzを110MHzのIFに落とした後、これで設計したことがありました。) そうなると、最終的には、この問題のキーポイントは、ARCTANをいかに計算するかだと思います。 ARCTANの計算は、CORDICや+テーラ展開等、数種類の計算方法があります。 => 三角関数の公式をひっぱりだしてさらに検討しました。(休日のPM, 微積もすっかり忘れていました。) 別な視点からよい方法を思いつきました。S/Nを無視すれば、紙の上では、うまくいきそうな多分新しいアルゴリズムです。 とりあえず、NETでSearchした論文で実装例は、ありませんでした。Spartan3(1月号付録)で260MHz位行きそうです。また、検証のためのH/W(PICが小さそう)も余裕で載るでしょう。 後は、
=>設計方針を決めました。
メモリ 18ビットx1KWORDx4=>2x1Kx4のサンプルが可=>8KBのデータが収納可能=>ベースバンドで60個分は入る。=>アイは可能
このまま、アルゴリズムが破綻しないようだったら、Verilog上でのシミュレーションは、システム/RTL/ゲートと3回行うことになります。抽象から具体設計に落としていくトップダウン設計手法のよい例になっている(いく?)と思うので、 実際に設計、製作していきたいと思います。また、RTLシミュレーションまで行って、行けそうだったらそのまま応募してみたいと思います。 =>システムシミュレーション ![]() 1)送信波形の作成 ベースバンドのランダムNRZ波形を作成します。これは、$random %2でOKでしょう。($randomは、Verilog2001仕様では、生成Seq.のシミュレータ互換があります。) これを、ガウスフィルタ(アナログベッセル5次で近似)に通します。 FIRでも記述できますが、Veritakの場合、$rungekuttaを使えるので、こちらの方が簡単です。 次に、FSK変調ですが、f(t)を変調後の波形とすると、瞬時的には、 f(t)=sin(ωt)ですから、 ω=ωc+ωb ここで、ωcは、キャリア、ωbは、ベースバンドのVCOの角速度になります。 ところが、ωbは変化するので、若干の積分操作が必要になります。 ベースバンドのVCOは、ガウスフィルタの出力に変調度1%を乗算したものにすればOKです。以上の式をそのまま、Verilogで書けば、送信波形の出来上がりです。MATLAB並みに数十行で書けてしまうところがVerilogのいいところです。 ところで、GFSKは、DECT・GSM・Bluetoothで使われている、歴史のある変調方式です。Bluetoothでは、FHのテクニックで、2.4GHz ISM帯79Channelに拡散されます。 課題は、アナログを想定しているみたいですが、こういったDigital変復調について検討しても面白いのではないでしょうか? Verilog2001スタイルで書いてみました。 これは、最終的なソースリストです。 Xilinx Coregenで生成したもの以外は、これとuartソース(下)が全てです。
一番上が、NRZ波形、filt_outがベッセルフィルタを通した波形です。過渡がとれて、オーバシュートのない綺麗な波形になります。この性質が、狭帯域かつ信号自体の劣化を最小にするという目的に合致しています。 一番下が送信波形ですが、Aliasingのためまともに見えません。 ![]() そこで、カーソル間を拡大してみると、sin波になっていることがわかりますが、1%DeviationなのでDataの変化はよくわかりません。 この波形を復調して上のfilt_outに戻す操作がdemodulationです。 ![]() (2)Demodulation Zero-IFのブロック図は、下のようになります。4倍サンプリングすると、周波数変換のsin,cosの係数は、下のようになりますから、実際のところ乗算器は不要になります。 ![]() 同じ周波数のの掛け算は、その2倍の周波数成分を持ちます。そこで、2倍の周波数成分をLPFで落としてやる必要があります。これは、2次のFIRで十分です。 下図は、そのFIRの特性ですが、サンプリング周波数の1/2においてNullになっていることがわかります。(4倍サンプリングの1/2周波数=キャリアの2倍周波数) ![]() ところが、係数をsin、cosの係数は、2CLOCK毎0なので、これもまた、FIR演算を行う必要はないことがわかります。ここまで計算らしい計算は、必要ありませんでしたが、問題は、その後のI/Q計算、ARCTAN計算です。 それが、今回の課題のポイントです。 上記で、量子化誤差を考えなくて良いRealで書いてみました。 一番下の波形がDemodulateした波形で、期待通りベースバンド波形が再現していることが分かります。PLL復調方式と違いLatencyが非常に小さいところがこの方式の特徴になっています。 ![]() (3) アイパターン ベースバンドの転送レートを倍にするとどうなるでしょうか?ベッセル3dBのカットオフを変えずに、ベースバンドの転送レートを倍の2MBPSにしてみます。 ベースバンド波形は、歪み、遷移時間が小さいところでは、振幅も小さくなります。 ![]() この波形をオシロでTriggerをかけて見たとすると下の絵のように見えます。これをアイパターンといいます。 振幅方向の余裕は半分以下になってしまいますが、アイの中央でサンプリングすれば、未だデータの判別は可能です。 ![]() アイパターンは、やはりVerilogでプログラムを書いてExcelで処理しました。Verilog2001では、多次元配列も使えるのでテストベンチを書くときは重宝します。(上記ソース参照) (4)RTL 次は、Demodulatorを実際のH/Wを意識した書き方に直します。量子化誤差、Overflow等を考慮して演算ビット幅を決定します。 その前に、設計仕様をもう少し具体化します。 GFSKとして代表的なものは、Bluetoothです。コンテスト課題のModulationは+-1%と、BluetoothからSpec.をもらい設計仕様として以下の通りまとめました。
なお、最終的なClockは、論理合成後に、決定します。Targetは、Starter Kit(-4)です。 =>とりあえず、8ビット乗算器だけで、論理合成すると276MHzになりました。ところが、波形入力用のRAMを記述すると230MHz位に落ちてしまいました。明らかにRAMで律速しています。 しょうがないので実機でのターゲットは、200MHz、としました。 アイパターンは、VGAで出力させる方式としました。VGAは初めてなのですが、R-2RラダーDAコンバート、アナログオシロで撮るよりもスマートです。 VGAは、50MHzを使うみたいですし、Xilinx Starter Kitは50MHzClockなので、4倍CLOCKとすれば、相性がいいのです。VGAのコネクタもついているので、特に半田付けすることもありません。 また一回作っておけば、あとあとアナログオシロの代わりにもなるような気がします。 以上より 実機検証用のTargetは次の通りとします。
上記パラメータでRTLを記述してみました。Bluetooth Spec.で規定されている波形に近くなりました。 Modulation Index 0.3というのは、+-0.15で、ベースバンド転送レート3.33Mbpsに対して、Peak-Peakで1MHzで、振れることを意味しています。 3.33Mbpsを送るのに3.33MHzは使っていません。このようにGFSKは、狭帯域の通信に適しています。 量子化の影響(8ビット)が、懸念事項だったのですが、案の定、量子化の影響がでています。(下図、真中) RTLのシステムシミュレーションでは、ビット幅をパラメータ化しておくことで、インスタンス化時に簡単にその影響をシミュレーションすることができます。 この辺は、Hardに近い(Hard そのもの)の記述なので、Matlabよりもある意味便利です。ビット幅を12ビットくらいにすると、もう上のRealの波形と区別できないくらいになるのが確認できました。 ところで、量子化というのは、ノイズを加えたことと同じです。今回の量子化が白色Noiseかどうかはわかりませんが、簡単には、1次のIIRフィルタで逃げることができます。(一番下の波形) これで、システムとしてのシミュレーションが終わりました。 アルゴリズムに破綻がなかったので、応募してみたいと思います。 ![]() Demodulator単体での合成結果です。最初の記述では、100MHz程度でした。 パイプライン化と、演算ビット数の削減によって240MHz程度まできました。合成→システムシミュレーション→..を繰り返し、Reductionによって特性が変化しないことを確認しながら進めます。 目標の100LUT以下は達成できたので、ここでよしとします。 Critical Pathは、16ビット減算部分です。Demodulatorの記述は、約100行です。
=> DCM を合成してみました。Spec.を見ると結構ジッタが大きいです。真面目に考えると、このジッタでサンプリングをすると位相誤差が無視できないほど大きいという問題があります。 また、最大駆動周波数に、ジッタで失われるはずのタイミングマージン減少が計算に入っていないようですが、いいのでしょうか? (4)XILINX RTLマッピング 検証方法について、検討したのですが、VGAよりUARTの方が簡単そうです。UARTでDUAL PORT RAM内容を垂れ流しし、PC側でExcel処理することにします。 周辺回路としてDCM,UARTを使う構成がイメージできたので、次のSTEPは、システムシミュレーション記述をXILINX RTLにマッピングすることです。 そのためには、ブロックRAMをROMとして使う必要があります。RAMの初期化データを作成しなくてはいけません。これは、システムシミュレーションからROMのVerilog記述を生成することにします。 また、ROMが記述できると、自律システムとして動きますから、今までの仮想クロックの代わりにDCM200MHz クロックで駆動します。 量子化誤差を考えなくてもよいrealでの記述から、量子化するHardware記述への記述で注意する点は、二つあります。一つは、realからbitに変換する操作での四捨五入です。 Verilogの変換は、C言語と同様切捨てなので、たとえば、2.999であっても2になってしまいます。この場合は、3にした方がよいことはいうまでもありません。 Veritakは、四捨五入function $rintがあるのでそれを使ってreal上で四捨五入(この場合は3.0に変換)した後に整数に変換します。 二つ目は、演算回路は、精度と速度のトレードオフになる場合が多く、後でビット幅を変えたくなる場合もあります。 Verilog2001 になってparameterが強力になっているので、それを駆使してビット幅を可変にしておくと後で楽ができます。 =>UARTをハードで回すとなると、やはり面倒です。PCで受け取ってEXCEL形式にDATAを整理するのも面倒です。JTAGを使えば、簡単にダンプできそうですが、理解している時間的な余裕がありません。 C言語で記述できるCPUがあり、かつStarter Kitに載って70MHz以上で動けば、UART出力時にフォーマット化してだすことも容易です。そんなCPU...ありました。YACCです。 最小で去年のDWM付録に載りますからStarter Kitに載らないはずはありません。 速度的にも、70MHzは、容易でしょう。そこで、安直にYACCのRAM周りの記述をXILINXにポートしてみることにしました。 =>[チームVeritak」として応募することにしました。応募レポートは、応募後に公開します。(2月1日以降) Xilinx spartan3スタータキット上にC言語で動作するYACC(32ビットMIPST互換CPU)とFM Demodulatorのトップダウン設計及び実装例になると思います。 =>YACCを合成してみました。 Alteraからのポートが必要なのは次の2箇所です。
また、ModulatorのROMファイルは、上のシステムシミュレーション上でCOEファイルを生成するVerilog記述としました。(上記ソース参照 COEファイルが出来たらCoregenでメモリを生成します。このときmifファイルが生成され、Veritakはこれを読みます。合成シンセサイズ用のファイルはまた別のファイルになります。 つまりHDLシミュレーションだけのときは、mifを書き換えてもいいのですが、合成した結果には反映されません。 レジスタファイルにも少し工夫が要ります。3PORTのレジスタファイルが要るのですがAlteraでは、これを3PORTのRAMで合成することができました。Xilinxでは、それはありません。 分散RAMを使えばできるのですが、それでは、リソース消費が大きすぎ、到底載りません。そこで、2ポートのブロックRAMを2つ使う(同じデータを書きます)ことにしました。 勿体ないですが、こうするとFFの消費を抑えることができます。結果90%でFITしました。 これは、予想外に大きく、Demodulatorを載せるとギリギリです。FITしないかもしれません。そこで、この方法は、断念し単純にUART垂れ流し方式としました。 (5)ゲートレベルシミュレーション 合成後のRTLシミュレーションをまず行いました。この段階での問題は、signedの解釈がHDLシミュレータとシンセサイザで異なることです。Xilinxシンセサイザでは、$singedもサポートされていません。 ここでは、詳しく述べませんがsignedとビット幅拡張に関してはHDLシミュレータとシンセサイザの解釈が一致しない場合が多々あるので注意が必要です。 今回は、問題のある箇所をVerilog2001 signed から従来と同じ書き方(signedに依存しない書き方)に変更して一致させました。 Coregen Libraryを使ってシミュレーションを行いましたが、この辺は、論理合成しゲートレベルのシミュレーションをしないと問題の有無を確認できません。 ゲートレベルシミュレーションは、「遅延シミュレーション」の他に論理合成とHDLの一致検証としても意味を持ちます。 ![]() 一番上の波形は、50MHzの入力波形です。DCMがLOCKEDすると、4xCLOCKのDCM生成波形(5ns=200MHz)が出力されていることが分かります。その下は、8ビットROMの波形です。 4倍サンプリングの為現波形のsin波を想像するのは、難しいかもしれません。これが、Demodulatorの入力になります。 ちなみにその下の波形は、DCMの内部信号を出したものです。180度、270度等の信号が生成されているのが分かります。 ![]() このときのDemodulator出力波形です。転送レートは、3.3MBPSですから、最小ピークは、300nsになっています。 ![]() ゲートシミュレーションについてXILINXでは、MAPとPOST ROUTEというシミュレーションファイルが生成されます。MAPでは、Placeだけの情報のみらしく、ほぼ上の合成速度でシミュレーションができました。 しかし、POST ROUTEでVeritakゲートシミュレーションを行ってみると、Setup/Hold エラーのオンパレードで200MHzでは動きませんでした。 Systhesize Reportでは、240MHz出ていたのにPOST ROUTE TIMING REPORTを見ると、確かに200MHzより落ちています。150Mhzしかでていません。20%劣化なら仕方ないような気もしますが、50%近くなると詐欺に近い。。。。。 とりあえず、現在200MHzでは動かないので、150MHzでPostLayoutGateシミュレーションを行ってみました。下の上の波形がそうですがカーソルを拡大してみるとノイズのようなヒゲがでています。(左下) さらにヒゲの部分を拡大してみると複数ビットのばたつきであることがわかります。この信号は、12ビット幅のREG出力です。各ビットのピン出力の遅延時間が異なるためにこのように見えます。 (右下波形で、最小CLKFXより細かくバタついている様子がお分かりでしょう。ちなみに遅延シミュレーションなので、全波形Saveではメモリ消費も激しいです。下の波形で、155.1MBも食っています。) ![]() とりあえず、どうするか考え中です。CDが150KB/Secなので、CD音質を転送しようと考えると1.2Mbpsあれば、よいですが、パケット化を考えると50%程度に低下するので、3Mbpsは欲しい。。。。 => 結局、200MHzも無理という結論に達しました。何の論理がはいらないFF間の接続だけで、3.xns食っています。乗算器で2nsはくいますからどう頑張っても200MHzは無理だろうという結論です。 (フローアプランをちゃんとやれば、入るのかもしれませんが。。) そこで、DSMを使ってキャリア周波数のCLOCKで動く仕様に変更することにします。Alteraでもそうでしたが、120MHzを超えたあたりから配線遅延が支配的になってしまって性能がだしづらくなります。(もぐらたたき状態) そこで、本来の目的に立ち返り、拘束条件下で、最速ベースバンドを達成するという目的を思い出しました。その為には、4xクロックを1xクロックにしてしまえばいいことになります。 位相が4倍速くまわるので、パイプライン化でごまかせない部分がでてきますが、それでも100MHz程度はいくでしょう。6.7Mbps位は行くと思います。パイプラインが使えないので、LUT数は、減少します。
=> どうにも駆動周波数があがりません。多分、積和演算を1CLOCKで処理しようとすると、130MHz位が限界なのではないでしょうか? ということで、IIRも止めにしてデシメーションだけで行くことにしました。これで、150MHz狙いで、10Mbpsになります。10Mbpsというのは、課題提示の1000倍なので、受け狙いとしては、いいかもしれません。 普通は、設計が進むにつれて、LUT数が上がるのに、今回は、下がっていくという不思議な事例です。 締め切りも近いので、ハードをFIXさせて、実機で動かしてみることにしましょう。 =>その前に周辺TEST回路を書かなくてはいけません。コンテスト課題は、最終的には、70行の答案になってしまったのですが、周辺回路の記述は、その数十倍です。かけた時間と反比例しています。 (仕事の量が、ソース行数で推し量れない例ですね。 遅延シミュレーションでは、150MHzの波形が、観測可能ですが、実デバイス上では、(高度なロジアナを持ち合わせていないので)、出力DataをDUAL PORT RAMに保存しておいて、別CLOCK(50MHz)でUARTに出力します。 ここでは、Verilog2001、@* の記述が参考になるかもしれません。Sensibity Listをいちいち書かなくてよいので、組み合わせ回路でラッチが合成されてしまうという、誰でも一度は犯しただろうバグを回避することができます。 RTLテストするときは、UARTのブロックだけでテストします。分解能1psの世界とμsecの世界を一緒にテストしようとしても時間の浪費です。 上の回路は、1psのジッタがあると、波形上で、ノイズとして観測できますが、下の回路は、数%クロック周期がずれていたところで、問題なく動くでしょう。そういう回路感覚でテストベンチを記述することも重要かもしれません。 UARTのテストです。波形を見ていて、「動いているみたいだが、正しいかどうかわからない」ときがあります。(シリアルを人間UARTするのはつらい。)こんなときは、接続されるポートをモデリングしてしまいます。 今回は、READのUARTを接続してコンソールにメッセージをだすとよいでしょう。波形は、ステートマシンの状態遷移とボーレートの確認に使います。 ![]()
=> ようやく、最終回路で150MHzを超えることができ遅延シミュレーションも通りました。UARTは、50MHz CLOCKなので楽勝です。後は火をいれるだけです。火を入れる瞬間はいつもワクワクします。 =>火をいれてみました。 ボーレート115.2Kbpsで接続すると、設計通り、UARTの垂れ流し状態になっています。 これを、テキストファイルに落としてVerilogで12ビットの符号付で読めば、シミュレーション時の波形が再現するという訳です。 XilinxのStarter KitのUCFファイル等は、佐藤さんのファイルを参考にさせていただきました。 ![]() $readmemhで、上のテキストファイルを読み込みます。UARTは、垂れ流しなので、読み込んだ波形位置(時間)をRTL波形と同じになるように調整します。視覚的には、同じように見えるので多分OKです。 正確には、下のように、RTLシミュレーション波形(demodulated_output)と読み込んだ波形(UARTが受け取った結果:uart_output)と波形比較コマンドで比較して、相違がないことを確認しておきます。 ![]() 以上で、RTLシミュレーションと実機との整合検証が完了しました。 課題に対する最終合成結果です。(配置配線前です。) ![]() リファレンス回路(parity.vhdl)の合成結果です。(配置配線前です。) ![]() 最終的な検証回路での緒元です。
これで、終わりです。12月半ばから構想を始めて、設計、製作と行って、ほぼ休日は、この作業でした。見積もり時間の3倍位かかってしまいました。 提出したレポートは、割と綺麗にまとまっているように見えますが、実際は、上のように紆余曲折しながらの設計です。 ともあれ、思いついたアイデアを視覚的に検証することができ、結果がでるまで延々とコーディングするストレスがなく設計自体を楽しむことができました。(課題から大きく道をそれてしまったようなので、入賞は難しいと思いますが。) 提出したレポートです。 アナログFMの汎用的なアルゴリズムである(つまり、150MHzのアナログFM信号がそのままで復調可能です。)ことも補足しておきます。オープンコアでもFMのプロジェクトがあるので、そのうち提案してみるかも。 それにしてもFMという古典的なテーマでありながら、いろいろな設計があるものですね。公開されていない他のチームの設計も是非見てみたいと思いました。 演算 除算/対数/平方根のH/Wアルゴリズム(終了) OpencoresにはIEEE754適合のFPUソースがあります。しかし、テストベンチのデータを展開すると500MB にもなってしまうので、上ではUpしませんでした。 Opencoresでなくても、小規模な演算ソースを公開されている方を見つけました。 VHDLで書いておられるので、トランスレータを使ってVerilogにしてみたいと思います。(Version1.47では、Single VHDLファイルならDrag&Dropでトランスレートします。) 掲載許可をいただきましたが、テストベンチを消失してしまったそうです。そこで、Verilogによるテストベンチを書いてみることにしました。以下は、
注意:なお、フリーハードウェアですが、著作権は原作者にあります。サポートはありませんし特許が成立している可能性もあります。使う方の自己責任でどうぞ。 1)平方根 36bitから18ビットの根を出力します。 テストベンチは、手書きで書きました。網羅的にチェックしました。OKです。 こんな感じです。 ![]() オリジナルのソースコードとトランスレータ出力です。組み合わせ回路のみで記述されています。実際的には、パイプライン化が必要になるでしょう。
2)除算 正の整数 X 18ビットを正の整数 Y 18ビットで除算する回路です。 テストベンチは、時間がかかるので網羅的にしていません。$randomのxを作り、それ以下の全てのyに対してチェックしています。 ![]() オリジナルのソースコードとトランスレータ出力です。
3)平方根の逆数PartT Z = 1/√X (但し X=0 の場合 Z=0) 正の整数 X 20 ビットの平方根の逆数をとる回路 出力は浮動小数点形式で指数部(ZN) 4 ビット,仮数部(ZM) 20 ビットです。 テストベンチは、網羅的に書きました。 ![]() オリジナルのソースコードとトランスレータ出力です。
3)平方根の逆数PartU Z = 1/√X (但し X=0 の場合 Z=0) 正の整数 X 20 ビットの平方根の逆数をとる回路 出力は浮動小数点形式で指数部(ZN) 4 ビット,仮数部(ZM) 20 ビットです。 テストベンチは、網羅的に書きました。パートTとの違いは、精度です。 ![]()
4)逆数PartT Z = 1/X (但し X>0, X=0 の場合 Z=0) 正の整数 X 20 ビットの逆数をとる回路 出力は浮動小数点形式で指数部(ZN) 5 ビット,仮数部(ZM) 20 ビット です。 テストベンチです。 ![]()
Z = 1/X (但し X>0, X=0 の場合 Z=0) 正の整数 X 20 ビットの逆数をとる回路 出力は浮動小数点形式で指数部(ZN) 5 ビット,仮数部(ZM) 20 ビット です。 テストベンチです。パートTとの違いは、精度です。 ![]()
6)自然対数 Z = log(X) (但し X>0, X=0 の場合 Z=0) 正の整数 X 20 ビットの自然対数をとる回路 出力は固定小数点形式で整数部 4 ビット,小数部 16 ビット ![]()
7)Cordic sin,cos,atan(y/x)の計算を行います。 テストベンチです。signedを使って素直に書けます。
オリジナルソースとトランスレート後です。
下記計画中の以下のプロジェクトは、現在休止しています。 .マイクロプログラムによるパターンジェネレータ マイクロプログラムは、高度な順序制御器としての側面をもっています。 CPUでは遅すぎるし、ステートマシンでは複雑すぎる制御に好適です。意外と知られていないのでとりあげることにします。応用例としてパターンジェネレータを設計してみます。 .誤り訂正 CRCやビタビ、リードソロモン等のECCコアを設計してみます。また、YACCプロジェクトで、ガロア体ALUと演算命令を追加したYACCプロセッサでもC言語で記述してみます。 .サーボ制御 現代制御理論によるサーボ制御をVeritakシミュレータ上で実装制御してみます。ロボット制御等に使われる制御理論の基礎をとりあげます。 .DPLL デジタルPLLについてとりあげます。 ソフトライブラリ( 9,10) 算術ライブラリ Verilog2001のVPI(PLI Version2.0)を使ってCのMathライブラリを呼び出しました。Verilogと外部言語とIFするには、Cまたは、C++でDLLを作ってやるのが簡単です。この例では、C++で簡単に記述してみました。 Verilog2001になって従来のPLIは、保守扱いになっていますのでこれからVerilogとのIFを他の言語で書こうとする場合は、VPIをお勧めします。 ところが、VPIは、シミュレータの内部状態を極限まで制御できるようになっていて、汎用的な反面、抽象的でわかりずらい面があると思います。ソースのコメントを参考に雰囲気をつかんでいただけたら、と思います。 なお、本ライブラリは、Veritak Version1.02よりmath_vpi.dllとしてインストールパッケージに追加されています。 10.Verilogでアナログシミュレート? PLL/サーボ系を含んだシステムでは、Verilogコーディング前にMatlabもしくはクローン、もしくはC/C++でシステムシミュレーションをするのが普通です。その後、デザインの最終段階であるVerilogではどうしているでしょうか? LSIはデジタルでも現実の周辺は、アナログフィルタ、DCモータやVCM,あるいはOPAMPといったアナログで動く場合もあるでしょう。 Verilogですべてをモデリングすることも不可能ではないですが、シミュレーション効率から言って、あまり得策とは言えません。 (特にVeritakの場合、内部がインタプリタコードで、REALの実装もBITVECTORと同じ4値にしてしまっているのでREALが多いとシミュレーションスピードとメモリ効率が低下してしまいます。) こんなときは、軽めのアナログシミュレータが使えると便利です。そこで、これもVPIを使って線形システムのシュミレータを実装してみました。 シミュレータは、状態空間型の行列を定義したテキストファイルを読み込んで、毎クロック$runge_kutta(..)で呼び出します。引数にREAL型アレーを記述してIFします。 SPICEのように非線形素子は扱えませんが、アナログフィルタの時間応答を見るとか、サーボ系の動作検証といった用途には使えると思います。また、VPIのサンプルとしてもよい例になっていると思います。 筆者の処理系は、VC7C++ですが、__int64をlong longとすれば、GCC系でもコンパイルできると思います。 なお、本ライブラリは、Veritak Version1.02よりmath_vpi.dllとしてインストールパッケージに追加されています。(Jan.6.2005 update) VeriPad Veritakに付属しているテキストエディタでVC++ソースです。オリジナルは、NYSLライセンスのGreenPadです。 Veritakとの連携を取るべく筆者がブレークポイント、ツールチップ、タグジャンプ、その他Veritak とのIFをGreenPadに追加し勝手にVeriPadとしてしましました。 その気のある方は、究極のカスタマイズが可能です。 オリジナルは、Super Tiny記述になっていて、EXEサイズが50KBという極小サイズを実現していましたが、筆者がSTLを使ってしまい普通のサイズになってしまいました。 好みのEditorのスクリプト等を使って、VeriPadもどきを実現したい方は、Windows通信のIFであるGpMain.cppから見てゆけばよいでしょう。 VeriPadソースのダウンロード(1MB) Jul.11.2005版 Release Note:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||