Frequency counting...


hamster

Recommended Posts

I'm thinking about a new version of my frequency counting project, but this time I am thinking something a little more classy for the front end.

 

I'm plaining on feeding the signal under test to be on a clock input, and to be driving a 5-bit Gray counter (either implemented in logic or a RAM block by feeding DOUT into the ADDR), depending on timing. 

 

In the project's 'normal' clock domain I will then sample this counter, and use the current and previous value to a look up how many steps the counter has advanced (in a 1024 entry table). These steps can then be accumulated and gated as required.

 

Because it will use Gray coding (where at most only one bit changes at a time) I can drag values back from a high speed domain to the low speed one without any clock domain crossing issues.

 

This should allow signals of up to the max input clock speed to be counted. The main logic can be running at 32MHz or less even less.

 

Anybody see any flaws?

 

 

Link to comment
Share on other sites

Even if you use gray coding it is not guaranteed to work 100%.

 

I'd suggest using a input clock buffer with enable signal. When you need to transfer data to the other clock domain, disable the external clock first, let signals settle for a bit, and then transfer the data.

 

This, along with gray encoding, should work well enough.

 

Alvie

Link to comment
Share on other sites

Humm... why would just using gray encoding not work 100%?

 

Say the counter's output goes  ...00100, 00101, 00111...  @ 400MHz, and is presented on registered outputs, and is then captured in registers in the slow domain @ 32MHz, I also want the paths from the counter to the capturing registers be constrianed to 2.5ns.

 

if I capture in the middle of 00101 state, the worst I can have happen is that I get 00100 or 00111 (so either +/- one count), and that gets netted out the next time you capture the counter. If it attempts to capture 'dead on' the 00100 -> 00101 transistion you still get a valid usable value (as long as you synchronise it properly to avoid metastability issues)

 

The key idea is to never reset the gray counter, and to sample it fast enough that it will never 'wrap around' and give misleading deltas. 

Link to comment
Share on other sites

The update rate of the counter is way larger than the internal domain clock. I am not sure you can safely tell that only a bit will be modified during the move to the internal clock domain. Have you tried to apply a 400MHz constraint for the counter, and see what the synthesis tool tells you ?

Link to comment
Share on other sites

Well, I got a first attempt at a design up and running, and the syntheisis says that it will work with the slow domain at 31.25 ns (32 MHz) and the fast domain at 2.7 ns ( 370 MHz) for a -2 part.

 

I am now confused, as it *should* give some timing errors between the two domains - it is the nature of gray encoding that makes it work. I tried other timings and can't get it to fail.

 

I was expecting that I would need to need to use 32 Mhz and  an integer multiple (e.g. 320 or 253 MHz)  for the fast clock, and then to lie about the phase relationship of the two to make it pass timing.

 

But the Static timing report looks correct, with two timing domains and the right logic running at the right speeds. 

 

Humm... next step is to build a UART output and see.if it actually works at all...

 

Mike

Link to comment
Share on other sites

Well, gave it a try and it looks to work.

 

I used the 32 MHz clock, and a test signal of 32*32/7 = 146,285,714.285714.... Hz

 

It is outputting over the UART every second, and this is what I get....

 

....

146,285,714
146,285,714
146,285,715
146,285,714
146,285,714
146,285,715
146,285,714
146,285,714
146,285,714
146,285,715
146,285,714
146,285,714
146,285,715
146,285,714
146,285,714
....
 
Given that it is sampling the gray codes 32M times a second, a one in a million error should be visible. So it is looking pretty good.
 
Now to get a second board out and try it with an external signal!
Link to comment
Share on other sites

  • 2 weeks later...

So, I got a little bit board - watching a disaster mini-series about the local earthquake... yawn...

 

Added this to the top module:

process(test_signal)   begin      if rising_edge(test_signal) then         prescaled <= not prescaled;      end if;   end process;

And then counted "prescaled" instead, with a 2 second gate time (to double the count after being divided by two).

 

Now seems to be good to 1.051ns (950 MHz) - passes with the following timing constraints:

NET clk32 LOC="P94"  | IOSTANDARD=LVTTL | PERIOD=31.25ns;      # CLKNET TX    LOC="P105" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=SLOW; # TXNET test_signal_p  LOC = "P51" | IOSTANDARD = LVDS_33 | PERIOD=1.051ns;NET test_signal_n  LOC = "P50" | IOSTANDARD = LVDS_33;NET "prescaled"  PERIOD=2.102;
Link to comment
Share on other sites

Mike, I don't see any LVDS termination.  Have you tried this?

 

NET clk32 LOC="P94"  | IOSTANDARD=LVTTL | PERIOD=31.25ns;      # CLK
NET TX    LOC="P105" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=SLOW; # TX
NET test_signal_p  LOC = "P51" | IOSTANDARD = LVDS_33 | DIFF_TERM = "TRUE" | PERIOD=1.051ns;
NET test_signal_n  LOC = "P50" | IOSTANDARD = LVDS_33 | DIFF_TERM = "TRUE";
NET "prescaled"  PERIOD=2.102;

Link to comment
Share on other sites

Magnus - Did it in the primitive :-)

  -- Input bufferi_IBUFDS : IBUFDS   generic map (      DIFF_TERM => TRUE,      IBUF_LOW_PWR => TRUE,      IOSTANDARD => "DEFAULT")   port map (      O  => test_signal,      I  => test_signal_p,      IB => test_signal_n   );

@Alvie: I used the technique from page 47 of http://www.origin.xilinx.com/publications/archives/xcell/Xcell32.pdf - a single bit async counter used as a prescaler, as the toggle rate of a CLB is faster than a clock buffer.is rated for.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.