TangNano9kでDDSを製作

電子工作

 TangNano9kという安価なFPGAボードでDDS(DirectDigitalSynthesizer)を製作しました。DDSの設計に当たってはAI(Gemini)に色々と相談しました。

 foutは、位相アキュムレータのビット数とシステムクロックfclkおよびFTWで決まります。当初PLLで200MHzを生成して、これをシステムクロックとして使ったところ、変な波形が出ました。原因は、FPGA内部の遅延時間が5ns(1/200MHz)を超えていたものと考えられます。詳しくFPGAのタイミングレポートをみると、内部遅延時間によりクロックは最大120MHzまでということが判明しました。FPGA内部のPLLで生成可能な周波数には制限があります。TangNano9kのXtalは27MHzなので、整数倍の108MHzをPLLで生成してシステムクロックにしました。

 foutとして、500kHz, 1MHz, 2MHz, 5MHz, 10MHz, 20MHzの6波をボード上の押し釦スイッチで選択することとしました。どの周波数を出力しているのか目視できるように6つのLEDランプに対応させました。FTWは前述の計算から次のようになります。

 冒頭の写真は10MHzを発生させた時のオシロスコープ画像です。1クロック(10ns)分のジッターがあることが良く分かります。しかし、オシロスコープは周波数が10.0000MHzだと表示しています。このDDSをベースにしてSDRを作ってみようと考えていますが、果たしてこのジッターがどのような影響をもたらすでしょうか?

 以下にVerilogのソースコードを示します。

module tang_nano_9k_dds_top (
    input  wire       sys_clk,   // 27MHz 外部入力 (Pin 52)
    input  wire       sys_rst_n, // S2ボタンをリセットに割り当て (Pin 4)
    input  wire       btn_next,  // S1ボタンを周波数切り替えに (Pin 3)
    output wire [5:0] led,       // オンボードLED
    output wire       out_dds
);
    wire clk_pll;

    // PLL instance
    Gowin_rPLL PLLosc(
        .clkout(clk_pll), //output clkout
        .lock(lock), //output lock
        .clkin(sys_clk) //input clkin
    );


    // ---  DDS & 周波数切り替えロジック ---
    // PLLがロックしてから動作を開始させる
    dds_control u_dds (
        .clk_PLL(clk_pll),
        .rst_n(sys_rst_n & lock),
        .btn_next(btn_next),
        .led(led),
        .dds_out(out_dds)
    );

endmodule


module dds_control (
    input  wire       clk_PLL, // clock from PLL
    input  wire       rst_n,
    input  wire       btn_next, // 周波数切り替えボタン (S1など)
    output reg [5:0]  led,      // オンボードLED 6個
    output wire       dds_out  // dds output
);

    // --- 1. 周波数テーブル (32bit step値) @PLL=108MHz---
    reg [31:0] step_table [0:5];
    initial begin
        step_table[0] = 32'h012f684b; // 500kHz
        step_table[1] = 32'h025ed097; // 1MHz
        step_table[2] = 32'h04bda12f; // 2MHz
        step_table[3] = 32'h0bda12f6; // 5MHz
        step_table[4] = 32'h17b425ed; // 10MHz
        step_table[5] = 32'h2f684bda; // 20MHz
    end

    // --- 2. ボタンのデバウンス (簡易版) ---
    reg [19:0] btn_cnt;
    reg btn_state, btn_prev;
    wire btn_edge;

    always @(posedge clk_PLL) begin
        btn_cnt <= btn_cnt + 1'b1;
        if (btn_cnt == 0) begin
            btn_state <= ~btn_next; // 物理ボタンはLow-Activeなので反転
        end
        btn_prev <= btn_state;
    end
    assign btn_edge = btn_state & ~btn_prev; // 立ち上がり検知

    // --- 3. ステート管理 ---
    reg [2:0] mode_idx;
    always @(posedge clk_PLL or negedge rst_n) begin
        if (!rst_n) begin
            mode_idx <= 3'd0;
        end else if (btn_edge) begin
            if (mode_idx >= 5) mode_idx <= 0;
            else mode_idx <= mode_idx + 1'b1;
        end
    end

    // LED表示更新 (1点灯)
    always @(*) begin
        led = ~(6'b000001 << mode_idx); // Tang Nano 9kのLEDはLow点灯のため反転
    end

    // --- 4. DDSコア ---
    reg [31:0] phase_acc;
    always @(posedge clk_PLL or negedge rst_n) begin
        if (!rst_n) phase_acc <= 0;
        else phase_acc <= phase_acc + step_table[mode_idx];
    end


    wire w_out_internal = phase_acc[31];
    assign dds_out = w_out_internal;

endmodule

コメント