reliable clock detect and reset signal generation in verilog - Page 1
Forum section: electronics / general

author: radiolistener
author_rank: Super Contributor
timestamp: June 11, 2022, 06:12:09 am
content: I have two clocks: clk1 and clk2.clk1 - has high frequency (about 100 MHz) but sometimes connected/disconnected and is unstable during switch transitionclk2 - 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?

author: BrianHG
author_rank: Super Contributor
timestamp: June 11, 2022, 06:22:29 am
content: 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.

author: Someone
author_rank: Super Contributor
timestamp: June 11, 2022, 06:26:20 am
content: Quote from: radiolistener on June 11, 2022, 06:12:09 amVerilog 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...

author: fourfathom
author_rank: Super Contributor
timestamp: June 11, 2022, 06:56:29 am
content: Quote from: Someone on June 11, 2022, 06:26:20 amIt'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.

author: radiolistener
author_rank: Super Contributor
timestamp: June 11, 2022, 07:35:43 am
content: 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: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) beginif (clk1_cnt < RATE) beginclk1_cnt <= clk1_cnt + 1;end else beginclk1_cnt <= 0;pulse <= 1;endendreg present = 0;reg [31:0] clk0_cnt = 0;integer k;always @(posedge clk0) begin// count clk0 edges and assign resultif (!pulse) beginclk0_cnt <= clk0_cnt + 1;end else beginclk0_cnt <= 0;pulse <= 0;present <= clk0_cnt >= N;endendassign good = present;endmodule

author: radiolistener
author_rank: Super Contributor
timestamp: June 11, 2022, 10:43:44 am
content: Quote from: fourfathom on June 11, 2022, 06:56:29 amjust tried this, here is my code:Code: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 domainwire 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 edgesshift <= {shift[1:0], clk0div_synced};// set result at RATE periodif (clk1_cnt < RATE) beginclk1_cnt <= clk1_cnt + 1;// count edgesif (shift ^ shift) beginclk0_cnt <= clk0_cnt+1;endend else beginclk0_cnt <= 0;clk1_cnt <= 0;present <= clk0_cnt >= N / 10;endend// transfer result to clk0 domainsync_reset #(.N(4)) sync_reset_i (.clk(clk0),.rst(~present),.out(rst));endmodulemodule clk_div #(parameter WIDTH = 3, // Width of the register requiredparameter 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)beginif (reset)beginr_reg <= 0;clk_track <= 1'b0;endelse if (r_nxt == N)beginr_reg <= 0;clk_track <= ~clk_track;endelser_reg <= r_nxt;endassign r_nxt = r_reg+1;assign clk_out = clk_track;endmodulemodule sync_signal #(parameter WIDTH=1, // width of the input and output signalsparameter 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) beginsync_reg <= in;for (k = 1; k < N; k = k + 1) beginsync_reg <= sync_reg[k-1];endendendmodulemodule 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) beginif (rst) beginsync_reg <= {N{1'b1}};end else beginsync_reg <= {sync_reg[N-2:0], 1'b0};endendendmoduleis it correct?

author: fourfathom
author_rank: Super Contributor
timestamp: June 11, 2022, 05:56:01 pm
content: Quote from: radiolistener on June 11, 2022, 10:43:44 amI 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

author: BrianHG
author_rank: Super Contributor
timestamp: June 11, 2022, 08:59:32 pm
content: Quote from: radiolistener on June 11, 2022, 07:35:43 amOk, 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: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 sourcereg [3:0] clk0_div = 4'd0;wire clk0_12m = clk0_div; // 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 ^ clk0_12m_tr ; // 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) beginclk0_12m_tr <= {clk0_12m_tr, clk0_12m }; // serial shift in the clk_12m from the other clock domainclk0_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 +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg +clk0_toggle_reg) > 2 ;endendmoduleYou 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.