Rocket-chip-RoCC(6)

本文详细解析了CharacterCount模块的工作原理及其实现细节,包括状态机流程、数据读取与匹配等关键技术点。

下面是对CharacterCount模块的具体实现进行说明。

3、CharacterCount

类CharacterCountExample,需要输入opcodes参数,里面使用new创建CharacterCountExampleModuleImp的对象。声明atlNode为TLClientNode的类,用于TileLink协议,TLClientNode应该是作为master方的,用于发起数据的请求 。

class  CharacterCountExample(opcodes: OpcodeSet)(implicit p: Parameters) extends LazyRoCC(opcodes) {
   
   
  override lazy val module = new CharacterCountExampleModuleImp(this)
  override val atlNode = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters("CharacterCountRoCC")))))
}

CharacterCountExampleModuleImp类中混入LazyRoCCModuleImp、HasL1CacheParameters和HasCoreParameters。

  • 声明cacheParams值,如果存在tileParams.icache(存在icache),则cacheParams为1,不然为None(val cacheParams = tileParams.icache.get这句含义我也不太确定),但可以知道的是CharacterCountExampleModuleImp类的下文没有使用到cacheParams的值,所以暂时先这样理解。如果有明确知道的请告诉我,我再修改补充,谢谢。
  • 声明私有的值blockOffset,是blockOffBits的值,而追下去可以找到:
def blockOffBits = lgCacheBlockBytes
def lgCacheBlockBytes = log2Up(cacheBlockBytes)
def cacheBlockBytes = p(CacheBlockBytes)
class WithCacheBlockBytes(linesize: Int) extends Config((site, here, up) => {
   
   
  case CacheBlockBytes => linesize
})
  • 也就是blockOffset的值可以通过WithCacheBlockBytes()来设定的。
  • 声明私有的值beatOffset,是以2为底(cacheDataBits/8)的对数。
  • 声明needle为寄存器,位宽为8。
  • 声明addr为寄存器,位宽为coreMaxAddrBits。
  • 声明count为寄存器,位宽为xLen。
  • 声明resp_rd为寄存器,位宽和io.resp.bits.rd一致,应该是5。
  • 声明addr_block为addr的某些位,应该是addr[31:6]。
  • 声明offset为addr的某些位,应该是addr[5:0]。
  • 声明next_addr为(addr_block+1)的值左移6位。
  • 声明状态机的状态,分别为s_idle、s_acq、s_gnt、s_check和s_resp。
  • 声明state寄存器,用于存储状态机的状态,初始值为s_idle。
  • 声明tl_out值,将与core tilelink总线连接的信号atlNode.out(0)赋给它,这里的edgesOut我理解为边缘触发的意思。
  • 将tl_out.d.bits各信号(tilelink的d组信号,有多个信号并非只有一个信号)赋值给gnt,即gnt为d组信号的组合。
  • 声明recv_data为寄存器,位宽为cacheDataBits。
  • 声明recv_beat为寄存器,位宽是以2为底(cacheDataBeats+1)的对数,且初始值为0。
  • 声明一维数组data_bytes,深度为cacheDataBits/8,i为深度减1。当i=0时,data_bytes[0]= recv_data(7,0),当i=1时,data_bytes[1]= recv_data(15,8),当i=2时,data_bytes[2]= recv_data(23,16),当i=3时,data_bytes[3]= recv_data(31,24)。
  • 声明zero_match信号组,当data_bytes[0] = 0,则zero_match[0]为1,当data_bytes[1] = 0,则zero_match[1]为1,当data_bytes[2] = 0,则zero_match[2]为1,当data_bytes[3] = 0,则zero_match[3]为1。
    声明needle_match信号组,当data_bytes[0] = needle,则needle_match[0]为1,当data_bytes[1] = needle,则needle_match[1]为1,当data_bytes[2] = needle,则needle_match[2]为1,当data_bytes[3] = needle,则needle_match[3]为1。
  • 声明first_zero的值,对zero_match信号组进行优先级解码。即当{zero_match[0],zero_match[1],zero_match[2],zero_match[3]}={1’b1,1’bx,1’bx,1’bx}时,first_zero=0;当{zero_match[0],zero_match[1],zero_match[2],zero_match[3]}={1’b0,1’b1,1’bx,1’bx}时,first_zero=1;当{zero_match[0],zero_match[1],zero_match[2],zero_match[3]}={1’b0,1’b0,1’b1,1’bx}时,first_zero=2;当{zero_match[0],zero_match[1],zero_match[2],zero_match[3]}={1’b0,1’b0,1’b0,1’bx}时,first_zero=3。
  • 声明chars_found的值,此值通过一系列操作得到。needle_match.zipWithIndex.map {},做的就是将needle_match信号组按index展开,然后进行{}里面的操作。
case (matches, i) =>
      val idx = Cat(recv_beat - UInt(1), UInt(i, beatOffset))
      matches && idx >= offset && UInt(i) <= first_zero
  • 因为needle_match会展开为各个信号,所以这里的matches就是指对应展开的needle_match_X(X=0/1/2/3),i就是index,可以为0/1/2/3。即needle_match_0和i=0进行一次val idx = Cat(recv_beat - UInt(1), UInt(i, beatOffset))和matches && idx >= offset && UInt(i) <= first_zero操作,以此类推。idx为信号拼接,分别采用recv_beat - UInt(1)和UInt(i, beatOffset)拼成。UInt(x,y),x数值,y为位宽。后面一行是条件判断。
  • PopCount()操作是将括号内的结果逐一相加。
  • 那么这段操作的意思是:
  1. 取i=0,matches=needle_match_0,得到idx0={recv_beat - UInt(1), UInt(0)},然后判断needle_match_0 && (idx0 >= offset) && (0 <= first_zero)的结果,并存入tmp0中。
  2. 取i=1,matches=needle_match_1,得到idx1={recv_beat - UInt(1), UInt(1)},然后判断needle_match_1 && (idx1 >= offset) && (1 <= first_zero)的结果,并存入tmp1中。
  3. 取i=2,matches=needle_match_2,得到idx2={recv_beat - UInt(1), UInt(2)},然后判断needle_match_2 && (idx2 >= offset) && (2 <= first_zero)的结果,并存入tmp2中。
  4. 取i=3,matches=needle_match_3,得到idx3={recv_beat - UInt(1), UInt(3)},然后判断needle_match_3 && (idx3 >= offset) && (3 <= first_zero)的结果,并存入tmp3中。
  5. 最后利用PopCount()操作,将tmp0,tmp1,tmp2和tmp3加起来,得到一个3bits的数据,赋给chars_found[2:0]。
    声明zero_found,其值为zero_match_0 || zero_match_1 || zero_match_2 || zero_match_3。
  • 声明finished为单bit寄存器。
  • 当state为s_idle时,io.cmd.ready输出为1。
  • 当state为s_resp时,io.resp.valid输出为1。
  • io.resp.bits.rd等于 resp_rd,io.resp.bits.data等于count。
  • 当state为s_acq时,tl_out.a.valid输出为1。
  • tl_out.a.bits是a组多个信号的捆绑,使用edgesOut.Get(这里的Get是TileLink的一种操作方式,可以简单理解为数据获取,是通过A组信号进行的,与缓存操作没有关系,具体说明大家可以查看TileLink协议)对不同信号进行赋值。edgesOut.Get在tilelink/Edges.scala中定义,代码如下。
 // Accesses
  def Get(fromSource: UInt, toAddress: UInt, lgSize: UInt) = {
   
   
 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值