Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
cde47f1
TLB: remove implicit parameters
May 5, 2023
ab51a09
TLB: parameterize
May 5, 2023
641b389
TLB: move source file
May 7, 2023
727fc84
TLB: refactor constructor call parameters
May 7, 2023
7754371
TLB: refactor to Chisel native coverage
May 7, 2023
f014cd6
TLB: move `TLBPermissions`
May 8, 2023
50225e5
TLB: move `Consts`
May 8, 2023
b6d23f4
TLB: refactor traits in `Consts` to objects
May 8, 2023
46cbcac
TLB: add minimal `CSR` for usages in `TLB`
May 8, 2023
9f27cd3
TLB: refactor dependencies
May 8, 2023
f25bb2a
TLB: remove unnecessary dependencies
May 8, 2023
20ae4bc
TLB: remove redundant dependencies
May 8, 2023
5a86910
TLB: refactor deprecated code style
May 8, 2023
79bb9fd
TLB: move `Instructions`
May 10, 2023
e951e2a
TLB: add `util`
May 10, 2023
d53136b
TLB: refactor `TLSlaveParameters` related things
May 15, 2023
f590f91
TLB: refactor memory slave parameters
May 17, 2023
b10b560
TLB: refactor `AddressSet`
May 19, 2023
c0b907d
TLB: minor modifications
May 22, 2023
58c6fe4
TLB: move `AddressDecoder`
May 22, 2023
2d67054
TLB: remove some clutters
Jun 1, 2023
8974671
TLB: fix Option syntax
Jun 1, 2023
030f957
Merge branch 'split' into refactor_tlb
Jun 1, 2023
3e7498b
TLB: resolve collisions
Jun 3, 2023
6134d13
TLB: resolve dependencies
Jun 3, 2023
8bda869
PTW: migration and introduce dependencies
Jun 3, 2023
d7168cf
TLB: resolve dependencies
Jun 5, 2023
2d7cadc
TLB: migrate HellaCache
Jun 5, 2023
8557028
TLB: restore diplomatic
Jun 6, 2023
784e8bb
TLB: resolve diplomatic dependency conflicts
Jun 6, 2023
8d0dac1
Merge branch 'split' into refactor_tlb
Jun 6, 2023
9549113
TLB: remove some diplomatic
Jun 6, 2023
8ee5169
Update .gitignore
Jun 6, 2023
f638f26
Introduce components in util
Jun 12, 2023
f9bc252
Add CoreMonitorBundle
Jun 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
out/
.*.sw[a-p]
.bsp
.idea
.bsp/
.idea/
.bloop/
.metals/
.vscode/
.scala-build/
24 changes: 24 additions & 0 deletions rocket/src/util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package org.chipsalliance.rocket

import chisel3._
import chisel3.util._
import scala.collection.immutable
import scala.collection.mutable

//todo: remove util
package object util {
Expand Down Expand Up @@ -189,4 +191,26 @@ package object util {

implicit def uintToBitPat(x: UInt): BitPat = BitPat(x)

def bitIndexes(x: BigInt, tail: Seq[Int] = Nil): Seq[Int] = {
require (x >= 0)
if (x == 0) {
tail.reverse
} else {
val lowest = x.lowestSetBit
bitIndexes(x.clearBit(lowest), lowest +: tail)
}
}

/** Similar to Seq.groupBy except this returns a Seq instead of a Map
* Useful for deterministic code generation
*/
def groupByIntoSeq[A, K](xs: Seq[A])(f: A => K): immutable.Seq[(K, immutable.Seq[A])] = {
val map = mutable.LinkedHashMap.empty[K, mutable.ListBuffer[A]]
for (x <- xs) {
val key = f(x)
val l = map.getOrElseUpdate(key, mutable.ListBuffer.empty[A])
l += x
}
map.view.map({ case (k, vs) => k -> vs.toList }).toList
}
}
134 changes: 134 additions & 0 deletions rocket/src/util/AddressDecoder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// See LICENSE.SiFive for license details.

package org.chipsalliance.rocket.util

import Chisel.log2Ceil

object AddressDecoder
{
type Port = Seq[AddressSet]
type Ports = Seq[Port]
type Partition = Ports
type Partitions = Seq[Partition]

val addressOrder = Ordering.ordered[AddressSet]
val portOrder = Ordering.Iterable(addressOrder)
val partitionOrder = Ordering.Iterable(portOrder)

// Find the minimum subset of bits needed to disambiguate port addresses.
// ie: inspecting only the bits in the output, you can look at an address
// and decide to which port (outer Seq) the address belongs.
def apply(ports: Ports, givenBits: BigInt = BigInt(0)): BigInt = {
val nonEmptyPorts = ports.filter(_.nonEmpty)
if (nonEmptyPorts.size <= 1) {
givenBits
} else {
// Verify the user did not give us an impossible problem
nonEmptyPorts.combinations(2).foreach { case Seq(x, y) =>
x.foreach { a => y.foreach { b =>
require (!a.overlaps(b), s"Ports cannot overlap: $a $b")
} }
}

val maxBits = log2Ceil(1 + nonEmptyPorts.map(_.map(_.base).max).max)
val (bitsToTry, bitsToTake) = (0 until maxBits).map(BigInt(1) << _).partition(b => (givenBits & b) == 0)
val partitions = Seq(nonEmptyPorts.map(_.sorted).sorted(portOrder))
val givenPartitions = bitsToTake.foldLeft(partitions) { (p, b) => partitionPartitions(p, b) }
val selected = recurse(givenPartitions, bitsToTry.reverse.toSeq)
val output = selected.reduceLeft(_ | _) | givenBits

// Modify the AddressSets to allow the new wider match functions
val widePorts = nonEmptyPorts.map { _.map { _.widen(~output) } }
// Verify that it remains possible to disambiguate all ports
widePorts.combinations(2).foreach { case Seq(x, y) =>
x.foreach { a => y.foreach { b =>
require (!a.overlaps(b), s"Ports cannot overlap: $a $b")
} }
}

output
}
}

// A simpler version that works for a Seq[Int]
def apply(keys: Seq[Int]): Int = {
val ports = keys.map(b => Seq(AddressSet(b, 0)))
apply(ports).toInt
}

// The algorithm has a set of partitions, discriminated by the selected bits.
// Each partion has a set of ports, listing all addresses that lead to that port.
// Seq[Seq[Seq[AddressSet]]]
// ^^^^^^^^^^^^^^^ set of addresses that are routed out this port
// ^^^ the list of ports
// ^^^ cases already distinguished by the selected bits thus far
//
// Solving this problem is NP-hard, so we use a simple greedy heuristic:
// pick the bit which minimizes the number of ports in each partition
// as a secondary goal, reduce the number of AddressSets within a partition

def bitScore(partitions: Partitions): Seq[Int] = {
val maxPortsPerPartition = partitions.map(_.size).max
val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max
val sumSquarePortsPerPartition = partitions.map(p => p.size * p.size).sum
val sumSquareSetsPerPartition = partitions.map(_.map(p => p.size * p.size).sum).max
Seq(maxPortsPerPartition, maxSetsPerPartition, sumSquarePortsPerPartition, sumSquareSetsPerPartition)
}

def partitionPort(port: Port, bit: BigInt): (Port, Port) = {
val addr_a = AddressSet(0, ~bit)
val addr_b = AddressSet(bit, ~bit)
// The addresses were sorted, so the filtered addresses are still sorted
val subset_a = port.filter(_.overlaps(addr_a))
val subset_b = port.filter(_.overlaps(addr_b))
(subset_a, subset_b)
}

def partitionPorts(ports: Ports, bit: BigInt): (Ports, Ports) = {
val partitioned_ports = ports.map(p => partitionPort(p, bit))
// because partitionPort dropped AddresSets, the ports might no longer be sorted
val case_a_ports = partitioned_ports.map(_._1).filter(!_.isEmpty).sorted(portOrder)
val case_b_ports = partitioned_ports.map(_._2).filter(!_.isEmpty).sorted(portOrder)
(case_a_ports, case_b_ports)
}

def partitionPartitions(partitions: Partitions, bit: BigInt): Partitions = {
val partitioned_partitions = partitions.map(p => partitionPorts(p, bit))
val case_a_partitions = partitioned_partitions.map(_._1).filter(!_.isEmpty)
val case_b_partitions = partitioned_partitions.map(_._2).filter(!_.isEmpty)
val new_partitions = (case_a_partitions ++ case_b_partitions).sorted(partitionOrder)
// Prevent combinational memory explosion; if two partitions are equal, keep only one
// Note: AddressSets in a port are sorted, and ports in a partition are sorted.
// This makes it easy to structurally compare two partitions for equality
val keep = (new_partitions.init zip new_partitions.tail) filter { case (a,b) => partitionOrder.compare(a,b) != 0 } map { _._2 }
new_partitions.head +: keep
}

// requirement: ports have sorted addresses and are sorted lexicographically
val debug = false
def recurse(partitions: Partitions, bits: Seq[BigInt]): Seq[BigInt] = {
if (partitions.map(_.size <= 1).reduce(_ && _)) Seq() else {
if (debug) {
println("Partitioning:")
partitions.foreach { partition =>
println(" Partition:")
partition.foreach { port =>
print(" ")
port.foreach { a => print(s" ${a}") }
println("")
}
}
}
val candidates = bits.map { bit =>
val result = partitionPartitions(partitions, bit)
val score = bitScore(result)
if (debug)
println(" For bit %x, %s".format(bit, score.toString))
(score, bit, result)
}
val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Seq[Int], BigInt, Partitions), Iterable[Int]](_._1.toIterable))
if (debug) println("=> Selected bit 0x%x".format(bestBit))
bestBit +: recurse(bestPartitions, bits.filter(_ != bestBit))
}
}
}
51 changes: 51 additions & 0 deletions rocket/src/util/ClockGate.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// See LICENSE.SiFive for license details.

package org.chipsalliance.rocket.util

import chisel3._
import chisel3.util.{HasBlackBoxResource, HasBlackBoxPath}

import java.nio.file.{Files, Paths}

abstract class ClockGate extends BlackBox
with HasBlackBoxResource with HasBlackBoxPath {
val io = IO(new Bundle{
val in = Input(Clock())
val test_en = Input(Bool())
val en = Input(Bool())
val out = Output(Clock())
})

def addVerilogResource(vsrc: String): Unit = {
if (Files.exists(Paths.get(vsrc)))
addPath(vsrc)
else
addResource(vsrc)
}
}

object ClockGate {
def apply[T <: ClockGate](
in: Clock,
en: Bool,
modelFile: Option[String],
name: Option[String] = None): Clock = {
val cg = Module(new EICG_wrapper)
name.foreach(cg.suggestName(_))
modelFile.map(cg.addVerilogResource(_))

cg.io.in := in
cg.io.test_en := false.B
cg.io.en := en
cg.io.out
}

def apply[T <: ClockGate](
in: Clock,
en: Bool,
name: String): Clock =
apply(in, en, Some(name))
}

// behavioral model of Integrated Clock Gating cell
class EICG_wrapper extends ClockGate
28 changes: 28 additions & 0 deletions rocket/src/util/CoreMonitorBundle.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// See LICENSE.Berkeley for license details.
// See LICENSE.SiFive for license details.

package org.chipsalliance.rocket.util

import chisel3._

// this bundle is used to expose some internal core signals
// to verification monitors which sample instruction commits
class CoreMonitorBundle(val xLen: Int, val fLen: Int) extends Bundle {
val excpt = Bool()
val priv_mode = UInt(width = 3.W)
val hartid = UInt(width = xLen.W)
val timer = UInt(width = 32.W)
val valid = Bool()
val pc = UInt(width = xLen.W)
val wrdst = UInt(width = 5.W)
val wrdata = UInt(width = (xLen max fLen).W)
val wrenx = Bool()
val wrenf = Bool()
@deprecated("replace wren with wrenx or wrenf to specify integer or floating point","Rocket Chip 2020.05")
def wren: Bool = wrenx || wrenf
val rd0src = UInt(width = 5.W)
val rd0val = UInt(width = xLen.W)
val rd1src = UInt(width = 5.W)
val rd1val = UInt(width = xLen.W)
val inst = UInt(width = 32.W)
}
30 changes: 30 additions & 0 deletions rocket/src/util/DescribedSRAM.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// See LICENSE.Berkeley for license details.
// See LICENSE.SiFive for license details.

package org.chipsalliance.rocket.util

import chisel3.{Data, SyncReadMem, Vec}
import chisel3.util.log2Ceil

object DescribedSRAM {
def apply[T <: Data](
name: String,
desc: String,
size: BigInt, // depth
data: T
): SyncReadMem[T] = {

val mem = SyncReadMem(size, data)

mem.suggestName(name)

val granWidth = data match {
case v: Vec[_] => v.head.getWidth
case d => d.getWidth
}

val uid = 0

mem
}
}
Loading