分析 Rocket Chip 中 Diplomacy 系统

分析 Rocket Chip 中 Diplomacy 系统

杰哥的{运维,编程,调板子}小笔记

分析 Rocket Chip 中 Diplomacy 系统

背景

Rocket Chip 大量使用了 Diplomacy 系统来组织它的总线、中断和时钟网络。因此,如果想要对 Rocket Chip 进行定制,那么必须要对 Rocket Chip 中 Diplomacy 系统的使用有充分的了解,而这方面的文档比较欠缺。本文是对 Rocket Chip 中 Diplomacy 系统的使用的分析。阅读本文前,建议阅读先前的 分析 Diplomacy 系统 文章,对 Diplomacy 系统的设计和内部实现获得一定的了解。

Rocket Chip 总线结构概要

Rocket Chip 主要有以下几个总线:

  1. sbus: System Bus
  2. mbus: Memory Bus
  3. cbus: Control Bus
  4. pbus: Periphery Bus
  5. fbus: Frontend Bus

图示可以见参考文档中的链接,不过链接中的结构和实际的有一些区别。目前的 Rocket Chip 的总线结构大致是这样:

fbus -> sbus -> mbustile --/ \-> cbus -> pbus

主要是 pbus 的位置从连接 sbus 移动到了连接 cbus。

根据配置不同,总线结构也不同,例如在有 coh(coherence manager) 的时候,是:

fbus -> sbus -> coh -> mbustile --/ \-> cbus -> pbus

深入分析 Rocket Chip 总线结构

下面是一个双核 Rocket Chip 的 GraphML 导出来用 yED 绘制的架构图:

接下来深入分析图中的各个连接关系以及对应的代码。

TileLink 和 AXI 总线

这个图比较复杂,混合了多个 Diplomacy 网络,首先是总线的部分,包括 TileLink 和 AXI:

  1. 两个 Tile,对应一个双核的系统;每个 Tile 内部有一个 dcache 和 icache,连接到一个 tlMasterXbar 上,再通过 coupler_from_rockettile 连接到 fixer 再到 system_bus_xbar
  2. 从 system_bus_xbar 分出来三路 Slave:
    1. 第一路是 cbus,通过 out_xbar,连接到多个 slave:debug,error device,plic,clint,l2 control,bootrom
    2. 第二路是 mmio,通过 tl2axi4,转成 AXI4 连接到外部的 MMIO 外设
    3. 第三路是 coh,连接到 InclusiveCache,再连接到 mbus,通过 tl2axi4,转成 AXI4 连接到外部的内存
  3. system_bus_xbar 除了每个 tile 对应一个 master 以外,还有一个 master:fbus,它从外部的 AXI4 进来,通过 axi42tl 转换,接到 fbus,提供一个有缓存一致性的 AXI 访问接口,用于 DMA

简化后的结构如图:

flowchart TD subgraph tile0 dcache0[dcache] icache0[icache] tlMasterXbar0[tlMasterXbar] dcache0 --> tlMasterXbar0 icache0 --> tlMasterXbar0 end subgraph tile1 dcache1[dcache] icache1[icache] tlMasterXbar1[tlMasterXbar] dcache1 --> tlMasterXbar1 icache1 --> tlMasterXbar1 end sbus tlMasterXbar0 --> sbus tlMasterXbar1 --> sbus axi_fbus --> axi42tl --> fbus --> sbus sbus --> cbus --> out_xbar out_xbar --> debug out_xbar --> error out_xbar --> plit out_xbar --> clint out_xbar --> l2_ctrl out_xbar --> bootrom cbus --> pbus sbus --> tl2axi4_mmio[tl2axi4] --> axi_mmio sbus --> coh --> mbus --> tl2axi4_mem[tl2axi4] --> axi_mem

那么这些连接关系在代码中是怎么搭建的呢:

  1. 首先是 tile 内部,dcache 和 icache 分别通过一个 widget 连到一个 tlMasterXbar 上:
    class Frontend(val icacheParams: ICacheParams, tileId: Int)(implicit p: Parameters) extends LazyModule { lazy val module = new FrontendModule(this) // icache resides in frontend val icache = LazyModule(new ICache(icacheParams, tileId)) val masterNode = icache.masterNode val slaveNode = icache.slaveNode val resetVectorSinkNode = BundleBridgeSink[UInt](Some(() => UInt(masterNode.edges.out.head.bundle.addressBits.W)))}trait HasICacheFrontend extends CanHavePTW { this: BaseTile => val frontend = LazyModule(new Frontend(tileParams.icache.get, tileId)) // tlMasterXbar.node &LT-- TLWidthWidget &LT-- frontend.masterNode(i.e. icache.masterNode) tlMasterXbar.node := TLWidthWidget(tileParams.icache.get.rowBits/8) := frontend.masterNode}trait HasHellaCache { this: BaseTile => lazy val dcache: HellaCache = LazyModule(p(BuildHellaCache)(this)(p)) tlMasterXbar.node := TLWidthWidget(tileParams.dcache.get.rowBits/8) := dcache.node}

    2. 其次是每个 tile 的 tlMasterXbar 连接到 coupler_from_rockettile,再连到 sbus(system_bus_xbar)
    // in HasTiles.scala/** Connect the port where the tile is the master to a TileLink interconnect. */def connectMasterPorts(domain: TilePRCIDomain[TileType], context: Attachable): Unit = { implicit val p = context.p // crossingParams.master.where defaults to SBUS, see below // so dataBus is system_bus_xbar val dataBus = context.locateTLBusWrapper(crossingParams.master.where) // coupler_from_rockettile (val baseName = "rockettile" in RocketTileParams) dataBus.coupleFrom(tileParams.baseName) { bus => // crossMasterPort is defined below bus :=* crossingParams.master.injectNode(context) :=* domain.crossMasterPort(crossingParams.crossingType) }}// in HierarchicalElementPRCIDomain.scaladef crossMasterPort(crossingType: ClockCrossingType): TLOutwardNode = { val tlMasterResetXing = this { DisableMonitors { implicit p => element { element.makeMasterBoundaryBuffers(crossingType) } :=* // masterNode is defined below element_reset_domain.crossTLOut(element.masterNode) } } val tlMasterClockXing = this.crossOut(tlMasterResetXing) tlMasterClockXing(crossingType)}// in RocketTile.scalatlOtherMastersNode := tlMasterXbar.nodemasterNode :=* tlOtherMastersNode// crossingParams defaults to RocketCrossingParams()// in RocketSubsystem.scalacase class RocketCrossingParams( crossingType: ClockCrossingType = SynchronousCrossing(), master: HierarchicalElementPortParamsLike = HierarchicalElementMasterPortParams(), slave: HierarchicalElementSlavePortParams = HierarchicalElementSlavePortParams(), mmioBaseAddressPrefixWhere: TLBusWrapperLocation = CBUS, resetCrossingType: ResetCrossingType = NoResetCrossing(), forceSeparateClockReset: Boolean = false) extends HierarchicalElementCrossingParamsLike// default crossingParams.master.where is SBUS(System Bus)case class HierarchicalElementMasterPortParams( buffers: Int = 0, cork: Option[Boolean] = None, where: TLBusWrapperLocation = SBUS) extends HierarchicalElementPortParamsLike { def injectNode(context: Attachable)(implicit p: Parameters): TLNode = { (TLBuffer.chainNode(buffers) :=* cork.map { u => TLCacheCork(unsafe = u) } .getOrElse { TLTempNode() }) }}

  2. 接着是 sbus 连接到 coh,coh 连接到 mbus:
    // in BusTopology.scala/** Parameterization of a topology containing a banked coherence manager and a bus for attaching memory devices. */case class CoherentBusTopologyParams( mbus: MemoryBusParams, coherence: BankedCoherenceParams, sbusToMbusXType: ClockCrossingType = NoCrossing, driveMBusClockFromSBus: Boolean = true) extends TLBusWrapperTopology( // instantiate mbus and coherence manager instantiations = (if (coherence.nBanks == 0) Nil else List( (MBUS, mbus), (COH, CoherenceManagerWrapperParams(mbus.blockBytes, mbus.beatBytes, coherence.nBanks, COH.name)(coherence.coherenceManager)))), connections = if (coherence.nBanks == 0) Nil else List( // (master, slave, parameters) // coh := sbus (SBUS, COH, TLBusWrapperConnection(driveClockFromMaster = Some(true), nodeBinding = BIND_STAR)()), // mbus := coh (COH, MBUS, TLBusWrapperConnection.crossTo( xType = sbusToMbusXType, driveClockFromMaster = if (driveMBusClockFromSBus) Some(true) else None, nodeBinding = BIND_QUERY)) ))// in BusWrapper.scalaclass TLBusWrapperTopology( val instantiations: Seq[(Location[TLBusWrapper], TLBusWrapperInstantiationLike)], val connections: Seq[(Location[TLBusWrapper], Location[TLBusWrapper], TLBusWrapperConnectionLike)]) extends CanInstantiateWithinContextThatHasTileLinkLocations with CanConnectWithinContextThatHasTileLinkLocations{ def instantiate(context: HasTileLinkLocations)(implicit p: Parameters): Unit = { instantiations.foreach { case (loc, params) => context { params.instantiate(context, loc) } } } def connect(context: HasTileLinkLocations)(implicit p: Parameters): Unit = { connections.foreach { case (master, slave, params) => context { params.connect(context, master, slave) } } }}

  3. 为了让 Rocket Chip 可以访问外部的 AXI MMIO 设备,在 sbus 下面添加了 tl 到 axi 的一条路径:
    /** Adds a AXI4 port to the system intended to master an MMIO device bus */trait CanHaveMasterAXI4MMIOPort { this: BaseSubsystem => private val mmioPortParamsOpt = p(ExtBus) private val portName = "mmio_port_axi4" private val device = new SimpleBus(portName.kebab, Nil) val mmioAXI4Node = AXI4SlaveNode( mmioPortParamsOpt.map(params => AXI4SlavePortParameters( slaves = Seq(AXI4SlaveParameters( address = AddressSet.misaligned(params.base, params.size), resources = device.ranges, executable = params.executable, supportsWrite = TransferSizes(1, params.maxXferBytes), supportsRead = TransferSizes(1, params.maxXferBytes))), beatBytes = params.beatBytes)).toSeq) // in BaseSubsystem.scala: // def viewpointBus: TLBusWrapper = tlBusWrapperLocationMap(p(TLManagerViewpointLocated(location))) // case class TLManagerViewpointLocated(where: HierarchicalLocation) extends Field[Location[TLBusWrapper]](SBUS) // so viewpointBus points to sbus by default mmioPortParamsOpt.map { params => viewpointBus.coupleTo(s"port_named_$portName") { (mmioAXI4Node := AXI4Buffer() := AXI4UserYanker() := AXI4Deinterleaver(viewpointBus.blockBytes) := AXI4IdIndexer(params.idBits) := TLToAXI4() := TLWidthWidget(viewpointBus.beatBytes) := _) } } val mmio_axi4 = InModuleBody { mmioAXI4Node.makeIOs() }}

  4. 类似地,为了让 Rocket Chip 可以访问外部的 AXI Memory,在 mbus 下面添加了 tl 到 axi 的一条路径:
    /** Adds a port to the system intended to master an AXI4 DRAM controller. */trait CanHaveMasterAXI4MemPort { this: BaseSubsystem => private val memPortParamsOpt = p(ExtMem) private val portName = "axi4" private val device = new MemoryDevice private val idBits = memPortParamsOpt.map(_.master.idBits).getOrElse(1) private val mbus = tlBusWrapperLocationMap.get(MBUS).getOrElse(viewpointBus) val memAXI4Node = AXI4SlaveNode(memPortParamsOpt.map({ case MemoryPortParams(memPortParams, nMemoryChannels, _) => Seq.tabulate(nMemoryChannels) { channel => val base = AddressSet.misaligned(memPortParams.base, memPortParams.size) val filter = AddressSet(channel * mbus.blockBytes, ~((nMemoryChannels-1) * mbus.blockBytes)) AXI4SlavePortParameters( slaves = Seq(AXI4SlaveParameters( address = base.flatMap(_.intersect(filter)), resources = device.reg, regionType = RegionType.UNCACHED, // cacheable executable = true, supportsWrite = TransferSizes(1, mbus.blockBytes), supportsRead = TransferSizes(1, mbus.blockBytes), interleavedId = Some(0))), // slave does not interleave read responses beatBytes = memPortParams.beatBytes) } }).toList.flatten) for (i &LT- 0 until memAXI4Node.portParams.size) { val mem_bypass_xbar = mbus { TLXbar() } // Create an incoherent alias for the AXI4 memory memPortParamsOpt.foreach(memPortParams => { memPortParams.incohBase.foreach(incohBase => { val cohRegion = AddressSet(0, incohBase-1) val incohRegion = AddressSet(incohBase, incohBase-1) val replicator = tlBusWrapperLocationMap(p(TLManagerViewpointLocated(location))) { val replicator = LazyModule(new RegionReplicator(ReplicatedRegion(cohRegion, cohRegion.widen(incohBase)))) val prefixSource = BundleBridgeSource[UInt](() => UInt(1.W)) replicator.prefix := prefixSource // prefix is unused for TL uncached, so this is ok InModuleBody { prefixSource.bundle := 0.U(1.W) } replicator } viewpointBus.coupleTo(s"memory_controller_bypass_port_named_$portName") { (mbus.crossIn(mem_bypass_xbar)(ValName("bus_xing"))(p(SbusToMbusXTypeKey)) := TLWidthWidget(viewpointBus.beatBytes) := replicator.node := TLFilter(TLFilter.mSubtract(cohRegion)) := TLFilter(TLFilter.mResourceRemover) := _ ) } }) }) mbus.coupleTo(s"memory_controller_port_named_$portName") { (memAXI4Node := AXI4UserYanker() := AXI4IdIndexer(idBits) := TLToAXI4() := TLWidthWidget(mbus.beatBytes) := mem_bypass_xbar := _ ) } } val mem_axi4 = InModuleBody { memAXI4Node.makeIOs() }}

  5. 类似地,为了让外部的 AXI Master 可以访问一致的内存,在 fbus 上面添加了从 axi 到 tl 的一条路径,而 fbus 是连到 sbus 上的:
    /** Adds an AXI4 port to the system intended to be a slave on an MMIO device bus */trait CanHaveSlaveAXI4Port { this: BaseSubsystem => private val slavePortParamsOpt = p(ExtIn) private val portName = "slave_port_axi4" private val fifoBits = 1 private val fbus = tlBusWrapperLocationMap.get(FBUS).getOrElse(viewpointBus) val l2FrontendAXI4Node = AXI4MasterNode( slavePortParamsOpt.map(params => AXI4MasterPortParameters( masters = Seq(AXI4MasterParameters( name = portName.kebab, id = IdRange(0, 1 << params.idBits))))).toSeq) slavePortParamsOpt.map { params => fbus.coupleFrom(s"port_named_$portName") { ( _ := TLBuffer(BufferParams.default) := TLFIFOFixer(TLFIFOFixer.all) := TLWidthWidget(params.beatBytes) := AXI4ToTL() := AXI4UserYanker(Some(1 << (params.sourceBits - fifoBits - 1))) := AXI4Fragmenter() := AXI4IdIndexer(fifoBits) := l2FrontendAXI4Node ) } } val l2_frontend_bus_axi4 = InModuleBody { l2FrontendAXI4Node.makeIOs() }}case class HierarchicalBusTopologyParams( pbus: PeripheryBusParams, fbus: FrontBusParams, cbus: PeripheryBusParams, xTypes: SubsystemCrossingParams, driveClocksFromSBus: Boolean = true) extends TLBusWrapperTopology( instantiations = List( (PBUS, pbus), (FBUS, fbus), (CBUS, cbus)), connections = List( // (master, slave, params) // cbus := sbus (SBUS, CBUS, TLBusWrapperConnection .crossTo(xTypes.sbusToCbusXType, if (driveClocksFromSBus) Some(true) else None)), // pbus := cbus (CBUS, PBUS, TLBusWrapperConnection .crossTo(xTypes.cbusToPbusXType, if (driveClocksFromSBus) Some(true) else None)), // sbus := fbus (FBUS, SBUS, TLBusWrapperConnection.crossFrom(xTypes.fbusToSbusXType, if (driveClocksFromSBus) Some(false) else None))))

  6. 上一段代码中,在 sbus 的下游挂载了 cbus,在 cbus 下游挂载了 pbus;那么 debug/plic/clint 等设备都是挂载在 cbus 下的:
    // in HasPeripheryDebug of Periphery.scala// default to cbusprivate lazy val tlbus = locateTLBusWrapper(p(ExportDebug).slaveWhere)val tlDM = LazyModule(new TLDebugModule(tlbus.beatBytes))tlDM.node := tlbus.coupleTo("debug"){ TLFragmenter(tlbus.beatBytes, tlbus.blockBytes, nameSuffix = Some("Debug")) := TLBuffer() := _ }// in CanHavePeripheryCLINT of CLINT.scala// default to cbusval tlbus = locateTLBusWrapper(p(CLINTAttachKey).slaveWhere)val clintDomainWrapper = tlbus.generateSynchronousDomain("CLINT").suggestName("clint_domain")val clint = clintDomainWrapper { LazyModule(new CLINT(params, tlbus.beatBytes)) }clintDomainWrapper { clint.node := tlbus.coupleTo("clint") { TLFragmenter(tlbus, Some("CLINT")) := _ } }// in CanHavePeripheryPLIC of Plic.scala// default to cbusval tlbus = locateTLBusWrapper(p(PLICAttachKey).slaveWhere)val plicDomainWrapper = tlbus.generateSynchronousDomain("PLIC").suggestName("plic_domain")val plic = plicDomainWrapper { LazyModule(new TLPLIC(params, tlbus.beatBytes)) }plicDomainWrapper { plic.node := tlbus.coupleTo("plic") { TLFragmenter(tlbus, Some("PLIC")) := _ } }plicDomainWrapper { plic.intnode :=* ibus.toPLIC }

至此就把前面提到的 Rocket Chip 的总线结构在源码中的对应关系都找到了。

除了这一组大的总线结构,实际上调试模块内部还有一个小的总线,主要是把 RISC-V Debug 的 DMI 转化为 TileLink,然后访问内部的一些寄存器。

中断

除了总线,中断也是通过 Diplomacy 管理的。首先可以看到,每个 Tile 有一个中断的 SinkNode:

// Use diplomatic interrupts to external interrupts from the subsystem into the tiletrait SinksExternalInterrupts { this: BaseTile => val intInwardNode = intXbar.intnode :=* IntIdentityNode()(ValName("int_local")) protected val intSinkNode = IntSinkNode(IntSinkPortSimple()) intSinkNode := intXbar.intnode // go from flat diplomatic Interrupts to bundled TileInterrupts def decodeCoreInterrupts(core: TileInterrupts): Unit = { val async_ips = Seq(core.debug) val periph_ips = Seq( core.msip, core.mtip, core.meip) val seip = if (core.seip.isDefined) Seq(core.seip.get) else Nil val core_ips = core.lip val (interrupts, _) = intSinkNode.in(0) (async_ips ++ periph_ips ++ seip ++ core_ips).zip(interrupts).foreach { case(c, i) => c := i } }}class TileInterrupts(implicit p: Parameters) extends CoreBundle()(p) { val debug = Bool() val mtip = Bool() val msip = Bool() val meip = Bool() val seip = usingSupervisor.option(Bool()) val lip = Vec(coreParams.nLocalInterrupts, Bool()) val nmi = usingNMI.option(new NMI(resetVectorLen))}

它通过 Diplomacy 的 intXbar 输入多路的中断,然后按照顺序,还原出对应的 debug/mtip/msip/seip 等中断信号。从前面的图中,也可以看到 intXbar 的第一个输入 debug(经过 intsink)来自 dmOuter 也就是调试模块,第二个和第三个输入 msip 和 mtip(经过 intsink_1)来自 clint(负责时钟 mtimer 和软件中断),最后的 meip 和 seip(经过 intsink_2/3)来自 plic(负责外部中断)。为了处理外部中断,从外面接了 6 位的中断信号到 plic。

时钟

最后,时钟(时钟加上复位)也是由 Diplomacy 管理的:从前面的图中,从 aggregator 进来,首先到 sbus,然后分出来多路的时钟信号:第一路到 cbus,用于 cbus 的各个外设(plic/clint 等),进一步也从 cbus 引到 pbus;第二路到各个 tile;第三路到 coh(coherence wrapper);第四路到 fbus。默认配置下,这些时钟都是同一个信号,没有额外的处理,但是通过配置,可以把它们区分开,放到不同的时钟域,并在跨时钟域的时候,添加合适的跨时钟域的处理。

那么这些时钟是怎么分出来的呢:

  1. aggregator 把时钟暴露到 IO 上,然后内部暴露一个 allClockGroupsNode,连接到 sbus 上:
    // in BaseSubsystem.scalatrait HasConfigurablePRCILocations { this: HasPRCILocations => val ibus = LazyModule(new InterruptBusWrapper) val allClockGroupsNode = ClockGroupIdentityNode() val io_clocks = if (p(SubsystemDriveClockGroupsFromIO)) { val aggregator = ClockGroupAggregator() val source = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) allClockGroupsNode :*= aggregator := source Some(InModuleBody { val elements = source.out.map(_._1.member.elements).flatten val io = IO(Flipped(RecordMap(elements.map { case (name, data) => name -> data.cloneType }:_*))) elements.foreach { case (name, data) => io(name).foreach { data := _ } } io }) } else { None }}abstract class BaseSubsystem(val location: HierarchicalLocation = InSubsystem) (implicit p: Parameters) extends BareSubsystem with HasDTS with Attachable with HasConfigurablePRCILocations with HasConfigurableTLNetworkTopology{ // viewpointBus points to sbus by default viewpointBus.clockGroupNode := allClockGroupsNode}

  2. 前面提到,通过 CoherentBusTopologyParams,实现 mbus := coh := sbus 的连接,通过 HierarchicalBusTopologyParams,实现 pbus := cbus := sbus := fbus 的连接,与此同时,时钟也被接上了:
    // in BusTopology.scala// (master, slave, parameters)// from CoherentBusTopologyParams// coh := sbus, use sbus's clock for coh(SBUS, COH, TLBusWrapperConnection(driveClockFromMaster = Some(true), nodeBinding = BIND_STAR)()),// mbus := coh, use coh's clock for mbus by default(COH, MBUS, TLBusWrapperConnection.crossTo( xType = sbusToMbusXType, driveClockFromMaster = if (driveMBusClockFromSBus) Some(true) else None, nodeBinding = BIND_QUERY))// from HierarchicalBusTopologyParams// cbus := sbus, use sbus's clock for cbus by default(SBUS, CBUS, TLBusWrapperConnection .crossTo(xTypes.sbusToCbusXType, if (driveClocksFromSBus) Some(true) else None)),// pbus := cbus, use cbus's clock for pbus by default(CBUS, PBUS, TLBusWrapperConnection .crossTo(xTypes.cbusToPbusXType, if (driveClocksFromSBus) Some(true) else None)),// sbus := fbus, use sbus's clock for fbus by default(FBUS, SBUS, TLBusWrapperConnection.crossFrom(xTypes.fbusToSbusXType, if (driveClocksFromSBus) Some(false) else None)))

  3. 具体地,每个 bus 有一个自己的 clockGroupNode,bus 之间的 clockGroupNode 按照上面所属的方式连接,然后 bus 下面的设备再挂到 fixedClockNode 下面:
    abstract class TLBusWrapper(params: HasTLBusParams, val busName: String)(implicit p: Parameters) extends ClockDomain with HasTLBusParams with CanHaveBuiltInDevices{ private val clockGroupAggregator = LazyModule(new ClockGroupAggregator(busName){ override def shouldBeInlined = true }).suggestName(busName + "_clock_groups") private val clockGroup = LazyModule(new ClockGroup(busName){ override def shouldBeInlined = true }) val clockGroupNode = clockGroupAggregator.node // other bus clock groups attach here val clockNode = clockGroup.node val fixedClockNode = FixedClockBroadcast(fixedClockOpt) // device clocks attach here private val clockSinkNode = ClockSinkNode(List(ClockSinkParameters(take = fixedClockOpt))) clockGroup.node := clockGroupAggregator.node fixedClockNode := clockGroup.node // first member of group is always domain's own clock clockSinkNode := fixedClockNode def clockBundle = clockSinkNode.in.head._1}// in ClockDomain.scalaabstract class Domain(implicit p: Parameters) extends LazyModule with HasDomainCrossing{ def clockBundle: ClockBundle lazy val module = new Impl class Impl extends LazyRawModuleImp(this) { childClock := clockBundle.clock childReset := clockBundle.reset override def provideImplicitClockToLazyChildren = true // these are just for backwards compatibility with external devices // that were manually wiring themselves to the domain's clock/reset input: val clock = IO(Output(chiselTypeOf(clockBundle.clock))) val reset = IO(Output(chiselTypeOf(clockBundle.reset))) clock := clockBundle.clock reset := clockBundle.reset }}

Rocket Chip 中用 Diplomacy 实现 TileLink 总线的连接。涉及到的相关结构如下:

  1. TLBundle:代表 TileLink 总线的接口,根据 TLBundleParameters 例化
  2. TLMasterPortParameters:信息 TileLink Master 的信息,从 Upstream 向 Downstream 传递
  3. TLSlavePortParameters:信息 TileLink Slave 的信息,从 Downstream 向 Upstream 传递
  4. TLEdgeOut:记录 Outward 边,也就是 Master 侧的 TileLink 的信息
  5. TLEdgeIn:记录 Inward 边,也就是 Slave 侧的 TileLink 的信息
  6. TLImp: extends NodeImp[TLMasterPortParameters, TLSlavePortParameters, TLEdgeOut, TLEdgeIn, TLBundle],基于这个类型来导出各种类型的 TileLink Node
  7. TLXBar:TileLink 的 Crossbar,生成一个继承 NexusNode 的 TLNexusNode,它的信息传递方式是,把下游的各个 Slave 信息拼起来传给上游,使得 Master 可以看到所有 Slave 的信息;把上游的各个 Master 信息拼起来传给下游,使得 Slave 可以看到所有 Master 的信息
  8. TLToAXI4:生成一个继承 AdapterNode 的 TLToAXI4Node,把 TileLink Master 转成 AXI4 Master,把上游的 TileLink Master 信息转换为 AXI Master 传递给下游,把下游的 AXI Slave 信息转换为 TileLink Slave 传递给上游

参考文档

Generated by RSStT. The copyright belongs to the original author.

Source

Report Page