<jupyter_start><jupyter_text>Unified Processing Engine Verification | Version 0.6.2 | Updated 2018.7.31___  Setup<jupyter_code>val path = System.getProperty("user.dir") + "/source/load-ivy.sc"
interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))
import chisel3._
import chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}

import scala.math.pow<jupyter_output><empty_output><jupyter_text>Register File  Definition<jupyter_code>class PartialRFConfig(
        val numInputs: Int,
        val numOutputs: Int,
        val numCrossInputs: Int,
        val addrWidth: Int,
        val bpSoft: Boolean,
        val bpFirm: Boolean)

class PartialRFControl(c: PartialRFConfig) extends Bundle {
    val wEnable = Vec(c.numInputs, Bool())
    val rEnable = Vec(c.numOutputs, Bool())
    val wAddr = if (!c.bpFirm) Some(Vec(c.numInputs, UInt(c.addrWidth.W))) else None
    val rAddr = if (!c.bpFirm) Some(Vec(c.numOutputs, UInt(c.addrWidth.W))) else None
    // Each output can select which input of the opposite bus to bypass from
    val bpSel = if (c.bpSoft || c.bpFirm) Some(Vec(c.numOutputs, Vec(c.numCrossInputs, Bool()))) else None
}

class RFConfig(
        val numIntInputs: Int,
        val numExtInputs: Int,
        val numIntOutputs: Int,
        val numExtOutputs: Int,
        val addrWidth: Int,
        val dataWidth: Int,
        val bpType: String) {
    
    val bpNone = (bpType == "None")
    val bpSoft = (bpType == "Soft")
    val bpFirm = (bpType == "Firm")
    
    require(bpNone || bpSoft || bpFirm, "Invalid Bypass type.\n")
    require(numIntInputs > 0 || numExtInputs > 0, "Must have at least one input.\n")
    require(numIntOutputs > 0 || numExtOutputs > 0, "Must have at least one output.\n")
    require(dataWidth > 0, "Data bitwidth must be at least one.\n") 
    if (bpFirm) { require(addrWidth == 0, "Address width must be 0 when Firm Bypassing.\n") }
    
    val intConfig = new PartialRFConfig(
        numIntInputs, numIntOutputs, numExtOutputs, addrWidth, bpSoft, bpFirm)
    
    val extConfig = new PartialRFConfig(
        numExtInputs, numExtOutputs, numIntOutputs, addrWidth, bpSoft, bpFirm)
}

class RFControl(c: RFConfig) extends Bundle {
    
    override def cloneType = (new RFControl(c)).asInstanceOf[this.type]
    
    val internal = if (c.numIntInputs > 0 || c.numIntOutputs > 0)
        Some(new PartialRFControl(c.intConfig)) else None
    val external = if (c.numExtInputs > 0 || c.numExtOutputs > 0)
        Some(new PartialRFControl(c.extConfig)) else None
}

class RF(c: RFConfig) extends Module {
    
    val io = IO(new Bundle {
        val control = Input(new RFControl(c))
        val wInternal = Input(Vec(c.numIntInputs, SInt(c.dataWidth.W))) 
        val wExternal = Input(Vec(c.numExtInputs, SInt(c.dataWidth.W)))
        val rInternal = Output(Vec(c.numIntOutputs, SInt(c.dataWidth.W)))
        val rExternal = Output(Vec(c.numExtOutputs, SInt(c.dataWidth.W)))
    })
    
    val dataRegister = if (!c.bpFirm) 
        Some(RegInit(Vec.fill(pow(2, c.addrWidth).toInt){0.S(c.dataWidth.W)})) else None
    
    // Need to bypass through a register to prevent combinational loops
    val bpAny = c.bpSoft || c.bpFirm
    val bpRegisterInt = if (bpAny && c.numIntInputs > 0)
        Some(RegInit(Vec.fill(c.numIntInputs){0.S(c.dataWidth.W)})) else None
    val bpRegisterExt = if (bpAny && c.numExtInputs > 0)
        Some(RegInit(Vec.fill(c.numExtInputs){0.S(c.dataWidth.W)})) else None
    
    for (i <- 0 until c.numIntInputs) {
        when (io.control.internal.get.wEnable(i)) {
            if (!c.bpFirm) { dataRegister.get(io.control.internal.get.wAddr.get(i)) := io.wInternal(i) }
            if (bpRegisterInt.isDefined) { bpRegisterInt.get(i) := io.wInternal(i) }
        }
    }
    
    for (i <- 0 until c.numExtInputs) {
        when (io.control.external.get.wEnable(i)) {
            if (!c.bpFirm) { dataRegister.get(io.control.external.get.wAddr.get(i)) := io.wExternal(i) }
            if (bpRegisterExt.isDefined) { bpRegisterExt.get(i) := io.wExternal(i) }
        }
    }
    
    for (i <- 0 until c.numIntOutputs) {
        when (io.control.internal.get.rEnable(i)) {
            if (c.bpFirm) {
                io.rInternal(i) := PriorityMux(io.control.internal.get.bpSel.get(i), bpRegisterExt.get)
            } else if (c.bpSoft) {
                when (io.control.internal.get.bpSel.get(i).contains(true.B)) {
                    // External write bypasses to Internal read
                    io.rInternal(i) := PriorityMux(io.control.internal.get.bpSel.get(i), bpRegisterExt.get)
                } .otherwise {
                    io.rInternal(i) := dataRegister.get(io.control.internal.get.rAddr.get(i))
                }
            } else {
                io.rInternal(i) := dataRegister.get(io.control.internal.get.rAddr.get(i))
            }
        } .otherwise {
            io.rInternal(i) := 0.S
        }
    }
    
    for (i <- 0 until c.numExtOutputs) {
        when (io.control.external.get.rEnable(i)) {
            if (c.bpFirm) {
                io.rExternal(i) := PriorityMux(io.control.external.get.bpSel.get(i), bpRegisterInt.get)
            } else if (c.bpSoft) {
                when (io.control.external.get.bpSel.get(i).contains(true.B)) {
                    // Internal write bypasses to External read
                    io.rExternal(i) := PriorityMux(io.control.external.get.bpSel.get(i), bpRegisterInt.get)
                } .otherwise {
                    io.rExternal(i) := dataRegister.get(io.control.external.get.rAddr.get(i))
                }
            } else {
                io.rExternal(i) := dataRegister.get(io.control.external.get.rAddr.get(i))
            }
        } .otherwise {
            io.rExternal(i) := 0.S
        }
    }
}<jupyter_output><empty_output><jupyter_text>Verification<jupyter_code>/*
Basic Test Checklist:
[x] Optional Hardware
    [x] No Internal Read Port
    [x] No External Read Port
    [x] No Internal Write Port
    [x] No External Write Port

[-] No Bypass
    [x] Standard Read/Write
    [x] Port Independence 
    [x] Read Enable
    [x] Write Enable

[-] Soft Bypass
    [-] Standard Read/Write
    [-] Port Independence
    [-] Read Enable
    [-] Write Enable 
    [-] Bypass Enable/Select
    
[-] Hard Bypass
    [-] Bypass Enable/Select
    
[x] Enable
    [x] Read
    [x] Write

Better would be to check these together.
Even better would be to use Golden Model...
*/

// TODO: Do this.
val exRFConfigNoIntWrite = new RFConfig(0, 2, 2, 2, 4, 8, "None")
val exRFConfigNoExtWrite = new RFConfig(2, 0, 2, 2, 4, 8, "None")
val exRFConfigNoIntRead = new RFConfig(2, 2, 0, 2, 4, 8, "None")
val exRFConfigNoExtRead = new RFConfig(2, 2, 2, 0, 4, 8, "None")

val exRFConfigNoBypass = new RFConfig(2, 2, 2, 2, 4, 8, "None")
val exRFConfigSoftBypass = new RFConfig(2, 2, 2, 2, 4, 8, "Soft")
val exRFConfigHardBypass = new RFConfig(2, 2, 2, 2, 0, 8, "Firm")

// There has to be a better way than these monolithic blocks of code..

val noIntWriteTest = Driver(() => new RF(exRFConfigNoIntWrite)) {
    uut => new PeekPokeTester(uut) {
        poke(uut.io.control.external.get.wEnable(0), true)
        poke(uut.io.control.external.get.wEnable(1), false)
        poke(uut.io.control.external.get.rEnable(0), false)
        poke(uut.io.control.external.get.rEnable(1), false)
        poke(uut.io.control.external.get.wAddr.get(0), 0)
        poke(uut.io.control.external.get.wAddr.get(1), 1)
        poke(uut.io.control.external.get.rAddr.get(0), 0)
        poke(uut.io.control.external.get.rAddr.get(1), 1)
        
        poke(uut.io.wExternal(0), 10)
        poke(uut.io.wExternal(1), 11)
        
        poke(uut.io.control.internal.get.rEnable(0), false)
        poke(uut.io.control.internal.get.rEnable(1), false)
        poke(uut.io.control.internal.get.rAddr.get(0), 0)
        poke(uut.io.control.internal.get.rAddr.get(1), 1)
        
        step(1)
        
        expect(uut.io.rInternal(0), 0)
        expect(uut.io.rInternal(1), 0)
        expect(uut.io.rExternal(0), 0)
        expect(uut.io.rExternal(1), 0)
        
        poke(uut.io.control.internal.get.rEnable(0), true)
        poke(uut.io.control.internal.get.rEnable(1), true)
        poke(uut.io.control.external.get.rEnable(0), true)
        poke(uut.io.control.external.get.rEnable(1), true)
        
        expect(uut.io.rInternal(0), 10)
        expect(uut.io.rInternal(1), 0)
        expect(uut.io.rExternal(0), 10)
        expect(uut.io.rExternal(1), 0)
    }
}

val noExtWriteTest = Driver(() => new RF(exRFConfigNoExtWrite)) {
    uut => new PeekPokeTester(uut) {
        poke(uut.io.control.internal.get.wEnable(0), true)
        poke(uut.io.control.internal.get.wEnable(1), false)
        poke(uut.io.control.internal.get.rEnable(0), false)
        poke(uut.io.control.internal.get.rEnable(1), false)
        poke(uut.io.control.internal.get.wAddr.get(0), 0)
        poke(uut.io.control.internal.get.wAddr.get(1), 1)
        poke(uut.io.control.internal.get.rAddr.get(0), 0)
        poke(uut.io.control.internal.get.rAddr.get(1), 1)
        
        poke(uut.io.wInternal(0), 10)
        poke(uut.io.wInternal(1), 11)
        
        poke(uut.io.control.external.get.rEnable(0), false)
        poke(uut.io.control.external.get.rEnable(1), false)
        poke(uut.io.control.external.get.rAddr.get(0), 0)
        poke(uut.io.control.external.get.rAddr.get(1), 1)
        
        step(1)
        
        expect(uut.io.rInternal(0), 0)
        expect(uut.io.rInternal(1), 0)
        expect(uut.io.rExternal(0), 0)
        expect(uut.io.rExternal(1), 0)
        
        poke(uut.io.control.internal.get.rEnable(0), true)
        poke(uut.io.control.internal.get.rEnable(1), true)
        poke(uut.io.control.external.get.rEnable(0), true)
        poke(uut.io.control.external.get.rEnable(1), true)
        
        expect(uut.io.rInternal(0), 10)
        expect(uut.io.rInternal(1), 0)
        expect(uut.io.rExternal(0), 10)
        expect(uut.io.rExternal(1), 0)
    }
}

val noIntReadTest = Driver(() => new RF(exRFConfigNoIntRead)) {
    uut => new PeekPokeTester(uut) {
        poke(uut.io.control.internal.get.wEnable(0), true)
        poke(uut.io.control.internal.get.wEnable(1), true)
        poke(uut.io.control.internal.get.wAddr.get(0), 0)
        poke(uut.io.control.internal.get.wAddr.get(1), 1)
        
        poke(uut.io.wInternal(0), 10)
        poke(uut.io.wInternal(1), 11)
        
        poke(uut.io.control.external.get.wEnable(0), true)
        poke(uut.io.control.external.get.wEnable(1), true)
        poke(uut.io.control.external.get.rEnable(0), false)
        poke(uut.io.control.external.get.rEnable(1), false)
        poke(uut.io.control.external.get.wAddr.get(0), 2)
        poke(uut.io.control.external.get.wAddr.get(1), 3)
        poke(uut.io.control.external.get.rAddr.get(0), 2)
        poke(uut.io.control.external.get.rAddr.get(1), 3)
        
        poke(uut.io.wExternal(0), 12)
        poke(uut.io.wExternal(1), 13)
        
        step(1)
        
        expect(uut.io.rExternal(0), 0)
        expect(uut.io.rExternal(1), 0)
        
        poke(uut.io.control.external.get.rEnable(0), true)
        poke(uut.io.control.external.get.rEnable(1), true)
        
        expect(uut.io.rExternal(0), 12)
        expect(uut.io.rExternal(1), 13)
    }
}

val noExtReadTest = Driver(() => new RF(exRFConfigNoExtRead)) {
    uut => new PeekPokeTester(uut) {
        poke(uut.io.control.external.get.wEnable(0), true)
        poke(uut.io.control.external.get.wEnable(1), true)
        poke(uut.io.control.external.get.wAddr.get(0), 0)
        poke(uut.io.control.external.get.wAddr.get(1), 1)
        
        poke(uut.io.wExternal(0), 10)
        poke(uut.io.wExternal(1), 11)
        
        poke(uut.io.control.internal.get.wEnable(0), true)
        poke(uut.io.control.internal.get.wEnable(1), true)
        poke(uut.io.control.internal.get.rEnable(0), false)
        poke(uut.io.control.internal.get.rEnable(1), false)
        poke(uut.io.control.internal.get.wAddr.get(0), 2)
        poke(uut.io.control.internal.get.wAddr.get(1), 3)
        poke(uut.io.control.internal.get.rAddr.get(0), 2)
        poke(uut.io.control.internal.get.rAddr.get(1), 3)
        
        poke(uut.io.wInternal(0), 12)
        poke(uut.io.wInternal(1), 13)
        
        step(1)
        
        expect(uut.io.rInternal(0), 0)
        expect(uut.io.rInternal(1), 0)
        
        poke(uut.io.control.internal.get.rEnable(0), true)
        poke(uut.io.control.internal.get.rEnable(1), true)
        
        expect(uut.io.rInternal(0), 12)
        expect(uut.io.rInternal(1), 13)
    }
}

val noBypassTest = Driver(() => new RF(exRFConfigNoBypass)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

val softBypassTest = Driver(() => new RF(exRFConfigSoftBypass)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

val hardBypassTest = Driver(() => new RF(exRFConfigHardBypass)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}<jupyter_output>[[35minfo[0m] [0.001] Elaborating design...
[[35minfo[0m] [0.089] Done elaborating.
Total FIRRTL Compile Time: 374.2 ms
Total FIRRTL Compile Time: 149.2 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.002] SEED 1533862079968
test cmd3WrapperHelperRF Success: 8 tests passed in 6 cycles taking 0.052909 seconds
[[35minfo[0m] [0.017] RAN 1 CYCLES PASSED
[[35minfo[0m] [0.001] Elaborating design...
[[35minfo[0m] [0.010] Done elaborating.
Total FIRRTL Compile Time: 104.5 ms
Total FIRRTL Compile Time: 98.8 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862081153
test cmd3WrapperHelperRF Success: 8 tests passed in 6 cycles taking 0.020892 seconds
[[35minfo[0m] [0.011] RAN 1 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.013] Done elaborating.
Total FIRRTL Compile Time: 121.6 ms
Total FIRRTL Compile Time: 111.8 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862081[...]<jupyter_text>Inner Product Unit  Definition<jupyter_code>class IPUConfig(val width: Int, val inBitWidth: Int, val outBitWidth: Int, val bpType: String) {
    
    require(width >= 1, "Width must be at least one.\n")
    require(List("None", "Firm").contains(bpType), "Bypass must be \"None\" or \"Firm\".\n")
    require(inBitWidth > 0 && outBitWidth > 0, "Data bitwidth must be greater than 0\n")
    
    val bpFirm = (bpType == "Firm")
}

class IPUOutput(outBitWidth: Int, bp: Boolean) extends Bundle {
    
    override def cloneType = (new IPUOutput(outBitWidth, bp)).asInstanceOf[this.type]
    
    val innerProd = SInt(outBitWidth.W)
    // Extending the bitwidths for consistency
    val bpWeight = if (bp) Some(SInt(outBitWidth.W)) else None
    val bpActvtn = if (bp) Some(SInt(outBitWidth.W)) else None
}


class IPU(c: IPUConfig) extends Module {

    val io = IO(new Bundle {
        val bpSel = if (c.bpFirm) Some(Input(Vec(c.width, Bool()))) else None
        val weightIn = Input(Vec(c.width, SInt(c.inBitWidth.W)))
        val actvtnIn = Input(Vec(c.width, SInt(c.inBitWidth.W)))
        val out = Output(new IPUOutput(c.outBitWidth, c.bpFirm))
    })
    
    private class PMult extends Module {
        val io = IO(new Bundle {
            val weightVec = Input(Vec(c.width, SInt(c.inBitWidth.W)))
            val actvtnVec = Input(Vec(c.width, SInt(c.inBitWidth.W)))
            val pairwiseProd = Output(Vec(c.width, SInt(c.outBitWidth.W)))
        })
        io.pairwiseProd := (io.weightVec zip io.actvtnVec).map { case(a, b) => a * b }
    }
    
    private class SumTree extends Module {
        val io = IO(new Bundle {
            val inVec = Input(Vec(c.width, SInt(c.outBitWidth.W)))
            val sum = Output(SInt(c.outBitWidth.W))
        })
        
        // Recursively creates a balanced syntax tree
        private def adjReduce[A](xs: List[A], op: (A, A) => A): A = xs match {
            case List(single) => single
            case default => {
                val grouped = default.grouped(2).toList
                val result = for (g <- grouped) yield { g match {
                    case List(a, b) => op(a, b)
                    case List(x) => x
                }}
                adjReduce(result, op)
            }
        }
        
        io.sum := adjReduce(io.inVec.toList, (x: SInt, y: SInt) => x + y)
    }
    
    private val pMult = Module(new PMult)
    pMult.io.weightVec := io.weightIn
    pMult.io.actvtnVec := io.actvtnIn
    
    private val sumTree = Module(new SumTree)
    sumTree.io.inVec := pMult.io.pairwiseProd
    
    io.out.innerProd := sumTree.io.sum
    
    if (c.bpFirm) {
        io.out.bpWeight.get := PriorityMux(io.bpSel.get, io.weightIn)
        io.out.bpActvtn.get := PriorityMux(io.bpSel.get, io.actvtnIn)
    }
}<jupyter_output><empty_output><jupyter_text>Verification<jupyter_code>/*
Basic Test Checklist:
[-] Inner Products

[-] None Bypass

[-] Firm Bypass
    [-] Bypass Select

[-] Differing bitwidths
    [-] Small -> Large
    [-] Large -> Small
    [-] Bypass bitwidth

Better would be to check these together.
Even better would be to use Golden Model...
*/

val exIPUConfigNoBypass = new IPUConfig(4, 8, 8, "None")
val exIPUConfigFirmBypass = new IPUConfig(4, 8, 8, "Firm")
val exIPUConfigDiffBW1 = new IPUConfig(4, 4, 8, "None")
val exIPUConfigDiffBW2 = new IPUConfig(4, 8, 4, "None")

Driver(() => new IPU(exIPUConfigNoBypass)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new IPU(exIPUConfigFirmBypass)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new IPU(exIPUConfigDiffBW1)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new IPU(exIPUConfigDiffBW2)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}<jupyter_output>[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.025] Done elaborating.
Total FIRRTL Compile Time: 39.8 ms
Total FIRRTL Compile Time: 22.7 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862091419
test cmd5WrapperHelperIPU Success: 0 tests passed in 5 cycles taking 0.005219 seconds
[[35minfo[0m] [0.001] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.006] Done elaborating.
Total FIRRTL Compile Time: 28.4 ms
Total FIRRTL Compile Time: 32.2 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862091544
test cmd5WrapperHelperIPU Success: 0 tests passed in 5 cycles taking 0.007576 seconds
[[35minfo[0m] [0.000] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.009] Done elaborating.
Total FIRRTL Compile Time: 25.0 ms
Total FIRRTL Compile Time: 19.3 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862091639[...]<jupyter_text>ALU  Definition<jupyter_code>class ALUConfig(val dataWidth: Int, val funcs: List[String]) {
    
    require(funcs.length > 0, "Must support at least one function.")
    for(x <- funcs) { 
        require(List("Identity", "Add", "Max", "Accumulate").contains(x), "Unsupported function.")
    }
    
    val idnSupp = funcs.contains("Identity")
    val addSupp = funcs.contains("Add")
    val maxSupp = funcs.contains("Max")
    val accSupp = funcs.contains("Accumulate")
    val addBypassIn = addSupp || maxSupp
}

class ALUFSel(c: ALUConfig) extends Bundle {
    
    override def cloneType = (new ALUFSel(c)).asInstanceOf[this.type]
    
    // Priority is given from top to bottom
    val idnEnable = if (c.idnSupp) Some(Bool()) else None
    val addEnable = if (c.addSupp) Some(Bool()) else None
    val maxEnable = if (c.maxSupp) Some(Bool()) else None
    val accEnable = if (c.accSupp) Some(Bool()) else None
}

class ALU(c: ALUConfig) extends Module {
 
    val io = IO(new Bundle {
        val fSel = Input(new ALUFSel(c))
        val ipu = Input(new IPUOutput(c.dataWidth, c.addBypassIn))
        val rf = if (c.accSupp) Some(Input(SInt(c.dataWidth.W))) else None
        val out = Output(SInt(c.dataWidth.W))
    })
    
    // The inner "OrElse" clauses are logically unnecessary,
    // but Chisel can't infer that.
    when (io.fSel.idnEnable.getOrElse(false.B)) {
        io.out := io.ipu.innerProd
    } .elsewhen (io.fSel.addEnable.getOrElse(false.B)) {
        io.out := io.ipu.bpWeight.getOrElse(0.S) + io.ipu.bpActvtn.getOrElse(0.S)
    } .elsewhen (io.fSel.maxEnable.getOrElse(false.B)) {
        when (io.ipu.bpWeight.getOrElse(0.S) > io.ipu.bpActvtn.getOrElse(0.S)) {
            io.out := io.ipu.bpWeight.getOrElse(0.S)
        } .otherwise {
            io.out := io.ipu.bpActvtn.getOrElse(0.S)
        }
    } .elsewhen (io.fSel.accEnable.getOrElse(false.B)) {
        io.out := io.ipu.innerProd + io.rf.getOrElse(0.S)
    } .otherwise {
        io.out := 0.S
    }
}<jupyter_output><empty_output><jupyter_text>Verification<jupyter_code>/*
Basic Test Checklist:
[-] Alone
    [-] Identity
    [-] Add
    [-] Max
    [-] Accumulate

[-] All functions together
    [-] Identity
    [-] Add
    [-] Max
    [-] Accumulate
    [-] Priority

Better would be to check these together.
Even better would be to use Golden Model...
*/

val exALUConfigIdn = new ALUConfig(8, List("Identity"))
val exALUConfigAdd = new ALUConfig(8, List("Add"))
val exALUConfigMax = new ALUConfig(8, List("Max"))
val exALUConfigAcc = new ALUConfig(8, List("Accumulate"))
val exALUConfigAll = new ALUConfig(8, List("Identity", "Add", "Max", "Accumulate"))


Driver(() => new ALU(exALUConfigIdn)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new ALU(exALUConfigAdd)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new ALU(exALUConfigMax)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new ALU(exALUConfigAcc)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}

Driver(() => new ALU(exALUConfigAll)) {
    uut => new PeekPokeTester(uut) {
        // TODO
    }
}<jupyter_output>[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.015] Done elaborating.
Total FIRRTL Compile Time: 13.7 ms
Total FIRRTL Compile Time: 10.5 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862100653
test cmd7WrapperHelperALU Success: 0 tests passed in 5 cycles taking 0.001040 seconds
[[35minfo[0m] [0.000] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.002] Done elaborating.
Total FIRRTL Compile Time: 12.2 ms
Total FIRRTL Compile Time: 9.1 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862100706
test cmd7WrapperHelperALU Success: 0 tests passed in 5 cycles taking 0.001181 seconds
[[35minfo[0m] [0.001] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.003] Done elaborating.
Total FIRRTL Compile Time: 10.7 ms
Total FIRRTL Compile Time: 9.3 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862100752
t[...]<jupyter_text>Nonlinear Unit<jupyter_code>class NLUConfig(val inBitWidth: Int, val outBitWidth: Int, val funcs: List[String]) {
    
    for(x <- funcs) {
        require(List("Identity", "ReLu").contains(x), "Unsupported Function")
    }
    
    val idSupp = funcs.contains("Identity")
    val reluSupp = funcs.contains("ReLu")
    val tanhSupp = false //funcs.contains("tanh")
    val sinhSupp = false //funcs.contains("sinh")
}

class NLUFSel(c: NLUConfig) extends Bundle {
    
    override def cloneType = (new NLUFSel(c)).asInstanceOf[this.type]
    
    val idEnable = if (c.idSupp) Some(Bool()) else None
    val reluEnable = if (c.reluSupp) Some(Bool()) else None
    val tanhEnable = if (c.tanhSupp) Some(Bool()) else None 
    val sinhEnable = if (c.sinhSupp) Some(Bool()) else None
}

class NLU(c: NLUConfig) extends Module {
    
    val io = IO(new Bundle {
        val fSel = Input(new NLUFSel(c))
        val in = Input(SInt(c.inBitWidth.W))
        val out = Output(SInt(c.outBitWidth.W))
    })
    
    when (io.fSel.idEnable.getOrElse(false.B)) {
        io.out := io.in
    } .elsewhen (io.fSel.reluEnable.getOrElse(false.B)) {
        when (io.in.data > 0.S) {
            io.out := io.in.data
        } .otherwise {
            io.out := 0.S
        }
    } .elsewhen (io.fSel.tanhEnable.getOrElse(false.B)) {
        // TODO
        io.out := 0.S
    } .elsewhen (io.fSel.sinhEnable.getOrElse(false.B)) {
        // TODO
        io.out := 0.S
    } .otherwise {
        io.out := 0.S
    }
}
/*
Basic Test Checklist:
[ ] Alone
    [ ] Identity
    [ ] ReLu
    [ ] tanh
    [ ] sinh

[ ] All functions together
    [ ] Identity works
    [ ] ReLu works
    [ ] tanh works
    [ ] sinh works
    [ ] Correct Priority

Better would be to check these together.
Even better would be to use Golden Model...
*/

val exNLUConfigId = new NLUConfig(8, 8, List("Identity"))
val exNLUConfigReLu = new NLUConfig(8, 8, List("ReLu"))
val exNLUConfigIdReLu = new NLUConfig(8, 8, List("Identity", "ReLu"))

Driver(() => new NLU(exNLUConfigId)) {
    uut => new PeekPokeTester(uut) {

    }
}

Driver(() => new NLU(exNLUConfigReLu)) {
    uut => new PeekPokeTester(uut) {

    }
}

Driver(() => new NLU(exNLUConfigIdReLu)) {
    uut => new PeekPokeTester(uut) {

    }
}<jupyter_output>[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.012] Done elaborating.
Total FIRRTL Compile Time: 6.4 ms
Total FIRRTL Compile Time: 5.2 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862114359
test cmd9WrapperHelperNLU Success: 0 tests passed in 5 cycles taking 0.000944 seconds
[[35minfo[0m] [0.000] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.004] Done elaborating.
Total FIRRTL Compile Time: 6.9 ms
Total FIRRTL Compile Time: 5.1 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862114396
test cmd9WrapperHelperNLU Success: 0 tests passed in 5 cycles taking 0.000946 seconds
[[35minfo[0m] [0.001] RAN 0 CYCLES PASSED
[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.002] Done elaborating.
Total FIRRTL Compile Time: 6.6 ms
Total FIRRTL Compile Time: 4.9 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533862114425
test [...]<jupyter_text>Control  Decoder  Definition<jupyter_code>class PEConfig(
        val weightRFConfig: RFConfig,
        val actvtnRFConfig: RFConfig,
        val scratchRFConfig: RFConfig,
        val ipuConfig: IPUConfig,
        val aluConfig: ALUConfig,
        val nluConfig: NLUConfig,
        //val smConfig: StateMachineConfig,
        val smWidth: Int,
        val decodeWeightRF: (UInt, RFConfig) => Data,
        val decodeActvtnRF: (UInt, RFConfig) => Data,
        val decodeScratchRF: (UInt, RFConfig) => Data,
        val decodeIPU: (UInt, IPUConfig) => Data,
        val decodeALU: (UInt, ALUConfig) => Data,
        val decodeNLU: (UInt, NLUConfig) => Data) {

    require(ipuConfig.width == weightRFConfig.numIntOutputs, 
        "IPU input width not equal to Weight RF Internal Output width.\n")
    require(ipuConfig.width == actvtnRFConfig.numIntOutputs,
        "IPU input width not equal to Activation RF Internal Output width.\n")
    
    if(ipuConfig.bpFirm) {
        require(aluConfig.addSupp || aluConfig.maxSupp,
            "Incompatible ALU and IPU Configurations")
    }
}

class MemoryControl(c: PEConfig) extends Bundle {
    
    override def cloneType = (new MemoryControl(c)).asInstanceOf[this.type]
    
    val weightRF = new RFControl(c.weightRFConfig)
    val actvtnRF = new RFControl(c.actvtnRFConfig)
    val scratchRF = new RFControl(c.scratchRFConfig)
}

class ProcessControl(c: PEConfig) extends Bundle {
    
    override def cloneType = (new ProcessControl(c)).asInstanceOf[this.type]
    
    val aluFSel = Output(new ALUFSel(c.aluConfig))
    val nluFSel = Output(new NLUFSel(c.nluConfig))
    
    val ipuBpSel = if (c.ipuConfig.bpFirm) Some(Output(Vec(c.ipuConfig.width, Bool()))) else None
}

class Decoder(c: PEConfig) extends Module {
    
    val io = IO(new Bundle {
        val state = Input(UInt(c.smWidth.W))
        val mem = Output(new MemoryControl(c))
        val proc = Output(new ProcessControl(c))
    })
    
    io.mem.weightRF <> c.decodeWeightRF(io.state, c.weightRFConfig)
    io.mem.actvtnRF <> c.decodeActvtnRF(io.state, c.actvtnRFConfig)
    io.mem.scratchRF <> c.decodeScratchRF(io.state, c.scratchRFConfig)
    
    if (c.ipuConfig.bpFirm) { 
        io.proc.ipuBpSel.get := c.decodeIPU(io.state, c.ipuConfig)
    }
    
    io.proc.aluFSel <> c.decodeALU(io.state, c.aluConfig)
    io.proc.nluFSel <> c.decodeNLU(io.state, c.nluConfig)
}<jupyter_output><empty_output><jupyter_text>Example<jupyter_code>val exampleDecodeWeightRF = (state: UInt, c: RFConfig) => {
    
    val data = Wire(new RFControl(c))
    
    val ic = c.intConfig
    val ec = c.extConfig
    
    when (state === 0.U) {
        
        // Can't call Vec.fill on size 0.
        // You shouldn't be doing anything like this since you know
        // the shape of the RFs in advanced. Also, this is just terrible.
        if (ic.numInputs > 0) {
            data.internal.get.wEnable := Vec.fill(ic.numInputs){true.B}
            data.internal.get.wAddr.get := Vec.fill(ic.numInputs){1.U}
        }
        
        if (ic.numOutputs > 0) {
            data.internal.get.rEnable := Vec.fill(ic.numOutputs){true.B}
            data.internal.get.rAddr.get := Vec.fill(ic.numOutputs){2.U}
            if (ic.numCrossInputs > 0) {
                data.internal.get.bpSel.get := Vec.fill(ic.numOutputs){
                    Vec.fill(ic.numCrossInputs){true.B}}
            }
        }
        
        if (ec.numInputs > 0) {
            data.external.get.wEnable := Vec.fill(ec.numInputs){true.B}
            data.external.get.wAddr.get := Vec.fill(ec.numInputs){3.U}
        }
        
        if (ec.numOutputs > 0) {
            data.external.get.rEnable := Vec.fill(ec.numOutputs){true.B}
            data.external.get.rAddr.get := Vec.fill(ec.numOutputs){4.U}
            if (ec.numCrossInputs > 0) {
                data.external.get.bpSel.get := Vec.fill(ec.numOutputs){
                    Vec.fill(ec.numCrossInputs){true.B}}
            }
        }
        
    } .otherwise {
        
        if (ic.numInputs > 0) {
            data.internal.get.wAddr.get := Vec.fill(ic.numInputs){5.U}
            data.internal.get.wEnable := Vec.fill(ic.numInputs){false.B}
        }
        
        if (ic.numOutputs > 0) {
            data.internal.get.rEnable := Vec.fill(ic.numOutputs){false.B}
            data.internal.get.rAddr.get := Vec.fill(ic.numOutputs){6.U}
            if(ic.numCrossInputs > 0) {
                data.internal.get.bpSel.get := Vec.fill(ic.numOutputs){
                    Vec.fill(ic.numCrossInputs){false.B}}
            }
        }
        
        if (ec.numInputs > 0) {
            data.external.get.wEnable := Vec.fill(ec.numInputs){false.B}
            data.external.get.wAddr.get := Vec.fill(ec.numInputs){7.U}
        }
        
        if(ec.numOutputs > 0) {
            data.external.get.rEnable := Vec.fill(ec.numOutputs){false.B}
            data.external.get.rAddr.get := Vec.fill(ec.numOutputs){8.U}
            if(ec.numCrossInputs > 0) {
                data.external.get.bpSel.get := Vec.fill(ec.numOutputs){
                    Vec.fill(ec.numCrossInputs){false.B}}
        
            }
        }
    }
    
    data
}

val exampleDecodeActvtnRF = exampleDecodeWeightRF
val exampleDecodeScratchRF = exampleDecodeWeightRF

def exampleDecodeIPU(state: UInt, c: IPUConfig) = {
    
    val data = Wire(Vec(c.width, Bool()))
    
    when (state === 0.U) {
        data := Vec(false.B :: true.B :: Nil)
    } .otherwise {
        data := Vec(true.B :: false.B :: Nil)
    }
    
    data
}

def exampleDecodeALU(state: UInt, c: ALUConfig) = {
    
    val data = Wire(new ALUFSel(c))
    
    when (state === 0.U) {
        data.idnEnable.get := true.B
        data.addEnable.get := false.B
        data.maxEnable.get := false.B
        data.accEnable.get := false.B
    } .otherwise {
        data.idnEnable.get := false.B
        data.addEnable.get := true.B
        data.maxEnable.get := false.B
        data.accEnable.get := false.B
    }
    
    data
}

def exampleDecodeNLU(state: UInt, c: NLUConfig) = {
    
    val data = Wire(new NLUFSel(c))
    
    when (state === 0.U) {
        data.idEnable.get := true.B
        data.reluEnable.get := false.B
    } .otherwise {
        data.idEnable.get := false.B
        data.reluEnable.get := true.B
    }
    
    data
}<jupyter_output><empty_output><jupyter_text>Verification<jupyter_code>val examplePEConfigForDecoder = new PEConfig(
    new RFConfig(1, 1, 2, 1, 4, 8, "Soft"),
    new RFConfig(1, 1, 2, 1, 4, 8, "Soft"),
    new RFConfig(1, 1, 1, 1, 4, 8, "Soft"),
    new IPUConfig(2, 8, 8, "Firm"),
    new ALUConfig(8, List("Identity", "Add", "Max", "Accumulate")),
    new NLUConfig(8, 8, List("Identity", "ReLu")),
    new StateMachineConfig(4, 4, exampleStateMap),
    exampleDecodeWeightRF,
    exampleDecodeActvtnRF,
    exampleDecodeScratchRF,
    exampleDecodeIPU,
    exampleDecodeALU,
    exampleDecodeNLU
)


Driver(() => new Decoder(examplePEConfigForDecoder)) {
    
    uut => new PeekPokeTester(uut) {
        
        poke(uut.io.state, 0.U)
        step(1)
        
        expect(uut.io.mem.weightRF.internal.get.wEnable(0), true.B)
        expect(uut.io.mem.weightRF.internal.get.rEnable(0), true.B)
        expect(uut.io.mem.weightRF.internal.get.wAddr.get(0), 1.U)
        expect(uut.io.mem.weightRF.internal.get.rAddr.get(0), 2.U)
        expect(uut.io.mem.weightRF.internal.get.bpSel.get(0)(0), true.B)
        
        expect(uut.io.mem.weightRF.external.get.wEnable(0), true.B)
        expect(uut.io.mem.weightRF.external.get.rEnable(0), true.B)
        expect(uut.io.mem.weightRF.external.get.wAddr.get(0), 3.U)
        expect(uut.io.mem.weightRF.external.get.rAddr.get(0), 4.U)
        expect(uut.io.mem.weightRF.external.get.bpSel.get(0)(0), true.B)
        
        expect(uut.io.mem.actvtnRF.internal.get.wEnable(0), true.B)
        expect(uut.io.mem.actvtnRF.internal.get.rEnable(0), true.B)
        expect(uut.io.mem.actvtnRF.internal.get.wAddr.get(0), 1.U)
        expect(uut.io.mem.actvtnRF.internal.get.rAddr.get(0), 2.U)
        expect(uut.io.mem.actvtnRF.internal.get.bpSel.get(0)(0), true.B)
        
        expect(uut.io.mem.actvtnRF.external.get.wEnable(0), true.B)
        expect(uut.io.mem.actvtnRF.external.get.rEnable(0), true.B)
        expect(uut.io.mem.actvtnRF.external.get.wAddr.get(0), 3.U)
        expect(uut.io.mem.actvtnRF.external.get.rAddr.get(0), 4.U)
        expect(uut.io.mem.actvtnRF.external.get.bpSel.get(0)(0), true.B)
        
        expect(uut.io.proc.ipuBpSel.get(0), 0)
        expect(uut.io.proc.ipuBpSel.get(1), 1)
        
        expect(uut.io.proc.aluFSel.idnEnable.get, 1)
        expect(uut.io.proc.aluFSel.addEnable.get, 0)
        expect(uut.io.proc.aluFSel.maxEnable.get, 0)
        expect(uut.io.proc.aluFSel.accEnable.get, 0)
        
        expect(uut.io.mem.scratchRF.internal.get.wEnable(0), true.B)
        expect(uut.io.mem.scratchRF.internal.get.rEnable(0), true.B)
        expect(uut.io.mem.scratchRF.internal.get.wAddr.get(0), 1.U)
        expect(uut.io.mem.scratchRF.internal.get.rAddr.get(0), 2.U)
        expect(uut.io.mem.scratchRF.internal.get.bpSel.get(0)(0), true.B)
        
        expect(uut.io.mem.scratchRF.external.get.wEnable(0), true.B)
        expect(uut.io.mem.scratchRF.external.get.rEnable(0), true.B)
        expect(uut.io.mem.scratchRF.external.get.wAddr.get(0), 3.U)
        expect(uut.io.mem.scratchRF.external.get.rAddr.get(0), 4.U)
        expect(uut.io.mem.scratchRF.external.get.bpSel.get(0)(0), true.B)

        expect(uut.io.proc.nluFSel.idEnable.get, 1)
        expect(uut.io.proc.nluFSel.reluEnable.get, 0)
        
        poke(uut.io.state, 1.U) 
        step(1)
        
        expect(uut.io.mem.weightRF.internal.get.wEnable(0), false.B)
        expect(uut.io.mem.weightRF.internal.get.rEnable(0), false.B)
        expect(uut.io.mem.weightRF.internal.get.wAddr.get(0), 5.U)
        expect(uut.io.mem.weightRF.internal.get.rAddr.get(0), 6.U)
        expect(uut.io.mem.weightRF.internal.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.mem.weightRF.external.get.wEnable(0), false.B)
        expect(uut.io.mem.weightRF.external.get.rEnable(0), false.B)
        expect(uut.io.mem.weightRF.external.get.wAddr.get(0), 7.U)
        expect(uut.io.mem.weightRF.external.get.rAddr.get(0), 8.U)
        expect(uut.io.mem.weightRF.external.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.mem.actvtnRF.internal.get.wEnable(0), false.B)
        expect(uut.io.mem.actvtnRF.internal.get.rEnable(0), false.B)
        expect(uut.io.mem.actvtnRF.internal.get.wAddr.get(0), 5.U)
        expect(uut.io.mem.actvtnRF.internal.get.rAddr.get(0), 6.U)
        expect(uut.io.mem.actvtnRF.internal.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.mem.actvtnRF.external.get.wEnable(0), false.B)
        expect(uut.io.mem.actvtnRF.external.get.rEnable(0), false.B)
        expect(uut.io.mem.actvtnRF.external.get.wAddr.get(0), 7.U)
        expect(uut.io.mem.actvtnRF.external.get.rAddr.get(0), 8.U)
        expect(uut.io.mem.actvtnRF.external.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.proc.ipuBpSel.get(0), 1)
        expect(uut.io.proc.ipuBpSel.get(1), 0)
        
        expect(uut.io.proc.aluFSel.idnEnable.get, 0)
        expect(uut.io.proc.aluFSel.addEnable.get, 1)
        expect(uut.io.proc.aluFSel.maxEnable.get, 0)
        expect(uut.io.proc.aluFSel.accEnable.get, 0)
        
        expect(uut.io.mem.scratchRF.internal.get.wEnable(0), false.B)
        expect(uut.io.mem.scratchRF.internal.get.rEnable(0), false.B)
        expect(uut.io.mem.scratchRF.internal.get.wAddr.get(0), 5.U)
        expect(uut.io.mem.scratchRF.internal.get.rAddr.get(0), 6.U)
        expect(uut.io.mem.scratchRF.internal.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.mem.scratchRF.external.get.wEnable(0), false.B)
        expect(uut.io.mem.scratchRF.external.get.rEnable(0), false.B)
        expect(uut.io.mem.scratchRF.external.get.wAddr.get(0), 7.U)
        expect(uut.io.mem.scratchRF.external.get.rAddr.get(0), 8.U)
        expect(uut.io.mem.scratchRF.external.get.bpSel.get(0)(0), false.B)
        
        expect(uut.io.proc.nluFSel.idEnable.get, 0)
        expect(uut.io.proc.nluFSel.reluEnable.get, 1)
        
    }
}<jupyter_output>[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.051] Done elaborating.
Total FIRRTL Compile Time: 81.5 ms
Total FIRRTL Compile Time: 43.2 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533145921179
test cmd13WrapperHelperDecoder Success: 76 tests passed in 7 cycles taking 0.028451 seconds
[[35minfo[0m] [0.022] RAN 2 CYCLES PASSED<jupyter_text>PE  Definition<jupyter_code>class PE(c: PEConfig) extends Module {
    
    val cw = c.weightRFConfig
    val ca = c.actvtnRFConfig
    val cs = c.scratchRFConfig
    
    val io = IO(new Bundle {
        val stateCtrl = Input(UInt(c.smWidth.W))
        val toWeightRF = Input(Vec(cw.numExtInputs, SInt(cw.dataWidth.W))) 
        val toActvtnRF = Input(Vec(ca.numExtInputs, SInt(ca.dataWidth.W)))
        val toScratchRF = Input(Vec(cs.numExtInputs, SInt(cs.dataWidth.W)))
        val fromWeightRF = Output(Vec(cw.numExtOutputs, SInt(cw.dataWidth.W)))
        val fromActvtnRF = Output(Vec(ca.numExtOutputs, SInt(ca.dataWidth.W)))
        val fromScratchRF = Output(Vec(cs.numExtOutputs, SInt(cs.dataWidth.W)))
        val totalOutput = Output(SInt(c.nluConfig.outBitWidth.W))
    })
    
    val decoder = Module(new Decoder(c))
    decoder.io.state := io.stateCtrl
    
    val weightRF = Module(new RF(cw))
    weightRF.io.control <> decoder.io.mem.weightRF
    weightRF.io.wExternal := io.toWeightRF
    io.fromWeightRF := weightRF.io.rExternal
    
    val actvtnRF = Module(new RF(ca))
    actvtnRF.io.control <> decoder.io.mem.actvtnRF
    actvtnRF.io.wExternal := io.toActvtnRF
    io.fromActvtnRF := actvtnRF.io.rExternal
       
    val ipu = Module(new IPU(c.ipuConfig))
    if (ipu.io.bpSel.isDefined) { 
        ipu.io.bpSel.get := decoder.io.proc.ipuBpSel.get 
    }
    ipu.io.weightIn := weightRF.io.rInternal
    ipu.io.actvtnIn := actvtnRF.io.rInternal

    val alu = Module(new ALU(c.aluConfig))
    alu.io.fSel <> decoder.io.proc.aluFSel
    alu.io.ipu <> ipu.io.out
    
    val scratchRF = Module(new RF(cs))
    scratchRF.io.control <> decoder.io.mem.scratchRF
    scratchRF.io.wExternal := io.toScratchRF
    scratchRF.io.wInternal(0) := alu.io.out // TODO: Add Req. for this
    io.fromScratchRF := scratchRF.io.rExternal
    if(alu.io.rf.isDefined) { 
        alu.io.rf.get := scratchRF.io.rInternal(0) // TODO: Add Req. for this
    }
    
    val nlu = Module(new NLU(c.nluConfig))
    nlu.io.fSel <> decoder.io.proc.nluFSel
    nlu.io.in := scratchRF.io.rInternal(0) // TODO: Add Req. for this
    
    io.totalOutput := nlu.io.out
}<jupyter_output><empty_output><jupyter_text>Verification<jupyter_code>/*
 * Row stationary 1D convolution
 *
 * (1, 2, 3) * (1, 2, 3, 4, 5) = (14, 20, 26)
 */

val rsDecodeWeightRF = (state: UInt, c: RFConfig) => {
    
    val ctrl = Wire(new RFControl(c))
    
    when (state === 0.U || state === 1.U || state === 2.U) {
        ctrl.external.get.wEnable(0) := true.B
        ctrl.external.get.wAddr.get(0) := state
    } .otherwise {
        ctrl.external.get.wEnable(0) := false.B
        ctrl.external.get.wAddr.get(0) := DontCare
    }
    
    when (state >= 1.U && state <= 9.U) {
        ctrl.internal.get.rEnable(0) := true.B
        ctrl.internal.get.rAddr.get(0) := (state - 1.U) % 3.U
    } .otherwise {
        ctrl.internal.get.rEnable(0) := false.B
        ctrl.internal.get.rAddr.get(0) := DontCare
    }

    ctrl
}

val rsDecodeActvtnRF = (state: UInt, c: RFConfig) => {
    
    val ctrl = Wire(new RFControl(c))
    
    when (state <= 8.U) {
        ctrl.external.get.wEnable(0) := true.B
        ctrl.external.get.wAddr.get(0) := state % 3.U
    } .otherwise {
        ctrl.external.get.wEnable(0) := false.B
        ctrl.external.get.wAddr.get(0) := DontCare
    }
    
    when (state >= 1.U && state <= 9.U) {
        ctrl.internal.get.rEnable(0) := true.B
        ctrl.internal.get.rAddr.get(0) := (state - 1.U) % 3.U
    } .otherwise {
        ctrl.internal.get.rEnable(0) := false.B
        ctrl.internal.get.rAddr.get(0) := DontCare
    }
    
    ctrl
}

val rsDecodeScratchRF = (state: UInt, c: RFConfig) => {
    
    val ctrl = Wire(new RFControl(c))
    
    when (state >= 1.U && state <= 9.U) {
        ctrl.internal.get.wEnable(0) := true.B
    } .otherwise {
        ctrl.internal.get.wEnable(0) := false.B
    }
    
    when (state >= 1.U && state <= 10.U && state =/= 4.U && state =/= 7.U) {
        ctrl.internal.get.rEnable(0) := true.B
    } .otherwise {
        ctrl.internal.get.rEnable(0) := false.B
    }
    
    when (state === 1.U) {
        ctrl.internal.get.rAddr.get(0) := DontCare
        ctrl.internal.get.wAddr.get(0) := 0.U
    } .elsewhen (state === 2.U) {
        ctrl.internal.get.rAddr.get(0) := 0.U
        ctrl.internal.get.wAddr.get(0) := 1.U
    } .elsewhen (state === 3.U) {
        ctrl.internal.get.rAddr.get(0) := 1.U
        ctrl.internal.get.wAddr.get(0) := 0.U
    } .elsewhen (state === 4.U) {
        ctrl.internal.get.rAddr.get(0) := DontCare
        ctrl.internal.get.wAddr.get(0) := 1.U
    } .elsewhen (state === 5.U) {
        ctrl.internal.get.rAddr.get(0) := 1.U
        ctrl.internal.get.wAddr.get(0) := 2.U
    } .elsewhen (state === 6.U) {
        ctrl.internal.get.rAddr.get(0) := 2.U
        ctrl.internal.get.wAddr.get(0) := 1.U
    } .elsewhen (state === 7.U) {
        ctrl.internal.get.rAddr.get(0) := DontCare
        ctrl.internal.get.wAddr.get(0) := 2.U
    } .elsewhen (state === 8.U) {
        ctrl.internal.get.rAddr.get(0) := 2.U
        ctrl.internal.get.wAddr.get(0) := 3.U
    } .elsewhen (state === 9.U) {
        ctrl.internal.get.rAddr.get(0) := 3.U
        ctrl.internal.get.wAddr.get(0) := 2.U
    } .elsewhen (state === 10.U) {
        ctrl.internal.get.rAddr.get(0) := 2.U
        ctrl.internal.get.wAddr.get(0) := DontCare
    } .otherwise {
        ctrl.internal.get.rAddr.get(0) := DontCare
        ctrl.internal.get.wAddr.get(0) := DontCare
    }
    
    ctrl
}

val rsDecodeIPU = (state: UInt, c: IPUConfig) => {
    val bpSel = Wire(Vec(c.width, Bool()))
    bpSel
}

val rsDecodeALU = (state: UInt, c: ALUConfig) => {
    val fSel = Wire(new ALUFSel(c)) 
    when (state === 1.U || state === 4.U || state === 7.U) {
        fSel.idnEnable.get := true.B
        fSel.accEnable.get := false.B
    } .elsewhen (state > 1.U && state < 10.U) {
        fSel.idnEnable.get := false.B
        fSel.accEnable.get := true.B
    } .otherwise {
        fSel.idnEnable.get := false.B
        fSel.accEnable.get := false.B
    }
    fSel
}

val rsDecodeNLU = (state: UInt, c: NLUConfig) => {
    val fSel = Wire(new NLUFSel(c))
    fSel.idEnable.get := true.B
    fSel
}

val rsStateMap = (state: UInt, control: UInt, c: StateMachineConfig) => {
    
    val nextState = Wire(UInt(c.stateWidth.W))
    
    when(control === 1.U && state < 10.U) {
        nextState := state + 1.U
    } .otherwise {
        nextState := state
    }
    
    nextState
}

val rsPEConfig = new PEConfig(
    new RFConfig(0, 1, 1, 0, 4, 16, "None"),
    new RFConfig(0, 1, 1, 0, 4, 16, "None"),
    new RFConfig(1, 0, 1, 0, 4, 16, "None"),
    new IPUConfig(1, 16, 16, "None"),
    new ALUConfig(8, List("Identity", "Accumulate")),
    new NLUConfig(8, 8, List("Identity")),
    new StateMachineConfig(4, 4, rsStateMap),
    rsDecodeWeightRF,
    rsDecodeActvtnRF,
    rsDecodeScratchRF,
    rsDecodeIPU,
    rsDecodeALU,
    rsDecodeNLU
)

Driver(() => new PE(rsPEConfig)) {
    uut => new PeekPokeTester(uut) {
        
    }
}<jupyter_output>[[35minfo[0m] [0.000] Elaborating design...
[[35minfo[0m] [0.047] Done elaborating.
Total FIRRTL Compile Time: 164.2 ms
Total FIRRTL Compile Time: 125.6 ms
End of dependency graph
Circuit state created
[[35minfo[0m] [0.000] SEED 1533165266381
test cmd16WrapperHelperPE Success: 0 tests passed in 5 cycles taking 0.012225 seconds
[[35minfo[0m] [0.000] RAN 0 CYCLES PASSED
{'fraction_non_alphanumeric': 0.14866862320858593, 'fraction_numerical': 0.02917675285448041, 'mean_word_length': 2.5640555810047334, 'pattern_counts': {'":': 0, '<': 72, '<?xml version=': 0, '>': 157, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '467796', 'license': 'MIT', 'n_tokens_mistral': 18725, 'n_tokens_neox': 17550, 'n_words': 3277, 'path': 'Verification - Unified Processing Engine.ipynb', 'repo_name': 'shuangchenli/processing-engine', 'size': '46681'}