Author Topic: reliable clock detect and reset signal generation in verilog  (Read 1110 times)

0 Members and 1 Guest are viewing this topic.

Online radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3371
  • Country: ua
I have two clocks: clk1 and clk2.
clk1 - has high frequency (about 100 MHz) but sometimes connected/disconnected and is unstable during switch transition
clk2 - has low frequency (about 50 MHz) but it is always on (backup clock)

Clocks are not synchronized.

I want to generate reset signal which should going to 1 when clk1 disconnected or unstable and going to 0 when clk1 is connected and stable. I need it to reset some logic which can going into unstable state when clock is missing or unstable.

What is the reliable way to generate such reset signal in verilog?
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7740
  • Country: ca
Re: reliable clock detect and reset signal generation in verilog
« Reply #1 on: June 11, 2022, 06:22:29 am »
So, what you are saying is that you want to program a frequency counter running on CLK2 to time CLK1, a counter with really poor accuracy.  If that counter sees 100MHz, then you turn off the reset, if it does not see 100MHz, then you turn on the reset.

Doesn't sound too difficult.  You only need an accuracy of ~3 bits, where your counter total being 0= no clock, 1=25mhz, 2=50mhz, 3=75mhz, ect... if the result is 3 or larger, then the CLK1 is ~100mhz.

You can also make a counter which resets once every mhz, then you will know the frequency of clk1 within 1 mhz, but you would need a 7 bit counter to see the 99-101mhz result.

 

Online Someone

  • Super Contributor
  • ***
  • Posts: 4531
  • Country: au
    • send complaints here
Re: reliable clock detect and reset signal generation in verilog
« Reply #2 on: June 11, 2022, 06:26:20 am »
What is the reliable way to generate such reset signal in verilog?
Verilog is just a language to describe models, what actually happens in hardware is only ever an approximation of that. As soon as you enter the world of asynchronous signals, all bets are off...
 

Offline fourfathom

  • Super Contributor
  • ***
  • Posts: 1884
  • Country: us
Re: reliable clock detect and reset signal generation in verilog
« Reply #3 on: June 11, 2022, 06:56:29 am »
As soon as you enter the world of asynchronous signals, all bets are off...

It's not really that bad.  The easiest way to test for your asynchronous high-speed clock is to code a divide-by-8 that is synchronous to the 100 MHz unknown clock.  Take the 12.5 MHz output of that divider and run it through a two or three-stage synchronizer, clocked by your stable 50 MHz clock.  You can then test that re-synchronized signal as you like.

You can also (in verilog) design an asynch set-reset flop that operates across two clock domains, but that's less obvious to describe.  I think the pre-divider / synchronizer approach is cleaner.
We'll search out every place a sick, twisted, solitary misfit might run to! -- I'll start with Radio Shack.
 
The following users thanked this post: radiolistener

Online radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3371
  • Country: ua
Re: reliable clock detect and reset signal generation in verilog
« Reply #4 on: June 11, 2022, 07:35:43 am »
the problem here is that clocks are asynchronous, they generated from different clock sources. And I'm not well understand pitfalls and approaches when synchronizing between two clock domains.

Here is my draft. It doesn't works because I'm trying to modify pulse reg from different clock domains, but I think it shows what I'm trying to do.

Is it possible to solve it? Or maybe I'm doing it in a wrong way?

Code: [Select]
module clock_detect #(
    parameter RATE  = 5_000_000,  // clk1 division factor (testing interval)
    parameter N     = 100_000     // clk0 detect threshold
)(
    input   clk0,               // testing clock (100 MHz)
    input   clk1,               // backup clock (50 MHz)
    output  good                // clk0 is good
);
    reg         pulse = 0;
    reg [31:0]  clk1_cnt = 0;

    always @(posedge clk1) begin
        if (clk1_cnt < RATE) begin
            clk1_cnt <= clk1_cnt + 1;
        end else begin
            clk1_cnt <= 0;
            pulse <= 1;
        end
    end

    reg         present = 0;
    reg [31:0]  clk0_cnt = 0;
    integer k;
   
    always @(posedge clk0) begin
        // count clk0 edges and assign result
        if (!pulse) begin
            clk0_cnt <= clk0_cnt + 1;
        end else begin
            clk0_cnt <= 0;
            pulse <= 0;
            present <= clk0_cnt >= N;
        end
    end
    assign good = present;

endmodule
« Last Edit: June 11, 2022, 07:43:25 am by radiolistener »
 

Online radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3371
  • Country: ua
Re: reliable clock detect and reset signal generation in verilog
« Reply #5 on: June 11, 2022, 10:43:44 am »
It's not really that bad.  The easiest way to test for your asynchronous high-speed clock is to code a divide-by-8 that is synchronous to the 100 MHz unknown clock.  Take the 12.5 MHz output of that divider and run it through a two or three-stage synchronizer, clocked by your stable 50 MHz clock.  You can then test that re-synchronized signal as you like.

just tried this, here is my code:
Code: [Select]
module clock_detect #(
    parameter RATE  = 5_000_000,  // clk1 division factor (testing interval)
    parameter N     = 100_000     // clk0 detect threshold
)(
    input   clk0,               // testing clock (100 MHz)
    input   clk1,               // backup clock (50 MHz)
    output  rst                 // 1 when clk0 is not good
);
    // divide clk0 by 10 and transfer to clk1 domain
    wire clk0div, clk0div_synced;
   
    clk_div #(.WIDTH(4), .N(5)) clk0div_i(
        .clk(clk0),
        .clk_out(clk0div));
       
    sync_signal sync_clk1_i(
        .clk(clk1),
        .in(clk0div),
        .out(clk0div_synced));
       
    reg [2:0]   shift = 0;
    reg [31:0]  clk0_cnt = 0;
    reg [31:0]  clk1_cnt = 0;
    reg         present = 0;
   
    always @(posedge clk1) begin
        // push into shift reg to catch edges
        shift <= {shift[1:0], clk0div_synced};
       
        // set result at RATE period
        if (clk1_cnt < RATE) begin
            clk1_cnt <= clk1_cnt + 1;
           
            // count edges
            if (shift[2] ^ shift[1]) begin
                clk0_cnt <= clk0_cnt+1;
            end
        end else begin
            clk0_cnt <= 0;
            clk1_cnt <= 0;
            present <= clk0_cnt >= N / 10;
        end
    end

    // transfer result to clk0 domain
    sync_reset #(.N(4)) sync_reset_i (
        .clk(clk0),
        .rst(~present),
        .out(rst)
    );
endmodule

module clk_div #(
    parameter WIDTH = 3,    // Width of the register required
    parameter N = 6         // We will divide by 12 for example in this case
)(
    input clk,
    input reset,
    output clk_out
);

reg [WIDTH-1:0] r_reg;
wire [WIDTH-1:0] r_nxt;
reg clk_track;
 
always @(posedge clk or posedge reset)
begin
    if (reset)
        begin
            r_reg <= 0;
            clk_track <= 1'b0;
        end
    else if (r_nxt == N)
        begin
            r_reg <= 0;
            clk_track <= ~clk_track;
        end
  else
    r_reg <= r_nxt;
end
assign r_nxt = r_reg+1;
assign clk_out = clk_track;
endmodule

module sync_signal #(
    parameter WIDTH=1, // width of the input and output signals
    parameter N=2 // depth of synchronizer
)(
    input wire clk,
    input wire [WIDTH-1:0] in,
    output wire [WIDTH-1:0] out
);

(*preserve*)
reg [WIDTH-1:0] sync_reg[N-1:0];

assign out = sync_reg[N-1];

integer k;

always @(posedge clk) begin
    sync_reg[0] <= in;
    for (k = 1; k < N; k = k + 1) begin
        sync_reg[k] <= sync_reg[k-1];
    end
end
endmodule

module sync_reset #
(
    parameter N = 2 // depth of synchronizer
)
(
    input  wire clk,
    input  wire rst,
    output wire out
);

(*preserve*)
(* srl_style = "register" *)
reg [N-1:0] sync_reg = {N{1'b1}};

assign out = sync_reg[N-1];

always @(posedge clk or posedge rst) begin
    if (rst) begin
        sync_reg <= {N{1'b1}};
    end else begin
        sync_reg <= {sync_reg[N-2:0], 1'b0};
    end
end
endmodule

is it correct?
 

Offline fourfathom

  • Super Contributor
  • ***
  • Posts: 1884
  • Country: us
Re: reliable clock detect and reset signal generation in verilog
« Reply #6 on: June 11, 2022, 05:56:01 pm »
is it correct?

I haven't studied your code in detail, but what I glanced at looks OK.

Here is my cut at it, using non-parameterized code.  I also don't have any reset logic since the FPGA I've been using defaults all that.  There may be errors, this is non-tested (except for the "synch3" module, which I use a lot.)

Quote
/////////////////////
module div8(
   input clkHS,
   output q
   );
   
   reg[2:0] counter;
   assign q = counter[2];
   
   always @(posedge clk) counter <= counter + 3'd1;
   
endmodule


/////////////////////
module synch3(
   input clk, // the lower-speed global clock
   input d, // asynchronous input, freq < (clk / 2)
   output q,
   output rising, // positive enable triggered by rising-edge of input d
   output falling
   );
   
   reg [2:0] sr;
   assign rising = (sr[2:1] == 2'b01);
   assign falling = (sr[2:1] == 2'b10);
   assign q = sr[2];
   always @(posedge clk) sr <= {sr[1:0], d};

endmodule


/////////////////////
module testClkHS(
      input clkLS; // the lower-speed global clock (50 MHz?)
      input clkHS; // the high-speed clock being tested (100 MHz?)
      output clkStatus // whatever you want it to be
      );
      
   wire clkDiv8;
   wire posEdge;
   
   // status will be the result of whatever testing you want to do on the high-speed clock-derived signal
   reg status;
   //
   
   assign clkStatus = status;
      
   divider div8(
      .clkHS (clkHS),
      .q (clkDiv8)
      );
         
   synch synch3(
      .clk (clkLS),
      .d (clkDiv8),
      .rising (posEdge)
      );
      
   //////
   // your test circuit goes here -- something like this simple frequency counter (count between 7 and 9 considered OK):
   //   
      reg[3:0] gateTimer; // divides clkLS by 16 for counter gate
      reg[3:0] counter; // counts posEdge
      wire onFreq = (counter == 4'd7) || (counter == 4'd8) || (counter == 4'd9);
      
      always #(posedge clkLS) gateTimer <= gateTimer + 4'd1;
      
      always #(posedge clkLS) begin
         if (gateTimer == 4'd0) begin
            status <= onFreq;
            counter <= 4'd0;
         end else
            counter <= counter + 4'd1;
      end
   //
   //
   //////
      
endmodule
/////////////////////
We'll search out every place a sick, twisted, solitary misfit might run to! -- I'll start with Radio Shack.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7740
  • Country: ca
Re: reliable clock detect and reset signal generation in verilog
« Reply #7 on: June 11, 2022, 08:59:32 pm »
the problem here is that clocks are asynchronous, they generated from different clock sources. And I'm not well understand pitfalls and approaches when synchronizing between two clock domains.

Here is my draft. It doesn't works because I'm trying to modify pulse reg from different clock domains, but I think it shows what I'm trying to do.

Is it possible to solve it? Or maybe I'm doing it in a wrong way?

Code: [Select]
module clock_detect #(
    parameter RATE  = 5_000_000,  // clk1 division factor (testing interval)
    parameter N     = 100_000     // clk0 detect threshold
)(
    input   clk0,               // testing clock (100 MHz)
    input   clk1,               // backup clock (50 MHz)
    output  good                // clk0 is good
);
    reg         pulse = 0;
    reg [31:0]  clk1_cnt = 0;

    always @(posedge clk1) begin
        if (clk1_cnt < RATE) begin
            clk1_cnt <= clk1_cnt + 1;
        end else begin
            clk1_cnt <= 0;
            pulse <= 1;
        end
    end

    reg         present = 0;
    reg [31:0]  clk0_cnt = 0;
    integer k;
   
    always @(posedge clk0) begin
        // count clk0 edges and assign result
        if (!pulse) begin
            clk0_cnt <= clk0_cnt + 1;
        end else begin
            clk0_cnt <= 0;
            pulse <= 0;
            present <= clk0_cnt >= N;
        end
    end
    assign good = present;

endmodule

Ok, this is one approach, though it has a large set of regs.
Remember that CLK1 is guaranteed, not CLK0 and you want to avoid cross domain communications except for 1 async wire.  I would try something like this:

Code: [Select]
module clock_detect (
    input   clk0,               // testing clock (100 MHz)
    input   clk1,               // backup clock (50 MHz)
    output  good                // clk0 is good
);

// Step 1, make a new 12.5Mhz clock from the unstable CLK0 source
    reg [3:0]  clk0_div = 4'd0;
    wire       clk0_12m = clk0_div[3]; // clk0_12m will cycle at 12.5MHz if CLK0 has a 100MHz clock.
    always @(posedge clk0) clk0_div <= clk0_div  + 1'b1;

// Step 2, on our guaranteed 50MHz clk1, verify that clk0_12m is running at least at 12MHz to
// generate a good signal.  This way, we are only analyzing a single wire clk0_12m across a clock domain
// with no async feedback or reset.

reg [1:0] clk0_12m_tr = 2'd0;  // A serial shift in register storing clk0_12m from clk0 domain.
wire      clk0_toggle_bit = clk0_12m_tr[0] ^ clk0_12m_tr[1] ; // Detect a high or low transition of the clk0_12m.
reg [9:0] clk0_toggle_reg =10'd0; // A serial storage of a train of clk0_toggle_bits.

    always @(posedge clk1) begin

               clk0_12m_tr     <= {clk0_12m_tr[0], clk0_12m }; // serial shift in the clk_12m from the other clock domain
               clk0_toggle_reg <= {clk0_toggle_reg[8:0], clk0_toggle_bit} ; // serial shift in the toggle detection

 // If enough 12.5Mhz toggles have been summed up in 10 x 50MHz clocks, set the output to GOOD.
              outgood          <= (clk0_toggle_reg[0] +
                                   clk0_toggle_reg[1] +
                                   clk0_toggle_reg[2] +
                                   clk0_toggle_reg[3] +
                                   clk0_toggle_reg[4] +
                                   clk0_toggle_reg[5] +
                                   clk0_toggle_reg[6] +
                                   clk0_toggle_reg[7] +
                                   clk0_toggle_reg[8] +
                                   clk0_toggle_reg[9]) > 2 ;
    end
endmodule

You may adjust my concept, like changing the 'clk0_toggle_reg' into an authentic counter and time how long it takes that counter to reach for example a count of 100 before making the outgood reg.  All timing and checks can now be done in the clk1 domain without sending anything back to the clk0 domain.
« Last Edit: June 11, 2022, 09:26:52 pm by BrianHG »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf