Skip to content

Instantly share code, notes, and snippets.

@pavel-kirienko
Created August 6, 2025 12:33
Show Gist options
  • Save pavel-kirienko/7ccd1f7079f5c90dfdd03a29b36e10f5 to your computer and use it in GitHub Desktop.
Save pavel-kirienko/7ccd1f7079f5c90dfdd03a29b36e10f5 to your computer and use it in GitHub Desktop.

Revisions

  1. pavel-kirienko created this gist Aug 6, 2025.
    37 changes: 37 additions & 0 deletions nco.v
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    /// A numerically controlled oscillator (NCO) that outputs a sawtooth wave, whose frequency is a function of
    /// clk rate and frequency_control_word, and the amplitude spans the range [0, 2**OUTPUT_WIDTH).
    /// The output frequency is defined as:
    ///
    /// f_out = (f_clk * frequency_control_word) / (2**PHASE_ACCUMULATOR_WIDTH)
    ///
    /// Solve for frequency_control_word:
    ///
    /// frequency_control_word = ((2**PHASE_ACCUMULATOR_WIDTH) * f_out) / f_clk
    ///
    /// The phase of the output signal can be adjusted using phase_control_word, which is a value in the range
    /// [0, 2**PHASE_ACCUMULATOR_WIDTH) that maps to [0, 2 pi).
    ///
    /// Both of the control words can be changed arbitrarily; changes take effect in the next cycle.
    module nco #(
    parameter OUTPUT_WIDTH = 8, ///< Larger values reduce phase noise.
    parameter PHASE_ACCUMULATOR_WIDTH = 64 ///< Larger values reduce frequency error.
    )
    (
    input wire clk,
    input wire reset,
    input wire [PHASE_ACCUMULATOR_WIDTH-1:0] frequency_control_word,
    input wire [PHASE_ACCUMULATOR_WIDTH-1:0] phase_control_word,
    output wire [OUTPUT_WIDTH-1:0] out
    );
    reg [PHASE_ACCUMULATOR_WIDTH-1:0] acc = 0;
    wire [PHASE_ACCUMULATOR_WIDTH-1:0] acc_phased = acc + phase_control_word;
    assign out = acc_phased[PHASE_ACCUMULATOR_WIDTH-1:PHASE_ACCUMULATOR_WIDTH-OUTPUT_WIDTH];

    always @(posedge clk or posedge reset) begin
    if (reset) begin
    acc <= 0;
    end else begin
    acc <= acc + frequency_control_word;
    end
    end
    endmodule
    32 changes: 32 additions & 0 deletions nco_tb.v
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    /// iverilog -Wall -Wno-timescale -y. nco_tb.v && vvp a.out
    /// Then open nco.vcd in GTK Wave or whatever.

    `timescale 1ns/1ns

    module nco_tb;
    reg reset = 0;
    initial begin
    # 1 reset = 1;
    # 2 reset = 0;
    # 100 pcw = 32;
    # 100 fcw = 4;
    # 100 pcw = 16;
    # 100 fcw = 8;
    # 100 $finish;
    end

    reg clk = 0;
    always begin
    #1 clk = !clk;
    end

    reg [5:0] fcw = 1;
    reg [5:0] pcw = 0;
    wire [1:0] out;
    nco #(2, 6) nco_it (clk, reset, fcw, pcw, out);

    initial begin
    $dumpfile("nco.vcd");
    $dumpvars();
    end
    endmodule