aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/timing
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/timing')
-rw-r--r--fpga/usrp3/lib/timing/Makefile.srcs17
-rw-r--r--fpga/usrp3/lib/timing/pps_generator.v29
-rw-r--r--fpga/usrp3/lib/timing/pps_synchronizer.v42
-rw-r--r--fpga/usrp3/lib/timing/pulse_generator.v29
-rw-r--r--fpga/usrp3/lib/timing/time_compare.v54
-rw-r--r--fpga/usrp3/lib/timing/timekeeper.v87
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