JiJingGuo

2019.9.1

Parametric Design

SpinalHDL

fishsemi.com

Verilog不够用吗?

  • 例化不方便,大量的连线(插件只能解决部分问题)
  • 大量的重复声明(无休止的中间信号位宽声明)
  • 函数不能带参数(摆设)
  • 模块参数化能力弱鸡(基于宏,parameter不支持函数)
  • 错误检测能力弱(需要依赖EDA工具检查错误)
  • 重构、增减信号麻烦,即便是依赖脚本,也不通用
  • 低级错误注入频繁,增加验证成本,增长验证收敛周期
  • 基础电路重复书写,工程身心疲惫
  • 电路复用程度低,开发周期跟模块大小成线性关系

生产工具决定生产效率

fishsemi.com

Designer

Compile

  • 位宽不能写错
  • 端口不能写反
  • 小心溢出,做好饱和
  • 端口自己连
  • 跨时钟要小心
  • 锁存器要小心
  • 组合逻辑环自己查
  • 改1bit信号,要改一圈代码
  • ....

这些统统我不管

怎么让工具解决这些问题?

Pay For EDA

Situation

fishsemi.com

五个灵魂追问

  • 能不能愉悦的开发?不想干重复的脏活
  • 能不能参数化设计?想一劳永逸
  • 能不能方便的复用?我懒 
  • 能不能检查错误    ?我粗心
  • 能不能扩展它        ?emm... 这个... 老板肯定会问

能不能?

Scala

SCALA

fishsemi.com

Scala能做什么

  • 类型推断,脚本的使用体验,静态语言的安全性
  • 函数式编程,非常适合做DSL
  • 惰性求值,Talk before Compile
  • 模式匹配,分支编译器帮你完成,减轻程序员负担
  • 面向对象,参数化是最基本的素质
  • 强大的推断能力,自定义各种检查规则

Compile

Engineer

Why Not ?

最少的信息写出最正确的程序

fishsemi.com

  • 非常好用的位宽推断(甚至跨模块边界
  • 错误检查能力(Lint, CDC, timing评估, more)
  • 彻底的参数化能力
  • 大量的基础组件以及可重用IP
  • 至少一个完整的Soc框架
  • 继承Scala平台所有强大的语言特性

泛CHISEL特性

  • 解放工程师,避免重复琐碎,避免低级错误
  • 加快开发周期,开发周期和模块大小不再是线性,有望指数

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

多级级联滤波器功能需求

  • 滤波器有多级(默认3级), 每一级为对称n(默认33)阶滤波器
  • 每一级支持上采样和下采样,采样倍数1、2、3、4、5可配
  • 每一级支持bypass
  • 每一级系数可配
  • 每一级增益可配
  • 滤波器的数据输出方式
    • 每级滤波器支持单独冲刷
    • 每级滤波器要补零将尾巴输出
    • 滤波器级联连续输出

fishsemi.com

多级联滤波器组

fishsemi.com

定义一个FIR的参数

case class FIRConfig(iqWidth: Int = 8,
                     coefWidth: Int = 8,
                     tapNumbers: Int = 33,
                     maxUpSmpTimes : Int = 5
                     )
{
  def cyclesPerSmp(): Int = (tapNumbers>>1) + 1
  def cyclesPerSmp_(): Int = cyclesPerSmp - 1
  val accWidth: Int = iqWidth + coefWidth + log2Up(tapNumbers)
  val fixSelWidth: Int = log2Up(coefWidth + log2Up(tapNumbers))
  def busWordNumbers: Int = cyclesPerSmp() + 2
  val busSize = 32
  val spaceSize = scala.math.pow(2,log2Up(busWordNumbers)).toInt * (busSize/8) Byte
  val bmiConfig = BmiConfig(log2Up(busWordNumbers)+log2Up(32/8),busSize)
}

fishsemi.com

可见参数

内部参数计算

`define iqWidth        8 
`define coefWidth      8 
`define tapNumbers     33 
`define maxUpsmpTimes  5 

`define accWidth  = `iqWidth + `coefWidth +  log2Up(tapNumbers) ??
`define fixWidth  = log2up(`coefWidth + log2Up(tapNumbers)) ??

缺乏最基础的数学运算库

要么手算,要么笨拙的右移判断

Verilog

SpinalHDL

涉及到稍微复杂点的

参数运算就无能为力

定义Bus配置

fishsemi.com

module FIR(/*autoarg*/);
input                 clk;
input                 rstn;
 //sw config interface 
 input                 smpUp;
 input [`smpWidth-1:0] smpStep;
 input [`fixWidth-1:0] fixSel;
 input                 bypass;
 //smp in data interface 
 input [`iqWidth-1:0]  in_I;
 input [`iqWidth-1:0]  in_Q;
 input                 in_valid;
 output                to_in_ready;
 //filter out data interface 
 output [`iqWidth-1:0] out_I;
 output [`iqWidth-1:0] out_Q;
 output                out_valid;
 input                 to_out_ready;

采样类型、采样倍数

位宽截取

输入IQ数据

添加握手信号

输出IQ-stream

 //logic 

  ...
 
 
 endmodule
class FIR(fc: FIRconfig) extends Component{
  val swif  = in SWif(fc)
   val iqIn  = slave  Stream(IQ(fc.iqWidth bits))
   val iqOut = master Stream(IQ(fc.iqWidth bits))
   //logic
   ....
 }

Verilog

SpinalHDL

参数化FIR模块实例

case class FIR(fc: FIRConfig) extends Component {
  val io = new Bundle {
    val bmi = slave(Bmi(fc.bmiConfig))
    val ins = slave Stream (IQ(fc.iqWidth bits))
    val ots = master Stream (IQ(fc.iqWidth bits))
  }
  val u_ctrl = new FIRCtrl(fc)
  u_ctrl.io.ready := io.ots.ready
  val u_core = new FIRCore(fc)
  u_core.io.ins      << u_ctrl.io.iq2Core
  u_core.io.tapRaddr << u_ctrl.io.tapRaddr
  u_core.io.bmi      << io.bmi
  u_core.io.flush    := io.flush
  u_ctrl.io.swif     := u_core.io.swif
  when(u_core.io.swif.bypass) {
    io.ots << io.ins
  }.otherwise{
    io.ots << u_core.io.ots.toStream.queue(1)
  }
}

Config伴侣

fishsemi.com

定义级联滤波器顶层参数

case class FilterGroupConfig(stagesNumber: Int = 3,
                             hwFreq: HertzNumber = 200 MHz,
                             sampleFreq: HertzNumber = 1.92 MHz){
  val firsConfig: Array[FIRConfig] = Array.fill(stagesNumber)(FIRConfig(8,8,33))
}
val u3_fgc = FilterGroupConfig(3, 200 MHz, 1.92 Mhz)

u3_fgc.firsConfig(0) = FIRConfig(8,8,33,6)
u3_fgc.firsConfig(1) = FIRConfig(16,16,21,5)
u3_fgc.firsConfig(2) = FIRConfig(8,8,63,3)
val u4_fgc = FilterGroupConfig(5, 400 MHz, 1.92 Mhz)
for(i <- 0 until u4_fgc.length){
    u4_fgc.firsConfig(i) = FIRConfig(16,16,23,5)
}
//Array.copy(Array.fill(5)(FIRconfig(16,16,23,5)),0,u4_fgc.firsConfig,0,5)

定制FIR的配置

定制5级滤波器

fishsemi.com

fishsemi.com

class FilterGroupTop(fgc: FilterGroupConfig) extends Component{
  val io = new Bundle{
    val ahb   = slave(AhbLite3(fgc.ahbConfig))
    val ins   = slave Flow(IQ(fgc.firsConfig.head.iqWidth bits))
    val ots   = master Stream(IQ(fgc.firsConfig.last.iqWidth bits))
  }
  val u_fifo_in = StreamFifoCC(//aSync Fifo
    dataType  = io.ins.payload,
    depth     = 8,
    pushClock = ClockDomain.external("smp"),//1.92 MHz Sample clock
    popClock  = clockDomain //default 200 MHz HW clock
  )
  io.ins.toStream  >> u_fifo_in.io.push
  val u_fir0 = FIR(fgc.firsConfig(0))
  val u_fir1 = FIR(fgc.firsConfig(1))
  val u_fir2 = FIR(fgc.firsConfig(2))
  u_fifo_in.io.pop >> u_fir0.io.ins
  val u_fifo_out = StreamFifo(
    dataType = u_fir2.io.ots.payload,
    depth    = 16)  
  u_fir2.io.ots >> u_fifo_out.io.push
  io.ots << u_fifo_out.io.pop
}

u_fifo_in

u_fir0

u_fifo_out

u_fir1

u_fir2

  u_fir0 --> u_fir1 --> u_fir2

FilterGroupTop

class FIR {
  ...
  def -->(that: FIR) = {
    this.io.ots >> that.io.ins
    that
  }
}

代码即文档

伪代码也不过如此

想象一下用

Verilog组织该设计

设计自己的组件

fishsemi.com

case class IQ(w: BitCount) extends Bundle{
  val I = SInt(w)
  val Q = SInt(w)
  def toBits: Bits = (I @@ Q).asBits
  def +(that: IQ) : IQ = {
    val ret = IQ((this.width max that.width)  bits)
    ret.I := this.I + that.I
    ret.Q := this.Q + that.Q
    ret
  }
  def +!(that: IQ) : IQ = {}
  def *[T <: SInt](coef: T): IQ = {
    val ret = IQ(this.width + coef.getWidth bits)
    ret.I := this.I * coef
    ret.Q := this.Q * coef
    ret
  }
  def *(that: IQ): IQ = {}
  def init[T <: Data](x: T): IQ = {}
  def clear() = {
    this.I = 0
    this.Q = 0
    this
  }
 }
val ins      = slave Flow(IQ(fc.iqWidth + 1 bits))
val iqXcoef  = RegNextWhen(ins.payload * io.coef, ins.valid) init 0
val Acc      = Reg(IQ(fc.accWidth bits)) init 0
when(accClear) {
    Acc.clear
  }.elsewhen(accValid) {
    Acc := Acc + iqXcoef.resized
  }

可以像原生类型一样定义+-* init clear等方法

在通信IP设计中IQ数据流是一对伴生基友,

对于这种常见的信号可以将它包装成组件,设计常用方法

fishsemi.com

SpinalVerilog(new FirGroupTop(FirGroupConfig()))
      defaultConfigForClockDomains = ClockDomainConfig(resetKind = ASYNC,
        clockEdge = RISING, 
        resetActiveLevel = LOW),
      targetDirectory="rtl/")
      .generate(new FilterGroupTop(u3_filterGroupConfig))

生成Verilog代码

SpinalConfig(mode = Verilog,
 

默认生成到当前目录,异步上升沿复位

也可以自定义,包括目录,默认时钟类型,模块命名空间 等等

异步复位定义成下降沿

fishsemi.com

  case class SpinalConfig(
    mode                           : SpinalMode = null,
    flags                          : mutable.HashSet[Any] = mutable.HashSet[Any](),
    debugComponents                : mutable.HashSet[Class[_]] = mutable.HashSet[Class[_]](),
    keepAll                        : Boolean = false,
    defaultConfigForClockDomains   : ClockDomainConfig = ClockDomainConfig(),
    onlyStdLogicVectorAtTopLevelIo : Boolean = false,
    defaultClockDomainFrequency    : IClockDomainFrequency = UnknownFrequency(),
    targetDirectory                : String = ".",
    oneFilePerComponent            : Boolean = false,
    netlistFileName                : String = null,
    dumpWave                       : DumpWaveConfig = null,
    globalPrefix                   : String = "",
    var privateNamespace           : Boolean = false,
    var formalAsserts              : Boolean = false,
    anonymSignalPrefix             : String = null,
    device                         : Device = Device(),
    inlineRom                      : Boolean = false,
    genVhdlPkg                     : Boolean = true,
    verbose                        : Boolean = false,
    mergeAsyncProcess              : Boolean = false,
    asyncResetCombSensitivity      : Boolean = false,
    anonymSignalUniqueness         : Boolean = false,
    noRandBoot                     : Boolean = false,
    randBootFixValue               : Boolean = true,
    noAssert                       : Boolean = false,
    phasesInserters                : ArrayBuffer[(ArrayBuffer[Phase]) => Unit] = ...,
    transformationPhases           : ArrayBuffer[Phase] = ...,
    memBlackBoxers                 : ArrayBuffer[Phase] = ...,
    rtlHeader                      : String = null,
    private [core] var _withEnumString : Boolean = true
  ){

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

如何用SpinalHDL生成一个Soc系统


import spinal.lib.soc.pinsec._

SpinalVerilog(new Pinsec(500 MHz))

fishsemi.com

如何用定制PinSec

可插拔式的CPU定制

val myCpuConfig = RiscvCoreConfig(
        pcWidth = 32,
        addrWidth = 32,
        startAddress = 0x00000000,
        regFileReadyKind = sync,
        branchPrediction = dynamic,
        bypassExecute0 = true,
        bypassExecute1 = true,
        bypassWriteBack = true,
        bypassWriteBackBuffer = true,
        collapseBubble = false,
        fastFetchCmdPcCalculation = true,
        dynamicBranchPredictorCacheSizeLog2 = 7
      ) 
myCpuConfig.add(new MulExtension)
myCpuConfig.add(new DivExtension)
myCpuConfig.add(new BarrelShifterFullExtension)
myCpuConfig.add(new MyFloatExtension)
myCpuConfig.add(new MyVectorExtension) 
val myiCacheConfig = InstructionCacheConfig(
        cacheSize    = 4096,
        bytePerLine  = 32,
        wayCount     = 1,  

iCache 配置

val mySocConfig = PinsecConfig(
    axiFrequency   = 100 MHz,
    onChipRamSize  = 4 KiB,
    sdramLayout    = IS42x320D.layout,
    sdramTimings   = IS42x320D.timingGrade7,
    cpu            = myCpuConfig,
    iCache         = myiCacheConfig)
        wrappedMemAccess = true,
        addressWidth = 32,
        cpuDataWidth = 32,
        memDataWidth = 32
      )

SpinalVerilog(new Pinsec(mySocConfig))

fishsemi.com

一探究竟

class Pinsec(config: PinsecConfig) extends Component{
  val io = new Bundle{
    //Clocks / reset
    val asyncReset = in Bool
    val axiClk     = in Bool
    val vgaClk     = in Bool

    //Main components IO
    val jtag       = slave(Jtag())
    val sdram      = master(SdramInterface(sdramLayout))

    //Peripherals IO
    val gpioA      = master(TriStateArray(32 bits))
    val gpioB      = master(TriStateArray(32 bits))
    val uart       = master(Uart())
    val vga        = master(Vga(vgaRgbConfig))
    val timerExternal = in(PinsecTimerCtrlExternal())
  }
  ...
  io.gpioA          <> axi.gpioACtrl.io.gpio
  io.gpioB          <> axi.gpioBCtrl.io.gpio
  io.timerExternal  <> axi.timerCtrl.io.external
  io.jtag           <> axi.jtagCtrl.io.jtag
  io.uart           <> axi.uartCtrl.io.uart
  io.sdram          <> axi.sdramCtrl.io.sdram
  io.vga            <> axi.vgaCtrl.io.vga
}

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

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
}

SpinalHDL总线简介

- Apb3

- AhbLite3

- AxiLite4

- Axi4

- AvalonMM

module T2 (
      output [11:0] busout_PADDR,
      output [0:0] busout_PSEL,
      output  busout_PENABLE,
      input   busout_PREADY,
      output  busout_PWRITE,
      output [31:0] busout_PWDATA,
      input  [31:0] busout_PRDATA,
      input   busout_PSLVERROR,
      input  [11:0] busin_PADDR,
      input  [0:0] busin_PSEL,
      input   busin_PENABLE,
      output  busin_PREADY,
      input   busin_PWRITE,
      input  [31:0] busin_PWDATA,
      output [31:0] busin_PRDATA,
      output  busin_PSLVERROR);
  assign busout_PADDR = busin_PADDR;
  assign busout_PSEL = busin_PSEL;
  assign busout_PENABLE = busin_PENABLE;
  assign busin_PREADY = busout_PREADY;
  assign busout_PWRITE = busin_PWRITE;
  assign busout_PWDATA = busin_PWDATA;
  assign busin_PRDATA = busout_PRDATA;
  assign busin_PSLVERROR = busout_PSLVERROR;
endmodule 
SpinalVerilog(new T2)

端口并没有强制放io Bundle的要求

  • 生成Verilog
  • 简单Apb示例
  • SpinalHDL支持的总线类型

fishsemi.com

Apb3Decoder(Apb3Config(16,32),

总线译码

            List((0x0000,2 KiB),
                 (0x1000,1 KiB),
                 (0x5000,3 KiB))))

传递Apb3Config

  • 总线译码
  • 总线路由
class Top extends Component{
    val din  =  slave(Apb3(Apb3Config(16,32)))
    val do1  = master(Apb3(Apb3Config( 8,32)))
    val do2  = master(Apb3(Apb3Config(12,32)))
    val do3  = master(Apb3(Apb3Config(12,32)))
    val do4  = master(Apb3(Apb3Config( 2,32)))

地址Mapping

val mux = Apb3Decoder(master = din, 
                      slaves = List(do1 ->  (0x0000,  64 ),
                                    do2 ->  (0x1000,1 KiB),
                                    do3 ->  (0x2000,3 KiB),
                                    do4 ->  (0x3000,  32 )))

传递apb总线实例

fishsemi.com

val axiCrossbar = Axi4CrossbarFactory()

axiCrossbar.addSlaves(
  ram.io.axi       -> (0x00000000L,   onChipRamSize),
  sdramCtrl.io.axi -> (0x40000000L,   sdramLayout.capacity),
  apbBridge.io.axi -> (0xF0000000L,   1 MiB)
)

总线路由

axiCrossbar.addConnections(
  core.io.i       -> List(ram.io.axi, sdramCtrl.io.axi),
  core.io.d       -> List(ram.io.axi, sdramCtrl.io.axi, apbBridge.io.axi),
  jtagCtrl.io.axi -> List(ram.io.axi, sdramCtrl.io.axi, apbBridge.io.axi),
  vgaCtrl.io.axi  -> List(                              sdramCtrl.io.axi)
)
axiCrossbar.addPipelining(apbBridge.io.axi)((crossbar,bridge) => {
  crossbar.sharedCmd.halfPipe() >> bridge.sharedCmd
  crossbar.writeData.halfPipe() >> bridge.writeData
  crossbar.writeRsp             << bridge.writeRsp
  crossbar.readRsp              << bridge.readRsp
})
  • Pinsec SOC总线路由源码
axiCrossbar.addPipelining(sdramCtrl.io.axi)((crossbar,ctrl) => {
  crossbar.sharedCmd.halfPipe()  >>  ctrl.sharedCmd
  crossbar.writeData            >/-> ctrl.writeData
  crossbar.writeRsp              <<  ctrl.writeRsp
  crossbar.readRsp               <<  ctrl.readRsp
})

axiCrossbar.build()

为slave 分配地址空间

指定拓扑关系

手动插入Pipeline打断路径

  • 拓扑结构

想象一下

Verilog代码

fishsemi.com

import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.amba3.ahblite._

SpinalHDL总线转换

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
}

showRtl(new Top(AhbLite3Config(16,32),Apb3Config(16,32)))
  • Apb3地址分配
  • AhbLiteToApb3Bridge
  • Axi4SharedToApb3Bridge
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
}

showRtl(new Top(20,32,2))
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)
  )
)

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

简单示例

class Top extends Component{
    val a = in Bits(8 bits)
    val b = RegNext(a) init 0
}
showRtl(new Top)
class Top extends Component{
    val myclk,myrst = in Bool()
    val a = in Bits(8 bits)
    val b = out Bits()
    new ClockingArea(ClockDomain(myclk,myrst)){
      val reg0 = RegNext(a) init 0
      b := reg0
    }
    
}
showRtl(new Top)
module Top (
      input   myclk,
      input   myrst,
      input  [7:0] a,
      output [7:0] b);
  reg [7:0] _zz_1_;
  assign b = _zz_1_;
  always @ (posedge myclk or posedge myrst) begin
    if (myrst) begin
      _zz_1_ <= (8'b00000000);
    end else begin
      _zz_1_ <= a;
    end
  end
endmodule
module Top (
      input  [7:0] a,
      input   clk,
      input   reset);
  reg [7:0] b;
  always @ (posedge clk or posedge reset) begin
    if (reset) begin
      b <= (8'b00000000);
    end else begin
      b <= a;
    end
  end
endmodule
  • 默认时钟
  • 指定自己定义时钟

创建clockingArea包裹时序寄存器

fishsemi.com

简单示例

class Top extends Component{
    val myclk,myrst = in Bool()
    val a = in Bits(8 bits)
    val b = out Bits()
    new ClockingArea(ClockDomain(myclk,myrst, config = ClockDomainConfig(
      clockEdge        = RISING,
      resetKind        = ASYNC,
      resetActiveLevel = LOW
    ))){
      val reg0 = RegNext(a) init 0
      b := reg0
    }
}
module Top (
      input   myclk,
      input   myrst,
      input  [7:0] a,
      output [7:0] b);
  reg [7:0] _zz_1_;
  assign b = _zz_1_;
  always @ (posedge myclk or negedge myrst) begin
    if (!myrst) begin
      _zz_1_ <= (8'b00000000);
    end else begin
      _zz_1_ <= a;
    end
  end
endmodule
  • 配置异步下降沿复位
class SUB extends Component{
    val a = in Bits(8 bits)
    val b = out(RegNext(a) init 0)
}
class Top extends Component{
   val myclk,myrst = in Bool()
   val cd = ClockDomain(myclk,myrst, 
      config = ClockDomainConfig(
      clockEdge        = RISING,
      resetKind        = ASYNC,
      resetActiveLevel = LOW))
    val u_sub0 = cd(new SUB)
}
module Sub (
      input  [7:0] a,
      output reg [7:0] b,
      input   myclk,
      input   myrst);
  always @ (posedge myclk or negedge myrst) begin
    if (!myrst) begin
      b <= (8'b00000000);
    end else begin
      b <= a;
    end
  end
endmodule
module Top (
...
endmodule
  • 覆盖Sub模块的默认时钟

子模块SUB例化时用cd时钟域包裹

fishsemi.com

时钟域配置

ClockDomain(
  clock: Bool
  [,reset: Bool]
  [,softReset: Bool]
  [,clockEnable: Bool]
  [,frequency: IClockDomainFrequency]
  [,config: ClockDomainConfig]
)
  • 配置异步下降沿复位
class SUB extends Component{
    val a = in Bits(8 bits)
    val b = out(RegNext(a) init 0)
}
class Top extends Component{
   val myclk,myrst = in Bool()
   val cd = ClockDomain(myclk,myrst, 
      config = ClockDomainConfig(
      clockEdge        = RISING,
      resetKind        = ASYNC,
      resetActiveLevel = LOW))
    val u_sub0 = cd(new SUB)
}
module SUB (
      input  [7:0] a,
      output reg [7:0] b,
      input   myclk,
      input   myrst);
  always @ (posedge myclk or negedge myrst) begin
    if (!myrst) begin
      b <= (8'b00000000);
    end else begin
      b <= a;
    end
  end
endmodule
module Top (
...
endmodule
  • 覆盖SUB模块的默认时钟

子模块SUB例化时用cd时钟域包裹

case class ClockDomainConfig(clockEdge: EdgeKind = RISING,
                             resetKind: ResetKind = ASYNC,
                             resetActiveLevel: Polarity = HIGH,
                             softResetActiveLevel: Polarity = HIGH,
                             clockEnableActiveLevel: Polarity = HIGH)

fishsemi.com

时钟域创建

class ExternalClockExample extends Component {
  val io = new Bundle {
    val result = out UInt (4 bits)
  }

  • external时钟创建
class InternalClockWithPllExample extends Component {
  val io = new Bundle {
    val clk100M = in Bool
    val aReset  = in Bool
    val result  = out UInt (4 bits)
  }
  // myClockDomain.clock will be named myClockName_clk
  // myClockDomain.reset will be named myClockName_reset
  val myClockDomain = ClockDomain.internal("myClockName")
  • internal时钟创建

时钟复位来自PLL

  // On top level you have two signals  :
  //     myClockName_clk and myClockName_reset
  val myClockDomain = ClockDomain.external("myClock")
  val myArea = new ClockingArea(myClockDomain){
    val myReg = Reg(UInt(4 bits)) init(7)
    myReg := myReg + 1
    io.result := myReg
  }
}
  // Instanciate a PLL (probably a BlackBox)
  val pll = new Pll()
  pll.io.clkIn := io.clk100M

  // Assign myClockDomain signals with something
  myClockDomain.clock := pll.io.clockOut
  myClockDomain.reset := io.aReset || !pll.io.

  // Do whatever you want with myClockDomain
  val myArea = new ClockingArea(myClockDomain){
    val myReg = Reg(UInt(4 bits)) init(7)
    myReg := myReg + 1

    io.result := myReg
  }
}

自动从顶层创建时钟

module ExternalClockExample (
      output [3:0] io_result,
      input   myClockName_clk,
      input   myClockName_reset);
  reg [3:0] myArea_myReg;
  assign io_result = myArea_myReg;
  always @ (posedge myClockName_clk or posedge myClockName_reset)
  begin
    if (myClockName_reset) begin
      myArea_myReg <= (4'b0111);
    end else begin
      myArea_myReg <= (myArea_myReg + (4'b0001));
    end
  end
endmodule

fishsemi.com

时钟域门控

class Top extends Component {
   val u_cgCell0 = new CG
   val u_cgCell1 = new CG
  • 手动CG后的两个时钟域处理
   ..
   val cgd0 = ClockDomain(u_cgCell0.ECK, clockDomain.readResetWire)
   val cgd1 = ClockDomain(u_cgCell1.ECK, clockDomain.readResetWire)
   
   cgd0.setSyncWith(cgd1)

   val u_sub0 = cgd0(new MySub)
   val u_sub1 = cgd1(new MySub)
}    

两个域设置同步

fishsemi.com

门控,分频时钟域同步自动处理

class Top extends Component {
   val cg_en0 = in Bool()
   val cg_en1 = in Bool()
  • 对于分频或者门控后的时钟同步自动完成
   ..
   val cgd0  = ClockDomain.cgFrom(clockDomain,  cg_en)
   val cgd1  = ClockDomain.cgFrom(clockDomain,  cg_en)
   
   val cdiv2 = ClockDomain.divFrom(clockDomain, times=2)   
   val cdiv3 = ClockDomain.divFrom(clockDomain, times=4)

   val u_sub0 = cgd0(new MySub)
   val u_sub1 = cgd1(new MySub)
   val u_sub3 = cdiv2(new MySub)
   val u_sub4 = cdiv3(new MySub)   
}    

指定门控使能和时钟源

:该方案在开发中,未进正式版本

指定分频倍数和时钟源

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

  • 工作量繁重,体力活
  • 容易引入错误,增加验证工作量
  • 文档和代码不同步,增加软件调试工作量

手写寄存器接口的弊端

如何优雅的生成寄存器接口

  • 唯一源头
  • 错误检查能力
  • 一次书写,多种输出

Excel生成寄存器接口代码文档

DSL-hdl生成寄存器接口文档

verilog

verilog

fishsemi.com

SpinalHDL支持的寄存器类型

| AccessType | Description                                                                     | Status |
|------------+---------------------------------------------------------------------------------+--------|
| RO         | w: no effect, r: no effect                                                      | UVM    |
| RW         | w: as-is, r: no effect                                                          | UVM    |
| RC         | w: no effect, r: clears all bits                                                | UVM    |
| RS         | w: no effect, r: sets all bits                                                  | UVM    |
| WRC        | w: as-is, r: clears all bits                                                    | UVM    |
| WRS        | w: as-is, r: sets all bits                                                      | UVM    |
| WC         | w: clears all bits, r: no effect                                                | UVM    |
| WS         | w: sets all bits, r: no effect                                                  | UVM    |
| WSRC       | w: sets all bits, r: clears all bits                                            | UVM    |
| WCRS       | w: clears all bits, r: sets all bits                                            | UVM    |
| W1C        | w: 1/0 clears/no effect on matching bit, r: no effect                           | UVM    |
| W1S        | w: 1/0 sets/no effect on matching bit, r: no effect                             | UVM    |
| W1T        | w: 1/0 toggles/no effect on matching bit, r: no effect                          | UVM    |
| W0C        | w: 1/0 no effect on/clears matching bit, r: no effect                           | UVM    |
| W0S        | w: 1/0 no effect on/sets matching bit, r: no effect                             | UVM    |
| W0T        | w: 1/0 no effect on/toggles matching bit, r: no effect                          | UVM    |
| W1SRC      | w: 1/0 sets/no effect on matching bit, r: clears all bits                       | UVM    |
| W1CRS      | w: 1/0 clears/no effect on matching bit, r: sets all bits                       | UVM    |
| W0SRC      | w: 1/0 no effect on/sets matching bit, r: clears all bits                       | UVM    |
| W0CRS      | w: 1/0 no effect on/clears matching bit, r: sets all bits                       | UVM    |
| WO         | w: as-is, r: error                                                              | UVM    |
| WOC        | w: clears all bits, r: error                                                    | UVM    |
| WOS        | w: sets all bits, r: error                                                      | UVM    |
| W1         | w: first one after ~hard~ reset is as-is, other w have no effects, r: no effect | UVM    |
| WO1        | w: first one after ~hard~ reset is as-is, other w have no effects, r: error     | UVM    |
| NA         | w: reserved, r: reserved                                                        | New    |
| W1P        | w: 1/0 pulse/no effect on matching bit, r: no effect                            | New    |
| W0P        | w: 0/1 pulse/no effect on matching bit, r: no effect                            | New    |

完整支持25种UVM寄存器类型

新增3种寄存器类型

fishsemi.com

SpianlHDL生成文档、电路

Verilog

fishsemi.com

class RegIfExample2 extends Component {
  val io = new Bundle {
    val apb = slave(Apb3(Apb3Config(16,dataWidth=32)))
  }
  val busif = BusInterface(io.apb, (0x000, 100 Byte))
  
  val M_REG0  = busif.newReg(doc="Word 0")
  val M_REG1  = busif.newReg(doc="Word 1")
  val M_REG2  = busif.newReg(doc="Word 2")
  val M_REGn  = busif.newRegAt(address=0x40, doc="Word n")
  val M_REGn1 = busif.newReg(doc="Word n+1")
  val M_REGm  = busif.newRegAt(address=0x100, doc="word m")
  val M_REGm1 = busif.newReg(doc="word m + 1")

寄存器地址自动分配和冲突检测

若地址超过分配的空间会自动报错

若给定的地址不能按字对齐会报错

fishsemi.com

class RegIfExample2 extends Component {
  val io = new Bundle {
    val apb = slave(Apb3(Apb3Config(16,dataWidth=32)))
  }
  val busif = BusInterface(io.apb, (0x000, 100 Byte))
  val M_REG0  = busif.newReg(doc="Word 0")
  ....
  val fd0 = M_REG0.field(2 bits, RW, doc= "fields 0")

寄存器字段自动分配和冲突检测

  M_REG0.reserved(5 bits)
  val fd1 = M_REG0.field(3 bits, RW, doc= "fields 1")
  val fd2 = M_REG0.field(3 bits, RW, doc= "fields 2")
  val fd3 = M_REG0.fieldAt(pos=16, 4 bits, RW, doc= "fields 3")

自动插入 3 bit reserved

剩余 bit 自动 reserved

手动reserved 5 bits

若指定位置和已分配字段冲突会报错

若总位宽超出总线数据位宽会报错

fishsemi.com

class cpInterruptExample extends Component {
  val io = new Bundle {
    val tx_done, rx_done, frame_end = in Bool()
    val interrupt = out Bool()
    val apb = slave(Apb3(Apb3Config(16, 32)))
  }
  val busif = Apb3BusInterface(io.apb, (0x000, 100 Byte))
  val M_CP_INT_EN    = busif.newReg(doc="cp int enable register")
  val tx_int_en      = M_CP_INT_EN.field(1 bits, RW, doc="tx interrupt enable register")(0)
  val rx_int_en      = M_CP_INT_EN.field(1 bits, RW, doc="rx interrupt enable register")(0)
  val frame_int_en   = M_CP_INT_EN.field(1 bits, RW, doc="frame interrupt enable register")(0)
  val M_CP_INT_MASK  = busif.newReg(doc="cp int mask register")
  val tx_int_mask      = M_CP_INT_MASK.field(1 bits, RW, doc="tx interrupt mask register")(0)
  val rx_int_mask      = M_CP_INT_MASK.field(1 bits, RW, doc="rx interrupt mask register")(0)
  val frame_int_mask   = M_CP_INT_MASK.field(1 bits, RW, doc="frame interrupt mask register")(0)
  val M_CP_INT_STATE   = busif.newReg(doc="cp int state register")
  val tx_int_state      = M_CP_INT_STATE.field(1 bits, RW, doc="tx interrupt mask register")(0)
  val rx_int_state      = M_CP_INT_STATE.field(1 bits, RW, doc="rx interrupt mask register")(0)
  val frame_int_state   = M_CP_INT_STATE.field(1 bits, RW, doc="frm interrupt mask register")(0)
  when(io.rx_done && rx_int_en){tx_int_state.set()}
  when(io.tx_done && tx_int_en){tx_int_state.set()}
  when(io.frame_end && frame_int_en)){tx_int_state.set()}
  io.interrupt := (tx_int_mask && tx_int_state  ||
    rx_int_mask && rx_int_state ||
    frame_int_mask && frame_int_state)
}

中断寄存器手动例化

手写容易引入错误

中断状态寄存器类型应为RC(读清)

创建中断使能寄存器

创建中断屏蔽寄存器

创建中断状态寄存器

触发中断状态

中断合并到一个中断输出信号

中断触发信号

中断输出信号

fishsemi.com

中断寄存器工厂模式

class cpInterruptExample extends Component {
  val io = new Bundle {
    val tx_done, rx_done, frame_end = in Bool()
    val interrupt = out Bool()
    val apb = slave(Apb3(Apb3Config(16, 32)))
  }
  val busif = Apb3BusInterface(io.apb, (0x000, 100 Byte))

  io.interrupt := busif.interruptFacotry("M_CP", 
                  io.tx_done,io.rx_done,io.frame_end)
                  
}

中断寄存器前缀名

将trigger信号one by one

当做参数送给工厂函数

自动生成中断寄存器,并产生输出中断

fishsemi.com

class RegIfExample2 extends Component {
  val io = new Bundle {
    val apb = slave(Apb3(Apb3Config(16,dataWidth=32)))
  }
  val busif = BusInterface(io.apb, (0x000, 100 Byte))
  val M_REG0  = busif.newReg(doc="Word 0")


  val busif = BusInterface(io.ahb, (0x000, 100 Byte))
  val busif = BusInterface(io.axiLite, (0x000, 100 Byte))
  val busif = BusInterface(io.axi4, (0x000, 100 Byte))

BusInterface支持多种总线类型

BusInteface接受不同的总线类型

  def apply(bus: YourBus, sizeMap: SizeMapping): BusIf = YourBusInterface(bus, sizeMap)
}

object BusInterface {
  def apply(bus: Apb3, sizeMap: SizeMapping, selID: Int): BusIf = Apb3BusInterface(bus, sizeMap, selID)
  def apply(bus: AhbLite3, sizeMap: SizeMapping): BusIf = AhbLite3BusInterface(bus, sizeMap)
  def apply(bus: Axi4, sizeMap: SizeMapping): BusIf = Axi4BusInterface(bus, sizeMap)
  def apply(bus: AxiLite4, sizeMap: SizeMapping): BusIf = AxiLite4BusInterface(bus, sizeMap)

也可以适配自己定义总线

适配层源码

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

定点化的目的:

定点化举例:

在性能可接受的范围内尽可能的的压缩数据bit位宽以便节省资源

fishsemi.com

定点化操作可划分为2部分

    一: 低位Round操作

     二: 高位饱和操作

  Wikipedia-Name    API           Matmatic-Alogrithm      
 -------------------------------------------------------- 
  RoundDown         floor         floor(x)                
  RoundToZero       floorToZero   sign*floor(abs(x))      
  RoundUp           ceil          ceil(x)                 
  • 饱和操作(溢出饱和到正负最大值)
  • 丢弃操作(无数学意义)
  • 对称操作(8bit  -128对称后为-127)

关于不同Round方式的说明可以参见https://en.wikipedia.org/wiki/Rounding

  RoundToInf        ceilToInf     sign*ceil(abs(x))       
  RoundHalfUp       roundUp       floor(x+0.5)            
  RoundHalfDown     roundDown     ceil(x-0.5)             
  RoundHalfToZero   roundToZero   sign*ceil(abs(x)-0.5)   
  RoundHalfToInf    roundToInf    sign*floor(abs(x)+0.5)  
  RoundHalfToEven   roundToEven                       
  RoundHalfToOdd    roundToOdd                       

fishsemi.com

Verilog 手动定点化

fishsemi.com

//==================================================================================
// Example:                                                                     
// fixpoint (15,11,4,1,1) u_add1_fix (.din(fixin),.dout(fixout));               
// fixpoint (15,10,4,1,1) u_add2_fix (.din(fixin),.dout(fixout)); need saturate 1bit
//==================================================================================
module fixpoint(din,dout);   
parameter DIN_WIDTH  = 15 ;    
parameter DOUT_WIDTH = 14 ;  
parameter CUT_WIDTH  = 1  ;      
parameter DATA_TYPE  = 0  ;// 0:unsigned ,1:signed  
parameter MODE       = 0  ;// 0:Floor    ,1:RoundToInf   ,2:RoundUp(LowCost)  
input     [DIN_WIDTH-1:0]           din           ;
output    [DOUT_WIDTH-1:0]          dout          ;
reg       [DOUT_WIDTH-1:0]          dout          ; 
wire      [DIN_WIDTH-CUT_WIDTH:0]   LSB_cut_out   ; 
wire                                sign_flag     ;
//Sign flag 
generate
if(DATA_TYPE==0) begin : unsigned_uut
    assign sign_flag = 1'b0 ;
end else begin : signed_uut           
    assign sign_flag = din[DIN_WIDTH-1] ;
end                                      
endgenerate    
generate
if(MODE==0 || CUT_WIDTH==0) begin : Floor_UUT 
    //Floor
....

封装为参数化定点模块

fixpoint (15,11,4,1,1) u_add1_fix (.din(fixin),.dout(fixout));
fixpoint (15,10,4,1,1) u_add2_fix (.din(fixin),.dout(fixout));

fishsemi.com

Spinal支持常见的8种Round操作

RoundToEven/RoundToOdd 情况比较特殊,比较少见,一般用在大数据统计以及对精度要求比较高的场景,目前spinalHDL暂未支持这2种类型

fishsemi.com

不同语言默认Round操作不同

RoundToINF最为常见,SpinalHDL默认Round方式为RoundToINF  

注: 不同的Round方式对于一般算法性能区别不大,所以在一般的硬件设计当中推荐使用RoundUp向正无穷四舍五入)。

RoundUP电路最为简单,消耗的门电路少,timing最好

fishsemi.com

val A  = SInt(10 bit)

SpinalHDL定点方式

val B  = A.fixTo(7 downto 3)
val B  = A.roundToInf(3).sat(3)

fishsemi.com

import spinal.lib.dsptool._

dsptool 定点库介绍

val a = FixData(-7.327743,SQ(16,10))
println(a.value) //-7.328125
println(a.hex)   //0xe2b0
println(a.bin)   //0b1110001010110000
println(a.oct)   //0o161260
println(a)
>FixData : -7.328125, Quantized by
>QFormat : Q(16,10,signed)
>resolution: 9.765625E-4
>maxValue  : 31.9990234375
>minValue  : -32.0
import FixSwitchOff._
val a = FixData(-7.327743,SQ(16,10))
println(a)
>FixData : -7.327743, FixSwitchOff

将浮点数定点

查看定点信息

查看硬件值

关闭定点化

定点未生效,精度保留

  1.定点仿真

  2.关闭定点化,全精度仿真

val x = toFixData(0xFFAE,SQ(8,4))
=> FixData: -5.125, QFormat: Q(8,4,signed) 

  3.反向Debug

toFixData(322111, SQ(8,4)) 
=> FixData: -8.0,   QFormat: Q(8,4,signed)
toFixData(322111, UQ(8,4)) 
=> FixData: 7.9375, QFormat: Q(8,4,unsigned)
toFixData(-322111,SQ(8,4)) 
=> FixData: -8.0,   QFormat: Q(8,4,signed)
toFixData(-322111,UQ(8,4)) 
=> FixData: 0,      QFormat: Q(8,4,unsigned)
toFixData(-0x0f,  SQ(8,4)) 
=> FixData: -0.9375,QFormat: Q(8,4,signed)

查看定点硬件对应的的浮点值

List.fill(10000)(rand.nextGaussian,SQ(8,5))

SpinalHDL 参数化设计

fishsemi.com

  • 参数化滤波器组设计举例
  • SpinalHDL Soc系统
  • AMBA总线介绍
  • 时钟域介绍
  • regIf寄存器接口
  • 定点化介绍
  • 一些讨论

fishsemi.com

Scala硬件开发现状

理想

传统方法

现实

  • Scala目前充当的是生成器的角色
  • 对于验证和后端来说是透明的

fishsemi.com

Chisel的意义和它的问题

  •  借着RISCV的东风,将Scala硬件开发变得流行起来,让更多的人知道
  •  验证了可行性, 相比Verilog沉睡的40年, 它的出现称得上是革命性的。
  •  泛CHISEL特性提供了一个很好的示范
  •  缺乏系统性的文档,绝大多数文档是语法类的文档实无必要
  •  组织混乱(代码结构,sbt版本),设计混乱,软硬件概念模糊
  •  有些命题脱离实际(或者说太超前)

功(启发意义)

过(产品角度)

  •  firrtl看似合理,目前是个伪命题
  •  ioTester方向错误,太慢了,没有实用意义

团队背景:计算机专业、学生为主

小黄车第一个发明共享单车,

现在小黄GameOver,但共享单车是个好东西

会以更健康的商业方式生存下去。

 

  • 思维活跃,勇于尝试创新
  • 缺乏工程经验,不管是软件还是硬件
  • 对RTL工程师的需求理解欠准确
  • 理性正确看待它的先进和不足,莫盲目宗教式的崇拜
  • 解决问题不应该过多的引入其他问题
  • Scala硬件开发应该更健康正确的方式发展

fishsemi.com

关于隐式参数

  • 慎用隐式参数





class People2(implicit fruid: Fruid) {
  def eatFruid() = println(s"eating ${fruid}")
}
val xiaming = new People
xiaoming.eatFruid
class Pepole3(imlicit lemen: Lemen) {
  def eatLemen() = println(s"eating ${lemen} Chiken")
}
implict class PeopleWithEatLemen(p: People) {
  def eatLemen = p.eat(Fruid("Leamon"))
}
class Peopole {
  def eatFruid(fruid: Fruid) = println(s"eating ${fruid}")
}
val xiaming = new People
xiaoming.eatFruid("Apple")
xiaoming.eatFruid("Pear")
val xiaoming = new People
xiaoming.eatLenmen
xiaoming.eatLemon

鼓励参数层层设计,层层传递,参数和模块相生相伴

Rocket的隐式参数p非常糟糕, 回到扁平化的问题

implicit val fruid = Fruid(     )
implicit val fruid = Fruid(     )
implicit val fruid = Fruid(     )
val sftRst0 = busIf.creatReg(...)(name = "sftRst0")
val crcPoly = busIf.creatReg(...)(name = "crcPoly")
def creatField(...)(implicit name: ValName)
...
val sftRst0 = busIf.creatReg(...)
val crcPoly = busIf.creatReg(...)
object ValName {
  implicit def generator: ValName = macro Macro.valNameImpl
}
val a = FixData(-3.785333,SQ(8,4),fixon: Boolean)
...
val b = FixData(31.333,SQ(8,4),fixon: Boolean)
case class FixData(...)
(implicit button: FixSwitch = FixSwitchOn.fixButton )
import FixSwitchOff._
val a = FixData(-3.785333,SQ(8,4))

fishsemi.com

3   bits
100 Mhz
4   MiB
3.bits()
100.Mhz()
4.MiB()
val data = out Reg(UInt(10 bits)) init 0
val data = out Reg(UInt(10 bits)) init U(0)
val data = out Reg(UInt(10 bits)) init U(0, 10 bits)

隐式扩展

隐式转换

  • SpinalHDL隐式转换

隐式转换,隐式扩展是个好主意

有理数的加法举例

way2: 隐式扩展,在不修改原类的情况下自由增强类型的功能

    val x: Double = 2

动机:对于已知的信息不应重复输入

原则:需要信息输入时候莫擅做主张

Int + Rational 报错

 

    val b: Int = 3.0
    val b: Int = 3.0 toInt
class Rational(n: Int, d:Int) {
  required(d != 0)
  override def toString = n +"/" + d
  def +(x:Rational): Rational 
  def +(x: Int): Rational
}
implicit def suibianqiInt2Rational(x :Int): Rational = Rational(x,1)
scala> val r = new Rational(2,3) 
r: Rational = 2/3
Rational(2,3) + Rational(2,1)
Rational(2,3) + 2
2 + Rational(2,3)
2 + Rational(2,3) => Rational(2,1) + Rational(2,3)
implicit class IntExtend(t: Int) {
  def +(x: Rational) = Ratinal(t) + x
}
2 + Rational(2,3) 

way1: 隐式转换(静静的帮你转换)

fishsemi.com

Firrtl现状讨论

  • 愿景
  • firrtl的问题,打断了该环路,又没有工具能跟进
  • 写代码是小头,debug是大头
  • 希望尽可能忠实还原设计意图,不要擅自优化
  • 自测
  • 验证
  • ECO
  • 除非基于Firrtl的EDA工具,firrtl才有意义,但目前看来很难
  • IC开发非常依赖波形调试环路

fishsemi.com

关于敏捷开发

快速迭代,关注沟通(减少文档)

敏捷开发

瀑布流开发

IC敏捷不敏捷

  • 减少节点迭代
  • 注重错误检查,前期把事情做对
  • 基础复用,快速开发,加快收敛

注重节点交付质量,减少迭代

Speed Up

fishsemi.com

关于生态

软件库

Cordic同样的算法功能,可能会有多个版本不同的面积

一维

MIPS

时间开销

面积开销

硬件库

二维

甚至

三维

timing

基础组件

总线Lib

Cpu-core

IP-core

fishsemi.com

我选择SpinalHDL的理由

Spinal还有哪些工作要做

虽然生成的Verilog代码可读性要比chisel好很多,

不过还不够,目标是所见即所得

简单

不失强大

Good Taste

好在spinalHDL提供可插拔时的目标HDL后台

任何人可以定制自己的Veirlog后台

fishsemi.com

SpianlHDL使用技巧

  • Componets  Area
  • Bits/UInts/SInts
class T2  extends Component{ 
   class Dog{
      def genTimer(n: Int) = {
         val timer = Reg(UInt(n bits)) init 0 
         val clearTimer = in Bool()
         when(clearTimer){
            timer init 0
         }.otherwise {
            timer := timer + 1
         }
         (clearTimer,timer)
      }
   }
class T2  extends Component{ 
    val a = out Bits(9 bit)
    a := B(32,9 bit)
    class Dog{}
    val xiaogou = new Dog
}
showRtl(new T2) 
   val xiaogou = new Dog
   val (weigou,timer) = xiaogou.genTimer(8)
} 

牢记电路对象

运用Scala编程

  • 电路节点作为scala的对象可以像普通对象一样传递,处理
  • 参数和模块分离(config伴侣)
  • 生成器只会对命名空间内的电路对象生成RTL代码

fishsemi.com

关于简单

200 Mhz, 10 ns
100 kiB, 4 MiB
val busi = slave(new APB)
val buso = master(new APB)
data === M"101--"

往往简单的东西是自洽的,自洽的系统是干净整洁,

理解也是连贯的,避免特例。

基础组件如果出现特例,上层实现将是一场灾难。

Git 3对象

  • Blob
  • tree
  • commit

Lisp 7原语

  • (quote x)
  • (atom x)
  • (eq x y)
  • (car x)
  • (cdr x)
  • (cons x y)
  • (cond (...) ...(...))

Simple can be harder than complex:

You have to work hard to get your thinking clean to make it simple. But it’s worth it in the end because once you get there, you can move mountains.

— Steve Jobs

关于品位

SpinalVerilog(new Top)
SpinalVerilog(Pinsec(500 Mhz))

一些细节对比

-
-
val busi = new APB
val buso = Flipped(new APB)
data === BitPat("b101??")
chisel3.Driver.execute(Array("--target-dir","rtl"),()=>new Top)
?@#$%^*& implicit p **&$@!@)#

Chisel

data := 33
data := 33.U
(data & 5'b11100) == 5'b10100
val uut = new Top
val uut = Module(new Top)

SpinalHDL

val a = UInt(8 bits)
val a = UInt(8.W)
val a = UInt(8 bits)
val a = Wire(UInt(8.W))

fishsemi.com

理想中的Scala硬件框架

Bits/UInt/SInt
Component/Area/BlackBox
in/out/inout/analogy/reg
assign/mux/operation

Core

Facility

Library

ClockDomain/ClockArea
Mhz,KiB,bits,slave,master
Bundle/Vec/Flow/Stream/
Utils/Gray/OneHot/FixPoint

AMBA-bus, BusIf
RISCV-core
Com/i2c/jtag/spi/uart/gpio
Soc-frame/H.264/MP3

 My-facility

 My-Lib/My-IP

基础做好了,上层生长、繁荣是自然而然的

足够稳固

fishsemi.com

Thank You!

http://jijing.site

http://github.com/jijingg