risc-v中文社区

 找回密码
 立即注册
查看: 12184|回复: 3

[原创] rocketchip高级参数化机制--Diplomacy实验代码(10)

  [复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2021-10-14 15:10:59 | 显示全部楼层 |阅读模式
一、Diplomacy概述
diplomacy是一个chisel开发团队开发的chisel库,主要实现两个功能:
1)实现模块之间的参数协商。参数在模块之间传递时可以根据需求协商与检查,更加且不容易出错。
2)快速实现设计拓扑的参数化。因为verilog需要大量的define,很容易出错且写起来困难。
那diplomacy是如何做的呢?它是将模块之间的组织关系抽象成一张有向无环图。模块具有结点,相互之间的连接关系是边。

将模块A与模块B的bundle之间的连接抽象成Node之间的连接,输入输出端口统一用Node代替,Node
就是模块的Port。采用bindoperation操作,可以在两个Node之间创建一条边,用于参数协商。两这个
Bind operation本业就是可以在参数化的,有没有这条边都是可以配置的。
二、加法器例子
为了展示diplomacy的上述两个特性,在此实现一个有意思的加法器,加法器的输入端口个数被参数化,同时实现参数的简单的协商,让输入输出
结点都同位宽小的作为实际使用参数。

为了展示拓扑参数化,实现思路如上图。将加法器的输入抽象成driver模块。实现操作的是adder模块。然后来一个checker模块,看看计算结果是不是正确。
driver产生随机数,adder累加,checker看看加的是不是对,输出判断结果。
其中driver的数目是可参数化的,结点之间传递的参数要经过协商,取小的值。
1)配置环境
由于chisel3把这些参数化的库都独立出去了,所以我们要引用rocketchip的库。根据https://blog.csdn.net/qq_39507748/article/details/120587159
这个网页所说,就是需要import freechips.rocketchip.diplomacy等,那么需要在build.sbt中导入相应的库,
https://zhuanlan.zhihu.com/p/366476459中有提到libraryDependencies += "edu.berkeley.cs" %% "rocketchip" % "1.2.0"
(补充说明:在build.sbt中%%自动给库包的包名结尾加上Scala的版本号,而%用于分割groupid与artifactid)
2)定义传递的参数以及结点实现
case class UpwardParam(width:Int)
case class DownwardParam(width:Int)
case class EdgeParam(width:Int)
因为本例子很简单,我们定义结点向上传递的参数与向下传递的参数均只包含一个int成员变量,实际上这个参数可以非常复杂。
EdgeParam写上的意义在于UpwardParma和DownwardParam商量一下,得出一个最终用的EdgeParam。
由于我们用到的所有结点协商规律是一样的,所以我们只需要定义结点的实现。
object AdderNodeImp extends SimpleNodeImp[DownwardParam,UpwardParam,EdgeParam,UInt] {
        def edge(pd: DownwardParam,pu:UpwardParam,p: Parameters,sourceInfo:SourceInfo):EdgeParam = {
                if(pd.width < pu.width) EdgeParam(pd.width) else EdgeParam(pu.width)
        }
        def bundle(e:EdgeParam) = UInt(e.width.W)
        def render(e:EdgeParam) = RenderedEdge("blue",s"width=${e.width}")
}
通过虚函数edge,bundle的实现来参数协商。其中render是用来画图的一个函数。
如上图,最终给EdgeParam赋值的是位宽小的值,然后用这个EdgeParam赋给bundle,用作真正的连接。
3)定义三种结点
根据上图可知,实际上我们完成这个设计用到了三种结点
        a)SourceNode 类型的结点,只有输出没有输入
        b)SinkNode 类型的结点,只有输入没有输出
        c)NexusNode 类型的结点,输入输出都有若干
不同的结点全定义在Nodes.scala这个文件中,还有些其它的结点,但常用的是这三种。我们定义这三个类:
class AdderDriverNode(width:Seq[DownwardParam])(implicit valName:ValName) extends SourceNode(AdderNodeImp)(widths)
//为什么需要有隐式参数implicit valName:ValName)????????
//我查看SourceNode源代码发现这个类:
class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])(implicit valName: ValName)
  extends MixedNode(imp, imp) {
        protected[diplomacy] def mapParamD(n:Int,p:Seq[D]) = po
        protected[diplomacy] def mapParamU(n:Int,p:Seq[U])=Seq()
}
是这样定义的,而AdderNodeImp是object AdderNodeImp extends SimpleNodeImp{override def edge... override def bundle...}
难道可以将一个object传给SourceNode吗???语法好象与我的理解不一样啊:实验发现代码中这样传object是合法的。
class AdderMonitorNode(width:UpwardParam)(implicit valName:ValName) extends SinkNode(AdderNodeImp)(Seq(width))
//查看SinkNode源码,觉得应该改为:
class AdderMonitorNode(widths:Seq[UpwardParam])(implicit valName:ValName) extends SinkNode(AdderNodeImp)(widths)

class AdderNode(dFn:Seq[DownwardParam]=>DownwardParam,uFn:Seq[UpwardParam]=>UpwardParam)(implicit valName:ValName) extends NexusNode(AdderNodeImp)(dFn,uFn)
4)定义Adder模块
class Adder(implicit p: Parameters) extends LazyModule {
        //这个Adder模块中有一个继承自NexusNode(具有若干输入和输出的节点)的AdderNode
        //在AdderNode中对这些输入和输出节点进行过滤处理
        val node = new AdderNode(//AdderNode是有若干输入和输出的NexusNode节点
                {
                        dFn:Seq[DownwardParam]=>require(dFn.forall(p=>p.width == dFn.head.width),"inward,downward widths not equivalent")
                        dFn.head
                },
                {
                        uFn:Seq[UpwardParam]=>require(uFn.forall(p=>p.width == uFn.head.widthd),"outward,upward widths not equivalent")
                        uFn.head
                }
        )
        //既然LazyModuleImp其实就是MutiIOModule,那就说明val module就代表着一个多端口的模块
        lazy val module: LazyModuleImp = new LazyModuleImp(this) { //LazyModuleImp继承自MutiIOModule with LazyModuleImpLike,
        //而trait LazyModuleImpLike又继承自RawModule,并且MutiIOModule也是继承自RawModule
        //也就是说有class R,class M extends R,trait L extend R,class LMI extends M with L
                required(node.in.size >= 2)
                node.out.head._1
        }
        override lazy val desiredName = "Adder" //模块名称
}
5)定义driver模块
class AdderDriver(width:Int,numoutputs:Int)(implicit p: Parameters) extends LazyModule {
        //因为AdderDriverNode继承自SourceNode表示只有输出没有输入的节点,所以在此创建这个节点实例,主构造参数是Seq[DownwardParam]
        //numoutputs表示有多少个DownwardParam,每个DownwardParam的位宽是width,width是要参与协商的位宽参数
        val node = new AdderDriverNode(Array.fill(numoutputs)(DownwardParam(width)))
        lazy val module = new LazyModuleImp(wrapper = this) {
                /*
                AdderDriverNode 只有一个输出边edge,也即node.edges.out(其实是因为AdderDriverNode<--SourceNode<--MixedNode,在Nodes.scala中
                case class Edges[EI,EO](in:EI,out:EO),而sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]
                中有lazy val edges:Edges(edgesIn,edgesOut),        protected[Diplomacy] lazy val edgesIn: Seq[EI]=(iPorts zip uiParams).map{..},
                lazy val edgesOut:Seq[EO]=(oPorts zip doParams).map{..})
                node.edges.out类型是case class Edges[EI,EO)(in:EI,out:EO)中的out,实际传入的是Seq[EO],是一个集合类型,但实际元素类型是EO,
                代表协商后的参数(具体是DownwardParam还是width???),而不是数据,数据是BO。这是因为
                AdderDriver需要使用协商后的位宽参数,所以这里才访问了node.edges.out,并不是只有这里才有???。这里的out包含两个EO,因为有两个
                AdderDriverNode,各自有一个边edge,所以协商参数有两个。记住协商时是以边edge为单位,也即每个边edge都会参与协商,所以才会有两个协商参数,
                取head即可       
                */
                /*
                protected[deplomacy] lazy val edgesOut = (oPorts zip doParams).map{case((i,n,p,s),o) => outer.edgeO(o,n,uiParams(i),p,s)}
                其中oPorts的类型是List[Int,InWardNode[DO,UO,BO],Parameters,SourceInfo],doParams 的是Seq[DO],在lazy val doParams:Seq[DO]={。。}
                的语句块中有val o = mapParamsD(oPorts.size,diParmas),在MixdNode中def mapParmasD(n:Int,p:Seq[DI]):Seq[DO]以及def mapParamsU(
                n:Int,p:Seq[UO]):Seq[UI]都是抽象方法,在SourceNode类中(只有输出没有输入端口)mapParamsU是Seq(),而mapParamsD值=po,而po是SourceNode
                的主构造器中的参数po:Seq[D],也就是本例中的Array.fill(numoutputs)(DownwardParam(width))的值即Seq[DownwardParam],
                经过mapParamsD处理之后,再o.map(outer.mixO(_,node=this)处理,再看outer:
                outer 是sealed abstract class MixedNode的主构造参数val outer:OutwardNodeImp[DO,UI,EI,BI]
                这个 outer 值就是我们的class AdderDriverNode 传入的第二个参数: object AdderNodeImp extends SimpleNodeImp[DownardParam,UpwardParam,EdgeParam,UInt]
                关于OuterwardNodeImp和SimpleNodeImp的关系:
                trait OutwardNodeImp[Do,UO,EO,BO <: Data] {
                //DO是指Downwards Parameters,当然是从节点的输出口
                //UO是指Upwards Parameters,当然也是节点的输出口
                //EO是指Edge Parameters,输出端口相连的边
                //BO是节点的输出边相连的Bundle
                def edgeO(pd: DO,pu:UO,p: Parameters,sourceInfo:SourceInfo):EO
                def bundle(eo:EO):BO
                //还有二个可选的方法def mixO,def getI
                def mixO(pd: DO,node:OutwardNode[DO,UO,BO]): DO = pd
                }
                //既然OutwardNodeImp是一个trait,名称其实不应该带有Imp,估计是为了配合NodeImp才带了一个Imp
                abstract class NodeImp[D,U,EO,EI,B<ata] extends Object with InwardNodeImp[D,U,EI,B] with OutwardNodeImp[D,U,EO,B]
                abstract class SimpleNodeImp[D,U,E,B] extends NodeImp[D,U,E,E,B]
                可以总结出来: SimpleNodeImp[D,U,E,B] <- NodeImp[D,U,EI,EO,B] <- OutwardNodeImp[D,U,EO,B]
                也就是说outer.mixO(..)其实是我们的AdderNodeImp在进行mixO处理,在trait OutwardNodeImp 中的def mixO返回的是其第一个参数pd: DO
                所以最后o.map返回的Seq,其中的元素类型还是我们的 AdderNodeImp规定的case class DownwardParam,也就是说doParams的类型也是Seq[DownardParam]
                (oPorts zip doParams)的结果就是一个List,但List中元素数量以oPorts和doParams较短者为最终数量,每个元素类型是元组,比如(oPorts(0),doParams(0))
                doParams(0)就是我们传入的DownwardParam
                再来看oPorts(0),源码中:lazy val oPorts = oBindings.flatMap{...}
                而oBindings的类型是:List[(Int,InwardNode[DO,UO,BO],NodeBinding,Parameters,SourceInfo)]
                oBinding.flatMap{case (i表示Int,n表示InwardNode[DO,UO,BO],_,p表示Parameters,s表示SourceInfo) =>
                        val (start,end) = n.iPortMapping(i)
                        (start until end) map { j => (j,n,p,s)}
                }
                val (start,end) = n.iPortMapping(i)其实是 trait InwardNode[DO,UO,BO] extends BaseNode {
                        private val accPI = ListBuffer[(Int,OutwardNode[DI,UI,BI],NodeBinding,Parameters,SourceInfo)]() //在def oPush中添加元素,并且是在子类MixedNode的def bind添加
                        protected[diplomacy] val iPortMapping:Seq[(Int,Int)]
                        protected[diplomacy] val iStar:Int
                        protected[diplomacy] val diParams:Seq[DI] //从连接的节点中来的 DI: Downward Parameters from inward ports
                        protected[diplomacy] val uiParams:Seq[UI] //从本节点中来的 UI:Upward Parameters from inward ports               
                }的iPortMapping(i)
                InwardNode 中的抽象变量都在子类MixedNode中用protected[diplomacy] lazy val (oPortMapping,iPortMapping,oStar,iStar)=...实例化       
               
               
                再来分析protected[deplomacy] lazy val degesOut = (oPorts zip doParams).map语句块中的case((i,n,p,s),o)=>outer.edgeO(o,n.uiParams(i),p,s):
                outer 是我们传进来的object AdderNodeImp extends SimpleNodeImp[DownwardParam,UpwardParam,EdgeParam,UInt]
                在SimpleNodeImp类中 def edge0(pd: D(其实是DownwardParam),pu:U(其实是UpwardParam),p: Parameters,sourceInfo:SourceInfo):E(其实是EdgeParam)
                        = edge(pd,pu,p,sourceInfo)
                在SimpleNodeImp类中抽象方法def edge(pd(其实是DownwardParam),pu:U(其实是UpwardParam),parameters,sourceInfo:SourceInfo):E(其实是EdgeParam)
                当然SimpleNodeImp类中还有一个抽象方法def bundle(e:E(其实是EdgeParam)):B(其实就是我们的UInt)
                所以outer.edge0方法就是调用我们的AdderNodeImp这个SimpleNodeImp实现类中的def edge方法,而我们的edge方法根据位宽选较小位宽的返回
               
                */
                //node.edges.out其实就是MixedNode中的val edges = Edges(edgesIn,edgesOut)的 edgesOut,根据上面分析可知它是调用了我们的
                //节点协商实现类(object AdderNodeImp ) 中的def edge,已经获得了协商结果即EdgeParam类型值,当然是List[EdgeParam]
                val negotiatedWidths = node.edges.out.map(_.width)  
                val finalWidth = negotiatedWidths.head
                // generate random addend (notice the use of the negotiated width)
                val randomAddend = FibonacciLFSR.maxPeriod(finalWidth)
                // drive signals 每次赋值都会产生不一样的值。
                // node.out是Seq[(BO, EO)],这里其实就是只对BO进行赋值,本例中有两个BO
                //node是AdderDriverNode extends SourceNode,它的out是在MixedNode中定义的def out:Seq[(BO,EO)] 这个out方法用于在LazyModuleImp中访问协商的结果
                //def out:Seq[(BO,EO)] = { bundleOut zip edgesOut} //bundleOut调用我们的节点协商实现类(object AdderDriverNode)中的def bundle根据协商结果最终获得对应的数据类型值即UInt(width)
                //当然,bundleOut返回的也是Seq[BO]即Seq[UInt]
                node.out.foreach { case (addend,_) => addend := randomAddend } //获得了addend:BO,即UInt类型,然后再连线到某个随机值
        }
        override lazy val desiredName = "AdderDriver"
}
//6)定义Monitor模块
class AdderMonitor(width:Int,numOperands:Int)(implicit p: Parameters) extends lazyModule {
        //AdderMonitorNode extends SinkNode(AdderNodeImp) 只有输入没有输出的节点
        //每个lazyModule模块中,需要一个继承自SourceNode或SinkNode或NexusNode的节点类,除了这个类需要的参数Seq[DownwardParam]或Seq[UpwardParam]
        //或Seq[DownwardParam]=>DownwardParam,Seq[UpwardParam]=>UpwardParam外,所有这些节点类还需要将一个节点参数协商类即NodeImp的实现类传给
        //对应的父类:SourceNode或SinkNode或NexusNode,一般来说这个参数协商类要用object(单一性)来定义,就象本例中object AdderNodeImp,以示协商的标准一致
        //rocket chip库中已经定义了一些这样的参数协商object,比如:SimpleNodeImp,查源码发现还有一个object TLImp(用于TileLink总线,位于tilelink\Nodes.scala)
        //object TLImp extends NodeImp[TLClientPortParaeters,TLManagerPortParameters,TLEdgeOut,TLEdgeIn,TLBundle] {
        //        def edgeO(pd:TLClientPortParameters,pu:TLManagerPortParameters,p: Parameters,sourceInfo:SourceInfo)..
        //        def edgeI(pd:TLClientPortParameters,pu:TLManagerPortParameters,p: PARAMETERS,SOURCEInfo:SourceInfo)...
        //        def bundleO(eo:TLEdgeOut)...
        //        def bundleI(ei:TLEdgeIn)...
        //        def render(ei:TLEdgeIn)...
        //        override def monitor(bundle:TLBundle,edge:TLEdgeIn)...
        //        override def mixO..
        //        override def mixI...
        //}
        /*
        class SinkNode[D,U,EO,EI,B<: Data](imp:NodeImp[D,U,EO,EI,B])(pi:Seq[U])(implicit valName:ValName) extends MexedNode(imp,imp) {
                protected[diplomacy] def mapParamsD(n:Int,p:Seq[D]):Seq[D] = Seq()
                protected[diplomacy] def mapParamsU(n:Int,p:Seq[U]):Seq[U] = pi
        }
       
        */
        //两个节点,二个连driver,一个连adder
        val nodeSeq = Array.fill(numOperands){new AdderMonitorNode(UpwardParam(width))}
        val nodeSum = new AdderMonitorNode(UpwardParam(width))
        lazy val module = new LazyModuleImp(wrapper = this){
                val io = IO(new Bundle {
                        val error = Output(Bool())
                })
                /*
                sealed abstract class MixedNode[DI,UI,EI,BI<ata,DO,UO,EO,BO<ata](
                val inner:InwardNodeImp[DI,UI,EI,BI],
                val outer:OutwardNodeImp[DO,UO,EO,BO])(implicit valName:ValName) extends BaseNode with NodeHandle with InwardNode with OutwardNode
                {
                        def in:Seq[(BI,EI)] = { bundleIn zip edgesIn}
                        //e:EI,EI其实就是我们的EdgeParam,inner其实就是我们的继承自 SimpleNodeImp 协商实现类 AdderNodeImp
                        //所以inner.bundleI(e)最终会调用我们的协商实现类 AdderNodeImp 中的def bundle(e:EdgeParam):UInt
                        lazy val bundleIn:Seq[BI] = edgesIn.map(e=>Wire(inner.bundleI(e)))
                       
                        //n:OutwardNode[DI,UI,BI],o:Int,i:UI ,其中,DI是DownwardParam,UI是UpwardParam,BI是UInt
                        lazy val edgesIn = (iPorts zip uiParams).map{case((o,n,p,s),i)=>inner.edgeI(n,doParams(o),i,p,s)}
                       
                                //trait InwardNode[DI,UI,BI<ata] extends BaseNode {
                                //        private val accPI = ListBuffer[(Int,OutwardNode[DI,UI,BI],NodeBinding,Parameters,SourceInfo)]() //实际上DI,UI,BI分别是我们定义的Down/UpwardParam和UInt
                                //        lazy val iBindings = {iRealized = true;accPI.result()}
                                //        def iPushed = accPI.size
                                //        def iPush(index:Int,node:OutwardNode[DI,UI,BI],binding:NodeBinding)(implicit p: Parameters,sourceInfo:SourceInfo){ accPI += ((index,node,binding,p,sourceInfo))
                                //        val iStar:Int
                                //        val iPortMapping:Seq[(Int,Int)]
                                //        val diParams:Seq[DI] //from connected nodes
                                //        val uiParams:Seq[UI] //from this node
                                //        def bind(h: outwardNode[DI,UI,BI],binding:NodeBinding)(implicit p: Parameters,sourceInfo:SourceInfo):Unit
                                //}
                                //再来看 OutwardNode 的代码:
                                //trait OutwardNode[DO,UO,BO<ata] extends BaseNode {
                                //        private val accPO = ListBuffer[(Int,InwardNode[DO,UO,BO],NodeBinding,parameters,SourceInfo)]()
                                //        def oPushed:Int = accPO.size
                                //        def oPush(index:Int,node:InwardNode[DO,UO,BO],binding:NodeBinding)(implicit parameters,sourceInfo:SourceInfo) {
                                //                accPO += ((index,node,binding,p,sourceInfo))
                                //        }
                                //        lazy val oBinding = {..;accPO.result()}
                                //        val oStar:Int
                                //        val oPortMapping:Seq[(Int,Int)]
                                //        val uoParams:Seq[UO] //from connected nodes
                                //        val doParams:Seq[DO] //from thsi node
                                //}
                                //OutwardNode中的oPortMapping:Seq[(Int,Int)]是在MixedNode中通过lazy val (oPortMapping, iPortMapping, oStar, iStar)实例化的
                        lazy val iPorts = iBindings.flatMap{case(i,n_,p,s)=>
                                val (start,end) = n.oPortMapping(i) //n是 outwardNode[DI,UI,BI]
                                (start until end) map {j => (j,n,p,s)}
                        }
                        lazy val uiParams:Seq[UI] = { //通过下面的分析可知 uiParams 就是我们传入的Seq[UpwardParam]参数值即widths:Seq[UpwardParam]
                                try {
                                        ...
                                        //mapParamsU 在MixedNode中是一个抽象方法,本AdderMonitor中用的是AdderMonitorNode extends SinkNode,SinkNode extends MixedNode
                                        //在SinkNode中有实现:
                                        //class SinkNode[D,U,EO,EI,B<ata](imp:NodeImp[D,U,EO,EI,B])(pi:Seq[U])(implicit valName:ValName) extends MixedNode(imp,imp) {
                                        //        protected[diplomacy] def mapParamsD(n:Int,p:Seq[D]):Seq[D]=Seq()
                                        //        protected[diplomacy] def mapParamsU(n:Int,p:Seq[U]):Seq[U] = pi
                                        //}
                                        val i = mapParamsU(iPorts.size,uoParams) //所以i值还是我们传入的Seq[UpwardParam]
                                        i.map(inner.mixI(_,node=this)) //inner是我们的 AdderNodeImp 协商实现类 <-SimpleNodeImp <-NodeImp <- InwardNodeImp以及InwardNode
                                                                                                   //在InwardNodeImp中有def mixI(pu:UI,node:InwardNode[DI,UI,BI]):UI=pu // insert node into parameters
                                        //所以i.map返回的集合就是Seq[UpwardParam]即Seq[UI]
                                }catch {
                                        case c:.....
                                }
                        }
                }
                */
                //nodeSum是只有输入没有输出的SinkNode,in是MixedNode中的def in:Seq[(BI,EI)] = {bundleIn zip edgesIn} ,BI和EI本模块中是UInt和EdgeParam
                //nodeSum.in.head._1获得的是BI即UInt数据
                //二个节点连到Adder,nodeSeq.map(_.in.head._1),将两个节点
                io.error := nodeSum.in.head._1 =/= nodeSeq.map(_.in.head._1).reduce(_ + _)
        }
        override lazy val desiredName = "AdderMonitor"
       
}


回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-10-14 15:17:00 | 显示全部楼层
还有两个step...
回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-10-17 20:00:17 | 显示全部楼层
//7)连接模块
class AdderTestHarness()(implicit parameters) extends LazyModule {
        val numOperands = 2 //操作数个数
        val adder = LazyModule(new Adder) //在lazyModule内部实例化模块则只能用LazyModule.apply不能用LazyModuleImp
        //AdderDriverNode是SourceNode,按下面代码则它的widths:Seq[DownwardParam]中有二个元素
        //AdderDriver模块中的节点是AdderDriveNode
        //所以说一个AdderDriver模块就有二个DownwardParam协商参数给下游,其实一个Downward到Adder,一个Downward到AdderMonitor
        val drivers = Array.fill(numOperands){LazyModule(new AdderDriver(withd=8,numOutputs=2))} //人为设置AdderDriver模块的DownwardParam中的位宽为8,要分成二个Downward,一个到Adder,
        //一个到AdderMonitor,AdderDriver模块共有二个即有二个操作数 问题:如果AdderDriver模块分出的二个DownwardParam,到Adder的是位宽大的,到AdderMonitor是位宽小的,这种如何做???
        val monitor = LazyModule(new AdderMonitor(width=4,numOperands = numOperands)) //monitor有numOperands个AdderMonitorNode用来连接AdderDriver的AdderDriverNode,还有一个节点连接Adder的AdderNode

        //定义好这些模块之后,需要创建有向无环图

        //连接Adder和Driver之间的node
        drivers.foreach{driver => adder.node := driver.node} //一个driver模块中有二个DownwardParam到下游,此处adder.node:=driver.node肯定不能理解成adder的node连接driver的二个DownwardParam带来的信号,
        //只能理解为:我们现在正在创建有向无环图,用":="表示两者之间有连接即可,不需要管数量和方向

        //连接drivers和monitor之间的node
        //drivers有二个AdderDriver模块,每个AdderDriver中有二个AdderDriverNode,monitor中有三个节点,二个(nodeSeq)连AdderDriver,一个(nodeSum)连Adder模块中的AdderNode
        //drivers.zip(monitor.nodeSeq)的结果是Array[(AdderDriver,AdderMonitorNode)],并不是Array[LazyModule,AdderMonitorNode)],因为LazyModule.apply返回的是AdderDriver
        drivers.zip(monitor.nodeSeq).foreach{case (driver,monitorNode)=>monitorNode := driver.node}
        //连接adder和monitor之间的node
        monitor.node := adder.nodeSum //注意:此处连接不能写成adder.nodeSum := monitor.node,因为monitor.nodeSum (a sink) cannot appear right ,这是因为monitor中的nodeSeq和nodeSum都是只有输入没有输出的SinkNode,所以只能出现在:=的左边,可以想象:SourceNode不能出现在左边

        lazy val module = new LazyModuleImp(wrapper = this) {
                val io = IO(new Bundle {
                        val finished = Output(Bool())
                })
                when(monitor.module.io.error) {
                        printf("something went wrong")
                }
                io.finished := monitor.module.io.error
        }
        override lazy val desiredName = "AdderTestHarness"
       
}
//8)生成结果
class AdderDiplomacy()(implicit parameters) extends Module {
        val io = IO(new Bundle {
                val success = Output(Bool())
        })
        val ldut = LazyModule(new AdderTestHarness())
        val dut = Module(ldut.module)
        io.success := dut.io.finished
}
//产生verilog代码
object AdderDiplomacy extends App {
        Driver.execute(Array("--target-dir","generated/AdderDiplomacy"),()=>new AdderDiplomacy()(Parameters.Empty)
}









回复

使用道具 举报

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
 楼主| 发表于 2021-10-17 20:01:38 | 显示全部楼层
生成的verilog代码如下:

module Adder( // @[: @3.2]
  output [3:0] auto_out // @[: @6.4]
);
  assign auto_out = 4'h2 + 4'h1; // @[LazyModule.scala 173:49: @17.4]
endmodule
module AdderMonitor( // @[: @58.2]
  input  [3:0] auto_node_sum_in, // @[: @61.4]
  output       io_error // @[: @62.4]
);
  wire [3:0] _T_21; // @[AdderDiplomacy.scala 142:75: @78.4]
  assign _T_21 = 4'h2 + 4'h1; // @[AdderDiplomacy.scala 142:75: @78.4]
  assign io_error = auto_node_sum_in != _T_21; // @[AdderDiplomacy.scala 142:14: @80.4]
endmodule
module AdderTestHarness( // @[: @82.2]
  input   clock, // @[: @83.4]
  input   reset, // @[: @84.4]
  output  io_finished // @[: @86.4]
);
  wire [3:0] adder_auto_out; // @[AdderDiplomacy.scala 157:25: @92.4]
  wire [3:0] monitor_auto_node_sum_in; // @[AdderDiplomacy.scala 161:27: @110.4]
  wire  monitor_io_error; // @[AdderDiplomacy.scala 161:27: @110.4]
  wire  _T_9; // @[AdderDiplomacy.scala 180:13: @124.6]
  Adder adder ( // @[AdderDiplomacy.scala 157:25: @92.4]
    .auto_out(adder_auto_out)
  );
  AdderMonitor monitor ( // @[AdderDiplomacy.scala 161:27: @110.4]
    .auto_node_sum_in(monitor_auto_node_sum_in),
    .io_error(monitor_io_error)
  );
  assign _T_9 = reset == 1'h0; // @[AdderDiplomacy.scala 180:13: @124.6]
  assign io_finished = monitor_io_error; // @[AdderDiplomacy.scala 182:17: @129.4]
  assign monitor_auto_node_sum_in = adder_auto_out; // @[LazyModule.scala 167:57: @117.4]
  always @(posedge clock) begin
    `ifndef SYNTHESIS
    `ifdef PRINTF_COND
      if (`PRINTF_COND) begin
    `endif
        if (monitor_io_error & _T_9) begin
          $fwrite(32'h80000002,"something went wrong"); // @[AdderDiplomacy.scala 180:13: @126.8]
        end
    `ifdef PRINTF_COND
      end
    `endif
    `endif // SYNTHESIS
  end
endmodule
module AdderDiplomacy( // @[: @131.2]
  input   clock, // @[: @132.4]
  input   reset, // @[: @133.4]
  output  io_success // @[: @134.4]
);
  wire  ldut_clock; // @[AdderDiplomacy.scala 194:19: @136.4]
  wire  ldut_reset; // @[AdderDiplomacy.scala 194:19: @136.4]
  wire  ldut_io_finished; // @[AdderDiplomacy.scala 194:19: @136.4]
  AdderTestHarness ldut ( // @[AdderDiplomacy.scala 194:19: @136.4]
    .clock(ldut_clock),
    .reset(ldut_reset),
    .io_finished(ldut_io_finished)
  );
  assign io_success = ldut_io_finished; // @[AdderDiplomacy.scala 195:14: @139.4]
  assign ldut_clock = clock; // @[: @137.4]
  assign ldut_reset = reset; // @[: @138.4]
endmodule

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-4-26 15:03 , Processed in 0.017657 second(s), 17 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表