20201225 SpinalHDL中实现简单握手(Stream类部分用法梳理)

本文介绍了SpinalHDL的lib中Stream类,它继承自Bundle类,包含valid、ready、payload三个信号,可构成简单握手接口。文中阐述了Stream的方向定义、实现简单握手时序的方法,还探讨了valid赋值条件不止握手成功以及payload经组合逻辑传递时的处理方式,强调阅读源码的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Stream类简介

SpinalHDL的lib中提供了一个Stream类,这个类继承自SpinalHDL的Bundle类,支持Master/Slave,携带数据,因此主要包括三个信号:valid、ready、payload。
这三个信号可以构成最简单的握手接口,满足大部分情况下的module间交互。接下来介绍其中的一些方法,与Verilog中写简单握手的方法一一对应。

1.1 Stream的方向定义

  val io = new Bundle{
    val data_in  = slave Stream(UInt(8 bits))
    val data_out = master Stream(UInt(8 bits))
  }

上图为一个流水级寄存器的io信号示例,data_in为模块输入,则对于简单握手协议来说本模块是接收数据的slave,因此data_in作为一个Stream类包含的三个信号及其方向分别为:valid(in)、ready(out)、payload(in)。
反之,data_out为模块输出,其包含的三个信号及其方向分别为:valid(out)、ready(in)、payload(out)。

1.2 Stream实现简单握手时序

在这里插入图片描述
一个典型的简单握手时序如图所示,上级的valid与payload同时产生,直至下级ready拉高,实现上级valid与下级ready同时为高时握手成功。valid只有在握手成功的下一拍才允许拉低,若握手失败,则valid需保持为高。
在这里插入图片描述
在这里插入图片描述
在SpinalHDL的document中,给出了上述的几种连接方式。那么在SpinalHDL中,data_in和data_out应如何连接才能实现上图的握手时序呢?
由于要实现寄存器打拍的效果,那么必须选择latency为1的连接方式。①和③两种latency为0的连接方式显然不能满足。其中x << y或y >> x就相当于是使用Verilog中的assign赋值语句。
而②可以实现打拍的连接方式,并且自动完成了本级valid、payload的时序逻辑赋值和ready的组合逻辑赋值。<-<看似一个赋值保留字,其实是Stream的一个方法:

/*Connect that to this. The valid/payload/ready path are cut by an register stage */
/* Connect that to this. The valid/payload path are cut by an register stage */
  def <-<(that: Stream[T]): Stream[T] = {
    this << that.stage()
    that
  }

/* Connect this to that. The valid/payload path are cut by an register stage */
  def >->(into: Stream[T]): Stream[T] = {
    into <-< this
    into
  }

顺藤摸瓜,继续看看源码中的stage方法:

  /** Connect this to a valid/payload register stage and return its output stream
  */
  def stage() : Stream[T] = this.m2sPipe()

  //! if collapsBubble is enable then ready is not "don't care" during valid low !
  def m2sPipe(collapsBubble : Boolean = true,crossClockData: Boolean = false, flush : Bool = null): Stream[T] = {
    val ret = Stream(payloadType).setCompositeName(this, "m2sPipe", true)

    val rValid = RegInit(False).setCompositeName(this, "m2sPipe_rValid", true)
    val rData = Reg(payloadType).setCompositeName(this, "m2sPipe_rData", true)
    if (crossClockData) rData.addTag(crossClockDomain)

    this.ready := (Bool(collapsBubble) && !ret.valid) || ret.ready

    when(this.ready) {
      rValid := this.valid
      rData := this.payload
    }

    if(flush != null) rValid clearWhen(flush)

    ret.valid := rValid
    ret.payload := rData
    ret
  }

可以发现,最后摸到了m2sPipe方法,这对应了表格中描述的connect y to x through a m2sPipe.
该方法返回的是一个Stream对象,其中valid和payload被定义为了Reg类型,并且只有在下级ready拉高时,才会将上级valid与payload赋值给下级。而下级ready的赋值逻辑则有两种情况:① 下级valid为低,即下级在当前拍处于无数据状态;② 下下级ready为高,代表下级在下一拍可以接受新数据。

1.3 若valid的赋值条件不仅仅是握手成功

在一些情况中,下级valid的赋值条件不仅仅是上级与下级握手成功,还有一些特定的逻辑。例如多对一握手与一对多握手,可能需要改变valid和ready的赋值条件。那么,也可以使用Stream中的方法来实现:

/** Block this when cond is False. Return the resulting stream */
  def continueWhen(cond: Bool): Stream[T] = {
    val next = new Stream(payloadType)
    next.valid := this.valid && cond
    this.ready := next.ready && cond
    next.payload := this.payload
    return next
  }

使用示例如下:

data_out <-< data_in.continueWhen(cond)

1.4 若payload经过组合逻辑而非直接向下传递

在流水线结构中,两个流水级寄存器之间通常存在组合逻辑,实现特定的功能。那么此时两个模块之间虽然valid-ready信号还是正常传递,但payload就不是简单地将上级传递来的payload向下传递了。这种情况恐怕是最常见的,一开始在document中没有找到合适的方法,差点弃用了Stream。

但是,阅读源码之后发现了一个可以实现这种需求的方法:

/** Replace this stream's payload with another one */
  def translateWith[T2 <: Data](that: T2): Stream[T2] = {
    val next = new Stream(that).setCompositeName(this, "translated", true)
    next.arbitrationFrom(this)
    next.payload := that
    next
  }

示例如下:

data_out <-< data_in.translateWith(anotherData)

原理是valid与ready仍然按照m2sPipe方法传递,而payload则传递另外的data,如经过组合逻辑之后的data。

至此,本文梳理了有关SpinalHDL lib中Stream的部分方法,用来实现常用的简单握手。上文介绍的方法已经可以覆盖许多简单握手的使用场景,但是对Stream的挖掘依然远远不够。SpinalHDL lib中还有许多有用的类可以使用,关键是在阅读document之外,多多阅读源码,会有不错的发现!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值