OpenCores
URL https://opencores.org/ocsvn/openarty/openarty/trunk

Subversion Repositories openarty

[/] [openarty/] [trunk/] [rtl/] [gpsclock.v] - Blame information for rev 12

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    gpsclock.v
4
//              
5
// Project:     A GPS Schooled Clock Core
6
//
7
// Purpose:     The purpose of this module is to school a counter, run off of
8
//              the FPGA's local oscillator, to match a GPS 1PPS signal.  Should
9
//              the GPS 1PPS vanish, the result will flywheel with its last
10
//              solution (both frequency and phase) until GPS is available
11
//              again.
12
//
13
//              This approach can be used to measure the speed of the
14
//              local oscillator, although there may be other more appropriate
15
//              means to do this.
16
//
17
//              Note that this core does not produce anything more than
18
//              subsecond timing resolution.
19
//
20
// Parameters:  This core needs two parameters set below, the DEFAULT_STEP
21
//              and the DEFAULT_WORD_STEP.  The first must be set to
22
//              2^RW / (nominal local clock rate), whereas the second must be
23
//              set to 2^(RW/2) / (nominal clock rate), where RW is the register
24
//              width used for our computations.  (64 is sufficient for up to
25
//              4 GHz clock speeds, 56 is minimum for 100 MHz.)  Although
26
//              RW is listed as a variable parameter, I have no plans to 
27
//              test values other than 64.  So your mileage might vary there.
28
//
29
//              Other parameters, alpha, beta, and gamma are specific to the
30
//              loop bandwidth you would like to choose.  Please see the
31
//              accompanying specification for a selection of what values
32
//              may be useful.
33
//
34
// Inputs:
35
//      i_clk   A synchronous clock signal for all logic.  Must be slow enough
36
//              that the FPGA can accomplish 64 bit math.
37
//
38
//      i_rst   Resets the clock speed / counter step to be the nominal
39
//              value given by our parameter.  This is useful in case the
40
//              control loop has gone off into never never land and doesn't
41
//              seem to be returning.
42
//
43
//      i_pps   The 1PPS signal from the GPS chip.
44
//
45
//      Wishbone bus
46
//
47
// Outputs:     
48
//      o_led   No circuit would be complete without a properly blinking LED.
49
//              This one blinks an LED at the top of the GPS 1PPS and the
50
//              internal 1PPS.  When the two match, the LED will be on for
51
//              1/16th of a second.  When no GPS 1PPS is present, the LED
52
//              will blink with a 50% duty cycle.
53
//
54
//      o_tracking      A boolean value indicating whether the control loop
55
//              is open (0) or closed (1).  Does not indicate performance.
56
//
57
//      o_count         A counter, from zero to 2^RW-1, indicating the position
58
//              of the current clock within a second.  (This'll be off by 
59
//              two clocks due to internal latencies.)
60
//
61
//      o_step          The amount the counter, o_count, is stepped each clock.
62
//              This is related to the actual speed of the oscillator (when
63
//              locked) by f_XO = 2^(RW) / o_step.
64
//
65
//      o_err   For those interested in how well this device is performing,
66
//              this is the error signal coming out of the device.
67
//
68
//      o_locked        Indicates a locked condition.  While it should work,
69
//              it isn't the best and most versatile lock indicator.  A better
70
//              indicator should be based upon how good the user wants the
71
//              lock indicator to be.  This isn't that.
72
//
73
//
74
// Creator:     Dan Gisselquist, Ph.D.
75
//              Gisselquist Technology, LLC
76
//
77
////////////////////////////////////////////////////////////////////////////////
78
//
79
// Copyright (C) 2015, Gisselquist Technology, LLC
80
//
81
// This program is free software (firmware): you can redistribute it and/or
82
// modify it under the terms of  the GNU General Public License as published
83
// by the Free Software Foundation, either version 3 of the License, or (at
84
// your option) any later version.
85
//
86
// This program is distributed in the hope that it will be useful, but WITHOUT
87
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
88
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
89
// for more details.
90
//
91
// You should have received a copy of the GNU General Public License along
92
// with this program.  (It's in the $(ROOT)/doc directory.  Run make with no
93
// target there if the PDF file isn't present.)  If not, see
94
// <http://www.gnu.org/licenses/> for a copy.
95
//
96
// License:     GPL, v3, as defined and found on www.gnu.org,
97
//              http://www.gnu.org/licenses/gpl.html
98
//
99
//
100
////////////////////////////////////////////////////////////////////////////////
101
module  gpsclock(i_clk, i_rst, i_pps, o_pps, o_led,
102
                i_wb_cyc_stb, i_wb_we, i_wb_addr, i_wb_data,
103
                        o_wb_ack, o_wb_stall, o_wb_data,
104
                o_tracking, o_count, o_step, o_err, o_locked, o_dbg);
105
        parameter       RW=64, // Needs to be 2ceil(Log_2(i_clk frequency))
106
                        DW=32, // The width of our data bus
107
                        ONE_SECOND = 0,
108
                        NPW=RW-DW, // Width of non-parameter data
109
                        HRW=RW/2; // Half of RW
110
        input           i_clk, i_rst;
111
        input           i_pps;  // From the GPS device
112
        output  reg     o_pps;  // To our local circuitry
113
        output  reg     o_led;  // A blinky light showing how well we're doing
114
        // Wishbone Configuration interface
115
        input                           i_wb_cyc_stb, i_wb_we;
116
        input           [1:0]            i_wb_addr;
117
        input           [(DW-1):0]       i_wb_data;
118
        output  reg                     o_wb_ack;
119
        output  wire                    o_wb_stall;
120
        output  reg     [(DW-1):0]       o_wb_data;
121
        // Status and timing outputs
122
        output  reg                     o_tracking; // 1=closed loop, 0=open
123
        output  reg     [(RW-1):0]       o_count, // Fraction of a second
124
                                        o_step, // 2^RW / clock speed (in Hz)
125
                                        o_err; // Fraction of a second err
126
        output  reg                     o_locked; // 1 if Locked, 0 o.w.
127
        output  wire    [1:0]            o_dbg;
128
 
129
 
130
        // Clock resynchronization variables
131
        reg     pps_d, ck_pps, lst_pps;
132
        wire    tick;           // And a variable indicating the top of GPS 1PPS
133
 
134
        //
135
        // Configuration variables.  These control the loop bandwidth, the speed
136
        // of convergence, the speed of adaptation to changes, and more.  If
137
        // you adjust these outside of what the specification recommends, 
138
        // be careful that the control loop still synchronizes!
139
        reg                     new_config;
140
        reg     [5:0]            r_alpha;
141
        reg     [(DW-1):0]       r_beta, r_gamma, r_def_step;
142
        reg     [(RW-1):0]       pre_step;
143
 
144
        //
145
        // This core really operates rather slowly, in FPGA time.  Of the
146
        // millions of ticks per second, we only do things on about less than
147
        // a handful.  These timing signals below help us to determine when
148
        // our data is valid during those handful.
149
        //
150
        // Timing
151
        reg     err_tick, mpy_aux, mpy_sync_two, delay_step_clk;
152
        wire    sub_tick, fltr_tick;
153
 
154
        //
155
        // When tracking, each second we'll produce a lowpass filtered_err
156
        // (via a recursive average), a count_correction and a step_correction.
157
        // The two _correction terms then get applied at the top of the second.
158
        // Here's the declaration of those parameters.  The
159
        // 'pre_count_correction' parameter allows us to avoid adding three
160
        // 64-bit numbers in a single clock, splitting part of that amount into
161
        // an earlier clock.
162
        //
163
        // Tracking
164
        reg     [(RW-1):0]       count_correction, pre_count_correction;
165
        reg     [(HRW-1):0]      step_correction;
166
        reg     [(HRW-1):0]      delayed_step_correction, delayed_step;
167
        reg     signed [(HRW-1):0]       mpy_input;
168
        wire            [(RW-1):0]       w_mpy_out;
169
        wire    signed [(RW-1):0]        filter_sub_count, filtered_err;
170
 
171
 
172
 
173
        //
174
        //
175
        //
176
        // Wishbone access ... adjust our tracking parameters
177
        //
178
        //
179
        //
180
        // DEFAULT_STEP = 64'h0000_002a_f31d_c461, // 2^64 / 100 MHz
181
        initial r_def_step = 32'h8_2af_31dc;
182
        always @(posedge i_clk)
183
                pre_step <= { 16'h00,
184
                        (({ r_def_step[27:0], 20'h00 })>>r_def_step[31:28])};
185
 
186 12 dgisselq
        // Delay writes by one clock
187
        wire    [1:0]    wb_addr;
188
        wire    [31:0]   wb_data;
189
        reg             wb_write;
190
        reg     [1:0]    r_wb_addr;
191
        reg     [31:0]   r_wb_data;
192
        always @(posedge i_clk)
193
                wb_write <= (i_wb_cyc_stb)&&(i_wb_we);
194
        always @(posedge i_clk)
195
                r_wb_data <= i_wb_data;
196
        always @(posedge i_clk)
197
                r_wb_addr <= i_wb_addr;
198
        assign  wb_data = r_wb_data;
199
        assign  wb_addr = r_wb_addr;
200
 
201 3 dgisselq
        initial new_config = 1'b0;
202
        always @(posedge i_clk)
203 12 dgisselq
                if (wb_write)
204 3 dgisselq
                begin
205
                        new_config = 1'b1;
206 12 dgisselq
                        case(wb_addr)
207
                        2'b00: r_alpha    <= wb_data[5:0];
208
                        2'b01: r_beta     <= wb_data;
209
                        2'b10: r_gamma    <= wb_data;
210
                        2'b11: r_def_step <= wb_data;
211 3 dgisselq
                        default: begin end
212
                        // r_defstep <= i_wb_data;
213
                        endcase
214
                end else
215
                        new_config = 1'b0;
216
        always @(posedge i_clk)
217
                case (i_wb_addr)
218
                        2'b00: o_wb_data <= { 26'h00, r_alpha };
219
                        2'b01: o_wb_data <= r_beta;
220
                        2'b10: o_wb_data <= r_gamma;
221
                        2'b11: o_wb_data <= r_def_step;
222
                        default: o_wb_data <= 0;
223
                endcase
224
 
225
        reg     dly_config;
226
        initial dly_config = 1'b0;
227
        always @(posedge i_clk)
228
                dly_config <= new_config;
229
        always @(posedge i_clk)
230
                o_wb_ack <= i_wb_cyc_stb;
231
        assign  o_wb_stall = 1'b0;
232
 
233
 
234
        //
235
        //
236
        // Deal with the realities of an unsynchronized 1PPS signal: 
237
        // register it with two flip flops to avoid metastability issues.
238
        // Create a 'tick' variable to note the top of a second.
239
        //
240
        //
241
        always @(posedge i_clk)
242
        begin
243
                pps_d <= i_pps;
244
                ck_pps <= pps_d;
245
                lst_pps <= ck_pps;
246
        end
247
 
248
        // Provide a touch of debounce protection ... equal to about
249
        // one quarter of a second.
250
        reg     [(RW-3):0]       tick_enable_counter;
251
        wire    [(RW-1):0]       w_tick_enable_sum;
252
        wire                    w_tick_enable, w_tick_enable_unused;
253
        bigadd  enabler(i_clk, 1'b0, o_step, { 2'b0, tick_enable_counter },
254
                w_tick_enable_sum, w_tick_enable_unused);
255
        initial tick_enable_counter = 0;
256
        always @(posedge i_clk)
257
        begin
258
                if (tick)
259
                        tick_enable_counter <= 0;
260
                else if (|w_tick_enable_sum[(RW-1):(RW-2)])
261
                        tick_enable_counter <= {(RW-2){1'b1}};
262
                else
263
                        tick_enable_counter <= w_tick_enable_sum[(RW-3):0];
264
        end
265
        assign  w_tick_enable = tick_enable_counter[(RW-3)];
266
 
267
        assign  tick= (ck_pps)&&(~lst_pps)&&(w_tick_enable);
268
        assign  o_dbg[0] = tick;
269
        assign  o_dbg[1] = w_tick_enable;
270
 
271
        //
272
        //
273
        // Here's our counter proper: Add o_step to o_count each clock tick
274
        // to have a current time value.  Corrections are applied at the top
275
        // of the second if we are in tracking mode.  The 'o_pps' signal is
276
        // generated from the carry/overflow of the o_count addition.
277
        //
278
        //
279
        reg     cnt_carry;
280
        reg     [31:0]   p_count;
281
        initial o_count = 0;
282
        initial o_pps = 1'b0;
283
        always @(posedge i_clk)
284
                if ((o_tracking)&&(tick))
285
                begin
286
                        { cnt_carry, p_count } <= p_count[31:0] + count_correction[31:0];
287
                        if (~count_correction[(RW-1)])
288
                        begin
289
                                // Note that we don't create an o_pps just
290
                                // because the gps_pps states that there should
291
                                // be one.  Instead, we hold to the normal
292
                                // means of business.  At the tick, however,
293
                                // we add both the step and the correction to
294
                                // the current count.
295
                                { o_pps, o_count[63:32] } <= o_count[63:32] +count_correction[63:32]+ { 31'h00, cnt_carry };
296
                        end else begin
297
                                // If the count correction is negative, it means
298
                                // we need to go backwards.  In this case,
299
                                // there shouldn't be any o_pps, least we get
300
                                // two of them.
301
                                o_pps <= 1'b0;
302
                                o_count[63:32] <= o_count[63:32] + count_correction[63:32];
303
                        end
304
                end else begin
305
                        // The difference between count_correction and
306
                        // o_step is the phase correction from the last tick.
307
                        // If we aren't tracking, we don't want to use the
308
                        // correction.  Likewise, even if we are, we only
309
                        // want to use it on the ticks.
310
                        { cnt_carry, p_count } <= p_count + o_step[31:0];
311
                        { o_pps, o_count[63:32] } <= o_count[63:32] + o_step[63:32];
312
                end
313
        always @(posedge i_clk)
314
                o_count[31:0] <= p_count;
315
 
316
        reg     [(HRW):0]        step_correction_plus_carry;
317
        always @(posedge i_clk)
318
                step_correction_plus_carry = step_correction + { 31'h00, delayed_carry };
319
 
320
        wire    w_step_correct_unused;
321
        wire    [(RW-1):0]       new_step;
322
        bigadd  getnewstep(i_clk, 1'b0, o_step,
323
                        { { (HRW-1){step_correction_plus_carry[HRW]} },
324
                                step_correction_plus_carry},
325
                        new_step, w_step_correct_unused);
326
 
327
        reg     delayed_carry;
328
        initial delayed_carry = 0;
329
        initial o_step = 64'h002af31dc461;
330
        always @(posedge i_clk)
331
                if ((i_rst)||(dly_config))
332
                        o_step <= pre_step;
333
                else if ((o_tracking) && (tick))
334
                        o_step <= new_step;
335
 
336
        initial delayed_step = 0;
337
        always @(posedge i_clk)
338
                if ((i_rst)||(dly_config))
339
                        delayed_step <= 0;
340
                else if (delay_step_clk)
341
                        { delayed_carry, delayed_step } <= delayed_step
342
                                        + delayed_step_correction;
343
 
344
 
345
 
346
        //
347
        //
348
        // Now to start our tracking loop.  The steps are:
349
        //      1. Measure our error
350
        //      2. Filter our error (lowpass, recursive averager)
351
        //      3. Multiply the filtered error by two user-supplied constants
352
        //              (beta and gamma)
353
        //      4. The results of this multiply then become the new
354
        //              count and step corrections.
355
        //
356
        //
357
        // A negative error means we were too fast ... the count rolled over
358
        // and is near zero, the o_err is then the negation of this when the
359
        // tick does show up.
360
        //
361
        initial o_err = 0;
362
        always @(posedge i_clk)
363
                if (tick)
364
                        o_err <= ONE_SECOND - o_count;
365
 
366
        initial err_tick = 1'b0;
367
        always @(posedge i_clk)
368
                err_tick <= tick;
369
 
370
        bigsub  suberri(i_clk, err_tick, o_err,
371
                        filtered_err, filter_sub_count, sub_tick);
372
 
373
        //
374
        // This shouldn't be required: We only want to shift our 
375
        // filter_sub_count by r_alpha bits, why the extra struggles?
376
        // Why is because Verilator decides that these values are unsigned,
377
        // and so despite being told that they are signed values, verilator
378
        // doesn't sign extend them upon shifting.  Put together,
379
        // { shift_hi[low-bits], shift_lo[low-bits] } make up a full RW
380
        // bit correction factor.
381
        reg     signed [(RW-1):0] shift_hi, shift_lo;
382
        always @(posedge i_clk)
383
        begin
384
                shift_hi <= { {(HRW){filter_sub_count[(RW-1)]}},
385
                                filter_sub_count[(RW-1):HRW] }>>r_alpha;
386
                shift_lo <= filter_sub_count[(RW-1):0]>>r_alpha;
387
        end
388
 
389
        bigadd adderr(i_clk, sub_tick, filtered_err,
390
                        { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] },
391
                        filtered_err, fltr_tick);
392
        /*
393
        always @(posedge i_clk)
394
                if ((o_tracking)&&(sub_tick))
395
                        filtered_err<= filtered_err
396
                                + { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] };
397
        */
398
 
399
        always @(posedge i_clk)
400
                if (fltr_tick)
401
                        mpy_input <= r_beta;
402
                else
403
                        mpy_input <= r_gamma;
404
        always @(posedge i_clk)
405
                mpy_aux <= fltr_tick;
406
 
407
        //
408
        // The multiply
409
        //
410
        wire                    mpy_sync;
411
        wire    [(RW-1):0]       mpy_out;
412
        initial mpy_sync_two = 1'b0;
413
        // Sign extend all inputs to RW bits
414
        wire    signed  [(RW-1):0]       w_mpy_input, w_mpy_err;
415
        assign  w_mpy_input = { {(RW-DW){mpy_input[(DW-1)]}}, mpy_input[(DW-1):0]};
416
        assign  w_mpy_err   = { {(RW-NPW){filtered_err[(RW-1)]}}, filtered_err[(RW-1):(RW-NPW)]};
417
        bigsmpy mpyi(i_clk, mpy_aux, 1'b1, w_mpy_input[31:0], w_mpy_err[31:0],
418
                        mpy_out, mpy_sync);
419
        always @(posedge i_clk)
420
                mpy_sync_two <= mpy_sync;
421
        assign  w_mpy_out = mpy_out;
422
 
423
        // The post-multiply
424
        initial pre_count_correction    = 0;
425
        initial step_correction         = 0;
426
        initial delayed_step_correction = 0;
427
        always @(posedge i_clk)
428
                if (mpy_sync)
429
                        pre_count_correction <= w_mpy_out;
430
                else if (mpy_sync_two) begin
431
                        step_correction <= w_mpy_out[(RW-1):HRW];
432
                        delayed_step_correction <= w_mpy_out[(HRW-1):0];
433
                end
434
        always @(posedge i_clk)
435
                count_correction <= pre_count_correction + o_step;
436
 
437
        initial delay_step_clk = 1'b0;
438
        always @(posedge i_clk)
439
                delay_step_clk <= mpy_sync_two;
440
 
441
        //
442
        //
443
        // LED Logic -- Note that this is where we tell if we've had a GPS
444
        // 1PPS pulse or not.  To have had such a pulse, it needs to have
445
        // been within the last two seconds.
446
        //
447
        //
448
        reg     no_pulse;
449
        reg     [32:0]   time_since_pps;
450
        initial no_pulse = 1'b1;
451
        initial time_since_pps = 33'hffffffff;
452
        always @(posedge i_clk)
453
                if (tick)
454
                begin
455
                        time_since_pps <= 0;
456
                        no_pulse <= 0;
457
                end else if (time_since_pps[32:29] == 4'hf)
458
                begin
459
                        time_since_pps <= 33'hffffffff;
460
                        no_pulse <= 1'b1;
461
                end else
462
                        time_since_pps <= time_since_pps + pre_step[(RW-1):HRW];
463
 
464
        //
465
        // 1. Pulse with a 50% duty cycle every second if no GPS is available.
466
        // 2. Pulse with a 6% duty cycle any time a pulse is present, and any
467
        //      time we think (when a pulse is present) that we have time.
468
        //
469
        // This should produce a set of conflicting pulses when out of lock,
470
        // and a nice short once per second pulse when locked.  Further, you
471
        // should be able to tell when the core is flywheeling by the duration
472
        // of the pulses (50% vs 6%).
473
        //
474
        always @(posedge i_clk)
475
                if (no_pulse)
476
                        o_led <= o_count[(RW-1)];
477
                else
478
                        o_led <= ((time_since_pps[31:28] == 4'h0)
479
                                ||(o_count[(RW-1):(RW-4)]== 4'h0));
480
 
481
        //
482
        //
483
        // Now, are we tracking or not?
484
        // We'll attempt to close the loop after seeing 7 valid GPS 1PPS
485
        // rising edges.
486
        //
487
        //
488
        reg     [2:0]    count_valid_ticks;
489
        initial count_valid_ticks = 3'h0;
490
        always @(posedge i_clk)
491
                if ((tick)&&(count_valid_ticks < 3'h7))
492
                        count_valid_ticks <= count_valid_ticks+1;
493
                else if (no_pulse)
494
                        count_valid_ticks <= 3'h0;
495
        initial o_tracking = 1'b0;
496
        always @(posedge i_clk)
497
                if ((tick)&&(&count_valid_ticks))
498
                        o_tracking <= 1'b1;
499
                else if ((tick)||(count_valid_ticks == 0))
500
                        o_tracking <= 1'b0;
501
 
502
        //
503
        //
504
        // Are we locked or not?
505
        // We'll use the top eight bits of our error to tell.  If the top eight
506
        // bits are all ones or all zeros, then we'll call ourselves locked.
507
        // This is equivalent to calling ourselves locked if, at the top of
508
        // the second, we are within 1/128th of a second of the GPS 1PPS.
509
        //
510
        initial o_locked = 1'b0;
511
        always @(posedge i_clk)
512
                if ((o_tracking)&&(tick)&&(
513
                        ((   o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'hff))
514
                        ||((~o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'h00))))
515
                        o_locked <= 1'b1;
516
                else if (tick)
517
                        o_locked <= 1'b0;
518
 
519
endmodule
520
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.