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的两个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是View的子类,它的确实现了find方法,但是又引入了抽象的chain方法,所以我们只需要关心Parameters的子类是如何实现chain方法的。子类中的chain方法的行为,才真正地决定着查找参数的逻辑。因为下面不再有子类了,chain方法也不是抽象的,而是有具体内容的方法了。所以我们非常有必要解读下Parameters
的子类是如何实现自己的抽象chain方法的。
主要有三个子类需要关注:PartialParameters
、ChainParameters
、EmptyParameters
。
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。这里很重要,机制中自动往上层查找参数的逻辑就是这里实现的。
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。
++、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类也是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
之间的继承关系如下:
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
。
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]
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?