下面分别对cake pattern中的关键概念进行说明,最后以chipyard为例对cake pattern的用法进行说明,cake pattern对Diplomacy机制至关重要。
一、Lazy Module
定义惰性模块,实现延迟加载,这和scala中lazy val的概念有些相似,但在chisel中,lazy module是diplomacy的基础。 LazyModule提供一个框架,以容纳内部LazyModule和节点, LazyModuleImp实现LazyModule的逻辑,如果把LazyModule和LazyModuleImp作为一个整体看的话,又可以把他们理解为一个硬件模块:包含了输入输出,包含了内部的子模块(LazyModule)和子节点(Node)。
下图是一个Diplomacy框架下的模块:
- LazyModule:用于进行参数协商。可以理解为对内部Chisel代码的一个封装。
- LazyModuleImp:内部包含实际的Chisel代码,在chisel实际进行电路生成的过程中,可以访问在上一步Diplomacy协商过程中得到edge中的参数进行实际电路构架。
- node:进行参数传递和自动连线的接口,需要在
LazyModule
内部进行声明,一个LazyModule
可以有多个node。node之间使用:=
在在该模块外部进行绑定声明,这里的绑定并不是Chisel中的连线操作,而是Diplomacy中的互联关系声明。Diplomacy在运行过程中,参数会沿着node绑定形成的有向无环图进行传递。 - edge:edge用于描述两个node之间的连接关系的数据结构的统称,每次绑定,就会在这两个node之间例化一组edge。edge具体包括例化两侧模块所需的参数;用于自动化接线的信号类型;用于进行模块间交互所用的方法。
在使用Diplomacy协议后,硬件生成前需要进行参数协商,根据用户对LazyModule
的声明和对node的绑定声明生成描述各个模块之间连接关系的数据结构,Diplomacy按照这个图中进行参数传播并依此例化每个模块的外壳,每个模块根据自己的需要抓取来自协商阶段传递得到的参数并进行符合自身要求的检查。将模块的外壳用线进行互联,线的类型来自上一步的参数传递得到:标准总线协议或clock、中断等。
在chipyard中,用cake pattern进行混入新特征时也要遵循Diplomacy,反映到底层即需要继承两个"cake",一个是"LazyModule",一个是"LazyModuleImp"。
The lazy module defines all the logical connections between generators and exchanges configuration information among them, while the lazy module implementation performs the actual Chisel RTL elaboration.
DigitalTop的config,遵循cake pattern
class DigitalTop(implicit p: Parameters) extends ChipyardSystem with testchipip.CanHavePeripheryCustomBootPin // Enables optional custom boot pin with testchipip.HasPeripheryBootAddrReg // Use programmable boot address register with testchipip.CanHaveTraceIO // Enables optionally adding trace IO with testchipip.CanHaveBackingScratchpad // Enables optionally adding a backing scratchpad with testchipip.CanHavePeripheryBlockDevice // Enables optionally adding the block device with testchipip.CanHavePeripheryTLSerial // Enables optionally adding the backing memory and serial adapter with sifive.blocks.devices.i2c.HasPeripheryI2C // Enables optionally adding the sifive I2C with sifive.blocks.devices.pwm.HasPeripheryPWM // Enables optionally adding the sifive PWM with sifive.blocks.devices.uart.HasPeripheryUART // Enables optionally adding the sifive UART with sifive.blocks.devices.gpio.HasPeripheryGPIO // Enables optionally adding the sifive GPIOs with sifive.blocks.devices.spi.HasPeripherySPIFlash // Enables optionally adding the sifive SPI flash controller with sifive.blocks.devices.spi.HasPeripherySPI // Enables optionally adding the sifive SPI port with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget with chipyard.example.CanHavePeripheryStreamingFIR // Enables optionally adding the DSPTools FIR example widget with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the DSPTools streaming-passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA with chipyard.clocking.HasChipyardPRCI // Use Chipyard reset/clock distribution with fftgenerator.CanHavePeripheryFFT // Enables optionally having an MMIO-based FFT block with constellation.soc.CanHaveGlobalNoC // Support instantiating a global NoC interconnect { override lazy val module = new DigitalTopModule(this) } class DigitalTopModule[+L <: DigitalTop](l: L) extends ChipyardSystemModule(l) with testchipip.CanHaveTraceIOModuleImp with sifive.blocks.devices.i2c.HasPeripheryI2CModuleImp with sifive.blocks.devices.pwm.HasPeripheryPWMModuleImp with sifive.blocks.devices.uart.HasPeripheryUARTModuleImp with sifive.blocks.devices.gpio.HasPeripheryGPIOModuleImp with sifive.blocks.devices.spi.HasPeripherySPIFlashModuleImp with sifive.blocks.devices.spi.HasPeripherySPIModuleImp with chipyard.example.CanHavePeripheryGCDModuleImp with freechips.rocketchip.util.DontTouch
二、Outer and Inner
Note that the outer class extends
LazyModule
, and the inner class extendsLazyModuleImp
--LazyModuleImp
is passed a reference to the matching outer class. Both are defined in the Diplomacy package.
即”一“中实现Diplomacy的lazymodule和lazymoduleImp,下面看一个例子来说明:
//This is the outer class class Example1TileModule(implicit p: Parameters) extends LazyModule { // When creating a class (module) that has a TL connection, there is special code that is in a library // and has to be pulled in. This special code is what participates in Diplomacy negotiations and this // special code implements the actual TL wires. Below, TLClientNode is the name of this special code // (there are a few other types of such special code in the TL library). It will generate verilog, and // the verilog wires of the TL channels will be wired to the verilog generated from TLClientNode. // It is weird syntax because of Scala language mechanics. Use "Seq" to package multiple parameters // that are passed to TLClientNode. Best to just copy-paste and follow the pattern. val Node0 = TLClientNode(Seq(TLClientPortParameters( Seq(TLClientParameters( name = "IdentifierForThisTLConnection", // shows up when Diplomacy reports an error sourceId = IdRange(0, 1)))))) // says at most 1 TL transaction can be outstanding (in-flight) // on this TL connection, increase if want more // Instantiate the inner module! By instantiating here, the inner module can create Chisel wires to node0 lazy val module = new ExampleTileModuleImp(this) //Note, this all that you put in outer module!! All logic, wires, etc go inside inner module. } // This is the inner class. This is where the RTL behavior code goes. class Example1TileModuleImp (outer: ExampleTileModule) extends LazyModuleImp(outer) { val (tl0, edge0) = outer.dmanode0.out(0) val addrBits0 = edge0.bundle.addressBits val beatBytes = (edge0.bundle.dataBits / 8) val blackbox_inst = Module(new tile_blackbox) tl0.a <> blackbox_inst.io.tile_a_chan tl0.d <> blackbox_inst.io.tile_d_chan }
三、Mixins
Mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. Mixins are sometimes described as being "included" rather than "inherited". //定义
Mixins generally use the "trait" keyword and the "with" keyword. The thing you want included is declared using "trait" keyword, and then included using "with" keyword.. (However, once you have created at trait, you can still use "extends" to inherit that trait as the superclass.) //用法
Here lies the important difference between the concepts of mixins and inheritance, in that the child class can still inherit all the features of the parent class, but, the semantics about the child "being a kind of" the parent need not be necessarily applied. //区别
一个简单的实例:
abstract class A { val message: String } class B extends A { val message = "I'm an instance of class B" } trait C extends A { def loudMessage = message.toUpperCase() } class D extends B with C val d = new D println(d.message) // I'm an instance of class B println(d.loudMessage) // I'M AN INSTANCE OF CLASS B
在上面的例子中,D有一个父类B和一个混入特征C,class可以有多个mixins但只能有一个superclass。
更多有关mixins的内容参见:trait, with, Mixins · librecores/riscv-sodor Wiki (github.com)
https://docs.google.com/document/d/1uitsrMXCNGNCOWTkbR36DLpZAZm2y-Ss7Fu1w3Y7wPA/edit
有关cake pattern的更多信息,参见:Cake Pattern · Intensivate/learning-journey Wiki (github.com)
四、Chipyard中扩展MMIO
chipyard版本:1.8.1
为了基于tilelink添加MMIO外设,我们需要做三件事:
(1)在generators/chipyard/src/main/scala/example目录下创建我们要包含的外设模块
(2)修改DigitalTop.scala以混入特征
(3)在RocketConfig.scala中添加新的配置
为了进行验证,我们需要做两件事:
(1)产生一个仿真器,没有vcs可以用verilator
(2)写c程序,交叉编译验证
1,params和key
2,IO接口
有关verilog和chisel的接口不同,可以参考https://github.com/Intensivate/learning-journey/wiki/Comparison-to-Verilog
将上面创建的IO接口封装成trait用于mixins
3,chisel外设模块
4,Memory Mapping
顶层控制信号
5,用TLRegisterRouter将外设包装进TL接口
6,创建inner和outer twins用于cake pattern
In Rocket-chip world this is done using common practice known as the Cake Pattern. Basically, this means there will be two more traits – one of them is called inner twin, the other outer twin. The former holds the implementation of our widget, i.e. all the stuff we wrote (
ChiselModule
wrapped inHasRegMap
extension wrapped in TileLink Register Router node) and it’s pretty much unaware of the outside world (the rest of SoC). The latter extendsLazyModule
, wherelazy
stands for the code that gets executed before any of the hardware (implementation = inner twin) is elaborated.
7,mixing
编辑Digitaltop.scala,混入outer和inner trait
在rocketconfigs.scala中添加新的配置
最后在verilator中make,遗憾报错:
根据提示warn可以知道:
method toVariableWidthSlave in trait CanAttachTLSlaves is deprecated (since rocket-chip 1.3): Replace with e.g. bus.coupleTo(s"slave_named_${name}"){ slave.controlXing(NoCrossing) :*= TLFragmenter(bus.beatBytes, bus.blockBytes) :*= _ }
在仔细检查语法和缩进后,warning消失,但error仍在。
在尝试运用原来教程的config配置后,显示iobinder中trait不存在,显然这是由于版本原因,以前的教程在1.8.1版本上不适用了。
所以唯一可以参考的就是官方doc,尝试仿照GCD.scala进行改写:
标签:LazyModule,adding,optionally,Diplomacy,Pattern,chip,Cake,Enables,class From: https://www.cnblogs.com/hwzhao/p/17125261.html