Nuke
This commit is contained in:
commit
932413bb3d
61 changed files with 7249 additions and 0 deletions
352
src/test/scala/RISCV/printUtils.scala
Normal file
352
src/test/scala/RISCV/printUtils.scala
Normal file
|
@ -0,0 +1,352 @@
|
|||
package FiveStage
|
||||
import cats.data.Writer
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
|
||||
import fansi._
|
||||
|
||||
import Ops._
|
||||
import Data._
|
||||
import VM._
|
||||
import fileUtils._
|
||||
|
||||
object PrintUtils {
|
||||
|
||||
// Minimal typeclass def for being fancy
|
||||
trait Fancy[-A] { def s(a: A): fansi.Str }
|
||||
implicit class FancyOps[-A](a: A)(implicit ev: Fancy[A]){ def show: fansi.Str = ev.s(a) }
|
||||
implicit val DoubleFancy = new Fancy[fansi.Str]{ def s(a: fansi.Str) = a }
|
||||
implicit val FancyMonoid = new Monoid[fansi.Str] {
|
||||
def empty: fansi.Str = fansi.Str("")
|
||||
def combine(self: fansi.Str, that: fansi.Str): fansi.Str = self ++ that
|
||||
}
|
||||
|
||||
implicit class ExtraFancy(a: fansi.Str) {
|
||||
def leftPad(length: Int): fansi.Str = Str((0 until length).map(_ => ' ').mkString) ++ a
|
||||
def replace(from: Char, to: Char): fansi.Str = fansi.Str(a.toString.replace(from, to))
|
||||
def padTo(length: Int, pad: Char = ' '): fansi.Str = a ++ (0 until length - a.length).map(_ => pad).mkString
|
||||
def trim: fansi.Str = fansi.Str(a.toString.trim)
|
||||
}
|
||||
|
||||
implicit val RegFancy = new Fancy[Reg] { def s(r: Reg) = fansi.Str(regNames.canonicalName.lift(r.value).getOrElse("ERROR")) }
|
||||
implicit val ImmFancy = new Fancy[Imm] { def s(r: Imm) = Str(r.value.hs) }
|
||||
implicit val AddrFancy = new Fancy[Addr]{ def s(r: Addr) = Str(r.value.hs) }
|
||||
|
||||
implicit val ExecutionEventFancy = new Fancy[ExecutionEvent]{
|
||||
def s(a: ExecutionEvent): fansi.Str = a match {
|
||||
case RegUpdate(reg, word) => Str(s"R(${reg.show}) <- 0x${word.hs}")
|
||||
case MemWrite(addr, word) => fansi.Color.Blue(s"M[${addr.show}] <- 0x${word.hs}")
|
||||
case MemRead(addr, word) => fansi.Color.Red(f"M[${addr.show}] -> 0x${word.hs}")
|
||||
|
||||
// addr is the target address
|
||||
case PcUpdateJALR(addr) => fansi.Color.Green(s"PC updated to ${addr.show} via JALR")
|
||||
case PcUpdateJAL(addr) => fansi.Color.Magenta(s"PC updated to ${addr.show} via JAL")
|
||||
case PcUpdateB(addr) => fansi.Color.Yellow(s"PC updated to ${addr.show} via Branch")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val ExecutionTraceEventFancy = new Fancy[ExecutionTraceEvent]{
|
||||
def s(a: ExecutionTraceEvent): fansi.Str = a.event.toList match {
|
||||
case (h: ExecutionEvent) :: (t: RegUpdate) :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemWrite) :: t :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemRead) :: t :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemWrite) :: Nil => h.show.leftPad(25)
|
||||
case (h: MemRead) :: Nil => h.show.leftPad(25)
|
||||
case h :: t :: Nil => h.show ++ t.show
|
||||
case (h: RegUpdate) :: Nil => h.show
|
||||
case h :: Nil => h.show.leftPad(25)
|
||||
case _ => Str("")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val ChiselEventFancy = new Fancy[ChiselEvent]{
|
||||
def s(e: ChiselEvent) = e match {
|
||||
case ChiselRegEvent(addr, reg, word) => fansi.Str(s"R(${reg.show}) <- ${word.hs}")
|
||||
case ChiselMemWriteEvent(addr, memAddr, word) => fansi.Str(s"M[${memAddr.show}] <- ${word.hs}")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val RegsFancy = new Fancy[Regs]{
|
||||
def s(e: Regs) = {
|
||||
(0 to 9).map{ idx =>
|
||||
val cols = List.range(idx, 32, 10)
|
||||
cols.map{ reg =>
|
||||
(fansi.Color.Yellow(Reg(reg).show.padTo(5)) ++ Str(e.repr.lift(Reg(reg)).getOrElse(0).hs)).padTo(16)
|
||||
}.showN("| ")
|
||||
}.toList.showN("\n", "\n", "\n")
|
||||
}
|
||||
}
|
||||
|
||||
val UNKNOWN = "UNKNOWN"
|
||||
|
||||
def printInstruction(op: Ops.Op, labelMap: Map[Label, Addr]): fansi.Str = op match {
|
||||
case op: Branch => fansi.Color.Red(s"B${op.comp}\t${op.rs1.show}, ${op.rs2.show}, ${op.dst.show}\t[${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: Arith => s"${op.op}\t${op.rd.show}, ${op.rs1.show}, ${op.rs2.show}"
|
||||
case op: ArithImm => s"${op.op}I\t${op.rd.show}, ${op.rs1.show}, ${op.imm.show}"
|
||||
case op: JALR => fansi.Color.Green(s"JALR\t${op.rd.show}, ${op.rs1.show}, ${op.dst.show}\t[${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: JAL => fansi.Color.Magenta(s"JAL\t${op.rd.show}, ${op.dst.show} [${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: LW => s"LW\t${op.rd.show}, ${op.offset.show}(${op.rs1.show})"
|
||||
case op: SW => s"SW\t${op.rs2.show}, ${op.offset.show}(${op.rs1.show})"
|
||||
case op: LUI => s"LUI\t${op.rd.show}, ${op.imm.show}"
|
||||
case op: AUIPC => s"AUIPC\t${op.rd.show}, ${op.imm.show}"
|
||||
case DONE => s"DONE"
|
||||
}
|
||||
|
||||
|
||||
implicit class IntPrinters(i: Int) {
|
||||
def hs: String = if(i > 65535) f"0x$i%08X" else f"0x$i%04X"
|
||||
def binary: String = String.format("%" + 32 + "s", i.toBinaryString)
|
||||
.replace(' ', '0').grouped(4)
|
||||
.map(x => x + " ").mkString
|
||||
}
|
||||
|
||||
|
||||
|
||||
def printProgram(vm: VM): String = {
|
||||
val withLabels: List[Either[(Addr, Op), (Label, Addr)]] = (vm.imem.toList.map(Left(_)) ::: vm.labelMap.toList.map(Right(_)))
|
||||
.sortBy { case Left(x) => x._1.value.toDouble + 0.1; case Right(x) => x._2.value.toDouble }
|
||||
|
||||
withLabels.map{
|
||||
case Left((addr, op)) => s"$addr:\t\t${printInstruction(op, vm.labelMap)}"
|
||||
case Right((label, addr)) => s"$addr:\t$label:"
|
||||
}.mkString("\n","\n","\n")
|
||||
}
|
||||
|
||||
|
||||
def printProgram(p: Program): String = printProgram(p.vm)
|
||||
|
||||
|
||||
def printBinary(bin: Map[Addr, Int]): String = {
|
||||
bin.toList.sortBy(_._1.value).map{ case(addr, op) => s"$addr: ${op.hs}" }.mkString("\n","\n","\n")
|
||||
}
|
||||
|
||||
|
||||
def printChiselLogEntry(event: (Addr, List[ChiselEvent])): fansi.Str = {
|
||||
val (addr, log) = event
|
||||
val events = log match {
|
||||
case (h: ChiselRegEvent) :: (t: ChiselMemWriteEvent) :: Nil => h.show.padTo(25, ' ') ++ t.show
|
||||
case (h: ChiselMemWriteEvent) :: Nil => h.show.leftPad(25)
|
||||
case (h: ChiselRegEvent) :: Nil => h.show
|
||||
case _ => fansi.Str("")
|
||||
}
|
||||
events
|
||||
}
|
||||
|
||||
|
||||
def printVMtrace(trace: List[ExecutionTraceEvent], program: Program): String = {
|
||||
val blocks: List[List[(ExecutionTraceEvent, Int)]] = LogParser.splitToBlocks(trace).zipWithIndexNested
|
||||
blocks.map{ block =>
|
||||
val name = LogParser.guessBlockName(block.map(_._1.pc), program.labelMapReverse)
|
||||
val blockString = block.map{ case(event, idx) =>
|
||||
Str(s"Step: $idx,").padTo(12) ++
|
||||
Str("PC: ") ++
|
||||
event.pc.show ++
|
||||
Str("\t") ++
|
||||
event.show.padTo(70) ++
|
||||
printSourceLine(event.pc, program)
|
||||
}
|
||||
fansi.Color.Yellow(name) ++ Str("\n") ++ blockString.showN("\n")
|
||||
}.mkStringN
|
||||
}
|
||||
|
||||
def printSourceLine(addr: Addr, program: Program): fansi.Str = program.sourceMap.lift(addr).map(fansi.Str(_)).getOrElse(fansi.Str("???"))
|
||||
|
||||
def printMergedTraces(
|
||||
mt: (List[ExecutionTraceEvent], List[CircuitTrace]),
|
||||
program: Program
|
||||
): List[String] = {
|
||||
|
||||
|
||||
/**
|
||||
* For branch predicting processors we may end up with a spurious block causing a transient
|
||||
* desynchronization. Therefore it is necessary to have a certain tolerance before calling a divergence.
|
||||
*/
|
||||
// TODO: Implement a synchronization test to pinpoint where a desync happens
|
||||
// This is not straight forward in the case of processors which perform branch prediction.
|
||||
def isSynchronized(desyncs: Int, traces: (List[ExecutionTraceEvent], List[CircuitTrace]) ): Boolean = ???
|
||||
|
||||
|
||||
def helper(mt: (List[ExecutionTraceEvent], List[CircuitTrace])): List[List[fansi.Str]] = mt match {
|
||||
/**
|
||||
* VM trace and execution log synchronized, step both
|
||||
*/
|
||||
case (hVM :: tVM, hC :: tC) if (hVM.pc == hC._1) => {
|
||||
val address = hVM.pc
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = hVM.show
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(address.show, chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((tVM, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* VM trace and execution log unsynchronized, step only chisel trace
|
||||
* Helper is called with (hVM :: tVM) to only step the chisel log.
|
||||
*/
|
||||
case (hVM :: tVM, hC :: tC) => {
|
||||
val address = hC._1
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = Str("")
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(address.show, chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((hVM :: tVM, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Block contains no more instructions performed by the VM. This
|
||||
* happens naturally since 5-stage pipelines will fetch spurious instructions.
|
||||
*/
|
||||
case (Nil, hC :: tC) => {
|
||||
val address = hC._1
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = fansi.Str("")
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(fansi.Color.LightBlue(address.show), chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((Nil, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Block contains no more instructions performed by the circuit.
|
||||
* This happens when the circuit takes a jump the VM does not.
|
||||
* This is an error *unless* the circuit has a branch predictor.
|
||||
*
|
||||
* If you want to you can splice the logs when this happens, but it's typically easy to spot.
|
||||
*/
|
||||
case (hVM :: tVM, Nil) => {
|
||||
val address = hVM.pc
|
||||
val chiselLogEntry = fansi.Str("")
|
||||
val vmEntry = hVM.show
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(fansi.Color.LightRed(address.show), chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((tVM, Nil))
|
||||
}
|
||||
|
||||
case (Nil, Nil) => Nil
|
||||
}
|
||||
|
||||
|
||||
def format(triplet: List[fansi.Str]): String = {
|
||||
// Ahh, the GNU toolchain and its tabs
|
||||
val annoyingTabCharacter = ' '
|
||||
triplet match {
|
||||
case address :: ch :: vm :: source :: Nil => {
|
||||
val vmLine: fansi.Str = vm.padTo(60, ' ')
|
||||
val chiselLine: fansi.Str = ch.padTo(50, ' ')
|
||||
val sourceLines: fansi.Str = source
|
||||
((address ++ Str(":")).padTo(14, ' ') ++ vmLine ++ fansi.Str("| ") ++ chiselLine ++ Str("| ") ++ sourceLines).toString
|
||||
}
|
||||
case _ => ""
|
||||
}
|
||||
}
|
||||
|
||||
val blockName = LogParser.guessBlockName(mt._1.map(_.pc), program.labelMapReverse)
|
||||
|
||||
(fansi.Color.Yellow(s"$blockName").padTo(74, ' ').toString ++ "|".padTo(55, ' ') ++ "|") :: helper(mt).map(format)
|
||||
}
|
||||
|
||||
|
||||
def printChiselError(e: Throwable): String = {
|
||||
val rawError = """
|
||||
|This typically occurs when you forget to wrap a module
|
||||
|e.g
|
||||
|
|
||||
|class MyBundle extends Bundle {
|
||||
| val signal = UInt(32.W)
|
||||
|}
|
||||
|val mySignal = new MyBundle
|
||||
| ^^^^ Wrong!
|
||||
|should be
|
||||
|val mySignal = Wire(new MyBundle)
|
||||
""".stripMargin
|
||||
|
||||
def getFiveStageStacktrace(strace: Array[StackTraceElement]): String =
|
||||
strace.map(_.toString).filter(_.contains("FiveStage")).toList.take(5).mkStringN
|
||||
|
||||
e match {
|
||||
case e: firrtl.passes.CheckInitialization.RefNotInitializedException => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design has unconnected wires!"
|
||||
|error:\n"
|
||||
|${e.getMessage}
|
||||
|
|
||||
|
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: chisel3.core.Binding.ExpectedHardwareException => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design is using raw chisel types!
|
||||
|error:\n
|
||||
|${e.getMessage}
|
||||
|
|
||||
|$rawError
|
||||
|The error should be here somewhere:
|
||||
|${getFiveStageStacktrace(e.getStackTrace)}
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: firrtl.passes.PassExceptions => {
|
||||
val rawErrorMsg = if(e.getMessage.contains("raw"))
|
||||
s"One of the errors was use of raw chisel types. $rawError"
|
||||
else
|
||||
""
|
||||
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design has multiple errors!
|
||||
|error:
|
||||
|${e.getMessage}
|
||||
|$rawErrorMsg
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: Exception => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Unexpected Chisel tester error (could be a chisel error I havent accounted for, or a bug in the tester like index out of bounds.)
|
||||
|error:
|
||||
|${e.getMessage}
|
||||
|reduced stacktrace:
|
||||
|${getFiveStageStacktrace(e.getStackTrace)}
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def printLogSideBySide(trace: List[ExecutionTraceEvent], chiselTrace: List[CircuitTrace], program: Program): String = {
|
||||
import LogParser._
|
||||
val traces = mergeTraces(trace, chiselTrace).map(x => printMergedTraces((x), program))
|
||||
traces.map(_.mkString("\n")).mkString("\n", "\n--------------------------------------------------------------------------+------------------------------------------------------+-------------------------------\n", "\n")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue