Repository navigation
Expand file tree
/
Copy pathclock_ppb_meter.vhd
More file actions
182 lines (158 loc) · 7.77 KB
/
Copy pathclock_ppb_meter.vhd
File metadata and controls
182 lines (158 loc) · 7.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
-- ============================================================================
-- clock_ppb_meter.vhd
--
-- Measures edge counts of two clocks (wallclock_512fs vs. pll_512fs) over a
-- one-second measurement window defined by wallclock_second_pulse_i.
-- The raw counter values are output for MCU to calculate PPB.
--
-- Operation:
-- 1. Assert start_i -> module waits for the next second pulse.
-- 2. On first second pulse: valid_o goes low, edge counters start.
-- 3. On next second pulse: counting stops, counters latched.
-- 4. Results appear on count_wc_o / count_pll_o, valid_o goes high.
--
-- MCU calculates PPB: ppb = (count_pll - count_wc) * 1e9 / count_wc
-- Positive -> PLL is running fast relative to wallclock.
-- Negative -> PLL is running slow relative to wallclock.
-- ============================================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clock_ppb_meter is
port (
sys_clk : in std_logic; -- 125 MHz system clock
reset_n : in std_logic; -- Active-low reset
-- Asynchronous clock inputs to be measured
wallclock_512fs_in : in std_logic; -- 512·fs from wallclock
pll_512fs_in : in std_logic; -- 512·fs from local PLL
-- Control (active in sys_clk domain)
wallclock_second_pulse_i : in std_logic; -- 1-PPS from wallclock
start_i : in std_logic; -- Start a measurement
-- Results: raw counter values for MCU to compute PPB
count_wc_o : out unsigned(24 downto 0); -- Wallclock edge count
count_pll_o : out unsigned(24 downto 0); -- PLL edge count
valid_o : out std_logic -- High when counts are valid
);
end entity clock_ppb_meter;
architecture rtl of clock_ppb_meter is
-- ======================================================================
-- FSM (simplified - no calculation needed)
-- ======================================================================
type state_t is (
S_IDLE, -- Waiting for start_i
S_WAIT_PULSE, -- Waiting for first second-pulse (alignment)
S_COUNTING, -- Counting edges between two second-pulses
S_DONE -- Counting complete, output valid
);
signal state : state_t := S_IDLE;
-- ======================================================================
-- CDC synchronisers (3-FF: meta-stability + edge detection)
-- ======================================================================
signal wc_sync : std_logic_vector(2 downto 0) := (others => '0');
signal pll_sync : std_logic_vector(2 downto 0) := (others => '0');
signal wc_rise : std_logic;
signal pll_rise : std_logic;
-- ======================================================================
-- Edge detection for signals assumed to be in sys_clk domain
-- ======================================================================
signal sec_d : std_logic := '0';
signal sec_rise : std_logic;
signal start_d : std_logic := '0';
signal start_rise : std_logic;
-- ======================================================================
-- Edge counters (25 bits → max 33 554 431, covers 512·48 000 = 24 576 000)
-- ======================================================================
signal count_wc : unsigned(24 downto 0) := (others => '0');
signal count_pll : unsigned(24 downto 0) := (others => '0');
-- ======================================================================
-- Output registers
-- ======================================================================
signal count_wc_reg : unsigned(24 downto 0) := (others => '0');
signal count_pll_reg : unsigned(24 downto 0) := (others => '0');
signal valid_reg : std_logic := '0';
begin
count_wc_o <= count_wc_reg;
count_pll_o <= count_pll_reg;
valid_o <= valid_reg;
-- ==================================================================
-- CDC: Synchronise the two asynchronous 512·fs clocks into sys_clk
-- ==================================================================
p_cdc : process(sys_clk)
begin
if rising_edge(sys_clk) then
wc_sync <= wc_sync(1 downto 0) & wallclock_512fs_in;
pll_sync <= pll_sync(1 downto 0) & pll_512fs_in;
end if;
end process p_cdc;
-- Rising-edge detectors (compare stage 2 and stage 3)
wc_rise <= wc_sync(1) and not wc_sync(2);
pll_rise <= pll_sync(1) and not pll_sync(2);
-- ==================================================================
-- Edge detection for second-pulse and start (sys_clk domain)
-- ==================================================================
p_edge : process(sys_clk)
begin
if rising_edge(sys_clk) then
sec_d <= wallclock_second_pulse_i;
start_d <= start_i;
end if;
end process p_edge;
sec_rise <= wallclock_second_pulse_i and not sec_d;
start_rise <= start_i and not start_d;
-- ==================================================================
-- Main state machine (simplified - just count, no calculation)
-- ==================================================================
p_fsm : process(sys_clk, reset_n)
begin
if reset_n = '0' then
state <= S_IDLE;
valid_reg <= '0';
count_wc <= (others => '0');
count_pll <= (others => '0');
count_wc_reg <= (others => '0');
count_pll_reg<= (others => '0');
elsif rising_edge(sys_clk) then
case state is
-- =====================================================
-- IDLE – wait for start_i rising edge
-- =====================================================
when S_IDLE =>
if start_rise = '1' then
valid_reg <= '0';
state <= S_WAIT_PULSE;
end if;
-- =====================================================
-- WAIT_PULSE – align to the next second boundary
-- =====================================================
when S_WAIT_PULSE =>
if sec_rise = '1' then
count_wc <= (others => '0');
count_pll <= (others => '0');
state <= S_COUNTING;
end if;
-- =====================================================
-- COUNTING – accumulate rising edges for one second
-- =====================================================
when S_COUNTING =>
if wc_rise = '1' then
count_wc <= count_wc + 1;
end if;
if pll_rise = '1' then
count_pll <= count_pll + 1;
end if;
if sec_rise = '1' then
-- Latch final counts to output registers
count_wc_reg <= count_wc;
count_pll_reg <= count_pll;
state <= S_DONE;
end if;
-- =====================================================
-- DONE – output is valid, wait for next measurement
-- =====================================================
when S_DONE =>
valid_reg <= '1';
state <= S_IDLE;
end case;
end if;
end process p_fsm;
end architecture rtl;