Hi everyone, I'm supposed to implement a serializer where the parallel data and clock arrive from outside the chip and I have an 8x internal clock which is frequency locked to the incoming clock but the phase relationship between internal fast clock and external slow clock is unknown (but they are frequency locked ie the phase difference between the edges is not changing). Another constraint is that I have to do this with minimum latency so I can't use a synchronous fifo to gearshift. And unfortunately I don't have a PLL or DLL to create a zero-delay clock either. So what I am doing is an oversampling phase lock. I have a counter running at the fast clock and I sample the incoming clock with the fast clock 3 times and try to find the positive edge using the last two samples and I record the count when I see the edge and go back in the count value to sample the data as early as possible. If I account for the uncertainty of sampling this works OK. The issue is that if I am sampling at close to transition of incoming clock, the phase I will detect will change one tap occasionally (even maybe frequently; I'm assuming I'm not hitting a meta-stable case here). To prevent this I'm considering adding a small delay line myself (I have cells which have relatively large delay independent of PVT) and if I detect the phase changing, adding/subtracting one delay element to get stable sampling. Does this sound like a workable solution just for finding the phase of the clock? For data I'll implement the same delay line and change the sampling location similarly. Any suggestions are welcome.
oversampling serializer?
Jul 10, 2008
4 Replies
Thanks for giving me the excuse to tidy up this thing, which I've used in similar situations. Its latency is approximately half a cycle of the source domain clock, which should be OK.
It absolutely assumes that the phase offset between source clock and destination 8x-oversampling clock is fixed, but makes no assumptions about the value of that phase offset. It should tolerate phase jitter of about +/- a quarter-cycle of the source clock.
You can think of it as a 2-deep FIFO whose read and write pointers stay in lockstep, or a ping-pong buffer. Either way, it works for me.
~~~~~~~~~~~~~~~~~~~~~~~~ begin code ~~~~~~~~~~~~~~~~~~~~~
module isochronous_x8 (
// resynchronizes between a source clock and // a destination clock that is known to be running // at exactly 8x the source frequency, with an // unknown but fixed phase offset
// INITIALISATION input asynch_init,
// SOURCE DOMAIN input source_clock, input [7:0] source_data,
// DESTINATION DOMAIN input dest_x8_clock, output reg [7:0] dest_data, output reg dest_strobe, output reg locked );
reg wrA; // Chooses which buffer slot to write reg [7:0] d0; // buffer slots reg [7:0] d1;
// Source domain logic: // write into the two buffer slots alternately // always @(posedge source_clock or posedge asynch_init) if (asynch_init) begin wrA
Muzaffer, it looks like you asked exactly the right questi> Thanks for giving me the excuse to tidy up this thing, which I've
A very clever and complete verilog tutorial, and the rtl viewer does a good job with it.
I often find it easier to write code than read it, but this an uncommon exception.
It did 292 MHz on the 8x clk on a stratix2 without touching a thing. Thanks for taking the time to post it.
-- Mike Treseler
[...]
AAAAAARGHHH.
Rule #1 of Usenet: Errors will be detected 30 seconds after hitting send.
The code I provided was adapted from something else and, although it works correctly, it has a lot of redundant logic. There is no need for the two ping-pong buffers; a single buffer is enough. The double buffer trick was used in a system where I didn't have the luxury of oversampling.
Here's a corrected version, and a testbench. The logic's simpler, so it should please Mike by going even faster :-)
Apologies.
~~~~~~~~~~~~~~~~~~~~ begin device code ~~~~~~~~~~~~~~~~~ module mesochronous_x8 (
// resynchronizes between a source clock and // a destination clock that is known to be running // at exactly 8x the source frequency, with an // unknown but fixed phase offset
// INITIALISATION input asynch_init,
// SOURCE DOMAIN input source_clock, input [7:0] source_data,
// DESTINATION DOMAIN input dest_x8_clock, output reg [7:0] dest_data, output reg dest_strobe, output reg locked );
reg c2; // Divide clock by 2 reg [7:0] d; // buffer
// Source domain logic: // write into the two buffer slots alternately // always @(posedge source_clock or posedge asynch_init) if (asynch_init) begin c2
But knowing Rule #1 does not affect the applicability of Rule #1. There is no other path to enlightenment :)
Thanks, I'll hang on to both.
Which completes the verilog tutorial. Thanks.
Very nice. 490.44 MHz 9 luts, 30 regs.
-- Mike Treseler
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required