Spinal的lib.bus除了AMBA总线,并且还支持AvalonMM,当然你也很容易扩展自定义的总线。
Apb3总线例化 一个简单的Apb3总线接口例化示例
1 2 3 4 5 6 7 import spinal.lib.bus.amba3.apb._class T2 extends Component { val busin = slave(Apb3 (Apb3Config (12 ,32 ))) val busout = master(Apb3 (Apb3Config (12 ,32 ))) busout <> busin } SpinalVerilog (new T2 )
总线的例化也非方便简单,`Apb3` 传入Apb3Config(addrWidth=12,dataWidth=32)即可生成一个总线,关键字slave,master设计非常漂亮,可以瞬间 指定总线内部的接口方向,除此之外,`\<>` 操作符为SpinalHDL定义的总线自动互联函数,不用你手动一个一个连。像这类操作符SpinalHDL有很多,像\”>>\” \”>-/->\”, \”\</\<\” 等等,不用害怕,这些只不过是定义的一个恰巧叫这个符号的函数而已,本质上跟你定义的autoConnect名字的函数并无二致,但是它要比文字函数要形象的多,并且假装操作符也是那么的自然。除了spinalHDL定义的操作符以外,你自己也可以定义各种各样简便的符号帮助你完成复杂琐碎的事情。41 可以看到生成的Verilog总线已经完全连上了。
Apb3总线译码 Apb3Decoder是一个工厂函数,它支持多种参数的传入
1 2 3 4 Apb3Decoder (Apb3Config (16 ,32 ), List ((0x0000 ,2 KiB ), (0x1000 ,1 KiB ), (0x5000 ,3 KiB ))))
可以得到42
1 2 3 4 5 6 7 8 9 10 11 12 class Top extends Component { val m0 = slave(Apb3 (Apb3Config (16 ,32 ))) val s1 = master(Apb3 (Apb3Config ( 8 ,32 ))) val s2 = master(Apb3 (Apb3Config (12 ,32 ))) val s3 = master(Apb3 (Apb3Config (12 ,32 ))) val s4 = master(Apb3 (Apb3Config ( 2 ,32 ))) val mux = Apb3Decoder (master = m0, slaves = List (s1 -> (0x0000 , 64 ), s2 -> (0x1000 ,1 KiB ), s3 -> (0x2000 ,3 KiB ), s4 -> (0x3000 , 32 )))
得到:
43
Axi-crossbar Spinal pinsec的总线拓扑结构44
源码:45
这么复杂一个链接关系,在SpinalHDL上不到50行代码,相信一般的伪代码都做不到这么简洁。因此阅读Spinal的源码简直就是一种享受,有时候我在恍惚这是文档还是代码。
想想一下,如果这个工作让你用Verilog实现,那是多么的操蛋。当然目前cross-bar都是由脚本或者ARM的工具来自动生成。但是对于外挂,互联,顶层的例化 还得要手动做,这同样是一件极其繁琐容易出错的工作。但是现在放在Scala上是一个极其简单的伪代码式的描述即可完成。除此之外还可以做一些高级的检查, 参数的协商,DAG(有向无环图)的检查都很容易办到。
总线转换 AHB总线到APB总线的转换示例
1 2 3 4 5 6 7 8 9 10 import spinal.lib.bus.amba3.apb._import spinal.lib.bus.amba3.ahblite._class Top (ahbConfig:AhbLite3Config , apbConfig:Apb3Config ) extends Component { val ahb = slave(AhbLite3 (ahbConfig)) val apb = master(Apb3 (apbConfig)) val bridge = AhbLite3ToApb3Bridge (ahbConfig,apbConfig) ahb <> bridge.io.ahb apb <> bridge.io.apb }
AXI到APB3总线转换示例
1 2 3 4 5 6 7 8 9 10 import spinal.lib.bus.amba3.apb._import spinal.lib.bus.amba4.axi._class Top (aw: Int ,dw: Int ,iw: Int ) extends Component { val axi = slave(Axi4Shared (Axi4Config (aw,dw,iw))) val apb = master(Apb3 (Apb3Config (aw,dw,iw))) val bridge = Axi4SharedToApb3Bridge (aw,dw,iw) axi <> bridge.io.axi apb <> bridge.io.apb }
以下是pinsec中的源码,创建一个AXI到APB3的桥,然后从桥可以互联到其他外围。
1 2 3 4 5 6 7 8 9 10 val apbDecoder = Apb3Decoder ( master = apbBridge.io.apb, slaves = List ( gpioACtrl.io.apb -> (0x00000 , 4 KiB ), gpioBCtrl.io.apb -> (0x01000 , 4 KiB ), uartCtrl.io.apb -> (0x10000 , 4 KiB ), timerCtrl.io.apb -> (0x20000 , 4 KiB ), vgaCtrl.io.apb -> (0x30000 , 4 KiB ), core.io.debugBus -> (0xF0000 , 4 KiB )) )
可以非常方便的将这些外围连起来:46 以上的代码绝大多数都能直接运行起来,能用如此简单寥寥几行就将原来复杂繁琐的工作安全可靠的完成,还有什么理由不使用它。