nco.v.txt
上传用户:saul_905
上传日期:2013-11-27
资源大小:184k
文件大小:7k
源码类别:

VHDL/FPGA/Verilog

开发平台:

Visual C++

  1. //
  2. // NCO Demo (Numerically controlled Oscillator).
  3. //
  4. // One type of NCO is based on the idea of a continuously wrapping modulo
  5. // counter.  The NCO is a programmable modulo counter.  For example, if
  6. // the MOD input is set to 10, then it'll count 0,1,2,3,4,5,6,7,8,9,0,1,2 etc.
  7. // The STEP input is continuously added to the modulo counter.  What's important
  8. // is not the counter value itself, but the number of times it "wraps".  If
  9. // you do the math, the counter will wrap at a rate of F*S/M (F is the system
  10. // clock frequency, S is the step input, and M is the modulo).  Every wrap
  11. // occurance is a little pulse.  The pulse output could be used as the clock itself
  12. // except that it may have a very small duty cycle.  Instead, take the WRAP
  13. // pulse signal and use it to increment a TICKS counter.  The TICKS counter will
  14. // then count at the programmed frequency.
  15. //
  16. // The TICK counter could be used in itself for, say, a TDMA slot counter.  Any
  17. // number of TICKS LSB bits can be thrown away to "reduce jitter" but also
  18. // reduce the rate.  This NCO has a little MASK input and an internal OR gate
  19. // so you can pick off whichever TICKS bits you wish for your final FOUT output.
  20. //
  21. // In an open-loop mode, you program your Modulo and Step inputs and you are done.
  22. // If you are tracking some other reference (e.g. like in a PLL style circuit) you
  23. // would somehow control STEP via some sort of "phase detector" and loop filter.
  24. // None of this is shown here.
  25. //
  26. // Anyway, this is just the guts of the NCO, which can be applied in many, many
  27. // ways.
  28. //
  29. // Oh.. And this NCO seems to be off by a small percentage..?!..  I'm not sure
  30. // why.  It may be the circuit or the testbench.  Let me know if you play with it
  31. // and find out.  My application is closed-loop, so, I'm not highly motivated
  32. // to figure it out.
  33. //
  34. // Written by tom coonan
  35. //
  36. module nco (
  37.    clk,
  38.    resetb,
  39.    step, // Step input is continuously added to the modulo counter
  40.    mod, // modulo
  41.    mask, // Mask is ANDed with ticks and gen ORed to produce fout
  42.    ticks, // Tick counter output
  43.    fout // Output.
  44. );
  45. parameter W_ACCUM = 24; // Width of the Accumulator
  46. parameter W_TICK  = 8; // Width of the Tick counter.
  47. parameter W_STEP  = 24;
  48. parameter W_MOD   = 24;
  49. input clk;
  50. input resetb;
  51. input [W_STEP-1:0] step;
  52. input [W_MOD-1:0] mod;
  53. input [W_TICK-1:0] mask;
  54. output [W_TICK-1:0] ticks;
  55. output fout;
  56. // Registered outputs
  57. // Internals
  58. reg [W_ACCUM-1:0] accum, accum_in;
  59. reg [W_TICK-1:0] ticks;
  60. // *** Modulo Counter ***
  61. // Registered outputs
  62. reg wrap;
  63. wire [W_ACCUM-1:0] sum = accum + step;
  64. wire [W_ACCUM-1:0] rem = sum - mod;
  65. wire over = (sum >= mod);
  66. always @(posedge clk or negedge resetb)
  67.    if (~resetb) accum <= 0;
  68.    else         accum <= accum_in;
  69.    
  70. always @(over or rem or sum) begin
  71.    if (over) begin
  72.       // Wrap!
  73.       accum_in <= rem; // load remainder instead of sum
  74.       wrap <= 1;
  75.    end
  76.    else begin
  77.       // No wrap, just add
  78.       accum_in <= sum;
  79.       wrap <= 0;
  80.    end
  81. end
  82. // *** Tick Counter ***
  83. //
  84. always @(posedge clk) begin
  85.    if (~resetb) ticks <= 0;
  86.    else begin
  87.       // Whenever Modulo counter wraps, increment the tick counter.
  88.       if (wrap) 
  89.          ticks <= ticks + 1;
  90.    end
  91. end
  92. // *** Masks and final output *** //
  93. assign fout = |(ticks & mask);
  94. endmodule
  95. module ncotest;
  96. reg clk;
  97. reg resetb;
  98. reg [23:0] step;
  99. reg [23:0] mod;
  100. reg [7:0] mask;
  101. wire fout;
  102. wire [7:0] ticks;
  103. parameter W_ACCUM = 24; // Width of the Accumulator
  104. parameter W_TICK  = 8; // Width of the Tick counter.
  105. parameter W_STEP  = 24;
  106. parameter W_MOD   = 24;
  107. nco nco1 (
  108.    .clk(clk),
  109.    .resetb(resetb),
  110.    .step(step),
  111.    .mod(mod),
  112.    .mask(mask),
  113.    .fout(fout),
  114.    .ticks(ticks)
  115. );
  116. parameter PERIOD_NS = 36;
  117. parameter DUMP_ON = 1;
  118. real sys_freq;
  119. initial begin
  120.    step = 0;
  121.    mod = 0;
  122.    mask = 8'b00000001; // Final Divider for FOUT
  123.    
  124.    sys_freq = 1000000000.0/(PERIOD_NS);
  125.    
  126.    #300;
  127.    // Display the basic such as system clock frequency, etc.
  128.    //
  129.    $display ("NCO Test.  NCO Accumulator width is %0d bits, system clock period is %0d ns (%fMHz).",
  130.       W_ACCUM,
  131.       PERIOD_NS,
  132.       sys_freq
  133.    );
  134.    
  135.    // Program Modulo and Step for desired frequency.  Modulo and Step values should
  136.    // not be divisible by each other (I'm not mathematically strong enough to
  137.    // justify this...).  Find the ratio of S/M, integerize it, and reduce to lowest
  138.    // common divisors.  Then, multiply up by a big power of 2 so we can get some
  139.    // resolution on the NCO.
  140.    // 
  141.    
  142.    // Let's generate 1Mhz, Fsys*(Step/Mod)/2 = 27777777.777*(S/M)/2 = 1000000
  143.    //    S/M = 0.072 = 72/1000 = 9/125 (9 * 2^12) / (125 * 2^12)
  144.    //
  145.    mod  = 125 << 12; // Shift up so we get resolution..
  146.    step =   9 << 12; // Shift up so we get resolution..
  147.    nco_test (mod, step, 1000000); // Run test for specified interval (units are NS)
  148.    
  149.    // Generate 10.24MHz: Fsys*(Step/Mod)/2 = 27777777.777*(S/M)/2 = 10240000
  150.    //    S/M = 0.73728 = 73728/100000 = 9216/12500 = 4608/6250 = 2304/3125
  151.    //
  152.    mod  = 3125 << 10; // Shift up so we get resolution..
  153.    step = 2304 << 10; // Shift up so we get resolution..
  154.    nco_test (mod, step, 1000000); // Run test for specified interval (units are NS)
  155.    
  156.    // Generate 32.768 KHz using TICKS MSB (divide by 8): 
  157.    //    Fsys*(Step/Mod) = 27777777.777*(S/M) = 32768*256
  158.    //    S/M = 0.301989888 =~ 0.302 = 302/1000 = 151/500
  159.    //
  160.    mask = 8'b10000000; // Divide by 256
  161.    mod  = 500 << 12;
  162.    step = (151 << 12) - 9566;  // Manually Tweaked this to get the right number..
  163.                                // this is expected since didn't have a good integer
  164.                                // ratio above..
  165.    nco_test (mod, step, 1000000); // Run test for specified interval (units are NS)
  166.       
  167.    $display ("Done.");
  168.    
  169.    $finish;
  170. end
  171. // Run a single trial of the NCO test.
  172. //   
  173. task nco_test;
  174.    input [23:0] mod_arg;
  175.    input [23:0] step_arg;
  176.    input interval;
  177.    
  178.    integer interval;   // Use $time..  Make sure timescale is correct!
  179.    integer start_time;
  180.    integer fout_edges;
  181.    
  182.    begin
  183.       step = step_arg; // Configure NCO
  184.       mod  = mod_arg;
  185.       
  186.       // Count rising edges on FOUT which is the output frequency
  187.       fout_edges = 0;
  188.       start_time = $time; // Note our starting time
  189.       // Loop for at least the specified amount of time
  190.       while ( ($time - start_time) < interval) begin
  191.          @(posedge fout); // Wait for an edge on FOUT
  192.          fout_edges = fout_edges + 1;
  193.       end
  194.       
  195.       // Done.  Display results and expected results.
  196.       $display ("For Mod=%0d(0x%h), Step=%0d(0x%h), Frequency of fout = %f Hz, Expected fout is %f Hz.",
  197.          mod, mod,
  198.          step, step,
  199.          ((fout_edges*1.0)/($time - start_time))*1000000000.0, // measured..
  200.          ((step*1.0)/(mod*1.0))*(sys_freq)/(mask*2.0)   // expected..
  201.       );
  202.    end
  203. endtask
  204. // Let's clock it at about 27 MHz
  205. initial begin
  206.    clk = 0;
  207.    forever begin
  208.       #(PERIOD_NS/2) clk = ~clk;
  209.    end
  210. end
  211. initial begin
  212.    resetb = 0;
  213.    #200 resetb = 1;
  214. end
  215. initial begin
  216.    if (DUMP_ON) begin
  217.       $dumpfile ("nco.vcd");
  218.       $dumpvars (0,ncotest);   
  219.    end
  220. end
  221. endmodule