.. role:: raw-html-m2r(raw)
:format: html
.. _clock_domain:
Clock domains
=============
Introduction
------------
In *SpinalHDL*\ , clock and reset signals can be combined to create a **clock domain**. Clock domains could be applied to some area of the design and then all synchronous elements instantiated into this area will then **implicitly** use this clock domain.
Clock domain application work like a stack, which mean, if you are in a given clock domain, you can still apply another clock domain locally.
.. _clock_domain_instantiation:
Instantiation
-------------
The syntax to define a clock domain is as follows (using EBNF syntax):
.. code-block:: scala
ClockDomain(
clock: Bool
[,reset: Bool]
[,softReset: Bool]
[,clockEnable: Bool]
[,frequency: IClockDomainFrequency]
[,config: ClockDomainConfig]
)
This definition takes five parameters:
.. list-table::
:header-rows: 1
:widths: 1 10 1
* - Argument
- Description
- Default
* - ``clock``
- Clock signal that defines the domain
-
* - ``reset``
- Reset signal. If a register which need a reset and his clock domain didn't provide one, an error message happen
- null
* - ``softReset``
- Reset which infer an additional synchronous reset
- null
* - ``clockEnable``
- The goal of this signal is to disable the clock on the whole clock domain without having to manually implement that on each synchronous element
- null
* - ``frequency``
- Allow to specify the frequency of the given clock domain and later get it in your desing
- UnknownFrequency
* - ``config``
- Specify polarity of signals and the nature of the reset
- Current config
An applied example to define a specific clock domain within the design is as follows:
.. code-block:: scala
val coreClock = Bool
val coreReset = Bool
// Define a new clock domain
val coreClockDomain = ClockDomain(coreClock,coreReset)
// Use this domain in an area of the design
val coreArea = new ClockingArea(coreClockDomain){
val coreClockedRegister = Reg(UInt(4 bit))
}
Configuration
^^^^^^^^^^^^^
In addition to the constructor parameters given :ref:`here `\ , the following elements of each clock domain are configurable via a ``ClockDomainConfig``\ class :
.. list-table::
:header-rows: 1
:widths: 1 5
* - Property
- Valid values
* - ``clockEdge``
- ``RISING``\ , ``FALLING``
* - ``ResetKind``
- ``ASYNC``\ , ``SYNC``\ , ``BOOT`` which is supported by some FPGA (FF values loaded by the bitstream)
* - ``resetActiveLevel``
- ``HIGH``\ , ``LOW``
* - ``softResetActiveLevel``
- ``HIGH``\ , ``LOW``
* - ``clockEnableActiveLevel``
- ``HIGH``\ , ``LOW``
.. code-block:: scala
class CustomClockExample extends Component {
val io = new Bundle {
val clk = in Bool
val resetn = in Bool
val result = out UInt (4 bits)
}
// configure the clock domain
val myClockDomain = ClockDomain(
clock = io.clk,
reset = io.resetn,
config = ClockDomainConfig(
clockEdge = RISING,
resetKind = ASYNC,
resetActiveLevel = LOW
)
)
// Define an Area which use myClockDomain
val myArea = new ClockingArea(myClockDomain) {
val myReg = Reg(UInt(4 bits)) init(7)
myReg := myReg + 1
io.result := myReg
}
}
By default, a ClockDomain is applied to the whole design. The configuration of this one is :
* Clock : rising edge
* Reset : asynchronous, active high
* No clock enable
Internal clock
^^^^^^^^^^^^^^
An alternative syntax to create a clock domain is the following :
.. code-block:: scala
ClockDomain.internal(
name: String,
[config: ClockDomainConfig,]
[withReset: Boolean,]
[withSoftReset: Boolean,]
[withClockEnable: Boolean,]
[frequency: IClockDomainFrequency]
)
This definition takes six parameters:
.. list-table::
:header-rows: 1
:widths: 1 5 1
* - Argument
- Description
- Default
* - ``name``
- Name of clk and reset signal
-
* - ``config``
- Specify polarity of signals and the nature of the reset
- Current config
* - ``withReset``
- Add a reset signal
- true
* - ``withSoftReset``
- Add a soft reset signal
- false
* - ``withClockEnable``
- Add a clock enable
- false
* - ``frequency``
- Frequency of the clock domain
- UnknownFrequency
It's advantage is to create clock and reset signals with a specified name inplace of an inherited one. Then you have to assign those ClockDomain's signals as for instance in the example bellow :
.. code-block:: scala
class InternalClockWithPllExample extends Component {
val io = new Bundle {
val clk100M = in Bool
val aReset = in Bool
val result = out UInt (4 bits)
}
// myClockDomain.clock will be named myClockName_clk
// myClockDomain.reset will be named myClockName_reset
val myClockDomain = ClockDomain.internal("myClockName")
// Instanciate a PLL (probably a BlackBox)
val pll = new Pll()
pll.io.clkIn := io.clk100M
// Assign myClockDomain signals with something
myClockDomain.clock := pll.io.clockOut
myClockDomain.reset := io.aReset || !pll.io.
// Do whatever you want with myClockDomain
val myArea = new ClockingArea(myClockDomain){
val myReg = Reg(UInt(4 bits)) init(7)
myReg := myReg + 1
io.result := myReg
}
}
External clock
^^^^^^^^^^^^^^
You can define everywhere a clock domain which is driven by the outside. It will then automatically add clock and reset wire from the top level inputs to all synchronous elements.
.. code-block:: scala
ClockDomain.external(
name: String,
[config: ClockDomainConfig,]
[withReset: Boolean,]
[withSoftReset: Boolean,]
[withClockEnable: Boolean,]
[frequency: IClockDomainFrequency]
)
Arguments of the ``ClockDomain.external`` function are exactly the sames than for the ``ClockDomain.internal`` one. Below an example of a desing using ``ClockDomain.external``.
.. code-block:: scala
class ExternalClockExample extends Component {
val io = new Bundle {
val result = out UInt (4 bits)
}
// On top level you have two signals :
// myClockName_clk and myClockName_reset
val myClockDomain = ClockDomain.external("myClockName")
val myArea = new ClockingArea(myClockDomain){
val myReg = Reg(UInt(4 bits)) init(7)
myReg := myReg + 1
io.result := myReg
}
}
Context
^^^^^^^
At any moment you can retrieve in which clock domain you are by calling ``ClockDomain.current``.
Then the returned instance (which is a ClockDomain one) as following functions that you can call :
.. list-table::
:header-rows: 1
:widths: 1 5 1
* - name
- Description
- Return
* - frequency.getValue
- Return the frequency of the clock domain
- Double
* - hasReset
- Return if the clock domain has a reset signal
- Boolean
* - hasSoftReset
- Return if the clock domain has a reset signal
- Boolean
* - hasClockEnable
- Return if the clock domain has a clock enable signal
- Boolean
* - readClockWire
- Return a signal derived by the clock signal
- Bool
* - readResetWire
- Return a signal derived by the reset signal
- Bool
* - readSoftResetWire
- Return a signal derived by the reset signal
- Bool
* - readClockEnableWire
- Return a signal derived by the clock enable signal
- Bool
* - isResetActive
- Return True when the reset has effect
- Bool
* - isSoftResetActive
- Return True when the softReset has effect
- Bool
* - isClockEnableActive
- Return True when the clock enable has effect
- Bool
There is an example with an UART controller that use the frequency specification to set its clock divider :
.. code-block:: scala
val coreClockDomain = ClockDomain(coreClock, coreReset, frequency=FixedFrequency(100e6))
val coreArea = new ClockingArea(coreClockDomain){
val ctrl = new UartCtrl()
ctrl.io.config.clockDivider := (coreClk.frequency.getValue / 57.6e3 / 8).toInt
}
Clock domain crossing
---------------------
SpinalHDL checks at compile time that there is no unwanted/unspecified cross clock domain signal reads. If you want to read a signal that is emitted by another ``ClockDomain`` area, you should add the ``crossClockDomain`` tag to the destination signal as depicted in the following example:
.. code-block:: scala
// _____ _____ _____
// | | (crossClockDomain) | | | |
// dataIn -->| |--------------------->| |---------->| |--> dataOut
// | FF | | FF | | FF |
// clkA -->| | clkB -->| | clkB -->| |
// rstA -->|_____| rstB -->|_____| rstB -->|_____|
// Implementation where clock and reset pins are given by components IO
class CrossingExample extends Component {
val io = new Bundle {
val clkA = in Bool
val rstA = in Bool
val clkB = in Bool
val rstB = in Bool
val dataIn = in Bool
val dataOut = out Bool
}
// sample dataIn with clkA
val area_clkA = new ClockingArea(ClockDomain(io.clkA,io.rstA)){
val reg = RegNext(io.dataIn) init(False)
}
// 2 register stages to avoid metastability issues
val area_clkB = new ClockingArea(ClockDomain(io.clkB,io.rstB)){
val buf0 = RegNext(area_clkA.reg) init(False) addTag(crossClockDomain)
val buf1 = RegNext(buf0) init(False)
}
io.dataOut := area_clkB.buf1
}
//Alternative implementation where clock domains are given as parameters
class CrossingExample(clkA : ClockDomain,clkB : ClockDomain) extends Component {
val io = new Bundle {
val dataIn = in Bool
val dataOut = out Bool
}
// sample dataIn with clkA
val area_clkA = new ClockingArea(clkA){
val reg = RegNext(io.dataIn) init(False)
}
// 2 register stages to avoid metastability issues
val area_clkB = new ClockingArea(clkB){
val buf0 = RegNext(area_clkA.reg) init(False) addTag(crossClockDomain)
val buf1 = RegNext(buf0) init(False)
}
io.dataOut := area_clkB.buf1
}
Even shorter by importing the lib ``import spinal.lib._`` SpinalHDL offers a cross clock domain buffer ``BufferCC(input: T, init: T = null, bufferDepth: Int = 2)`` to avoid metastability issues.
.. code-block:: scala
class CrossingExample(clkA : ClockDomain,clkB : ClockDomain) extends Component {
val io = new Bundle {
val dataIn = in Bool
val dataOut = out Bool
}
// sample dataIn with clkA
val area_clkA = new ClockingArea(clkA){
val reg = RegNext(io.dataIn) init(False)
}
// BufferCC to avoid metastability issues
val area_clkB = new ClockingArea(clkB){
val buf1 = BufferCC(area_clkA.reg, False)
}
io.dataOut := area_clkB.buf1
}
Special clocking Area
---------------------
Slow Area
^^^^^^^^^
``SlowArea`` is used to create a new clock domain area which is slower than the current one.
.. code-block:: scala
class TopLevel extends Component {
// Use the current clock domain : 100MHz
val areaStd = new Area {
val counter = out(CounterFreeRun(16).value)
}
// Slow the current clockDomain by 4 : 25 MHz
val areaDiv4 = new SlowArea(4){
val counter = out(CounterFreeRun(16).value)
}
// Slow the current clockDomainn to 50MHz
val area50Mhz = new SlowArea(50 MHz){
val counter = out(CounterFreeRun(16).value)
}
}
def main(args: Array[String]) {
new SpinalConfig(
defaultClockDomainFrequency = FixedFrequency(100 MHz)
).generateVhdl(new TopLevel)
}
ResetArea
^^^^^^^^^
``ResetArea`` is used to create a new clock domain area where a special reset is combined or not with the current clock domain reset.
.. code-block:: scala
class TopLevel extends Component {
val specialReset = Bool
// The reset of this area is done with the specialReset signal
val areaRst_1 = new ResetArea(specialReset, false){
val counter = out(CounterFreeRun(16).value)
}
// The reset of this area is a combination between the current reset and the specialReset
val areaRst_2 = new ResetArea(specialReset, true){
val counter = out(CounterFreeRun(16).value)
}
}
ClockEnableArea
^^^^^^^^^^^^^^^
``ClockEnableArea`` is used to add one more clock enable in the current clock domain.
.. code-block:: scala
class TopLevel extends Component {
val clockEnable = Bool
// Add a clock enable for this area
val area_1 = new ClockEnableArea(clockEnable){
val counter = out(CounterFreeRun(16).value)
}
}