Add perl scripts
This commit is contained in:
parent
504ecdc43b
commit
63abcdc980
|
@ -0,0 +1,47 @@
|
|||
package vivadoHLS
|
||||
|
||||
import Chisel._
|
||||
|
||||
//These are definitions of the bus standards used on Vivado HLS generated accelerators
|
||||
|
||||
//Request Packet Format
|
||||
class ApBusReq(dataWidth:Int, addrWidth:Int) extends Bundle{
|
||||
//Req specific lines
|
||||
//Specifies a write request
|
||||
val din = Bool(OUTPUT) //req_din in verilog
|
||||
|
||||
//Lines used for req
|
||||
val address = UInt(OUTPUT, width = addrWidth)
|
||||
val dataout = UInt(OUTPUT, width = dataWidth)
|
||||
val size = UInt(OUTPUT, width = addrWidth)
|
||||
override def cloneType: this.type = new ApBusReq(dataWidth, addrWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
//Response Packet Format
|
||||
class ApBusRsp(dataWidth:Int) extends Bundle{
|
||||
val datain = UInt(INPUT , width = dataWidth)
|
||||
override def cloneType: this.type = new ApBusRsp(dataWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class ApBusIO(dataWidth:Int = 64, addrWidth:Int = 32) extends Bundle{
|
||||
val req = new ApBusReq(dataWidth, addrWidth)
|
||||
val req_full_n = Bool(INPUT ) //req_full_n in verilog
|
||||
//Write the request
|
||||
val req_write = Bool(OUTPUT) //req_write in verilog
|
||||
|
||||
val rsp = new ApBusRsp(dataWidth)
|
||||
val rsp_empty_n = Bool(INPUT )
|
||||
val rsp_read = Bool(OUTPUT)
|
||||
override def cloneType: this.type = new ApBusIO(dataWidth, addrWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class ApCtrlIO(dataWidth:Int = 64) extends Bundle{
|
||||
//val clk = Bool(INPUT )
|
||||
//val rst = Bool(INPUT )
|
||||
val start = Bool(INPUT )
|
||||
val done = Bool(OUTPUT)
|
||||
val idle = Bool(OUTPUT)
|
||||
val ready = Bool(OUTPUT)
|
||||
val rtn = UInt(OUTPUT, width = dataWidth)
|
||||
override def cloneType: this.type = new ApCtrlIO(dataWidth).asInstanceOf[this.type]
|
||||
}
|
|
@ -0,0 +1,511 @@
|
|||
package controlUtils
|
||||
|
||||
import Chisel._
|
||||
|
||||
//This is based on the existing chisel arbiters but has a different implementation
|
||||
class PriorityArbiterIO[T <: Data](gen: T, n: Int, priorityBits: Int) extends Bundle{
|
||||
val in = Vec(n, Flipped(Decoupled(gen)))
|
||||
val out = Decoupled(gen)
|
||||
val chosen = UInt(OUTPUT, log2Up(n))
|
||||
//Priorities of the inputs (0 is max priority)
|
||||
val priority = Vec(n,Input(UInt(priorityBits.W)))
|
||||
//Priority of the outputed value
|
||||
val priorityOut = UInt(OUTPUT, width = priorityBits)
|
||||
override def cloneType: this.type = new PriorityArbiterIO(gen, n, priorityBits).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class PriorityArbiter[T <: Data](gen: T, n: Int, priorityBits: Int) extends Module{
|
||||
val io = IO(new PriorityArbiterIO(gen, n, priorityBits))
|
||||
|
||||
//val indexedPriority = io.priority.zipWithIndex;
|
||||
|
||||
//val indexedPriorityValid = indexedPriority.zip(io.in.map(_.valid))
|
||||
|
||||
if(n == 1){
|
||||
//No arbitration required!
|
||||
io.out.valid := io.in(0).valid
|
||||
io.out.bits := io.in(0).bits
|
||||
io.chosen := UInt(0)
|
||||
io.priorityOut := io.priority(0)
|
||||
io.in(0).ready := io.out.ready
|
||||
}
|
||||
else{
|
||||
|
||||
//val minPriorityValue = indexedPriorityValid.slice(1, n).foldLeft(indexedPriorityValid(0))((a, b) => Mux((a._2 === Bool(false) && b._2 === Bool(true) || (b._2 === Bool(true) && (b._1)._1 < (a._1)._1)), b, a)) //the max priority (smaller priority values are more important)
|
||||
|
||||
//The slice is becuase we favor the left element in this simple arbiter
|
||||
//We scan from left to right (from the first element to the last) and check
|
||||
//if the next element has a smaller priority value. If it does, it becomes
|
||||
//the element that is checked against for the remainder of the list. The slice
|
||||
//is because an initial value has to be given for fold and it makes little sense
|
||||
//to compare the first element with itself. The (a._1)._1 is accessing the 1st
|
||||
//element of the zipped tuple (prority, index). The first part of the boolean
|
||||
//function also ensures that if in(0) is not valid but in(y) is valid, that in(y)
|
||||
//is selected even if it has a higher priority value than in(0). This in effect
|
||||
//allows us to avoid filtering the list to only include valid inputs as a seperate
|
||||
//step.
|
||||
|
||||
//If no element is valid, in(0) is selected and is connected to the output.
|
||||
//Since its valid signal is low, this should have no effect and the priorityOut
|
||||
//value should be ignored.
|
||||
|
||||
//val chosenPriority = (minPriorityValue._1)._1
|
||||
//val chosenInd = UInt((minPriorityValue._1)._2)
|
||||
|
||||
//Well ... I was having a bunch of type errors trying to do this functioanlly (chisel/scala type system), so let's do it iterativly
|
||||
|
||||
//The use of var here is based on the chisel implementation of the basic arbiter
|
||||
//Normally, we would use a val but, in this case, it appears we can use a var as we are basically
|
||||
//proceduarally constructing a tree of muxes. The initial node is 0 but, on each itteration,
|
||||
//it is set to the muxof the current node and the selected one. The final value is then connected
|
||||
//to the output and contains the whole tree of muxes
|
||||
var chosenInd = UInt(0)
|
||||
for(i <- 1 until n){
|
||||
val chooseThis = (io.in(chosenInd).valid === Bool(false) && io.in(i).valid === Bool(true)) || (io.in(i).valid === Bool(true) && io.priority(i) < io.priority(chosenInd))
|
||||
chosenInd = Mux(chooseThis, UInt(i), chosenInd)
|
||||
}
|
||||
|
||||
//Set IO lines
|
||||
for(i <- 0 to n-1 by 1){
|
||||
//set the ready lines going to the input ports so that the chosen one
|
||||
//gets the value of io.out.ready and the rest get a value of Bool(false)
|
||||
io.in(i).ready := (chosenInd === UInt(i)) && io.out.ready
|
||||
}
|
||||
|
||||
io.out.valid := io.in(chosenInd).valid
|
||||
io.out.bits := io.in(chosenInd).bits
|
||||
io.chosen := chosenInd
|
||||
io.priorityOut := io.priority(chosenInd)
|
||||
}
|
||||
}
|
||||
|
||||
class InitCounter(val n: Int, val initVal: Int) {
|
||||
/** current value of the counter */
|
||||
val value = if (n == 1) UInt(0) else Reg(init=UInt(initVal, log2Up(n)))
|
||||
/** increment the counter
|
||||
* @return if the counter is at the max value */
|
||||
def inc(): Bool = {
|
||||
if (n == 1) Bool(true)
|
||||
else {
|
||||
val wrap = value === UInt(n-1)
|
||||
value := Mux(Bool(!isPow2(n)) && wrap, UInt(0), value + UInt(1))
|
||||
wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object InitCounter
|
||||
{
|
||||
def apply(n: Int, initVal: Int): InitCounter = new InitCounter(n, initVal)
|
||||
/** Get a counter which takes an input Bool of when to increment
|
||||
* @return a UInt which is the value of the counter and a Bool indicating when the counter resets
|
||||
*/
|
||||
def apply(cond: Bool, n: Int, initVal: Int): (UInt, Bool) = {
|
||||
val c = new InitCounter(n, initVal)
|
||||
var wrap: Bool = null
|
||||
when (cond) { wrap = c.inc() }
|
||||
(c.value, cond && wrap)
|
||||
}
|
||||
}
|
||||
|
||||
//Really want a way to initialize memory but this will do for now
|
||||
/*class RegisterQueue[T <: Data](gen: T, val entries: Int, initVals: Seq[T],
|
||||
pipe: Boolean = false,
|
||||
flow: Boolean = false,
|
||||
_reset: Option[Bool] = None)
|
||||
extends Module(_reset=_reset)*/
|
||||
|
||||
class RegisterQueue[T <: Data](gen: T, val entries: Int, initVals: Seq[T], pipe: Boolean = false, flow: Boolean = false) extends Module{
|
||||
//override val ram = Vec.tabulate(entries)((i) => (if(i<initVals.length) Reg(init=initVals(i)) else Reg(gen)))
|
||||
|
||||
|
||||
/** The I/O for this queue */
|
||||
val io = IO(new QueueIO(gen, entries))
|
||||
|
||||
|
||||
//val ending = Vec.fill(entries-initVals.length)(gen)
|
||||
val ending = Vec(entries-initVals.length, gen)
|
||||
val fullInitVals = Vec(initVals ++ ending)
|
||||
|
||||
val ram = Reg(init=fullInitVals)
|
||||
//val ram = Mem(entries, gen)
|
||||
//var registers = new ArraySeq(entries)
|
||||
//for(i <- 0 until entries){
|
||||
//if(i < initVals.length){
|
||||
//registers(i) = Reg(init=initVals(i))
|
||||
//}
|
||||
//else{
|
||||
//registers(i) = Reg(gen)
|
||||
//}
|
||||
//}
|
||||
|
||||
//override val ram = Vec(registers)
|
||||
|
||||
//if(initVals.length < entries)
|
||||
//{
|
||||
//enq_ptr.value := UInt(initVals.length)
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
//maybe_full := Bool(true)
|
||||
//}
|
||||
|
||||
val enq_ptr = InitCounter(entries, if(initVals.length < entries) initVals.length else 0)
|
||||
val deq_ptr = Counter(entries)
|
||||
val maybe_full = Reg(init=(if(initVals.length < entries) Bool(false) else Bool(true)))
|
||||
|
||||
val ptr_match = enq_ptr.value === deq_ptr.value
|
||||
val empty = ptr_match && !maybe_full
|
||||
val full = ptr_match && maybe_full
|
||||
val maybe_flow = Bool(flow) && empty
|
||||
val do_flow = maybe_flow && io.deq.ready
|
||||
|
||||
val do_enq = io.enq.ready && io.enq.valid && !do_flow
|
||||
val do_deq = io.deq.ready && io.deq.valid && !do_flow
|
||||
when (do_enq) {
|
||||
ram(enq_ptr.value) := io.enq.bits
|
||||
enq_ptr.inc()
|
||||
}
|
||||
when (do_deq) {
|
||||
deq_ptr.inc()
|
||||
}
|
||||
when (do_enq =/= do_deq) {
|
||||
maybe_full := do_enq
|
||||
}
|
||||
|
||||
io.deq.valid := !empty || Bool(flow) && io.enq.valid
|
||||
io.enq.ready := !full || Bool(pipe) && io.deq.ready
|
||||
io.deq.bits := Mux(maybe_flow, io.enq.bits, ram(deq_ptr.value))
|
||||
|
||||
val ptr_diff = enq_ptr.value - deq_ptr.value
|
||||
if (isPow2(entries)) {
|
||||
io.count := Cat(maybe_full && ptr_match, ptr_diff)
|
||||
} else {
|
||||
io.count := Mux(ptr_match,
|
||||
Mux(maybe_full,
|
||||
UInt(entries), UInt(0)),
|
||||
Mux(deq_ptr.value > enq_ptr.value,
|
||||
UInt(entries) + ptr_diff, ptr_diff))
|
||||
}
|
||||
}
|
||||
|
||||
class ValidDemuxIO(fanout:Int) extends Bundle{
|
||||
val validIn = Bool(INPUT)
|
||||
val validSelect = UInt(INPUT, log2Up(fanout))
|
||||
val validOut = Vec(fanout, Bool(OUTPUT))
|
||||
//val validOut = Vec.fill(fanout)(Bool(OUTPUT))
|
||||
|
||||
override def cloneType: this.type = new ValidDemuxIO(fanout).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class ValidDemux(fanout:Int) extends Module{
|
||||
val io = IO(new ValidDemuxIO(fanout))
|
||||
|
||||
for(i <- 0 until fanout){
|
||||
io.validOut(i) := io.validIn && (UInt(i) === io.validSelect)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class RegisterQueueTestWrapper(val entries:Int, initVals: Seq[Int]) extends Module{
|
||||
val io = IO(new QueueIO(UInt(width=32), entries))
|
||||
|
||||
val uintInit = initVals.map(UInt(_))
|
||||
val c = Module(new RegisterQueue(UInt(width=32), entries, uintInit))
|
||||
io.enq.valid <> c.io.enq.valid
|
||||
io.enq.ready <> c.io.enq.ready
|
||||
io.enq.bits <> c.io.enq.bits
|
||||
|
||||
io.deq.valid <> c.io.deq.valid
|
||||
io.deq.ready <> c.io.deq.ready
|
||||
io.deq.bits <> c.io.deq.bits
|
||||
|
||||
io.count <> c.io.count
|
||||
|
||||
}
|
||||
|
||||
class RegisterQueueTests(c: RegisterQueueTestWrapper) extends Tester(c){
|
||||
poke(c.io.deq.ready, 1)
|
||||
|
||||
expect(c.io.deq.valid, 1)
|
||||
expect(c.io.deq.bits, 2)
|
||||
expect(c.io.count, 4)
|
||||
|
||||
step(1)
|
||||
expect(c.io.deq.valid, 1)
|
||||
expect(c.io.deq.bits, 4)
|
||||
expect(c.io.count, 3)
|
||||
|
||||
step(1)
|
||||
expect(c.io.deq.valid, 1)
|
||||
expect(c.io.deq.bits, 6)
|
||||
expect(c.io.count, 2)
|
||||
|
||||
step(1)
|
||||
expect(c.io.deq.valid, 1)
|
||||
expect(c.io.deq.bits, 8)
|
||||
expect(c.io.count, 1)
|
||||
|
||||
step(1)
|
||||
expect(c.io.deq.valid, 0)
|
||||
expect(c.io.count, 0)
|
||||
|
||||
}
|
||||
|
||||
class PriorityArbiterTests(c: PriorityArbiter[UInt]) extends Tester(c) {
|
||||
//Test when no inputs are valid and output is not ready
|
||||
poke(c.io.in(0).valid, 0)
|
||||
poke(c.io.in(1).valid, 0)
|
||||
poke(c.io.in(2).valid, 0)
|
||||
poke(c.io.in(3).valid, 0)
|
||||
|
||||
poke(c.io.in(0).bits, 10)
|
||||
poke(c.io.in(1).bits, 11)
|
||||
poke(c.io.in(2).bits, 12)
|
||||
poke(c.io.in(3).bits, 13)
|
||||
|
||||
poke(c.io.priority(0), 20)
|
||||
poke(c.io.priority(1), 21)
|
||||
poke(c.io.priority(2), 22)
|
||||
poke(c.io.priority(3), 23)
|
||||
|
||||
poke(c.io.out.ready, 0)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 10)
|
||||
|
||||
expect(c.io.priorityOut, 20)
|
||||
|
||||
expect(c.io.out.valid, 0)
|
||||
|
||||
step(1)
|
||||
//Test when no inputs are valid and output is ready
|
||||
poke(c.io.out.ready, 1)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 1)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 10)
|
||||
|
||||
expect(c.io.priorityOut, 20)
|
||||
|
||||
expect(c.io.out.valid, 0)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when one input is valid and output is not ready
|
||||
poke(c.io.in(0).valid, 0)
|
||||
poke(c.io.in(1).valid, 0)
|
||||
poke(c.io.in(2).valid, 1)
|
||||
poke(c.io.in(3).valid, 0)
|
||||
|
||||
poke(c.io.in(0).bits, 10)
|
||||
poke(c.io.in(1).bits, 11)
|
||||
poke(c.io.in(2).bits, 12)
|
||||
poke(c.io.in(3).bits, 13)
|
||||
|
||||
poke(c.io.priority(0), 20)
|
||||
poke(c.io.priority(1), 21)
|
||||
poke(c.io.priority(2), 22)
|
||||
poke(c.io.priority(3), 23)
|
||||
|
||||
poke(c.io.out.ready, 0)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 12)
|
||||
|
||||
expect(c.io.priorityOut, 22)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when one input is valid and output is ready
|
||||
poke(c.io.out.ready, 1)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 1)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 12)
|
||||
|
||||
expect(c.io.priorityOut, 22)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when multiple inputs are valid and one has the lowest priority value, and output is not ready
|
||||
poke(c.io.in(0).valid, 1)
|
||||
poke(c.io.in(1).valid, 1)
|
||||
poke(c.io.in(2).valid, 1)
|
||||
poke(c.io.in(3).valid, 1)
|
||||
|
||||
poke(c.io.in(0).bits, 10)
|
||||
poke(c.io.in(1).bits, 11)
|
||||
poke(c.io.in(2).bits, 12)
|
||||
poke(c.io.in(3).bits, 13)
|
||||
|
||||
poke(c.io.priority(0), 20)
|
||||
poke(c.io.priority(1), 10)
|
||||
poke(c.io.priority(2), 20)
|
||||
poke(c.io.priority(3), 20)
|
||||
|
||||
poke(c.io.out.ready, 0)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 11)
|
||||
|
||||
expect(c.io.priorityOut, 10)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when multiple inputs are valid and one has the lowest priority value, and output is ready
|
||||
poke(c.io.out.ready, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 1)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 11)
|
||||
|
||||
expect(c.io.priorityOut, 10)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when multiple inputs are valid and multiple have the lowest priority value, and output is not ready
|
||||
poke(c.io.in(0).valid, 1)
|
||||
poke(c.io.in(1).valid, 1)
|
||||
poke(c.io.in(2).valid, 1)
|
||||
poke(c.io.in(3).valid, 1)
|
||||
|
||||
poke(c.io.in(0).bits, 10)
|
||||
poke(c.io.in(1).bits, 11)
|
||||
poke(c.io.in(2).bits, 12)
|
||||
poke(c.io.in(3).bits, 13)
|
||||
|
||||
poke(c.io.priority(0), 30)
|
||||
poke(c.io.priority(1), 30)
|
||||
poke(c.io.priority(2), 20)
|
||||
poke(c.io.priority(3), 20)
|
||||
|
||||
poke(c.io.out.ready, 0)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 12)
|
||||
|
||||
expect(c.io.priorityOut, 20)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//Test when multiple inputs are valid and multiple has the lowest priority value, and output is ready
|
||||
poke(c.io.out.ready, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 1)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 12)
|
||||
|
||||
expect(c.io.priorityOut, 20)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//0 is valid lowest priority value, and output is not ready
|
||||
poke(c.io.in(0).valid, 1)
|
||||
poke(c.io.in(1).valid, 1)
|
||||
poke(c.io.in(2).valid, 1)
|
||||
poke(c.io.in(3).valid, 1)
|
||||
|
||||
poke(c.io.in(0).bits, 10)
|
||||
poke(c.io.in(1).bits, 11)
|
||||
poke(c.io.in(2).bits, 12)
|
||||
poke(c.io.in(3).bits, 13)
|
||||
|
||||
poke(c.io.priority(0), 10)
|
||||
poke(c.io.priority(1), 20)
|
||||
poke(c.io.priority(2), 30)
|
||||
poke(c.io.priority(3), 40)
|
||||
|
||||
poke(c.io.out.ready, 0)
|
||||
|
||||
step(1)
|
||||
expect(c.io.in(0).ready, 0)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 10)
|
||||
|
||||
expect(c.io.priorityOut, 10)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
//0 is valid lowest priority value, and output is ready
|
||||
//Test when multiple inputs are valid and multiple has the lowest priority value, and output is ready
|
||||
poke(c.io.out.ready, 1)
|
||||
|
||||
step(1)
|
||||
|
||||
expect(c.io.in(0).ready, 1)
|
||||
expect(c.io.in(1).ready, 0)
|
||||
expect(c.io.in(2).ready, 0)
|
||||
expect(c.io.in(3).ready, 0)
|
||||
|
||||
expect(c.io.out.bits, 10)
|
||||
|
||||
expect(c.io.priorityOut, 10)
|
||||
|
||||
expect(c.io.out.valid, 1)
|
||||
|
||||
}
|
||||
|
||||
object PriorityArbiterMain {
|
||||
def main(args: Array[String]): Unit = {
|
||||
chiselMainTest(args, () => Module(new PriorityArbiter(UInt(width = 32), 4, 32))){c => new PriorityArbiterTests(c)}
|
||||
chiselMainTest(args, () => Module(new RegisterQueueTestWrapper(4, List(2, 4, 6, 8)))){
|
||||
c => new RegisterQueueTests(c)
|
||||
}
|
||||
}
|
||||
}*/
|
|
@ -0,0 +1,430 @@
|
|||
package memControl
|
||||
|
||||
import Chisel._
|
||||
import Chisel.ImplicitConversions._
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.config._
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.rocket._
|
||||
import freechips.rocketchip.tilelink._
|
||||
//import freechips.rocketchip.util.InOrderArbiter
|
||||
import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.system._
|
||||
|
||||
import vivadoHLS._
|
||||
import controlUtils._
|
||||
|
||||
class RequestParserIO(dataWidth: Int, addrWidth: Int) extends Bundle{
|
||||
val reqIn = new ApBusReq(dataWidth, addrWidth).flip
|
||||
val reqOut = new ApBusReq(dataWidth, addrWidth)
|
||||
val offsetAddr = UInt(INPUT, width=addrWidth)
|
||||
val loadOffset = Bool(INPUT)
|
||||
override def cloneType: this.type = new RequestParserIO(dataWidth, addrWidth).asInstanceOf[this.type]
|
||||
}
|
||||
class RequestParser(dataWidth: Int, addrWidth: Int) extends Module{
|
||||
val io = IO(new RequestParserIO(dataWidth, addrWidth))
|
||||
|
||||
val offsetReg = Reg(init = UInt(0, width=addrWidth))
|
||||
|
||||
val multAmt = UInt(dataWidth/8)
|
||||
|
||||
when(io.loadOffset){
|
||||
offsetReg := io.offsetAddr
|
||||
}
|
||||
|
||||
io.reqOut.din := io.reqIn.din
|
||||
io.reqOut.dataout := io.reqIn.dataout
|
||||
io.reqOut.size := io.reqIn.size
|
||||
|
||||
io.reqOut.address := (io.reqIn.address*multAmt) + offsetReg
|
||||
}
|
||||
|
||||
class TimestampedRequestIO(dataWidth:Int, addrWidth:Int, counterSize: Int) extends Bundle{
|
||||
val req = new ApBusReq(dataWidth, addrWidth)
|
||||
val timestamp = UInt(INPUT, log2Up(counterSize))
|
||||
override def cloneType: this.type = new TimestampedRequestIO(dataWidth, addrWidth, counterSize).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class ApBusReqType (dataWidth:Int, addrWidth:Int) extends Bundle{
|
||||
//Req specific lines
|
||||
//Specifies a write request
|
||||
val din = Bool() //req_din in verilog
|
||||
|
||||
//Lines used for req
|
||||
val address = UInt(width = addrWidth)
|
||||
val dataout = UInt(width = dataWidth)
|
||||
val size = UInt(width = addrWidth)
|
||||
|
||||
override def cloneType: this.type = new ApBusReqType(dataWidth, addrWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class TimestampedRequestIOType(dataWidth:Int, addrWidth:Int, counterSize: Int) extends Bundle{
|
||||
val req = new ApBusReqType(dataWidth, addrWidth)
|
||||
val timestamp = UInt(width = log2Up(counterSize))
|
||||
|
||||
override def cloneType: this.type = new TimestampedRequestIOType(dataWidth, addrWidth, counterSize).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
|
||||
class RequestIngestIO(dataWidth: Seq[Int], addrWidth: Seq[Int], counterSize: Int, inputBufferLen: Int) extends Bundle{
|
||||
//val reqsIn = Vec.tabulate(dataWidth.length)((i) => Wire(new ApBusReq(dataWidth(i), addrWidth(i)).flip))
|
||||
val reqsIn = HeterogeneousBag(dataWidth.zip(addrWidth).map {
|
||||
case (dw, aw) => new ApBusReq(dw, aw)
|
||||
}).flip
|
||||
|
||||
val reqsFullN = Vec(dataWidth.length, Output(Bool()))
|
||||
//val reqsWrite = Vec(dataWidth.length, Bool(INPUT)).flip
|
||||
val reqsWrite = Vec(dataWidth.length, Input(Bool()))
|
||||
//val offsetAddrs = Vec.tabulate(dataWidth.length)((i) => UInt(INPUT, width=addrWidth(i)))
|
||||
//val offsetAddrs = HeterogeneousBag(addrWidth.map(aw => UInt(INPUT, width = aw)))
|
||||
val offsetAddrs = HeterogeneousBag(addrWidth.map(aw => Input(UInt(aw.W))))
|
||||
//val loadOffsets = Vec(dataWidth.length, Bool(INPUT)).flip
|
||||
val loadOffsets = Vec(dataWidth.length, Input(Bool()))
|
||||
|
||||
val newRequests = UInt(OUTPUT, width = log2Up(dataWidth.length)+1) //The number of new requests recieved in this cycle (used to track number of outstanding requests)
|
||||
|
||||
//The widths are the maximums of all of the input widths
|
||||
val reqOut = Decoupled(new ApBusReq(if (dataWidth.length > 0) dataWidth.max else 0, if (addrWidth.length > 0) addrWidth.max else 0))
|
||||
val selectedBus = UInt(OUTPUT, log2Up(dataWidth.length))
|
||||
override def cloneType: this.type = new RequestIngestIO(dataWidth, addrWidth, counterSize, inputBufferLen).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class RequestIngest(dataWidth: Seq[Int], addrWidth: Seq[Int], inputBufferLen: Int) extends Module{
|
||||
val busCount = dataWidth.length
|
||||
//We only need enough counter values to account for the worst case scenario when memory is stalled and each buffer is filled one at a time
|
||||
//Adding an extra 1 is probably overly conservative as the extra one may be reassigned but is not at the front of the queue. If it was, then a queue is not
|
||||
//Taking the base 2 log and rasing 2 to that power ensures that the overflow semantics are what is expected. This is important for the priority difference
|
||||
val counterSize = BigInt(2).pow(log2Up(inputBufferLen*busCount)).toInt
|
||||
|
||||
val io = IO(new RequestIngestIO(dataWidth, addrWidth, counterSize, inputBufferLen))
|
||||
|
||||
val counter = Counter(counterSize)
|
||||
|
||||
val prevTimestamp = Reg(init = UInt(0, width=log2Up(counterSize)))
|
||||
|
||||
val parsers = Seq.tabulate(busCount)((i) => Module(new RequestParser(dataWidth(i), addrWidth(i))).io)
|
||||
|
||||
//val queues = Vec.tabulate(busCount)((i) => Module(new Queue(new TimestampedRequestIOType(dataWidth(i), addrWidth(i), log2Up(counterSize)), inputBufferLen)))
|
||||
val queues = Seq.tabulate(busCount)((i) => Module(new Queue(new TimestampedRequestIOType(dataWidth(i), addrWidth(i), log2Up(counterSize)), inputBufferLen)).io)
|
||||
/*val queues = HeterogeneousBag(dataWidth.zip(addrWidth).map{
|
||||
case (dw, aw) => Module(new Queue(new TimestampedRequestIOType(dw, aw, log2Up(counterSize)), inputBufferLen))
|
||||
})*/
|
||||
//Vec.tabulate(busCount)((i) => Module(new Queue(new TimestampedRequestIO(dataWidth(i), addrWidth(i), log2Up(counterSize)), inputBufferLen)).io)
|
||||
|
||||
val arbiter = Module(new PriorityArbiter(new ApBusReq(if (dataWidth.length > 0) dataWidth.max else 0, if (addrWidth.length > 0) addrWidth.max else 0), busCount, log2Up(counterSize)))
|
||||
|
||||
val incrCounter = Wire(Bool(false))
|
||||
if (dataWidth.length > 0) {
|
||||
incrCounter := io.reqsWrite.reduce(_||_)
|
||||
}
|
||||
//+1 is because we need to be able to represent 0 and BusCount not BusCount-1
|
||||
val requestsUInt = io.reqsWrite.map((x) => Mux(x, UInt(1, width=busCount), UInt(0, width=busCount+1)))
|
||||
|
||||
if (dataWidth.length > 0) {
|
||||
io.newRequests := requestsUInt.reduce(_+_)
|
||||
}
|
||||
|
||||
//Increment counter when any request comes in
|
||||
when(incrCounter){
|
||||
counter.inc()
|
||||
}
|
||||
|
||||
for(i <- 0 until busCount){
|
||||
parsers(i).offsetAddr := io.offsetAddrs(i)
|
||||
parsers(i).loadOffset := io.loadOffsets(i)
|
||||
parsers(i).reqIn := io.reqsIn(i)
|
||||
//parsers(i).reqIn <> io.reqsIn(i)
|
||||
queues(i).enq.bits.req := parsers(i).reqOut
|
||||
queues(i).enq.bits.timestamp := counter.value //put in the timestamp!
|
||||
io.reqsFullN(i) := queues(i).enq.ready
|
||||
queues(i).enq.valid := io.reqsWrite(i)
|
||||
queues(i).deq.ready := arbiter.io.in(i).ready
|
||||
arbiter.io.in(i).valid := queues(i).deq.valid
|
||||
arbiter.io.in(i).bits := queues(i).deq.bits.req
|
||||
//Priority is oldest first. It is defined as the difference from the last serviced timestamp
|
||||
arbiter.io.priority(i) := queues(i).deq.bits.timestamp - prevTimestamp //This works even with overlfow so long as counterSize is a power of 2
|
||||
}
|
||||
|
||||
when(arbiter.io.out.valid && io.reqOut.ready){
|
||||
prevTimestamp := prevTimestamp + arbiter.io.priorityOut
|
||||
}
|
||||
|
||||
//io.reqOut := arbiter.io.out
|
||||
io.reqOut.valid := arbiter.io.out.valid
|
||||
arbiter.io.out.ready := io.reqOut.ready
|
||||
io.reqOut.bits := arbiter.io.out.bits
|
||||
io.selectedBus := arbiter.io.chosen
|
||||
}
|
||||
|
||||
class RequestIssuerIO(dataWidth: Int, addrWidth:Int, maxReqWidth:Int, numBus:Int, roccAddrWidth:Int, roccDataWidth:Int, roccTagWidth:Int, roccCmdWidth:Int, roccTypWidth:Int) extends Bundle{
|
||||
//Incoming request from arbiter
|
||||
//val reqIn = Decoupled(new ApBusReq(dataWidth, addrWidth)).flip
|
||||
val reqIn = Flipped(Decoupled(new ApBusReq(dataWidth, addrWidth)))
|
||||
//The bus that the request came from
|
||||
val reqBus = UInt(INPUT, width=log2Up(numBus))
|
||||
|
||||
//Lines for table address check and for updating the table
|
||||
val accessWidth = UInt(OUTPUT, width=maxReqWidth)
|
||||
val accessRead = Bool(INPUT)
|
||||
val conflict = Bool(INPUT)
|
||||
//val tagQueueIO = Decoupled(UInt(OUTPUT, width=roccTagWidth)).flip
|
||||
val tagQueueIO = Flipped(Decoupled(Output(UInt(roccTagWidth.W))))
|
||||
val busNum = UInt(OUTPUT, width=log2Up(numBus))
|
||||
|
||||
//RoCC Lines
|
||||
val roCCReqAddr = UInt(OUTPUT, width = roccAddrWidth) // coreMaxAddrBits)
|
||||
val roCCReqTag = UInt(OUTPUT, width = roccTagWidth) //coreDCacheReqTagBits)
|
||||
val roCCReqCmd = UInt(OUTPUT, width = roccCmdWidth) //M_SZ)
|
||||
val roCCReqTyp = UInt(OUTPUT, width = roccTypWidth) //
|
||||
val roCCReqData = UInt(OUTPUT, width = roccDataWidth) //coreDataBits)
|
||||
val roCCReqValid = Bool(OUTPUT)
|
||||
val roCCReqRdy = Bool(INPUT)
|
||||
|
||||
val reqWidth = UInt(OUTPUT, width = maxReqWidth) //Pass to table to specify width of request
|
||||
|
||||
val reqSent = Bool(OUTPUT)
|
||||
|
||||
override def cloneType: this.type = new RequestIssuerIO(dataWidth, addrWidth, maxReqWidth, numBus, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
//maxReqBytes = 8 (64 bit) in our case
|
||||
class RequestIssuer(dataWidth: Int, addrWidth:Int, maxReqBytes:Int, roccAddrWidth:Int, roccDataWidth:Int, roccTagWidth:Int, roccCmdWidth:Int, roccTypWidth:Int, busDataWidths:Seq[Int]) extends Module{
|
||||
val maxReqWidth = log2Up(maxReqBytes)
|
||||
val numBus = busDataWidths.length
|
||||
val io = IO(new RequestIssuerIO(dataWidth, addrWidth, maxReqWidth, numBus, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth))
|
||||
|
||||
if (busDataWidths.length > 0) {
|
||||
val busByteWidth = Vec(busDataWidths.map((x) => UInt(x)/UInt(8)))
|
||||
|
||||
val currentByteWidth = busByteWidth(io.reqBus)
|
||||
|
||||
|
||||
io.busNum := io.reqBus
|
||||
io.roCCReqAddr := io.reqIn.bits.address
|
||||
io.roCCReqData := io.reqIn.bits.dataout
|
||||
io.roCCReqTag := io.tagQueueIO.bits
|
||||
io.roCCReqCmd := Mux(io.reqIn.bits.din, M_XWR, M_XRD) //Set the transaction type (Write / Read)
|
||||
io.roCCReqTyp := Mux(currentByteWidth===UInt(8), log2Ceil(8).U, Mux(currentByteWidth===UInt(4), log2Ceil(4).U, Mux(currentByteWidth===UInt(2), log2Ceil(2).U, log2Ceil(1).U))) //Set transaction width
|
||||
io.reqWidth := currentByteWidth
|
||||
|
||||
io.accessWidth := currentByteWidth
|
||||
|
||||
io.roCCReqValid := io.reqIn.valid && !io.conflict && io.tagQueueIO.valid
|
||||
|
||||
val memTransactSent = io.reqIn.valid && io.roCCReqRdy && !io.conflict && io.tagQueueIO.valid
|
||||
|
||||
io.reqIn.ready := memTransactSent
|
||||
io.tagQueueIO.ready := memTransactSent //We used a tag when we issued a request
|
||||
io.reqSent := memTransactSent
|
||||
}
|
||||
}
|
||||
|
||||
class RoutingTableIO(tagWidth:Int, numBus:Int, addrWidth: Int, maxReqWidth: Int) extends Bundle{
|
||||
val reqValid = Bool(INPUT)
|
||||
val reqTag = UInt(INPUT, width = tagWidth)
|
||||
val reqWrite = Bool(INPUT) //If the transaction is a write
|
||||
val reqAddr = UInt(INPUT, width = addrWidth)
|
||||
val reqBus = UInt(INPUT, log2Up(numBus))
|
||||
val reqWidth = UInt(INPUT, width = maxReqWidth)
|
||||
|
||||
val checkAddr = UInt(INPUT, width = addrWidth)
|
||||
val checkWidth = UInt(INPUT, width = maxReqWidth)
|
||||
val checkRead = Bool(INPUT)
|
||||
val conflict = Bool(OUTPUT)
|
||||
|
||||
val respTag = UInt(INPUT, width = tagWidth)
|
||||
val respVaid = Bool(INPUT)
|
||||
val respBus = UInt(OUTPUT, width = log2Up(numBus))
|
||||
override def cloneType: this.type = new RoutingTableIO(tagWidth, numBus, addrWidth, maxReqWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
//maxReqBytes = 8 (64 bit) in our case
|
||||
class RoutingTable(tagWidth:Int, numTags:Int, numBus:Int, addrWidth: Int, maxReqBytes: Int) extends Module{
|
||||
val maxReqWidth = log2Up(maxReqBytes)
|
||||
//val numTags = BigInt(2).pow(tagWidth).toInt
|
||||
|
||||
val io = IO(new RoutingTableIO(tagWidth, numBus, addrWidth, maxReqWidth))
|
||||
|
||||
if (addrWidth > 0) {
|
||||
|
||||
val v = Reg(Vec.fill(numTags)(Bool(false))) //valid (outstanding memory request)
|
||||
val write = Reg(Vec.fill(numTags)(Bool(false))) //outstanding request is a write
|
||||
val addr = Reg(Vec.fill(numTags)(UInt(0, width=addrWidth)))
|
||||
val width = Reg(Vec.fill(numTags)(UInt(0, width=maxReqWidth)))
|
||||
val bus = Reg(Vec.fill(numTags)(UInt(0, width=log2Up(numBus))))
|
||||
|
||||
//write Req into table
|
||||
when(io.reqValid){
|
||||
v(io.reqTag) := Bool(true) //Set table entry as valid
|
||||
write(io.reqTag) := io.reqWrite //Set write flag
|
||||
addr(io.reqTag) := io.reqAddr //Set addr
|
||||
width(io.reqTag) := io.reqWidth //Set transaction width (in bytes)
|
||||
bus(io.reqTag) := io.reqBus //Set the requesting bus
|
||||
}
|
||||
|
||||
//clear returned transaction. The tag queue prevents accedently claring a tag that has not yet returned
|
||||
when(io.respVaid){
|
||||
v(io.respTag) := Bool(false) //This entry is no longer valid
|
||||
}
|
||||
|
||||
//Return the bus that requsted the returning transaction
|
||||
io.respBus := bus(io.respTag)
|
||||
|
||||
//Reads can execute if there is no outstanding write to the address (v==false || write==false)
|
||||
// Reads stall when v==true and write==true
|
||||
|
||||
//Writes can only occure if there are no outstanding ops (v==false)
|
||||
// Writes stall when v==true
|
||||
|
||||
val addrsConflicting = Vec.tabulate(numTags)((i) => !((io.checkAddr + io.checkWidth <= addr(i)) || (addr(i) + width(i) <= io.checkAddr)))
|
||||
|
||||
//There is a conflict if: the address is conflicting, the entry is valid, and if(reading, there is an outstanding write)
|
||||
val conflicting = Vec.tabulate(numTags)((i) => addrsConflicting(i) && v(i) && Mux(io.checkRead, write(i), Bool(true)))
|
||||
|
||||
io.conflict := conflicting.reduce(_||_)
|
||||
}
|
||||
}
|
||||
|
||||
class MemControllerIO(dataWidth:Seq[Int], addrWidth:Seq[Int], roccAddrWidth:Int, roccDataWidth:Int, roccTagWidth:Int, roccCmdWidth:Int, roccTypWidth:Int) extends Bundle{
|
||||
//----ap_bus requests----
|
||||
val reqsIn = HeterogeneousBag(dataWidth.zip(addrWidth).map {
|
||||
case (dw, aw) => new ApBusReq(dw, aw)
|
||||
}).flip
|
||||
//Decoupled signals for requests
|
||||
val reqsFullN = Vec(dataWidth.length, Bool(OUTPUT))
|
||||
//val reqsWrite = Vec(dataWidth.length, Bool(INPUT)).flip
|
||||
val reqsWrite = Vec(dataWidth.length, Input(Bool()))
|
||||
//Offset address lines
|
||||
//val offsetAddrs = Vec.tabulate(dataWidth.length)((i) => UInt(INPUT, width=addrWidth(i)))
|
||||
val offsetAddrs = HeterogeneousBag(addrWidth.map(aw => UInt(INPUT, width = aw)))
|
||||
//val loadOffsets = Vec(dataWidth.length, Bool(INPUT)).flip
|
||||
val loadOffsets = Vec(dataWidth.length,Input(Bool()))
|
||||
|
||||
//----status line----
|
||||
val memBusy = Bool(OUTPUT)
|
||||
|
||||
//----ap_bus response----
|
||||
//val rspOut = Vec.tabulate(dataWidth.length)((i) => new ApBusRsp(dataWidth(i)).flip)
|
||||
val rspOut = HeterogeneousBag(dataWidth.map(dw => new ApBusRsp(dw))).flip
|
||||
val rsp_empty_n = Vec(dataWidth.length, Output(Bool())) //This is the same as valid
|
||||
val rsp_read = Vec(dataWidth.length, Input(Bool())) //This is the same as ready
|
||||
|
||||
//----RoCC Mem Req----
|
||||
val roCCReqAddr = UInt(OUTPUT, width = roccAddrWidth) // coreMaxAddrBits)
|
||||
val roCCReqTag = UInt(OUTPUT, width = roccTagWidth) //coreDCacheReqTagBits)
|
||||
val roCCReqCmd = UInt(OUTPUT, width = roccCmdWidth) //M_SZ)
|
||||
val roCCReqTyp = UInt(OUTPUT, width = roccTypWidth) //MT_SZ)
|
||||
val roCCReqData = UInt(OUTPUT, width = roccDataWidth) //
|
||||
val roCCReqValid = Bool(OUTPUT)
|
||||
val roCCReqRdy = Bool(INPUT)
|
||||
|
||||
//val roCCRespAddr = UInt(INPUT, width = roccAddrWidth) // coreMaxAddrBits)
|
||||
val roCCRspTag = UInt(INPUT, width = roccTagWidth) //coreDCacheReqTagBits)
|
||||
val roCCRspCmd = UInt(INPUT, width = roccCmdWidth) //M_SZ)
|
||||
val roCCRspData = UInt(INPUT, width = roccDataWidth) //MT_SZ)
|
||||
//val roCCRespTyp = UInt(INPUT, width = roccTypWidth) //MT_SZ)
|
||||
val roCCRspValid = Bool(INPUT)
|
||||
override def cloneType: this.type = new MemControllerIO(dataWidth, addrWidth, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class MemController(dataWidth:Seq[Int], addrWidth:Seq[Int], reqBufferLen:Int, rspBufferLen:Int, maxReqBytes:Int, roccAddrWidth:Int, roccDataWidth:Int, roccTagWidth:Int, roccCmdWidth:Int, roccTypWidth:Int, numTags:Int, tagOffset:Int ) extends Module{
|
||||
val io = IO(new MemControllerIO(dataWidth, addrWidth, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth))
|
||||
|
||||
val numBus = dataWidth.length
|
||||
if (numBus > 0){
|
||||
val reqIngest = Module(new RequestIngest(dataWidth, addrWidth, reqBufferLen))
|
||||
val reqIssuer = Module(new RequestIssuer(if (dataWidth.length > 0) dataWidth.max else 0, if (addrWidth.length > 0) addrWidth.max else 0, maxReqBytes, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth, dataWidth))
|
||||
val scoreboard = Module(new RoutingTable(roccTagWidth, numTags, numBus, if (addrWidth.length > 0) addrWidth.max else 0, maxReqBytes))
|
||||
|
||||
//Tags may have an offset if this is not the only accelerator in the system
|
||||
val tags = (tagOffset until (tagOffset+numTags))
|
||||
val tagUInts = tags.map((x) => UInt(x, width=roccTagWidth).asUInt)
|
||||
val tagQueue = Module(new RegisterQueue(gen=UInt(width=roccTagWidth), entries=numTags, initVals=tagUInts, flow=true))
|
||||
val rspQueues = Seq.tabulate(numBus)((i) => Module(new Queue(UInt(width=dataWidth(i)), rspBufferLen)).io)
|
||||
val validDemux = Module(new ValidDemux(numBus))
|
||||
|
||||
val currentRequestNum = Reg(init=UInt(0, width=(reqBufferLen*numBus+1+numTags)))
|
||||
|
||||
//Logic for number of outstanding requests
|
||||
currentRequestNum := currentRequestNum + reqIngest.io.newRequests - Mux(io.roCCRspValid, UInt(1), UInt(0))
|
||||
io.memBusy := currentRequestNum =/= UInt(0)
|
||||
|
||||
//==== Ingest Logic ====
|
||||
//Hook up ap_bus request lines to ingest logic
|
||||
//reqIngest.io.reqsIn <> io.reqsIn
|
||||
reqIngest.io.reqsIn := io.reqsIn
|
||||
io.reqsFullN := reqIngest.io.reqsFullN
|
||||
reqIngest.io.reqsWrite := io.reqsWrite
|
||||
//reqIngest.io.offsetAddrs := io.offsetAddrs
|
||||
io.offsetAddrs := reqIngest.io.offsetAddrs
|
||||
reqIngest.io.loadOffsets := io.loadOffsets
|
||||
|
||||
//val newRequests
|
||||
|
||||
//val reqOut
|
||||
//val selectedBus
|
||||
|
||||
//====Req Issuer ====
|
||||
//Incoming request from arbiter
|
||||
//reqIssuer.io.reqIn <> reqIngest.io.reqOut
|
||||
reqIssuer.io.reqIn.bits := reqIngest.io.reqOut.bits
|
||||
reqIssuer.io.reqIn.valid := reqIngest.io.reqOut.valid
|
||||
reqIngest.io.reqOut.ready := reqIssuer.io.reqIn.ready
|
||||
//The bus that the request came from
|
||||
reqIssuer.io.reqBus := reqIngest.io.selectedBus
|
||||
|
||||
//Lines for table address check and for updating the table
|
||||
//val accessWidth
|
||||
//val containsAddr
|
||||
//reqIssuer.io.tagQueue <> tagQueue.io.deq
|
||||
reqIssuer.io.tagQueueIO.bits := tagQueue.io.deq.bits
|
||||
reqIssuer.io.tagQueueIO.valid := tagQueue.io.deq.valid
|
||||
tagQueue.io.deq.ready := reqIssuer.io.tagQueueIO.ready
|
||||
//val busNum
|
||||
//val reqWidth //Pass to table to specify width of request
|
||||
|
||||
//RoCC Lines
|
||||
io.roCCReqAddr := reqIssuer.io.roCCReqAddr
|
||||
io.roCCReqTag := reqIssuer.io.roCCReqTag
|
||||
io.roCCReqCmd := reqIssuer.io.roCCReqCmd
|
||||
io.roCCReqTyp := reqIssuer.io.roCCReqTyp
|
||||
io.roCCReqValid := reqIssuer.io.roCCReqValid
|
||||
reqIssuer.io.roCCReqRdy := io.roCCReqRdy
|
||||
io.roCCReqData := reqIssuer.io.roCCReqData
|
||||
|
||||
//====Scoreboard====
|
||||
scoreboard.io.reqValid := reqIssuer.io.reqSent //Do not commit into the table unles there is a fire
|
||||
scoreboard.io.reqTag := reqIssuer.io.roCCReqTag
|
||||
scoreboard.io.reqWrite := (reqIssuer.io.roCCReqCmd === M_XWR) //If the transaction is a write
|
||||
scoreboard.io.reqAddr := reqIssuer.io.roCCReqAddr
|
||||
scoreboard.io.reqBus := reqIssuer.io.busNum
|
||||
scoreboard.io.reqWidth := reqIssuer.io.reqWidth
|
||||
|
||||
scoreboard.io.checkAddr := reqIssuer.io.roCCReqAddr
|
||||
scoreboard.io.checkWidth := reqIssuer.io.accessWidth
|
||||
scoreboard.io.checkRead := reqIssuer.io.accessRead
|
||||
reqIssuer.io.conflict := scoreboard.io.conflict
|
||||
|
||||
scoreboard.io.respTag := io.roCCRspTag
|
||||
scoreboard.io.respVaid := io.roCCRspValid
|
||||
//val scoreboard.io.respBus
|
||||
|
||||
//====TagQueue====
|
||||
tagQueue.io.enq.bits := io.roCCRspTag
|
||||
tagQueue.io.enq.valid := io.roCCRspValid
|
||||
|
||||
//====Demux=====
|
||||
validDemux.io.validIn := io.roCCRspValid && io.roCCRspCmd===M_XRD //Only return to the bus if this is a response to a read request
|
||||
validDemux.io.validSelect := scoreboard.io.respBus
|
||||
//val validOut = Vec.fill(fanout)(Bool(OUTPUT))
|
||||
|
||||
//====RespQueues====
|
||||
for(i <- 0 until numBus){
|
||||
rspQueues(i).enq.bits := io.roCCRspData((dataWidth(i)-1),0) //Pass the data to all output queues (slicing to approprite width), only give one the valid signal
|
||||
rspQueues(i).enq.valid := validDemux.io.validOut(i)
|
||||
io.rspOut(i).datain := rspQueues(i).deq.bits
|
||||
io.rsp_empty_n(i) := rspQueues(i).deq.valid
|
||||
rspQueues(i).deq.ready := io.rsp_read(i)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1-manager.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
sub generate_accel{
|
||||
|
||||
my @accel_tuples= @{$_[0]};
|
||||
|
||||
foreach my $accel_tuple_ref (@accel_tuples) {
|
||||
print($accel_tuple_ref);
|
||||
my @accel_tuple = @{$accel_tuple_ref};
|
||||
|
||||
my $pgm = $accel_tuple[0];
|
||||
my $func = $accel_tuple[1];
|
||||
my $bm_path = $accel_tuple[2];
|
||||
my $bm_path_c = $bm_path.'/src/main/c/';
|
||||
|
||||
my $is_rocc = $accel_tuple[3];
|
||||
my $idx_addr = $accel_tuple[4];
|
||||
|
||||
my $prefix=" ";
|
||||
|
||||
my $num_args = scalar @accel_tuple;
|
||||
if ($num_args > 5) {
|
||||
$prefix = $accel_tuple[5];
|
||||
}
|
||||
|
||||
print("Pgm: ".$pgm."\n");
|
||||
print("Func: ".$func."\n");
|
||||
print("Path: ".$bm_path."\n");
|
||||
print("Is RoCC not TL?: ".$is_rocc."\n");
|
||||
print("RoCC Idx or TL Addr: ".$idx_addr."\n");
|
||||
print("Prefix: ".$prefix."\n");
|
||||
$ENV{'PGM'} = $pgm;
|
||||
$ENV{'FUNC'} = $func;
|
||||
my $PGM = $pgm;
|
||||
my $FUNC = $func;
|
||||
my $RDIR = $rdir;
|
||||
|
||||
system("mkdir -p $bm_path/src/main/c");
|
||||
chdir("$bm_path/src/main/c/") or die $!;
|
||||
system("cp $RDIR/tools/centrifuge/examples/${PGM}/* $bm_path_c");
|
||||
system("cp $RDIR/tools/centrifuge/scripts/run_hls.pl $bm_path_c");
|
||||
#system("cp $RDIR/hls/sw/time.h $bm_path/src/main/c/");
|
||||
#system("cp $RDIR/hls/sw/rocc.h $bm_path/src/main/c/");
|
||||
|
||||
# Specialize the Makefile for this function
|
||||
system("sed -i 's/^FUNC=.*/FUNC=$func/g' $bm_path_c/Makefile");
|
||||
|
||||
my $dir = getcwd;
|
||||
print("$dir\n");
|
||||
#next;
|
||||
|
||||
system("perl run_hls.pl ${PGM} ${FUNC} $prefix");
|
||||
|
||||
if ($is_rocc) {
|
||||
system("cp $RDIR/tools/centrifuge/scripts/run_chisel.pl $bm_path_c");
|
||||
system("cp $RDIR/tools/centrifuge/scripts/generate_wrapper.pl $bm_path_c");
|
||||
system("perl run_chisel.pl ${PGM} ${FUNC} $prefix");
|
||||
system("perl generate_wrapper.pl ${PGM} ${FUNC} $idx_addr $prefix");
|
||||
#system("make clean");
|
||||
#system("make CUSTOM_INST=1");
|
||||
} else {
|
||||
system("cp $RDIR/tools/centrifuge/scripts/run_chisel_tl.pl $bm_path_c");
|
||||
system("cp $RDIR/tools/centrifuge/scripts/generate_wrapper_tl.pl $bm_path_c");
|
||||
system("perl run_chisel_tl.pl ${PGM} ${FUNC} $idx_addr $prefix");
|
||||
system("perl generate_wrapper_tl.pl ${PGM} ${FUNC} $idx_addr $prefix");
|
||||
#system("make clean");
|
||||
#system("make CUSTOM_DRIVER=1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Example with RoCC and TL accel
|
||||
#my @input = (["vadd", "vadd", "$rdir/sim/target-rtl/firechip/hls_vadd_vadd/src/main/c", 1, "0", "rocc0_"], ["vadd_tl", "vadd", "$rdir/sim/target-rtl/firechip/hls_vadd_tl_vadd/src/main/c", 0, "0x20000", "tl0_"]);
|
||||
#generate_accel(\@input);
|
||||
1;
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
sub generate_build_sbt {
|
||||
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1-manager.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
# back up the build sbt fie
|
||||
copy("$rdir/build.sbt","$rdir/build.sbt.bk") or die "Copy failed: $!";
|
||||
|
||||
open SBT, ">$rdir/build.sbt";
|
||||
|
||||
# hash of all hls bm and its path
|
||||
my $soc_name = $_[0];
|
||||
my %bm_path = %{$_[1]};
|
||||
|
||||
my $build_sbt_template = "$rdir/tools/centrifuge/scripts/build_sbt_template";
|
||||
open my $fh, '<', $build_sbt_template or die "error opening $build_sbt_template $!";
|
||||
my $build_sbt = do { local $/; <$fh> };
|
||||
|
||||
# print list of hls accels
|
||||
my $dep_template='lazy val BM = (project in file("PATH"))
|
||||
.dependsOn(rocketchip, testchipip, midasTargetUtils, icenet)
|
||||
.settings(commonSettings)
|
||||
';
|
||||
|
||||
keys %bm_path;
|
||||
|
||||
while(my($bm, $path) = each %bm_path) {
|
||||
my $dep = $dep_template;
|
||||
$dep =~ s/BM/$bm/;
|
||||
$dep =~ s/PATH/$path/;
|
||||
$build_sbt = $build_sbt."\n".$dep;
|
||||
}
|
||||
|
||||
my @bm = (keys %bm_path);
|
||||
my $bm_size = @bm;
|
||||
my $bms = '';
|
||||
if ($bm_size > 0) {
|
||||
$bms = ", ".join(", ", @bm);
|
||||
}
|
||||
|
||||
my $soc_template = '
|
||||
lazy val SOC_NAME = conditionalDependsOn(project in file("generators/SOC_NAME"))
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilitiesBMS)
|
||||
.settings(commonSettings)
|
||||
';
|
||||
my $soc = $soc_template;
|
||||
$soc =~ s/SOC_NAME/$soc_name/g;
|
||||
$soc =~ s/BMS/$bms/;
|
||||
$build_sbt = $build_sbt.$soc;
|
||||
my $firechip_template = '
|
||||
lazy val example = conditionalDependsOn(project in file("generators/example"))
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities, sha3, SOC_NAME)
|
||||
.settings(commonSettings)
|
||||
|
||||
lazy val firechip = (project in file("generators/firechip"))
|
||||
.dependsOn(example, icenet, testchipip, tracegen, midasTargetUtils, midas, firesimLib % "test->test;compile->compile")
|
||||
.settings(
|
||||
commonSettings,
|
||||
testGrouping in Test := isolateAllTests( (definedTests in Test).value )
|
||||
)
|
||||
';
|
||||
my $firechip_dep = $firechip_template;
|
||||
$firechip_dep =~ s/SOC_NAME/$soc_name/g;
|
||||
$build_sbt = $build_sbt.$firechip_dep;
|
||||
|
||||
print SBT $build_sbt;
|
||||
close SBT;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,119 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
sub generate_config {
|
||||
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1-manager.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
my @rocc_func_names = @{$_[0]};
|
||||
my @tll2_func_names = @{$_[1]};
|
||||
|
||||
my $postfix = "";
|
||||
my $num_args = scalar @_;
|
||||
if ($num_args > 2) {
|
||||
$postfix= $_[2];
|
||||
}
|
||||
|
||||
my $rocc = "";
|
||||
#if (@rocc_func_names > 0) {
|
||||
$rocc .= "
|
||||
class WithHLSRoCCExample extends Config((site, here, up) => {
|
||||
case BuildRoCC => Seq(
|
||||
";
|
||||
#}
|
||||
for( my $i = 0; $i < @rocc_func_names; $i = $i + 1 ){
|
||||
if ($i ne 0) { $rocc.=",
|
||||
";}
|
||||
$rocc .="
|
||||
(p: Parameters) => {
|
||||
val hls_$rocc_func_names[$i] = LazyModule(new HLS$rocc_func_names[$i]Control(OpcodeSet.custom$i)(p))
|
||||
hls_$rocc_func_names[$i]
|
||||
}";
|
||||
}
|
||||
|
||||
if (scalar @rocc_func_names ne 0) { $rocc.=",
|
||||
";}
|
||||
$rocc .= "
|
||||
(p: Parameters) => {
|
||||
val translator = LazyModule(new TranslatorExample(OpcodeSet.custom3)(p))
|
||||
translator
|
||||
})";
|
||||
|
||||
#if (@rocc_func_names > 0) {
|
||||
$rocc .= "\n})\n";
|
||||
#}
|
||||
|
||||
open CONFIG, ">$rdir/generators/example/src/main/scala/HLSConfig.scala" or die "$!\n";
|
||||
my $config="package example
|
||||
import chisel3._
|
||||
import freechips.rocketchip.diplomacy.{LazyModule, ValName}
|
||||
import freechips.rocketchip.config.{Parameters, Config}
|
||||
import testchipip.{WithBlockDevice, BlockDeviceKey, BlockDeviceConfig}
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.system.DefaultConfig
|
||||
import freechips.rocketchip.rocket._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.devices.tilelink._
|
||||
import freechips.rocketchip._
|
||||
import testchipip._
|
||||
import sifive.blocks.devices.uart.{PeripheryUARTKey,UARTParams}
|
||||
|
||||
import sifive.blocks.devices.uart._
|
||||
import java.io.File
|
||||
import ConfigValName._
|
||||
";
|
||||
foreach my $func_name (@rocc_func_names) {
|
||||
$config .= "import hls_$func_name.HLS$func_name"."Control\n";
|
||||
}
|
||||
foreach my $func_name (@tll2_func_names) {
|
||||
$config .= "import hls_$func_name._\n";
|
||||
}
|
||||
|
||||
$config .= $rocc;
|
||||
$config .="
|
||||
class HLSRocketConfig extends Config(
|
||||
new WithHLSTop ++
|
||||
new WithBootROM ++
|
||||
new freechips.rocketchip.subsystem.WithInclusiveCache ++
|
||||
new WithHLSRoCCExample ++
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
new freechips.rocketchip.system.BaseConfig)
|
||||
";
|
||||
|
||||
$config .="
|
||||
|
||||
class WithHLSTop extends Config((site, here, up) => {
|
||||
case BuildTop => (clock: Clock, reset: Bool, p: Parameters) =>
|
||||
Module(LazyModule(new TopWithHLS()(p)).module)
|
||||
})
|
||||
|
||||
class TopWithHLS(implicit p: Parameters) extends Top ";
|
||||
|
||||
foreach my $func_name (@tll2_func_names) {
|
||||
$config .= "\n with HasPeripheryHLS$func_name"."AXI";
|
||||
}
|
||||
|
||||
$config .=' {
|
||||
override lazy val module = new TopWithHLSModule(this)
|
||||
}
|
||||
|
||||
class TopWithHLSModule(l: TopWithHLS) extends TopModule(l)
|
||||
';
|
||||
foreach my $func_name (@tll2_func_names) {
|
||||
$config .= " with HasPeripheryHLS$func_name"."AXIImp\n";
|
||||
}
|
||||
|
||||
print CONFIG $config;
|
||||
|
||||
close CONFIG;
|
||||
}
|
||||
1;
|
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
my $dir = getcwd;
|
||||
my $json_fn = $ARGV[0];
|
||||
my $soc_name = $json_fn;
|
||||
$soc_name =~ s/.json//;
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
|
||||
my $postfix="";
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 1) {
|
||||
$postfix= $ARGV[1];
|
||||
}
|
||||
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1-manager.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (not defined($json_fn)){
|
||||
print("Please specify a json config file\!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
my $scripts_dir = $rdir.'/tools/centrifuge/scripts/';
|
||||
require $scripts_dir.'parse_json.pl';
|
||||
require $scripts_dir.'generate_accel.pl';
|
||||
require $scripts_dir.'generate_build_sbt.pl';
|
||||
require $scripts_dir.'generate_config.pl';
|
||||
require $scripts_dir.'generate_f1_scripts.pl';
|
||||
require $scripts_dir.'generate_xsim_scripts.pl';
|
||||
|
||||
# Parse Json file
|
||||
my ($RoCC_ref, $TLL2_ref) = parse_json($json_fn);
|
||||
my @RoCC_accels = @$RoCC_ref;
|
||||
my @TLL2_accels = @$TLL2_ref;
|
||||
|
||||
my %hls_bm = ();
|
||||
my @Accel_tuples = ();
|
||||
my @RoCC_names = ();
|
||||
my @TLL2_names = ();
|
||||
my $idx = 0;
|
||||
foreach my $RoCC_accel (@RoCC_accels){
|
||||
my @arr = @{$RoCC_accel};
|
||||
my $pgm = $arr[0];
|
||||
my $func = $arr[1];
|
||||
|
||||
my $bm_path = "";
|
||||
if (scalar @arr > 2) {
|
||||
$bm_path = $arr[2];
|
||||
} else {
|
||||
$bm_path = $rdir."/generators/$soc_name/hls_$pgm"."_$func"; ;
|
||||
}
|
||||
my $prefix = "rocc".$idx."_";
|
||||
# 3rd arg is_rocc is set to 1
|
||||
push(@Accel_tuples, [$pgm, $func, $bm_path, 1, $idx, $prefix]);
|
||||
$func=$prefix.$func;
|
||||
$hls_bm{"hls_$func"} = $bm_path;
|
||||
push(@RoCC_names, $func);
|
||||
$idx += 1;
|
||||
}
|
||||
|
||||
$idx = 0;
|
||||
foreach my $TLL2_accel (@TLL2_accels){
|
||||
my @arr = @{$TLL2_accel};
|
||||
my $pgm = $arr[0];
|
||||
my $func = $arr[1];
|
||||
my $addr = $arr[2];
|
||||
|
||||
my $bm_path = "";
|
||||
if (scalar @arr > 3) {
|
||||
$bm_path = $arr[3];
|
||||
} else {
|
||||
$bm_path = $rdir."/generators/$soc_name/hls_$pgm"."_$func"; ;
|
||||
}
|
||||
|
||||
my $prefix = "tl".$idx."_";
|
||||
push(@Accel_tuples, [$pgm, $func, $bm_path, 0, $addr, $prefix]);
|
||||
$func=$prefix.$func;
|
||||
$hls_bm{"hls_$func"} = $bm_path;
|
||||
push(@TLL2_names, $func);
|
||||
$idx += 1;
|
||||
}
|
||||
|
||||
# Generate the verilog and chisel code
|
||||
generate_accel(\@Accel_tuples);
|
||||
# Generate build.sbt under firesim/sim
|
||||
generate_build_sbt($soc_name, \%hls_bm);
|
||||
# Generate HLSConfig file for RoCC Accelerators
|
||||
generate_config(\@RoCC_names, \@TLL2_names, $postfix);
|
||||
|
||||
# F1
|
||||
#generate_f1_scripts(\%hls_bm);
|
||||
#generate_xsim_scripts(\%hls_bm);
|
||||
#compile_xsim_libs($postfix, "clean", 0);
|
||||
#compile_replace_rtl($postfix, "clean", 0);
|
||||
#print_xsim_cmd($postfix, 0);
|
||||
|
||||
# Ax machines
|
||||
#compile_vcs("clean");
|
||||
#copy_verilog(\%hls_bm, "$rdir/sim/generated-src/f1/FireSimHLS-HLSFireSimRocketChipConfig-FireSimConfig/FPGATop.v");
|
||||
|
||||
sub print_xsim_cmd{
|
||||
my $postfix= $_[0];
|
||||
my $with_nic = $_[1];
|
||||
my $nic = "NoNIC";
|
||||
if ($with_nic) {
|
||||
$nic = "";
|
||||
}
|
||||
print("\n");
|
||||
print("Source Full Env:\n source sourceme-f1-full.sh\n");
|
||||
print("XSim Compile:\n".'cd $RDIR/sim '."&& make DESIGN=FireSimHLS$nic TARGET_CONFIG=HLSFireSimRocketChipConfig$postfix PLATFORM_CONFIG=FireSimConfig xsim\n");
|
||||
#cl_FireSimHLSNoNIC-HLSFireSimRocketChipConfig-FireSimConfig/verif
|
||||
print("Remove Sim Folder:\n".'rm -rf cl_'."FireSimHLS$nic-HLSFireSimRocketChipConfig$postfix-FireSimConfig/verif/sim\n");
|
||||
print("XSim Run Driver:\n".'cd $RDIR/sim '."&& make DESIGN=FireSimHLS$nic TARGET_CONFIG=HLSFireSimRocketChipConfig$postfix PLATFORM_CONFIG=FireSimConfig xsim-dut\n");
|
||||
print("XSim Run Test:\n".'cd $RDIR/sim '."&& make DESIGN=FireSimHLS$nic TARGET_CONFIG=HLSFireSimRocketChipConfig$postfix PLATFORM_CONFIG=FireSimConfig run-xsim SIM_BINARY=".'$RDIR/sim/target-rtl/firechip/hls_${PGM}_${FUNC}/src/main/c/${PGM}.riscv');
|
||||
print("\n");
|
||||
#print("LD_LIBRARY_PATH=output/f1/FireSimHLS$nic-HLSFireSimRocketChipConfig$postfix-FireSimConfig/ output/f1/FireSimHLS$nic-HLSFireSimRocketChipConfig$postfix-FireSimConfig/FireSimHLS$nic-f1 ".'+mm_readLatency=10 +mm_writeLatency=10 +mm_readMaxReqs=4 +mm_writeMaxReqs=4 +netburst=8 +slotid=0 $RDIR/sim/target-rtl/firechip/hls_${PGM}_${FUNC}/src/main/c/${PGM}.riscv');
|
||||
}
|
||||
|
||||
sub compile_xsim_libs{
|
||||
my $postfix= $_[0];
|
||||
my $clean = $_[1];
|
||||
my $with_nic = $_[2];
|
||||
my $nic = "NoNIC";
|
||||
if ($with_nic) {
|
||||
$nic = "";
|
||||
}
|
||||
chdir("$rdir/sim");
|
||||
system("make DESIGN=FireSimHLS$nic TARGET_CONFIG=HLSFireSimRocketChipConfig$postfix PLATFORM_CONFIG=FireSimConfig $clean xsim");
|
||||
}
|
||||
|
||||
sub compile_replace_rtl{
|
||||
my $postfix= $_[0];
|
||||
my $clean = $_[1];
|
||||
my $with_nic = $_[2];
|
||||
my $nic = "NoNIC";
|
||||
if ($with_nic) {
|
||||
$nic = "";
|
||||
}
|
||||
chdir("$rdir/sim");
|
||||
system("make DESIGN=FireSimHLS$nic TARGET_CONFIG=HLSFireSimRocketChipConfig$postfix PLATFORM_CONFIG=FireSimConfig $clean replace-rtl");
|
||||
}
|
||||
|
||||
sub compile_vcs{
|
||||
my $clean = $_[0];
|
||||
chdir("$rdir/sims/vcs");
|
||||
system("make $clean debug CONFIG=");
|
||||
}
|
||||
|
||||
sub copy_verilog{
|
||||
my %bm_path = %{$_[0]};
|
||||
my $FPGATop_path = $_[1];
|
||||
|
||||
while(my($bm, $path) = each %bm_path) {
|
||||
system("cat $path/src/main/verilog/*.v >> $FPGATop_path");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
use List::Util qw(first);
|
||||
|
||||
# Inputs: file_name, func_name, rocc_index, prefix(Optional)
|
||||
my $dir = getcwd;
|
||||
my $file_name = $ARGV[0];
|
||||
my $func_name = $ARGV[1];
|
||||
my $rocc_index= $ARGV[2];
|
||||
|
||||
my $prefix = undef;
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 3) {
|
||||
$prefix = $ARGV[3];
|
||||
}
|
||||
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
#print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1.sh!\n");
|
||||
exit();
|
||||
}
|
||||
my $bm_path = $rdir."/sim/target-rtl/firechip/hls_$file_name"."_$func_name";
|
||||
my $wrapper_func_name = $func_name."_wrapper";
|
||||
my $wrapper_header= "bm_wrapper.h";
|
||||
if ($prefix) {
|
||||
$func_name = $prefix.$func_name;
|
||||
}
|
||||
|
||||
#############################PARSE Verilog##############################
|
||||
my $verilog_file = "$dir/../verilog/$func_name".".v";
|
||||
my $line = undef;
|
||||
my @verilog_input = ();
|
||||
my @verilog_input_size = ();
|
||||
my @verilog_output = ();
|
||||
my @verilog_output_size = ();
|
||||
|
||||
print "Parsing ".$verilog_file."\n";
|
||||
# parse the verilog file to get the info we need
|
||||
if(!open VERILOG, "$verilog_file"){
|
||||
print $!;
|
||||
} else {
|
||||
while(<VERILOG>){
|
||||
$line = $_;
|
||||
if($line =~ m/^\s*input\s+(.*)/){
|
||||
my $input = $1;
|
||||
#print "input:$input\n";
|
||||
if($input =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $input_name = $3;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
my $size = $end - $start + 1;
|
||||
push(@verilog_input_size, $size);
|
||||
}elsif ($input =~ m/\s*(.*)\s*;/){
|
||||
my $input_name = $1;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
push(@verilog_input_size, 1);
|
||||
}
|
||||
|
||||
}elsif($line =~ m/^\s*output\s+(.*)/){
|
||||
my $output = $1;
|
||||
#print "output:$output\n";
|
||||
if($output =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $output_name = $3;
|
||||
#print "here!"."$output_name\n";
|
||||
push(@verilog_output, $output_name);
|
||||
my $size = $end - $start + 1;
|
||||
push(@verilog_output_size, $size);
|
||||
}elsif ($output =~ m/\s*(.*)\s*;/){
|
||||
my $output_name = $1;
|
||||
#print "here!"."$output_name\n";
|
||||
push (@verilog_output, $output_name);
|
||||
push(@verilog_output_size, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
print("Inputs:");
|
||||
my $in_str = join ' ', @verilog_input;
|
||||
print $in_str."\n";
|
||||
print("Outputs:");
|
||||
my $out_str = join ' ', @verilog_output;
|
||||
print $out_str."\n";
|
||||
}
|
||||
|
||||
#creat scala folder
|
||||
my $scala_dir = "$dir/../scala";
|
||||
mkdir $scala_dir unless (-d $scala_dir);
|
||||
|
||||
##############################################################################################################################
|
||||
print "Generating BlackBox file ...\n";
|
||||
# should be under scala folder
|
||||
|
||||
my $blackbox1 = "
|
||||
package hls_test_c
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.{Parameters, Field}
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.util._
|
||||
import vivadoHLS._
|
||||
|
||||
class test_c() extends BlackBox() {
|
||||
";
|
||||
$blackbox1 =~ s/test_c/$func_name/g;
|
||||
|
||||
|
||||
my $i = undef;
|
||||
my $bb_body = "";
|
||||
|
||||
# now if the input name does not start with ap, we assume it is an arg
|
||||
my $ap_return = 0;
|
||||
my $ap_clk = 0;
|
||||
my $ap_rst = 0;
|
||||
my @verilog_input_scalar = ();
|
||||
my %verilog_input_pointer = ();
|
||||
my @verilog_input_pointer_arg = (); # An ordered list of args
|
||||
|
||||
my $arg_count = 0;
|
||||
my @sindices = ();
|
||||
my @pindices = ();
|
||||
|
||||
for( $i = 0; $i < @verilog_input; $i = $i + 1 ){
|
||||
my $input_name = $verilog_input[$i];
|
||||
my $input_size = $verilog_input_size[$i];
|
||||
|
||||
if ($input_name =~ m/ap_clk(.*)/){
|
||||
$ap_clk = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($input_name =~ m/ap_rst(.*)/){
|
||||
$ap_rst = 1;
|
||||
}
|
||||
|
||||
# If the input is a ap_bus port, the signals should match the following format
|
||||
# There should be 3 different input signals
|
||||
elsif($input_name =~ m/(\S+)_req_full_n/ or $input_name =~ m/(\S+)_rsp_empty_n/ or $input_name =~ m/(\S+)_datain/){
|
||||
my $arg_name = $1;
|
||||
if ($input_name =~ m/(\S+)_datain/) {
|
||||
push(@pindices, $arg_count);
|
||||
$arg_count = $arg_count + 1;
|
||||
push(@verilog_input_pointer_arg, $arg_name);
|
||||
}
|
||||
if (defined $verilog_input_pointer{$arg_name}) {
|
||||
$verilog_input_pointer{$arg_name} += 1;
|
||||
} else {
|
||||
$verilog_input_pointer{$arg_name} = 1;
|
||||
}
|
||||
}
|
||||
elsif(!($input_name =~ m/ap_(,*)/)){
|
||||
push (@verilog_input_scalar, $input_name);
|
||||
push(@sindices, $arg_count);
|
||||
$arg_count = $arg_count + 1;
|
||||
}
|
||||
else{
|
||||
print("Not func args: $input_name\n");
|
||||
}
|
||||
}
|
||||
|
||||
#foreach my $arg (keys %verilog_input_pointer) {
|
||||
foreach my $arg (@verilog_input_pointer_arg) {
|
||||
print("pointer_arg: $arg\n");
|
||||
}
|
||||
my $hash_count = keys %verilog_input_pointer;
|
||||
print("hash_count: $hash_count\n");
|
||||
if(@verilog_input_scalar + $hash_count> 2){
|
||||
print "verilog_input_scalar: ";
|
||||
my $in_str = join ' ', @verilog_input_scalar;
|
||||
print $in_str."\n";
|
||||
die "Only accept function with no more than 2 arguments!\n";
|
||||
}
|
||||
|
||||
foreach my $arg (keys %verilog_input_pointer) {
|
||||
if ($verilog_input_pointer{$arg} ne 3) {
|
||||
die "The AP bus interfance did not generate expected number of inputs!\n";
|
||||
}
|
||||
}
|
||||
|
||||
for( $i = 0; $i < @verilog_output; $i = $i + 1 ){
|
||||
|
||||
my $output_name = $verilog_output[$i];
|
||||
my $output_size = $verilog_output_size[$i];
|
||||
|
||||
if ($output_name =~ m/ap_return(.*)/){
|
||||
$ap_return = 1;
|
||||
}
|
||||
|
||||
$bb_body = $bb_body."\tio.".$output_name.".setName(\"".$output_name."\")\n";
|
||||
}
|
||||
|
||||
if ($ap_clk eq 1){
|
||||
$bb_body = $bb_body."addClock(Driver\.implicitClock)\n".'renameClock("clk", "ap_clk")'."\n";
|
||||
}
|
||||
|
||||
if ($ap_rst eq 1){
|
||||
$bb_body = $bb_body.'renameReset("ap_rst")'."\n";
|
||||
}
|
||||
|
||||
my $bb_def = "class HLS$func_name"."Blackbox() extends Module {\n";
|
||||
|
||||
# Scalar IO Parameter
|
||||
my @sdata_widths = ();
|
||||
#my @sindices = ();
|
||||
#my $sidx = 0;
|
||||
foreach my $arg (@verilog_input_scalar) {
|
||||
my $sdata_idx = first { $verilog_input[$_] eq $arg} 0..$#verilog_input;
|
||||
my $sdata_width = $verilog_input_size[$sdata_idx];
|
||||
push(@sdata_widths, $sdata_width);
|
||||
#push(@sindices, $sidx);
|
||||
#$sidx += 1;
|
||||
}
|
||||
my $sindices_str = join ',',@sindices;
|
||||
my $sdata_widths_str = join ',',@sdata_widths;
|
||||
print "scalar data_widths: $sdata_widths_str\n";
|
||||
|
||||
$bb_def .= "\tval scalar_io_dataWidths = List($sdata_widths_str)\n";
|
||||
$bb_def .= "\tval scalar_io_argLoc = List($sindices_str) //Lists the argument number of the scalar_io\n";
|
||||
|
||||
# Pointer IO Parameter
|
||||
my @addr_widths = ();
|
||||
my @data_widths = ();
|
||||
#my @indices = ();
|
||||
my $idx = 0;
|
||||
foreach my $arg (sort keys %verilog_input_pointer) {
|
||||
my $addr_signal = $arg."_address";
|
||||
my $data_signal = $arg."_dataout";
|
||||
my $addr_idx = first { $verilog_output[$_] eq $addr_signal } 0..$#verilog_output;
|
||||
my $data_idx = first { $verilog_output[$_] eq $data_signal } 0..$#verilog_output;
|
||||
#my $addr_width = $verilog_output_size[$addr_idx];
|
||||
my $addr_width = "64";
|
||||
|
||||
my $data_width = $verilog_output_size[$data_idx];
|
||||
push(@addr_widths, $addr_width);
|
||||
push(@data_widths, $data_width);
|
||||
#push(@indices, $idx);
|
||||
$idx += 1;
|
||||
}
|
||||
#my $indices_str = join ',',@indices;
|
||||
my $pindices_str = join ',',@pindices;
|
||||
my $addr_widths_str = join ',',@addr_widths;
|
||||
print "addr_widths: $addr_widths_str\n";
|
||||
my $data_widths_str = join ',',@data_widths;
|
||||
print "data_widths: $data_widths_str\n";
|
||||
|
||||
|
||||
foreach my $arg (@verilog_input_pointer_arg) {
|
||||
print("pointer_arg: $arg\n");
|
||||
}
|
||||
|
||||
my $wrapper ='
|
||||
#ifdef CUSTOM_INST
|
||||
#include "rocc.h"
|
||||
#endif
|
||||
';
|
||||
|
||||
my $return_type = "void ";
|
||||
if($ap_return){
|
||||
$return_type = "uint64_t ";
|
||||
}
|
||||
|
||||
my $total_args = @verilog_input_scalar + $hash_count;
|
||||
$wrapper .= "$return_type $wrapper_func_name(";
|
||||
|
||||
my @args = ();
|
||||
foreach my $arg (@verilog_input_scalar) {
|
||||
push(@args, $arg);
|
||||
}
|
||||
foreach my $arg (@verilog_input_pointer_arg) {
|
||||
push(@args, $arg);
|
||||
}
|
||||
|
||||
my $arg_str = join ', ', @args;
|
||||
my $i = 0;
|
||||
foreach my $arg (@args) {
|
||||
if ($i != 0){
|
||||
$wrapper .=", "
|
||||
}
|
||||
$wrapper .="uint64_t $arg";
|
||||
|
||||
$i=1;
|
||||
}
|
||||
$wrapper .= ") {
|
||||
";
|
||||
|
||||
if($ap_return){
|
||||
$wrapper .= " uint64_t ret_val;\n";
|
||||
}
|
||||
$wrapper .="
|
||||
#ifdef CUSTOM_INST
|
||||
#define XCUSTOM_ACC ";
|
||||
$wrapper .= $rocc_index."\n";
|
||||
|
||||
if ($ap_return){
|
||||
if ($total_args == 0) {
|
||||
$wrapper.=" ROCC_INSTRUCTION_D(XCUSTOM_ACC, ret_val, 0);\n";
|
||||
} elsif ($total_args == 1) {
|
||||
$wrapper.=" ROCC_INSTRUCTION_DS(XCUSTOM_ACC, ret_val, $arg_str, 0);\n";
|
||||
} else {
|
||||
$wrapper.=" ROCC_INSTRUCTION_DSS(XCUSTOM_ACC, ret_val, $arg_str, 0);\n";
|
||||
}
|
||||
} else{
|
||||
if ($total_args == 0) {
|
||||
$wrapper.=" ROCC_INSTRUCTION(XCUSTOM_ACC, 0);\n";
|
||||
} elsif ($total_args == 1) {
|
||||
$wrapper.=" ROCC_INSTRUCTION_S(XCUSTOM_ACC, $arg_str, 0);\n";
|
||||
} else {
|
||||
$wrapper.=" ROCC_INSTRUCTION_SS(XCUSTOM_ACC, $arg_str, 0);\n";
|
||||
}
|
||||
}
|
||||
$wrapper .= " ROCC_BARRIER();\n";
|
||||
$wrapper.=" #endif\n";
|
||||
if($ap_return){
|
||||
$wrapper .= " return ret_val;\n";
|
||||
}
|
||||
$wrapper.="}";
|
||||
|
||||
open FILE, "> $wrapper_header";
|
||||
print FILE $wrapper;
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
use List::Util qw(first);
|
||||
use Tie::IxHash;
|
||||
|
||||
# Inputs: file_name, func_name, func_base_addr, prefix(Optional)
|
||||
my $dir = getcwd;
|
||||
my $file_name = $ARGV[0];
|
||||
my $func_name = $ARGV[1];
|
||||
my $func_base_addr = $ARGV[2];
|
||||
|
||||
my $prefix = undef;
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 3) {
|
||||
$prefix = $ARGV[3];
|
||||
}
|
||||
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
#print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
my $bm_path = $rdir."/sim/target-rtl/firechip/hls_$file_name"."_$func_name";
|
||||
my $wrapper_func_name = $func_name."_wrapper";
|
||||
my $wrapper_header= "bm_wrapper.h";
|
||||
|
||||
if ($prefix) {
|
||||
$func_name = $prefix.$func_name;
|
||||
}
|
||||
|
||||
my $bm_inc_path = $rdir."/hls/sw/bm/";
|
||||
#############################PARSE Verilog##############################
|
||||
|
||||
my %var_dict;
|
||||
tie %var_dict, "Tie::IxHash";
|
||||
my $verilog_file = "$dir/../verilog/$func_name"."_control_s_axi.v";
|
||||
print "Parsing ".$verilog_file."\n";
|
||||
# parse the verilog file to get the info we need
|
||||
if(!open VERILOG, "$verilog_file"){
|
||||
print $!;
|
||||
} else {
|
||||
my $start = 0;
|
||||
my $line = undef;
|
||||
while(<VERILOG>){
|
||||
$line = $_;
|
||||
|
||||
if($line =~ m/------------------------Parameter----------------------/){
|
||||
$start = 0;
|
||||
}
|
||||
if($start){
|
||||
|
||||
if($line =~ m/(0x\S+) : Data signal of (\S+)/){
|
||||
my $base_addr = $1;
|
||||
my $var = $2;
|
||||
#print("$base_addr : $var\n");
|
||||
if (exists $var_dict{$var}) {
|
||||
push (@{$var_dict{$var}}, $base_addr);
|
||||
} else {
|
||||
my @addr = ();
|
||||
push (@addr, $base_addr);
|
||||
$var_dict{$var} = \@addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if($line =~ m/------------------------Address Info------------------/){
|
||||
$start= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#############################GENERATE Software Bare-metal Wrappers##############################
|
||||
# We want ordered hash so we didn't add this piece of code into a func
|
||||
#sub generate_bm_wrapper {
|
||||
# my %var_dict=%{$_[0]};
|
||||
# tie %var_dict, "Tie::IxHash";
|
||||
# my $func_base_addr = $_[1];
|
||||
foreach my $var (keys %var_dict) {
|
||||
print($var.": ");
|
||||
|
||||
my @addr = @{$var_dict{$var}};
|
||||
foreach my $base_addr(@addr) {
|
||||
|
||||
print($base_addr."\t");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
my $wrapper = '#include "'.$bm_inc_path.'/mmio.h"'."\n";
|
||||
#$wrapper .= '#include "'.$bm_inc_path.'/time.h"'."\n";
|
||||
|
||||
$wrapper .= '#define ACCEL_BASE '.$func_base_addr."\n";
|
||||
|
||||
$wrapper .= "#define AP_DONE_MASK 0b10\n";
|
||||
$wrapper .= "#define ACCEL_INT 0x4\n";
|
||||
foreach my $var (keys %var_dict) {
|
||||
|
||||
my @addr = @{$var_dict{$var}};
|
||||
my $idx = 0;
|
||||
|
||||
foreach my $base_addr(@addr) {
|
||||
$wrapper .="#define "."ACCEL_$var"."_$idx"." $base_addr\n";
|
||||
$idx +=1;
|
||||
}
|
||||
}
|
||||
|
||||
my $ap_return = 0;
|
||||
my $ap_return_type = "uint32_t";
|
||||
if (exists $var_dict{"ap_return"}) {
|
||||
my $size=@{$var_dict{"ap_return"}};
|
||||
if ($size == 2){
|
||||
$ap_return_type = "uint64_t";
|
||||
}
|
||||
$ap_return = 1;
|
||||
}
|
||||
|
||||
if ($ap_return){
|
||||
$wrapper .= $ap_return_type." $wrapper_func_name(";
|
||||
} else {
|
||||
$wrapper .="void $wrapper_func_name(";
|
||||
}
|
||||
|
||||
my @arglist=();
|
||||
foreach my $var (keys %var_dict) {
|
||||
if ($var eq "ap_return") {
|
||||
next;
|
||||
}
|
||||
|
||||
my $var_type = "uint32_t";
|
||||
my $size=@{$var_dict{$var}};
|
||||
if ($size == 2){
|
||||
$var_type = "uint64_t";
|
||||
}
|
||||
push(@arglist, "$var_type $var");
|
||||
}
|
||||
|
||||
my $args = join ', ', @arglist;
|
||||
$wrapper.= $args.") {";
|
||||
|
||||
$wrapper.= '
|
||||
// Disable Interrupt
|
||||
reg_write32(ACCEL_BASE + ACCEL_INT, 0x0);
|
||||
';
|
||||
|
||||
foreach my $var (keys %var_dict) {
|
||||
if ($var eq "ap_return") {
|
||||
next;
|
||||
}
|
||||
|
||||
my @addr = @{$var_dict{$var}};
|
||||
my $idx = 0;
|
||||
foreach my $base_addr(@addr) {
|
||||
my $shift = "";
|
||||
if ($idx == 1){
|
||||
$shift = " >> 32";
|
||||
}elsif($idx > 1){
|
||||
die "Index exceeds limit!\n";
|
||||
}
|
||||
$wrapper .=" reg_write32(ACCEL_BASE + ACCEL_$var"."_$idx, (uint32_t) ($var$shift));\n";
|
||||
$idx +=1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$wrapper .='
|
||||
// Write to ap_start to start the execution
|
||||
reg_write32(ACCEL_BASE, 0x1);
|
||||
|
||||
// Done?
|
||||
int done = 0;
|
||||
while (!done){
|
||||
done = reg_read32(ACCEL_BASE) & AP_DONE_MASK;
|
||||
}
|
||||
';
|
||||
|
||||
# If there a return value
|
||||
if ($ap_return){
|
||||
my @addr = @{$var_dict{"ap_return"}};
|
||||
|
||||
$wrapper .= "
|
||||
$ap_return_type ret_val = 0;\n";
|
||||
my $idx = 0;
|
||||
foreach my $base_addr(@addr) {
|
||||
my $shift = "";
|
||||
if ($idx == 1){
|
||||
$shift = " >> 32";
|
||||
}elsif($idx > 1){
|
||||
die "Index exceeds limit!\n";
|
||||
}
|
||||
$wrapper .=" ret_val = (reg_read32(ACCEL_BASE + ACCEL_ap_return"."_$idx)$shift) | ret_val;\n";
|
||||
$idx +=1;
|
||||
}
|
||||
$wrapper .= " return ret_val;\n";
|
||||
}
|
||||
|
||||
$wrapper .="}\n";
|
||||
open FILE, "> $wrapper_header";
|
||||
print FILE $wrapper;
|
||||
#}
|
||||
|
||||
#generate_bm_wrapper(\%var_dict, $func_base_addr);
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use JSON qw( decode_json );
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
# Take in 1 arg which is the json file path
|
||||
# Return two arrays of arrays
|
||||
sub parse_json {
|
||||
my $json_fn = $_[0];
|
||||
open my $fh, '<', $json_fn or die "error opening $json_fn: $!";
|
||||
my $json = do { local $/; <$fh> };
|
||||
|
||||
my $decoded = decode_json($json);
|
||||
|
||||
my @RoCC_accels = ();
|
||||
my $i;
|
||||
print("\nRoCC Accels: \n");
|
||||
for( $i = 0; $i < 4; $i = $i + 1 ){
|
||||
|
||||
if ((exists $decoded -> {'RoCC'}{"custom$i"}{'pgm'}) and (exists $decoded -> {'RoCC'}{"custom$i"}{'func'} )){
|
||||
my $pgm = $decoded -> {'RoCC'}{"custom$i"}{'pgm'};
|
||||
my $func = $decoded -> {'RoCC'}{"custom$i"}{'func'};
|
||||
if(($pgm ne "") and ($func ne "")){
|
||||
print("\tpgm: $pgm\t func: $func\n");
|
||||
my @tup = ();
|
||||
push (@tup, $pgm);
|
||||
push (@tup, $func);
|
||||
push (@RoCC_accels, \@tup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("TLL2 Accels: \n");
|
||||
my @TLL2_accels = ();
|
||||
if (exists $decoded -> {'TLL2'}){
|
||||
my @TLL2_arr = @{$decoded-> {'TLL2'}};
|
||||
foreach my $accel (@TLL2_arr) {
|
||||
if( (exists $accel->{'pgm'}) and (exists $accel->{'func'} and (exists $accel->{'addr'}))){
|
||||
my $pgm = $accel->{'pgm'};
|
||||
my $func = $accel->{'func'};
|
||||
my $addr = $accel->{'addr'};
|
||||
if ($pgm ne "" and $func ne "" and $addr ne ""){
|
||||
|
||||
print("\tpgm: $pgm\t func: $func\t addr: $addr\n");
|
||||
my @tup = ();
|
||||
push (@tup, $pgm);
|
||||
push (@tup, $func);
|
||||
push (@tup, $addr);
|
||||
push (@TLL2_accels, \@tup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (\@RoCC_accels, \@TLL2_accels);
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,724 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
use List::Util qw(first);
|
||||
|
||||
my $dir = getcwd;
|
||||
my $file_name = $ARGV[0];
|
||||
my $func_name = $ARGV[1];
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
|
||||
my $prefix = undef;
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 2) {
|
||||
$prefix = $ARGV[2];
|
||||
}
|
||||
|
||||
if ($prefix) {
|
||||
$func_name = $prefix.$func_name;
|
||||
}
|
||||
|
||||
#print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
my $verilog_file = "$dir/../verilog/$func_name".".v";
|
||||
my $line = undef;
|
||||
my @verilog_input = ();
|
||||
my @verilog_input_size = ();
|
||||
my @verilog_output = ();
|
||||
my @verilog_output_size = ();
|
||||
|
||||
print "Parsing ".$verilog_file."\n";
|
||||
# parse the verilog file to get the info we need
|
||||
if(!open VERILOG, "$verilog_file"){
|
||||
print $!;
|
||||
} else {
|
||||
while(<VERILOG>){
|
||||
$line = $_;
|
||||
if($line =~ m/^\s*input\s+(.*)/){
|
||||
my $input = $1;
|
||||
#print "input:$input\n";
|
||||
if($input =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $input_name = $3;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
my $size = $end - $start + 1;
|
||||
push(@verilog_input_size, $size);
|
||||
}elsif ($input =~ m/\s*(.*)\s*;/){
|
||||
my $input_name = $1;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
push(@verilog_input_size, 1);
|
||||
}
|
||||
|
||||
}elsif($line =~ m/^\s*output\s+(.*)/){
|
||||
my $output = $1;
|
||||
#print "output:$output\n";
|
||||
if($output =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $output_name = $3;
|
||||
#print "here!"."$output_name\n";
|
||||
push(@verilog_output, $output_name);
|
||||
my $size = $end - $start + 1;
|
||||
push(@verilog_output_size, $size);
|
||||
}elsif ($output =~ m/\s*(.*)\s*;/){
|
||||
my $output_name = $1;
|
||||
#print "here!"."$output_name\n";
|
||||
push (@verilog_output, $output_name);
|
||||
push(@verilog_output_size, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
print("Inputs:");
|
||||
my $in_str = join ' ', @verilog_input;
|
||||
print $in_str."\n";
|
||||
print("Outputs:");
|
||||
my $out_str = join ' ', @verilog_output;
|
||||
print $out_str."\n";
|
||||
}
|
||||
|
||||
#creat scala folder
|
||||
my $scala_dir = "$dir/../scala";
|
||||
mkdir $scala_dir unless (-d $scala_dir);
|
||||
|
||||
##############################################################################################################################
|
||||
print "Generating BlackBox file ...\n";
|
||||
# should be under scala folder
|
||||
open BB, ">$scala_dir/$func_name"."_blackbox.scala";
|
||||
|
||||
my $blackbox1 = "
|
||||
package hls_test_c
|
||||
import Chisel._
|
||||
import chisel3.experimental.dontTouch
|
||||
import freechips.rocketchip.config.{Parameters, Field}
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.util._
|
||||
import vivadoHLS._
|
||||
|
||||
class test_c() extends BlackBox() {
|
||||
";
|
||||
$blackbox1 =~ s/test_c/$func_name/g;
|
||||
|
||||
print BB $blackbox1;
|
||||
|
||||
print BB "\tval io = new Bundle {\n";
|
||||
my $i = undef;
|
||||
my $bb_body = "";
|
||||
|
||||
# now if the input name does not start with ap, we assume it is an arg
|
||||
my $ap_return = 0;
|
||||
my $ap_clk = 0;
|
||||
my $ap_rst = 0;
|
||||
my @verilog_input_scalar = ();
|
||||
my %verilog_input_pointer = ();
|
||||
my @verilog_input_pointer_arg = (); # An ordered list of args
|
||||
|
||||
my $arg_count = 0;
|
||||
my @sindices = ();
|
||||
my @pindices = ();
|
||||
|
||||
for( $i = 0; $i < @verilog_input; $i = $i + 1 ){
|
||||
my $input_name = $verilog_input[$i];
|
||||
my $input_size = $verilog_input_size[$i];
|
||||
|
||||
if ($input_name =~ m/ap_clk(.*)/){
|
||||
$ap_clk = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($input_name =~ m/ap_rst(.*)/){
|
||||
$ap_rst = 1;
|
||||
}
|
||||
|
||||
# If the input is a ap_bus port, the signals should match the following format
|
||||
# There should be 3 different input signals
|
||||
elsif($input_name =~ m/(\S+)_req_full_n/ or $input_name =~ m/(\S+)_rsp_empty_n/ or $input_name =~ m/(\S+)_datain/){
|
||||
my $arg_name = $1;
|
||||
if ($input_name =~ m/(\S+)_datain/) {
|
||||
push(@pindices, $arg_count);
|
||||
$arg_count = $arg_count + 1;
|
||||
push(@verilog_input_pointer_arg, $arg_name);
|
||||
}
|
||||
if (defined $verilog_input_pointer{$arg_name}) {
|
||||
$verilog_input_pointer{$arg_name} += 1;
|
||||
} else {
|
||||
$verilog_input_pointer{$arg_name} = 1;
|
||||
}
|
||||
}
|
||||
elsif(!($input_name =~ m/ap_(,*)/)){
|
||||
push (@verilog_input_scalar, $input_name);
|
||||
push(@sindices, $arg_count);
|
||||
$arg_count = $arg_count + 1;
|
||||
}
|
||||
else{
|
||||
print("Not func args: $input_name\n");
|
||||
}
|
||||
|
||||
print BB "\t\tval $input_name = ";
|
||||
if ($input_name =~ m/ap_clk(.*)/){
|
||||
print BB "Clock\(INPUT\)\n";
|
||||
}else{
|
||||
if ($input_size == 1){
|
||||
print BB "Bool\(INPUT\)\n";
|
||||
}else{
|
||||
print BB "Bits\(INPUT, width = $input_size\)\n";
|
||||
}
|
||||
}
|
||||
if($input_name ne "ap_clk" && $input_name ne "ap_rst"){
|
||||
$bb_body = $bb_body."\tio.".$input_name.".setName(\"".$input_name."\")\n";
|
||||
}
|
||||
}
|
||||
|
||||
#foreach my $arg (keys %verilog_input_pointer) {
|
||||
foreach my $arg (@verilog_input_pointer_arg) {
|
||||
print("pointer_arg: $arg\n");
|
||||
}
|
||||
my $hash_count = keys %verilog_input_pointer;
|
||||
print("hash_count: $hash_count\n");
|
||||
if(@verilog_input_scalar + $hash_count> 2){
|
||||
print "verilog_input_scalar: ";
|
||||
my $in_str = join ' ', @verilog_input_scalar;
|
||||
print $in_str."\n";
|
||||
die "Only accept function with no more than 2 arguments!\n";
|
||||
}
|
||||
|
||||
foreach my $arg (keys %verilog_input_pointer) {
|
||||
if ($verilog_input_pointer{$arg} ne 3) {
|
||||
die "The AP bus interfance did not generate expected number of inputs!\n";
|
||||
}
|
||||
}
|
||||
|
||||
for( $i = 0; $i < @verilog_output; $i = $i + 1 ){
|
||||
|
||||
my $output_name = $verilog_output[$i];
|
||||
my $output_size = $verilog_output_size[$i];
|
||||
|
||||
if ($output_name =~ m/ap_return(.*)/){
|
||||
$ap_return = 1;
|
||||
}
|
||||
|
||||
print BB "\t\tval $output_name = ";
|
||||
if ($output_size == 1){
|
||||
print BB "Bool(OUTPUT)\n";
|
||||
}else{
|
||||
print BB "Bits(OUTPUT, width = $output_size)\n";
|
||||
}
|
||||
|
||||
$bb_body = $bb_body."\tio.".$output_name.".setName(\"".$output_name."\")\n";
|
||||
}
|
||||
|
||||
if ($ap_clk eq 1){
|
||||
$bb_body = $bb_body."addClock(Driver\.implicitClock)\n".'renameClock("clk", "ap_clk")'."\n";
|
||||
}
|
||||
|
||||
if ($ap_rst eq 1){
|
||||
$bb_body = $bb_body.'renameReset("ap_rst")'."\n";
|
||||
}
|
||||
|
||||
print BB "\t}\n";
|
||||
#print BB "$bb_body\n";
|
||||
#print BB "moduleName = "."\"$func_name\"\n";
|
||||
print BB "}\n";
|
||||
|
||||
my $bb_def = "class HLS$func_name"."Blackbox() extends Module {\n";
|
||||
|
||||
# Scalar IO Parameter
|
||||
my @sdata_widths = ();
|
||||
#my @sindices = ();
|
||||
#my $sidx = 0;
|
||||
foreach my $arg (@verilog_input_scalar) {
|
||||
my $sdata_idx = first { $verilog_input[$_] eq $arg} 0..$#verilog_input;
|
||||
my $sdata_width = $verilog_input_size[$sdata_idx];
|
||||
push(@sdata_widths, $sdata_width);
|
||||
#push(@sindices, $sidx);
|
||||
#$sidx += 1;
|
||||
}
|
||||
my $sindices_str = join ',',@sindices;
|
||||
my $sdata_widths_str = join ',',@sdata_widths;
|
||||
print "scalar data_widths: $sdata_widths_str\n";
|
||||
|
||||
$bb_def .= "\tval scalar_io_dataWidths = List($sdata_widths_str)\n";
|
||||
$bb_def .= "\tval scalar_io_argLoc = List($sindices_str) //Lists the argument number of the scalar_io\n";
|
||||
|
||||
# Pointer IO Parameter
|
||||
my @addr_widths = ();
|
||||
my @data_widths = ();
|
||||
#my @indices = ();
|
||||
my $idx = 0;
|
||||
foreach my $arg (sort keys %verilog_input_pointer) {
|
||||
my $addr_signal = $arg."_address";
|
||||
my $data_signal = $arg."_dataout";
|
||||
my $addr_idx = first { $verilog_output[$_] eq $addr_signal } 0..$#verilog_output;
|
||||
my $data_idx = first { $verilog_output[$_] eq $data_signal } 0..$#verilog_output;
|
||||
#my $addr_width = $verilog_output_size[$addr_idx];
|
||||
my $addr_width = "64";
|
||||
|
||||
my $data_width = $verilog_output_size[$data_idx];
|
||||
push(@addr_widths, $addr_width);
|
||||
push(@data_widths, $data_width);
|
||||
#push(@indices, $idx);
|
||||
$idx += 1;
|
||||
}
|
||||
#my $indices_str = join ',',@indices;
|
||||
my $pindices_str = join ',',@pindices;
|
||||
my $addr_widths_str = join ',',@addr_widths;
|
||||
print "addr_widths: $addr_widths_str\n";
|
||||
my $data_widths_str = join ',',@data_widths;
|
||||
print "data_widths: $data_widths_str\n";
|
||||
|
||||
$bb_def .= "\tval ap_bus_addrWidths = List(".$addr_widths_str.")\n";
|
||||
$bb_def .= "\tval ap_bus_dataWidths = List(".$data_widths_str.")\n";
|
||||
|
||||
#$bb_def .= "\tval ap_bus_argLoc = List(".$indices_str.")\n";
|
||||
$bb_def .= "\tval ap_bus_argLoc = List(".$pindices_str.")\n";
|
||||
|
||||
my $ret_width = 1;
|
||||
if ($ap_return eq 1){
|
||||
my $ret_idx = first { $verilog_output[$_] eq 'ap_return'} 0..$#verilog_output;
|
||||
$ret_width = $verilog_output_size[$ret_idx];
|
||||
}
|
||||
|
||||
$bb_def .= "\tval io = new Bundle {
|
||||
\tval ap = new ApCtrlIO(dataWidth = $ret_width)
|
||||
\tval ap_bus = HeterogeneousBag(ap_bus_addrWidths.zip(ap_bus_dataWidths).map {
|
||||
case (aw, dw) => new ApBusIO(dw, aw)
|
||||
})
|
||||
";
|
||||
|
||||
if (@verilog_input_scalar > 0){
|
||||
$bb_def .="\tval scalar_io = HeterogeneousBag(scalar_io_dataWidths.map(w => UInt(INPUT, width = w)))";
|
||||
}
|
||||
$bb_def .="
|
||||
}
|
||||
|
||||
\tval bb = Module(new $func_name())
|
||||
|
||||
\tbb.io.ap_start := io.ap.start
|
||||
\tio.ap.done := bb.io.ap_done
|
||||
\tio.ap.idle := bb.io.ap_idle
|
||||
\tio.ap.ready := bb.io.ap_ready
|
||||
";
|
||||
|
||||
if ($ap_return eq 1) {
|
||||
$bb_def .= "\tio.ap.rtn := bb.io.ap_return\n";
|
||||
}
|
||||
|
||||
if ($ap_rst eq 1) {
|
||||
$bb_def .= "\tbb.io.ap_rst := reset\n";
|
||||
}
|
||||
if ($ap_clk eq 1) {
|
||||
$bb_def .= "\tbb.io.ap_clk := clock\n";
|
||||
}
|
||||
|
||||
$idx = 0;
|
||||
#foreach my $arg (keys %verilog_input_pointer) {
|
||||
foreach my $arg (@verilog_input_pointer_arg) {
|
||||
$bb_def.="\tio.ap_bus($idx).req.din := bb.io.$arg"."_req_din
|
||||
\tbb.io.$arg"."_req_full_n := io.ap_bus($idx).req_full_n
|
||||
\tio.ap_bus($idx).req_write := bb.io.$arg"."_req_write
|
||||
\tbb.io.$arg"."_rsp_empty_n := io.ap_bus($idx).rsp_empty_n
|
||||
\tio.ap_bus($idx).rsp_read := bb.io.$arg"."_rsp_read
|
||||
\tio.ap_bus($idx).req.address := bb.io.$arg"."_address
|
||||
\tbb.io.$arg"."_datain := io.ap_bus($idx).rsp.datain
|
||||
\tio.ap_bus($idx).req.dataout := bb.io.$arg"."_dataout
|
||||
\tio.ap_bus($idx).req.size := bb.io.$arg"."_size
|
||||
";
|
||||
$idx += 1;
|
||||
}
|
||||
$idx = 0;
|
||||
foreach my $arg (@verilog_input_scalar) {
|
||||
$bb_def .="\tbb.io.$arg := io.scalar_io($idx)\n";
|
||||
$idx += 1;
|
||||
}
|
||||
|
||||
$bb_def .= "}";
|
||||
|
||||
print BB $bb_def;
|
||||
close BB;
|
||||
|
||||
##############################################################################################################################
|
||||
print "Copying Vivado HLS Interface file ...\n";
|
||||
copy("$rdir/tools/centrifuge/scripts/chisel_rocc_aux/ap_bus.scala", "$scala_dir/") or die "Copy failed: $!";
|
||||
|
||||
##############################################################################################################################
|
||||
print "Copying ROCC Memory Controller file ...\n";
|
||||
copy("$rdir/tools/centrifuge/scripts/chisel_rocc_aux/memControllerComponents.scala", "$scala_dir/") or die "Copy failed: $!";
|
||||
|
||||
##############################################################################################################################
|
||||
print "Copying Controller Utilities file ...\n";
|
||||
copy("$rdir/tools/centrifuge/scripts/chisel_rocc_aux/controlUtils.scala", "$scala_dir/") or die "Copy failed: $!";
|
||||
|
||||
##############################################################################################################################
|
||||
print "Generating Control file ...\n";
|
||||
|
||||
open CT, ">$scala_dir/$func_name"."_accel.scala";
|
||||
my $control1 = '
|
||||
package hls_test_c
|
||||
import Chisel._
|
||||
import chisel3.experimental.dontTouch
|
||||
import freechips.rocketchip.config.{Parameters, Field}
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.config._
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.rocket._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.system._
|
||||
|
||||
import vivadoHLS._
|
||||
import memControl._
|
||||
import hls_test_c._
|
||||
|
||||
class HLStest_cControl(opcodes: OpcodeSet)(implicit p: Parameters) extends LazyRoCC(opcodes) {
|
||||
override lazy val module = new HLStest_cControlModule(this)
|
||||
}
|
||||
|
||||
class HLStest_cControlModule(outer: HLStest_cControl)(implicit p: Parameters) extends LazyRoCCModuleImp(outer)
|
||||
with HasCoreParameters {
|
||||
';
|
||||
|
||||
$control1 =~ s/test_c/$func_name/g;
|
||||
print CT $control1;
|
||||
#TODO modify accelerator arg!
|
||||
my $control2 = '
|
||||
val result = Reg(init=Bits(0, width=xLen))
|
||||
val respValid = Reg(init=Bool(false))
|
||||
val rdy = Reg(init=Bool(true))
|
||||
val busy = Reg(init=Bool(false))
|
||||
val bufferedCmd = Reg(init=Wire( new RoCCCommand()(p)))
|
||||
|
||||
val cmd = Queue(io.cmd)
|
||||
val funct = bufferedCmd.inst.funct
|
||||
val rs1 = bufferedCmd.rs1
|
||||
val rs2 = bufferedCmd.rs2
|
||||
val rdTag = bufferedCmd.inst.rd
|
||||
val doAdd = funct === UInt(0)
|
||||
|
||||
val rs1_unbuffered = cmd.bits.rs1
|
||||
val rs2_unbuffered = cmd.bits.rs2
|
||||
|
||||
val idle :: working :: Nil = Enum(UInt(),2)
|
||||
val state = Reg(init=idle)
|
||||
|
||||
when(reset.toBool){
|
||||
bufferedCmd.inst.funct := 0.asUInt(7.W)
|
||||
bufferedCmd.inst.rs1 := 0.asUInt(5.W)
|
||||
bufferedCmd.inst.rs2 := 0.asUInt(5.W)
|
||||
bufferedCmd.inst.rd := 0.asUInt(5.W)
|
||||
bufferedCmd.inst.opcode := 0.asUInt(5.W)
|
||||
bufferedCmd.rs1 := 0.asUInt(64.W)
|
||||
bufferedCmd.rs2 := 0.asUInt(64.W)
|
||||
}
|
||||
|
||||
// Assign Outputs to Appropriate registers
|
||||
io.resp.valid := respValid && bufferedCmd.inst.xd
|
||||
|
||||
//need to set rd to the value in the request. Otherwise bad things happen
|
||||
//in this case, processor stalls
|
||||
io.resp.bits.rd := rdTag
|
||||
io.resp.bits.data := result
|
||||
io.busy := busy
|
||||
cmd.ready := rdy
|
||||
|
||||
//===== Begin Accelerator =====
|
||||
val accel = Module(new HLStest_cBlackbox())
|
||||
|
||||
//Acclerator Registers (we buffer inputs to accelerator)
|
||||
val ap_start = Reg(init=Bool(false))
|
||||
|
||||
//Assign Inputs to Accelerator
|
||||
accel.io.ap.start := ap_start
|
||||
';
|
||||
#accel.io.test_c_rs1 := rs1 //ACCEL IO NAME CAN CHANGE (NAMED IN C)
|
||||
#accel.io.test_c_rs2 := rs2 //ACCEL IO NAME CAN CHANGE (NAMED IN C)
|
||||
#my $rs1 = $verilog_input_scalar[0];
|
||||
#my $rs2 = $verilog_input_scalar[1];
|
||||
|
||||
for( $i = 0; $i < @verilog_input_scalar; $i = $i + 1 ){
|
||||
my $number = $i + 1;
|
||||
$control2 = $control2."accel.io.scalar_io($i) := rs$number\n";
|
||||
}
|
||||
|
||||
if ($ap_return eq 1){
|
||||
$control2 = $control2."val ap_return = accel.io.ap.rtn\n";
|
||||
}else{
|
||||
$control2 = $control2."val ap_return = UInt(4)\n";
|
||||
}
|
||||
|
||||
|
||||
$control2 = $control2.'//Accelerator Outputs
|
||||
val ap_done = accel.io.ap.done
|
||||
val ap_idle = accel.io.ap.idle
|
||||
val ap_ready = accel.io.ap.ready
|
||||
|
||||
//===== End Accelerator =====
|
||||
|
||||
//===== Begin Mem Controller =====
|
||||
//The following are specific to the accelerator. They set the address and data widths of the ap_bus interfaces
|
||||
val dataWidth = accel.ap_bus_dataWidths
|
||||
val addrWidth = accel.ap_bus_addrWidths
|
||||
val reqBufferLen = 4
|
||||
val rspBufferLen = 4
|
||||
val maxReqBytes = xLen/8
|
||||
val roccAddrWidth = coreMaxAddrBits
|
||||
val roccDataWidth = coreDataBits
|
||||
val roccTagWidth = coreDCacheReqTagBits
|
||||
val roccCmdWidth = M_SZ
|
||||
val roccTypWidth = log2Ceil(coreDataBytes.log2 + 1)
|
||||
//val numTags = p(RoccMaxTaggedMemXacts)
|
||||
val numTags = 16
|
||||
val tagOffset = 0 //Used if multiple accelerators to avoid tag collisions
|
||||
|
||||
//Instantiate Controller
|
||||
val memControl = Module(new MemController(dataWidth, addrWidth, reqBufferLen, rspBufferLen, maxReqBytes, roccAddrWidth, roccDataWidth, roccTagWidth, roccCmdWidth, roccTypWidth, numTags, tagOffset))
|
||||
|
||||
if(accel.io.ap_bus.length > 0){
|
||||
//We have memory bus interfaces on the accelerator, create a memory controller
|
||||
|
||||
//Hook up controller
|
||||
for(i <- 0 until accel.io.ap_bus.length){
|
||||
//memControl.io.reqsIn(i) <> accel.io.ap_bus(i).req
|
||||
memControl.io.reqsIn(i) := accel.io.ap_bus(i).req
|
||||
|
||||
accel.io.ap_bus(i).req_full_n := memControl.io.reqsFullN(i)
|
||||
|
||||
memControl.io.reqsWrite(i) := accel.io.ap_bus(i).req_write
|
||||
|
||||
accel.io.ap_bus(i).rsp.datain := memControl.io.rspOut(i).datain
|
||||
accel.io.ap_bus(i).rsp_empty_n := memControl.io.rsp_empty_n(i)
|
||||
memControl.io.rsp_read(i) := accel.io.ap_bus(i).rsp_read
|
||||
}
|
||||
io.mem.req.bits.addr := memControl.io.roCCReqAddr
|
||||
io.mem.req.bits.tag := memControl.io.roCCReqTag
|
||||
io.mem.req.bits.cmd := memControl.io.roCCReqCmd
|
||||
io.mem.req.bits.size := memControl.io.roCCReqTyp
|
||||
|
||||
// If the address is not a mulitple of 8 byte which the coreDataBits width,
|
||||
// We have to shift the N-bit data to the right place in a 64-bit word
|
||||
val shift = (memControl.io.roCCReqAddr & UInt( log2Up(coreDataBits) - 1 )) << UInt(3)
|
||||
io.mem.req.bits.data := memControl.io.roCCReqData << shift(7,0)
|
||||
io.mem.req.valid := memControl.io.roCCReqValid
|
||||
memControl.io.roCCReqRdy := io.mem.req.ready
|
||||
//io.mem.req.bits.phys := Bool(true)
|
||||
|
||||
//val roCCRespAddr = UInt(INPUT, width = roccAddrWidth) // coreMaxAddrBits)
|
||||
memControl.io.roCCRspTag := io.mem.resp.bits.tag
|
||||
memControl.io.roCCRspCmd := io.mem.resp.bits.cmd
|
||||
memControl.io.roCCRspData := io.mem.resp.bits.data
|
||||
//val roCCRespTyp
|
||||
memControl.io.roCCRspValid := io.mem.resp.valid
|
||||
}
|
||||
|
||||
//===== End Mem Controller =====
|
||||
';
|
||||
# The sequence of arg 1 and 2 depends on the sequence they show up in the verilog file
|
||||
# TODO think about a better way to add this
|
||||
|
||||
$control2 .= '//===== Begin Argument Handling =====
|
||||
//TODO: currently only works for 2 argument calls. Generalize
|
||||
|
||||
val cArgs = List(rs1, rs2)
|
||||
val cArgsUnbuffered = List(rs1_unbuffered, rs2_unbuffered)
|
||||
//Argument numbers are specified in the blackbox
|
||||
';
|
||||
|
||||
if (@verilog_input_scalar > 0){
|
||||
$control2 .= '//Scalar values
|
||||
for(i <- 0 until accel.io.scalar_io.length){
|
||||
accel.io.scalar_io(i) := cArgs(accel.scalar_io_argLoc(i))
|
||||
}
|
||||
';
|
||||
}
|
||||
|
||||
$control2 .= '//ap_bus offsets
|
||||
for(i <- 0 until memControl.io.offsetAddrs.length){
|
||||
//ap_bus uses the unbuffered input because it is buffered on the first cycle
|
||||
memControl.io.offsetAddrs(i) := cArgsUnbuffered(accel.ap_bus_argLoc(i))
|
||||
}
|
||||
//===== End Argument Handling =====
|
||||
';
|
||||
|
||||
|
||||
$control2 .='
|
||||
if(accel.io.ap_bus.length > 0){
|
||||
//Will run ap_start after offsets loaded
|
||||
for(i <- 0 until memControl.io.loadOffsets.length){
|
||||
memControl.io.loadOffsets(i) := (state === idle) && cmd.fire()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===== Begin Controller State Machine Logic =====
|
||||
switch(state){
|
||||
is (idle){
|
||||
//Waiting for command
|
||||
|
||||
when(cmd.fire()){
|
||||
//We have a valid, unserviced command. This code takes ready low so
|
||||
//we should not accedently cause an infinite loop
|
||||
|
||||
bufferedCmd := cmd.bits //Accelerator takes from bufferedCmd directly
|
||||
busy := Bool(true)
|
||||
rdy := Bool(false)
|
||||
|
||||
//Load the offsets
|
||||
/*if(accel.io.ap_bus.length > 0){
|
||||
//Will run ap_start after offsets loaded
|
||||
for(i <- 0 until memControl.io.loadOffsets.length){
|
||||
memControl.io.loadOffsets(i) := Bool(true)
|
||||
}
|
||||
}*/
|
||||
|
||||
ap_start := Bool(true) //Set next state
|
||||
state := working
|
||||
//Note: Based on timing diagram in Vivado HLS user guide (pg 157), read occurs
|
||||
//AFTER the 1st cycle. There will be a 1 cycle delay before input read as
|
||||
//ap_start will be seen on next cycle. Idealy, ap_start would be raised 1 cycle
|
||||
//earlier (ie. not using a register) or it would read the input immediatly
|
||||
//when ap_start is raised (I assume this is due to an internal state machine).
|
||||
//However, this would ruin the sequential nature of the state machine. It is
|
||||
//possible to save a cycle by assigning ap_start as cmd.valid && state===idle
|
||||
//&& !returned which would be asyncronous and probably trigger 1 cycle earlier.
|
||||
//There would be more stringent timing requirements in this case though as the
|
||||
//result would need to propogate before the next posEdge of the clk.
|
||||
|
||||
|
||||
}
|
||||
when(respValid && io.resp.ready){
|
||||
//The processor has read the response. There is no more data for it
|
||||
//Drive resp.valid low to avoid stalling processor
|
||||
respValid := Bool(false)
|
||||
}
|
||||
}
|
||||
is (working){
|
||||
|
||||
//Stop Loading offsets
|
||||
/*if(accel.io.ap_bus.length > 0){
|
||||
//Will run ap_start after offsets loaded
|
||||
for(i <- 0 until memControl.io.loadOffsets.length){
|
||||
memControl.io.loadOffsets(i) := Bool(false)
|
||||
}
|
||||
}*/
|
||||
|
||||
//Waiting for accelerator to finish
|
||||
|
||||
//All of the conditionals below can occure simultaniously
|
||||
//and should be kept as seperart when statements
|
||||
when(ap_done){
|
||||
//The accelerator has completed operation (user guidepg 156) and has
|
||||
//has optionally generated a result (not not all accelerators will
|
||||
//generated a result. This is technically not the same as ap_idle
|
||||
//which signals when the accelerator is no longer busy. It is actually
|
||||
//ap_ready actually determines when the accelerator is ready to accept
|
||||
//more inputs. This is important for accelerators that do not operate
|
||||
//in a syncronous mode. This is not true for the types of accelerators
|
||||
//we are creating.
|
||||
|
||||
result := ap_return
|
||||
respValid := Bool(true)
|
||||
}
|
||||
when(ap_ready){
|
||||
//The accelerator has read the inputs and is ready to accept new ones.
|
||||
//According to the timing diagram,
|
||||
//ap_start should be deasserted for the next posedge.
|
||||
ap_start := Bool(false)
|
||||
}
|
||||
|
||||
|
||||
if(accel.io.ap_bus.length == 0){
|
||||
when(ap_idle){
|
||||
//if the operation was completed (result valid), and the accelerator is ready
|
||||
//the accerator is ready for the next operation and the controller is
|
||||
//returned to the idle state to wait for a new command. the ready line
|
||||
//is pulled high to advertise that the accelerator is ready.
|
||||
|
||||
//from the manual, it appears that ap_done is always asserted when the
|
||||
//accelerator is finished. if this is true, using ap_done && ap_ready
|
||||
//should save one cycle over using ap_idle as the trigger. this is because,
|
||||
//according to the timing diagram in the user manual (pg
|
||||
|
||||
rdy := Bool(true) // ready to accept new commands
|
||||
busy := Bool(false) // operation complete, no longer busy
|
||||
state := idle
|
||||
|
||||
//note: this code could possibly be placed in the ap_done action to save
|
||||
//one wasted cycle. it is not clear from the user guide (pg 157), ap_idle
|
||||
//is asserted one cycle after ap_done. if this arrangment has problems,
|
||||
//transitioning on ap_idle should work but will result in an unnessicary
|
||||
//extra cycle.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
when(ap_idle && !memControl.io.memBusy){
|
||||
//if the operation was completed (result valid), and the accelerator is ready
|
||||
//the accerator is ready for the next operation and the controller is
|
||||
//returned to the idle state to wait for a new command. the ready line
|
||||
//is pulled high to advertise that the accelerator is ready.
|
||||
|
||||
//from the manual, it appears that ap_done is always asserted when the
|
||||
//accelerator is finished. if this is true, using ap_done && ap_ready
|
||||
//should save one cycle over using ap_idle as the trigger. this is because,
|
||||
//according to the timing diagram in the user manual (pg
|
||||
|
||||
rdy := Bool(true) // ready to accept new commands
|
||||
busy := Bool(false) // operation complete, no longer busy
|
||||
state := idle
|
||||
|
||||
//note: this code could possibly be placed in the ap_done action to save
|
||||
//one wasted cycle. it is not clear from the user guide (pg 157), ap_idle
|
||||
//is asserted one cycle after ap_done. if this arrangment has problems,
|
||||
//transitioning on ap_idle should work but will result in an unnessicary
|
||||
//extra cycle.
|
||||
}
|
||||
}
|
||||
when(respValid && io.resp.ready){
|
||||
//The processor has read the response. There is no more data for it
|
||||
//Drive resp.valid low to avoid stalling processor
|
||||
respValid := Bool(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== End Controller State Machine Logic =====
|
||||
|
||||
// ===== Tie off these lines =====
|
||||
io.interrupt := Bool(false)
|
||||
// Set this true to trigger an interrupt on the processor (please refer to supervisor documentation)
|
||||
|
||||
// MEMORY REQUEST INTERFACE
|
||||
if(accel.io.ap_bus.length == 0){
|
||||
// No connected memory bus lines on accelerator
|
||||
// We will not be doing any memory ops in this accelerator
|
||||
io.mem.req.valid := Bool(false)
|
||||
io.mem.req.bits.addr := UInt(0)
|
||||
io.mem.req.bits.tag := UInt(0)
|
||||
io.mem.req.bits.cmd := M_XRD // perform a load (M_XWR for stores)
|
||||
io.mem.req.bits.size := log2Ceil(8).U
|
||||
io.mem.req.bits.signed := Bool(false)
|
||||
io.mem.req.bits.data := UInt(0) // not performing any stores
|
||||
}
|
||||
//io.mem.invalidate_lr := Bool(false)
|
||||
|
||||
//If enable physical addr, make sure to use pmp instr to set the right permission on addr range
|
||||
io.mem.req.bits.phys := Bool(false)
|
||||
';
|
||||
|
||||
|
||||
$control2 .= "}\n";
|
||||
# TODO no clock and reset signal
|
||||
$control2 =~ s/test_c/$func_name/g;
|
||||
|
||||
print CT $control2;
|
||||
|
||||
|
|
@ -0,0 +1,560 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
use List::Util qw(first);
|
||||
|
||||
# Inputs: file_name, func_name, func_base_addr, prefix(Optional)
|
||||
my $dir = getcwd;
|
||||
my $file_name = $ARGV[0];
|
||||
my $func_name = $ARGV[1];
|
||||
my $func_base_addr = $ARGV[2];
|
||||
my $rdir = $ENV{'RDIR'};
|
||||
|
||||
my $prefix = undef;
|
||||
my $i = undef;
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 3) {
|
||||
$prefix = $ARGV[3];
|
||||
}
|
||||
|
||||
#my $bm_path = $rdir."/sim/target-rtl/firechip/hls_$file_name"."_$func_name";
|
||||
if ($prefix) {
|
||||
$func_name = $prefix.$func_name;
|
||||
}
|
||||
|
||||
#print $rdir;
|
||||
if ((not defined($rdir)) or $rdir eq '') {
|
||||
print("Please source sourceme-f1.sh!\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
# my $build_sbt = '
|
||||
# organization := "edu.berkeley.cs"
|
||||
#
|
||||
# version := "1.0"
|
||||
#
|
||||
# name := "hls_test_c"';
|
||||
#
|
||||
# $build_sbt=~ s/test_c/$func_name/g;
|
||||
# my $build_sbt_path= "$bm_path/"."build.sbt";
|
||||
# open BUILD, ">$build_sbt_path";
|
||||
# print BUILD $build_sbt;
|
||||
# close BUILD;
|
||||
|
||||
my $verilog_file = "$dir/../verilog/$func_name".".v";
|
||||
my $line = undef;
|
||||
my @verilog_param = ();
|
||||
my @param_val = ();
|
||||
my @verilog_input = ();
|
||||
my @verilog_input_size = ();
|
||||
my @verilog_output = ();
|
||||
my @verilog_output_size = ();
|
||||
|
||||
#my $m_axi_data_width = undef;
|
||||
#my $s_axi_data_width = undef;
|
||||
|
||||
my @bus_names=();
|
||||
my @m_axi_data_widths = ();
|
||||
my $s_axi_data_width = undef;
|
||||
|
||||
print "Parsing ".$verilog_file."\n";
|
||||
# parse the verilog file to get the info we need
|
||||
if(!open VERILOG, "$verilog_file"){
|
||||
print $!;
|
||||
} else {
|
||||
while(<VERILOG>){
|
||||
$line = $_;
|
||||
|
||||
# Match AXI4 parameter
|
||||
if($line =~ m/parameter\s+(C_\S+) =\s+(.*);/){
|
||||
my $param = $1;
|
||||
my $val = $2;
|
||||
$param .="";
|
||||
if($param =~ m/C_M_AXI_(\S+)_DATA_WIDTH/){
|
||||
my $bus_name = lc $1;
|
||||
#$m_axi_data_width = $val;
|
||||
push(@bus_names, $bus_name);
|
||||
push(@m_axi_data_widths, $val);
|
||||
}
|
||||
if ($param eq "C_S_AXI_DATA_WIDTH") {
|
||||
$s_axi_data_width = $val;
|
||||
}
|
||||
push (@verilog_param, $param);
|
||||
push (@param_val, $val);
|
||||
} elsif($line =~ m/^\s*input\s+(.*)/){
|
||||
my $input = $1;
|
||||
#print "input:$input\n";
|
||||
if($input =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $input_name = $3;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
my $size = 0;
|
||||
if ($end =~ m/^\d+$/){
|
||||
$size = $end - $start + 1;
|
||||
$size = "".$size;
|
||||
} elsif($end =~m/(\S+) - 1/) {
|
||||
$size = $1;
|
||||
}
|
||||
push(@verilog_input_size, $size);
|
||||
}elsif ($input =~ m/\s*(.*)\s*;/){
|
||||
my $input_name = $1;
|
||||
#print "here!"."$input_name\n";
|
||||
push (@verilog_input, $input_name);
|
||||
push(@verilog_input_size, "1");
|
||||
}
|
||||
|
||||
}elsif($line =~ m/^\s*output\s+(.*)/){
|
||||
my $output = $1;
|
||||
#print "output:$output\n";
|
||||
if($output =~ m/\s*\[(.*):(.*)\]\s*(.*)\s*;/){
|
||||
my $end = $1;
|
||||
my $start = $2;
|
||||
my $output_name = $3;
|
||||
#print "here!"."$output_name\n";
|
||||
push(@verilog_output, $output_name);
|
||||
my $size = 0;
|
||||
if ($end =~ m/^\d+$/){
|
||||
$size = $end - $start + 1;
|
||||
$size = "".$size;
|
||||
} elsif($end =~m/(\S+) - 1/) {
|
||||
$size = $1;
|
||||
}
|
||||
push(@verilog_output_size, $size);
|
||||
}elsif ($output =~ m/\s*(.*)\s*;/){
|
||||
my $output_name = $1;
|
||||
#print "here!"."$output_name\n";
|
||||
push (@verilog_output, $output_name);
|
||||
push(@verilog_output_size, "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
print("Parameters: ");
|
||||
my $param_str = join ' ', @verilog_param;
|
||||
print $param_str."\n";
|
||||
|
||||
print("Inputs: ");
|
||||
my $in_str = join ' ', @verilog_input;
|
||||
print $in_str."\n";
|
||||
print("Outputs: ");
|
||||
my $out_str = join ' ', @verilog_output;
|
||||
print $out_str."\n";
|
||||
}
|
||||
|
||||
#creat scala folder
|
||||
my $scala_dir = "$dir/../scala";
|
||||
mkdir $scala_dir unless (-d $scala_dir);
|
||||
|
||||
##############################################################################################################################
|
||||
if(@m_axi_data_widths < 1){
|
||||
push(@bus_names, "gmem_dummy");
|
||||
push(@m_axi_data_widths, 32);
|
||||
}
|
||||
|
||||
if(not defined($s_axi_data_width)) {
|
||||
$s_axi_data_width=32
|
||||
}
|
||||
|
||||
print "Generating BlackBox file ...\n";
|
||||
for( $i = 0; $i < @m_axi_data_widths; $i = $i + 1 ){
|
||||
print "m_axi_data_width_ $bus_names[$i]= $m_axi_data_widths[$i]\n";
|
||||
}
|
||||
|
||||
print "s_axi_data_width = $s_axi_data_width\n";
|
||||
# should be under scala folder
|
||||
open BB, ">$scala_dir/$func_name"."_blackbox.scala";
|
||||
|
||||
my $blackbox1 = "
|
||||
package hls_test_c
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.{Parameters, Field}
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
class test_c() extends BlackBox() {
|
||||
";
|
||||
$blackbox1 =~ s/test_c/$func_name/g;
|
||||
|
||||
# Print parameters
|
||||
for( $i = 0; $i < @verilog_param; $i = $i + 1 ){
|
||||
$blackbox1 .= "val $verilog_param[$i] = $param_val[$i]\n";
|
||||
}
|
||||
|
||||
print BB $blackbox1;
|
||||
|
||||
|
||||
print BB "\tval io = new Bundle {\n";
|
||||
my $bb_body = "";
|
||||
|
||||
# now if the input name does not start with ap, we assume it is an arg
|
||||
my $ap_return = 0;
|
||||
my $ap_clk = 0;
|
||||
my $ap_rst = 0;
|
||||
my $ap_rst_n = 0;
|
||||
|
||||
my @verilog_axi_io = ();
|
||||
|
||||
for( $i = 0; $i < @verilog_input; $i = $i + 1 ){
|
||||
my $input_name = $verilog_input[$i];
|
||||
my $input_size = $verilog_input_size[$i];
|
||||
if ($input_name =~ m/^ap_clk$/){
|
||||
$ap_clk = 1;
|
||||
}
|
||||
elsif ($input_name =~ m/^ap_rst$/){
|
||||
$ap_rst = 1;
|
||||
}
|
||||
elsif ($input_name =~ m/^ap_rst_n$/){
|
||||
$ap_rst_n = 1;
|
||||
}
|
||||
elsif($input_name =~ m/^(m_axi|s_axi)\S+$/){
|
||||
push (@verilog_axi_io, $input_name);
|
||||
}
|
||||
|
||||
print BB "\t\tval $input_name = ";
|
||||
if ($input_name =~ m/ap_clk(.*)/){
|
||||
print BB "Clock\(INPUT\)\n";
|
||||
}else{
|
||||
print BB "Bits\(INPUT, width = $input_size\)\n";
|
||||
}
|
||||
}
|
||||
|
||||
for( $i = 0; $i < @verilog_output; $i = $i + 1 ){
|
||||
|
||||
my $output_name = $verilog_output[$i];
|
||||
my $output_size = $verilog_output_size[$i];
|
||||
|
||||
if ($output_name =~ m/ap_return(.*)/){
|
||||
$ap_return = 1;
|
||||
}
|
||||
elsif($output_name =~ m/^(m_axi|s_axi)\S+$/){
|
||||
push (@verilog_axi_io, $output_name);
|
||||
}
|
||||
|
||||
print BB "\t\tval $output_name = ";
|
||||
print BB "Bits(OUTPUT, width = $output_size)\n";
|
||||
|
||||
}
|
||||
|
||||
print BB "\t}\n";
|
||||
print BB "}\n";
|
||||
|
||||
close BB;
|
||||
##############################################################################################################################
|
||||
print "Generating Control file ...\n";
|
||||
|
||||
open CT, ">$scala_dir/$func_name"."_accel.scala";
|
||||
|
||||
#TODO Fix AXI4 params
|
||||
my $control1 = '
|
||||
package hls_test_c
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
import freechips.rocketchip.config.{Field, Parameters}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.amba.axi4._
|
||||
import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.subsystem._
|
||||
|
||||
class HLStest_cAXI (address: BigInt = 0x20000, beatBytes: Int = 8) (implicit p: Parameters) extends LazyModule {
|
||||
|
||||
val numInFlight = 8
|
||||
';
|
||||
|
||||
for( $i = 0; $i < @m_axi_data_widths; $i = $i + 1 ){
|
||||
$control1 .="
|
||||
val node_$bus_names[$i] = AXI4MasterNode(Seq(AXI4MasterPortParameters(
|
||||
masters = Seq(AXI4MasterParameters(
|
||||
name = \"axil_hub_mem_out_$i\",
|
||||
id = IdRange(0, numInFlight),
|
||||
aligned = true,
|
||||
maxFlight = Some(8)
|
||||
)),
|
||||
userBits = 0
|
||||
)
|
||||
))";
|
||||
}
|
||||
$control1 .='
|
||||
val slave_node = AXI4SlaveNode(Seq(AXI4SlavePortParameters(
|
||||
slaves = Seq(AXI4SlaveParameters(
|
||||
address = List(AddressSet(address,0x4000-1)),
|
||||
regionType = RegionType.UNCACHED,
|
||||
supportsWrite = TransferSizes(1, beatBytes),
|
||||
supportsRead = TransferSizes(1, beatBytes),
|
||||
interleavedId = Some(0)
|
||||
)),
|
||||
beatBytes = beatBytes
|
||||
)))
|
||||
|
||||
lazy val module = new HLStest_cAXIModule(this)
|
||||
}
|
||||
|
||||
class HLStest_cAXIModule(outer: HLStest_cAXI) extends LazyModuleImp(outer) {
|
||||
|
||||
//val (out, edge) = outer.node.out(0)
|
||||
val (slave_in, slave_edge) = outer.slave_node.in(0)
|
||||
|
||||
val bId = Reg(UInt(32.W))
|
||||
val rId = Reg(UInt(32.W))
|
||||
|
||||
val bb = Module(new test_c())
|
||||
';
|
||||
|
||||
for( $i = 0; $i < @m_axi_data_widths; $i = $i + 1 ){
|
||||
$control1 .="
|
||||
val (out_$bus_names[$i], edge_$bus_names[$i]) = outer.node_$bus_names[$i].out(0)";
|
||||
}
|
||||
$control1 .= "\n";
|
||||
$control1 =~ s/s_axi_data_width/$s_axi_data_width/g;
|
||||
|
||||
if ($ap_clk eq 1){
|
||||
$control1 .= "\tbb.io.ap_clk := clock\n";
|
||||
}
|
||||
if ($ap_rst eq 1){
|
||||
$control1 .= "\tbb.io.ap_rst := reset\n";
|
||||
}
|
||||
if ($ap_rst_n eq 1){
|
||||
$control1 .= "\tbb.io.ap_rst_n := !reset.toBool() \n";
|
||||
}
|
||||
|
||||
$control1 =~ s/test_c/$func_name/g;
|
||||
print CT $control1;
|
||||
#TODO modify accelerator arg!
|
||||
my $control2 = '
|
||||
';
|
||||
|
||||
# TODO Add support for multiple AXI buses
|
||||
# AXI Inputs Signals
|
||||
for( $i = 0; $i < @verilog_axi_io; $i = $i + 1 ){
|
||||
my $number = $i + 1;
|
||||
if ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|W|AR)READY$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.ready\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R|B)VALID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.valid\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R)DATA$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.bits.data\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R)LAST$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.bits.last\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R|B)ID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.bits.id\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R|B)RESP$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := out_$bus_name.$type.bits.resp\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(AW|W|AR)VALID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := slave_in.$type.valid\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(AW|AR)ADDR$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := slave_in.$type.bits.addr\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(W)DATA$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := slave_in.$type.bits.data\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(W)STRB$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := slave_in.$type.bits.strb\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(R|B)READY$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tbb.io.$verilog_axi_io[$i] := slave_in.$type.ready\n";
|
||||
}
|
||||
}
|
||||
|
||||
for( $i = 0; $i < @verilog_axi_io; $i = $i + 1 ){
|
||||
my $number = $i + 1;
|
||||
if ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|W|AR)VALID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.valid := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(R|B)READY$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.ready := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)ADDR$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.addr := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)ID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.id := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)LEN$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.len := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)SIZE$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.size := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)BURST$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.burst := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)LOCK$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.lock := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)CACHE$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.cache := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)PROT$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.prot := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)QOS$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.qos := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(AW|AR)REGION$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\t//out_$bus_name.$type.bits.region := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(W)DATA$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.data := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(W)STRB$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.strb := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/m_axi_(.*)_(W)LAST$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tout_$bus_name.$type.bits.last := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(AW|W|AR)READY$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tslave_in.$type.ready := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(R|B)VALID$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tslave_in.$type.valid := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(R)DATA$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tslave_in.$type.bits.data := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
elsif ($verilog_axi_io[$i] =~ m/s_axi_(.*)_(R|B)RESP$/){
|
||||
my $bus_name = $1;
|
||||
my $type = lc $2;
|
||||
$control2 .= "\tslave_in.$type.bits.resp := bb.io.$verilog_axi_io[$i]\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($ap_return eq 1){
|
||||
$control2 = $control2."\tval ap_return = accel.io.ap.rtn\n";
|
||||
}
|
||||
$control2 .= "
|
||||
// For AXI4lite, these two signals are always True
|
||||
slave_in.r.bits.last := true.B
|
||||
|
||||
when(slave_in.aw.fire()){
|
||||
bId := slave_in.aw.bits.id
|
||||
}
|
||||
|
||||
when(slave_in.ar.fire()){
|
||||
rId := slave_in.ar.bits.id
|
||||
}
|
||||
slave_in.r.bits.id := rId
|
||||
slave_in.b.bits.id := bId
|
||||
}
|
||||
";
|
||||
|
||||
# TODO Fix the width here
|
||||
$control2 .='
|
||||
trait HasPeripheryHLStest_cAXI { this: BaseSubsystem =>
|
||||
private val address = BigInt(base_addr)
|
||||
private val axi_m_portName = "HLS-Accelerator-test_c-master"
|
||||
private val axilite_s_portName = "HLS-Accelerator-test_c-slave"
|
||||
|
||||
//val accel_s_axi_width = s_axi_data_width
|
||||
//val hls_test_c_accel = LazyModule(new HLStest_cAXI(address, sbus.beatBytes))
|
||||
val hls_test_c_accel = LazyModule(new HLStest_cAXI(address, s_axi_data_width >> 3))
|
||||
';
|
||||
|
||||
|
||||
for( $i = 0; $i < @m_axi_data_widths; $i = $i + 1 ){
|
||||
$control2 .="
|
||||
sbus.fromPort(Some(axi_m_portName)) {
|
||||
(TLWidthWidget($m_axi_data_widths[$i]>> 3 )
|
||||
:= AXI4ToTL()
|
||||
:= AXI4UserYanker()
|
||||
:= AXI4Fragmenter()
|
||||
:= AXI4IdIndexer(1))
|
||||
}:=* hls_test_c_accel.node_$bus_names[$i]
|
||||
";
|
||||
}
|
||||
|
||||
$control2 .='
|
||||
hls_test_c_accel.slave_node :=* sbus.toFixedWidthPort(Some(axilite_s_portName)) {
|
||||
(AXI4Buffer()
|
||||
:= AXI4UserYanker()
|
||||
//:= AXI4IdIndexer(params.idBits)
|
||||
//:= AXI4Deinterleaver(sbus.blockBytes) // Assume there is no iterleaved requests, iterleaveId = Some(0)
|
||||
:= TLToAXI4()
|
||||
:= TLBuffer()
|
||||
//:= TLWidthWidget(s_axi_data_width >> 3)
|
||||
// Compared to TLWidthWidget, TLFragmenter saves the id info?
|
||||
:= TLFragmenter(s_axi_data_width >> 3, 64, true))
|
||||
}
|
||||
}
|
||||
|
||||
trait HasPeripheryHLStest_cAXIImp extends LazyModuleImp {
|
||||
val outer: HasPeripheryHLStest_cAXI
|
||||
}';
|
||||
|
||||
$control2 =~ s/test_c/$func_name/g;
|
||||
$control2 =~ s/base_addr/$func_base_addr/g;
|
||||
$control2 =~ s/s_axi_data_width/$s_axi_data_width/g;
|
||||
print CT $control2;
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Copy;
|
||||
|
||||
my $file_name = $ARGV[0];
|
||||
my $func_name = $ARGV[1];
|
||||
|
||||
my $prefix = undef;
|
||||
|
||||
my $num_args = $#ARGV + 1;
|
||||
if ($num_args > 2) {
|
||||
$prefix = $ARGV[2];
|
||||
}
|
||||
|
||||
#############################GENERATE HLS##############################
|
||||
|
||||
# Generate directive file based on LLVM emitted output
|
||||
# If the variable is of pointer type that an ap_bus interface is generated
|
||||
|
||||
my $directive_tcl_insn = 'set_directive_interface -mode ap_bus "test_c_func" test_var
|
||||
';
|
||||
|
||||
my $prefix_tcl = "";
|
||||
if ($prefix) {
|
||||
$prefix_tcl = "config_rtl -prefix ".$prefix."\n";
|
||||
}
|
||||
my $hls_pgm = undef;
|
||||
if (-f $file_name.".cpp"){
|
||||
$hls_pgm = $file_name.'.cpp -cflags "-std=c++0x" ';
|
||||
} else {
|
||||
$hls_pgm = $file_name.".c";
|
||||
}
|
||||
|
||||
# should change to add all .c files
|
||||
my $hls_tcl = 'open_project -reset test_c_prj
|
||||
set_top test_c_func
|
||||
add_files hls_pgm
|
||||
open_solution -reset "solution1"
|
||||
set_part {xcvu9p-flgb2104-2-i}
|
||||
config_compile -ignore_long_run_time
|
||||
create_clock -period 10 -name default
|
||||
'.$prefix_tcl.'
|
||||
#source "./test_c_prj/solution1/directives.tcl"
|
||||
#config_interface -clock_enable
|
||||
config_interface -m_axi_addr64
|
||||
csynth_design
|
||||
#export_design -format ip_catalog
|
||||
exit';
|
||||
|
||||
my $dir = getcwd;
|
||||
open HLS, ">$dir/run_hls.tcl";
|
||||
|
||||
# replace the function name and file name
|
||||
$hls_tcl =~ s/test_c_func/$func_name/g;
|
||||
$hls_tcl =~ s/test_c/$file_name/g;
|
||||
$hls_tcl =~ s/hls_pgm/$hls_pgm/g;
|
||||
|
||||
|
||||
# run vivado hls
|
||||
print HLS $hls_tcl;
|
||||
system("vivado_hls -f run_hls.tcl");
|
||||
|
||||
my $vivado_dir = "$dir/$file_name"."_prj/solution1/syn/verilog/";
|
||||
my $verilog_dir = "$dir/../verilog/";
|
||||
|
||||
mkdir $verilog_dir unless (-d $verilog_dir);
|
||||
unlink glob "$verilog_dir/*";
|
||||
|
||||
opendir(DIR, $vivado_dir) or die "Can't opendir $vivado_dir: $! \n";
|
||||
|
||||
my @files=readdir(DIR);
|
||||
closedir(DIR);
|
||||
|
||||
foreach my $v_file (@files){
|
||||
# Open and replace one line
|
||||
|
||||
chdir($vivado_dir);
|
||||
my $vivado_dir_escape = $vivado_dir;
|
||||
$vivado_dir_escape =~ s/\//\\\//g;
|
||||
my $perl_cmd = "perl -p -i -e 's/\$readmemh\\\(\\\"\\\.\/\$readmemh(\\\"$vivado_dir_escape/g' *";
|
||||
|
||||
print $perl_cmd;
|
||||
system ($perl_cmd);
|
||||
|
||||
$perl_cmd = "perl -p -i -e \"s/'bx/1'b0/g\" *";
|
||||
system ($perl_cmd);
|
||||
print $perl_cmd;
|
||||
|
||||
chdir($dir);
|
||||
|
||||
print "$v_file\n";
|
||||
if (-f "$vivado_dir/$v_file") {
|
||||
copy("$vivado_dir/$v_file", $verilog_dir) or die "File cannot be copied! $v_file $verilog_dir\n";
|
||||
}
|
||||
}
|
||||
|
||||
#die $!;
|
||||
|
Loading…
Reference in New Issue