0
0
VhdlHow-ToBeginner · 4 min read

VHDL Code for NCO Numerically Controlled Oscillator Example

A Numerically Controlled Oscillator (NCO) in VHDL uses a phase accumulator and a lookup table or direct calculation to generate a digital waveform. The core is a phase register incremented by a frequency control word each clock cycle, producing a stable output frequency proportional to that word.
📐

Syntax

An NCO in VHDL typically includes these parts:

  • Phase Accumulator: A register that adds the frequency control word every clock cycle.
  • Frequency Control Word (FCW): A constant or input that sets the output frequency.
  • Output Generation: Usually a lookup table or direct bit extraction from the phase accumulator to create a waveform.

This simple structure allows frequency tuning by changing the FCW.

vhdl
process(clk)
  variable phase_acc : unsigned(31 downto 0) := (others => '0');
begin
  if rising_edge(clk) then
    phase_acc := phase_acc + fcw;
    output <= phase_acc(31); -- MSB as output for square wave
  end if;
end process;
💻

Example

This example shows a simple NCO generating a square wave output by using the most significant bit of a 32-bit phase accumulator. The frequency is controlled by the 32-bit input fcw.

vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity nco is
  port(
    clk   : in std_logic;
    reset : in std_logic;
    fcw   : in unsigned(31 downto 0); -- Frequency Control Word
    out_wave : out std_logic
  );
end entity;

architecture rtl of nco is
  signal phase_acc : unsigned(31 downto 0) := (others => '0');
begin
  process(clk, reset)
  begin
    if reset = '1' then
      phase_acc <= (others => '0');
      out_wave <= '0';
    elsif rising_edge(clk) then
      phase_acc <= phase_acc + fcw;
      out_wave <= phase_acc(31); -- MSB as output
    end if;
  end process;
end architecture;
Output
The output <code>out_wave</code> toggles at a frequency proportional to the <code>fcw</code> value, producing a square wave.
⚠️

Common Pitfalls

Common mistakes when coding an NCO in VHDL include:

  • Not using an unsigned type for the phase accumulator, causing incorrect wrap-around behavior.
  • Forgetting to reset the phase accumulator, which can cause unpredictable startup output.
  • Using too small a width for the phase accumulator, limiting frequency resolution.
  • Extracting the wrong bit for output, which changes the output frequency unintentionally.
vhdl
process(clk)
  variable phase_acc : signed(31 downto 0) := (others => '0'); -- Wrong type
begin
  if rising_edge(clk) then
    phase_acc := phase_acc + fcw;
    output <= phase_acc(31); -- May cause errors due to signed overflow
  end if;
end process;

-- Corrected version:
process(clk)
  variable phase_acc : unsigned(31 downto 0) := (others => '0');
begin
  if rising_edge(clk) then
    phase_acc := phase_acc + fcw;
    output <= phase_acc(31); -- Correct unsigned usage
  end if;
end process;
📊

Quick Reference

Key points for implementing an NCO in VHDL:

  • Use a wide unsigned phase accumulator (e.g., 32 bits) for good frequency resolution.
  • Increment the accumulator by the frequency control word every clock cycle.
  • Output can be the MSB for a square wave or use a lookup table for sine waves.
  • Always reset the accumulator on system reset.

Key Takeaways

Use an unsigned phase accumulator to ensure correct frequency stepping and wrap-around.
The frequency control word sets the output frequency by controlling phase increments.
Output waveform can be generated by extracting bits or using a lookup table.
Always include a reset to initialize the phase accumulator properly.
Wider accumulators provide finer frequency resolution but use more resources.