aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/pulse_synchronizer.v
blob: af3460878862bcf0bb7c61b837a9f73149ca1f30 (plain)
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
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: pulse_synchronizer
// Description:
// - Synchronizes a single-cycle pulse or an edge from one
//   clock domain to another
// - Clocks A and B can be asynchronous
//

module pulse_synchronizer #(
  parameter MODE   = "PULSE", // Capture mode {PULSE, POSEDGE, NEGEDGE}
  parameter STAGES = 2        // Number of synchronizer stages
) (
  input   clk_a,    // Clock A
  input   rst_a,    // Reset in clock domain A
  input   pulse_a,  // Pulse in clock domain A to synchronize
  output  busy_a,   // Synchronizer is busy (pulse_a ignored when asserted)
  input   clk_b,    // Clock B
  output  pulse_b   // Pulse in clock domain B
);
  // Trigger logic based on the capture mode
  wire trigger;
  generate if (MODE == "POSEDGE") begin
    reg pulse_a_del_pe = 1'b0;
    always @ (posedge clk_a)
      pulse_a_del_pe <= rst_a ? 1'b0 : pulse_a;
    assign trigger = pulse_a & ~pulse_a_del_pe;
  end else if (MODE == "NEGEDGE") begin
    reg pulse_a_del_ne = 1'b1;
    always @ (posedge clk_a)
      pulse_a_del_ne <= rst_a ? 1'b1 : pulse_a;
    assign trigger = ~pulse_a & pulse_a_del_ne;
  end else begin
    assign trigger = pulse_a;
  end endgenerate

  // Translate pulse/edge to a level and synchronize that into the B domain
  reg pulse_toggle_a = 1'b0;
  always @(posedge clk_a) begin
    pulse_toggle_a <= rst_a ? 1'b0 : (pulse_toggle_a ^ (trigger & ~busy_a));
  end

  wire pulse_toggle_b;
  reg  pulse_toggle_b_del = 1'b0;
  wire handshake_toggle_a;

  synchronizer #(
    .STAGES(STAGES), .INITIAL_VAL(0)
  ) toggle_sync_i (
    .clk(clk_b), .rst(1'b0), .in(pulse_toggle_a), .out(pulse_toggle_b)
  );

  // Handshake toggle signal back into the A domain to deassert busy
  synchronizer #(
    .STAGES(STAGES), .INITIAL_VAL(0)
  ) handshake_sync_i (
    .clk(clk_a), .rst(1'b0), .in(pulse_toggle_b_del), .out(handshake_toggle_a)
  );

  always @(posedge clk_b) begin
    pulse_toggle_b_del <= pulse_toggle_b;
  end

  assign pulse_b = pulse_toggle_b_del ^ pulse_toggle_b;
  assign busy_a = pulse_toggle_a ^ handshake_toggle_a;

endmodule