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.