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

Subversion Repositories openarty

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

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 25 dgisselq
        parameter       DEFAULT_STEP = 32'h834d_c736;//2^64/81.25 MHz
106 3 dgisselq
        parameter       RW=64, // Needs to be 2ceil(Log_2(i_clk frequency))
107
                        DW=32, // The width of our data bus
108
                        ONE_SECOND = 0,
109
                        NPW=RW-DW, // Width of non-parameter data
110
                        HRW=RW/2; // Half of RW
111
        input           i_clk, i_rst;
112
        input           i_pps;  // From the GPS device
113
        output  reg     o_pps;  // To our local circuitry
114
        output  reg     o_led;  // A blinky light showing how well we're doing
115
        // Wishbone Configuration interface
116
        input                           i_wb_cyc_stb, i_wb_we;
117
        input           [1:0]            i_wb_addr;
118
        input           [(DW-1):0]       i_wb_data;
119
        output  reg                     o_wb_ack;
120
        output  wire                    o_wb_stall;
121
        output  reg     [(DW-1):0]       o_wb_data;
122
        // Status and timing outputs
123
        output  reg                     o_tracking; // 1=closed loop, 0=open
124
        output  reg     [(RW-1):0]       o_count, // Fraction of a second
125
                                        o_step, // 2^RW / clock speed (in Hz)
126
                                        o_err; // Fraction of a second err
127
        output  reg                     o_locked; // 1 if Locked, 0 o.w.
128
        output  wire    [1:0]            o_dbg;
129
 
130
 
131
        // Clock resynchronization variables
132
        reg     pps_d, ck_pps, lst_pps;
133
        wire    tick;           // And a variable indicating the top of GPS 1PPS
134
 
135
        //
136
        // Configuration variables.  These control the loop bandwidth, the speed
137
        // of convergence, the speed of adaptation to changes, and more.  If
138
        // you adjust these outside of what the specification recommends, 
139
        // be careful that the control loop still synchronizes!
140
        reg                     new_config;
141
        reg     [5:0]            r_alpha;
142
        reg     [(DW-1):0]       r_beta, r_gamma, r_def_step;
143
        reg     [(RW-1):0]       pre_step;
144
 
145
        //
146
        // This core really operates rather slowly, in FPGA time.  Of the
147
        // millions of ticks per second, we only do things on about less than
148
        // a handful.  These timing signals below help us to determine when
149
        // our data is valid during those handful.
150
        //
151
        // Timing
152
        reg     err_tick, mpy_aux, mpy_sync_two, delay_step_clk;
153
        wire    sub_tick, fltr_tick;
154
 
155
        //
156
        // When tracking, each second we'll produce a lowpass filtered_err
157
        // (via a recursive average), a count_correction and a step_correction.
158
        // The two _correction terms then get applied at the top of the second.
159
        // Here's the declaration of those parameters.  The
160
        // 'pre_count_correction' parameter allows us to avoid adding three
161
        // 64-bit numbers in a single clock, splitting part of that amount into
162
        // an earlier clock.
163
        //
164
        // Tracking
165
        reg     [(RW-1):0]       count_correction, pre_count_correction;
166
        reg     [(HRW-1):0]      step_correction;
167
        reg     [(HRW-1):0]      delayed_step_correction, delayed_step;
168
        reg     signed [(HRW-1):0]       mpy_input;
169
        wire            [(RW-1):0]       w_mpy_out;
170
        wire    signed [(RW-1):0]        filter_sub_count, filtered_err;
171
 
172
 
173
 
174
        //
175
        //
176
        //
177
        // Wishbone access ... adjust our tracking parameters
178
        //
179
        //
180
        //
181 25 dgisselq
        // DEFAULT_STEP = 64'h0000_0034_dc73_67da, // 2^64 / 100 MHz
182
        // 28'h34d_c736 << 8, and hence we have 32'h834d_c736
183
        initial r_def_step = DEFAULT_STEP;
184 3 dgisselq
        always @(posedge i_clk)
185
                pre_step <= { 16'h00,
186
                        (({ r_def_step[27:0], 20'h00 })>>r_def_step[31:28])};
187
 
188 12 dgisselq
        // Delay writes by one clock
189
        wire    [1:0]    wb_addr;
190
        wire    [31:0]   wb_data;
191
        reg             wb_write;
192
        reg     [1:0]    r_wb_addr;
193
        reg     [31:0]   r_wb_data;
194
        always @(posedge i_clk)
195
                wb_write <= (i_wb_cyc_stb)&&(i_wb_we);
196
        always @(posedge i_clk)
197
                r_wb_data <= i_wb_data;
198
        always @(posedge i_clk)
199
                r_wb_addr <= i_wb_addr;
200
        assign  wb_data = r_wb_data;
201
        assign  wb_addr = r_wb_addr;
202
 
203 3 dgisselq
        initial new_config = 1'b0;
204
        always @(posedge i_clk)
205 12 dgisselq
                if (wb_write)
206 3 dgisselq
                begin
207
                        new_config = 1'b1;
208 12 dgisselq
                        case(wb_addr)
209
                        2'b00: r_alpha    <= wb_data[5:0];
210
                        2'b01: r_beta     <= wb_data;
211
                        2'b10: r_gamma    <= wb_data;
212
                        2'b11: r_def_step <= wb_data;
213 25 dgisselq
                        // default: begin end
214 3 dgisselq
                        // r_defstep <= i_wb_data;
215
                        endcase
216
                end else
217
                        new_config = 1'b0;
218
        always @(posedge i_clk)
219
                case (i_wb_addr)
220
                        2'b00: o_wb_data <= { 26'h00, r_alpha };
221
                        2'b01: o_wb_data <= r_beta;
222
                        2'b10: o_wb_data <= r_gamma;
223
                        2'b11: o_wb_data <= r_def_step;
224 25 dgisselq
                        // default: o_wb_data <= 0;
225 3 dgisselq
                endcase
226
 
227
        reg     dly_config;
228
        initial dly_config = 1'b0;
229
        always @(posedge i_clk)
230
                dly_config <= new_config;
231
        always @(posedge i_clk)
232
                o_wb_ack <= i_wb_cyc_stb;
233
        assign  o_wb_stall = 1'b0;
234
 
235
 
236
        //
237
        //
238
        // Deal with the realities of an unsynchronized 1PPS signal: 
239
        // register it with two flip flops to avoid metastability issues.
240
        // Create a 'tick' variable to note the top of a second.
241
        //
242
        //
243
        always @(posedge i_clk)
244
        begin
245
                pps_d <= i_pps;
246
                ck_pps <= pps_d;
247
                lst_pps <= ck_pps;
248
        end
249
 
250
        // Provide a touch of debounce protection ... equal to about
251
        // one quarter of a second.
252
        reg     [(RW-3):0]       tick_enable_counter;
253
        wire    [(RW-1):0]       w_tick_enable_sum;
254
        wire                    w_tick_enable, w_tick_enable_unused;
255
        bigadd  enabler(i_clk, 1'b0, o_step, { 2'b0, tick_enable_counter },
256
                w_tick_enable_sum, w_tick_enable_unused);
257
        initial tick_enable_counter = 0;
258
        always @(posedge i_clk)
259
        begin
260
                if (tick)
261
                        tick_enable_counter <= 0;
262
                else if (|w_tick_enable_sum[(RW-1):(RW-2)])
263
                        tick_enable_counter <= {(RW-2){1'b1}};
264
                else
265
                        tick_enable_counter <= w_tick_enable_sum[(RW-3):0];
266
        end
267
        assign  w_tick_enable = tick_enable_counter[(RW-3)];
268
 
269
        assign  tick= (ck_pps)&&(~lst_pps)&&(w_tick_enable);
270
        assign  o_dbg[0] = tick;
271
        assign  o_dbg[1] = w_tick_enable;
272
 
273
        //
274
        //
275
        // Here's our counter proper: Add o_step to o_count each clock tick
276
        // to have a current time value.  Corrections are applied at the top
277
        // of the second if we are in tracking mode.  The 'o_pps' signal is
278
        // generated from the carry/overflow of the o_count addition.
279
        //
280
        //
281
        reg     cnt_carry;
282
        reg     [31:0]   p_count;
283
        initial o_count = 0;
284
        initial o_pps = 1'b0;
285
        always @(posedge i_clk)
286
                if ((o_tracking)&&(tick))
287
                begin
288
                        { cnt_carry, p_count } <= p_count[31:0] + count_correction[31:0];
289
                        if (~count_correction[(RW-1)])
290
                        begin
291
                                // Note that we don't create an o_pps just
292
                                // because the gps_pps states that there should
293
                                // be one.  Instead, we hold to the normal
294
                                // means of business.  At the tick, however,
295
                                // we add both the step and the correction to
296
                                // the current count.
297
                                { o_pps, o_count[63:32] } <= o_count[63:32] +count_correction[63:32]+ { 31'h00, cnt_carry };
298
                        end else begin
299
                                // If the count correction is negative, it means
300
                                // we need to go backwards.  In this case,
301
                                // there shouldn't be any o_pps, least we get
302
                                // two of them.
303
                                o_pps <= 1'b0;
304
                                o_count[63:32] <= o_count[63:32] + count_correction[63:32];
305
                        end
306
                end else begin
307
                        // The difference between count_correction and
308
                        // o_step is the phase correction from the last tick.
309
                        // If we aren't tracking, we don't want to use the
310
                        // correction.  Likewise, even if we are, we only
311
                        // want to use it on the ticks.
312
                        { cnt_carry, p_count } <= p_count + o_step[31:0];
313
                        { o_pps, o_count[63:32] } <= o_count[63:32] + o_step[63:32];
314
                end
315
        always @(posedge i_clk)
316
                o_count[31:0] <= p_count;
317
 
318
        reg     [(HRW):0]        step_correction_plus_carry;
319
        always @(posedge i_clk)
320
                step_correction_plus_carry = step_correction + { 31'h00, delayed_carry };
321
 
322
        wire    w_step_correct_unused;
323
        wire    [(RW-1):0]       new_step;
324
        bigadd  getnewstep(i_clk, 1'b0, o_step,
325
                        { { (HRW-1){step_correction_plus_carry[HRW]} },
326
                                step_correction_plus_carry},
327
                        new_step, w_step_correct_unused);
328
 
329
        reg     delayed_carry;
330
        initial delayed_carry = 0;
331
        initial o_step = 64'h002af31dc461;
332
        always @(posedge i_clk)
333
                if ((i_rst)||(dly_config))
334
                        o_step <= pre_step;
335
                else if ((o_tracking) && (tick))
336
                        o_step <= new_step;
337
 
338
        initial delayed_step = 0;
339
        always @(posedge i_clk)
340
                if ((i_rst)||(dly_config))
341
                        delayed_step <= 0;
342
                else if (delay_step_clk)
343
                        { delayed_carry, delayed_step } <= delayed_step
344
                                        + delayed_step_correction;
345
 
346
 
347
 
348
        //
349
        //
350
        // Now to start our tracking loop.  The steps are:
351
        //      1. Measure our error
352
        //      2. Filter our error (lowpass, recursive averager)
353
        //      3. Multiply the filtered error by two user-supplied constants
354
        //              (beta and gamma)
355
        //      4. The results of this multiply then become the new
356
        //              count and step corrections.
357
        //
358
        //
359
        // A negative error means we were too fast ... the count rolled over
360
        // and is near zero, the o_err is then the negation of this when the
361
        // tick does show up.
362
        //
363
        initial o_err = 0;
364
        always @(posedge i_clk)
365
                if (tick)
366
                        o_err <= ONE_SECOND - o_count;
367
 
368
        initial err_tick = 1'b0;
369
        always @(posedge i_clk)
370
                err_tick <= tick;
371
 
372
        bigsub  suberri(i_clk, err_tick, o_err,
373
                        filtered_err, filter_sub_count, sub_tick);
374
 
375
        //
376
        // This shouldn't be required: We only want to shift our 
377
        // filter_sub_count by r_alpha bits, why the extra struggles?
378
        // Why is because Verilator decides that these values are unsigned,
379
        // and so despite being told that they are signed values, verilator
380
        // doesn't sign extend them upon shifting.  Put together,
381
        // { shift_hi[low-bits], shift_lo[low-bits] } make up a full RW
382
        // bit correction factor.
383
        reg     signed [(RW-1):0] shift_hi, shift_lo;
384
        always @(posedge i_clk)
385
        begin
386
                shift_hi <= { {(HRW){filter_sub_count[(RW-1)]}},
387
                                filter_sub_count[(RW-1):HRW] }>>r_alpha;
388
                shift_lo <= filter_sub_count[(RW-1):0]>>r_alpha;
389
        end
390
 
391
        bigadd adderr(i_clk, sub_tick, filtered_err,
392
                        { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] },
393
                        filtered_err, fltr_tick);
394
        /*
395
        always @(posedge i_clk)
396
                if ((o_tracking)&&(sub_tick))
397
                        filtered_err<= filtered_err
398
                                + { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] };
399
        */
400
 
401
        always @(posedge i_clk)
402
                if (fltr_tick)
403
                        mpy_input <= r_beta;
404
                else
405
                        mpy_input <= r_gamma;
406
        always @(posedge i_clk)
407
                mpy_aux <= fltr_tick;
408
 
409
        //
410
        // The multiply
411
        //
412
        wire                    mpy_sync;
413
        wire    [(RW-1):0]       mpy_out;
414
        initial mpy_sync_two = 1'b0;
415
        // Sign extend all inputs to RW bits
416
        wire    signed  [(RW-1):0]       w_mpy_input, w_mpy_err;
417
        assign  w_mpy_input = { {(RW-DW){mpy_input[(DW-1)]}}, mpy_input[(DW-1):0]};
418
        assign  w_mpy_err   = { {(RW-NPW){filtered_err[(RW-1)]}}, filtered_err[(RW-1):(RW-NPW)]};
419
        bigsmpy mpyi(i_clk, mpy_aux, 1'b1, w_mpy_input[31:0], w_mpy_err[31:0],
420
                        mpy_out, mpy_sync);
421
        always @(posedge i_clk)
422
                mpy_sync_two <= mpy_sync;
423
        assign  w_mpy_out = mpy_out;
424
 
425
        // The post-multiply
426
        initial pre_count_correction    = 0;
427
        initial step_correction         = 0;
428
        initial delayed_step_correction = 0;
429
        always @(posedge i_clk)
430
                if (mpy_sync)
431
                        pre_count_correction <= w_mpy_out;
432
                else if (mpy_sync_two) begin
433
                        step_correction <= w_mpy_out[(RW-1):HRW];
434
                        delayed_step_correction <= w_mpy_out[(HRW-1):0];
435
                end
436
        always @(posedge i_clk)
437
                count_correction <= pre_count_correction + o_step;
438
 
439
        initial delay_step_clk = 1'b0;
440
        always @(posedge i_clk)
441
                delay_step_clk <= mpy_sync_two;
442
 
443
        //
444
        //
445
        // LED Logic -- Note that this is where we tell if we've had a GPS
446
        // 1PPS pulse or not.  To have had such a pulse, it needs to have
447
        // been within the last two seconds.
448
        //
449
        //
450
        reg     no_pulse;
451
        reg     [32:0]   time_since_pps;
452
        initial no_pulse = 1'b1;
453
        initial time_since_pps = 33'hffffffff;
454
        always @(posedge i_clk)
455
                if (tick)
456
                begin
457
                        time_since_pps <= 0;
458
                        no_pulse <= 0;
459
                end else if (time_since_pps[32:29] == 4'hf)
460
                begin
461
                        time_since_pps <= 33'hffffffff;
462
                        no_pulse <= 1'b1;
463
                end else
464
                        time_since_pps <= time_since_pps + pre_step[(RW-1):HRW];
465
 
466
        //
467
        // 1. Pulse with a 50% duty cycle every second if no GPS is available.
468
        // 2. Pulse with a 6% duty cycle any time a pulse is present, and any
469
        //      time we think (when a pulse is present) that we have time.
470
        //
471
        // This should produce a set of conflicting pulses when out of lock,
472
        // and a nice short once per second pulse when locked.  Further, you
473
        // should be able to tell when the core is flywheeling by the duration
474
        // of the pulses (50% vs 6%).
475
        //
476
        always @(posedge i_clk)
477
                if (no_pulse)
478
                        o_led <= o_count[(RW-1)];
479
                else
480
                        o_led <= ((time_since_pps[31:28] == 4'h0)
481
                                ||(o_count[(RW-1):(RW-4)]== 4'h0));
482
 
483
        //
484
        //
485
        // Now, are we tracking or not?
486
        // We'll attempt to close the loop after seeing 7 valid GPS 1PPS
487
        // rising edges.
488
        //
489
        //
490
        reg     [2:0]    count_valid_ticks;
491
        initial count_valid_ticks = 3'h0;
492
        always @(posedge i_clk)
493
                if ((tick)&&(count_valid_ticks < 3'h7))
494
                        count_valid_ticks <= count_valid_ticks+1;
495
                else if (no_pulse)
496
                        count_valid_ticks <= 3'h0;
497
        initial o_tracking = 1'b0;
498
        always @(posedge i_clk)
499
                if ((tick)&&(&count_valid_ticks))
500
                        o_tracking <= 1'b1;
501
                else if ((tick)||(count_valid_ticks == 0))
502
                        o_tracking <= 1'b0;
503
 
504
        //
505
        //
506
        // Are we locked or not?
507
        // We'll use the top eight bits of our error to tell.  If the top eight
508
        // bits are all ones or all zeros, then we'll call ourselves locked.
509
        // This is equivalent to calling ourselves locked if, at the top of
510
        // the second, we are within 1/128th of a second of the GPS 1PPS.
511
        //
512
        initial o_locked = 1'b0;
513
        always @(posedge i_clk)
514
                if ((o_tracking)&&(tick)&&(
515
                        ((   o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'hff))
516
                        ||((~o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'h00))))
517
                        o_locked <= 1'b1;
518
                else if (tick)
519
                        o_locked <= 1'b0;
520
 
521
endmodule
522
 

powered by: WebSVN 2.1.0

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