您当前的位置: 首页 > 

耐心的小黑

暂无认证

  • 3浏览

    0关注

    323博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

chisel(Rocket Chip)中(Site/Here/Up)机制原理(config源码解读)

耐心的小黑 发布时间:2021-07-16 21:56:07 ,浏览量:3

2021.9.5 很多地方有改动,添加了自己的理解!!!

0 前言

用Chisel编写的CPU,比如Rocket-Chip、RISCV-Mini等,都有一个特点,就是可以用一个配置文件来裁剪电路,其实它就使用到了我们说的Site/Here/Up机制。该机制的实现只有不到100行的源码,存放于一个叫Config.scala的文件中。它利用了Scala的模式匹配、样例类、偏函数、可选值、隐式定义等语法。下面我们就阅读源码来解读下该机制是如何实现的。

先把我添加了注释的源码贴出来。

// Config.scala
object config {
  abstract class Field[T] private (val default: Option[T])
  {
    def this() = this(None)
    def this(default: T) = this(Some(default))
  }

  abstract class View {
    final def apply[T](pname: Field[T]): T = apply(pname, this)
    final def apply[T](pname: Field[T], site: View): T = {
      //查找pname会返回一个可选值,有可能是Some(value),也有可能是None
      val out = find(pname, site)
      //如果没有查找到pname,out是None,require报错
      require (out.isDefined, s"Key ${pname} is not defined in Parameters")
      //如果不报错,说明out是Some(value),就把value取出来返回
      out.get
    }

    final def lift[T](pname: Field[T]): Option[T] = lift(pname, this)
    final def lift[T](pname: Field[T], site: View): Option[T] = find(pname, site).map(_.asInstanceOf[T])

    protected[config] def find[T](pname: Field[T], site: View): Option[T]
  }

  abstract class Parameters extends View {
    final def ++ (x: Parameters): Parameters =
      new ChainParameters(this, x)
    //++ 右边的Parameters会被添加到链表的顶部,也就是左边的新Parameters会被添加到底部
    final def alter(f: (View, View, View) => PartialFunction[Any,Any]): Parameters =
      Parameters(f) ++ this

    final def alterPartial(f: PartialFunction[Any,Any]): Parameters =
      Parameters((_,_,_) => f) ++ this

    final def alterMap(m: Map[Any,Any]): Parameters =
      new MapParameters(m) ++ this

    protected[config] def chain[T](site: View, tail: View, pname: Field[T]): Option[T]
    //TerminalView表示链表的最顶层
    protected[config] def find[T](pname: Field[T], site: View) = chain(site, new TerminalView, pname)
  }

  object Parameters {

    def empty: Parameters = new EmptyParameters
    //根据传入的f构造PartialParameters对象
    def apply(f: (View, View, View) => PartialFunction[Any,Any]): Parameters = new PartialParameters(f)
  }

  class Config(p: Parameters) extends Parameters {
    // f作为辅助构造函数的入参,然后将Parameters(f)作为入参调用主构造函数
    def this(f: (View, View, View) => PartialFunction[Any,Any]) = this(Parameters(f))
    // p是Parameters类型,这里的chain函数其实就是直接调用的p.chain,
    // 虽然p是Parameters类型,但又有多种具体的子类型:
    /*
    * 1、PartialParameters 直接提供f,此时调用的就是PartialParameters的chain
    *    tail是 TerminalView,其find函数就是返回pname的default默认值,也就是None
    * 2、ChainParameters 直接使用主构造函数,此时调用的是链表最底部的Parameters的chain
    *    tail是 ChainView(y, tail),其find函数调用的是y的chain,而y刚好是上一层
    * */
    protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = p.chain(site, tail, pname)
    override def toString = this.getClass.getSimpleName
    def toInstance = this
  }

  // Internal implementation:

  private class TerminalView extends View {
    def find[T](pname: Field[T], site: View): Option[T] = pname.default
  }
  // ChainView的find函数,调用的是head,也就是第一个参数的chain函数
  private class ChainView(head: Parameters, tail: View) extends View {
    def find[T](pname: Field[T], site: View) = head.chain(site, tail, pname)
  }

  // 这里的x在链表底部,y在x的上一层
  // ChainView的find函数,调用的是head,也就是第一个参数的chain函数,
  // 所以这里的ChainView(y, tail) 的find函数是调用的y.chain,也就是链表上一层的chain
  private class ChainParameters(x: Parameters, y: Parameters) extends Parameters {
    def chain[T](site: View, tail: View, pname: Field[T]) = x.chain(site, new ChainView(y, tail), pname)
  }

  private class EmptyParameters extends Parameters {
    def chain[T](site: View, tail: View, pname: Field[T]) = tail.find(pname, site)
  }

  private class PartialParameters(f: (View, View, View) => PartialFunction[Any,Any]) extends Parameters {
    protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = {
      //f就是一个入参是(View, View, View),返回值是多个case语句组成的偏函数的函数,比如:
      /*
      * (site, here, up) => {
              // Core
              case XLEN => 32
              case Trace => true
              case XLEN_p  => (p: Parameters) => p(XLEN)
              case Trace_p => (p: Parameters) => p(Trace)
            }
      * */
      // 所以这里g就是返回的偏函数,也就是花括号里的内容
      val g = f(site, this, tail)
      //如果偏函数里面有pname这个值,就返回对应的值
      //注意,一条case对应一个asInstanceOf[T]和一个isInstanceOf[T]方法,可参考模式匹配那一章
      //g.apply(XLEN)就是返回32,然后再asInstanceOf[Int]
      if (g.isDefinedAt(pname)) Some(g.apply(pname).asInstanceOf[T])
      //如果没有pname这个值,就去上层去找,也就是执行tail.find
      else tail.find(pname, site)
    }
  }

  private class MapParameters(map: Map[Any, Any]) extends Parameters {
    protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = {
      val g = map.get(pname)
      if (g.isDefined) Some(g.get.asInstanceOf[T]) else tail.find(pname, site)
    }
  }
}
一、Field[T]类

抽象类Field[T]是一个类型构造器,它需要根据类型参数T来生成不同的类型。而T取决于传入的参数——可选值default:Option[T]的类型。例如,如果default = Some(10),那么所有的T都可以确定为Int。

Field[T]只有一个公有val字段,即主构造方法的参数default:Option[T]。因为主构造方法是私有的,所以我们只能访问两个公有的辅助构造方法:

def this() = this(None)
def this(default: T) = this(Some(default))
  • 第一个辅助构造方法不接收参数,所以会构造一个default =None的对象;
  • 第二个辅助构造方法接受一个T类型的参数,然后把参数打包成可选值Some(default),并把它赋给对象的可选值字段default。

我们可以看到Field[T]是抽象的,所以它只能用于继承给子类、子单例对象或子特质。之所以定义抽象类Field[T],是为了后面构造出它的样例子对象,并把这些样例对象用于偏函数(或者说用于模式匹配)。

例如,构造一个“case object isInt extends Field[Int]”,然后把样例对象isInt用于偏函数“case isInt => …”

为什么要把isInt构造成Field[Int]类型,而不是直接的Int类型呢?

  • 首先,我们想要偏函数的参数,也就是每个case分支后面的值,是一个常量,这样才能构成常量模式的模式匹配,一个常量模式控制一条配置选项。所以,要么定义一个样例对象,要么定义一个普通的Int对象比如1。这里我们选择定义样例对象,因为不仅会有Int类型,还可能有其他的自定义类型,它们可能是抽象的,无法直接创建实例对象。而且,用一个普通的Int对象来做模式匹配,会显得不那么独一无二。为了方便统一,全部构造成Field[T]类型的样例对象。例如,“case object isA extends Field[A]”、“case object isB extends Field[B]”等等。

  • 其次,为什么要引入Field[Int]而不是“case object isInt extends Int”呢?因为Scala的基本类型Int、Float、Double、Boolean等都是final修饰的抽象类,不能被继承。

下面举个例子,演示一下继承自Field[Int]类的样例对象如何使用:

import freechips.rocketchip.config._
case object MyObjecttype1 extends Field[Int]//不传参数
case object MyObjecttype2 extends Field[String]("hhh")//传参数

case object BuildALU extends Field[Parameters => Int]

object Field {
  def main(args: Array[String]): Unit = {
    /** ************************************ */

    val Objecttype1 = MyObjecttype1
    println(Objecttype1.default)

    val res1 = Objecttype1 match{
      case MyObjecttype1 => "this is MyObjecttype1!"
    }
    println(res1)

    val Objecttype2 = MyObjecttype2
    println(Objecttype2.default)
    val res2 = Objecttype2 match{
      case MyObjecttype2 => "this is MyObjecttype2!"
    }
    println(res2)
    /** ************************************ */
  }
}

打印结果:

None this is MyObjecttype1! Some(hhh) this is MyObjecttype2!

一般情况下,我们是不传入参数的,这样的话default字段的值就是None。由于我们在使用Site/Here/Up机制查找参数时,如果找不到某个参数就会返回其默认值,如果默认值是None,这样require函数就不能通过就会报错(后面会讲),就可以提示我们参数找不到。

二、View类

我们只需要关心抽象类View的两个apply方法。

final def apply[T](pname: Field[T]): T = apply(pname, this)
final def apply[T](pname: Field[T], site: View): T = {
   //查找pname会返回一个可选值,有可能是Some(value),也有可能是None
   val out = find(pname, site)
   //如果没有查找到pname,out是None,require报错
   require (out.isDefined, s"Key ${pname} is not defined in Parameters")
   //如果不报错,说明out是Some(value),就把value取出来返回
   out.get
 }

其中第一个apply方法只是调用了第二个apply方法,重点在第二个apply方法。第二个apply方法调用了View的find方法,而find方法是抽象的,目前只知道它的返回结果是一个可选值。View的子类应该实现这个find方法,并且find方法会影响apply方法。如果不同的子类实现了不同行为的find方法,那么apply方法可能也会有不同的行为。

参数pname的类型是Field[T],其实就是一个样例对象。而find方法应该就是在参数site里面找到是否包含pname,如果包含就返回一个可选值,否则就返回None。

根据require函数可以印证这一点:

  • 如果site里面没有pname,那么结果out就是None,out.isDefined就是false,require函数产生异常,并输出字符串“Key ${pname} is not defined in Parameters”,即找不到pname;
  • 反之,out.isDefined就是true,require函数通过,不会输出字符串,并执行后面的out.get,即把可选值解开并返回。
三、Parameters类及其伴生对象与子类

抽象类Parameters是View的子类,它的确实现了find方法,但是又引入了抽象的chain方法,所以我们只需要关心Parameters的子类是如何实现chain方法的。子类中的chain方法的行为,才真正地决定着查找参数的逻辑。因为下面不再有子类了,chain方法也不是抽象的,而是有具体内容的方法了。所以我们非常有必要解读下Parameters的子类是如何实现自己的抽象chain方法的。

主要有三个子类需要关注:PartialParametersChainParametersEmptyParameters

1、PartialParameters子类
private class PartialParameters(f: (View, View, View) => PartialFunction[Any,Any]) extends Parameters {
    protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = {
      val g = f(site, this, tail)
      if (g.isDefinedAt(pname)) Some(g.apply(pname).asInstanceOf[T])
      else tail.find(pname, site)
    }
  }

Parameters类的伴生对象里定义了一个apply工厂方法,该方法构造了一个PartialParameters对象。

  • 首先,PartialParameters类是Parameters的子类,所以工厂方法的返回类型可以是Parameters但实际返回结果是一个子类对象。
  • 其次,工厂方法的入参f是一个函数,这个函数有三个View类型的入参,然后返回一个偏函数,即f是一个返回偏函数的函数。根据偏函数的介绍内容,我们可以推测出f返回的偏函数应该是一系列的case语句,用于模式匹配。

接着,前面说过,我们只需要关心Parameters的子类是如何实现chain方法的,而子类PartialParameters则实现了chain方法的一个版本。这个chain方法:

  • 首先把PartialParameters的构造参数f返回的偏函数用g来引用,也就是说,g现在就是那个偏函数。至于f的三个入参site、this和tail则不是重点。
  • 然后,g.isDefinedAt(pname)表示在偏函数的可行域里寻找是否包含pname,如果有的话,则执行相应的case语句;否则,就用参数tail的find方法。结合如下代码定义,参数tail其实就是TerminalView的实例对象,它的find方法就是直接返回pname的可选值字段,也就是default的值。:
protected[config] def chain[T](site: View, tail: View, pname: Field[T]): Option[T]
protected[config] def find[T](pname: Field[T], site: View) = chain(site, new TerminalView, pname)
2、ChainParameters子类
  private class ChainParameters(x: Parameters, y: Parameters) extends Parameters {
    def chain[T](site: View, tail: View, pname: Field[T]) = x.chain(site, new ChainView(y, tail), pname)
  }

ChainParameters子类的主构造函数的入参是两个Parameters类型的对象x、y,并且它也实现了自己版本的chain函数。不过该chain函数调用的是x.chain,并且x.chain的第二个参数不再是tail,也即不再是new TerminalView( tail 参数是在Parameters类中的find方法中被设置成new TerminalView的),而变成了new ChainView(y, tail)

我们需要知道,入参x是链表中最下面那一层,而y是x上面的一层。所以,一开始查找时,还是先从x也就是最底层开始查找。但是由于x的chain函数中第二个参数是new ChainView(y, tail),也就是如果x中找不到,就调用new ChainView(y, tail)的find方法。我们就先来看下ChainView是个什么东西:

  private class ChainView(head: Parameters, tail: View) extends View {
    def find[T](pname: Field[T], site: View) = head.chain(site, tail, pname)
  }

它和TerminalView类似,也是View的子类。不过它的find方法不再是直接返回pname的默认值,而是调用主构造函数的第一个入参head的chain方法。由于在ChainView(y, tail)中,y刚好就是第一个参数,所以调用的其实就是y的chain函数。而y又是x的上一层,所以调用new ChainView(y, tail)的find方法其实就是在往上层去查找pname。这里很重要,机制中自动往上层查找参数的逻辑就是这里实现的。

3、EmptyParameters子类
  private class EmptyParameters extends Parameters {
    def chain[T](site: View, tail: View, pname: Field[T]) = tail.find(pname, site)
  }

EmptyParameters也是Parameters的子类,它也实现了自己版本的chain方法。该方法是直接调用tail.find(pname, site),而前面说过tail其实就是new TerminalView(在Parameters类中的find方法中被设置成的new TerminalView),所以它其实是直接返回pname的默认值,没有其他查找动作,所以才叫做Empty。

4、++、alter、alterPartial链表生长方法
final def ++ (x: Parameters): Parameters =
  new ChainParameters(this, x)
 //++ 右边的Parameters会被添加到链表的顶部,也就是左边的新Parameters会被添加到底部
final def alter(f: (View, View, View) => PartialFunction[Any,Any]): Parameters =
  Parameters(f) ++ this

final def alterPartial(f: PartialFunction[Any,Any]): Parameters =
  Parameters((_,_,_) => f) ++ this

此外Parameters类中还有三个比较常用的方法++、alter、alterPartial,我们只需要知道它们的作用其实就是将两个Parameters类的对象拼接起来,这样就能生成多层链表。

  • 注意,使用++符号时,右侧的Parameters对象x在上面,自己this会被添加到当前链表的最底层,返回的是ChainParameters类型的对象(这里之所以更具体的说明是ChainParameters类型,而不是直接说成Parameters类型,是为了和另外两个子类型区分开,在下一小节中会有用。)。

  • 而使用alter、alterPartial方法则是直接传入偏函数或者返回偏函数的函数作为参数,然后使用Parameters伴生对象中的apply方法构造Parameters类型的对象,最后再调用++方法将新构造的对象添加到当前链表的最底层。

四、Config类

首先,Config类也是Parameters的子类。它可以通过主构造方法接收一个Parameters类型的实例对象来构造一个Config类型的实例对象,或者通过辅助构造方法接收一个函数f来间接构造一个Config类型的实例对象。

观察这个辅助构造方法,它其实先调用了Parameters的工厂方法,也就是利用函数f先构造了一个PartialParameters类型的对象(是Parameters的子类型),再用这个PartialParameters类型的对象去运行主构造方法。

其次,我们仍然需要知道chain方法是如何实现的。这里,Config的chain方法是由构造时的参数p: Parameters决定的:

  • 如果一个Config的对象是用辅助构造方法和函数f构造的,那么上面已经说了,参数p其实就是一个PartialParameters的对象,构造出来的Config对象的chain方法实际上运行的是PartialParameters的chain方法。由于是直接传入的f,所以其实此时链表只有一层。而前面已经讲过,PartialParameters的chain方法在本层找不到就会直接返回pname的默认值,这和链表只有一层刚好对应,因为不存在上层,只能直接返回默认值。
  • 如果直接传入的是ChainParameters类型的对象,由于只有使用++、alter、alterPartial方法时会返回该类型的对象,而这三个方法又是在构造多层链表,所以此时的链表是多层的。前面已经讲过,ChainParameters类型的对象的chain方法如果在本层找不到pname就会往上层去查找,这其实和ChainParameters是多层链表刚好对应。
  • 如果传入的是EmptyParameters类型的对象,那么此时直接调用tail.find(pname, site)返回默认值,根本就不会去查找,也是因为此时就是一个空链表,本来就没有什么参数可以查找。

View、Parameters、Config之间的继承关系如下:

在这里插入图片描述

五、案例讲解 1、MiniConfig

前面讲解的内容相当于类库里预先定义好的内容。要配置自定义的电路,还需要一个自定义的类。比如,处理器RISCV-Mini就定义了下面的MiniConfig类:

// See LICENSE for license details.
 
package mini
 
import chisel3.Module
import freechips.rocketchip.config.{Parameters, Config}
import junctions._
 
class MiniConfig extends Config((site, here, up) => {
    // Core
    case XLEN => 32
    case Trace => true
    case BuildALU    => (p: Parameters) => Module(new ALUArea()(p))
    case BuildImmGen => (p: Parameters) => Module(new ImmGenWire()(p))
    case BuildBrCond => (p: Parameters) => Module(new BrCondArea()(p))
    // Cache
    case NWays => 1 // TODO: set-associative
    case NSets => 256 
    case CacheBlockBytes => 4 * (here(XLEN) >> 3) // 4 x 32 bits = 16B
    // NastiIO
    case NastiKey => new NastiParameters(
      idBits   = 5,
      dataBits = 64,
      addrBits = here(XLEN))
  }
)

MiniConfig类是Config的子类,其实它没有添加任何定义,只是给超类Config传递了所需要的构造参数。前面已经讲过,Config有两种构造方法,这里是用了给定函数f的方法。

那么函数f是什么呢?函数f的类型是“(View, View, View) => PartialFunction[Any,Any]”,这里给出的三个View类型入参是site、here和up。我们目前只知道site、here和up是View类型的对象,具体是什么,还无法确定,也无需关心。重点在于返回的偏函数是什么。偏函数是用花括号包起来的9个case语句,这呼应了我们前面讲过的用case语句组构造偏函数。case后面的XLEN、Trace等,就是一系列的Filed[T]类型的样例对象。

那么如何利用MiniConfig类呢?我们可以推测这个类包含了RISCV-Mini核全部的配置信息,然后看看处理器RISCV-Mini的顶层文件是如何描述的:

val params = (new MiniConfig).toInstance
val chirrtl = firrtl.Parser.parse(chisel3.Driver.emit(() => new Tile(params)))

这里,也就是直接构造了一个MiniConfig的实例,并把它传递给了需要它的顶层模块Tile

2、MiniConfig的运行原理

我们来看Tile模块的定义:

class Tile(tileParams: Parameters) extends Module with TileBase {
  implicit val p = tileParams
  val io     = IO(new TileIO)
  val core   = Module(new Core)
  val icache = Module(new Cache)
  val dcache = Module(new Cache)
  val arb    = Module(new MemArbiter)

  io.host  core.io.host
  core.io.icache  icache.io.cpu
  core.io.dcache  dcache.io.cpu
  arb.io.icache  icache.io.nasti
  arb.io.dcache  dcache.io.nasti
  io.nasti  arb.io.nasti
}

首先,Tile模块需要一个Parameters类型的参数,我们给了一个MiniConfig的实例,而MiniConfig继承自Config,Config继承自Parameters,所以这是合法的。

然后,Tile模块把入参赋给了隐式变量p。参考隐式定义的内容,这个隐式变量会被编译器传递给当前层次所有未显式给出的隐式参数。查看其他代码的定义,也就是后面实例化的TileIO、Core、Cache和MemArbiter需要隐式参数。由于没有显式给出隐式参数,那么它们都会接收这个隐式变量p,即MiniConfig实例。

以Core模块为例:

class Core(implicit val p: Parameters) extends Module with CoreParams {
  val io = IO(new CoreIO)
  val dpath = Module(new Datapath) 
  val ctrl  = Module(new Control)

  io.host  dpath.io.host
  dpath.io.icache  io.icache
  dpath.io.dcache  io.dcache
  dpath.io.ctrl  ctrl.io
} 

可以看到,Core模块确实需要接收一个隐式的Parameters类型的参数。

再来看Core混入的特质CoreParams:

abstract trait CoreParams {
     implicit val p: Parameters
     val xlen = p(XLEN)
}

这个特质有未实现的抽象成员,即隐式参数p。抽象成员需要子类给出具体的实现,这里也就是Core模块接收的MiniConfig实例。

那么“val xlen = p(XLEN)”意味着什么呢?我们知道,p是一个MiniConfig的实例对象,它继承了超类View的apply方法。查看apply的定义,也就是调用了:

final def apply[T](pname: Field[T]): T = apply(pname, this)

final def apply[T](pname: Field[T], site: View): T = {
  val out = find(pname, site)
  require (out.isDefined, s"Key ${pname} is not defined in Parameters")
  out.get
}

而XLEN被定义为:

case object XLEN extends Field[Int] 

“val xlen = p(XLEN)”相当于“val xlen = p.apply(XLEN, p)”。这里的this也就是把对象p自己传入。

紧接着,apply方法需要调用find方法,即“val out = find(XLEN, p)”。而MiniConfig继承了Parameters的find和chain方法,也就是:

protected[config] def chain[T](site: View, tail: View, pname: Field[T]): Option[T]
protected[config] def find[T](pname: Field[T], site: View) = chain(site, new TerminalView, pname) 

而chain方法继承自Config类:

protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = p.chain(site, tail, pname) 

注意这里的p是用MiniConfig传递给超类的函数f构造的PartialParameters对象,不是MiniConfig对象自己。即:“val out = (new PartialParameters((site, here, up) => {…})).chain(p, new TerminalView, XLEN)”

再来看PartialParameters类的chain方法的具体行为:

protected[config] def chain[T](site: View, tail: View, pname: Field[T]) = {
   val g = f(site, this, tail)
   if (g.isDefinedAt(pname)) Some(g.apply(pname).asInstanceOf[T]) else tail.find(pname, site)
}

注意,这里的f就是PartialParameters的构造参数,也就是MiniConfig传递给超类Config的函数:

(site, here, up) => {
    // Core
    case XLEN => 32
    case Trace => true
    case BuildALU    => (p: Parameters) => Module(new ALUArea()(p))
    case BuildImmGen => (p: Parameters) => Module(new ImmGenWire()(p))
    case BuildBrCond => (p: Parameters) => Module(new BrCondArea()(p))
    // Cache
    case NWays => 1 // TODO: set-associative
    case NSets => 256 
    case CacheBlockBytes => 4 * (here(XLEN) >> 3) // 4 x 32 bits = 16B
    // NastiIO
    case NastiKey => new NastiParameters(
        idBits   = 5,
        dataBits = 64,
        addrBits = here(XLEN))
}

至此,我们就可以确定site = p(MiniConfig对象自己)here = new PartialParameters((site, here, up) => {…})(注意这里的this应该是chain的调用对象),up = new TerminalView

而g就是由花括号里的9个case语句组成的偏函数。那么g.isDefinedAt(XLEN)就是true,最终chain返回的结果就是“Some(g.apply(XLEN).asInstanceOf[Int])”即可选值Some(32),注意XLEN是Field[Int]类型的,确定了T是Int。

得到了“val out = Some(32)”后,apply方法的require就能通过,同时返回结果“out.get”即32。最终,“val xlen = p(XLEN)”相当于“val xlen = 32”。也就是说,在混入特质CoreParams的地方,如果有一个隐式Parameters变量是MiniConfig的对象,就会得到一个名为“xlen”的val字段,它的值是32。

关于“here(XLEN)”,因为here已经确定是由f构成的PartialParameters对象,那么套用前述过程,其实也是返回32。

假设偏函数的可行域内没有XLEN,那么chain就会执行“(new TerminalView).find(XLEN, p)”,也就是返回XLEN.default。因为XLEN在定义时没给超类Filed[Int]传递参数,所以会调用Filed[T]的第一个辅助构造函数:

def this() = this(None) 

导致XLEN.default = None。这使得“val out = None”,apply方法的require产生异常报错,并打印信息“Key XLEN is not defined in Parameters”。注意字符串插值会把${pname}求值成XLEN

再来看Core模块里的CoreIO:

abstract class CoreBundle(implicit val p: Parameters) extends Bundle with CoreParams

class HostIO(implicit p: Parameters) extends CoreBundle()(p) {
  val fromhost = Flipped(Valid(UInt(xlen.W)))
  val tohost   = Output(UInt(xlen.W))
}

class CoreIO(implicit p: Parameters) extends CoreBundle()(p) {
  val host = new HostIO
  val icache = Flipped((new CacheIO))
  val dcache = Flipped((new CacheIO))
} 

抽象类CoreBundle混入了特质CoreParams,并接收HostIO传来的隐式参数——MiniConfig的对象(HostIO来自于CoreIO ,CoreIO来自于Core,Core来自于Tile),所以HostIO有了字段“val xlen = 32”,它定义的端口位宽也就是32位的了。

对于偏函数其他的case语句,原理一样:

case object Trace extends Field[Boolean]
case object BuildALU extends Field[Parameters => ALU]
case object BuildImmGen extends Field[Parameters => ImmGen]
case object BuildBrCond extends Field[Parameters => BrCond]
case object NWays extends Field[Int]
case object NSets extends Field[Int]
case object CacheBlockBytes extends Field[Int]
case object NastiKey extends Field[NastiParameters]

case class NastiParameters(dataBits: Int, addrBits: Int, idBits: Int)

if (p(Trace)) {
    printf("PC: %x, INST: %x, REG[%d]             
关注
打赏
1640088279
查看更多评论
0.0453s