3
\$\begingroup\$

I'm using an FPGA to sample a serial data stream (happens to be PCM audio in this case). Basically, there are two signals:

  • Bit clock: a basic clock signal (square wave)
  • Data: the bit to be read is present on the rising edge of the clock

Currently, I have an asynchronous edge detection on the bit clock. So basically, a flag will go high for one clock cycle when the bit clock is on the rising edge. My approach was to wait for this flag. When the flag goes high, I take the value of the data line and put it in an index of my buffer. However, I don't think this is working correctly. Can I not just sample the hardware value of the data line? Do I need to buffer it? Here is my code for reference:

always @(posedge clk) begin // posedge of system clock

    if(bclk_rise == 1) begin // Rising edge on BCLK detected, sample the bit and place it in the correct buffer index

        audio_data[data_count] <= data; // 'data' is the wire type that's connected to the ADC data line

        data_count <= data_count + 1; // 5-bit value, gets reset to zero every 32 loops

    end

end
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

You have two requirements here:

  1. You need to make sure you sample that data at the correct time according to the bus needs.
  2. You need to synchronize this data into your system clock domain.

There are a couple of ways to meet these needs.

First, if the bus clock is slow enough below your system clock, then you can synchronize the bus clock to your clock domain with a double flop. Then use a simple edge detector to determine when the rising edge is. This is used to safely sample the data line of the bus. Note that this achieves #2 automatically. Also, as noted here, this is the preferred way to do this in an FPGA.

This method has a drawback in that the data is sampled somewhere between two and three system clocks later than the actual 'on the pin' bus clock edge. If this is too long (due to your system clock not being fast enough in comparison), you have to go an alternative way.

In this method, you sample before synchronizing to the system clock domain. The reason is to make sure you are sampling at the right time according to the bus.

always @(posedge bclk) begin  // positive edge of bus clock
    sampled_data <= data;
end

At this point, you have sampled_data which is a signal in the bclk domain. You need to synchronize it to your system clock domain. To do this, you have to use handshaking or a FIFO.

One way that works is to do the shift register in the bus clock domain to get to parallel data. Then pass it through a dual clock FIFO to the other domain. FPGAs have primitives just for this use.

// Latch the data in the bclk domain
always @(posedge bclk) begin
    fifo_d <= {fifo_d[30:0], data};

    data_count <= data_count + 1;
    if (data_count == 31)
        fifo_wr <= 1'b1;  // This will latch fifo_d to the dual clock fifo input
    else
        fifo_wr <= 1'b0;
end

// Read the data out of the FIFO in the system clock domain.
always @(posedge clk) begin
    if (fifo_ready)
        synchronized_data <= fifo_q;   // Now the data is in your domain.
end

Other notes:

As with any I/O at the edge of the FPGA as well as between clock domains, you will need to correctly define the timing constraints. Do describe the details is a bit too general for this forum, but as recommended in the comments by Greg, this paper is a good source for understanding the needs for the clock domain crossing. The FPGA vendors tend to have decent write ups for input delay and output delay definitions as well.

\$\endgroup\$
5
  • \$\begingroup\$ I was told by this article that I shouldn't use posedge bclk due to some asynchronous stuff (I don't fully understand it): doulos.com/knowhow/fpga/synchronisation \$\endgroup\$
    – crocboy
    Jan 15, 2015 at 13:34
  • \$\begingroup\$ Everything he says is true, except he doesn't note that it won't work if your bus clock is close to the same speed as your system clock. His rise/fall lines are delayed at least 2 system clocks from the actual wire. If that is beyond your window to latch, then it won't work, so you have to do more along what I said. I hinted at that in the answer, but let me update to be more clear. \$\endgroup\$
    – caveman
    Jan 15, 2015 at 16:37
  • \$\begingroup\$ The main problem he is posing, btw, has more to do with not wanting to use limited FPGA clocking resources for this purpose. But if you simply can't get it done another way (or if you got clock buffers to spare), you get better timing by using a posedge bclk \$\endgroup\$
    – caveman
    Jan 15, 2015 at 16:48
  • \$\begingroup\$ If I wanted to use posedge bclk, wouldn't I have to do something special in my code/constraints file to make it a clocked input? \$\endgroup\$
    – crocboy
    Jan 15, 2015 at 17:30
  • 2
    \$\begingroup\$ I'll add a hint. You need to look into passing data between clock domains. This is called Clock Domain Crossing (CDC). I recommend this paper. \$\endgroup\$
    – Greg
    Jan 15, 2015 at 17:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.