Reliable Design With Multiple Clock Domains: Ed Czeck, Ravi Nanavati and Joe Stoy Bluespec Inc. Waltham MA 02451, USA
Reliable Design With Multiple Clock Domains: Ed Czeck, Ravi Nanavati and Joe Stoy Bluespec Inc. Waltham MA 02451, USA
Reliable Design With Multiple Clock Domains: Ed Czeck, Ravi Nanavati and Joe Stoy Bluespec Inc. Waltham MA 02451, USA
1. Introduction
Hardware designs these days typically make use of several clocks. This is partly to save power (by gating the
clock to a part of the circuit temporarily not in use, and by
ensuring that parts of the design are not run unnecessarily
fast, both of which reduce the designs dynamic power
consumption), and also to allow the design to communicate with parts of the external environment running asynchronously. Moreover, designs are increasingly becoming
systems on a chip (SoCs); this design methodology
brings together various blocks (IPs), possibly from difference vendors, and often each block has its own clocking
requirement. These different requirements may arise simply because each block was designed independently; but it
might be because different blocks are constrained by different standards (for example for external buses, or audio or
video I/O), each with its own clocking regime.
Where signals cross clock domain boundaries, the normal design conventions of digital logic break down. Special precautions must be taken to ensure that the signals
get across correctly; and care must be taken that there are
no accidental crossings which neglect such precautions. A
good hardware description language, particularly one which
claims to operate at a high level of abstraction, should have
features which make it natural for the designer to construct
circuits which correctly observe these constraints.
Principles
(* synthesize *)
module mkTransmitter(Transmitter#(24,81));
let controller
<- mkController;
let scrambler
<- mkScrambler_48;
let conv_encoder <- mkConvEncoder_24_48;
let interleaver <- mkInterleaver;
let mapper
<- mkMapper_48_64;
let ifft
<- mkIFFT_Pipe;
let cyc_extender <- mkCyclicExtender;
rule controller2scrambler;
connect(controller.getData, scrambler.fromControl);
endrule
...
endmodule
controller
<- mkController;
scrambler
<- mkScrambler_48;
conv_encoder <- mkConvEncoder_24_48;
interleaver <- mkInterleaver;
mapper
<- mkMapper_48_64;
ifft
<- mkIFFT_Pipe
(clocked_by fastClk, reset_by fastReset);
let cyc_extender <- mkCyclicExtender
(clocked_by outClk, reset_by outReset);
rule controller2scrambler;
connect(controller.getData, scrambler.fromControl);
endrule
...
endmodule
5. Other Features
5.1. First-class Properties of Clocks
We have stated that values of the Clock type enjoy most
of the privileges of first-class objects; and we have seen
them used as arguments to modules. This section illustrates
more such capabilities.
Selection of Clocks Let us suppose that for our mkTransmitter design there is a Boolean global variable oneClock,
to specify whether the design should use multiple clocks:
if it is True at compilation time, all the sub-modules are to
be clocked by the surrounding modules default clock, and
the extra clocks are to be ignored. The relevant code for
this is shown in Fig. 6. The IFFT and final sub-modules are
clocked either by the current clock or by the special clocks
supplied, depending on the setting of the Boolean. The
same setting also determines whether to instantiate the synchronizers (only if necessary) and which rules to use to connect things up. Note that the clock selection is necessary
the tool will not allow synchronizers to be omitted between
two different clocks supplied from outside.
This way of choosing clocks, by ordinary conditional expressions, is available only at compile time. It is possible to
choose between clocks in the generated hardware (for example, to allow a particular sub-modules speed, and hence
power consumption, to be varied dynamically), but this requires the use of a special primitive, mkClockMux, in case
the downstream flow requires special provisions for multiplexing within clock trees.
Testing for Equality The reader may have noticed the test
ifftClk==currentClock in Fig. 6the designer has chosen
to insert a test to check for confusion in the clock-handling
part of the design. It illustrates that two clocks may be tested
for equality (though this feature, like the previous one, is
available only at compile time).
The clockOf Function Clocks, like other first-class objects, may also be returned as the results of functions. A particularly useful example of this is the polymorphic clockOf
function, which may be applied to an expression of any
type, and returns the clock which should be used to sample its values. If the expression is an unclocked constant,
the special value noClock is returned. The value of this
function is always well-definedexpressions for which this
would not be so involve mixing values from several clock
domains, and would be illegal.
An example of the use of this function may be seen in
Fig. 9, below.
Figure 9. The
Implementation
Linguistic
Approach
rule ifft2cyclicExtender;
connect(ifftOut, cyc_extender.fromIFFT);
endrule
Simulator Clocks
Acknowledgments
References
[1] Atrenta, Inc. SpyGlass Predictive Analyzer User Guide,
2005. www.atrenta.com.
[2] P. E. Black. Gray code. In P. E. Black, editor, Dictionary of
Algorithms and Data Structures [online]. U.S. National In-
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
A. Bluespec SystemVerilog
Previous attempts at behavioral synthesis have tried to
optimize along three axes simultaneously: choice of microarchitecture, allocation of resources, and scheduling of concurrent operations. Doing all this together, however, is computationally hard. Besides, designers are good at evaluating micro-architectures, and like to be in control of resource allocation; handling concurrency, however, often becomes excessively complex. The Bluespec tool takes over
this task, while leaving the designer in control of the other
two. The result is a flexible tool, with which it is easy to do
architectural experiments, while producing RTL of comparable quality to hand-crafted Verilog.
The HDL for the Bluespec tool is Bluespec SystemVerilog (BSV). This is a variant of SystemVerilog [14] in which
behavior is specified, not by the usual always-blocks, but
by design assertions also known as rules. A rule consists of a condition and an action part. Only if the condition
is satisfied may the state transition specified by the action
part be executed. Each action executes in a single clock cycle. The tool generates scheduling logic which executes as
A.1. ExampleFactorial
Figure 12 shows a simple complete BSV design, containing a module mkFact for computing the factorial function,
and a testbench module mkTestFact for exercising it.
The interface provided by mkFact is of type NumFn; it
consists of a method start to initiate a calculation, and a
module mkFact(CLK,
RST_N,
start_x,
EN_start,
RDY_start,
result,
RDY_result);
input CLK;
input RST_N;
package Factorial;
typedef UInt#(32) Nat;
interface NumFn;
method Action start(Nat x);
method Nat result;
endinterface
(* synthesize *)
module mkFact(NumFn ifc);
Reg#(Nat) n <- mkReg(0);
Reg#(Nat) a <- mkRegU;
rule calc (n!=0);
a <= a * n;
n <= n - 1;
endrule
method Action start(x) if (n==0);
a <= 1;
n <= x;
endmethod
method result if (n==0);
return a;
endmethod
endmodule
(* synthesize *)
module mkTestFact(Empty);
NumFn fact <- mkFact;
Reg#(UInt#(2)) state <- mkReg(0);
rule start_test (state==0);
state <= 1;
fact.start(7);
endrule
rule show_result (state==1);
state <= 2;
$display("%d", fact.result);
endrule
rule end_test (state==2);
$finish(0);
endrule
endmodule
endpackage
amalgamating the actions of the rule and the method; all the
assignments are executed simultaneously.
Notice that in this very simple design, all the rules are
mutually exclusiveat most one is enabled at any one time.
There is therefore no possibility of conflict, and the scheduling is trivial: each rule may fire whenever it is enabled. In
general, however, many non-conflicting rules may fire during any one cycle.