天天看点

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

2021.9.5 有些地方添加了一点自己的理解!!!

0 前言

这篇讲chisel的高级参数化特性。个人感觉chisel相比于verilog优势可以总结为自顶向下的设计思想替代自底向上的设计思路。从参数化上体验尤为明显。高级参数化分为两部分讲,数值的参数化以及拓扑的参数化。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

说起参数化,我们希望芯片设计中什么样的参数化机制才是好的 ?大致有以下三个方面。

  • 可以变化的参数在顶层暴露,便于统一管理
  • 增删参数的时候尽量不动源代码
  • 参数要方便的查找,并尽量保证不会混用

传统的verilog在参数化的时候逐级传递其实一来不利于管理,而来也容易出错。所以chisel的开发团队开发出专门的parameter类用于管理较大芯片的参数。

这套机制此处称之为

site/here/up

链表机制,其原理是逐级例化时将参数例化为链表,逐级查找。由于scala代码个人觉得是一种写起来比较省事但是易读性差到令人发指的语言,所以直接讲原理可能有困难。

所以此处决定采用另一种讲法,先讲怎么用,再讲原理。先知其然,再知其所以然。

(注:chisel 2.0和3.0这部分其实有小区别的。chisel 2.0将

site/here/up

机制直接嵌入了chisel库,而3.0将这部分参数化机制分离出来作为单独的可选文件,需要引用,本文主要讲chisel 3.0)

一、如何使用rocketchip的参数系统

  • step0 在你的工程里加入一个写好的类库文件Config.scala,如下所示:
chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

这样就可以按照下面的写法import了。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制
  • step1 先来个祖传的加法器演示,只有1个参数—位宽
chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

如5行所示,用

Field[T]

给每个参数扩展出一个名字,Width.

如7~12行,直接继承

Config

类,构造我们自己的参数管理类

MyConfig

.

其中Config的构造函数是一个

(site,here,up)

元组到

偏函数(Partial Function

,这个词是部分函数的意思,翻译成偏函数其实是不准确的)。我们的所有参数就保存在这个偏函数内。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

我这里为了简单说明问题,就只有一个参数。

然后声明我们的module. 看14行,类的主构造函数的参数用implicit修饰,代表的是隐式参数的意思,也就是如果这个括号里没有代入值,则编译器直接找声明为implicit val的合适的值。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

后续会把我们的参数类的对象代入这个p。取参数也异常简单。直接用

p(WIDTH)

就能取出来,至于为什么能取出来,后续介绍。

看25,26行。例化module以后就能正确的产生RTL了。如下。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

是不是很简单?这个时候你就要问了,不是site/up/here机制么?为什么不见site/up/here?那是因为我们链表目前只有一层,所以目前的链表是这样的。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

二、使用

site/up/here

机制

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

再复杂一些,变成这样。16-23行,设计一个新的module, 直接将portb加上一个常数。如上图所示。ModuleA有一个参数,接收port_width. 同时还接收

Parameters

类型的隐式参数p,该参数用来传递配置参数.

在Helloworld Module给ModuleA传递参数的时候,使用parameter类的

alterpartial

函数给参数列表传进去一个新的偏函数。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

此时参数链表变成了:

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

那么在26行查到的WIDTH = 8, 在17行查到的WIDTH = 4,

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

为什么是这样呢?因为在Helloworld里看到的链表是链表1,传给Module A的链表是链表2.

至此你应该对链表的机制有了初步的了解。下面我们就一点一点的修改代码,以将

site、here和up

都使用上。

1、【here】
现在先说一个最简单的here。here的意思是在本偏函数内引用。

比如下面情况。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

例如我们想要个位宽为16的参数,采用上述写法。这种写法在模块参数有依赖是非常有用。

例如我们此时将ModuleA 中的

width

改成了

p(DBWIDTH)

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

此时会先查找参数最底一层,发现只有WIDTH和PA,没有DBWIDTH,所以往上一层查找。找到DHWIDTH=16。生产的verilog如下。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制
2、【site】

site机制就比较复杂一些,假如我们有一个参数名,例如DBWIDTH,但是在不同的Module里代表不同的意义,值不一样,此时就要用到site。此处为了简单,我们在Helloworld中例化两个ModuleA。一个叫

m_big_a

, 一个叫

m_small_a

为了好理解,此处给出代码的全文,再做解释。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

如上图所示。此时生产的RTL如下

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

此时,

m_big_a 和 m_small_a

模块看到的参数是不一样的。两个模块看到的参数链表如下:

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制
site指的是参数链表中最下面那一层。需要注意使用

alterpartial

函数添加新的偏函数时,就是在往链表底层方向添加,所以在该例中使用

alterpartial

函数新添加的偏函数就是site指向的地方。给ModuleA传入的是整个链表,但是最开始查询时,是从最底层开始查询的。

我们以例化

m_big_a

模块时查询

DBWIDTH

参数为例介绍查询过程。共经历了4个步骤。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

由于此处在ModuleA中查询参数DWIDTH,故site指向的是ModuleA的整个参数链表的最底层,也就是在原本参数链表的基础上新添加的一个偏函数,如下所示:

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

共有4步:

  • step 1:在ModuleA的参数表中查找DBWIDTH,发现没找到。
  • step 2:去上一层查找,找到了DBWIDTH,发现调用了

    site(A_TYPE)

  • step3:由于site指向的是ModuleA的参数表最底层,也就是新添加的偏函数,所以会查到

    ATYPE = “big_a”

  • step 4:根据

    “big_a”

    查到参数等于6。
3、【up】
up机制显然就是去上一层链表查找。
chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

例如将

m_big_a

的例化改成这样。则链表变为了

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

在ModuleA查询,此时up是上一层,也就是Helloworld。也是分为4步。

  • step1:先在ModuleA中查询DBWIDTH, 发现有。
  • step2:调用up函数
  • step3:在上层找到WIDTH
  • step4:返回24

至此,

site/up/here

机制的用法已经都有涉及。但是上面用法介绍无法展示其用途,下面通过一个例子来介绍其用途。

二、典型案例

此处给例子来自《Advanced Parameterization Manual》。如下图所示的一个典型的CPU架构。代码写法是chisel 2.0,与3.0略有差异,但是可以用来介绍用途。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

上面的参数化描述如下。加入我们加入一个ecc参数。规定在cache中的memroy要做ecc.则如下描述。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

在主模块中给出Location的偏函数。

chisel相比verilog优势之二:高级参数化---Site/Here/Up机制

在core中location指定的是incore, 在两个cache中location给定为incache即可。(注:此处为伪代码,实际上icache和dcache还要在参数表中给出

Cache_type => "i"

或者

Cache_type => "d"

用于查询ways以及sets.

三、注意事项

下面举两个例子,来引出两个在使用

site/up/here

时需要注意的点。

1、例一
package grammer

import freechips.rocketchip.config.{Config, Field, Parameters}

//这里没有提供参数,是为了让default为None
case object XLEN extends Field[Int](2)
case object Trace extends Field[String](default = "defaultTrace")
case object Nways extends Field[Int](2)
//case object XLEN_2 extends Field[Parameters => Int]
//case object Trace_2 extends Field[Parameters => Boolean]

case object XLEN_2 extends Field[Int](2)
case object Trace_2 extends Field[String](default = "defaultTrace_2")
case object NSets extends Field[Int](2)


class MiniConfig1 extends Config((site, here, up) => {
  // Core
  case XLEN => 1
  case Trace => "Wzx"
  case Nways => 16 * site(NSets)
})

class MiniConfig2 extends Config((site, here, up) => {
  // Core
  case XLEN_2 => 2 * up(XLEN)//4
  case Trace_2 => "Alex"
  case NSets => 2 * here(XLEN_2)//NSets = 4 * XLEN.default
})

class MiniConfig extends Config(
  new MiniConfig2 ++
  new MiniConfig1
)

object ConfigTest {
  def main(args: Array[String]): Unit = {
    def printConfig(implicit p: Parameters) = {
      println(s"XLEN = ${p(XLEN)}")
      println(s"Trace = ${p(Trace)}")
      println(s"Nways = ${p(Nways)}")
      println("......")
      println(s"XLEN_2 = ${p(XLEN_2)}")
      println(s"Trace_2 = ${p(Trace_2)}")
      println(s"NSets = ${p(NSets)}")
    }
    /** ************************************ */

    implicit val p: Parameters = new MiniConfig
    printConfig(p)
  }
}
           

打印结果:

XLEN = 1

Trace = Wzx

Nways = 128

XLEN_2 = 2

Trace_2 = Alex

NSets = 8

需要注意的是

MiniConfig2

中的

NSets

的值,它之所以等于

4 * XLEN.default

,是因为:

一旦使用到了here,那么后面,注意是后面(这个前后指的是查找顺序,而和case分支的顺序无关)如果再使用site或者up,仍然会一直查到最后,但是当第一次查到最顶层的一个case分支时就停止,不会再继续按照该顶层分支的内容继续查找。最后使用的是该分支的默认值。
2、例二
package grammer2

import freechips.rocketchip.config.{Config, Field, Parameters}

//这里没有提供参数,是为了让default为None
case object XLEN extends Field[Int](2)
case object Trace extends Field[String](default = "defaultTrace")
case object Nways extends Field[Int](2)
case object Test extends Field[Int](2)

case object XLEN_2 extends Field[Int](10)
case object Trace_2 extends Field[String](default = "defaultTrace_2")
case object NSets extends Field[Int](2)

class MiniConfig1 extends Config((site, here, up) => {
  // Core
  case XLEN => 2
  case Trace => "Wzx"
  case Nways => 16 * site(NSets)
})

object ConfigTest2 {
  def main(args: Array[String]): Unit = {
    def printConfig(implicit p: Parameters) = {
      println(s"XLEN = ${p(XLEN)}")
      println(s"Trace = ${p(Trace)}")
      println(s"Nways = ${p(Nways)}")
      println("......")
      println(s"XLEN_2 = ${p(XLEN_2)}")
      println(s"Trace_2 = ${p(Trace_2)}")
      println(s"NSets = ${p(NSets)}")
    }
    /** ************************************ */
    val Config1: Parameters = new MiniConfig1

    implicit val Config = Config1.alter((site, here, up) =>{
      // Core
      case XLEN_2 => 2 * up(Nways)
      case Trace_2 => "Alex"
      case NSets => here(XLEN_2)//2 * Nways.default
      case Test => 5
    })

    printConfig(Config)
  }
}
           

打印结果:

XLEN = 2

Trace = Wzx

Nways = 64

XLEN_2 = 64

Trace_2 = Alex

NSets = 4

需要注意的是

XLEN_2

的值,本来以为

XLEN_2 = 32 * NSets

,但其实不是,而是

32*NSets.default

。这是因为:

在使用

site/up/here

时,不能up上去再site下来,否则site的时候直接使用默认值,不会再回到下一层去寻找真实值。注意,这里指的是不能使用up手动去上层寻找然后再使用site下来找;但是可以在本层找不到时,由该机制本身自动去上一层查找然后再使用site下来找。

四、总结

本篇描述了

site/up/here

的用法,同时举了一个简单的例子说明了用途。这一套参数化系统很好的满足了参数统一、分层管理,不需要层层传递的要求。让系统的参数组织有序化。但

site/up/here

用的好还要了解其原理,关于

site/up/here

的原理可以先参考以下两篇文章:

  • 高级参数化: Site/Here/Up原理解析
  • Chisel进阶——隐式参数的应用

继续阅读