Skip to content

Commit e181ea5

Browse files
committed
Add PTP clock module and testbench
1 parent 352f52e commit e181ea5

File tree

3 files changed

+786
-0
lines changed

3 files changed

+786
-0
lines changed

rtl/ptp_clock.v

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*
2+
3+
Copyright (c) 2015-2019 Alex Forencich
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
23+
*/
24+
25+
// Language: Verilog 2001
26+
27+
`timescale 1ns / 1ps
28+
29+
/*
30+
* PTP clock module
31+
*/
32+
module ptp_clock #
33+
(
34+
parameter PERIOD_NS_WIDTH = 4,
35+
parameter OFFSET_NS_WIDTH = 4,
36+
parameter DRIFT_NS_WIDTH = 4,
37+
parameter FNS_WIDTH = 16,
38+
parameter PERIOD_NS = 4'h6,
39+
parameter PERIOD_FNS = 16'h6666,
40+
parameter DRIFT_ENABLE = 1,
41+
parameter DRIFT_NS = 4'h0,
42+
parameter DRIFT_FNS = 16'h0002,
43+
parameter DRIFT_RATE = 16'h0005
44+
)
45+
(
46+
input wire clk,
47+
input wire rst,
48+
49+
/*
50+
* Timestamp inputs for synchronization
51+
*/
52+
input wire [95:0] input_ts_96,
53+
input wire input_ts_96_valid,
54+
input wire [63:0] input_ts_64,
55+
input wire input_ts_64_valid,
56+
57+
/*
58+
* Period adjustment
59+
*/
60+
input wire [PERIOD_NS_WIDTH-1:0] input_period_ns,
61+
input wire [FNS_WIDTH-1:0] input_period_fns,
62+
input wire input_period_valid,
63+
64+
/*
65+
* Offset adjustment
66+
*/
67+
input wire [OFFSET_NS_WIDTH-1:0] input_adj_ns,
68+
input wire [FNS_WIDTH-1:0] input_adj_fns,
69+
input wire [15:0] input_adj_count,
70+
input wire input_adj_valid,
71+
output wire input_adj_active,
72+
73+
/*
74+
* Drift adjustment
75+
*/
76+
input wire [DRIFT_NS_WIDTH-1:0] input_drift_ns,
77+
input wire [FNS_WIDTH-1:0] input_drift_fns,
78+
input wire [15:0] input_drift_rate,
79+
input wire input_drift_valid,
80+
81+
/*
82+
* Timestamp outputs
83+
*/
84+
output wire [95:0] output_ts_96,
85+
output wire [63:0] output_ts_64,
86+
output wire output_ts_step,
87+
88+
/*
89+
* PPS output
90+
*/
91+
output wire output_pps
92+
);
93+
94+
parameter INC_NS_WIDTH = $clog2(2**PERIOD_NS_WIDTH + 2**OFFSET_NS_WIDTH + 2**DRIFT_NS_WIDTH);
95+
96+
reg [PERIOD_NS_WIDTH-1:0] period_ns_reg = PERIOD_NS;
97+
reg [FNS_WIDTH-1:0] period_fns_reg = PERIOD_FNS;
98+
99+
reg [OFFSET_NS_WIDTH-1:0] adj_ns_reg = 0;
100+
reg [FNS_WIDTH-1:0] adj_fns_reg = 0;
101+
reg [15:0] adj_count_reg = 0;
102+
reg adj_active_reg = 0;
103+
104+
reg [DRIFT_NS_WIDTH-1:0] drift_ns_reg = DRIFT_NS;
105+
reg [FNS_WIDTH-1:0] drift_fns_reg = DRIFT_FNS;
106+
reg [15:0] drift_rate_reg = DRIFT_RATE;
107+
108+
reg [INC_NS_WIDTH-1:0] ts_inc_ns_reg = 0;
109+
reg [FNS_WIDTH-1:0] ts_inc_fns_reg = 0;
110+
111+
reg [47:0] ts_96_s_reg = 0;
112+
reg [29:0] ts_96_ns_reg = 0;
113+
reg [FNS_WIDTH-1:0] ts_96_fns_reg = 0;
114+
reg [29:0] ts_96_ns_inc_reg = 0;
115+
reg [FNS_WIDTH-1:0] ts_96_fns_inc_reg = 0;
116+
reg [30:0] ts_96_ns_ovf_reg = 31'h7fffffff;
117+
reg [FNS_WIDTH-1:0] ts_96_fns_ovf_reg = 16'hffff;
118+
119+
reg [47:0] ts_64_ns_reg = 0;
120+
reg [FNS_WIDTH-1:0] ts_64_fns_reg = 0;
121+
122+
reg ts_step_reg = 1'b0;
123+
124+
reg [15:0] drift_cnt = 0;
125+
126+
reg [47:0] temp;
127+
128+
reg pps_reg = 0;
129+
130+
assign input_adj_active = adj_active_reg;
131+
132+
assign output_ts_96[95:48] = ts_96_s_reg;
133+
assign output_ts_96[47:46] = 2'b00;
134+
assign output_ts_96[45:16] = ts_96_ns_reg;
135+
assign output_ts_96[15:0] = FNS_WIDTH > 16 ? ts_96_fns_reg >> (FNS_WIDTH-16) : ts_96_fns_reg << (16-FNS_WIDTH);
136+
assign output_ts_64[63:16] = ts_64_ns_reg;
137+
assign output_ts_64[15:0] = FNS_WIDTH > 16 ? ts_64_fns_reg >> (FNS_WIDTH-16) : ts_64_fns_reg << (16-FNS_WIDTH);
138+
assign output_ts_step = ts_step_reg;
139+
140+
assign output_pps = pps_reg;
141+
142+
always @(posedge clk) begin
143+
ts_step_reg <= 0;
144+
145+
// latch parameters
146+
if (input_period_valid) begin
147+
period_ns_reg <= input_period_ns;
148+
period_fns_reg <= input_period_fns;
149+
end
150+
151+
if (input_adj_valid) begin
152+
adj_ns_reg <= input_adj_ns;
153+
adj_fns_reg <= input_adj_fns;
154+
adj_count_reg <= input_adj_count;
155+
end
156+
157+
if (DRIFT_ENABLE && input_drift_valid) begin
158+
drift_ns_reg <= input_drift_ns;
159+
drift_fns_reg <= input_drift_fns;
160+
drift_rate_reg <= input_drift_rate;
161+
end
162+
163+
// timestamp increment calculation
164+
{ts_inc_ns_reg, ts_inc_fns_reg} <= $signed({period_ns_reg, period_fns_reg}) +
165+
(adj_active_reg ? $signed({adj_ns_reg, adj_fns_reg}) : 0) +
166+
((DRIFT_ENABLE && drift_cnt == 0) ? $signed({drift_ns_reg, drift_fns_reg}) : 0);
167+
168+
// offset adjust counter
169+
if (adj_count_reg > 0) begin
170+
adj_count_reg <= adj_count_reg - 1;
171+
adj_active_reg <= 1;
172+
ts_step_reg <= 1;
173+
end else begin
174+
adj_active_reg <= 0;
175+
end
176+
177+
// drift counter
178+
if (drift_cnt == 0) begin
179+
drift_cnt <= drift_rate_reg-1;
180+
end else begin
181+
drift_cnt <= drift_cnt - 1;
182+
end
183+
184+
// 96 bit timestamp
185+
if (input_ts_96_valid) begin
186+
// load timestamp
187+
{ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg};
188+
{ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
189+
ts_96_s_reg <= input_ts_96[95:48];
190+
ts_96_ns_reg <= input_ts_96[45:16];
191+
ts_96_fns_reg <= FNS_WIDTH > 16 ? input_ts_96[15:0] << (FNS_WIDTH-16) : input_ts_96[15:0] >> (16-FNS_WIDTH);
192+
ts_step_reg <= 1;
193+
end else if (!ts_96_ns_ovf_reg[30]) begin
194+
// if the overflow lookahead did not borrow, one second has elapsed
195+
// increment seconds field, pre-compute both normal increment and overflow values
196+
{ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg};
197+
{ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
198+
{ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg};
199+
ts_96_s_reg <= ts_96_s_reg + 1;
200+
end else begin
201+
// no increment seconds field, pre-compute both normal increment and overflow values
202+
{ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg};
203+
{ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
204+
{ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg};
205+
ts_96_s_reg <= ts_96_s_reg;
206+
end
207+
208+
// 64 bit timestamp
209+
if (input_ts_64_valid) begin
210+
// load timestamp
211+
{ts_64_ns_reg, ts_64_fns_reg} <= input_ts_64;
212+
ts_step_reg <= 1;
213+
end else begin
214+
{ts_64_ns_reg, ts_64_fns_reg} <= {ts_64_ns_reg, ts_64_fns_reg} + {ts_inc_ns_reg, ts_inc_fns_reg};
215+
end
216+
217+
pps_reg <= !ts_96_ns_ovf_reg[30];
218+
219+
if (rst) begin
220+
period_ns_reg <= PERIOD_NS;
221+
period_fns_reg <= PERIOD_FNS;
222+
adj_ns_reg <= 0;
223+
adj_fns_reg <= 0;
224+
adj_count_reg <= 0;
225+
adj_active_reg <= 0;
226+
drift_ns_reg <= DRIFT_NS;
227+
drift_fns_reg <= DRIFT_FNS;
228+
drift_rate_reg <= DRIFT_RATE;
229+
ts_inc_ns_reg <= 0;
230+
ts_inc_fns_reg <= 0;
231+
ts_96_s_reg <= 0;
232+
ts_96_ns_reg <= 0;
233+
ts_96_fns_reg <= 0;
234+
ts_96_ns_inc_reg <= 0;
235+
ts_96_fns_inc_reg <= 0;
236+
ts_96_ns_ovf_reg <= 31'h7fffffff;
237+
ts_96_fns_ovf_reg <= {FNS_WIDTH{1'b1}};
238+
ts_64_ns_reg <= 0;
239+
ts_64_fns_reg <= 0;
240+
ts_step_reg <= 0;
241+
drift_cnt <= 0;
242+
pps_reg <= 0;
243+
end
244+
end
245+
246+
endmodule

0 commit comments

Comments
 (0)