8.5. Context-Dependent-Environments

Readers may notice that the parameterization system frequently uses (site, here, up). This construct is an artifact of the “context-dependent-environment” system which Chipyard and Rocket Chip both leverage for powerful composable hardware configuration.

The CDE parameterization system provides different “Views” of a single global parameterization. The syntax for accessing a Field within a View is my_view(MyKey, site_view), where site_view is a “global” view that will be passed recursively into various functions and key-lookups in the call-stack of my_view(MyKey, site_view).

Note

Rocket Chip based designs will frequently use val p: Parameters and p(SomeKey) to lookup the value of a key. Parameters is just a subclass of the View abstract class, and p(SomeKey) really expands into p(SomeKey, p). This is because we consider the call p(SomeKey) to be the “site”, or “source” of the original key query, so we need to pass in the view of the configuration provided by p recursively to future calls through the site argument.

Consider the following example using CDEs.

case object SomeKeyX extends Field[Boolean](false) // default is false
case object SomeKeyY extends Field[Boolean](false) // default is false
case object SomeKeyZ extends Field[Boolean](false) // default is false

class WithX(b: Boolean) extends Config((site, here, up) => {
  case SomeKeyX => b
})

class WithY(b: Boolean) extends Config((site, here, up) => {
  case SomeKeyY => b
})

When forming a query based on a Parameters object, like p(SomeKeyX), the configuration system traverses the “chain” of config fragments until it finds a partial function which is defined at the key, and then returns that value.

val params = new Config(new WithX(true) ++ new WithY(true)) // "chain" together config fragments
params(SomeKeyX) // evaluates to true
params(SomeKeyY) // evaluates to true
params(SomeKeyZ) // evaluates to false

In this example, the evaluation of params(SomeKeyX) will terminate in the partial function defined in WithX(true), while the evaluation of params(SomeKeyY) will terminate in the partial function defined in WithY(true). Note that when no partial functions match, the evaluation will return the default value for that parameter.

Config fragments take precedence from left to right, meaning that a fragment at the start of the chain can override the value of a fragment to the right. It helps to read the fragment chain from right to left.

case object SomeKeyX extends Field[Int](0)

class WithX(n: Int) extends Config((site, here, up) => {
  case SomeKeyX => n
})

val params = new Config(new WithX(10) ++ new WithX(5))
println(params(SomeKeyX)) // evaluates to 10

The real power of CDEs arises from the (site, here, up) parameters to the partial functions, which provide useful “views” into the global parameterization that the partial functions may access to determine a parameterization.

Note

Additional information on the motivations for CDEs can be found in Chapter 2 of Henry Cook’s Thesis .

8.5.1. Site

site provides a View of the “source” of the original parameter query.

class WithXEqualsYSite extends Config((site, here, up) => {
  case SomeKeyX => site(SomeKeyY) // expands to site(SomeKeyY, site)
})

val params_1 = new Config(new WithXEqualsYSite ++ new WithY(true))
val params_2 = new Config(new WithY(true) ++ new WithXEqualsYSite)
params_1(SomeKeyX) // evaluates to true
params_2(SomeKeyX) // evaluates to true

In this example, the partial function in WithXEqualsYSite will look up the value of SomeKeyY in the original params_N object, which becomes site in each call in the recursive traversal.

8.5.2. Here

here provides a View of the locally defined config, which typically just contains some partial function.

class WithXEqualsYHere extends Config((site, here, up) => {
  case SomeKeyY => false
  case SomeKeyX => here(SomeKeyY, site)
})

val params_1 = new Config(new WithXEqualsYHere ++ new WithY(true))
val params_2 = new Config(new WithY(true) ++ new WithXEqualsYHere)

params_1(SomeKeyX) // evaluates to false
params_2(SomeKeyX) // evaluates to false

In this example, note that although our final parameterization in params_2 has SomeKeyY set to true, the call to here(SomeKeyY, site) only looks in the local partial function defined in WithXEqualsYHere. Note that we pass site to here since site may be used in the recursive call.

8.5.3. Up

up provides a View of the previously defined set of partial functions in the “chain” of partial functions. This is useful when we want to lookup a previously set value for some key, but not the final value for that key.

class WithXEqualsYUp extends Config((site, here, up) => {
  case SomeKeyX => up(SomeKeyY, site)
})

val params_1 = new Config(new WithXEqualsYUp ++ new WithY(true))
val params_2 = new Config(new WithY(true) ++ new WithXEqualsYUp)

params_1(SomeKeyX) // evaluates to true
params_2(SomeKeyX) // evaluates to false

In this example, note how up(SomeKeyY, site) in WithXEqualsYUp will refer to either the the partial function defining SomeKeyY in WithY(true) or the default value for SomeKeyY provided in the original case object SomeKeyY definition, depending on the order in which the config fragments were used. Since the order of config fragments affects the the order of the View traversal, up provides a different View of the parameterization in params_1 and params_2.

Also note that again, site must be recursively passed through the call to up.