偏函数,在Scala中它是一个Trait, PartialFunction[A, B], 它接受一个A参数,返回一个B。
val f: PartialFunction[Int, String] = {
case 1 => "One",
case 2 => "Two",
case 3 => "Three",
case _ => "Other"
} 在Rocket-chip等生成器中,可以通过配置Config文件来定制生成CPU,接下来将介绍其实现原理
package freechips.rocketchip.config
abstract class Field[T] private(val default: Option[T])
{
def this() = this(None)
def this(default: T) = this(Some(default))
} 我们在config这个子模块中定义了Field类,作为一个抽象类它无法实例化,但是它提供了两个方法,this放回一个为None的Option,而this(default: T), 当以了一个内容为default的Option。
这个类型可以将所有的类型全部加一层Option。
abstract class View {
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
}
final def lift[T](pname: Field[T]): Option[T] = lift(pname, this)
final def lift[T](pname: Filed[T], site: View): Option[T] = find(pname, site) .map(_.asInstanceOf[T])
protected[config] def find[T](pname: Field[T], site: View): Option[T]
} 这里find是一个抽象方法,我们需要在继承View后,apply中调用了find方法。
abstract class Parameters extends View {
final def ++ (x: Parameters): Parameters =
new ChainParameters(this, x)
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]
protected[config] def find[T](pname: Field[T], site: View) = chain(site, new TerminalView, pname)
}
object Parameters {
def empty: Parameters = new EmptyParameters
def apply(f: (View, View, View) => PartialFunction[Any,Any]): Parameters = new PartialParameters(f)
}
private class TerminalView extends View {
def find[T](pname: Field[T], site: View): Option[T] = pname.default
}
private class ChainView(head: Parameters, tail: View) extends View {
def find[T](pname: Field[T], site: View) = head.chain(site, tail, pname)
} Parameters继承了View,实现了find方法,但是方法也调用了chain,chian中的site表示当前视图,tail表示了下一试图,pname则是要找的配置项。
class Config(p: Parameters) extends Parameters {
def this(f: (View, View, View)) => PartialFunction[Any, Any]) = this(Parameters(f))
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
}
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]) = {
val g = f(site, this, tail)
if (g.isDefinedAt(pname)) Some(g.apply(pname).asInstanceOf[T]) 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)
}
} 这里Config继承了Parameters,chian中直接调用了p参数的chain方法。使用PartialParameters,它是一个私有类。它会调用传入的隐式参数方法。
package mini
import chisel3.Module
import freechips.rocketchip.config.{Parameters, Config}
import junctions._
class MiniConfig extends Config((site, here, up)) => {
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))
case NWays => 1
case NSets => 256
case CacheBlockBytes => 4 * (here(XLEN) >> 3)
case NastiKey => new NastiParameters(
idBits = 5,
dataBits = 64,
addrBits = here(XLEN))
)
}实际使用的时候,可以先看顶层:
val params = (new MiniConfig).toInstance
val chirrtl = firrtl.Parser.parse(chisel3.Driver.emit(() => new Tile(params))) 这里在Tile中传入了param参数,由于隐私参数的性质,这个参数可以不断向下传播。
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.cache <> 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模块把入参赋值给隐式参数p。之后传入接下来所有的模块。以Core模块为例
abstract trait CoreParams {
implicit val p: Parameters
val xlen = p(XLEN)
} 这里p我们知道传入的是一个miniConfig, 有一个父类类型Parameter进行接受是合法的。接着我们调用了p(XLEN), 由于传入的XLEN是一个Field类型,这里会调用,View的apply方法,
最终会调用:
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
}这里的find又会调用之前介绍的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)
} 之后就会跟我们的(site, here, tail) => {case...}进行匹配。