diff options
Diffstat (limited to 'fpga/usrp3/lib/timing')
-rw-r--r-- | fpga/usrp3/lib/timing/Makefile.srcs | 17 | ||||
-rw-r--r-- | fpga/usrp3/lib/timing/pps_generator.v | 29 | ||||
-rw-r--r-- | fpga/usrp3/lib/timing/pps_synchronizer.v | 42 | ||||
-rw-r--r-- | fpga/usrp3/lib/timing/pulse_generator.v | 29 | ||||
-rw-r--r-- | fpga/usrp3/lib/timing/time_compare.v | 54 | ||||
-rw-r--r-- | fpga/usrp3/lib/timing/timekeeper.v | 87 |
6 files changed, 258 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/timing/Makefile.srcs b/fpga/usrp3/lib/timing/Makefile.srcs new file mode 100644 index 000000000..8d116c064 --- /dev/null +++ b/fpga/usrp3/lib/timing/Makefile.srcs @@ -0,0 +1,17 @@ +# +# Copyright 2013 Ettus Research LLC +# Copyright 2016 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +################################################## +# Timing Sources +################################################## +TIMING_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/timing/, \ +time_compare.v \ +timekeeper.v \ +pps_generator.v \ +pps_synchronizer.v \ +pulse_generator.v \ +)) diff --git a/fpga/usrp3/lib/timing/pps_generator.v b/fpga/usrp3/lib/timing/pps_generator.v new file mode 100644 index 000000000..9142c3a48 --- /dev/null +++ b/fpga/usrp3/lib/timing/pps_generator.v @@ -0,0 +1,29 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +module pps_generator #( + parameter CLK_FREQ = 32'd10_000_000, //Min:10kHz, Max:4GHz + parameter DUTY_CYCLE = 25 +) ( + input clk, + input reset, + output pps +); + reg [31:0] count; + + always @(posedge clk) begin + if (reset) begin + count <= 32'd0; + end else if (count >= CLK_FREQ - 1) begin + count <= 32'd0; + end else begin + count <= count + 32'd1; + end + end + + assign pps = (count < ((CLK_FREQ / 100) * DUTY_CYCLE)); +endmodule //pps_generator diff --git a/fpga/usrp3/lib/timing/pps_synchronizer.v b/fpga/usrp3/lib/timing/pps_synchronizer.v new file mode 100644 index 000000000..c984e9c86 --- /dev/null +++ b/fpga/usrp3/lib/timing/pps_synchronizer.v @@ -0,0 +1,42 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +module pps_synchronizer +( + input ref_clk, + input timebase_clk, + input pps_in, + output pps_out, + output reg pps_count +); + wire pps_refclk; + reg pps_out_del; + + //The input pps is treated an as async signal and is first synchronized + //to a common reference clock shared between multiple devices. It is then + //synchronized to the timebase clock which counts up the VITA time. + //The reference clock frequency must be equal to or smaller than the + //timebase clock frequency to remove any time ambiguity. + + //For robust synchronization across FPGA builds, the async delay for pps_in + //must be constrained along with the clock delay (or meet static timing there). + //The path length between the two synchronizers must also be constrained + synchronizer #(.INITIAL_VAL(1'b0), .FALSE_PATH_TO_IN(0)) pps_sync_refclk_inst ( + .clk(ref_clk), .rst(1'b0 /* no reset */), .in(pps_in), .out(pps_refclk)); + + synchronizer #(.INITIAL_VAL(1'b0), .FALSE_PATH_TO_IN(0)) pps_sync_tbclk_inst ( + .clk(timebase_clk), .rst(1'b0 /* no reset */), .in(pps_refclk), .out(pps_out)); + + //Implement a 1-bit counter to detect PPS edges + always @(posedge timebase_clk) + pps_out_del <= pps_out; + + always @(posedge timebase_clk) + if (~pps_out_del && pps_out) + pps_count <= ~pps_count; + +endmodule //pps_synchronizer diff --git a/fpga/usrp3/lib/timing/pulse_generator.v b/fpga/usrp3/lib/timing/pulse_generator.v new file mode 100644 index 000000000..12a98f5aa --- /dev/null +++ b/fpga/usrp3/lib/timing/pulse_generator.v @@ -0,0 +1,29 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: pulse_generator +// Description: +// Generates pulses of a given width at intervals of a given period based on +// a given input clock. + +module pulse_generator #(parameter WIDTH = 32) ( + input wire clk, /* clock */ + input wire reset, /* reset */ + input wire [WIDTH-1:0] period, /* period, in clk cycles */ + input wire [WIDTH-1:0] pulse_width, /* pulse width, in clk cycles */ + output reg pulse /* pulse */ +); + reg [WIDTH-1:0] count = 0; + + always @(posedge clk) begin + if (reset | count <= 1) + count <= period; + else + count <= count - 1; + + pulse <= (count > (period - pulse_width)); + end + +endmodule //pulse_generator diff --git a/fpga/usrp3/lib/timing/time_compare.v b/fpga/usrp3/lib/timing/time_compare.v new file mode 100644 index 000000000..60b472d05 --- /dev/null +++ b/fpga/usrp3/lib/timing/time_compare.v @@ -0,0 +1,54 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +// 64 bits worth of ticks +// +// Not concerned with clock wrapping, human race will likely have extermintated it's self by this time. +// + +module time_compare + ( + input clk, + input reset, + input [63:0] time_now, + input [63:0] trigger_time, + output now, + output early, + output late, + output too_early); + +/* + reg [63:0] time_diff; + + always @(posedge clk) begin + if (reset) begin + time_diff <= 64'b0; + now <= 1'b0; + late <= 1'b0; + early <= 1'b0; + end + else begin + time_diff <= trigger_time - time_now; + now <= ~(|time_diff); + late <= time_diff[63]; + early <= ~now & ~late; + end + end + //assign now = ~(|time_diff); + //assign late = time_diff[63]; + //assign early = ~now & ~late; + assign too_early = 0; //not implemented +*/ + + assign now = time_now == trigger_time; + assign late = time_now > trigger_time; + assign early = ~now & ~late; + assign too_early = 0; //not implemented + +endmodule // time_compare diff --git a/fpga/usrp3/lib/timing/timekeeper.v b/fpga/usrp3/lib/timing/timekeeper.v new file mode 100644 index 000000000..16b7f0fe6 --- /dev/null +++ b/fpga/usrp3/lib/timing/timekeeper.v @@ -0,0 +1,87 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +module timekeeper + #(parameter SR_TIME_HI = 0, + parameter SR_TIME_LO = 1, + parameter SR_TIME_CTRL = 2, + parameter INCREMENT = 64'h1) + (input clk, input reset, input pps, input sync_in, input strobe, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + output reg [63:0] vita_time, output reg [63:0] vita_time_lastpps, + output reg sync_out); + + ////////////////////////////////////////////////////////////////////////// + // timer settings for this module + ////////////////////////////////////////////////////////////////////////// + wire [63:0] time_at_next_event; + wire set_time_pps, set_time_now, set_time_sync; + wire cmd_trigger; + + setting_reg #(.my_addr(SR_TIME_HI), .width(32)) sr_time_hi + (.clk(clk), .rst(reset), .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(time_at_next_event[63:32]), .changed()); + + setting_reg #(.my_addr(SR_TIME_LO), .width(32)) sr_time_lo + (.clk(clk), .rst(reset), .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(time_at_next_event[31:0]), .changed()); + + setting_reg #(.my_addr(SR_TIME_CTRL), .width(3)) sr_ctrl + (.clk(clk), .rst(reset), .strobe(set_stb), .addr(set_addr), .in(set_data), + .out({set_time_sync, set_time_pps, set_time_now}), .changed(cmd_trigger)); + + ////////////////////////////////////////////////////////////////////////// + // PPS edge detection logic + ////////////////////////////////////////////////////////////////////////// + reg pps_del, pps_del2; + always @(posedge clk) + {pps_del2,pps_del} <= {pps_del, pps}; + + wire pps_edge = !pps_del2 & pps_del; + + ////////////////////////////////////////////////////////////////////////// + // arm the trigger to latch a new time when the ctrl register is written + ////////////////////////////////////////////////////////////////////////// + reg armed; + wire time_event = armed && ((set_time_now) || (set_time_pps && pps_edge) || (set_time_sync && sync_in)); + always @(posedge clk) begin + if (reset) armed <= 1'b0; + else if (cmd_trigger) armed <= 1'b1; + else if (time_event) armed <= 1'b0; + end + + ////////////////////////////////////////////////////////////////////////// + // vita time tracker - update every tick or when we get an "event" + ////////////////////////////////////////////////////////////////////////// + always @(posedge clk) begin + sync_out <= 1'b0; + if(reset) begin + vita_time <= 64'h0; + end else begin + if (time_event) begin + sync_out <= 1'b1; + vita_time <= time_at_next_event; + end else if (strobe) begin + vita_time <= vita_time + INCREMENT; + end + end + end + + ////////////////////////////////////////////////////////////////////////// + // track the time at last pps so host can detect the pps + ////////////////////////////////////////////////////////////////////////// + always @(posedge clk) + if(reset) + vita_time_lastpps <= 64'h0; + else if(pps_edge) + if(time_event) + vita_time_lastpps <= time_at_next_event; + else + vita_time_lastpps <= vita_time + INCREMENT; + +endmodule // timekeeper |