Crossing clock domains - Signal
From: http://www.fpga4fun.com/CrossClockDomain.html
A signal to another clock domain
Let's say a signal from clkA domain is needed in clkB domain. It needs to be "synchronized" to clkB domain, so we want to build a "synchronizer" design, which takes a signal from clkA domain, and creates a new signal into clkB domain.
In this first design, we assume that the signal-in changes "slowly" compared to both clkA and clkB clock speeds. Typically all you need to do is to use two flip-flops to move the signal from clkA to clkB (to learn why, get back to the links).
1
module
Signal_CrossDomain(
2
3
clkA, SignalIn,
4
5
clkB, SignalOut);
6
7
8
9
//
clkA domain signals
10
11
input
clkA;
12
13
input
SignalIn;
14
15
16
17
//
clkB domain signals
18
19
input
clkB;
20
21
output
SignalOut;
22
23
24
25
//
Now let's transfer SignalIn into the clkB clock domain
26
27
//
We use a two-stages shift-register to synchronize the signal
28
29
reg
[
1
:
0
] SyncA_clkB;
30
31
always
@(
posedge
clkB) SyncA_clkB[
0
]
<=
SignalIn;
//
notice that we use clkB
32
33
always
@(
posedge
clkB) SyncA_clkB[
1
]
<=
SyncA_clkB[
0
];
//
notice that we use clkB
34
35
36
37
assign
SignalOut
=
SyncA_clkB[
1
];
//
new signal synchronized to (=ready to be used in) clkB domain
38
39
endmodule
The two flip-flops have the side-effect of delaying the signal. For example, here are waveforms where you can see the slow moving signal being synchronized (and delayed) to clkB domain by the two flip-flops:
Crossing clock domains - Flag
A flag to another clock domain
If the signal that needs to cross the clock domains is just a pulse (i.e. it lasts just one clock cycle), we call it a "flag". The previous design usually doesn't work (the flag might be missed, or be seen for too long, depending on the ratio of the clocks used).
We still want to use a synchronizer, but one that works for flags.
The trick is to transform the flags into level changes, and then use the two flip-flops technique.
module Flag_CrossDomain(
clkA, FlagIn_clkA,
clkB, FlagOut_clkB);
// clkA domain signals
input clkA, FlagIn_clkA;
// clkB domain signals
input clkB;
output FlagOut_clkB;
reg FlagToggle_clkA;
reg [2:0] SyncA_clkB;
// this changes level when a flag is seen
always @(posedge clkA) if(FlagIn_clkA) FlagToggle_clkA <= ~FlagToggle_clkA;
// which can then be sync-ed to clkB
always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};
// and recreate the flag from the level change
assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);
endmodule
Now if you want the clkA domain to receive an acknowledgment (that clkB received the flag), add a busy signal.
1
module
FlagAck_CrossDomain(
2
3
clkA, FlagIn_clkA, Busy_clkA,
4
5
clkB, FlagOut_clkB);
6
7
8
9
//
clkA domain signals
10
11
input
clkA, FlagIn_clkA;
12
13
output
Busy_clkA;
14
15
16
17
//
clkB domain signals
18
19
input
clkB;
20
21
output
FlagOut_clkB;
22
23
24
25
reg
FlagToggle_clkA;
26
27
reg
[
2
:
0
] SyncA_clkB;
28
29
reg
[
1
:
0
] SyncB_clkA;
30
31
32
33
always
@(
posedge
clkA)
if
(FlagIn_clkA
&
~
Busy_clkA) FlagToggle_clkA
<=
~
FlagToggle_clkA;
34
35
always
@(
posedge
clkB) SyncA_clkB
<=
{SyncA_clkB[1:0], FlagToggle_clkA}
;
36
37
always
@(
posedge
clkA) SyncB_clkA
<=
{SyncB_clkA[0], SyncA_clkB[2]}
;
38
39
40
41
assign
FlagOut_clkB
=
(SyncA_clkB[
2
]
^
SyncA_clkB[
1
]);
42
43
assign
Busy_clkA
=
FlagToggle_clkA
^
SyncB_clkA[
1
];
44
45
endmodule
Crossing clock domains - Task
Getting a task done in another clock domain
If the clkA domain has a task that needs to be completed in the clkB domain, you can use the following design.
Here's one way to do it
1
module
TaskAck_CrossDomain(
2
3
clkA, TaskStart_clkA, TaskBusy_clkA, TaskDone_clkA,
4
5
clkB, TaskStart_clkB, TaskBusy_clkB, TaskDone_clkB);
6
7
8
9
//
clkA domain signals
10
11
input
clkA;
12
13
input
TaskStart_clkA;
14
15
output
TaskBusy_clkA, TaskDone_clkA;
16
17
18
19
//
clkB domain signals
20
21
input
clkB;
22
23
output
TaskBusy_clkB, TaskStart_clkB;
24
25
input
TaskDone_clkB;
26
27
28
29
reg
FlagToggle_clkA, FlagToggle_clkB, Busyhold_clkB;
30
31
reg
[
2
:
0
] SyncA_clkB, SyncB_clkA;
32
33
34
35
always
@(
posedge
clkA)
if
(TaskStart_clkA
&
~
TaskBusy_clkA) FlagToggle_clkA
<=
~
FlagToggle_clkA;
36
37
38
39
always
@(
posedge
clkB) SyncA_clkB
<=
{SyncA_clkB[1:0], FlagToggle_clkA}
;
40
41
assign
TaskStart_clkB
=
(SyncA_clkB[
2
]
^
SyncA_clkB[
1
]);
42
43
assign
TaskBusy_clkB
=
TaskStart_clkB
|
Busyhold_clkB;
44
45
always
@(
posedge
clkB) Busyhold_clkB
<=
~
TaskDone_clkB
&
TaskBusy_clkB;
46
47
always
@(
posedge
clkB)
if
(TaskBusy_clkB
&
TaskDone_clkB) FlagToggle_clkB
<=
FlagToggle_clkA;
48
49
50
51
always
@(
posedge
clkA) SyncB_clkA
<=
{SyncB_clkA[1:0], FlagToggle_clkB}
;
52
53
assign
TaskBusy_clkA
=
FlagToggle_clkA
^
SyncB_clkA[
2
];
54
55
assign
TaskDone_clkA
=
SyncB_clkA[
2
]
^
SyncB_clkA[
1
];
56
57
endmodule
and here's a matching simulation waveform
Crossing clock domains - Data bus
Data bus to another clock domain
To move a data bus (2 bits wide or more) from one clock domain to another, we have several techniques to our disposal.
Here are a few ideas.
- Gray code: If the data bus is a monotonic counter (i.e. only incrementing or decrementing), we can convert it to a gray code, which has the ability to cross clock domains (under certain timing conditions).
- Data freeze: If the data bus is non-monotonic, use a flag to signal the other domain to capture the value (while it is frozen in the source clock domain).
- Data burst: If the data bus has many consecutive values that need to cross the clock domain, use an asynchronous FIFO, where you push values from the source clock domain, and read back values at the other domain.