6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 5:42 am

All times are UTC




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: Clock syncronization
PostPosted: Wed Jul 04, 2018 7:55 pm 
Offline

Joined: Wed Mar 02, 2016 12:00 pm
Posts: 343
Hi

I had to work a lot to get clock synchronization working in a CPLD, so I thought I should share my experience.

What I have been doing is to use an external clock pulse and synchronizing this to an internal clock with a counter.

During the external clock pulse (an 1MHz clock), the internal clock (running at 55MHz) is used to trigger an increase to a counter. This counter increases by 1 for each internal clock pulse so that it counts to around 54 before being reset back to 0 (by the external clock having a negative edge).

Originally I thought this was going to be an easy exercise, but experience has proven me blue eyed (which I am) and naive.

The original code looked something like this:

Code:
always @(posedge fpga_clk)
   begin
      counting <= counting + 8'd1;
      if (counting==8'd56)      // Overflow, so return to zero
      begin
         counting <= 8'd0;
      end
      if (count_reset!=count_check)
      begin
         counting <= 8'd0;
      end
      count_check=count_reset;
   end

always @(negedge clk)
   begin
      count_reset=~count_reset;
   end

(some init code and variable declaration has been removed)

This code seems to work, but it doesn't. There are several problems with running this code, and it is not that I originally used blocking statements in the always block (which many also say that can generate problems in its own).

The problem with this code is that it is unstable. In 99.9% of the time, the "counting" variable goes back to zero during the clk negative edge (that generates a reset), but once in a while it will either miss the reset condition or it will reset to some arbitrary value (!). The reason for this is that the reset condition (count_reset=~count_reset) can happen just before the reset clearing (count_check=count_reset) so that we miss the reset altogether.

Even stranger is that this situation can endure even if one put the reset clearing (count_check=count_reset) inside the last IF statement.

Anyway, the solution to this involves using a two-stage reset. E.g. start with a reset (count_reset=~count_reset), then propagate that into a binary reset counter that counts two stages:

Code:
always @(posedge fpga_clk)
   begin
      if (count_reset==2'b10 || count_reset==2'b01)
         counting <= 8'b00000000;         // This is held for two cycles to ensure zeroing
      else counting <= counting + 8'b00000001;
      count_reset[1]<=count_reset[0];
   end

always @(negedge fpga_clk)
   begin
      if (old_count_reset!=init_count_reset)
      begin
         old_count_reset<=init_count_reset;
         count_reset[0]<=1'b1;            // Start counter reset (two cycles)
      end
      else
         count_reset[0]<=1'b0;
   end

always @(negedge clk)
   begin
      init_count_reset<=~init_count_reset;
   end

count_reset is here a two-bit value.

Now, this doesn't work either. And the reason for that was the external 1MHz clk which transits from 1 to 0 (and back again) does so in 20+ ns. That is apparently enough to confuse the (negedge clk) statement (probably since the transition can look like a high impedance state without being 0 or 1.. at least that is my theory).
The solution to this was again to work with a two-stage process to ensure that two stable values had been reached. So I changed the "always @(negedge clk)" into something else:

Code:
always @(posedge fpga_clk)
   begin
      if (oclk==2'b10)            // Look for clk2 edge to zero (PS: only way to ensure proper detection
      begin
         init_count_reset <= ~init_count_reset;      // Reset counter
      end
      oclk<={oclk[0],clk};
   end

In which oclk is a two-bit variable.

At last this code works. E.g its stable and generates a counting variable that goes from 0 to 54 and back to 0. It does so 100% of the time without missing anything.

Now, if someone else here has experienced the same troubles with external clocks and counters, it would be nice to hear their experience. :wink:


Top
 Profile  
Reply with quote  
 Post subject: Re: Clock syncronization
PostPosted: Tue Jul 10, 2018 6:12 pm 
Offline

Joined: Wed Apr 27, 2016 2:15 pm
Posts: 141
Location: South Africa
I can actually assosiate with the kind of pain you went through with the use of two clock frequencies in your system.

Recently I wanted to make a VGA implementation that displays a frame stored in SDRAM.

The catch here was that the pixel clock was running at 85.4MHz and the memory subsystem delivered the frame data at a rate of 100Mhz.

I also had weird scenarios where signals triggered when it shouldn't. I eventally solved the issues, but still cannot explain exactly why I saw the weird behaviour in the first place.

There is actually a lot of theory available on the Internet regarding the weird behaviour you and I have seen in our designs.

The term is cross clock domain issues and goes hand in hand with setup and hold violations that happens when you pass information between different clock domains.

The fix you did with the double flip-flop is actually a very common remedy all these theoretical sources suggest called a back-to-back flip-flop synchroniser.

It even gets more tricky when you need to pass xounters between two clock domains.

Anyway, I am glad you solved your problem ☺


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: