Find edges of a 20MHz clock generated with the clk_wiz (CDC) problem

I would appreciate some help with how to find the rising and falling edge of a 20MHz clock generated with the clk_wiz available within Vivado, FPGA is running at 100MHz. I need this 20 MHz clock to drive a dual ADC that uses the clock to (a) clock in the ADC data which is sampled simultaneously, and (b) transfer this ADC sample pair sequentially on a single ADC bus using the leading and trailing edges.

I tried using the 20MHz_clk as an internal signal or ‘variable’. As a result I get the following error:

  • “Project built but failed to meet timing.”

I was able to eliminate the error by routing the 20MHz internal clock signal to an OUTPUT pin, and then jumpering that signal to an INPUT pin into the FPGA.

I would like to do this without the external jumper.

The following code is working; however it is using the jumper method.

**********************************************************

output clk_ADC, //ouput from clk_wiz below

input read_20Mhz_in, //pin jumpered to above pin

input adc[14],

etc

clk_wiz_0 clk_wiz(.reset(rst_n), .clk_in1(clk))

always {

clk_ADC = clk_wiz.clk_20MHz //clk_ADC is an external output

fclk = clk_wiz.clk_100MHz //internal generated main clk

}

dff last_inp(.clk(fclk))

   last_inp.d = read_20Mhz_in  //read_20Mhz_in

    //look for rising edge

   if (read_20Mhz_in == 1 && last_inp.q == 0) { //read_20Mhz_in             

        tx_out_flag = 0;

        hi_pin = 1 //used to check timing with scope

        //read_ADC firts chan            

        usb.d\[29:16\] = adc                

    }

    

    if (read_20Mhz_in == 0 && last_inp.q == 1) { //read_20Mhz_in 

        tx_out_flag = 1 // set output flag

        lo_pin = 1 //used to check timing with scope

        //read_ADC second chan but tesing with a counter to look for missing data transfer.

        usb.d\[15:0\] = test_cntr.q                     

    }

***************************************************

This in then output to the FT+ board and a computer as continuous stream.

I tried to condition the 20_Mhz clock as follows:

dff sync1_20MHz(.clk(fclk)) //metasability 1

dff synced_20MHz(.clk(fclk)) //metasability 2

sync1_20MHz.d = clk_20mhz_int

synced_20MHz.d = sync1_20MHz.q

then use this code below which causes the Vivado error.

if (synced_20MHz.q == 1 && sync1_20MHz.q == 0) {

etc.

but after a LOT of AI questions on Google and messing around, no luck.

I looked at the Alchitry note on this,

FPGA Timing

FPGA Timing

Justin Rajewski

however, it references the Lattice compiler which don’t have – updating the app note for Vivado and V2 would be helpful.

I am new to FPGA and it has been a very steep learning curve.

Any help anyone can provide will be much appreciated.

If you’re project fails to meet timing and your solution is to route it externally, you’re just making the problem worse. The reason it doesn’t complain is because the timing of the external input is unknown so it can’t tell you that you will be violating timing even though you will be.

You should generate both a 100MHz and a 20MHz clock from the clock wizard and make sure they’re phase aligned (I think this is the default). Since they should line up nicely, you don’t need to worry about crossing clock domains. The tools should take care of that.

To find the rising edge just pipe the clock to the data signal of a dff that is clocked off the 100MHz. Use that to compare with the current value and if the dff is 0 but the current is 1 then you have a rising edge.

Here’s a quick demo.

    clk_wiz_0 clk_wiz(.reset(~rst_n), .clk_in1(clk))
    sig rst = !clk_wiz.locked

    .clk(clk_wiz.clk_100) {
        .rst(rst) {
            dff last_clk_20
        }
    }

    always {
        last_clk_20.d = clk_wiz.clk_20
        
        if (clk_wiz.clk_20 && !last_clk_20.q) { // rising edge
            // read date here
        }
    }

EDIT: This actually doesn’t work. You can’t feed clk_20 into the data of the dff clocked off of clk_100 because they’re phase aligned and the data and clock will change at the same time. This WILL violate timing.

This problem is made difficult because 20MHz is not an even multiple of 100MHz so you can’t just use a counter to divide it (unless you don’t care about a 50% duty cycle).

Here’s my updated solution. Instead of generating a 20MHz clock, I generated a 40MHz clock that can then be divided in 2 by a dff and that can be fed to the 100MHz domain without timing violations. This effectively adds a small phase shift to the 20MHz clock which is just enough to satisfy timing.

    clk_wiz_0 clk_wiz(.reset(~rst_n), .clk_in1(clk))
    sig rst = !clk_wiz.locked

    dff clk_20(.clk(clk_wiz.clk_40), .rst(rst))
    
    .clk(clk_wiz.clk_100) {
        .rst(rst) {
            dff last_clk_20
        }
    }

    always {
        clk_20.d = ~clk_20.q // invert every cycle
        last_clk_20.d = clk_20.q
        
        if (clk_20.q && !last_clk_20.q) { // rising edge
            // read data here
        }
    }

Thanks, I was about to say I still got the timing error.

The 50% duty cycle is very important for the ADC but phase with the 100MHz is not.

Will try that now.

Martin