fpga: x400: avoid false PPS rising edge on switching sources
Original-commit: d5876c82a02f7800141b74517d0b5461946de9fc
This commit is contained in:
+42
-32
@@ -100,30 +100,38 @@ module x4xx_pps_sync #(
|
|||||||
.out (pps_ext_brc)
|
.out (pps_ext_brc)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Synchronize the select bits over to the reference clock as well. Note that this is
|
// Synchronize the select bits over to the reference clock using a handshake
|
||||||
// a vector, so we could have some invalid values creep through when changing.
|
// to guarantee transfer of the complete pps_select signal.
|
||||||
// See the note below as to why this is safe.
|
// Further include the LMK sync trigger to make sure both signals are aligned.
|
||||||
wire [1:0] pps_select_brc;
|
reg [1:0] pps_select_brc;
|
||||||
synchronizer #(
|
reg pll_sync_trigger_brc;
|
||||||
.FALSE_PATH_TO_IN (1),
|
wire pps_handshake_out_valid;
|
||||||
.WIDTH (2)
|
wire [2:0] pps_handshake_out_data;
|
||||||
) synchronizer_pps_select (
|
handshake #(
|
||||||
.clk (base_ref_clk),
|
.WIDTH (3)
|
||||||
.rst (1'b0),
|
) handshake_pps (
|
||||||
.in (pps_select),
|
.clk_a (ctrl_clk),
|
||||||
.out (pps_select_brc)
|
.rst_a (1'b0),
|
||||||
|
.valid_a (1'b1),
|
||||||
|
.data_a ({pps_select, pll_sync_trigger}),
|
||||||
|
.busy_a (),
|
||||||
|
.clk_b (base_ref_clk),
|
||||||
|
.valid_b (pps_handshake_out_valid),
|
||||||
|
.data_b (pps_handshake_out_data)
|
||||||
);
|
);
|
||||||
|
always @(posedge base_ref_clk) begin
|
||||||
|
if (pps_handshake_out_valid) begin
|
||||||
|
{pps_select_brc, pll_sync_trigger_brc} <= pps_handshake_out_data;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
// PPS MUX - selects internal or external PPS.
|
// PPS MUX - selects internal or external PPS.
|
||||||
reg pps_brc = 1'b0;
|
// Generate the signal new_pps_selected to indicate a change in the PPS source.
|
||||||
|
reg pps_brc = 1'b0;
|
||||||
|
reg [1:0] pps_select_delayed_brc = 2'b00;
|
||||||
|
reg new_pps_selected = 1'b0;
|
||||||
always @(posedge base_ref_clk) begin
|
always @(posedge base_ref_clk) begin
|
||||||
// It is possible when the vector is being double-synchronized to the
|
// generate PPS signal based on selected source
|
||||||
// reference clock domain that there could be multiple bits asserted
|
|
||||||
// simultaneously. This is not problematic because the order of operations
|
|
||||||
// in the following selection mux should take over and only one PPS should
|
|
||||||
// win. This could result in glitches, but that is expected during ANY PPS
|
|
||||||
// switchover since the switch is performed asynchronously to the PPS
|
|
||||||
// signal.
|
|
||||||
case (pps_select_brc)
|
case (pps_select_brc)
|
||||||
PPS_INT_10MHZ: begin
|
PPS_INT_10MHZ: begin
|
||||||
pps_brc <= pps_int_10mhz_brc;
|
pps_brc <= pps_int_10mhz_brc;
|
||||||
@@ -135,6 +143,16 @@ module x4xx_pps_sync #(
|
|||||||
pps_brc <= pps_ext_brc;
|
pps_brc <= pps_ext_brc;
|
||||||
end
|
end
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
|
// Delay the select signal to compare with the current value
|
||||||
|
pps_select_delayed_brc <= pps_select_brc;
|
||||||
|
|
||||||
|
// Indicate the first cycle of a newly selected PPS source
|
||||||
|
if (pps_select_brc != pps_select_delayed_brc) begin
|
||||||
|
new_pps_selected <= 1'b1;
|
||||||
|
end else begin
|
||||||
|
new_pps_selected <= 1'b0;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// forward BRC based PPS to output
|
// forward BRC based PPS to output
|
||||||
@@ -146,23 +164,15 @@ module x4xx_pps_sync #(
|
|||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Detect rising edge of PPS
|
// Detect rising edge of PPS
|
||||||
|
// Ignore the first cycle after a new PPS source is selected as this might cause a change in the
|
||||||
|
// PPS signal as the PPS sources are not aligned.
|
||||||
|
// Skipping this switching cycle ensures the rising edge is detected from the selected PPS source.
|
||||||
reg pps_brc_delayed;
|
reg pps_brc_delayed;
|
||||||
wire pps_rising_edge_brc;
|
wire pps_rising_edge_brc;
|
||||||
always @(posedge base_ref_clk) begin
|
always @(posedge base_ref_clk) begin
|
||||||
pps_brc_delayed <= pps_brc;
|
pps_brc_delayed <= pps_brc;
|
||||||
end
|
end
|
||||||
assign pps_rising_edge_brc = pps_brc & ~pps_brc_delayed;
|
assign pps_rising_edge_brc = pps_brc & ~pps_brc_delayed & ~new_pps_selected;
|
||||||
|
|
||||||
// Transfer control signals to internal clock domain
|
|
||||||
wire pll_sync_trigger_brc;
|
|
||||||
synchronizer #(
|
|
||||||
.FALSE_PATH_TO_IN (1)
|
|
||||||
) synchronizer_sync_trigger (
|
|
||||||
.clk (base_ref_clk),
|
|
||||||
.rst (1'b0),
|
|
||||||
.in (pll_sync_trigger),
|
|
||||||
.out (pll_sync_trigger_brc)
|
|
||||||
);
|
|
||||||
|
|
||||||
// There is no data coherency guaranteed by this synchronizer, but this is
|
// There is no data coherency guaranteed by this synchronizer, but this is
|
||||||
// not required. The information is derived in the same clock domain as the
|
// not required. The information is derived in the same clock domain as the
|
||||||
|
|||||||
Reference in New Issue
Block a user