Spinal 时钟域
时钟域的例化和管理通常发生在top level,时钟信号和复位信号的互联在顶层或者次顶层。
Spinal中提供了ClockDomain类来建模和管理不同的时钟域。
一个系统如下:
系统内部包含一个外部时钟和三个内部时钟域。外部时钟域,即时钟源在片外,比如片外晶振,其产生的时钟信号通过IO pad连入chip,作为片内PLL的时钟输入,或者直接用。内部时钟域则是时钟源在片内产生的,比如PLL产生的时钟信号。
时钟域的例化
外部时钟域
1 | // 例化一个前缀为“ref”的外部时钟域 |
如上,例化了一个外部时钟域。它会在模块的顶层产生对应的前缀的时钟信号和复位信号,作为chip的输入端口。我们可以直接使用ClockingArea来给我们的设计定义为这一时钟域,也可以使用 mapClockDomain(<ext_clk_dom>, clock=<driven_clk>, reset = <driven_reset>)
来让外部时钟域定义的时钟信号和复位信号,驱动IP定义的时钟信号和复位信号。
1 | val iopll = IOPLL() |
内部时钟域
1 | // 例化一个前缀为“sys”的内部时钟域 |
与外部时钟域不同,内部时钟域的时钟信号和复位信号都由chip内部产生,需要用户自己定义内部时钟源和内部时钟域的连接关系。
它的使用也和外部时钟域类似,主要也是两种:
- 使用
mapClockDomain
函数,将时钟域和IP的时钟域绑定 - 使用 ClockingArea,将时钟域和用户逻辑绑定
时钟域的绑定
Component
的类内自带一个默认的 clockDomain
,其自动绑定到下面的时序单元。
通过上述的方法例化的时钟域,类似于入栈,压在默认时钟域上方,这些时钟域对应的 ClockingArea
中的用户逻辑,包括 Component
和 BlackBox
都会使用相应的栈顶的时钟域而不是栈底的时钟域。
用户逻辑的绑定
两种方式,一种直接将逻辑放在对应的时钟域内:
1 | val aclk = ClockDomain.external("a") |
缺点在于,内部逻辑是匿名的,无法被外部访问。声明只能都放在外面。适用于toplevel的连接。优点是简洁,不易产生cdc问题。
另外一种,声明一个 ClockingArea
,包住一个时钟域。随后逻辑放在Area内部。外面的逻辑可以直接访问内部逻辑。缺点是可能有cdc问题(人为失误)。另外Spinal提供预设的三种Clocking Area:
SlowArea
分频时钟域,该Area下的时钟域是当前时钟域的一个整数分频ResetArea
复位域,该Area下的时钟域与当前时钟域共享一个时钟,但是引入新的复位信号。原复位信号是否起作用,可以选择,cumulative
为true则起作用ClockEnableArea
门控时钟域,该Area下的时钟共享当前时钟域的时钟,并引入一个新的信号作为门控信号。
IP的绑定
通过 BlackBox
的方法: mapClockDomain
和 mapCurrentClockDomain
。传参有:
- clockDomain: 用作驱动的时钟域(
mapCurrentClockDomain
没有这一参数,它使用的就是当前时钟域,即IP被例化的位置所拥有的时钟域) - clock: 被驱动的时钟信号
- reset:被驱动的复位信号
- enable:被驱动的时钟门控信号