0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个简单的pipeline是如何构建起来的?

Spinal FPGA 来源:Spinal FPGA 2023-08-12 11:18 次阅读

续接上文,这一次我们来详细了解下一个简单的pipeline是如何构建起来的

》最简单的流水线

书接上文,一个最简单的流水线例子,这里对data_in打两拍做输出:

b5c6ab92-38b3-11ee-9e74-dac502259ad0.png

我们逐行分析pipeline里每一行代码都干了什么。

》Stage分析

在第八行我们声明了一个Stageable变量,如前文所述,Stageable变量并不会立即产生电路对象。

代码第9行,我们创建一个Stage,Stage中的下面的代码会通过调用Pipeline中的addStage函数向Pipeline中的StageSet添加当前Stage:

if(_pip != null) {
_pip.addStage(this)
}

在stage0中,第11行用io.data_in.valid驱动stage0中的internals.input.valid。而在第12行,从左向右看,this(payload)函数会调用Stage的apply函数:

def apply[T <: Data](key : Stageable[T]) : T = {
    apply(StageableKey(key.asInstanceOf[Stageable[Data]], null)).asInstanceOf[T]
}

其进一步调用:

defapply(key : StageableKey):Data = {
internals.stageableToData.getOrElseUpdate(key, ContextSwapper.outsideCondScope{
key.stageable()//.setCompositeName(this, s"${key}")
})
}

可以看到,这里的主要作用是一StageableKey作为Key查询internals.stageableToData中是否包含该key,如果有则返回其value,否则创建该key-value,并将value返回。

最终这里会返回一个UInt(8 bits)电路对象。并将io.data_in.payload赋值给该电路对象。此时,stage0中的internals.stageableToData中包含一个元素。

在第14行,我们创建了stage1,其例化时传入了Connection,其会调用:

 if(_pip != null) {
_pip.addStage(this)
}

def chainConnect(connection: ConnectionLogic): Unit ={
_pip.connect(_pip.stagesSet.takeWhile(_ != this).last, this)(connection)
}

def this(connection: ConnectionLogic)(implicit_pip: Pipeline) {
this()
chainConnect(connection)
}

这里的的调用关系是:

当前Pipeline的StageSet添加当前Stage元素

通过调用pipeline的connect函数和StageSet中的最后一个元素建立连接关系:

defconnect(m : Stage, s : Stage)(logics : ConnectionLogic*) = {
val c = new ConnectionModel
connections += c
c.m = m
c.s = s
c.logics ++= logics
c
}

在connection里,创建了一个连接关系ConnectionModel,用于连接stage0中的output接口和stage1的input接口,采用M2S()方式进行连接。

同样,15行同样stage2的创建亦如此。

代码第16行则是用stage2的output.valid驱动data_out的valid输出。而代码第17行通过同样的方式创建了一个UInt(8 bits)电路对象,用来驱动data_out.payload。此时stage2中的internals.stageableToData中包含一个元素。

最后调用pipeline的build函数进行流水线的搭建。

》build分析

我们按片段来分析build中的代码:

b6048a66-38b3-11ee-9e74-dac502259ad0.png

首先,上面第一行代码将connection中的stage添加到stageSet。在执行该行之前,stageSet中包含了stage0、stage1、stage2三个元素,而connection按照描述进行map后得到两个元素:(stage0,stage1),(stage1,stage2)。由于stageSet为LinkedHashSet,故执行完后依然是(stage0,stage1,stage2)。

第二行代码则是获取带有slave驱动的stage,这里即stage0(驱动stage1)、stage1(驱动stage2)。

第三行则是获取没有slave驱动的stage,这里只有stage2。

第7~8行则是分别建立每个stage都是由谁驱动的映射存储至stageMaster、而stage input的驱动逻辑则存储至stageDriver。同时这里也限制了每个stage,最多只能由一个驱动逻辑。处理完后,stageMaster、stageDriver中存储的元素分别为:

stageMaster:
stage0->ArrayBuffer()
stage1->ArrayBuffer(stage0)
stage2->ArrayBuffer(stage2)
stageDriver:
stage1->Connection(m=stage0,s=stage1,logic=M2S)
stage2->Connection(m=stage1,s=stage2,logic=M2S)

代码13~16行由于我们并没有使用到stageableResultingToData,这里可以暂时忽略,等后续章节再进行讨论。

val clFlush = mutable.LinkedHashMap[ConnectionLogic, Bool]()
val clFlushNext = mutable.LinkedHashMap[ConnectionLogic, Bool]()
val clFlushNextHit = mutable.LinkedHashMap[ConnectionLogic, Bool]()
val clThrowOne = mutable.LinkedHashMap[ConnectionLogic, Bool]()
val clThrowOneHit = mutable.LinkedHashMap[ConnectionLogic, Bool]()

这里声明的变量我们这里都是用不上,可以暂时先不进行关注。

def propagateData(key : StageableKey, stage : Stage): Boolean ={
if(stage.internals.stageableTerminal.contains(key)) returnfalse
stage.stageableToData.get(key) match {
caseNone => {
val hits = ArrayBuffer[Stage]()
for(m <- stageMasters(stage)){
        if(propagateData(key, m)){
          stage.apply(key) //Force creation
          hits += m
        }
      }
      hits.size match {
        case 0 => false
case1=> true
case2=> PendingError(s"$key at $stage has multiple drivers : ${hits.mkString(",")}"); false
}
}
caseSome(x) => true
}
}

for(stage <- stagesSet){
  for(key <- stage.stageableToData.keys){
    for(m <- stageMasters(stage)) {
      propagateData(key, m);
    }
  }
}

这里就有点儿意思了对于StageSet中的每个stage中stageableToData中的每个元素,都会调用propgateData函数进行处理。我们不妨先来看看此时各stage中的stageableToData中的元素:

stage0:
StageableKey(payload,null)->UInt(8)
stage1:
null
stage2:
StageableKey(payload,null)->UInt(8)

由于stage0没有master,无需考虑,stage1中stageableToData为空,也可以跳过。那么来看stage2,此时调用propagateData传入的参数为:

key=StageableKey(payload,null)->UInt(8)
m=stage1

进入propagateData函数,由于stageableTerminal我们并未使用,继续往下看,由于stage1中的stageableToData为空,故这里match匹配为None,此时会看stage1的master端口,此时嵌套调用propagateData,传入参数为:

key=StageableKey(payload,null)->UInt(8)
m=stage0

由于stage0中的stageableToData包含该key,那么此时返回true退出,再次回到头次调用的处理上。

由于嵌套返回true,那么此时会在stage1上调用apply函数,为stage1插入一个stageableKey。最终,各stage中stageableToData的结果为:

stage0:
StageableKey(payload,null)->UInt(8)
stage1:
StageableKey(payload,null)->UInt(8)
stage2:
StageableKey(payload,null)->UInt(8)

看到这里,是不是明白了一些门道了呢?pipeline自动帮我们补齐了stage1中所依赖的元素,完成了payload从stage0到stage2中的传输元素补齐。

对于propagateRequirements函数,这里我们并用不上,这里我们先无需关注。

接下来看时internal connections:

b6457df0-38b3-11ee-9e74-dac502259ad0.png

这里我们近会用到s.output.valid:=s.input.valid,即将每个stage中的internal.output.valid由internal.input.valid进行驱动。

剩下的就是stage之间的连接了:

for(c<- connections){
  val stageables = (c.m.stageableToData.keys).filter(key => c.s.stageableToData.contains(key) && !c.m.stageableTerminal.contains(key))
var m= ConnectionPoint(c.m.output.valid, c.m.output.ready, stageables.map(c.m.outputOf(_)).toList)
for((l, id) <- c.logics.zipWithIndex){
    val s = if(l == c.logics.last)
      ConnectionPoint(c.s.input.valid, c.s.input.ready, stageables.map(c.s.stageableToData(_)).toList)
    else {
      ConnectionPoint(Bool(), (m.ready != null) generate Bool(), stageables.map(_.stageable.craft()).toList)
    }
    val area = l.on(m, s, clFlush(l), clFlushNext(l), clFlushNextHit(l), clThrowOne(l), clThrowOneHit(l))
    if(c.logics.size != 1)
      area.setCompositeName(c, s"level_$id", true)
    else
      area.setCompositeName(c, true)
    m = s
  }
}

对于每个connection,首先是将master端和slave端stage共有的stageableToData给筛选到stageables中去,这里对应的为:

Connection(m=stage0,s=stage1,logic=M2S):
StageableKey(payload,null)
Connection(m=stage1,s=stage2,logic=M2S):
StageableKey(payload,null)

接着创建ConnectionPoint,对应的paylaod即为StageabelKey所对应的电路对象,也就意味着:

Connection(m=stage0,s=stage1,logic=M2S): 
m=ConenctionPoint(stage0.out.valid,stage0.out.ready,stage0.stageableToData(StageableKey(payload,null)))
s=ConenctionPoint(stage1.in.valid,stage1.in.ready,stage1.stageableToData(StageableKey(payload,null)))
Connection(m=stage1,s=stage2,logic=M2S):
m=ConenctionPoint(stage1.out.valid,stage1.out.ready,stage1.stageableToData(StageableKey(payload,null)))
s=ConenctionPoint(stage2.in.valid,stage2.in.ready,stage2.stageableToData(StageableKey(payload,null)))

最终,调用M2S.on创建stage之间的连接关系:

b6a44b0a-38b3-11ee-9e74-dac502259ad0.png

这里我们只用到6~7行,建立起各stage之间的连接关系。

至此!完成整个流水线的创建。

》写在最后

通过本篇,分析了一个简单的流水线在Pipeline中的创建实现,后续将陆续进行更加复杂流水线的Demo及其背后自动实现原理。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    19264

    浏览量

    229666
  • 驱动器
    +关注

    关注

    52

    文章

    8227

    浏览量

    146263
  • 存储器
    +关注

    关注

    38

    文章

    7484

    浏览量

    163768
  • 连接器
    +关注

    关注

    98

    文章

    14486

    浏览量

    136438
  • Pipeline
    +关注

    关注

    0

    文章

    28

    浏览量

    9361

原文标题:pipeline高端玩法(三)——一个pipeline是如何建立起来的

文章出处:【微信号:Spinal FPGA,微信公众号:Spinal FPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何构建简单的对讲电路

    在这个电路中,我们将构建非常简单的对讲电路,您可以使用该电路在房屋或学校内以两种方式进行本地通信。这个项目带来了童年的记忆,即使用火柴盒和线来制作
    的头像 发表于 11-21 17:26 1634次阅读
    如何<b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的对讲电路

    如何构建简单而强大的MP3播放器

    在本教程中,我们将学习如何构建简单而强大的 MP3 播放器,可以播放任何 MP3 歌曲,音量和曲目也可以借助按钮进行调整。通过些额外的
    的头像 发表于 01-25 17:32 5151次阅读
    如何<b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>而强大的MP3播放器

    分享51上写的简单的操作系统,包含完整全部程序

    外围电路和程序都写得很熟练了,要想成为单片机真正应用,必须懂时间片轮度法,状态机,最后达到单片机系统的境界,网上有很多RTOS,uc/os2,等系统,但那些都太专业,需要本书专门学,这里分享圈圈写的小系统,大家可以在里面专
    发表于 07-15 13:30

    电子时钟

    `在knewmaker上看见这么篇文章,感觉还行,转了数字电子时钟可是电子DIY入门第课,对于了解电路知识和提高动手能力有着很大的帮助。今天小编带来的是通过纯数字模块搭
    发表于 05-31 10:22

    针对ARM产品系列的Linux

    Linux是类似Unix的电脑操作系统,利用免费和开源的软件开发以及分销模式构建起来。专有嵌入式操作系统的优势包括,拥有不受单软件公司限制的员工作支持基础,能够修改和重新发布源代
    发表于 02-09 14:32

    微波滤波器应用选择需要注意哪些事项?

    微波滤波器搭建起来简单,但理解起来比较复杂。它们在系统中完成基本的功能:阻止某些信号,通过其它信号。但可以用许多不同的方式实现这种功能
    发表于 08-19 06:35

    如何构建简单的传感器?

    您可以自己构建简单的传感器。 我的博客上有 PNP 和
    发表于 04-28 08:23

    元宇宙概念是什么意思

    元宇宙般指通过多种科技所构建起来种虚拟的宇宙形式,平行于现实世界的虚拟世界,旨在元宇宙中实现在现实中人们可以做到的事。
    的头像 发表于 11-01 09:47 1.4w次阅读

    如何构建简单的基于红外的车门遥控器

    在这篇文章中,我们将学习如何构建简单但万无失的基于红外的遥控器,该遥控器可用于通过个人遥控手机锁定和解锁任何车门。
    的头像 发表于 04-02 10:37 1301次阅读
    如何<b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的基于红外的车门遥控器

    文解析新型电力系统与智能装备技术

    新型电力系统是指基于智能电网、清洁能源、能源互联网等技术和理念,构建起来的高效、安全、可靠、可持续的电力系统。
    发表于 04-24 11:50 3473次阅读
    <b class='flag-5'>一</b>文解析新型电力系统与智能装备技术

    构建简单的机械臂

    电子发烧友网站提供《构建简单的机械臂.zip》资料免费下载
    发表于 06-14 14:55 0次下载
    <b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的机械臂

    构建简单的提醒报警电路

    一个工作狂可能听起来有点酷。但研究表明,倾向于长时间坐在电脑前工作的人患糖尿病、心脏病等疾病的风险很高。所以我想设计简单的电路,可以帮
    的头像 发表于 06-18 10:14 805次阅读
    <b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的提醒报警电路

    如何构建简单的家庭自动化

    电子发烧友网站提供《如何构建简单的家庭自动化.zip》资料免费下载
    发表于 07-05 11:41 0次下载
    如何<b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的家庭自动化

    什么是pipeline?Go中构建流数据pipeline的技术

    本文介绍了在 Go 中构建流数据pipeline的技术。 处理此类pipeline中的故障很棘手,因为pipeline中的每个阶段可能会阻止尝试向下游发送值,并且下游阶段可能不再关心传
    的头像 发表于 03-11 10:16 600次阅读

    为THS3001构建简单的SPICE模型

    电子发烧友网站提供《为THS3001构建简单的SPICE模型.pdf》资料免费下载
    发表于 10-29 10:11 0次下载
    为THS3001<b class='flag-5'>构建</b><b class='flag-5'>一</b><b class='flag-5'>个</b><b class='flag-5'>简单</b>的SPICE模型