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

    文章

    18288

    浏览量

    222168
  • 驱动器
    +关注

    关注

    51

    文章

    7310

    浏览量

    142972
  • 存储器
    +关注

    关注

    38

    文章

    7151

    浏览量

    162001
  • 连接器
    +关注

    关注

    96

    文章

    12642

    浏览量

    133139
  • Pipeline
    +关注

    关注

    0

    文章

    27

    浏览量

    9294

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    电子时钟

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

    急求!电流放大电路三极管发烫严重。

    是做一个电流放大的电路,正负200ma放到到正负400ma,仿真通过了,但是实际搭建起来的电路板中第二运放和几个三极管发热非常严重,找不到具体原因,还请大神指点!谢谢!
    发表于 08-09 12:01

    针对ARM产品系列的Linux

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

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

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

    【HiSpark Wi-Fi IoT 智能家居套件试用】wifi连接并实现和上位机tcp client的通讯

    的python脚本,主要是连接该server,并创建2线程收发数据:记得把这些代码添加到工程并构建起来:编译好下载好代码之后,首先给板子上电,串口上能看到连接成功的打印。之后运行tcp_client.py脚本,就能看到通讯结果了。
    发表于 02-21 16:47

    请问vscode和eclipse哪个好使?哪个官方支持更好些?

    如题,最近开发esp32,在windows环境下。说实话,idf的开发环境比以前搭建起来简单多了,无论是vscode还是eclipse里都有键安装的插件,从前的环境搭建失败劝退多次噩梦不再重演
    发表于 02-21 08:55

    求助,能否在pipeline中添加多个音频输入流?

    能否在pipeline中添加多个音频输入流[,例如httpstream flash_tone_stream,因为音频的输入方向有两。或者能否进行
    发表于 03-10 08:09

    如何构建简单的传感器?

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

    构建简单的裸机程序使用Arm DS-5

    本教程将带您完成创建、配置和构建简单的裸机程序使用Arm DS-5。要在应用程序构建完成后运行它,本教程将带您完成配置到以软件实现的系统
    发表于 08-02 08:27

    没有云的工业物联网平台可以构建起来

    几乎任何云供应商或物联网(iot)平台提供商都会告诉您,基于云的服务对您的实施计划至关重要。
    发表于 12-23 11:08 807次阅读

    元宇宙概念是什么意思

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

    元宇宙概念是什么意思

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

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

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

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

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