Modern digital logic devices are usually(*) designed with "synchronous design practice": a globally synchronous edge-triggered register-transfer design style (RTL):
All sequential circuits are broken up into edge-triggered registers connected to the global clock signal CLK and pure combinational logic.
That design style allows people to quickly design digital logic systems without regard to timing. Their system will "just work" as long as there is enough time from one clock edge to the next for the internal state to settle.
With this design style, clock skew and other timing-related issues are irrelevant, except for figuring out "What's the maximum clock rate for this system?".
What exactly is clock skew?
For example:
...
R1 - register 1 R3
+-+
->| |------>( combinational ) +-+
...->| |------>( logic )->| |--...
->|^|------>( )->|^|
+-+ ( ) +-+
| +--->( ) |
CLK | +->( ) CLK
| |
R2: | |
+-+ | |
...->| |->+ |
->|^|->--+
+-+
|
CLK
In real hardware, the "CLK" signal never really switches exactly simultaneously at every register.
The clock skew Tskew is the delay of the downstream clock relative to the upstream clock (a):
Tskew(source, destination) = destination_time - source_time
where source_time is the time of an active clock edge at the upstream source register (in this case, R1 or R2), and destination_time is the time of "the same" active clock edge at some downstream destination register (in this case, R3).
- negative clock skew: the CLK at R3 switches before the clock at R1.
- positive clock skew: the CLK at R3 switches after the clock at R1.
What is the effect of clock skew?
(perhaps a timing diagram here would make this clearer)
For things to work properly, even in the worst case, R3's inputs must not change during R3's setup time or hold time.
In other worse, for things to work properly, we must design things such that:
Tskew(R1, R3) < Tco - Th.
Tclk_min = Tco + Tcalc + Tsu - Tskew(R1, R3).
where:
- Tcalc is the maximum worst-case settling time of any block of combinational logic anywhere in the system. (Sometimes we can re-design the block of combinational logic that is on the critical path, pushing parts upstream or downstream, or inserting another stage of pipelining, so the new design has a smaller Tcalc, which allows us to increase the clock rate).
- Tclk_min is the minimum time period from one active clock edge to the next active clock edge. We calculate it from the above equation.
- Tsu is the register setup time. The register manufacturer expects us to use a clock slow enough to always meet this requirement.
- Th is the register hold time. The register manufacturer expects us to control clock skew enough to always meet this requirement.
- Tco is the clock-to-output delay (propagation time). After each active clock edge, R1 and R2 continue to drive the old values to the combinational logic for a short time Tco before switching to the new values. This is set by the hardware and guaranteed by the manufacturer, but only as long as we meet the Tsu and Th and other requirements the manufacturer specifies for normal operation.
Too much positive skew is an unmitigated disaster. Too much positive skew can (with some data combinations) cause "sneak paths" such that, instead of R3 latching the "correct data" at clock N+1 (a deterministic function of the data previously latched into R1 and R2 at clock N), the new data latched into R1 and R2 at clock N+1 can leak through, upset the combinational logic, and cause wrong data to be latched into R3 at "the same" clock edge N+1.
Any amount of negative skew can be "fixed" by slowing down the clock rate.
It is only "bad" in the sense that it forces us to run the system at a slower clock rate,
in order to give the inputs of R3 time to settle after R1 and R2 latch new data at clock edge N, and then later R3 latches the result at "the next" clock edge N+1.
Many systems use a clock distribution network that tries to reduce the skew to zero.
Counter-intuitively, by carefully adding delays along the clock path -- the path from the clock generator to each register's CLK input -- it is possible to increase the apparent speed that the clock-edge wavefront physically travels from one register's CLK input to the next register's CLK input to faster than the speed of light.
The Altera documentation mentions
"Avoid using combinational logic in clock paths because it contributes to clock skew."
This is referring to the fact that
many people write HDL that gets compiled onto a FPGA in a way that somehow causes something other than the global CLK signal to drive the local CLK input of some registers.
(This may be "clock gating" logic so that new values are loaded into a register only when certain conditions are met; or "clock divider" logic that only let 1 out of N clocks through, or etc).
That local CLK is usually derived from the global CLK somehow -- the global CLK ticks, and then either the local CLK doesn't change, or (a short delay after the global CLK for the signal to propagate through that "something other") the local CLK changes once.
When that "something other" drives the CLK of the downstream register (R3), it makes the skew more positive.
When that "something other" drives the CLK of the upstream register (R1 or R2), it makes the skew more negative.
Occasionally, whatever drives the CLK of the upstream register and whatever drives the CLK of the downstream register have practically the same delay, making the skew between them practically zero.
The clock distribution network inside some ASICs is deliberately designed with small amounts of positive clock skew on some registers, which gives the combinational logic upstream slightly more time to settle and so the entire system can be run at a faster clock rate.
This is called "clock skew optimization" or "clock skew scheduling", and is related to "retiming".
I'm still mystified by the set_clock_uncertainty
command -- why would I ever want to "manually specify" skew?
(*) One exception:
asynchronous systems.