risc-v中文社区

 找回密码
 立即注册
查看: 1131|回复: 1

[原创] chisel RegFifo源码及测试源码解析

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2021-9-8 09:06:09 | 显示全部楼层 |阅读模式
import chisel3._
import chisel3.util._
class FifoIO[T <: Data](private val gen:T) extends Bundle { //一个混合信号Bundle
  val enq = Flipped(new DecoupledIO[T](gen))   //DecoupledIO-->>ReadyValidIO-->>Bundle  是带有valid和ready的复杂信号  Flipped则将bits,ready和valid信号切换方向
  val deq = new DecoupledIO[T](gen) //deq是正常的bits,ready,valid方向
}
abstract class Fifo[T <: Data](gen:T,depth:Int) extends Module {  //带有valid和ready的FIFO,并且有二个“group”,一个是enq,它的对方是Master,一个是deq,它的对方是Slave
  val io = IO(new FifoIO[T](gen))
  assert(depth > 0,"Number of buffer elements needs to be larger than 0")  //测试判断depth必须要大于0
}

import chisel3._
import chisel3.util._
//FIFO with read and write pointer using dedicated registers as memory
class RegFifo[T <: Data](gen:T,depth:Int) extends Fifo(gen,depth) {
  def counter(depth: Int,incr: Bool): (UInt,UInt) = {
    val cntReg = RegInit(0.U(log2Ceil(depth).W)) //log2Ceil(n:BigInt)求log2的值且向上取整 log2Ceil(1)->0,log2Ceil(2)->1,log2Ceil(3)->2,log2Ceil(4)->2,log2Ceil(5)->3
    //要想获取n值所对应的bit数量则log2Ceil(n+1)  4-->0100->3bit  log2Ceil(4+1)=3
    //cntReg表示深度depth-1值所需要的bit位数
    val nextVal = Mux(cntReg === (depth - 1).U,0.U,cntReg + 1.U) //达到最大深度则复位,否则下次值为cntReg加1
    when (incr) {
      cntReg := nextVal  //只要没有达到最大深度,每次加1
    }
    (cntReg,nextVal) //以元组类型返回计数器值以下一次值
  }
  val memReg = Reg(Vec(depth,gen)) //深度为depth的向量
  val incrRead = WireInit(false.B)
  val incrWrite = WireInit(false.B)
  val (readPtr,nextRead) = counter(depth,incrRead) //返回的元组中的readPtr和nextRead都是Reg硬件类型???
  val (writePtr,nextWrite) = counter(depth,incrWrite)

  val emptyReg = RegInit(true.B) //FIFO空标志
  val fullReg = RegInit(false.B) //FIFO满标志

  when(io.enq.valid && !fullReg) { //收到MASTER发来的valid,且FIFO不满
    memReg(writePtr) := io.enq.bits //writePtr指针所指的FIFO缓冲区的数据被设置为MASTER发来的数据 只有寄存器mmemReg是时序电路需要时钟上升沿(默认设置),其它是组合逻辑电路
    emptyReg := false.B  //需要清空 空标志位
    fullReg := nextWrite === readPtr //写指针和读指针相同时则置 满标志
    incrWrite := true.B  //启动写计数
  }
  when(io.deq.ready && !emptyReg) { //返回数据给MASTER的控制相关逻辑
    fullReg := false.B //清满标志位
    emptyReg := nextRead === writePtr //读写指针相等时则为空
    incrRead := true.B //启动读计数
  }
  io.deq.bits := memReg(readPtr) //根据读指针将缓冲区对应位置的数据返回
  io.enq.ready := !fullReg  //output出准备好信号 只要不满
  io.deq.valid := !emptyReg //只要不空 output类型的deq.valid就发出指标
}

回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-9-8 09:08:21 | 显示全部楼层
import chisel3._
import chisel3.iotesters._
import com.joe.stu.RegFifo
class FIFOTest(dut:RegFifo[UInt]) extends PeekPokeTester (dut){
  poke(dut.io.enq.bits.asUInt(),0xab) //为什么要asUnit()??????
  poke(dut.io.enq.valid,0) //io.enq端口收到valid无效
  poke(dut.io.deq.ready,0) //io.deq端口发出ready无效
  var ready = peek(dut.io.enq.ready) //1
  var valid = peek(dut.io.deq.valid) //0
  step(1)  //因为enq.valid=0所以0xab数据无效
  var ready1 = peek(dut.io.enq.ready) //1 io.enq.ready := !fullReg
  var valid1 = peek(dut.io.deq.valid) // 0  io.deq.valid := !emptyReg
  //write one value and expect it on the deq side
  poke(dut.io.enq.bits.asUInt(),0x123) //enq的数据输入0x123
  poke(dut.io.enq.valid,1) //enq的对面Master发出valid数据有效
  step(1) //每个step(1)表示一个时钟上升的到来
  //执行到此表示这个上升沿之后的电路
  poke(dut.io.enq.bits.asUInt(),0xab) //enq再次收到master发来的数据
  poke(dut.io.enq.valid,0) //但enq.valid却是无效指示  下次step(1)之后0xab数据不会存入FIFO
  step(12)  // 不管多少时钟,enq收到的数据都是无效的,deq应该会发出了deq.valid,但因为deq.ready没有收到指示所以deq.bits不会输出数据
  expect(dut.io.enq.ready,1) //只要fifo不满enq就会发出ready准备好接收数据指示
  expect(dut.io.deq.valid,1) //只要fifo不空deq就会发valid数据有效指示
  expect(dut.io.deq.bits.asUInt(),0x123) //第一个数据是0x123
  //read it out
  poke(dut.io.deq.ready,1) //deq对面是slave,所以deq的ready是input,deq的valid是output。所以此句ready置1表示slave已准备好接收数据
  step(1)
  expect(dut.io.deq.valid,0) //前面代码:enq.valid无效则enq不会收新的数据,而deq.valid=1数据有效了,deq.ready=1指示deq可以发数据,所以执行到本行代码时因FIFO是空所以deq.valid为0
  poke(dut.io.deq.ready,0) //deq对面的slave不再接收数据了
  step(1)
  var cnt = 1
  poke(dut.io.enq.valid,1) //enq的对面是MASTER,enq.valid是input,表示MASTER发来的数据有效
  for(i <- 0 until 12) {
    poke(dut.io.enq.bits.asUInt(),cnt.U) //模拟enq对面的MASTER发来的数据
    if(peek(dut.io.enq.ready) != 0) {  //enq发出ready,表示本enq接口已经将数据接收了 源码中:io.enq.ready := !fullReg
      //因为在ChiselFlatSpec的实现类中用的深度是depth=3,所以0 until 12次写数据到FIFO,只能是写3次,后面都full
      cnt += 1
    }
    step(1)
  }
  println(s"Wrote ${cnt-1} words")  //所以显示3
  expect(dut.io.enq.ready,0) //enq接收完数据之后ready复位为0吗? 源码中是:io.enq.ready := !fullReg  因depth=3,所以fullReg是满的,当然io.enq.ready不会发出指示了
  expect(dut.io.deq.valid,1) //源码中:io.deq.valid := !emptyReg
  expect(dut.io.deq.bits.asUInt(),1)//第一次写/读0x123之后读指针readPtr=1,writePtr=1,后面0 until12 多次写不会影响readPtr,但这12次中第一次写时数据是1,writePtr=1,而io.deq.bits:=memReg(readPtr),所以第一个数据1就会
  //now read it back
  var expected = 1
  poke(dut.io.enq.valid,0) //enq的对方即master数据无效
  poke(dut.io.deq.ready,1) //deq的对方即slave准备好接收数据
  for(i <- 0 until 12) {
    if(peek(dut.io.deq.valid) != 0) { //deq.valid为1 (deq发给slave的数据有效) 源码中:不空就valid有效, io.deq.valid :=!emptyReg
      expect(dut.io.deq.bits.asUInt(),expected)
      expected += 1
    }
    step(1)
  }
  //Do the speed test
  poke(dut.io.enq.valid,1) //enq的对方master发valid数据有效指示
  poke(dut.io.deq.ready,1) //deq的对方slave发ready数据已准备好接好指示
  cnt = 0
  expect(dut.io.deq.ready,1)
  for(i <- 0 until 100) {
    poke(dut.io.enq.bits.asUInt(),i.U) //enq端口接收到master发来的新数据
    if(peek(dut.io.enq.ready) != 0) { //enq发ready给master说明enq端口已接收好数据  前面已将deq.reaqdy置为1表示slave始终处于接收数据准备好状态,所以enq只要有数据都能发给deq的slave
        //其实每次进到这里,mmemReg(writePtr) := io.enq.bits并没有真正执行,只是到下面step(1)发出时钟上升沿之后才是表示真的执行完数据接收完毕
      cnt += 1
      println(s"cnt=$cnt")
    }
    step(1)
  }
  val cycles = 100.0 /cnt  //1
  println(s"$cnt words in 100 clock cycles,$cycles clock cycles per word")
  assert(cycles >= 0.99,"cannot be faster than on clock cycle per word")

}
class FIFOTestSpec extends ChiselFlatSpec {
  "FIFOTest" should "pass" in {
    iotesters.Driver(()=>new RegFifo(UInt(16.W),3))(t=>new FIFOTest(t))  //注意:测试FIFO的深度是3
  }
}

回复

使用道具 举报

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

本版积分规则



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

GMT+8, 2024-5-3 10:13 , Processed in 0.015253 second(s), 17 queries .

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

Copyright © 2018-2021, risc-v open source

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