MAXU PLD入門 その6 power up! your DDS 2.シミュレーションと、実験結果 //3.5MHz=32'h08F5C28F
@100MHz 100KHz= 32'h00418937; assign addr =
(count==0 ? add2[31:22] : count==3 ? add2[31:22] : add3[31:22]
); input [15:0] VDA
; default
romdat=16'h7FFF;
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までは、可能かも.....
シミュレーションは、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;
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;
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 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;
endcase
end
endmodule
/////////////////////////////////////////////////////////////////////
H.21.2.25