package rself

import chisel3._
import chisel3.util._
import chisel3.testers._
import org.scalatest.FlatSpec

import scala.io.Source
import scala.collection.mutable.ListBuffer

object PesudoAssemblyTools {
  import Constants._
  def LOAD(c: String, rd: Long, imm: Long, rs1: Long) = IMMTYPE("I", "LOAD", rd, rs1, 0, imm, LoadCodes(c), 0)
  def STORE(c: String, rs2: Long, imm: Long, rs1: Long) = IMMTYPE("S", "STORE", 0, rs1, rs2, imm, StoreCodes(c), 0)
  def IMMTYPE(immtype: String, op: String = "Z", rd: Long = 0, rs1: Long = 0, rs2: Long = 0, rawimm: Long = 0, funct3: Long = 0, funct7: Long = 0) = {
    // reassemble imm number to inst
    // mask = lambda a, b=-1: (1<<a) if b == -1 else (1<<(a+1))-(1<<b)
    // bitreplace = lambda p, a, b=-1: '(imm&{})<<{}'.format(hex(mask(a,b)),p - (a if b == -1 else b)).replace('<<-','>>').replace('<<0','')
    // makeimm = lambda args: ' ^ '.join([bitreplace(*a) for a in args])
    val imm_length = Map(
      "R" -> 0,
      "I" -> 12,
      "S" -> 12,
      "U" -> 32,
      "J" -> 21,
      "B" -> 13,
      "Z" -> 0)
    val imm = rawimm & ((1L<<imm_length(immtype)) - 1)
    immtype match {
      case "R" => Opcode(op) ^ rd<<7 ^ (funct3&0x7)<<12 ^ rs1<<15 ^ rs2<<20 ^ funct7<<25 ^ (funct3&0x8)<<27
      case "I" => Opcode(op) ^ rd<<7 ^ (funct3&0x7)<<12 ^ rs1<<15 ^ (funct3&0x8)<<27 ^ imm<<20
      case "S" => Opcode(op) ^ (imm&0x1f)<<7 ^ (funct3&0x7)<<12 ^ rs1<<15 ^ rs2<<20 ^ (imm&0xfe0)<<20
      case "B" => Opcode(op) ^ (funct3&0x7)<<12 ^ rs1<<15 ^ rs2<<20 ^ (imm&0x800)>>4 ^ (imm&0x1e)<<7 ^ (imm&0x7e0)<<20 ^ (imm&0x1000)<<19
      case "U" => Opcode(op) ^ rd<<7 ^ (imm&0xfffff000)
      case "J" => Opcode(op) ^ rd<<7 ^ (imm&0xff000) ^ (imm&0x800)<<9 ^ (imm&0x7fe)<<20 ^ (imm&0x100000)<<11
      case _ => Opcode(op)
    }
  }
  def ITYPE(op: String, rd: Long, rs1: Long, imm: Long) = IMMTYPE("I", "ITYPE", rd, rs1, 0, imm, ALU(op), 0)
  def RTYPE(op: String, rd: Long, rs1: Long, rs2: Long) = IMMTYPE("R", "RTYPE", rd, rs1, rs2, 0, ALU(op), 0)
  def BRC(op: String, rs1: Long, rs2: Long, imm: Long) = IMMTYPE("B", "BRANCH", 0, rs1, rs2, imm, BranchCodes(op), 0)
  def sys = Opcode("SYSTEM")
}

object TestBinary {
  def apply(name: String, bin: scala.collection.Iterable[Long], result: Long) = new TestBinary(name, bin.to[Seq], result)
  def apply(name: String, bin: scala.collection.Iterable[Long]) = {
    import PesudoAssemblyTools._
    val newbin = bin.to[Seq] ++ Seq(
      RTYPE("XOR", 1, 0, 0))
    new TestBinary(name, newbin, 0L)
  }
}

class TestBinary(name: String, bin: Seq[Long], result: Long) {
  def name(): String = name
  def binary(): Seq[Long] = bin
  def vec() = {
    import PesudoAssemblyTools._
    val newbin = bin ++ Seq(
      STORE("SW", 1, 0, 0),
      Constants.Opcode("SYSTEM"))
    VecInit(newbin.map(_.U(32.W)))
  }
  def res() = result.U(32.W)
  def size() = bin.size + 2
}

class DatapathTester(dp: => Datapath, bin: TestBinary) extends BasicTester with TestUtils {
  val tester = Module(dp)
  val mem = Mem(1 << 24, UInt(8.W))
  val runState = RegInit(false.B)

  val writeVaild = RegInit(false.B)
  tester.io.dmem.writer.vaild := writeVaild
  val readVaild = RegInit(false.B)
  val daddr = tester.io.dmem.reader.addr
  tester.io.dmem.reader.data  := Cat(mem(daddr+3.U), mem(daddr+2.U), mem(daddr+1.U), mem(daddr))
  tester.io.dmem.reader.vaild := readVaild
  val iaddr = tester.io.imem.addr
  tester.io.imem.data  := Cat(mem(iaddr+3.U), mem(iaddr+2.U), mem(iaddr+1.U), mem(iaddr))
  tester.io.imem.vaild := runState

  val insts = bin.vec()
  val result = bin.res()
  val (cntr, done) = Counter(~runState, bin.size)

  val stopper = RegInit(0.U(16.W))
  stopper := stopper + 1.U
  when (stopper > 0x8000.U) {
    printf("time too long.\n")
    assert(false.B)
    stop(); stop()
  }

  when (~runState) {
    // memory ready
    val data = insts(cntr)
    val wiaddr = (cntr << 2.U) + Const.PC_START.U(32.W)
    mem(wiaddr      ) := data(7, 0)
    mem(wiaddr + 1.U) := data(15, 8)
    mem(wiaddr + 2.U) := data(23, 16)
    mem(wiaddr + 3.U) := data(31, 24)
    when(done) { runState := true.B }
  } .otherwise {
    when (tester.io.imem.ren & tester.io.imem.vaild) {
      printf("INS[%x] ==> %x, iaddr: %x\n", iaddr, Cat(mem(iaddr+3.U), mem(iaddr+2.U), mem(iaddr+1.U), mem(iaddr)), iaddr)
    }
    when (tester.io.dmem.writer.wen) {
      // memory write
      val waddr = tester.io.dmem.writer.addr
      val wdata = tester.io.dmem.writer.data
      switch(tester.io.dmem.writer.mode) {
        is (0.U) {
          mem(waddr) := wdata(7, 0)
          printf("MEM[%x] <== BYTE %x\n", waddr, wdata(7,0))
        }
        is (1.U) {
          mem(waddr      ) := wdata(7, 0)
          mem(waddr + 1.U) := wdata(15, 8)
          printf("MEM[%x] <== HALF %x\n", waddr, wdata(15,0))
        }
        is (2.U) {
          mem(waddr      ) := wdata(7, 0)
          mem(waddr + 1.U) := wdata(15, 8)
          mem(waddr + 2.U) := wdata(23, 16)
          mem(waddr + 3.U) := wdata(31, 24)
          printf("MEM[%x] <== WORD %x\n", waddr, wdata)
        }
        is (3.U) {
          mem(waddr      ) := wdata(7, 0)
          mem(waddr + 1.U) := wdata(15, 8)
          mem(waddr + 2.U) := wdata(23, 16)
          mem(waddr + 3.U) := wdata(31, 24)
          printf("MEM[%x] <== WORD %x\n", waddr, wdata)
        }
      }
      writeVaild := true.B
    } .otherwise {
      writeVaild := false.B
    }
    when (tester.io.dmem.reader.ren) {
      printf("MEM[%x] ==> %x\n", daddr, Cat(mem(daddr+3.U), mem(daddr+2.U), mem(daddr+1.U), mem(daddr)))
      readVaild := true.B
    } .otherwise {
      readVaild := false.B
    }
    when(tester.io.sys) {
      printf("RESULT %x ?= %x\n", Cat(mem(3), mem(2), mem(1), mem(0)), result)
      assert(Cat(mem(3), mem(2), mem(1), mem(0)) === result)
      stop(); stop()
    }
  }
}

object TestBinaryBundles {
  import PesudoAssemblyTools._

  val first = TestBinary("Hello", Seq(
    ITYPE("XOR", 2, 0, 0x2f), // r2 = 0x2f
    ITYPE("XOR", 3, 0, 0x1c), // r3 = 0x1c
    RTYPE("ADD", 2, 2, 3),    // r2 = r2 + r3
    ITYPE("XOR", 1, 2, 0x14b) // r1(ret) = r2 ^ 0x14b ; == 0x100
  ), 0x100)

  val hundredsum = TestBinary("Hundred-Sum", Seq(
    RTYPE("XOR", 2, 0, 0),      // r2(add) = 0
    RTYPE("XOR", 3, 0, 0),      // r3(sum) = 0
    ITYPE("XOR", 4, 0, 100),    // r4 = 100
    ITYPE("ADD", 2, 2, 1),      // r2 = r2 + 1 ; label
    RTYPE("ADD", 3, 3, 2),      // r3 = r3 + r2
    BRC("BLT", 2, 4, -(2L<<2)), // jump to label if r2 < r4
    RTYPE("XOR", 1, 3, 0)       // r1(ret) = r3
  ), 5050)

  val writeback = TestBinary("Write-Back", Seq(
    RTYPE("XOR", 2, 0, 0),
    ITYPE("ADD", 2, 2, 1),
    ITYPE("ADD", 2, 2, 2),
    ITYPE("ADD", 2, 2, 3),
    ITYPE("ADD", 2, 2, 4),
    RTYPE("XOR", 1, 2, 0)
  ), 10)

  val collection = Array(
    first, writeback,
    TestBinary("Load-Store", Seq(
      RTYPE("XOR", 4, 0, 0),       // r4 = 0
      ITYPE("XOR", 2, 0, -1),      // r2 = 0xffffffff
      STORE("SW",  2, 0, 0),       // SW x2, 0(0)
      LOAD( "LBU",  3, 0, 0),       // LB x3, 0(0)
      ITYPE("SLL", 4, 4, 1),       // r4 <<= 1
      ITYPE("XOR", 5, 0, 0xff),    // r5 = 0xff
      BRC(  "BEQ", 3, 5, (2L<<2)), // jump next code if r3 == r5
      ITYPE("ADD", 4, 4, 1)        // r4 += 1
    ), 0),
    hundredsum
  )
}

class DatapathTests extends FlatSpec {
  TestBinaryBundles.collection foreach { test =>

      // Console.print(s"${test.name}:\n")
      // test.binary foreach { e =>
      //   Console.printf ("  %08x\n", e)
      // }
      assert(TesterDriver execute (() => new DatapathTester(new Datapath, test)))
    }
  }
}

"DataPath" should s"pass ${test.name}" in {


package rself

import chisel3._
import chisel3.util._
import chisel3.testers._
import org.scalatest.FlatSpec

trait ExecuteUtils extends TestUtils {
  val regData = (0 until 32).map((i) => if (i != 0) random32() else 0L)
  def instResults(cs: List4): List4 = {
    val (inst, mode, sel, imm) = cs
    val (data, rd, npc, jmp) = TestList.List4()
    val size = inst.size
    import Constants._
    val Act = ControlActs.map(_.swap)
    val Imm = ImmModes.map(_.swap)
    for (i <- 0 until size) {
      def B(hi: Int, lo: Int = -1) = {
        val low = if (lo == -1) {hi} else {lo}
        (inst(i) & ((1<<(hi+1)) - (1<<low))) >> low
      }
      val rs3 = immData(inst(i), Imm(imm(i)))
      val rs2 = regData(B(24, 20).toInt)
      val rs1 = regData(B(19, 15).toInt)
      val rd0 = B(11, 7)
      // TODO: make assertion results
    }
    return (data, rd, npc, jmp)
  }
}

class ExecuteTester(exe: => Execute) extends BasicTester with ExecuteUtils {
  val tester = Module(exe)
  val (ctl_inst, ctl_mode, ctl_sel, ctl_imm) = randomInsts() // TestUtils::randomInsts
  ctl_inst += 127; ctl_mode += 0; ctl_sel += 4; ctl_imm += 6 // invaild instruction

  val inst = interface(ctl_inst)
  val regs = interface(regData)
  val pcReg = RegInit(Const.PC_START.U(32.W))
  pcReg := pcReg + 1.U
  val (cntr, done) = Counter(true.B, inst.size)
  tester.io.en := true.B
  tester.io.pc := pcReg
  tester.io.inst := inst(cntr)
  tester.io.reg.rdata1 := regs(tester.io.reg.raddr1)
  tester.io.reg.rdata2 := regs(tester.io.reg.raddr2)
  tester.io.mem.writer.vaild := true.B
  tester.io.mem.reader.vaild := true.B
  tester.io.mem.reader.data := 0.U

  printf("Counter: %d, Inst: %b, isJump: %b, nextPC: %x, rd: %d, data: %x, sys:%b\n",
    cntr, tester.io.inst, tester.io.jmp, tester.io.npc, tester.io.rd, tester.io.data, tester.io.sys)
  // TODO: no assertion
}

class ExecuteTests extends FlatSpec {
  "Execute" should "pass" in {
    assert(TesterDriver execute (() => new ExecuteTester(new Execute)))
  }
}


package rself

import chisel3._
import chisel3.util._
import chisel3.testers._
import org.scalatest.FlatSpec

import IMM_MODE._

class ImmGenTester(img: => ImmGen) extends BasicTester with TestUtils {
  val tester = Module(img)
  import scala.collection.mutable.ListBuffer
  val img_inst = TestList()
  val img_mode = TestList()
  val img_val  = TestList()
  for (mod <- Constants.ImmModes; i <- 0 until 15) {
    val (modstr, modop) = mod
    val inst = random32()
    img_inst += inst
    img_mode += modop
    img_val += immData(inst, modstr) // TestUtils::immData
  }

  shuffle(img_inst, img_mode, img_val)
  img_inst += 0; img_mode += IMM_Z.litValue().toLong; img_val += 0 // unreachable test

  val inst  = interface(img_inst)
  val mode  = interface(img_mode, 3)
  val value = interface(img_val)
  val (cntr, done) = Counter(true.B, inst.size)
  tester.io.inst := inst(cntr)
  tester.io.mode := mode(cntr)

  printf("Counter: %d, Inst: %b, mod: %x, value: %x ?= %x\n",
    cntr, tester.io.inst, tester.io.mode, tester.io.value, value(cntr))
  assert(tester.io.value === value(cntr))
}

class ImmGenTests extends FlatSpec {
  "ImmGen" should "pass" in {
    assert(TesterDriver execute (() => new ImmGenTester(new ImmGen)))
  }
}


package rself

import chisel3._
import chisel3.util._
import chisel3.testers._
import org.scalatest.FlatSpec

// This test file is for src/main/scala/Jump.scala
//   with modules `Jump' `AddUpperImmPC' and `LoadUpperImm'

class JumpOnlyTester(jmp: => Jump) extends BasicTester with TestUtils {
  val tester = Module(jmp)
  val mode = TestList()
  val pc   = TestList()
  val imm  = TestList()
  val base = TestList()
  val ret  = TestList()
  val addr = TestList()
  for (m <- Array(0, 1); i <- 0 until 10) {
    mode += m
    val (r_pc, r_base) = (random32(), random32())
    val bits = (m match {
      case 0 => 12
      case 1 => 21
    })
    val vaild_imm = randombits(bits).toSigned(bits)
    val redundancy_imm = randombits(32-bits)
    imm += (vaild_imm.toUnsigned(bits) + (redundancy_imm<<bits))
    ret += (r_pc + 4).toUnsigned()
    pc += r_pc; base += r_base
    addr += ((if (m == 1) {r_pc} else {r_base}) + vaild_imm).toUnsigned()
  }
  shuffle(mode, pc, imm, base, ret, addr)
  mode += 0; pc += 0; imm += 0; base += 0; ret += 4; addr += 0 // unreachable test

  val m = interface(mode, 1)
  val p = interface(pc)
  val i = interface(imm)
  val b = interface(base)
  val r = interface(ret)
  val a = interface(addr)
  val (cntr, done) = Counter(true.B, m.size)
  tester.io.mode := m(cntr)
  tester.io.pc   := p(cntr)
  tester.io.imm  := i(cntr)
  tester.io.base := b(cntr)

  when(done) { stop(); stop() }
  printf("Counter: %d, Mode: %b, PC: %x, IMM: %x, RS1: %x, PC+4: %x ?= %x, jmpAddr: %x ?= %x\n",
    cntr, tester.io.mode, tester.io.pc, tester.io.imm, tester.io.base, tester.io.ret, r(cntr), tester.io.jaddr, a(cntr))
  assert(tester.io.ret === r(cntr))
  assert(tester.io.jaddr === a(cntr))
}

class AUIPCTester(aui: => AddUpperImmPC) extends BasicTester with TestUtils {
  val tester = Module(aui)
  val pc  = TestList()
  val imm = TestList()
  val ret = TestList()
  for (i <- 0 until 10) {
    val r = (random32(), random32())
    pc += r._1; imm += r._2; ret += (r._1 + r._2).toUnsigned()
  }
  shuffle(pc, imm, ret)
  pc += 0; imm += 0; ret += 0 // unreachable test

  val p = interface(pc)
  val i = interface(imm)
  val r = interface(ret)
  val (cntr, done) = Counter(true.B, r.size)
  tester.io.pc  := p(cntr)
  tester.io.imm := i(cntr)

  when(done) { stop(); stop() }
  printf("Counter: %d, PC: %x, IMM: %x, ret: %x ?= %x\n",
    cntr, tester.io.pc, tester.io.imm, tester.io.ret, r(cntr))
  assert(tester.io.ret === r(cntr))
}

class LUITester(lui: => LoadUpperImm) extends BasicTester with TestUtils {
  val tester = Module(lui)
  val pc  = TestList()
  val imm = TestList()
  val ret = TestList()
  for (i <- 0 until 10) {
    val r = random32()
    imm += r; ret += r
  }
  shuffle(imm, ret)
  imm += 0; ret += 0 // unreachable test

  val i = interface(imm)
  val r = interface(ret)
  val (cntr, done) = Counter(true.B, r.size)
  tester.io.imm := i(cntr)

  printf("Counter: %d, IMM: %x, ret: %x ?= %x\n",
    cntr, tester.io.imm, tester.io.ret, r(cntr))
  assert(tester.io.ret === r(cntr))
}

class JumpTests extends FlatSpec {
  "Jump" should "pass" in {
    assert(TesterDriver execute (() => new JumpOnlyTester(new Jump)))
  }
  "AUIPC" should "pass" in {
    assert(TesterDriver execute (() => new AUIPCTester(new AddUpperImmPC)))
  }
  "LUI" should "pass" in {
    assert(TesterDriver execute (() => new LUITester(new LoadUpperImm)))
  }
}


package rself

import chisel3._
import chisel3.util._
import chisel3.testers._
import org.scalatest.FlatSpec

// simple implementation for Memory, only for test
class Memory extends Module {
  val io = IO(new MemoryIO)
}

class PosedgeTester(p: => Posedge) extends BasicTester with TestUtils {
  val tester = Module(p)

  val ser = TestList()
  val res = TestList()
  val out = TestList()
  for (i <- 0 until 100) {
    ser += 1; res += 1; out += 1
    for (j <- 0L until randombits(3)) {
      ser += 0; res += 1; out += 1
    }
    res += 0; ser += 0; out += 0
    for (j <- 0L until randombits(2)) {
      ser += 0; res += 1; out += 0
    }
  }

  val o = interface(ser, 1)
  val e = interface(res, 1)
  val t = interface(out, 1)
  val (cntr, done) = Counter(true.B, o.size)

  tester.io.in := o(cntr)
  tester.io.re := e(cntr)
  assert(tester.io.out === t(cntr))
  // printf("%d ", tester.io.out)
}

object LoadStoreTesterProperty {
  type LoadStoreTesterProperty = Int
  val LoadTest  = 0
  val StoreTest = 1
}
import LoadStoreTesterProperty._

abstract class LoadStoreTester(ls: => LoadStore)(implicit property: LoadStoreTesterProperty) extends BasicTester with TestUtils {
  val tester = Module(ls)

  val _time = TestList()
  val _mode = TestList()
  val _imm  = TestList()
  val _rs1  = TestList()
  val _rs2  = TestList()
  val _rd   = TestList()
  val _cal  = TestList()
  val _mem  = TestList()
  val _ret  = TestList()

  for (i <- 0 until 100) {
    val r = (random32(), randombits(12))
    _time += randombits(3)
    _rs1 += r._1
    _imm += r._2
    _cal += (r._1 + r._2.toSigned(12)).toUnsigned()
    val rmem = random32()
    _mem += rmem
    val mod    = rnd.nextInt(3)
    property match {
      case LoadTest  => {
        val signed = randombits(1)
        _mode += (0 << 3) + (signed << 2) + mod
        _rd  += randombits(5)
        _rs2 += 0
        _ret += (if (signed == 0) rmem.toSigned(1 << (3+mod)) else rmem.toUnsigned(1 << (3+mod))).toUnsigned()
      }
      case StoreTest => {
        _mode += (1 << 3) + mod
        _rd  += 0
        _rs2 += random32()
        _ret += 0
      }
    }
  }

  val time = interface(_time, 8)
  val mode = interface(_mode, 4)
  val rs3  = interface(_imm)
  val rs2  = interface(_rs2)
  val rs1  = interface(_rs1)
  val rd   = interface(_rd, 5)
  val inst = interface(_rd map (x => x<<7))

  val mem = interface(_mem)
  val ret = interface(_ret)
  val pos = interface(_cal)

  val counter     = RegInit(0.U(16.W))
  val timecounter = RegInit(0.U(8.W))

  // tester.io.mem.reader.addr := DontCare
  // tester.io.mem.reader.ren  := DontCare
  // tester.io.mem.writer.addr := DontCare
  // tester.io.mem.writer.mode := DontCare
  // tester.io.mem.writer.wen  := DontCare
  // tester.io.mem.writer.data := DontCare

  tester.io.mode := mode(counter)
  tester.io.inst := inst(counter)
  tester.io.rs3  := rs3(counter)
  tester.io.rs2  := rs2(counter)
  tester.io.rs1  := rs1(counter)

  val vaild = WireInit(false.B)
  val rdata = WireInit(0.U(32.W))
  val enabl = WireInit(false.B)

  val retmem = Wire(UInt(32.W))
  val nowmem = Wire(UInt(32.W))
  retmem := ret(counter)
  nowmem := mem(counter)

  property match {
    case LoadTest  => {
      tester.io.mem.writer.vaild := false.B
      tester.io.mem.reader.vaild := vaild
      tester.io.mem.reader.data  := rdata
    }
    case StoreTest => {
      tester.io.mem.reader.vaild := false.B
      tester.io.mem.writer.vaild := vaild
      tester.io.mem.reader.data  := 0.U
    }
  }
  tester.io.en := enabl

  val stopper = RegInit(0.U(16.W))
  stopper := stopper + 1.U
  when (stopper > 0x8000.U) { stop(); stop() }

  when (counter >= time.size.U) { stop(); stop() }

  when (timecounter >= time(counter)) {
    vaild := true.B
    rdata  := mem(counter)
    printf("Counter: %d, TimeReturn, Wait: %d ?= %d\n",
      counter, timecounter, time(counter))
  } .otherwise {
    vaild := false.B
  }

  when ((timecounter === 0.U) && (time(counter) =/= 0.U)) {
    printf("Counter: %d, Enable,     Wait: %d, rs3: %x, rs2: %x, rs1: %x  =>  rd: %d\n",
      counter, time(counter), rs3(counter), rs2(counter), rs1(counter), rd(counter))
    enabl := true.B
  } .elsewhen (time(counter) === 0.U) {
    printf("Counter: %d, Disable\n", counter)
    enabl := false.B
    counter := counter + 1.U
  } .otherwise {
    enabl := false.B
  }

  when (tester.io.mem.reader.ren || tester.io.mem.writer.wen) {
    timecounter := timecounter + 1.U
    when (tester.io.mem.reader.ren) {
      printf("Counter: %d, TimeCounter: %d, Addr: %x ?= %x\n", counter, timecounter, tester.io.mem.reader.addr, pos(counter))
      assert(tester.io.mem.reader.addr === pos(counter))
    } .elsewhen (tester.io.mem.writer.wen) {
      printf("Counter: %d, TimeCounter: %d, Addr: %x ?= %x, Data: %x ?= %x\n",
        counter, timecounter, tester.io.mem.writer.addr, pos(counter), tester.io.mem.writer.data, rs2(counter))
      assert(tester.io.mem.writer.addr === pos(counter))
      assert(tester.io.mem.writer.data === rs2(counter))
    }
  } .otherwise {
    timecounter := 0.U
  }

  when (tester.io.ready) {
    when (~tester.io.mode(3)) {
      // Load Ready
      printf("Counter: %d, Load Ready, Mod: %d %d, Data: %x ?= %x(%x)\n",
        counter, tester.io.mode(2), tester.io.mode(1, 0), tester.io.ret, retmem, nowmem)
      assert(timecounter === time(counter))
      assert(retmem === tester.io.ret)
      assert(tester.io.rd === rd(counter))
    } .otherwise {
      printf("Counter: %d, StoreReady, Mod: %d\n",
        counter, tester.io.mode(2, 0))
      assert(timecounter === time(counter))
      assert(tester.io.mem.writer.mode === tester.io.mode(1, 0))
      assert(tester.io.rd === 0.U)
    }
    counter := counter + 1.U
  }
}

class LoadTester (ls: => LoadStore) extends LoadStoreTester(ls)(LoadTest)

class StoreTester(ls: => LoadStore) extends LoadStoreTester(ls)(StoreTest)

class LoadStoreTests extends FlatSpec {
  "Posedge" should "pass" in {
    assert(TesterDriver execute (() => new PosedgeTester(new Posedge)))
  }
  "Load" should "pass" in {
    assert(TesterDriver execute (() => new LoadTester(new LoadStore)))
  }
  "Store" should "pass" in {
    assert(TesterDriver execute (() => new StoreTester(new LoadStore)))
  }
}

when(done) { printf("\n"); stop(); stop() }


package rself

import chisel3._

trait TestUtils {
  val rnd = new scala.util.Random(System.currentTimeMillis)

  type TestList = scala.collection.mutable.ListBuffer[Long]
  object TestList {
    import scala.collection.mutable.ListBuffer
    def apply(): ListBuffer[Long] = {
      val ref: ListBuffer[Long] = ListBuffer()
      ref
    }
    def List4() = (TestList(), TestList(), TestList(), TestList())
  }
  import scala.collection.mutable.ListBuffer

  def randombits(bl: Int) = {rnd.nextLong() & ((1L<<bl)-1)}
  def random32() = randombits(32)

  implicit class SignedLong(me: Long) {
    def toSigned(b: Int = 32):Long = {val rme = me & ((1L<<b) - 1);if ((me & (1L << (b-1))) == 0) {rme} else {rme - (1L<<b)}}
    def toUnsigned(b: Int = 32):Long = me & ((1L<<b) - 1)
  }

  def shuffle[T, CC[X] <: scala.collection.mutable.AbstractBuffer[X]](xs: CC[T]*): Unit = {
    val size = xs(0).size
    val index = scala.util.Random.shuffle((0 until size).toList)
    for (el <- xs) {
      val clone = el.clone
      for (i <- 0 until size) {
        el(i) = clone(index(i))
      }
    }
  }

  def interface(l: scala.collection.Iterable[Long], bits: Int = 32) = {
    VecInit(l.to[Seq].map(_.U(bits.W)))
  }

  type List4 = Tuple4[ListBuffer[Long], ListBuffer[Long], ListBuffer[Long], ListBuffer[Long]]

  def randomInsts(r: Int = 5): List4 = {
    val ctl_inst = TestList()
    val ctl_mode = TestList()
    val ctl_sel  = TestList()
    val ctl_imm  = TestList()
    import chisel3.core.UInt
    def append(t:Long, m:Long, s:Long, i:Long): Unit = {
      ctl_inst += t; ctl_mode += m; ctl_sel += s; ctl_imm += i
    }
    def appendEle(t:Long, m:Long, s:UInt, i:UInt): Unit = append(t, m.toLong, s.litValue().toLong, i.litValue().toLong)
    // operator `<<' higher than `^'
    for (op <- Constants.Opcode; i <- 0 until r) {
      val (opstr, opcode) = op
      import IMM_MODE._
      opstr match {
        case "LUI" =>
          appendEle((opcode ^ randombits(5)<<7 ^ randombits(20)<<12), 0, ACT.LUI, IMM_U)
        case "AUIPC" =>
          appendEle((opcode ^ randombits(5)<<7 ^ randombits(20)<<12), 0, ACT.AUI, IMM_U)
        case "JAL" =>
          appendEle((opcode ^ randombits(5)<<7 ^ randombits(20)<<12), 1, ACT.JMP, IMM_J)
        case "JALR" =>
          appendEle((opcode ^ randombits(5)<<7 ^ randombits(5)<<15 ^ randombits(12)<<20),
            0, ACT.JMP, IMM_I)
        case "BRANCH" => {
          for (bfunc <- Constants.BranchCodes) {
            val (bfs, bfmode) = bfunc
            appendEle((opcode ^ randombits(5)<<7 ^ bfmode<<12 ^ randombits(5)<<15 ^ randombits(5)<<20 ^ randombits(7)<<25),
              bfmode, ACT.BRC, IMM_B)
          }
        }
        case "LOAD" => {
          for (lfunc <- Constants.LoadCodes) {
            val (lfs, lfmode) = lfunc
            appendEle((opcode ^ randombits(5)<<7 ^ lfmode<<12 ^ randombits(5)<<15 ^ randombits(12)<<20),
              lfmode, ACT.LDS, IMM_I)
          }
        }
        case "STORE" => {
          for (sfunc <- Constants.LoadCodes) {
            val (sfs, sfmode) = sfunc
            appendEle((opcode ^ randombits(5)<<7 ^ sfmode<<12 ^ randombits(5)<<15 ^ randombits(5)<<20 ^ randombits(7)<<25),
              8 ^ sfmode, ACT.LDS, IMM_S)
          }
        }
        case "ITYPE" => {
          for (alufunc <- Constants.ALU) {
            var (alstr, alumode) = alufunc
            if (alstr == "SUB") {
              alstr = "ADD"
              alumode = alumode & 7
            }
            val alcode = alumode & 7
            val hicode = alumode >> 3
            val imm = if ((alcode & 3) == 1) {
              hicode<<10 ^ randombits(5)
            } else {
              randombits(12)
            }
            appendEle((opcode ^ randombits(5)<<7 ^ alcode<<12 ^ randombits(5)<<15 ^ imm<<20),
              alumode, ACT.ALU, IMM_I)
          }
        }
        case "RTYPE" => {
          for (alufunc <- Constants.ALU) {
            val (alstr, alumode) = alufunc
            val alcode = alumode & 7
            val hicode = alumode >> 3
            appendEle((opcode ^ randombits(5)<<7 ^ alcode<<12 ^ randombits(5)<<15 ^ randombits(5)<<20 ^ hicode<<30),
              alumode, ACT.ALU, IMM_R)
          }
        }
        case "MEMORY" => {
          for (i <- 0 until 2) {
            appendEle((opcode ^ i<<12), 0, ACT.FEN, IMM_Z)
          }
        }
        case "SYSTEM" => {
          for (i <- 0 until 2) {
            appendEle((opcode ^ i<<20), 0, ACT.SYS, IMM_Z)
          }
        }
        case _ =>
      }
    }
    return (ctl_inst, ctl_mode, ctl_sel, ctl_imm)
  }

  def immData(inst: Long, mode: String): Long = {
    def B(hi: Int, lo: Int = -1) = {

      (inst & ((1<<(hi+1)) - (1<<low))) >> low
    }
    return (mode match {
      // operator `<<' higher than `^'
      case "R" => 0
      case "I" => B(31, 20)
      case "S" => B(31, 25)<<5 ^ B(11, 7)
      case "B" => B(31)<<12 ^ B(7)<<11 ^ B(30, 25)<<5 ^ B(11, 8)<<1
      case "J" => B(31)<<20 ^ B(19, 12)<<12 ^ B(20)<<11 ^ B(30, 21)<<1
      case "U" => B(31, 12)<<12
      case "Z" => 0
      case  _  => 0
    })
  }

  def calculate(a: Long, b: Long, mod: Long): Long = {
    val shamt = b % (1<<5)
    val opstr = Constants.ALU.toMap.map(_.swap).get(mod).get
    return (opstr match {
      case "ADD" => a + b
      case "SUB" => a - b
      case "XOR" => a ^ b
      case "AND" => a & b
      case "OR"  => a | b
      case "SLT" => if (a.toInt < b.toInt) {1L} else {0L}
      case "SLTU"=> if (a < b) {1L} else {0L}
      case "SLL" => a << shamt
      case "SRL" => a >> shamt
      case "SRA" => (a.toInt >> shamt).toLong
      case _ => 0xffffffffL
    }).toUnsigned()
  }

  def branchCond(fn: String, rrs: Tuple2[Long, Long]): Long = (if (fn match {
    case "BEQ" => rrs._1 == rrs._2
    case "BNE" => rrs._1 != rrs._2
    case "BLT" => rrs._1.toInt < rrs._2.toInt
    case "BGE" => rrs._1.toInt >= rrs._2.toInt
    case "BLTU" => rrs._1 < rrs._2
    case "BGEU" => rrs._1 >= rrs._2
  }) {1L} else {0L})
}


package chipyard

import chisel3._

import scala.collection.mutable.{ArrayBuffer}

import freechips.rocketchip.prci.{ClockGroupIdentityNode, ClockSinkParameters, ClockSinkNode, ClockGroup}
import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey}
import freechips.rocketchip.config.{Parameters, Field}
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, LazyRawModuleImp, LazyModuleImpLike, BindingScope}
import freechips.rocketchip.util.{ResetCatchAndSync}
import chipyard.iobinders._

import barstools.iocell.chisel._

trait HasReferenceClockFreq {
  def refClockFreqMHz: Double
}

/**
 * The base class used for building chips. This constructor instantiates a module specified by the BuildSystem parameter,
 * named "system", which is an instance of DigitalTop by default. The diplomatic clocks of System, as well as its implicit clock,
 * is aggregated into the clockGroupNode. The parameterized functions controlled by ClockingSchemeKey and GlobalResetSchemeKey
 * drive clock and reset generation
 */

class ChipTop(implicit p: Parameters) extends LazyModule with BindingScope
    with HasTestHarnessFunctions with HasReferenceClockFreq with HasIOBinders {
  // The system module specified by BuildSystem
  lazy val lazySystem = LazyModule(p(BuildSystem)(p)).suggestName("system")

  // The implicitClockSinkNode provides the implicit clock and reset for the system (connected by clocking scheme)
  val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))

  // Generate Clocks and Reset
  val mvRefClkFreq = p(ClockingSchemeKey)(this)
  def refClockFreqMHz: Double = mvRefClkFreq.getWrappedValue

  // NOTE: Making this a LazyRawModule is moderately dangerous, as anonymous children
  // of ChipTop (ex: ClockGroup) do not receive clock or reset.
  // However. anonymous children of ChipTop should not need an implicit Clock or Reset
  // anyways, they probably need to be explicitly clocked.
  lazy val module: LazyModuleImpLike = new LazyRawModuleImp(this) {
    // These become the implicit clock and reset to the System
    val implicit_clock = implicitClockSinkNode.in.head._1.clock
    val implicit_reset = implicitClockSinkNode.in.head._1.reset

    // Connect the implicit clock/reset, if present
    lazySystem.module match { case l: LazyModuleImp => {
      l.clock := implicit_clock
      l.reset := implicit_reset
    }}
  }
}

case object BuildSystem extends Field[Parameters => LazyModule]((p: Parameters) => new DigitalTop()(p))


package chipyard

import chisel3._

import scala.collection.mutable.{ArrayBuffer}

import freechips.rocketchip.prci._
import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey, InstantiatesTiles}
import freechips.rocketchip.config.{Parameters, Field, Config}
import freechips.rocketchip.diplomacy.{ModuleValue, OutwardNodeHandle, InModuleBody, LazyModule}
import freechips.rocketchip.util.{ResetCatchAndSync}

import barstools.iocell.chisel._
import testchipip.{TLTileResetCtrl}

import chipyard.clocking._
import chipyard.iobinders._

/**
 * A simple reset implementation that punches out reset ports
 * for standard Module classes. The ChipTop reset pin is Async.
 * Synchronization is performed in the ClockGroupResetSynchronizer
 */
object GenerateReset {
  def apply(chiptop: ChipTop, clock: Clock): Reset = {
    implicit val p = chiptop.p
    // this needs directionality so generateIOFromSignal works
    val async_reset_wire = Wire(Input(AsyncReset()))
    val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(async_reset_wire, "reset", p(IOCellKey),
      abstractResetAsAsync = true)

    chiptop.iocells ++= resetIOCell
    chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => {
      reset_io := th.dutReset
      Nil
    })
    async_reset_wire
  }
}

case object ClockingSchemeKey extends Field[ChipTop => ModuleValue[Double]](ClockingSchemeGenerators.dividerOnlyClockGenerator)
/*
  * This is a Seq of assignment functions, that accept a clock name and return an optional frequency.
  * Functions that appear later in this seq have higher precedence that earlier ones.
  * If no function returns a non-empty value, the value specified in
  * [[DefaultClockFrequencyKey]] will be used.
  */
case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty)
case object DefaultClockFrequencyKey extends Field[Double]()

class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => {
  case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++
    Seq((cName: String) => if (cName == name) Some(fMHz) else None)
})

class ClockNameContainsAssignment(name: String, fMHz: Double) extends Config((site, here, up) => {
  case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++

})

object ClockingSchemeGenerators {
  val dividerOnlyClockGenerator: ChipTop => ModuleValue[Double] = { chiptop =>
    implicit val p = chiptop.p

    // Requires existence of undriven asyncClockGroups in subsystem
    val systemAsyncClockGroup = chiptop.lazySystem match {
      case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) =>
        l.asyncClockGroupsNode
    }

    // Add a control register for each tile's reset
    val resetSetter = chiptop.lazySystem match {
      case sys: BaseSubsystem with InstantiatesTiles => Some(TLTileResetCtrl(sys))
      case _ => None
    }
    val resetSetterResetProvider = resetSetter.map(_.tileResetProviderNode).getOrElse(ClockGroupEphemeralNode())

    val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node
    // provides the implicit clock to the system
    (chiptop.implicitClockSinkNode
      := ClockGroup()
      := aggregator)
    // provides the system clock (ex. the bus clocks)
    (systemAsyncClockGroup
      :*= ClockGroupNamePrefixer()
      :*= aggregator)

    val referenceClockSource =  ClockSourceNode(Seq(ClockSourceParameters()))
    val dividerOnlyClkGenerator = LazyModule(new DividerOnlyClockGenerator("buildTopClockGenerator"))
    // provides all the divided clocks (from the top-level clock)
    (aggregator
      := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey))
      := ClockGroupResetSynchronizer()
      := resetSetterResetProvider
      := dividerOnlyClkGenerator.node
      := referenceClockSource)

    val asyncResetBroadcast = FixedClockBroadcast(None)
    resetSetter.foreach(_.asyncResetSinkNode := asyncResetBroadcast)
    val asyncResetSource = ClockSourceNode(Seq(ClockSourceParameters()))
    asyncResetBroadcast := asyncResetSource

    InModuleBody {
      val clock_wire = Wire(Input(Clock()))
      val reset_wire = GenerateReset(chiptop, clock_wire)
      val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey))
      chiptop.iocells ++= clockIOCell

      referenceClockSource.out.unzip._1.map { o =>
        o.clock := clock_wire
        o.reset := reset_wire
      }

      asyncResetSource.out.unzip._1.map { o =>
        o.clock := false.B.asClock // async reset broadcast network does not provide a clock
        o.reset := reset_wire
      }

      chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => {
        clock_io := th.buildtopClock
        Nil })

      // return the reference frequency
      dividerOnlyClkGenerator.module.referenceFreq
    }
  }
}

Seq((cName: String) => if (cName.contains(name)) Some(fMHz) else None)


package chipyard.config

import scala.util.matching.Regex
import chisel3._
import chisel3.util.{log2Up}

import freechips.rocketchip.config.{Field, Parameters, Config}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.devices.tilelink.{BootROMLocated}
import freechips.rocketchip.devices.debug.{Debug, ExportDebug, DebugModuleKey, DMI}
import freechips.rocketchip.groundtest.{GroundTestSubsystem}
import freechips.rocketchip.tile._
import freechips.rocketchip.rocket.{RocketCoreParams, MulDivParams, DCacheParams, ICacheParams}
import freechips.rocketchip.tilelink.{HasTLBusParams}
import freechips.rocketchip.util.{AsyncResetReg, Symmetric}
import freechips.rocketchip.prci._
import freechips.rocketchip.stage.phases.TargetDirKey

import testchipip._

import boom.common.{BoomTileAttachParams}

import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._

import chipyard._

// -----------------------
// Common Config Fragments
// -----------------------

class WithBootROM extends Config((site, here, up) => {

})

// DOC include start: gpio config fragment
class WithGPIO extends Config((site, here, up) => {
  case PeripheryGPIOKey => Seq(
    GPIOParams(address = 0x10012000, width = 4, includeIOF = false))
})
// DOC include end: gpio config fragment

class WithUART(baudrate: BigInt = 115200) extends Config((site, here, up) => {
  case PeripheryUARTKey => Seq(
    UARTParams(address = 0x54000000L, nTxEntries = 256, nRxEntries = 256, initBaudRate = baudrate))
})

class WithSPIFlash(size: BigInt = 0x10000000) extends Config((site, here, up) => {
  // Note: the default size matches freedom with the addresses below
  case PeripherySPIFlashKey => Seq(
    SPIFlashParams(rAddress = 0x10040000, fAddress = 0x20000000, fSize = size))
})

class WithL2TLBs(entries: Int) extends Config((site, here, up) => {
  case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
    case tp: RocketTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nL2TLBEntries = entries)))
    case tp: BoomTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nL2TLBEntries = entries)))
    case other => other
  }
})

/**
 * Map from a hartId to a particular RoCC accelerator
 */
case object MultiRoCCKey extends Field[Map[Int, Seq[Parameters => LazyRoCC]]](Map.empty[Int, Seq[Parameters => LazyRoCC]])

/**
 * Config fragment to enable different RoCCs based on the hartId
 */
class WithMultiRoCC extends Config((site, here, up) => {
  case BuildRoCC => site(MultiRoCCKey).getOrElse(site(TileKey).hartId, Nil)
})

/**
 * Assigns what was previously in the BuildRoCC key to specific harts with MultiRoCCKey
 * Must be paired with WithMultiRoCC
 */
class WithMultiRoCCFromBuildRoCC(harts: Int*) extends Config((site, here, up) => {
  case BuildRoCC => Nil
  case MultiRoCCKey => up(MultiRoCCKey, site) ++ harts.distinct.map { i =>
    (i -> up(BuildRoCC, site))
  }
})

class WithTraceIO extends Config((site, here, up) => {
  case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
    case tp: BoomTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      trace = true))
    case other => other
  }
  case TracePortKey => Some(TracePortParams())
})

class WithNPerfCounters(n: Int = 29) extends Config((site, here, up) => {
  case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
    case tp: RocketTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nPerfCounters = n)))
    case tp: BoomTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nPerfCounters = n)))
    case other => other
  }
})

class WithNPMPs(n: Int = 8) extends Config((site, here, up) => {
  case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
    case tp: RocketTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nPMPs = n)))
    case tp: BoomTileAttachParams => tp.copy(tileParams = tp.tileParams.copy(
      core = tp.tileParams.core.copy(nPMPs = n)))
    case other => other
  }
})

class WithRocketICacheScratchpad extends Config((site, here, up) => {
  case RocketTilesKey => up(RocketTilesKey, site) map { r =>
    r.copy(icache = r.icache.map(_.copy(itimAddr = Some(0x300000 + r.hartId * 0x10000))))
  }
})

class WithRocketDCacheScratchpad extends Config((site, here, up) => {
  case RocketTilesKey => up(RocketTilesKey, site) map { r =>
    r.copy(dcache = r.dcache.map(_.copy(nSets = 32, nWays = 1, scratch = Some(0x200000 + r.hartId * 0x10000))))
  }
})

// Replaces the L2 with a broadcast manager for maintaining coherence
class WithBroadcastManager extends Config((site, here, up) => {
  case BankedL2Key => up(BankedL2Key, site).copy(coherenceManager = CoherenceManagerWrapper.broadcastManager)
})

// The default RocketChip BaseSubsystem drives its diplomatic clock graph
// with the implicit clocks of Subsystem. Don't do that, instead we extend
// the diplomacy graph upwards into the ChipTop, where we connect it to
// our clock drivers
class WithNoSubsystemDrivenClocks extends Config((site, here, up) => {
  case SubsystemDriveAsyncClockGroupsKey => None
})

class WithDMIDTM extends Config((site, here, up) => {
  case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI))
})

class WithNoDebug extends Config((site, here, up) => {
  case DebugModuleKey => None
})

class WithTLSerialLocation(masterWhere: TLBusWrapperLocation, slaveWhere: TLBusWrapperLocation) extends Config((site, here, up) => {
  case SerialTLAttachKey => up(SerialTLAttachKey, site).copy(masterWhere = masterWhere, slaveWhere = slaveWhere)
})

class WithTLBackingMemory extends Config((site, here, up) => {
  case ExtMem => None // disable AXI backing memory
  case ExtTLMem => up(ExtMem, site) // enable TL backing memory
})

class WithSerialTLBackingMemory extends Config((site, here, up) => {
  case ExtMem => None
  case SerialTLKey => up(SerialTLKey, site).map { k => k.copy(
    memParams = {
      val memPortParams = up(ExtMem, site).get
      require(memPortParams.nMemoryChannels == 1)
      memPortParams.master
    },
    isMemoryDevice = true
  )}
})

/**
  * Mixins to define either a specific tile frequency for a single hart or all harts
  *
  * @param fMHz Frequency in MHz of the tile or all tiles
  * @param hartId Optional hartid to assign the frequency to (if unspecified default to all harts)
  */
class WithTileFrequency(fMHz: Double, hartId: Option[Int] = None) extends ClockNameContainsAssignment({
    hartId match {
      case Some(id) => s"tile_$id"
      case None => "tile"
    }
  },
  fMHz)

class WithPeripheryBusFrequencyAsDefault extends Config((site, here, up) => {
  case DefaultClockFrequencyKey => (site(PeripheryBusKey).dtsFrequency.get / (1000 * 1000)).toDouble
})

class WithSystemBusFrequencyAsDefault extends Config((site, here, up) => {
  case DefaultClockFrequencyKey => (site(SystemBusKey).dtsFrequency.get / (1000 * 1000)).toDouble
})

class BusFrequencyAssignment[T <: HasTLBusParams](re: Regex, key: Field[T]) extends Config((site, here, up) => {
  case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++
    Seq((cName: String) => site(key).dtsFrequency.flatMap { f =>
      re.findFirstIn(cName).map {_ => (f / (1000 * 1000)).toDouble }
    })
})

/**
  * Provides a diplomatic frequency for all clock sinks with an unspecified
  * frequency bound to each bus.
  *
  * For example, the L2 cache, when bound to the sbus, receives a separate
  * clock that appears as "subsystem_sbus_<num>".  This fragment ensures that
  * clock requests the same frequency as the sbus itself.
  */

class WithInheritBusFrequencyAssignments extends Config(
  new BusFrequencyAssignment("subsystem_sbus_\\d+".r, SystemBusKey) ++
  new BusFrequencyAssignment("subsystem_pbus_\\d+".r, PeripheryBusKey) ++
  new BusFrequencyAssignment("subsystem_cbus_\\d+".r, ControlBusKey) ++
  new BusFrequencyAssignment("subsystem_fbus_\\d+".r, FrontBusKey) ++
  new BusFrequencyAssignment("subsystem_mbus_\\d+".r, MemoryBusKey)
)

/**
  * Mixins to specify crossing types between the 5 traditional TL buses
  *
  * Note: these presuppose the legacy connections between buses and set
  * parameters in SubsystemCrossingParams; they may not be resuable in custom
  * topologies (but you can specify the desired crossings in your topology).
  *
  * @param xType The clock crossing type
  *
  */

class WithSbusToMbusCrossingType(xType: ClockCrossingType) extends Config((site, here, up) => {
    case SbusToMbusXTypeKey => xType
})
class WithSbusToCbusCrossingType(xType: ClockCrossingType) extends Config((site, here, up) => {
    case SbusToCbusXTypeKey => xType
})
class WithCbusToPbusCrossingType(xType: ClockCrossingType) extends Config((site, here, up) => {
    case CbusToPbusXTypeKey => xType
})
class WithFbusToSbusCrossingType(xType: ClockCrossingType) extends Config((site, here, up) => {
    case FbusToSbusXTypeKey => xType
})

/**
  * Mixins to set the dtsFrequency field of BusParams -- these will percolate its way
  * up the diplomatic graph to the clock sources.
  */
class WithPeripheryBusFrequency(freqMHz: Double) extends Config((site, here, up) => {
  case PeripheryBusKey => up(PeripheryBusKey, site).copy(dtsFrequency = Some(BigInt((freqMHz * 1e6).toLong)))
})
class WithMemoryBusFrequency(freqMHz: Double) extends Config((site, here, up) => {
  case MemoryBusKey => up(MemoryBusKey, site).copy(dtsFrequency = Some(BigInt((freqMHz * 1e6).toLong)))
})
class WithSystemBusFrequency(freqMHz: Double) extends Config((site, here, up) => {
  case SystemBusKey => up(SystemBusKey, site).copy(dtsFrequency = Some(BigInt((freqMHz * 1e6).toLong)))
})
class WithFrontBusFrequency(freqMHz: Double) extends Config((site, here, up) => {
  case FrontBusKey => up(FrontBusKey, site).copy(dtsFrequency = Some(BigInt((freqMHz * 1e6).toLong)))
})
class WithControlBusFrequency(freqMHz: Double) extends Config((site, here, up) => {
  case ControlBusKey => up(ControlBusKey, site).copy(dtsFrequency = Some(BigInt((freqMHz * 1e6).toLong)))
})

class WithRationalMemoryBusCrossing extends WithSbusToMbusCrossingType(RationalCrossing(Symmetric))
class WithAsynchrousMemoryBusCrossing extends WithSbusToMbusCrossingType(AsynchronousCrossing())

class WithTestChipBusFreqs extends Config(
  // Frequency specifications
  new chipyard.config.WithTileFrequency(1600.0) ++       // Matches the maximum frequency of U540
  new chipyard.config.WithSystemBusFrequency(800.0) ++   // Put the system bus at a lower freq, representative of ncore working at a lower frequency than the tiles. Same freq as U540
  new chipyard.config.WithMemoryBusFrequency(1000.0) ++  // 2x the U540 freq (appropriate for a 128b Mbus)
  new chipyard.config.WithPeripheryBusFrequency(800.0) ++  // Match the sbus and pbus frequency
  new chipyard.config.WithSystemBusFrequencyAsDefault ++ // All unspecified clock frequencies, notably the implicit clock, will use the sbus freq (800 MHz)
  //  Crossing specifications
  new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS
  new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS
  new freechips.rocketchip.subsystem.WithRationalRocketTiles ++   // Add rational crossings between RocketTile and uncore
  new boom.common.WithRationalBoomTiles ++ // Add rational crossings between BoomTile and uncore
  new testchipip.WithAsynchronousSerialSlaveCrossing // Add Async crossing between serial and MBUS. Its master-side is tied to the FBUS
)

case BootROMLocated(x) => up(BootROMLocated(x), site).map(_.copy(contentFileName = s"${site(TargetDirKey)}/bootrom.rv${site(XLen)}.img"))


package chipyard

import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util.{Location, Symmetric}
import freechips.rocketchip.subsystem._

// I'm putting this code here temporarily as I think it should be a candidate
// for upstreaming based on input from Henry Cook, but don't wnat to deal with
// an RC branch just yet.

// For subsystem/BusTopology.scala

// Biancolin: This, modified from Henry's email
/** Parameterization of a topology containing a banked coherence manager and a bus for attaching memory devices. */
case class CoherentMulticlockBusTopologyParams(
  sbus: SystemBusParams, // TODO remove this after better width propagation
  mbus: MemoryBusParams,
  l2: BankedL2Params,
  sbusToMbusXType: ClockCrossingType = NoCrossing
) extends TLBusWrapperTopology(
  instantiations = (if (l2.nBanks == 0) Nil else List(
    (MBUS, mbus),
    (L2, CoherenceManagerWrapperParams(mbus.blockBytes, mbus.beatBytes, l2.nBanks, L2.name)(l2.coherenceManager)))),
  connections = if (l2.nBanks == 0) Nil else List(

// This differs from upstream only in that it does not use the legacy crossTo
// and crossFrom functions, and it ensures driveClockFromMaster = None
case class HierarchicalMulticlockBusTopologyParams(
  pbus: PeripheryBusParams,
  fbus: FrontBusParams,
  cbus: PeripheryBusParams,
  xTypes: SubsystemCrossingParams
) extends TLBusWrapperTopology(
  instantiations = List(
    (PBUS, pbus),
    (FBUS, fbus),
    (CBUS, cbus)),
  connections = List(
    (SBUS, CBUS, TLBusWrapperConnection.  crossTo(xType = xTypes.sbusToCbusXType, driveClockFromMaster = None)),
    (CBUS, PBUS, TLBusWrapperConnection.  crossTo(xType = xTypes.cbusToPbusXType, driveClockFromMaster = None)),
    (FBUS, SBUS, TLBusWrapperConnection.crossFrom(xType = xTypes.fbusToSbusXType, driveClockFromMaster = None)))
)

// For subsystem/Configs.scala

class WithMulticlockCoherentBusTopology extends Config((site, here, up) => {
  case TLNetworkTopologyLocated(InSubsystem) => List(
    JustOneBusTopologyParams(sbus = site(SystemBusKey)),
    HierarchicalMulticlockBusTopologyParams(
      pbus = site(PeripheryBusKey),
      fbus = site(FrontBusKey),
      cbus = site(ControlBusKey),
      xTypes = SubsystemCrossingParams(
        sbusToCbusXType = site(SbusToCbusXTypeKey),
        cbusToPbusXType = site(CbusToPbusXTypeKey),
        fbusToSbusXType = site(FbusToSbusXTypeKey))),
    CoherentMulticlockBusTopologyParams(
      sbus = site(SystemBusKey),
      mbus = site(MemoryBusKey),
      l2 = site(BankedL2Key),
      sbusToMbusXType = site(SbusToMbusXTypeKey)))
})

class WithMulticlockIncoherentBusTopology extends Config((site, here, up) => {
  case TLNetworkTopologyLocated(InSubsystem) => List(
    JustOneBusTopologyParams(sbus = site(SystemBusKey)),
    HierarchicalMulticlockBusTopologyParams(
      pbus = site(PeripheryBusKey),
      fbus = site(FrontBusKey),
      cbus = site(ControlBusKey),
      xTypes = SubsystemCrossingParams()))
})

    (SBUS, L2,   TLBusWrapperConnection(xType = NoCrossing, driveClockFromMaster = Some(true), nodeBinding = BIND_STAR)()),
    (L2,  MBUS,  TLBusWrapperConnection.crossTo(
      xType = sbusToMbusXType,
      driveClockFromMaster = None,


package chipyard

import chisel3._

import freechips.rocketchip.subsystem._
import freechips.rocketchip.system._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.devices.tilelink._

// ------------------------------------
// BOOM and/or Rocket Top Level Systems
// ------------------------------------

// DOC include start: DigitalTop
class DigitalTop(implicit p: Parameters) extends ChipyardSystem
  with testchipip.CanHavePeripheryCustomBootPin // Enables optional custom boot pin
  with testchipip.HasPeripheryBootAddrReg // Use programmable boot address register
  with testchipip.CanHaveTraceIO // Enables optionally adding trace IO
  with testchipip.CanHaveBackingScratchpad // Enables optionally adding a backing scratchpad
  with testchipip.CanHavePeripheryBlockDevice // Enables optionally adding the block device
  with testchipip.CanHavePeripheryTLSerial // Enables optionally adding the backing memory and serial adapter
  with sifive.blocks.devices.i2c.HasPeripheryI2C // Enables optionally adding the sifive I2C
  with sifive.blocks.devices.pwm.HasPeripheryPWM // Enables optionally adding the sifive PWM
  with sifive.blocks.devices.uart.HasPeripheryUART // Enables optionally adding the sifive UART
  with sifive.blocks.devices.gpio.HasPeripheryGPIO // Enables optionally adding the sifive GPIOs
  with sifive.blocks.devices.spi.HasPeripherySPIFlash // Enables optionally adding the sifive SPI flash controller
  with sifive.blocks.devices.spi.HasPeripherySPI // Enables optionally adding the sifive SPI port
{
  override lazy val module = new DigitalTopModule(this)
}

class DigitalTopModule[+L <: DigitalTop](l: L) extends ChipyardSystemModule(l)
  with testchipip.CanHaveTraceIOModuleImp
  with sifive.blocks.devices.i2c.HasPeripheryI2CModuleImp
  with sifive.blocks.devices.pwm.HasPeripheryPWMModuleImp
  with sifive.blocks.devices.uart.HasPeripheryUARTModuleImp
  with sifive.blocks.devices.gpio.HasPeripheryGPIOModuleImp
  with sifive.blocks.devices.spi.HasPeripherySPIFlashModuleImp

  with sifive.blocks.devices.spi.HasPeripherySPIModuleImp
  with freechips.rocketchip.util.DontTouch


package chipyard

import firrtl.options.{StageMain}
import chipyard.stage.ChipyardStage

object Generator extends StageMain(new ChipyardStage)


package chipyard.harness

import chisel3._
import chisel3.util._
import chisel3.experimental.{Analog, BaseModule, DataMirror, Direction}

import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImpLike}
import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4SlaveNode, AXI4MasterNode, AXI4EdgeParameters}
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.jtag.{JTAGIO}
import freechips.rocketchip.system.{SimAXIMem}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.util._

import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._

import barstools.iocell.chisel._

import testchipip._

import chipyard.{HasHarnessSignalReferences, HarnessClockInstantiatorKey}
import chipyard.iobinders.{GetSystemParameters, JTAGChipIO}

import scala.reflect.{ClassTag}

case object HarnessBinders extends Field[Map[String, (Any, HasHarnessSignalReferences, Seq[Data]) => Unit]](
  Map[String, (Any, HasHarnessSignalReferences, Seq[Data]) => Unit]().withDefaultValue((t: Any, th: HasHarnessSignalReferences, d: Seq[Data]) => ())
)

object ApplyHarnessBinders {
  def apply(th: HasHarnessSignalReferences, sys: LazyModule, portMap: Map[String, Seq[Data]])(implicit p: Parameters): Unit = {
    val pm = portMap.withDefaultValue(Nil)
    p(HarnessBinders).foreach { case (s, f) =>
      f(sys, th, pm(s))
      f(sys.module, th, pm(s))
    }
  }
}

// The ClassTags here are necessary to overcome issues arising from type erasure
class HarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](composer: ((T, S, Seq[U]) => Unit) => (T, S, Seq[U]) => Unit)(implicit systemTag: ClassTag[T], harnessTag: ClassTag[S], portTag: ClassTag[U]) extends Config((site, here, up) => {
  case HarnessBinders => up(HarnessBinders, site) + (systemTag.runtimeClass.toString ->
      ((t: Any, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
        val pts = ports.collect({case p: U => p})
        require (pts.length == ports.length, s"Port type mismatch between IOBinder and HarnessBinder: ${portTag}")
        val upfn = up(HarnessBinders, site)(systemTag.runtimeClass.toString)
        th match {
          case th: S =>
            t match {
              case system: T => composer(upfn)(system, th, pts)
              case _ =>
            }
          case _ =>
        }
      })
  )
})

class OverrideHarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](fn: => (T, S, Seq[U]) => Unit)
  (implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
    extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => fn)

class ComposeHarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](fn: => (T, S, Seq[U]) => Unit)
  (implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
    extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => (t, th, p) => {
      upfn(t, th, p)
      fn(t, th, p)
    })

class WithGPIOTiedOff extends OverrideHarnessBinder({
  (system: HasPeripheryGPIOModuleImp, th: HasHarnessSignalReferences, ports: Seq[Analog]) => {
    ports.foreach { _ <> AnalogConst(0) }
  }
})

// DOC include start: WithUARTAdapter
class WithUARTAdapter extends OverrideHarnessBinder({
  (system: HasPeripheryUARTModuleImp, th: HasHarnessSignalReferences, ports: Seq[UARTPortIO]) => {
    UARTAdapter.connect(ports)(system.p)
  }
})
// DOC include end: WithUARTAdapter

class WithSimSPIFlashModel(rdOnly: Boolean = true) extends OverrideHarnessBinder({
  (system: HasPeripherySPIFlashModuleImp, th: HasHarnessSignalReferences, ports: Seq[SPIChipIO]) => {
    SimSPIFlashModel.connect(ports, th.buildtopReset, rdOnly)(system.p)
  }
})

class WithSimBlockDevice extends OverrideHarnessBinder({
  (system: CanHavePeripheryBlockDevice, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
    implicit val p: Parameters = GetSystemParameters(system)
    ports.map { b => SimBlockDevice.connect(b.clock, th.buildtopReset.asBool, Some(b.bits)) }
  }
})

class WithBlockDeviceModel extends OverrideHarnessBinder({
  (system: CanHavePeripheryBlockDevice, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
    implicit val p: Parameters = GetSystemParameters(system)
    ports.map { b => withClockAndReset(b.clock, th.buildtopReset) { BlockDeviceModel.connect(Some(b.bits)) } }
  }
})

class WithSimAXIMem extends OverrideHarnessBinder({
  (system: CanHaveMasterAXI4MemPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
    val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
    (ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
      val mem = LazyModule(new SimAXIMem(edge, size=p(ExtMem).get.master.size)(p))
      withClockAndReset(port.clock, port.reset) {
        Module(mem.module).suggestName("mem")
      }
      mem.io_axi4.head <> port.bits
    }
  }
})

class WithSimAXIMemOverSerialTL extends OverrideHarnessBinder({
  (system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
    implicit val p = chipyard.iobinders.GetSystemParameters(system)

    p(SerialTLKey).map({ sVal =>
      require(sVal.axiMemOverSerialTLParams.isDefined)
      val axiDomainParams = sVal.axiMemOverSerialTLParams.get
      require(sVal.isMemoryDevice)

      val memFreq = axiDomainParams.getMemFrequency(system.asInstanceOf[HasTileLinkLocations])

      ports.map({ port =>
// DOC include start: HarnessClockInstantiatorEx
        withClockAndReset(th.buildtopClock, th.buildtopReset) {
          val memOverSerialTLClockBundle = p(HarnessClockInstantiatorKey).requestClockBundle("mem_over_serial_tl_clock", memFreq)
          val serial_bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
          val harnessMultiClockAXIRAM = SerialAdapter.connectHarnessMultiClockAXIRAM(
            system.serdesser.get,
            serial_bits,
            memOverSerialTLClockBundle,
            th.buildtopReset)
// DOC include end: HarnessClockInstantiatorEx
          val success = SerialAdapter.connectSimSerial(harnessMultiClockAXIRAM.module.io.tsi_ser, th.buildtopClock, th.buildtopReset.asBool)
          when (success) { th.success := true.B }

          // connect SimDRAM from the AXI port coming from the harness multi clock axi ram
          (harnessMultiClockAXIRAM.mem_axi4 zip harnessMultiClockAXIRAM.memNode.edges.in).map { case (axi_port, edge) =>
            val memSize = sVal.memParams.size
            val lineSize = p(CacheBlockBytes)
            val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), edge.bundle)).suggestName("simdram")
            mem.io.axi <> axi_port.bits
            mem.io.clock := axi_port.clock
            mem.io.reset := axi_port.reset
          }
        }
      })
    })
  }
})

class WithBlackBoxSimMem(additionalLatency: Int = 0) extends OverrideHarnessBinder({
  (system: CanHaveMasterAXI4MemPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
    val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
    (ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
      val memSize = p(ExtMem).get.master.size
      val lineSize = p(CacheBlockBytes)
      val clockFreq = p(MemoryBusKey).dtsFrequency.get
      val mem = Module(new SimDRAM(memSize, lineSize, clockFreq, edge.bundle)).suggestName("simdram")
      mem.io.axi <> port.bits
      // Bug in Chisel implementation. See https://github.com/chipsalliance/chisel3/pull/1781
      def Decoupled[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
        require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.")
        val d = Wire(new DecoupledIO(chiselTypeOf(irr.bits)))
        d.bits := irr.bits
        d.valid := irr.valid
        irr.ready := d.ready
        d
      }
      if (additionalLatency > 0) {
        withClockAndReset (port.clock, port.reset) {
          mem.io.axi.aw <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.aw))((t, _) => Queue(t, 1, pipe=true))
          mem.io.axi.w  <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.w ))((t, _) => Queue(t, 1, pipe=true))
          port.bits.b   <> (0 until additionalLatency).foldLeft(Decoupled(mem.io.axi.b))((t, _) => Queue(t, 1, pipe=true))
          mem.io.axi.ar <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.ar))((t, _) => Queue(t, 1, pipe=true))
          port.bits.r   <> (0 until additionalLatency).foldLeft(Decoupled(mem.io.axi.r))((t, _) => Queue(t, 1, pipe=true))
        }
      }
      mem.io.clock := port.clock
      mem.io.reset := port.reset
    }
  }
})

class WithSimAXIMMIO extends OverrideHarnessBinder({
  (system: CanHaveMasterAXI4MMIOPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
    val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
    (ports zip system.mmioAXI4Node.edges.in).map { case (port, edge) =>
      val mmio_mem = LazyModule(new SimAXIMem(edge, size = p(ExtBus).get.size)(p))
      withClockAndReset(port.clock, port.reset) {
        Module(mmio_mem.module).suggestName("mmio_mem")
      }
      mmio_mem.io_axi4.head <> port.bits
    }
  }
})

class WithTieOffInterrupts extends OverrideHarnessBinder({
  (system: HasExtInterruptsModuleImp, th: HasHarnessSignalReferences, ports: Seq[UInt]) => {
    ports.foreach { _ := 0.U }
  }
})

class WithTieOffL2FBusAXI extends OverrideHarnessBinder({
  (system: CanHaveSlaveAXI4Port, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[AXI4Bundle]]) => {

  }
})

class WithSimDebug extends OverrideHarnessBinder({
  (system: HasPeripheryDebug, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
    implicit val p: Parameters = GetSystemParameters(system)
    ports.map {
      case d: ClockedDMIIO =>
        val dtm_success = WireInit(false.B)
        when (dtm_success) { th.success := true.B }
        val dtm = Module(new SimDTM).connect(th.buildtopClock, th.buildtopReset.asBool, d, dtm_success)
      case j: JTAGChipIO =>
        val dtm_success = WireInit(false.B)
        when (dtm_success) { th.success := true.B }
        val jtag_wire = Wire(new JTAGIO)
        jtag_wire.TDO.data := j.TDO
        jtag_wire.TDO.driven := true.B
        j.TCK := jtag_wire.TCK
        j.TMS := jtag_wire.TMS
        j.TDI := jtag_wire.TDI
        val jtag = Module(new SimJTAG(tickDelay=3)).connect(jtag_wire, th.buildtopClock, th.buildtopReset.asBool, ~(th.buildtopReset.asBool), dtm_success)
    }
  }
})

class WithTiedOffDebug extends OverrideHarnessBinder({
  (system: HasPeripheryDebug, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
    ports.map {
      case j: JTAGChipIO =>
        j.TCK := true.B.asClock
        j.TMS := true.B
        j.TDI := true.B
      case d: ClockedDMIIO =>
        d.dmi.req.valid := false.B
        d.dmi.req.bits  := DontCare
        d.dmi.resp.ready := true.B
        d.dmiClock := false.B.asClock
        d.dmiReset := true.B
      case a: ClockedAPBBundle =>
        a.tieoff()
        a.clock := false.B.asClock
        a.reset := true.B.asAsyncReset
        a.psel := false.B
        a.penable := false.B
    }
  }
})

class WithSerialAdapterTiedOff extends OverrideHarnessBinder({
  (system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
    implicit val p = chipyard.iobinders.GetSystemParameters(system)
    ports.map({ port =>
      val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
      withClockAndReset(th.buildtopClock, th.buildtopReset) {
        val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
        SerialAdapter.tieoff(ram.module.io.tsi_ser)
      }
    })
  }
})

class WithSimSerial extends OverrideHarnessBinder({
  (system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
    implicit val p = chipyard.iobinders.GetSystemParameters(system)
    ports.map({ port =>
      val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
      withClockAndReset(th.buildtopClock, th.buildtopReset) {
        val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
        val success = SerialAdapter.connectSimSerial(ram.module.io.tsi_ser, th.buildtopClock, th.buildtopReset.asBool)
        when (success) { th.success := true.B }
      }
    })
  }
})

class WithTieOffCustomBootPin extends OverrideHarnessBinder({
  (system: CanHavePeripheryCustomBootPin, th: HasHarnessSignalReferences, ports: Seq[Bool]) => {
    ports.foreach(_ := false.B)
  }
})

ports.foreach({ p => p := DontCare; p.bits.tieoff() })


package chipyard.iobinders

import chisel3._
import chisel3.experimental.{Analog, IO, DataMirror}

import freechips.rocketchip.config._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.jtag.{JTAGIO}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.system.{SimAXIMem}
import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4SlaveNode, AXI4MasterNode, AXI4EdgeParameters}
import freechips.rocketchip.util._
import freechips.rocketchip.prci.{ClockSinkNode, ClockSinkParameters}
import freechips.rocketchip.groundtest.{GroundTestSubsystemModuleImp, GroundTestSubsystem}

import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._

import barstools.iocell.chisel._

import testchipip._

import scala.reflect.{ClassTag}

object IOBinderTypes {
  type IOBinderTuple = (Seq[Data], Seq[IOCell])
  type IOBinderFunction = (Boolean, => Any) => ModuleValue[IOBinderTuple]
}
import IOBinderTypes._

// System for instantiating binders based
// on the scala type of the Target (_not_ its IO). This avoids needing to
// duplicate harnesses (essentially test harnesses) for each target.

// IOBinders is map between string representations of traits to the desired
// IO connection behavior for tops matching that trait. We use strings to enable
// composition and overriding of IOBinders, much like how normal Keys in the config
// system are used/ At elaboration, the testharness traverses this set of functions,
// and functions which match the type of the DigitalTop are evaluated.

// You can add your own binder by adding a new (key, fn) pair, typically by using
// the OverrideIOBinder or ComposeIOBinder macros
case object IOBinders extends Field[Map[String, Seq[IOBinderFunction]]](
  Map[String, Seq[IOBinderFunction]]().withDefaultValue(Nil)
)

abstract trait HasIOBinders { this: LazyModule =>
  val lazySystem: LazyModule
  private val iobinders = p(IOBinders)
  // Note: IOBinders cannot rely on the implicit clock/reset, as they may be called from the
  // context of a LazyRawModuleImp
  private val lzy = iobinders.map({ case (s,fns) => s -> fns.map(f => f(true, lazySystem)) })
  private val imp = iobinders.map({ case (s,fns) => s -> fns.map(f => f(false, lazySystem.module)) })

  private lazy val lzyFlattened: Map[String, IOBinderTuple] = lzy.map({
    case (s,ms) => s -> (ms.map(_._1).flatten, ms.map(_._2).flatten)
  })
  private lazy val impFlattened: Map[String, IOBinderTuple] = imp.map({
    case (s,ms) => s -> (ms.map(_._1).flatten, ms.map(_._2).flatten)
  })

  // A publicly accessible list of IO cells (useful for a floorplanning tool, for example)
  lazy val iocells = (lzyFlattened.values ++ impFlattened.values).unzip._2.flatten.toBuffer

  // A mapping between stringified DigitalSystem traits and their corresponding ChipTop ports
  lazy val portMap = iobinders.keys.map(k => k -> (lzyFlattened(k)._1 ++ impFlattened(k)._1)).toMap
}

// Note: The parameters instance is accessible only through LazyModule
// or LazyModuleImpLike. The self-type requirement in traits like
// CanHaveMasterAXI4MemPort is insufficient to make it accessible to the IOBinder
// As a result, IOBinders only work on Modules which inherit LazyModule or
// or LazyModuleImpLike
object GetSystemParameters {
  def apply(s: Any): Parameters = {
    s match {
      case s: LazyModule => s.p
      case s: LazyModuleImpLike => s.p
      case _ => throw new Exception(s"Trying to get Parameters from a system that is not LazyModule or LazyModuleImpLike")
    }
  }
}

class IOBinder[T](composer: Seq[IOBinderFunction] => Seq[IOBinderFunction])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
  case IOBinders => up(IOBinders, site) + (tag.runtimeClass.toString -> composer(up(IOBinders, site)(tag.runtimeClass.toString)))
})

class ConcreteIOBinder[T](composes: Boolean, fn: T => IOBinderTuple)(implicit tag: ClassTag[T]) extends IOBinder[T](
  up => (if (composes) up else Nil) ++ Seq(((_, t) => { InModuleBody {
    t match {
      case system: T => fn(system)
      case _ => (Nil, Nil)
    }
  }}): IOBinderFunction)
)

class LazyIOBinder[T](composes: Boolean, fn: T => ModuleValue[IOBinderTuple])(implicit tag: ClassTag[T]) extends IOBinder[T](
  up => (if (composes) up else Nil) ++ Seq(((isLazy, t) => {
    val empty = new ModuleValue[IOBinderTuple] {
      def getWrappedValue: IOBinderTuple = (Nil, Nil)
    }
    if (isLazy) {
      t match {
        case system: T => fn(system)
        case _ => empty
      }
    } else {
      empty
    }
  }): IOBinderFunction)
)

// The "Override" binders override any previous IOBinders (lazy or concrete) defined on the same trait.
// The "Compose" binders do not override previously defined IOBinders on the same trait
// The default IOBinders evaluate only in the concrete "ModuleImp" phase of elaboration
// The "Lazy" IOBinders evaluate in the LazyModule phase, but can also generate hardware through InModuleBody

class OverrideIOBinder[T](fn: T => IOBinderTuple)(implicit tag: ClassTag[T]) extends ConcreteIOBinder[T](false, fn)
class ComposeIOBinder[T](fn: T => IOBinderTuple)(implicit tag: ClassTag[T]) extends ConcreteIOBinder[T](true, fn)

class OverrideLazyIOBinder[T](fn: T => ModuleValue[IOBinderTuple])(implicit tag: ClassTag[T]) extends LazyIOBinder[T](false, fn)
class ComposeLazyIOBinder[T](fn: T => ModuleValue[IOBinderTuple])(implicit tag: ClassTag[T]) extends LazyIOBinder[T](true, fn)

case object IOCellKey extends Field[IOCellTypeParams](GenericIOCellParams())

class WithGPIOCells extends OverrideIOBinder({
  (system: HasPeripheryGPIOModuleImp) => {
    val (ports2d, cells2d) = system.gpio.zipWithIndex.map({ case (gpio, i) =>
      gpio.pins.zipWithIndex.map({ case (pin, j) =>
        val g = IO(Analog(1.W)).suggestName(s"gpio_${i}_${j}")
        val iocell = system.p(IOCellKey).gpio().suggestName(s"iocell_gpio_${i}_${j}")
        iocell.io.o := pin.o.oval
        iocell.io.oe := pin.o.oe
        iocell.io.ie := pin.o.ie
        pin.i.ival := iocell.io.i
        iocell.io.pad <> g
        (g, iocell)
      }).unzip
    }).unzip
    val ports: Seq[Analog] = ports2d.flatten
    (ports, cells2d.flatten)
  }
})

// DOC include start: WithUARTIOCells
class WithUARTIOCells extends OverrideIOBinder({
  (system: HasPeripheryUARTModuleImp) => {
    val (ports: Seq[UARTPortIO], cells2d) = system.uart.zipWithIndex.map({ case (u, i) =>
      val (port, ios) = IOCell.generateIOFromSignal(u, s"uart_${i}", system.p(IOCellKey), abstractResetAsAsync = true)
      (port, ios)
    }).unzip
    (ports, cells2d.flatten)
  }
})
// DOC include end: WithUARTIOCells

class WithSPIIOCells extends OverrideIOBinder({
  (system: HasPeripherySPIFlashModuleImp) => {
    val (ports: Seq[SPIChipIO], cells2d) = system.qspi.zipWithIndex.map({ case (s, i) =>
      val name = s"spi_${i}"
      val port = IO(new SPIChipIO(s.c.csWidth)).suggestName(name)
      val iocellBase = s"iocell_${name}"

      // SCK and CS are unidirectional outputs
      val sckIOs = IOCell.generateFromSignal(s.sck, port.sck, Some(s"${iocellBase}_sck"), system.p(IOCellKey), IOCell.toAsyncReset)
      val csIOs = IOCell.generateFromSignal(s.cs, port.cs, Some(s"${iocellBase}_cs"), system.p(IOCellKey), IOCell.toAsyncReset)

      // DQ are bidirectional, so then need special treatment
      val dqIOs = s.dq.zip(port.dq).zipWithIndex.map { case ((pin, ana), j) =>
        val iocell = system.p(IOCellKey).gpio().suggestName(s"${iocellBase}_dq_${j}")
        iocell.io.o := pin.o
        iocell.io.oe := pin.oe
        iocell.io.ie := true.B
        pin.i := iocell.io.i
        iocell.io.pad <> ana
        iocell
      }

      (port, dqIOs ++ csIOs ++ sckIOs)
    }).unzip
    (ports, cells2d.flatten)
  }
})

class WithExtInterruptIOCells extends OverrideIOBinder({
  (system: HasExtInterruptsModuleImp) => {
    if (system.outer.nExtInterrupts > 0) {
      val (port: UInt, cells) = IOCell.generateIOFromSignal(system.interrupts, "ext_interrupts", system.p(IOCellKey), abstractResetAsAsync = true)
      (Seq(port), cells)
    } else {
      (Nil, Nil)
    }
  }
})

// Rocketchip's JTAGIO exposes the oe signal, which doesn't go off-chip
class JTAGChipIO extends Bundle {
  val TCK = Input(Clock())
  val TMS = Input(Bool())
  val TDI = Input(Bool())
  val TDO = Output(Bool())
}

class WithDebugIOCells extends OverrideLazyIOBinder({
  (system: HasPeripheryDebug) => {
    implicit val p = GetSystemParameters(system)
    val tlbus = system.asInstanceOf[BaseSubsystem].locateTLBusWrapper(p(ExportDebug).slaveWhere)
    val clockSinkNode = system.debugOpt.map(_ => ClockSinkNode(Seq(ClockSinkParameters())))
    clockSinkNode.map(_ := tlbus.fixedClockNode)
    def clockBundle = clockSinkNode.get.in.head._1

    InModuleBody { system.asInstanceOf[BaseSubsystem].module match { case system: HasPeripheryDebugModuleImp => {
      system.debug.map({ debug =>
        // We never use the PSDIO, so tie it off on-chip
        system.psd.psd.foreach { _ <> 0.U.asTypeOf(new PSDTestMode) }
        system.resetctrl.map { rcio => rcio.hartIsInReset.map { _ := clockBundle.reset.asBool } }
        system.debug.map { d =>
          // Tie off extTrigger
          d.extTrigger.foreach { t =>
            t.in.req := false.B
            t.out.ack := t.out.req
          }
          // Tie off disableDebug
          d.disableDebug.foreach { d => d := false.B }
          // Drive JTAG on-chip IOs
          d.systemjtag.map { j =>
            j.reset := ResetCatchAndSync(j.jtag.TCK, clockBundle.reset.asBool)
            j.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W)
            j.part_number := p(JtagDTMKey).idcodePartNum.U(16.W)
            j.version := p(JtagDTMKey).idcodeVersion.U(4.W)
          }
        }
        Debug.connectDebugClockAndReset(Some(debug), clockBundle.clock)

        // Add IOCells for the DMI/JTAG/APB ports
        val dmiTuple = debug.clockeddmi.map { d =>
          IOCell.generateIOFromSignal(d, "dmi", p(IOCellKey), abstractResetAsAsync = true)
        }

        val jtagTuple = debug.systemjtag.map { j =>
          val jtag_wire = Wire(new JTAGChipIO)
          j.jtag.TCK := jtag_wire.TCK
          j.jtag.TMS := jtag_wire.TMS
          j.jtag.TDI := jtag_wire.TDI
          jtag_wire.TDO := j.jtag.TDO.data
          IOCell.generateIOFromSignal(jtag_wire, "jtag", p(IOCellKey), abstractResetAsAsync = true)
        }

        val apbTuple = debug.apb.map { a =>
          IOCell.generateIOFromSignal(a, "apb", p(IOCellKey), abstractResetAsAsync = true)
        }

        val allTuples = (dmiTuple ++ jtagTuple ++ apbTuple).toSeq
        (allTuples.map(_._1).toSeq, allTuples.flatMap(_._2).toSeq)
      }).getOrElse((Nil, Nil))
    }}}
  }
})

class WithSerialTLIOCells extends OverrideIOBinder({
  (system: CanHavePeripheryTLSerial) => system.serial_tl.map({ s =>
    val sys = system.asInstanceOf[BaseSubsystem]
    val (port, cells) = IOCell.generateIOFromSignal(s.getWrappedValue, "serial_tl", sys.p(IOCellKey), abstractResetAsAsync = true)
    (Seq(port), cells)
  }).getOrElse((Nil, Nil))
})

class WithAXI4MemPunchthrough extends OverrideLazyIOBinder({
  (system: CanHaveMasterAXI4MemPort) => {
    implicit val p: Parameters = GetSystemParameters(system)
    val clockSinkNode = p(ExtMem).map(_ => ClockSinkNode(Seq(ClockSinkParameters())))
    clockSinkNode.map(_ := system.asInstanceOf[HasTileLinkLocations].locateTLBusWrapper(MBUS).fixedClockNode)
    def clockBundle = clockSinkNode.get.in.head._1

    InModuleBody {
      val ports: Seq[ClockedAndResetIO[AXI4Bundle]] = system.mem_axi4.zipWithIndex.map({ case (m, i) =>

        p.bits <> m
        p.clock := clockBundle.clock
        p.reset := clockBundle.reset
        p
      })
      (ports, Nil)
    }
  }
})

class WithAXI4MMIOPunchthrough extends OverrideLazyIOBinder({
  (system: CanHaveMasterAXI4MMIOPort) => {
    implicit val p: Parameters = GetSystemParameters(system)
    val clockSinkNode = p(ExtBus).map(_ => ClockSinkNode(Seq(ClockSinkParameters())))
    clockSinkNode.map(_ := system.asInstanceOf[HasTileLinkLocations].locateTLBusWrapper(MBUS).fixedClockNode)
    def clockBundle = clockSinkNode.get.in.head._1

    InModuleBody {
      val ports: Seq[ClockedAndResetIO[AXI4Bundle]] = system.mmio_axi4.zipWithIndex.map({ case (m, i) =>
        val p = IO(new ClockedAndResetIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mmio_${i}")
        p.bits <> m
        p.clock := clockBundle.clock
        p.reset := clockBundle.reset
        p
      })
      (ports, Nil)
    }
  }
})

class WithL2FBusAXI4Punchthrough extends OverrideLazyIOBinder({
  (system: CanHaveSlaveAXI4Port) => {
    implicit val p: Parameters = GetSystemParameters(system)
    val clockSinkNode = p(ExtIn).map(_ => ClockSinkNode(Seq(ClockSinkParameters())))
    clockSinkNode.map(_ := system.asInstanceOf[BaseSubsystem].fbus.fixedClockNode)
    def clockBundle = clockSinkNode.get.in.head._1

    InModuleBody {
      val ports: Seq[ClockedIO[AXI4Bundle]] = system.l2_frontend_bus_axi4.zipWithIndex.map({ case (m, i) =>
        val p = IO(new ClockedIO(Flipped(DataMirror.internal.chiselTypeClone[AXI4Bundle](m)))).suggestName(s"axi4_fbus_${i}")
        m <> p.bits
        p.clock := clockBundle.clock
        p
      })
      (ports, Nil)
    }
  }
})

class WithBlockDeviceIOPunchthrough extends OverrideIOBinder({
  (system: CanHavePeripheryBlockDevice) => {
    val ports: Seq[ClockedIO[BlockDeviceIO]] = system.bdev.map({ bdev =>
      val p = IO(new ClockedIO(new BlockDeviceIO()(GetSystemParameters(system)))).suggestName("blockdev")
      p <> bdev
      p
    }).toSeq
    (ports, Nil)
  }
})

class WithTraceIOPunchthrough extends OverrideIOBinder({
  (system: CanHaveTraceIOModuleImp) => {
    val ports: Option[TraceOutputTop] = system.traceIO.map { t =>
      val trace = IO(DataMirror.internal.chiselTypeClone[TraceOutputTop](t)).suggestName("trace")
      trace <> t
      trace
    }
    (ports.toSeq, Nil)
  }
})

class WithCustomBootPin extends OverrideIOBinder({
  (system: CanHavePeripheryCustomBootPin) => system.custom_boot_pin.map({ p =>
    val sys = system.asInstanceOf[BaseSubsystem]
    val (port, cells) = IOCell.generateIOFromSignal(p.getWrappedValue, "custom_boot", sys.p(IOCellKey), abstractResetAsAsync = true)
    (Seq(port), cells)
  }).getOrElse((Nil, Nil))
})

class WithDontTouchPorts extends OverrideIOBinder({
  (system: DontTouch) => system.dontTouchPorts(); (Nil, Nil)
})

val p = IO(new ClockedAndResetIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mem_${i}")


//******************************************************************************
// Copyright (c) 2019 - 2019, The Regents of the University of California (Regents).
// All Rights Reserved. See LICENSE and LICENSE.SiFive for license details.
//------------------------------------------------------------------------------

package chipyard

import chisel3._
import chisel3.internal.sourceinfo.{SourceInfo}

import freechips.rocketchip.prci._
import freechips.rocketchip.config.{Field, Parameters}
import freechips.rocketchip.devices.tilelink._
import freechips.rocketchip.devices.debug.{HasPeripheryDebug, HasPeripheryDebugModuleImp, ExportDebug, DebugModuleKey}
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.diplomaticobjectmodel.model.{OMInterrupt}
import freechips.rocketchip.diplomaticobjectmodel.logicaltree.{RocketTileLogicalTreeNode, LogicalModuleTree}
import freechips.rocketchip.tile._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.interrupts._
import freechips.rocketchip.util._
import freechips.rocketchip.subsystem._
import freechips.rocketchip.amba.axi4._

import boom.common.{BoomTile}

import testchipip.{CanHavePeripheryTLSerial, SerialTLKey}

trait CanHaveHTIF { this: BaseSubsystem =>
  // Advertise HTIF if system can communicate with fesvr
  if (this match {
    case _: CanHavePeripheryTLSerial if p(SerialTLKey).nonEmpty => true
    case _: HasPeripheryDebug if (!p(DebugModuleKey).isEmpty && p(ExportDebug).dmi) => true
    case _ => false
  }) {
    ResourceBinding {
      val htif = new Device {
        def describe(resources: ResourceBindings): Description = {
          val compat = resources("compat").map(_.value)
          Description("htif", Map(
            "compatible" -> compat))
        }
      }
      Resource(htif, "compat").bind(ResourceString("ucb,htif0"))
    }
  }
}

class ChipyardSubsystem(implicit p: Parameters) extends BaseSubsystem
  with HasTiles
  with CanHaveHTIF
{
  def coreMonitorBundles = tiles.map {
    case r: RocketTile => r.module.core.rocketImpl.coreMonitorBundle
    case b: BoomTile => b.module.core.coreMonitorBundle
  }.toList

  // Relying on [[TLBusWrapperConnection]].driveClockFromMaster for
  // bus-couplings that are not asynchronous strips the bus name from the sink
  // ClockGroup. This makes it impossible to determine which clocks are driven
  // by which bus based on the member names, which is problematic when there is
  // a rational crossing between two buses. Instead, provide all bus clocks
  // directly from the asyncClockGroupsNode in the subsystem to ensure bus
  // names are always preserved in the top-level clock names.
  //
  // For example, using a RationalCrossing between the Sbus and Cbus, and
  // driveClockFromMaster = Some(true) results in all cbus-attached device and
  // bus clocks to be given names of the form "subsystem_sbus_[0-9]*".
  // Conversly, if an async crossing is used, they instead receive names of the
  // form "subsystem_cbus_[0-9]*". The assignment below provides the latter names in all cases.
  Seq(PBUS, FBUS, MBUS, CBUS).foreach { loc =>

  }
  override lazy val module = new ChipyardSubsystemModuleImp(this)
}

class ChipyardSubsystemModuleImp[+L <: ChipyardSubsystem](_outer: L) extends BaseSubsystemModuleImp(_outer)
  with HasTilesModuleImp
{
  // create file with core params
  ElaborationArtefacts.add("""core.config""", outer.tiles.map(x => x.module.toString).mkString("\n"))
}

tlBusWrapperLocationMap.lift(loc).foreach { _.clockGroupNode := asyncClockGroupsNode }


//******************************************************************************
// Copyright (c) 2019 - 2019, The Regents of the University of California (Regents).
// All Rights Reserved. See LICENSE and LICENSE.SiFive for license details.
//------------------------------------------------------------------------------

package chipyard

import chisel3._

import freechips.rocketchip.config.{Parameters, Field}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.devices.tilelink._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.util.{DontTouch}

// ---------------------------------------------------------------------
// Base system that uses the debug test module (dtm) to bringup the core
// ---------------------------------------------------------------------

/**
 * Base top with periphery devices and ports, and a BOOM + Rocket subsystem
 */
class ChipyardSystem(implicit p: Parameters) extends ChipyardSubsystem
  with HasAsyncExtInterrupts
  with CanHaveMasterTLMemPort // export TL port for outer memory
  with CanHaveMasterAXI4MemPort // expose AXI port for outer mem
  with CanHaveMasterAXI4MMIOPort
  with CanHaveSlaveAXI4Port
{

  val bootROM  = p(BootROMLocated(location)).map { BootROM.attach(_, this, CBUS) }

  override lazy val module = new ChipyardSystemModule(this)
}

/**
 * Base top module implementation with periphery devices and ports, and a BOOM + Rocket subsystem
 */
class ChipyardSystemModule[+L <: ChipyardSystem](_outer: L) extends ChipyardSubsystemModuleImp(_outer)
  with HasRTCModuleImp
  with HasExtInterruptsModuleImp
  with DontTouch

// ------------------------------------
// TL Mem Port Mixin
// ------------------------------------

// Similar to ExtMem but instantiates a TL mem port
case object ExtTLMem extends Field[Option[MemoryPortParams]](None)

/** Adds a port to the system intended to master an TL DRAM controller. */
trait CanHaveMasterTLMemPort { this: BaseSubsystem =>

  require(!(p(ExtTLMem).nonEmpty && p(ExtMem).nonEmpty),
    "Can only have 1 backing memory port. Use ExtTLMem for a TL memory port or ExtMem for an AXI memory port.")

  private val memPortParamsOpt = p(ExtTLMem)
  private val portName = "tl_mem"
  private val device = new MemoryDevice
  private val idBits = memPortParamsOpt.map(_.master.idBits).getOrElse(1)

  val memTLNode = TLManagerNode(memPortParamsOpt.map({ case MemoryPortParams(memPortParams, nMemoryChannels) =>
    Seq.tabulate(nMemoryChannels) { channel =>
      val base = AddressSet.misaligned(memPortParams.base, memPortParams.size)
      val filter = AddressSet(channel * mbus.blockBytes, ~((nMemoryChannels-1) * mbus.blockBytes))

     TLSlavePortParameters.v1(
       managers = Seq(TLSlaveParameters.v1(
         address            = base.flatMap(_.intersect(filter)),
         resources          = device.reg,
         regionType         = RegionType.UNCACHED, // cacheable
         executable         = true,
         supportsGet        = TransferSizes(1, mbus.blockBytes),
         supportsPutFull    = TransferSizes(1, mbus.blockBytes),
         supportsPutPartial = TransferSizes(1, mbus.blockBytes))),
         beatBytes = memPortParams.beatBytes)
   }
 }).toList.flatten)

 mbus.coupleTo(s"memory_controller_port_named_$portName") {
   (memTLNode
     :*= TLBuffer()
     :*= TLSourceShrinker(1 << idBits)
     :*= TLWidthWidget(mbus.beatBytes)
     :*= _)
  }

  val mem_tl = InModuleBody { memTLNode.makeIOs() }
}

val maskROMs = p(MaskROMLocated(location)).map { MaskROM.attach(_, this, CBUS) }


package chipyard

import chisel3._

import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.config.{Field, Parameters}
import freechips.rocketchip.util.{ResetCatchAndSync}
import freechips.rocketchip.prci.{ClockBundle, ClockBundleParameters, ClockSinkParameters, ClockParameters}

import chipyard.harness.{ApplyHarnessBinders, HarnessBinders}
import chipyard.iobinders.HasIOBinders
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}

// -------------------------------
// Chipyard Test Harness
// -------------------------------

trait HasTestHarnessFunctions {
  val harnessFunctions = ArrayBuffer.empty[HasHarnessSignalReferences => Seq[Any]]
}

trait HasHarnessSignalReferences {
  // clock/reset of the chiptop reference clock (can be different than the implicit harness clock/reset)
  def buildtopClock: Clock
  def buildtopReset: Reset
  def dutReset: Reset
  def success: Bool
}

class HarnessClockInstantiator {
  private val _clockMap: LinkedHashMap[String, (Double, ClockBundle)] = LinkedHashMap.empty

  // request a clock bundle at a particular frequency
  def requestClockBundle(name: String, freqRequested: Double): ClockBundle = {
    val clockBundle = Wire(new ClockBundle(ClockBundleParameters()))
    _clockMap(name) = (freqRequested, clockBundle)
    clockBundle
  }

  // connect all clock wires specified to a divider only PLL
  def instantiateHarnessDividerPLL(refClock: ClockBundle): Unit = {
    val sinks = _clockMap.map({ case (name, (freq, bundle)) =>
      ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))), name=Some(name))
    }).toSeq

    val pllConfig = new SimplePllConfiguration("harnessDividerOnlyClockGenerator", sinks)
    pllConfig.emitSummaries()

    val dividedClocks = LinkedHashMap[Int, Clock]()
    def instantiateDivider(div: Int): Clock = {
      val divider = Module(new ClockDividerN(div))
      divider.suggestName(s"ClockDivideBy${div}")
      divider.io.clk_in := refClock.clock
      dividedClocks(div) = divider.io.clk_out
      divider.io.clk_out
    }

    // connect wires to clock source
    for (sinkParams <- sinks) {
      // bypass the reference freq. (don't create a divider + reset sync)
      val (divClock, divReset) = if (sinkParams.take.get.freqMHz != pllConfig.referenceFreqMHz) {
        val div = pllConfig.sinkDividerMap(sinkParams)
        val divClock = dividedClocks.getOrElse(div, instantiateDivider(div))
        (divClock, ResetCatchAndSync(divClock, refClock.reset.asBool))
      } else {
        (refClock.clock, refClock.reset)
      }

      _clockMap(sinkParams.name.get)._2.clock := divClock
      _clockMap(sinkParams.name.get)._2.reset := divReset
    }
  }
}

case object HarnessClockInstantiatorKey extends Field[HarnessClockInstantiator](new HarnessClockInstantiator)

class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSignalReferences {
  val io = IO(new Bundle {
    val success = Output(Bool())
  })

  val buildtopClock = Wire(Clock())
  val buildtopReset = Wire(Reset())

  val lazyDut = LazyModule(p(BuildTop)(p)).suggestName("chiptop")
  val dut = Module(lazyDut.module)

  io.success := false.B

  val freqMHz = lazyDut match {
    case d: HasReferenceClockFreq => d.refClockFreqMHz
    case _ => p(DefaultClockFrequencyKey)
  }
  val refClkBundle = p(HarnessClockInstantiatorKey).requestClockBundle("buildtop_reference_clock", freqMHz * (1000 * 1000))

  buildtopClock := refClkBundle.clock
  buildtopReset := WireInit(refClkBundle.reset)
  val dutReset = refClkBundle.reset.asAsyncReset

  val success = io.success

  lazyDut match { case d: HasTestHarnessFunctions =>
    d.harnessFunctions.foreach(_(this))
  }
  lazyDut match { case d: HasIOBinders =>
    ApplyHarnessBinders(this, d.lazySystem, d.portMap)
  }

  val implicitHarnessClockBundle = Wire(new ClockBundle(ClockBundleParameters()))
  implicitHarnessClockBundle.clock := clock
  implicitHarnessClockBundle.reset := reset
  p(HarnessClockInstantiatorKey).instantiateHarnessDividerPLL(implicitHarnessClockBundle)
}

case object BuildTop extends Field[Parameters => LazyModule]((p: Parameters) => new ChipTop()(p))


package chipyard

import scala.collection.mutable.{LinkedHashSet}

import freechips.rocketchip.subsystem._
import freechips.rocketchip.tile.{XLen, TileParams}
import freechips.rocketchip.config.{Parameters, Field, Config}
import freechips.rocketchip.system.{TestGeneration, RegressionTestSuite, RocketTestSuite}

/**
 * A set of pre-chosen regression tests
 */
object RegressionTestSuites
{
  val rv64RegrTestNames = LinkedHashSet(
    "rv64ud-v-fcvt",
    "rv64ud-p-fdiv",
    "rv64ud-v-fadd",
    "rv64uf-v-fadd",
    "rv64um-v-mul",
    "rv64mi-p-breakpoint",
    "rv64uc-v-rvc",
    "rv64ud-v-structural",
    "rv64si-p-wfi",
    "rv64um-v-divw",
    "rv64ua-v-lrsc",
    "rv64ui-v-fence_i",
    "rv64ud-v-fcvt_w",
    "rv64uf-v-fmin",
    "rv64ui-v-sb",
    "rv64ua-v-amomax_d",
    "rv64ud-v-move",
    "rv64ud-v-fclass",
    "rv64ua-v-amoand_d",
    "rv64ua-v-amoxor_d",
    "rv64si-p-sbreak",
    "rv64ud-v-fmadd",
    "rv64uf-v-ldst",
    "rv64um-v-mulh",
    "rv64si-p-dirty")

  val rv32RegrTestNames = LinkedHashSet(
    "rv32mi-p-ma_addr",
    "rv32mi-p-csr",
    "rv32ui-p-sh",
    "rv32ui-p-lh",
    "rv32uc-p-rvc",
    "rv32mi-p-sbreak",
    "rv32ui-p-sll")
}

/**
 * Helper functions to add BOOM or Rocket tests
 */
class TestSuiteHelper
{
  import freechips.rocketchip.system.DefaultTestSuites._
  import RegressionTestSuites._
  val suites = collection.mutable.ListMap[String, RocketTestSuite]()

  def addSuites(s: Seq[RocketTestSuite]) { s.foreach(addSuite) }

  /**
  * Add generic tests (asm, bmark, regression) for all cores.
  */
  def addGenericTestSuites(tiles: Seq[TileParams])(implicit p: Parameters) = {
    val xlen = p(XLen)
    tiles.find(_.hartId == 0).map { tileParams =>
      val coreParams = tileParams.core
      val vm = coreParams.useVM
      val env = if (vm) List("p","v") else List("p")
      coreParams.fpu foreach { case cfg =>
        if (xlen == 32) {
          addSuites(env.map(rv32uf))
          if (cfg.fLen >= 64)
            addSuites(env.map(rv32ud))
        } else {
          addSuite(rv32udBenchmarks)
          addSuites(env.map(rv64uf))
          if (cfg.fLen >= 64)
            addSuites(env.map(rv64ud))
        }
      }
      if (coreParams.useAtomics) {
        if (tileParams.dcache.flatMap(_.scratch).isEmpty)
          addSuites(env.map(if (xlen == 64) rv64ua else rv32ua))
        else
          addSuites(env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC))
      }
      if (coreParams.useCompressed) addSuites(env.map(if (xlen == 64) rv64uc else rv32uc))
      val (rvi, rvu) =
        if (xlen == 64) ((if (vm) rv64i else rv64pi), (if (coreParams.mulDiv.isDefined) rv64u else List(rv64ui)))
        else            ((if (vm) rv32i else rv32pi), (if (coreParams.mulDiv.isDefined) rv32u else List(rv32ui)))

      addSuites(rvi.map(_("p")))
      addSuites(rvu.map(_("p")))
      addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env))))
      addSuite(benchmarks)
      addSuite(new RegressionTestSuite(if (xlen == 64) rv64RegrTestNames else rv32RegrTestNames))
    }
  }
}

/**
 * Config key of custom test suite.
 */
case object TestSuitesKey extends Field[(Seq[TileParams], TestSuiteHelper, Parameters) => String]((tiles, helper, p) => {
  helper.addGenericTestSuites(tiles)(p)
  // Return an empty string as makefile additional snippets
  ""
})

def addSuite(s: RocketTestSuite) { suites += (s.makeTargetName -> s) }


// See LICENSE for license details.

package chipyard.clocking

import chisel3._
import chisel3.util._

class ClockDividerN(div: Int) extends BlackBox(Map("DIV" -> div)) with HasBlackBoxResource {
  require(div > 0);
  val io = IO(new Bundle {
    val clk_out = Output(Clock())
    val clk_in  = Input(Clock())
  })
  addResource("/vsrc/ClockDividerN.sv")
}

object ClockDivideByN {

  def apply(clockIn: Clock, div: Int): Clock = {
    val clockDivider = Module(new ClockDividerN(div))
    clockDivider.io.clk_in := clockIn
    clockDivider.io.clk_out
  }


package chipyard.clocking

import chisel3._

import freechips.rocketchip.config.{Parameters}
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.prci._

/**
  * This sort of node can be used when it is a connectivity passthrough, but modifies
  * the flow of parameters (which may result in changing the names of the underlying signals).
  */
class ClockGroupParameterModifier(
    sourceFn: ClockGroupSourceParameters => ClockGroupSourceParameters = { m => m },
    sinkFn:   ClockGroupSinkParameters   => ClockGroupSinkParameters   = { s => s })(
    implicit p: Parameters, v: ValName) extends LazyModule {
  val node = ClockGroupAdapterNode(sourceFn, sinkFn)
  lazy val module = new LazyRawModuleImp(this) {
    (node.out zip node.in).map { case ((o, _), (i, _)) =>
      (o.member.data zip i.member.data).foreach { case (oD, iD) => oD := iD }
    }
  }
}

/**
  * Pushes the ClockGroup's name into each member's name field as a prefix. This is
  * intended to be used before a ClockGroupAggregator so that sources from
  * different aggregated ClockGroups can be disambiguated by their names.
  */
object ClockGroupNamePrefixer {
  def apply()(implicit p: Parameters, valName: ValName): ClockGroupAdapterNode =
    LazyModule(new ClockGroupParameterModifier(sinkFn = { s => s.copy(members = s.members.zipWithIndex.map { case (m, idx) =>
      m.copy(name = m.name match {
          // This matches what the chisel would do if the names were not modified
          case Some(clockName) => Some(s"${s.name}_${clockName}")

      })
    })})).node
}

/**
  * [Word from on high is that Strings are in...]
  * Overrides the take field of all clocks in a group, by attempting to apply a
  * series of assignment functions:
  *   (name: String) => freq-in-MHz: Option[Double]
  * to each sink. Later functions that return non-empty values take priority.
  * The default if all functions return None.
  */
object ClockGroupFrequencySpecifier {
  def apply(
      assigners: Seq[(String) => Option[Double]],
      defaultFreq: Double)(
      implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = {

    def lookupFrequencyForName(clock: ClockSinkParameters): ClockSinkParameters = {
      require(clock.name.nonEmpty, "All clocks in clock group must have an assigned name")
      val clockFreq = assigners.foldLeft(defaultFreq)(
        (currentFreq, candidateFunc) => candidateFunc(clock.name.get).getOrElse(currentFreq))

      clock.copy(take = clock.take match {
        case Some(cp) =>
          println(s"Clock ${clock.name.get}: using diplomatically specified frequency of ${cp.freqMHz}.")
          Some(cp)
        case None => Some(ClockParameters(clockFreq))
      })
    }

    LazyModule(new ClockGroupParameterModifier(sinkFn = { s => s.copy(members = s.members.map(lookupFrequencyForName)) })).node
  }
}

case None            => Some(s"${s.name}_${idx}")