joe 发表于 2021-9-12 23:40:26

仲裁器Arbiter(3)

手写一个固定优先级仲裁器,将将源代码逐行注释说明(注释中逻辑值0,false,false.B都是一个意思,1,true,true.B也都是一个意思,不要追究语法了):

import chisel3._
import chisel3.util._
class JArbiterIO(private val gen:T,val n:Int) extends Bundle {
val enq = Flipped(Vec(n,Decoupled(gen)))
val deq = Decoupled(gen)
val chosen = Output(UInt(log2Ceil(n).W)) //enq是Vec集合类型,n个元素需要多少bit才能表示这个数
}
//选择哪个producer最后一个producer没有包含在内(why?) 将返回Seq序号0始终设置为true.B
private object JAribiterCtrl {
def apply(requests:Seq): Seq = requests.length match {
    case 0 => Seq()
    case 1 => Seq(true.B)
    case _ => true.B +: requests.tail.init.scanLeft(requests.head)(_ || _).map(!_)//+: 是中缀操作符,右边的操作数是调用方法的对象 true.B放在前面
      //SeqLike类中有:+和+:,其中:+是普通操作方法,将右加到左的尾部,+:则将左加到右的前部
    //总结:最开始添true.B,去掉last元素之后,只要某位置出现true.B,则从它开始包括它在内返回的Seq中都是false.B,
    //      如果是最后一个元素不管是true.B还是false.B,则最终返回的Seq全部都是本总结前面所述结果(因为最后一个元素被tail.init丢弃了)
}
}
class JArbiter(gen:T,n:Int) extends Module {
val io = IO(new JArbiterIO(gen,n))
//先要赋默认值
io.deq.bits := io.enq(n-1).bits //最后一个enq的数据
io.chosen := (n-1).asUInt() //最大序号
for(i <- (n-2) to 0 by -1) { //n=3 n=2 n=1 n=0(n=3时循环2次,n=2时循环1次,n=1或0时不循环) 也就是说(n-2) to 0 by -1也是将last enq去掉了
    when(io.enq(i).valid) { //只要某个输入的valid有效
      io.deq.bits := io.enq(i).bits //仲裁器的输出数据为它对应的数据 如果有多个valid,则根据语法,最后(序号小)一个的赋值将会是仲裁器数据输出
      io.chosen := i.asUInt()
    }
}
//如果只有一个in,则grant(0)=true.B
//如果只有二个in,grant(0)=true.B,grant(1)=io.enq(0).valid的值取反
//如果有三个in,grant(0)=true.B,grant(1)=io.enq(0).valid的值取反,grant(2)=io.enq(1).valid的值取反
val grant = JAribiterCtrl(io.enq.map(_.valid)) //所有enq的valid
for((enq,g) <- io.enq zip grant) { //Lit(1,2,3) zip (4,5)==>List((1,4),(2,5))
    enq.ready := g && io.deq.ready //总结:某个producer发出了valid则小于它序号的所有producer的ready都将ready=1
    //只要有io.deq.ready,那么第0序号的producer的ready肯定会true,如果还有其它序号producer的valid=true.B(多个producer时,先是取最小序号的producer,暂叫producerA),
    //那么producerA对应序号之前grant成员会是true.B,包括本producerA以及本producerA序号之后都是false.B
    //如果只有二个producer,当producer(1)valid=1而producer(0)valid=0时,grant(0)=true.B,grant(1)=其实就是producer(0)的valid值取反即=1
    //如果有三个producer,当producer(2)valid=1时,grant(0)=true.B,grant(1)=true.B(其实就是producer(0)的valid值取反),grant(2)=true.B(其实就是producer(1)的valid值取反)
    //如果只有二个producer,当producer(0)valid=1,grant(0)=true.B,grant(1)=false.B
    //如果有三个producer,当producer(1)valid=1时,grant(0)=true.B,grant(1)=true.B,grant(2)=false.B
}
//最后一个producer的valid=1即io.enq.last.valid=1 不管grantlast如何取值,最终 io.deq.valid:=1,但分析分析并且分析对应的数据输出
//如果grant.last=1,说明除了最后一个producer之外,其它所有producer的valid都是0,最终 io.deq.valid:=1,输出数据也是最后一个producer的数据
//如果grant.last=0,说明倒数第二个或倒数第三/第四等producer的valid总有一个或多个=1,所以io.deq.valid=1,从逻辑角度来看下游收到valid也没有错,此时要分析上面数据是否对应(最小序号的producer的数据)
//最后一个producer的valid=0即io.enq.last.valid=0 则
//如果grant.last=1,说明除了最后一个producer之外,其它所有producer的valid都是0,当然!grant.last=false,最终io.deq.valid=false
//如果grant.last=0,说明倒数第二个或倒数第三/第四等producer的valid总有一个或多个=1,所以io.deq.valid=1,从逻辑角度来看下游收到valid也没有错,此时要分析上面数据是否对应(最小序号的producer的数据)
io.deq.valid := !grant.last || io.enq.last.valid
//总结:当有多个有效producer时,只有序号最小的producer的数据才能输出
}

joe 发表于 2021-9-12 23:46:35

class MyArbiterTester(m:JArbiter) extends PeekPokeTester(m) {
poke(m.io.enq(0).valid,false)
poke(m.io.enq(1).valid,false)
poke(m.io.deq.ready,false)
println(s"启动:")
println(s"\tenq(0).ready=${peek(m.io.enq(0).ready)}, enq(1).ready=${peek(m.io.enq(1).ready)}")
println(s"\tenq(0).valid=${peek(m.io.enq(0).valid)}, enq(1).ready=${peek(m.io.enq(1).valid)}")
poke(m.io.enq(0).bits,18) //producer0的数据先输入在此,不变动
poke(m.io.enq(1).valid,true) //producer1的valid有效
poke(m.io.enq(1).bits,17)
poke(m.io.deq.ready,true)//下游发出ready
println(s"producer1有效:")
println(s"\tenq(0).ready=${peek(m.io.enq(0).ready)}, enq(1).ready=${peek(m.io.enq(1).ready)}")
println(s"deq.valid=${peek(m.io.deq.valid)},deq.bits=${peek(m.io.deq.bits)}")
poke(m.io.enq(0).valid,true)

println(s"producer1和2都有效:")
println(s"\tenq(0).ready=${peek(m.io.enq(0).ready)}, enq(1).ready=${peek(m.io.enq(1).ready)}")
println(s"deq.valid=${peek(m.io.deq.valid)},deq.bits=${peek(m.io.deq.bits)}")
poke(m.io.enq(1).valid,false)
println(s"producer0有效:")
println(s"\tenq(0).ready=${peek(m.io.enq(0).ready)}, enq(1).ready=${peek(m.io.enq(1).ready)}")
println(s"deq.valid=${peek(m.io.deq.valid)},deq.bits=${peek(m.io.deq.bits)}")

}
class DownTickerSpec extends ChiselFlatSpec {
"JArbiter" should "pass" in {
    iotesters.Driver(()=>new JArbiter(UInt(8.W),2))(t => new MyArbiterTester(t))
}
}
测试打印如下:
启动:
        enq(0).ready=0, enq(1).ready=0
        enq(0).valid=0, enq(1).ready=0
producer1有效:
        enq(0).ready=1, enq(1).ready=1
deq.valid=1,deq.bits=17
producer1和2都有效:
        enq(0).ready=1, enq(1).ready=0
deq.valid=1,deq.bits=18
producer0有效:
        enq(0).ready=1, enq(1).ready=0
deq.valid=1,deq.bits=18
页: [1]
查看完整版本: 仲裁器Arbiter(3)