Attention
This documentation is a work in progress. Expect to see errors and unfinished things.
evr_ts_cdc Source File
1// Move tinyEVR's 64-bit timestamp to another clock domain
2// Uses Gray codes to get (almost) ideal results.
3// Time delay is one evr_clk plus one-to-two usr_clk cycles.
4// Yes, that's the inevitable clock-domain-crossing jitter.
5//
6// The Gray code scheme fails at the time when ts_tcks gets reset to zero by the evr_pps event.
7// As a workaround, the first _two_ values of the output usr_tcks are forced to zero, every time usr_secs is updated.
8//
9// Obligatory xkcd: https://xkcd.com/2867/
10module evr_ts_cdc(
11 input evr_clk,
12 input [31:0] ts_secs,
13 input [31:0] ts_tcks,
14 input evr_pps,
15 //
16 input usr_clk,
17 output [31:0] usr_secs,
18 output [31:0] usr_tcks
19);
20
21// binary to Gray
22reg [31:0] ts_tgray=0;
23always @(posedge evr_clk) ts_tgray <= ts_tcks ^ {1'b0, ts_tcks[30:1]};
24
25// CDC
26reg [31:0] usr_tgray=0;
27always @(posedge usr_clk) usr_tgray <= ts_tgray;
28
29// Gray to binary
30// verilator lint_save
31// verilator lint_off UNOPTFLAT
32wire [31:0] usr_bin1 = usr_tgray ^ {1'b0, usr_bin1[30:1]};
33// verilator lint_restore
34reg [31:0] usr_bin=0;
35always @(posedge usr_clk) usr_bin <= usr_bin1;
36
37// Move coarse seconds across
38// Almost, but not quite, data_xdomain.
39reg pps_toggle=0;
40always @(posedge evr_clk) if (evr_pps) pps_toggle <= ~pps_toggle;
41reg pps_cap0=0, pps_cap1=0;
42reg [31:0] usr_sec1=0, usr_sec2=0;
43wire pps_grab = pps_cap0 != pps_cap1;
44always @(posedge usr_clk) begin
45 pps_cap0 <= pps_toggle;
46 pps_cap1 <= pps_cap0;
47 usr_sec1 <= ts_secs;
48 if (pps_grab) usr_sec2 <= usr_sec1;
49end
50
51// At this point usr_sec2 is right, and usr_bin is (almost always) right,
52// but there is a chance they will not roll over at the same time.
53// That means you can get usr_bin as 0 or 1 with the old value of usr_sec2
54// for a cycle, or usr_sec2 updated to the new value a cycle before usr_bin jumps back to 0 or 1.
55// My test bench is (sadly) not good enough to show the effect.
56// Eliminating that potential glitch isn't easy, and seems guaranteed to have side effects.
57// The approach below zeros out the tcks data from _both_ possibly corrupt edges.
58reg pps_grab1=0;
59reg [31:0] usr_bin2=0;
60wire pps_zero = pps_grab | pps_grab1;
61always @(posedge usr_clk) begin
62 pps_grab1 <= pps_grab;
63 usr_bin2 <= pps_zero ? 32'b0 : usr_bin;
64end
65assign usr_secs = usr_sec2;
66assign usr_tcks = usr_bin2;
67
68endmodule