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

Subversion Repositories openarty

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

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
        initial new_config = 1'b0;
187
        always @(posedge i_clk)
188
                if ((i_wb_cyc_stb)&&(i_wb_we))
189
                begin
190
                        new_config = 1'b1;
191
                        case(i_wb_addr)
192
                        2'b00: r_alpha    <= i_wb_data[5:0];
193
                        2'b01: r_beta     <= i_wb_data;
194
                        2'b10: r_gamma    <= i_wb_data;
195
                        2'b11: r_def_step <= i_wb_data;
196
                        default: begin end
197
                        // r_defstep <= i_wb_data;
198
                        endcase
199
                end else
200
                        new_config = 1'b0;
201
        always @(posedge i_clk)
202
                case (i_wb_addr)
203
                        2'b00: o_wb_data <= { 26'h00, r_alpha };
204
                        2'b01: o_wb_data <= r_beta;
205
                        2'b10: o_wb_data <= r_gamma;
206
                        2'b11: o_wb_data <= r_def_step;
207
                        default: o_wb_data <= 0;
208
                endcase
209
 
210
        reg     dly_config;
211
        initial dly_config = 1'b0;
212
        always @(posedge i_clk)
213
                dly_config <= new_config;
214
        always @(posedge i_clk)
215
                o_wb_ack <= i_wb_cyc_stb;
216
        assign  o_wb_stall = 1'b0;
217
 
218
 
219
        //
220
        //
221
        // Deal with the realities of an unsynchronized 1PPS signal: 
222
        // register it with two flip flops to avoid metastability issues.
223
        // Create a 'tick' variable to note the top of a second.
224
        //
225
        //
226
        always @(posedge i_clk)
227
        begin
228
                pps_d <= i_pps;
229
                ck_pps <= pps_d;
230
                lst_pps <= ck_pps;
231
        end
232
 
233
        // Provide a touch of debounce protection ... equal to about
234
        // one quarter of a second.
235
        reg     [(RW-3):0]       tick_enable_counter;
236
        wire    [(RW-1):0]       w_tick_enable_sum;
237
        wire                    w_tick_enable, w_tick_enable_unused;
238
        bigadd  enabler(i_clk, 1'b0, o_step, { 2'b0, tick_enable_counter },
239
                w_tick_enable_sum, w_tick_enable_unused);
240
        initial tick_enable_counter = 0;
241
        always @(posedge i_clk)
242
        begin
243
                if (tick)
244
                        tick_enable_counter <= 0;
245
                else if (|w_tick_enable_sum[(RW-1):(RW-2)])
246
                        tick_enable_counter <= {(RW-2){1'b1}};
247
                else
248
                        tick_enable_counter <= w_tick_enable_sum[(RW-3):0];
249
        end
250
        assign  w_tick_enable = tick_enable_counter[(RW-3)];
251
 
252
        assign  tick= (ck_pps)&&(~lst_pps)&&(w_tick_enable);
253
        assign  o_dbg[0] = tick;
254
        assign  o_dbg[1] = w_tick_enable;
255
 
256
        //
257
        //
258
        // Here's our counter proper: Add o_step to o_count each clock tick
259
        // to have a current time value.  Corrections are applied at the top
260
        // of the second if we are in tracking mode.  The 'o_pps' signal is
261
        // generated from the carry/overflow of the o_count addition.
262
        //
263
        //
264
        reg     cnt_carry;
265
        reg     [31:0]   p_count;
266
        initial o_count = 0;
267
        initial o_pps = 1'b0;
268
        always @(posedge i_clk)
269
                if ((o_tracking)&&(tick))
270
                begin
271
                        { cnt_carry, p_count } <= p_count[31:0] + count_correction[31:0];
272
                        if (~count_correction[(RW-1)])
273
                        begin
274
                                // Note that we don't create an o_pps just
275
                                // because the gps_pps states that there should
276
                                // be one.  Instead, we hold to the normal
277
                                // means of business.  At the tick, however,
278
                                // we add both the step and the correction to
279
                                // the current count.
280
                                { o_pps, o_count[63:32] } <= o_count[63:32] +count_correction[63:32]+ { 31'h00, cnt_carry };
281
                        end else begin
282
                                // If the count correction is negative, it means
283
                                // we need to go backwards.  In this case,
284
                                // there shouldn't be any o_pps, least we get
285
                                // two of them.
286
                                o_pps <= 1'b0;
287
                                o_count[63:32] <= o_count[63:32] + count_correction[63:32];
288
                        end
289
                end else begin
290
                        // The difference between count_correction and
291
                        // o_step is the phase correction from the last tick.
292
                        // If we aren't tracking, we don't want to use the
293
                        // correction.  Likewise, even if we are, we only
294
                        // want to use it on the ticks.
295
                        { cnt_carry, p_count } <= p_count + o_step[31:0];
296
                        { o_pps, o_count[63:32] } <= o_count[63:32] + o_step[63:32];
297
                end
298
        always @(posedge i_clk)
299
                o_count[31:0] <= p_count;
300
 
301
        reg     [(HRW):0]        step_correction_plus_carry;
302
        always @(posedge i_clk)
303
                step_correction_plus_carry = step_correction + { 31'h00, delayed_carry };
304
 
305
        wire    w_step_correct_unused;
306
        wire    [(RW-1):0]       new_step;
307
        bigadd  getnewstep(i_clk, 1'b0, o_step,
308
                        { { (HRW-1){step_correction_plus_carry[HRW]} },
309
                                step_correction_plus_carry},
310
                        new_step, w_step_correct_unused);
311
 
312
        reg     delayed_carry;
313
        initial delayed_carry = 0;
314
        initial o_step = 64'h002af31dc461;
315
        always @(posedge i_clk)
316
                if ((i_rst)||(dly_config))
317
                        o_step <= pre_step;
318
                else if ((o_tracking) && (tick))
319
                        o_step <= new_step;
320
 
321
        initial delayed_step = 0;
322
        always @(posedge i_clk)
323
                if ((i_rst)||(dly_config))
324
                        delayed_step <= 0;
325
                else if (delay_step_clk)
326
                        { delayed_carry, delayed_step } <= delayed_step
327
                                        + delayed_step_correction;
328
 
329
 
330
 
331
        //
332
        //
333
        // Now to start our tracking loop.  The steps are:
334
        //      1. Measure our error
335
        //      2. Filter our error (lowpass, recursive averager)
336
        //      3. Multiply the filtered error by two user-supplied constants
337
        //              (beta and gamma)
338
        //      4. The results of this multiply then become the new
339
        //              count and step corrections.
340
        //
341
        //
342
        // A negative error means we were too fast ... the count rolled over
343
        // and is near zero, the o_err is then the negation of this when the
344
        // tick does show up.
345
        //
346
        initial o_err = 0;
347
        always @(posedge i_clk)
348
                if (tick)
349
                        o_err <= ONE_SECOND - o_count;
350
 
351
        initial err_tick = 1'b0;
352
        always @(posedge i_clk)
353
                err_tick <= tick;
354
 
355
        bigsub  suberri(i_clk, err_tick, o_err,
356
                        filtered_err, filter_sub_count, sub_tick);
357
 
358
        //
359
        // This shouldn't be required: We only want to shift our 
360
        // filter_sub_count by r_alpha bits, why the extra struggles?
361
        // Why is because Verilator decides that these values are unsigned,
362
        // and so despite being told that they are signed values, verilator
363
        // doesn't sign extend them upon shifting.  Put together,
364
        // { shift_hi[low-bits], shift_lo[low-bits] } make up a full RW
365
        // bit correction factor.
366
        reg     signed [(RW-1):0] shift_hi, shift_lo;
367
        always @(posedge i_clk)
368
        begin
369
                shift_hi <= { {(HRW){filter_sub_count[(RW-1)]}},
370
                                filter_sub_count[(RW-1):HRW] }>>r_alpha;
371
                shift_lo <= filter_sub_count[(RW-1):0]>>r_alpha;
372
        end
373
 
374
        bigadd adderr(i_clk, sub_tick, filtered_err,
375
                        { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] },
376
                        filtered_err, fltr_tick);
377
        /*
378
        always @(posedge i_clk)
379
                if ((o_tracking)&&(sub_tick))
380
                        filtered_err<= filtered_err
381
                                + { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] };
382
        */
383
 
384
        always @(posedge i_clk)
385
                if (fltr_tick)
386
                        mpy_input <= r_beta;
387
                else
388
                        mpy_input <= r_gamma;
389
        always @(posedge i_clk)
390
                mpy_aux <= fltr_tick;
391
 
392
        //
393
        // The multiply
394
        //
395
        wire                    mpy_sync;
396
        wire    [(RW-1):0]       mpy_out;
397
        initial mpy_sync_two = 1'b0;
398
        // Sign extend all inputs to RW bits
399
        wire    signed  [(RW-1):0]       w_mpy_input, w_mpy_err;
400
        assign  w_mpy_input = { {(RW-DW){mpy_input[(DW-1)]}}, mpy_input[(DW-1):0]};
401
        assign  w_mpy_err   = { {(RW-NPW){filtered_err[(RW-1)]}}, filtered_err[(RW-1):(RW-NPW)]};
402
        bigsmpy mpyi(i_clk, mpy_aux, 1'b1, w_mpy_input[31:0], w_mpy_err[31:0],
403
                        mpy_out, mpy_sync);
404
        always @(posedge i_clk)
405
                mpy_sync_two <= mpy_sync;
406
        assign  w_mpy_out = mpy_out;
407
 
408
        // The post-multiply
409
        initial pre_count_correction    = 0;
410
        initial step_correction         = 0;
411
        initial delayed_step_correction = 0;
412
        always @(posedge i_clk)
413
                if (mpy_sync)
414
                        pre_count_correction <= w_mpy_out;
415
                else if (mpy_sync_two) begin
416
                        step_correction <= w_mpy_out[(RW-1):HRW];
417
                        delayed_step_correction <= w_mpy_out[(HRW-1):0];
418
                end
419
        always @(posedge i_clk)
420
                count_correction <= pre_count_correction + o_step;
421
 
422
        initial delay_step_clk = 1'b0;
423
        always @(posedge i_clk)
424
                delay_step_clk <= mpy_sync_two;
425
 
426
        //
427
        //
428
        // LED Logic -- Note that this is where we tell if we've had a GPS
429
        // 1PPS pulse or not.  To have had such a pulse, it needs to have
430
        // been within the last two seconds.
431
        //
432
        //
433
        reg     no_pulse;
434
        reg     [32:0]   time_since_pps;
435
        initial no_pulse = 1'b1;
436
        initial time_since_pps = 33'hffffffff;
437
        always @(posedge i_clk)
438
                if (tick)
439
                begin
440
                        time_since_pps <= 0;
441
                        no_pulse <= 0;
442
                end else if (time_since_pps[32:29] == 4'hf)
443
                begin
444
                        time_since_pps <= 33'hffffffff;
445
                        no_pulse <= 1'b1;
446
                end else
447
                        time_since_pps <= time_since_pps + pre_step[(RW-1):HRW];
448
 
449
        //
450
        // 1. Pulse with a 50% duty cycle every second if no GPS is available.
451
        // 2. Pulse with a 6% duty cycle any time a pulse is present, and any
452
        //      time we think (when a pulse is present) that we have time.
453
        //
454
        // This should produce a set of conflicting pulses when out of lock,
455
        // and a nice short once per second pulse when locked.  Further, you
456
        // should be able to tell when the core is flywheeling by the duration
457
        // of the pulses (50% vs 6%).
458
        //
459
        always @(posedge i_clk)
460
                if (no_pulse)
461
                        o_led <= o_count[(RW-1)];
462
                else
463
                        o_led <= ((time_since_pps[31:28] == 4'h0)
464
                                ||(o_count[(RW-1):(RW-4)]== 4'h0));
465
 
466
        //
467
        //
468
        // Now, are we tracking or not?
469
        // We'll attempt to close the loop after seeing 7 valid GPS 1PPS
470
        // rising edges.
471
        //
472
        //
473
        reg     [2:0]    count_valid_ticks;
474
        initial count_valid_ticks = 3'h0;
475
        always @(posedge i_clk)
476
                if ((tick)&&(count_valid_ticks < 3'h7))
477
                        count_valid_ticks <= count_valid_ticks+1;
478
                else if (no_pulse)
479
                        count_valid_ticks <= 3'h0;
480
        initial o_tracking = 1'b0;
481
        always @(posedge i_clk)
482
                if ((tick)&&(&count_valid_ticks))
483
                        o_tracking <= 1'b1;
484
                else if ((tick)||(count_valid_ticks == 0))
485
                        o_tracking <= 1'b0;
486
 
487
        //
488
        //
489
        // Are we locked or not?
490
        // We'll use the top eight bits of our error to tell.  If the top eight
491
        // bits are all ones or all zeros, then we'll call ourselves locked.
492
        // This is equivalent to calling ourselves locked if, at the top of
493
        // the second, we are within 1/128th of a second of the GPS 1PPS.
494
        //
495
        initial o_locked = 1'b0;
496
        always @(posedge i_clk)
497
                if ((o_tracking)&&(tick)&&(
498
                        ((   o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'hff))
499
                        ||((~o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'h00))))
500
                        o_locked <= 1'b1;
501
                else if (tick)
502
                        o_locked <= 1'b0;
503
 
504
endmodule
505
 

powered by: WebSVN 2.1.0

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