risc-v中文社区

 找回密码
 立即注册
查看: 1252|回复: 0

[原创] chisel对列---Queue(2)

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2021-9-15 20:43:41 | 显示全部楼层 |阅读模式
Queue源码分析:
class Queue[T <: Data](gen: T,
                       val entries: Int,
                       pipe: Boolean = false,
                       flow: Boolean = false)
                      (implicit compileOptions: chisel3.CompileOptions)
    extends Module() {
  require(entries > -1, "Queue must have non-negative number of entries")
  require(entries != 0, "Use companion object Queue.apply for zero entries")
  val genType = if (compileOptions.declaredTypeMustBeUnbound) {
    requireIsChiselType(gen)
    gen
  } else {
    if (DataMirror.internal.isSynthesizable(gen)) {
      chiselTypeOf(gen)
    } else {
      gen
    }
  }

  val io = IO(new QueueIO(genType, entries))

  private val ram = Mem(entries, genType)//写入/顺序/同步写 是在时钟上升沿,读是组合逻辑/异步 控制
  private val enq_ptr = Counter(entries)
  private val deq_ptr = Counter(entries)
  private val maybe_full = RegInit(false.B) //maybe_full表示enq接口上上游发来的valid和数据已被本queue接收了,并且返回ready给上游了,一句话:enq数据输入有效且已写入本queue

  private val ptr_match = enq_ptr.value === deq_ptr.value //enq的访问指针和deq的访问指针是否相等 有可能enq_ptr追上deq_ptr,也有可能deq_ptr出对列追上end_ptr
  //从软件QUEUE思想来看,主要是有一个maybe_full的判断
  private val empty = ptr_match && !maybe_full //enq和deq的访问指针相等且可能满标志false则队列空
  private val full = ptr_match && maybe_full //enq和deq的访问指针相等且可能满标志true则表示对列queue是满的 也就是说只有enq入对列追上deq_ptr时才会满
  private val do_enq = WireDefault(io.enq.fire()) //object ReadyValidIO中的隐式类转换(implicit xxx(target:ReadyValidIO)的方法:def fire():Bool = target.ready && target.valid
  private val do_deq = WireDefault(io.deq.fire()) //类似io.enq.fire(),查看deq的valid发出数据有效和下游发来的准备好接收数据ready是否同时都有效

  when (do_enq) { //enq接口的ready和valid同时有效即入对列
    ram(enq_ptr.value) := io.enq.bits //将数据保存在对列的Mem中,位置是enq_ptr指针所指位置
    enq_ptr.inc() //保存一次数据就需要将指针后移一次
  }
  when (do_deq) {//deq接口的ready和valid同时有效即出对列
    deq_ptr.inc() //将deq_ptr指针加一次 输出数据io.deq.bits始终是取ram(deq_ptr.value)的值
  }
  when (do_enq =/= do_deq) { //问题:如果两边的ready和valid都同时有效???其实就是上面两个when同时执行
    maybe_full := do_enq  //为什么do_enq =/= do_deq就表示满???不是同时入对列和出对列就将maybe_full可能满置为入对列状态与否
        //1)do_enq=1 do_deq=0
                //正在入对列但没有出对列 有可能满,所以需要更新标志为入对列状态
        //2)do_enq=1 do_deq=1
                //正在入对列同时也在出对列 在一个时钟上升沿可以同时入对列和出对列,所以不需要更新状态标志
        //3)do_enq=0 do_deq=0
                //没有入对列但也没有出对列,不需要更新状态
        //4)do_enq=0 do_deq=1
                //没有入对列,但有出对列,将状态更新为入对列状态即值为0,表示没有满,也没有错
  }

  io.deq.valid := !empty //不空则对列Queue的deq就发出数据有效valid
  io.enq.ready := !full //对列不满则enq就发ready给上游Master
  io.deq.bits := ram(deq_ptr.value) //数据始终输出deq_ptr指针所指数据 ram的读是组合逻辑控制/异步

  if (flow) { //flow模式,数据能在同一个周期输出
    when (io.enq.valid) { io.deq.valid := true.B } //当enq的valid有效时,deq的valid也有效
    when (empty) {
      io.deq.bits := io.enq.bits //前面有代码说明deq.bits始终输出deq_ptr指针所指的数据,此处代码说明当空时则输出当前enq的bits数据
      do_deq := false.B //前面代码说do_deq的值=deq.ready & deq.valid
      when (io.deq.ready) { do_enq := false.B } //空且deq.ready已收到则do_enq :=false.B
    }
  }

  if (pipe) { //pipe管道模式,全吞吐
    when (io.deq.ready) { io.enq.ready := true.B }//前面说了enq.reaqdy:=!full,但当pipe模式,deq收到ready时则enq的ready也要发出给enq的上游Master
  }

  private val ptr_diff = enq_ptr.value - deq_ptr.value  //读写指针差值 根据下面分析总结:因为ptr_diff其实是无符号类型,
  if (isPow2(entries)) { //entries数值是2的整数次幂
    io.count := Mux(maybe_full && ptr_match, entries.U, 0.U) | ptr_diff  //maybe_full && ptr_patch表示真的满了
        //在低版本chisel中此句是:io.count := Cat(myabe_full && ptr_match,ptr_diff)  //我的分析:Cat之后多了一位,如果多的这一位是1??? 8bit,8bit=254=0xfE,现在0x1fe=>转换到8bit=>0xfe
        //entries.U或0.U(不管怎么样都是无符号数) 按位或 ptr_diff分析:假设entries=0b10000=16
        //1)enq_ptr.value > deq_ptr.value 说明入比出的数量多,所以差值(范围是0~0b1111)代表queue中的数据量还有多少 这个值再 按位或一个0或0b10000则值不变
        //2)enq_ptr.value < deq_ptr.value 有没有这种可能?除非enq_ptr回绕了,可不可能回绕呢?达到0b1111之后再+1则就会是0了(如果不是2的整数次幂Counter中达到n-1则+1也是0)
        //                                                                  总结:因为queue的缓冲区逻辑上是一个环形结构,所以有可能enq_ptr<deq_ptr 那么这种情况下Mux(maybe_full & ptr_match,...)输出0.U,
        //                                                                  而ptr_diff相当于=enq_ptr-deq_ptr+2^N(N代表位宽),这样的结果其实ptr_diff并不因为+2^N而值有所变化,所以ptr_diff还是表示缓冲区中数据量的多少
        //3)enq_ptr.value == deq_ptr.value 有可能enq_ptr入对列很多追上deq_ptr,当然也有可能环回之后deq_ptr出对列追上enq_ptr
  } else {//entries不是2的整数次幂
    io.count := Mux(ptr_match,//两个指针相等
                    Mux(maybe_full, //可能满
                      entries.asUInt, 0.U),
                    Mux(deq_ptr.value > enq_ptr.value,//出对列指针大于入对列指针
                      entries.asUInt + ptr_diff, ptr_diff))
        //entries不是2的整数次幂 io.count的结果是Mux
        //1)当两个指针相等时,值又是一个Mux,此时根据maybe_full可能满(只可能发现在:有且仅有入对列情况下才会maybe_full==1)来区分
        //        a)确实是满了,取值为entries
        //        b)deq_ptr出队列追上enq_ptr,确实是空了,取值为0
        //2)当两个指针不相等时,根据出队列指针是否大于入队列指针区分
        //        a)deq_ptr>enq_ptr即出队列指针大于入队列指针,取值entries.asUInt + ptr_diff,entries=7=0b111  ptr_diff=2-4=0b010-0b100=0b110=>sint=-2 uint=6 则0b111+0b110=0b101=>uint=5 也就是说数据index是4,5,6(也是0),1,2共5个
        //        b)deq_ptr<enq_ptr即出队列指针小于入队列指针,取值ptr_diff entries=7,ptr_diff=4-2=>实际数量
  }
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-5-3 09:05 , Processed in 0.018900 second(s), 17 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表