MAXU PLD入門 その6 power up! your DDS


3.5MHz出力(1次ローパスフィルタ(fc=140KHz)を通しているので、かなり減衰しています。
でも、使えるかも....

0.移動平均の所で、迷いましたが、解決
 
        しもたっ! 移動平均をとる時に、遅延の事を忘れとった。
    どうも、話がうますぎると思いました....ごめんなさい。
    50MHzのクロックで、350nSほどの遅延か?
      
    しかし、計算自体は、1クロックで済んでいるのか?

    ?....思案中です、スマソ

     上記の疑問の解決です。

     やはり、1クロックで、移動平均を、こなしています。
     計算の遅延は、1クロックでした、ホッ、安堵 (^_^;;


    追記終わり
//////////////////////////////////////////////////////////////////////////////////
 
 1.MAXUPLDによるDDSを、100MHzクロックで作動させる

    実際に 水晶発信器を、100MHzに取り替えるのでは、ありません。 そのままです。

    例によって、クロックの立ち上がり、及び、立下りの両方で動作するように、verilogHDLで記述するのです。(後で、プログラムを記述します。)

    100MHz動作させると、どうなるのか?

                     50MHz動作                                                    100MHz動作

    ご覧のように、100MHz動作させると、3次以下の高調波が軽減されるのです。

     後ほど、romのsin波データの上下限値を90%に抑えると、更にきれいになりました。(100MHZ動作)



    と言っても、DDS出力、10KHzでの観測では、悪化しとりました....              同じく、romのsin波データの上下限値を90%に抑えると、更にきれいになりました。(100MHZ動作)




    でも、私の狙いは、高い周波数を出力する事に、ありますので、これでよし。

    ハムバンド 1.9MHz,3.5,7,10,14MHz あたりを出力する事を、狙っています。

    少なくとも、3.5MHzまでは、可能かも.....

 2.シミュレーションと、実験結果

    シミュレーションは、Model-sim Alteraで行いました。

                3.5MHz付近を出力時

               7MHz付近を出力時

    どうです? ローパスフィルタを通せば、いけそうな気がしませんか?

    アナログスイッチICを駆動するためには、

    厳密な正弦波が必要な訳では無く、、そこそこの、IQ出力であれば、よいのでは.....

    但し、クロック周波数に対して、比較的高い周波数を出力するには (例、 100MHzクロックで、3.5,7MHz出力等)

    PDM ( Pulse Density Modulation )による、D/A変換は、不向きだと思われます。

    その原理上、パルスの密度で、D/A変換するのですから、沢山の高調波が出ますから。

        でも、ローノイズも、凄い魅力なんです..... 1MHz以下の周波数出力なら、最高!

   以下の実験結果は、1次ローパスフィルタ(fc=140KHz)および、同じく1次ローパスフィルタ(fc=1MHz)を通した結果です。

3.5MHz

                FC=140KHz(出力周波数のレベルは、かなり減衰)                                       fc=1MHz(減衰は僅か)

7MHz

                FC=140KHz(出力周波数のレベルは、かなり減衰)                                    fc=1MHz(減衰は僅か)

14MHz

                   FC=140KHz(出力周波数のレベルは、かなり減衰)

    ご覧のように、ローパスフィルタのカットオフを低くすると、綺麗になるが、3.5,7,14MHzあたりでは、出力周波数が減衰しすぎています。

    かと言って、カットオフを高く設定すると、PDMによるD/A変換のため、メッチャ、スプリアスが増えて、使えません。

    どうも、PDMによる、D/A変換を使えない模様です。

    R-2Rのラダーで、D/A変換する? いやや、16bit やさかい、気が滅入る。

    どないしょ..... ビット数を落とすしか、なさそうですね.......

3.移動平均をMAXUPLDで

    以下のverilogHDLプログラムは、FFを直列に並べたものです。

    これが、しっかり、遅延器になりますので、移動平均を採る事ができます。
 
    しかも、移動平均を、たったの1クロックで、やっつけてしまう.....DSPも真っ青。


         100MHzクロックの時、5KHzを、50MHzと読み替える事 (ディジタル信号処理とDSP 三上先生 CQ出版)

   
module series_FF(clock,in1,out1);
 input   clock;
 input  [15:0]  in1;
 output [15:0]  out1;
 reg    [31:0] res1;
 reg [15:0]   z[0:15];
  
 assign out1=res1[19:4]; //これは res1>>4 と等価 、16で割る=右4ビットシフト
 
 always @(posedge clock) begin
  z[15] <= in1;
  z[14] <= z[15];
  z[13] <= z[14];
  z[12] <= z[13];
  z[11] <= z[12];
  z[10] <= z[11];
  z[9]  <= z[10];
  z[8]  <= z[9];
  z[7]  <= z[8];
  z[6]  <= z[7];
  z[5]  <= z[6];
  z[4]  <= z[5];
  z[3] <= z[4];
  z[2] <= z[3];
  z[1] <= z[2];
  z[0] <= z[1];
  
  res1 <= z[15]+z[14]+z[13]+z[12]+z[11]+z[10]+z[9]+z[8]+z[7]+z[6]+z[5]+z[4]+ z[3]+z[2]+z[1]+z[0];
 end
 initial begin
  res1 <=0;
  z[0] <=0;
  z[1] <=0;
  z[2] <=0;
  z[3] <=0;
  z[4] <=0;
  z[5] <=0;
  z[6] <=0;
  z[7] <=0;
  z[8] <=0;
  z[9] <=0;
  z[10] <=0;
  z[11] <=0;
  z[12] <=0;
  z[13] <=0;
  z[14] <=0;
  z[15] <=0;
 end
  
endmodule

      
  この移動平均を、以下のように DDSとPDMによるD/A変換の間に、埋め込みます。


     16個の移動平均でも、少し効果が出ています。

    こんどは、PDMの1ビットの出力に、移動平均を作動させました。


    凄い効果が、ありました。( in1が移動平均前、out1が移動平均後 )


    でも、これでは、PDMによる、D/A変換になりませんね。

    スプリアスだらけのFFTの結果でした......当然か...(^_^;;

    しかし、1ビットの積和演算って、魅力ですね、なんせ、フリップフロップも1ビットやから、LEの消費もすくないし。

    ...............................................................................................................................

    しっかし、MAXUを購入した時は、DDSの制御にも使えるかな位のつもりだったのに

    DDS自体を、MAXUで作れるなんて、グレート!

    最後に、MAXUPLDを、100MHz相当で働かせる為の、verilogHDLプログラムです。

    私は、verilogHDL入門2ヶ月目なんで、

    こんなもん、とっくの昔に発表されているでしょうが、私と同じ、verilogHDL好きの方の、ご参考になれば、幸いです。

////////////////////////////////////////////////////////////////////
//top module : this program works, as if clock is 100MHz (real clock is 50MHz)
module test_DDS4_final(clock,KEY,carry_out);
 input  clock;
 input  KEY;
 output carry_out;
// reg [31:0]  inc;   
 wire [31:0]  inc;
 wire [15:0] data;
 reg  [31:0]  inc2; 

//3.5MHz=32'h08F5C28F @100MHz  100KHz= 32'h00418937;
 assign inc =32'h08F5C28F;
 
 myDDS4_final myDDS4_final(clock,inc2,data); 
 acum   PDM(data,clock,carry_out);
 
 always @(negedge KEY) begin //every time press KEY, get ' inc ' Hz up
  inc2 <=inc2+inc;
 end
 
 initial begin
  inc2 <=inc;
 end
 
endmodule
/////////////////////////////////////////////////////////////////////
//address 32bit, data 16bit
module myDDS4_final(clock,inc,data);
 
input   clock;
 input  [31:0] inc;
 output  [15:0] data; //16bit data
 
 wire [9:0] addr;  //10bit rom address
 reg  [31:0]  add2,add3;  //32bit
 reg [1:0]  count;

 assign  addr =  (count==0  ?  add2[31:22] : count==3 ? add2[31:22] : add3[31:22] );
 
 mSinRom mSinRom1(addr,data);
 
 
always @(posedge clock) begin  //add2とadd3がposedgeと、negedgeで交互に増える
  add2 <= add3 + inc;    
  count[0] <=~count[0];      //こうすると、add2をaddrに代入するのは、count=0,3の時
 end
 
 always @(negedge clock) begin
  add3 <= add2 + inc ;
  count[1] <=~count[1];      //こうすると、add3をaddrに代入するのは、count=0,3以外の時

 end
 
 initial begin
  //inc <=32'h0083126F;//100KHz
  //inc <=32'h000D1B72;//10KHz
  count <=0;
  add2 <=0;
  add3 <=0;
 end
 
endmodule
/////////////////////////////////////////////////////////////////////
    赤印の所の、シミュレーションです。

  
    上記は、解りやすくするために、add2、add3の増加を小さく(1000h)した為、addrの値が変化していませんが
 
    実際は、以下のように、romを読むためのアドレス addr の値は、クロックの1/2毎に変化しています。



/////////////////////////////////////////////////////////////////////////////////////////////////////////////
  

module acum(VDA,clk,cy_res);

 input [15:0] VDA ;
 input  clk;
 output   cy_res;
  
 reg [15:0]   R;
 reg [15:0]   R1;
 reg    cy_out,cy_out1;
 wire    cy_res;
 reg [1:0]   count;
 
 assign cy_res =( count ==0 ?  cy_out : count==3 ?cy_out : cy_out1);
 
 initial begin
     R   <=0;
     R1 <=0;
     cy_out <=0;
     cy_out1 <=0;
     count <=0;
 end
 
 always @(posedge clk) begin
  { cy_out, R } <= VDA+R1;
  count[0] <=~count[0] ;
 end
 
 always @(negedge clk) begin
  {cy_out1, R1} <= VDA + R;
  count[1] <=~count[1];
 end
  
endmodule
/////////////////////////////////////////////////////////////////////
module mSinRom(adr,dat);
 input [9:0]  adr; //10bit address
 output [15:0] dat; //16bit data
 reg [15:0]  romdat;
 
 assign dat=romdat;
 
 always @(adr) begin
  case(adr)
  
10'h000:romdat=16'h7FFF;
10'h001:romdat=16'h80C8;
10'h002:romdat=16'h8191;
10'h003:romdat=16'h825A;
10'h004:romdat=16'h8323;
....................
10'h3FC:romdat=16'h7CDA;
10'h3FD:romdat=16'h7DA3;
10'h3FE:romdat=16'h7E6C;
10'h3FF:romdat=16'h7F35;

  default romdat=16'h7FFF;
 
  endcase
 end
endmodule
/////////////////////////////////////////////////////////////////////

 H.21.2.25