十二 · 2023年08月24日

pipeline高端玩法(二)——成员一览

✎ 编 者 按 

本篇先一览pipeline中所出现的五个基本要素

Stageable、StageableKey

stageable、StageableKey是最整个pipeline中的基本数据类型元素:

object Stageable{  def apply[T <: Data](gen : => T) = new Stageable(gen)  def apply[T <: Data](gen : HardType[T]) = new Stageable(gen.craft())}class Stageable[T <: Data](gen : => T) extends HardType(gen) with Nameable {}case class StageableOffset(val value : Any)object StageableOffsetNone extends StageableOffset(null)case class StageableKey(stageable: Stageable[Data], key : Any){  override def toString = {    var name = stageable.getName()    if(key != null) name = name + "_" + key    name  }}

对于Stageabel,我们可以传入任何SpinalHDL下面定义的隶属于Data的类型,不同于我们在电路里生命一个电路对象:

val a=UInt(8 bits)

此时,电路对象是立即生命存在的,如果我们不对他进行赋值会报错。而:

val b=Stageable(UInt(8 bits))

此时b并未真正生成电路对象,即使不对他进行赋值也并不会报错。

对于StageableKey,字如其名,其主要用作Key使用,用于建立Stageable映射时的key使用。

Stage

借用上一篇文章中的图:

image.png

Stage主要用于实现pipeline的功能实现。那么Stage则定义了这些功能的输入、输出:

val input = new {      val valid = Bool()      var ready : Bool = null    }val output = new Area {      val valid = Bool()      var ready : Bool = null    }

以及一些列的内部保存变量:

val arbitration = new {      var isRemoved : Bool = null      var isFlushed : Bool = null      var isThrown : Bool = null      var isForked : Bool = null      var isFlushingNext : Bool = null      var isFlushingRoot : Bool = null      var isHalted : Bool = null      var isHaltedByOthers : Bool = null      var propagateReady = false    }    val request = new {      val halts = ArrayBuffer[Bool]()      val throws = ArrayBuffer[Bool]()      val throwsRoot = ArrayBuffer[Bool]()      val forks = ArrayBuffer[Bool]()      val spawns = ArrayBuffer[Bool]()      val flush = ArrayBuffer[Bool]()      val flushRoot = ArrayBuffer[Bool]()      val flushNext = ArrayBuffer[Bool]()    }    val stageableToData = mutable.LinkedHashMap[StageableKey, Data]()    val stageableOverloadedToData = mutable.LinkedHashMap[StageableKey, Data]()    val stageableResultingToData = mutable.LinkedHashMap[StageableKey, Data]()    val stageableTerminal = mutable.LinkedHashSet[StageableKey]()

这些变量暂时看不懂也没关系,后续会通过例子逐一进行讲解。通过这些内部变量,pipeline在构建电路时处理Stage之间的依赖关系。

Connection

顾名思义,Connection用于负责Stage之间的连接关系,即其负责处理上一级Stage的output与下一级Stage的input之间的连接。在Lib中,Connection定义了四种连接关系:

  • DIRECT:类似一两个Stream直接相连
  • M2S:类似于Stream中的M2SPipe,对valid、payload进行打拍输出
  • S2M:类似于Stream中的S2MPipe,对于ready进行打拍输出

乍看其实现你会发现其中有好多参数一时不知用途,无妨,先放一放,回头逐一讲解。

pipeline

Pipeline中核心是一个build函数。当我们描述完各Stage的功能之后,通过调用Pipeline的build函数即可构建整个的流水线电路,其也是整个pipelines构建的核心,直接上来看可能会略觉麻烦,后面通过例子一点点来理解。

第一个例子

先有个概念之后,我们再来一个pipeline的第一个例子:

case class demo() extends Component{  val io=new Bundle{    val data_in=slave Flow(UInt(8 bits))    val data_out=master Flow(UInt(8 bits))  }  noIoPrefix()  val pip=new Pipeline{    val paylaod=Stageable(UInt(8 bits))    val stage0=new Stage{      import internals._      input.valid:=io.data_in.valid      this(paylaod):=io.data_in.payload    }    val stage1=new Stage(Connection.M2S())    val stage2=new Stage(Connection.M2S()){      io.data_out.valid:=internals.output.valid      io.data_out.payload:=this(paylaod)    }  }  pip.build()}

这个例子的功能是将Flow data_in打两拍输出给data_out,一个最简单的流水线结构,不牵涉到任何halt、flush等相关操作。如果你看不懂,可以先看它生成的RTL代码:

module demo (  input data_in_valid,  input [7:0] data_in_payload,  output data_out_valid,  output [7:0] data_out_payload,  input clk,  input reset);  reg        [7:0] pip_stage1_paylaod;  reg        [7:0] pip_stage2_paylaod;  wire       [7:0] pip_stage0_paylaod;  wire                pip_stage0_valid;  reg                 pip_stage1_valid;  reg                 pip_stage2_valid;  assign pip_stage0_valid = data_in_valid;  assign pip_stage0_paylaod = data_in_payload;  assign data_out_valid = pip_stage2_valid;  assign data_out_payload = pip_stage2_paylaod;  always @(posedge clk or posedge reset) begin    if(reset) begin      pip_stage1_valid <= 1'b0;      pip_stage2_valid <= 1'b0;    end else begin      pip_stage1_valid <= pip_stage0_valid;      pip_stage2_valid <= pip_stage1_valid;    end  end  always @(posedge clk) begin    pip_stage1_paylaod <= pip_stage0_paylaod;    pip_stage2_paylaod <= pip_stage1_paylaod;  endendmodule

一眼看去,可能觉得很怪,有很多疑问。比如说data_in的valid信号是如何从stage0传输到stage1的等等。下一篇我们将会以这个最简单的例子,来仔细讲解这里的pipeline是如何生效的。

☆ END ☆

作者:玉骐
原文链接:Spinal FPGA
微信公众号:
 title=

推荐阅读

更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
1581
内容数
133
用SpinalHDL提升生产力
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息