No matter what phase resolution is used for an NCO, phase drift will occur if phase increment value in not an integer. This is because the error of fraction builds up until the phase wraps up full cycle.This drift matters if your application is phase sensitive.
The code below shows two cases of NCO, one(generic) with phase resolution of 2^16 but with rounded increment value. the second(dedicated) with lower phase resolution but integer increment value.
Notice also that in case the accumulator resolution is higher than LUT and hence accumulator bits are to be truncated (and rounded) then it will result in further phase error build up. In this case unbiased rounding is useful.
clear all; close all; n = 50000; %number of test samples Fs = 100; %sampling rate in Msps Fo = 1.17; %target NCO frequency in MHz M = [2^16 10000]; %NCO phase resolution, two cases for i = 1:2 inc = round(M(i)*Fo/Fs); %phase increment lut = round(2047*cos(2*pi*(0:M(i)-1)/M(i))); %lut, one cycle cosine data addr = 0:inc:inc*(n-1); addr = round(mod(addr,M(i))); addr(addr >= M(i)) = addr(addr >= M(i)) - M(i); %check address overflow y(i,1:n) = lut(addr+1); %add 1 for matlab LUT end fprintf('increment value = %2.6f\r',M*Fo/Fs); subplot(2,1,1);hold; plot([y(1,1:1000) zeros(1,100) y(1,end-999:end)],'.-'); plot([y(2,1:1000) zeros(1,100) y(2,end-999:end)],'r.--'); legend('drift','no drift'); title('initial and last 1000 samples'); [P1, F1] = pwelch(y(1,:), hann(n), 0, n, Fs); [P2, F2] = pwelch(y(2,:), hann(n), 0, n, Fs); subplot(2,1,2); hold plot(F1,10*log10(P1)); plot(F2,10*log10(P2),'r--'); legend('drift','no drift') axis ([0 50 -20 100]); grid xlabel('MHz')