0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpinalHDL Simulation性能提升测试

Spinal FPGA 来源:Spinal FPGA 2023-08-06 17:10 次阅读

昨晚看SpinalHDL的Issues,其中有一个关于性能提升的case 吸引到了我,尝试实验到深夜,测试下在SpinalHDL以及cocotb下的性能优化手段。

SpinalHDL Simulation性能提升测试


无论是SpinalHDL还是cocotb,其在仿真方面所采用的思路是一样的。在SpinalHDL的那个Issue里,Dolu主要想做的是尽可能避免在信号的赋值和读取上的冗余代码。以toInt为例,其会调用getInt函数:

private defgetInt(bt: BaseType):Int = {
if(bt.getBitsWidth == 0) return0
val manager = SimManagerContext.current.manager
val signal = btToSignal(manager, bt)
manager.getInt(signal)
}


而Dolu的思路则是没有必要每次都重新寻找manager、signal这些信息,毕竟对于一个信号而言这两个值是不变的。而是提前准备好对于这种频繁使用的接口则能够尽可能降低不必要的开销。

这里做了个测试,testSimNormal采用普通的API调用形式访问信号,

testSimSpeed则采用加速后的方式进行访问一个512bit信号位宽。两者测试访问1亿次信号值所消耗的时间,测试结果如下:

testSimNormal 26998ms
testSimSpeed 24462ms

可以看出,还是能够加速仿真速度的。

考虑到在仿真过程中无非是信号的驱动和读取,那么这里应该是都适用的,遂以相同的DUT相同的Case尝试做了如下测试:


testSim1:采用SpinalHDL原生API进行仿真测试

testSim2:将信号的读取和赋值均改为优化后的方式

testSim3:在testSim2的基础上将时钟,复位驱动也改为优化后的方式

testSim4:在testSim2的基础上将waitSampling修改为优化后的方式

testSim5:在testSim3的基础上将时钟,将waitSampling修改为优化后的方式

测试结果如下:

testSim1 6469.675 ms
testSim2 6196.007 ms
testSim3 6196.007 ms
testSim4 6066.035 ms
testSim5 6076.121 ms


每个测试里面都是跑了500000周期,可以看到,对于降低延迟还是有效果的。对于更大的case,也许会有更有效的效果。

附上完整的测试代码(由于电脑较差,诸君可自行测试):

import spinal.core._
import spinal.lib._
import spinal.sim.{Signal, SimManager, SimManagerContext}

import scala.collection.mutable.ArrayBuffer

case class dut() extends Component {
val io = new Bundle {
val data_in = slave Flow (UInt(512 bits))
val data_out = master Flow (UInt(512 bits))
}
noIoPrefix()
io.data_out << io.data_in.translateWith(io.data_in.payload + 1).stage()
}
import spinal.core.sim._

object testSimNormal extends App {
  SimConfig.withFstWave.compile(dut()).doSim { dut =>
dut.io.data_in.valid #= false
dut.clockDomain.forkStimulus(10)
dut.clockDomain.waitSampling(10)
val startTime = System.currentTimeMillis()
for (index <- 0 until 100000000) {
      dut.io.data_out.payload.toBigInt
    }
    val endTime = System.currentTimeMillis()
    val totalTime = endTime - startTime
    println("代码运行时间:" + totalTime + "毫秒")
  }
}

object testSimSpeed extends App {
  implicit class SimBitVectorPimper(bt: BaseType) {
    class SimProxy(bt: BaseType) {
      val manager = SimManagerContext.current.manager
      val signal = manager.raw.userData.asInstanceOf[ArrayBuffer[Signal]](bt.algoInt)
      val alwaysZero = bt.getBitsWidth == 0

      def getLong = manager.getLong(signal)

      def getBoolean = manager.getLong(signal) != 0

      def getBigInt = manager.getBigInt(signal)

      def assignBoolean(value: Boolean) = manager.setLong(signal, value.toInt)

      def setLong(value: Long) = manager.setLong(signal, value)

      def assignBigInt(value: BigInt) = manager.setBigInt(signal, value)
    }

    def simProxy() = new SimProxy(bt)
  }

  SimConfig.withFstWave.compile(dut()).doSim { dut =>
dut.io.data_in.valid #= false
dut.clockDomain.forkStimulus(10)
dut.clockDomain.waitSampling(10)
val dataOutHdl = dut.io.data_out.payload.simProxy()
val startTime = System.currentTimeMillis()
for (index <- 0 until 100000000) {
      dataOutHdl.getBigInt
    }
    val endTime = System.currentTimeMillis()
    val totalTime = endTime - startTime
    println("代码运行时间:" + totalTime + "毫秒")
  }
}

object SimExtend {
  implicit class SimBitVectorPimper(bt: BaseType) {
    class SimProxy(bt: BaseType) {
      val manager = SimManagerContext.current.manager
      val signal = manager.raw.userData.asInstanceOf[ArrayBuffer[Signal]](bt.algoInt)
      val alwaysZero = bt.getBitsWidth == 0

      def getLong = manager.getLong(signal)

      def getBoolean = manager.getLong(signal) != 0

      def getBigInt = manager.getBigInt(signal)

      def assignBoolean(value: Boolean) = manager.setLong(signal, value.toInt)

      def setLong(value: Long) = manager.setLong(signal, value)

      def assignBigInt(value: BigInt) = manager.setBigInt(signal, value)
    }

    def simProxy() = new SimProxy(bt)
  }

  def getBool(manager: SimManager, who: Bool): Bool = {
    val component = who.component
    if ((who.isInput || who.isOutput) && component != null && component.parent == null) {
      who
    } else {
      manager.userData.asInstanceOf[Component].pulledDataCache.getOrElse(who, null).asInstanceOf[Bool]
    }
  }
}

object testSim extends App {
  val dutCompiled = SimConfig.withFstWave.compile(dut())

  /** *****************************************************************************************
   * testSim1
   * ***************************************************************************************** */
  dutCompiled.doSim { dut =>
dut.io.data_in.valid #= false
dut.clockDomain.forkStimulus(10)
dut.clockDomain.waitSampling(10)
var sum = BigInt(0)
for (index <- 0 until 500000) {
      dut.clockDomain.waitSampling()
      if (dut.io.data_out.valid.toBoolean) {
        sum = sum + dut.io.data_out.payload.toBigInt
      }
      dut.io.data_in.valid #= true
      dut.io.data_in.payload #= BigInt(index)
    }
  }

  /** *****************************************************************************************
   * testSim2
   * ***************************************************************************************** */
  dutCompiled.doSim { dut =>
import SimExtend._
val dataInValidHdl = dut.io.data_in.valid.simProxy()
val dataInDataHdl = dut.io.data_in.payload.simProxy()
val dataOutValidHdl = dut.io.data_out.valid.simProxy()
val dataOutDataHdl = dut.io.data_out.payload.simProxy()
dataInValidHdl.assignBoolean(false)
dut.clockDomain.forkStimulus(10)
dut.clockDomain.waitSampling(10)
var sum = BigInt(0)
for (index <- 0 until 500000) {
      dut.clockDomain.waitSampling()
      if (dataOutValidHdl.getBoolean) {
        sum = sum + dataOutDataHdl.getBigInt
      }
      dataInValidHdl.assignBoolean(true)
      dataInDataHdl.assignBigInt(index)
    }
  }

  /** *****************************************************************************************
   * testSim3
   * ***************************************************************************************** */
  dutCompiled.doSim { dut =>
import SimExtend._
val dataInValidHdl = dut.io.data_in.valid.simProxy()
val dataInDataHdl = dut.io.data_in.payload.simProxy()
val dataOutValidHdl = dut.io.data_out.valid.simProxy()
val dataOutDataHdl = dut.io.data_out.payload.simProxy()
val clock = getBool(SimManagerContext.current.manager, dut.clockDomain.clock).simProxy()
val reset = getBool(SimManagerContext.current.manager, dut.clockDomain.reset).simProxy()
dataInValidHdl.assignBoolean(false)
//clock generation
clock.assignBoolean(false)
reset.assignBoolean(true)
sleep(10 * 16)
reset.assignBoolean(false)
fork {
var value = false

def t: Unit = {
value = !value
clock.assignBoolean(value)
delayed(5)(t)
}

t
}
dut.clockDomain.waitSampling(10)
var sum = BigInt(0)
for (index <- 0 until 500000) {
      dut.clockDomain.waitSampling()
      if (dataOutValidHdl.getBoolean) {
        sum = sum + dataOutDataHdl.getBigInt
      }
      dataInValidHdl.assignBoolean(true)
      dataInDataHdl.assignBigInt(index)
    }
  }

  /** *****************************************************************************************
   * testSim4
   * ***************************************************************************************** */
  dutCompiled.doSim { dut =>
import SimExtend._
val dataInValidHdl = dut.io.data_in.valid.simProxy()
val dataInDataHdl = dut.io.data_in.payload.simProxy()
val dataOutValidHdl = dut.io.data_out.valid.simProxy()
val dataOutDataHdl = dut.io.data_out.payload.simProxy()
val clock = getBool(SimManagerContext.current.manager, dut.clockDomain.clock).simProxy()
val reset = getBool(SimManagerContext.current.manager, dut.clockDomain.reset).simProxy()
var rising = false
var last = false
dataInValidHdl.assignBoolean(false)
//clock generation
dut.clockDomain.forkStimulus(10)
dut.clockDomain.waitSampling(10)
var sum = BigInt(0)
for (index <- 0 until 500000) {
      waitUntil {
        rising = false
        val current = clock.getBoolean
        if ((!last) && current) {
          rising = true
        }
        last = current
        rising
      }
      if (dataOutValidHdl.getBoolean) {
        sum = sum + dataOutDataHdl.getBigInt
      }
      dataInValidHdl.assignBoolean(true)
      dataInDataHdl.assignBigInt(index)
    }
  }

  /** *****************************************************************************************
   * testSim5
   * ***************************************************************************************** */
  dutCompiled.doSim { dut =>
import SimExtend._
val dataInValidHdl = dut.io.data_in.valid.simProxy()
val dataInDataHdl = dut.io.data_in.payload.simProxy()
val dataOutValidHdl = dut.io.data_out.valid.simProxy()
val dataOutDataHdl = dut.io.data_out.payload.simProxy()
val clock = getBool(SimManagerContext.current.manager, dut.clockDomain.clock).simProxy()
val reset = getBool(SimManagerContext.current.manager, dut.clockDomain.reset).simProxy()
var rising = false
var last = false
dataInValidHdl.assignBoolean(false)
//clock generation
clock.assignBoolean(false)
reset.assignBoolean(true)
sleep(10 * 16)
reset.assignBoolean(false)
fork {
var value = false

def t: Unit = {
value = !value
clock.assignBoolean(value)
delayed(5)(t)
}

t
}
dut.clockDomain.waitSampling(10)
var sum = BigInt(0)
for (index <- 0 until 500000) {
      waitUntil {
        rising = false
        val current = clock.getBoolean
        if ((!last) && current) {
          rising = true
        }
        last = current
        rising
      }
      if (dataOutValidHdl.getBoolean) {
        sum = sum + dataOutDataHdl.getBigInt
      }
      dataInValidHdl.assignBoolean(true)
      dataInDataHdl.assignBigInt(index)
    }
  }
}
cocotb性能优化


cocotb的仿真速度一直我是持保留意见的。在SpinalHDL里面做完尝试,最近工作里用到的cocotb较多,就尝试看下能否应用到cocotb中。看了下cocotb中的信号读写封装背后的调用,其做了太多的封装和调用。遂采用了相同的DUT做了同样的测试。首先是做优化前后的一百万次的方式测试(跑一亿次真的太久了)

testSimNormal 3.58s
testSimSpeed 1.09s


可以看到,这里有明显的性能提升。


再来构建下面的六个case:


testCase0 :采用cocotb提供的API接口进行数据读写访问


testCase1: 仅将信号读更改为底层接口直接调用形式进行访问


testCase2:将信号读,信号写均改为底层接口直接调用形式进行访问


testCase3:在testCase2的基础上将信号接口提前生成好而不是使用时例化


testCase4:在testCase4的基础上将时钟生成修改为底层接口直接调用形式


testCase5: 在testCase0基础上,仅将时钟生成修改为底层接口直接调用的形式



测试结果如下:

190dbb3a-340b-11ee-9e74-dac502259ad0.jpg


每个Case中均做100000次周期测试。可以看到,与原生Case仿真相比,testCase5能提升1.7倍多,而testCase4则有4.8倍的性能提升。由此可见,cocotb中对于信号读写的封装由于做了太多安全和边界的处理导致这种在仿真中经常使用的函数带来挺大的开销。
由于Verilator好像不支持时钟下沉,如果将时钟的驱动给放到Verilog里面,也许还会有进一步的性能提升。
本人对于底层的东西不甚了解,单纯从仿真速度上,cocotb相较于SpinalHDL还是有较大的差距(《既生瑜何生亮——SpinalHDL VS Cocotb》),有一点有意思的额是在SpinalHDL里面修改时钟生成的方式并未有太大的性能提升,而在cocotb里确有明显改善,诸君有兴趣可以自行研究。
附上源码,感兴趣的小伙伴可以自行测试:
DUT:

// Generator : SpinalHDL v1.8.0b git head : 761a30e521263983ddf14de3592f7a9f38bf0589
// Component : simSpeedUpTest

`timescale 1ns/1ps

module dut (
input data_in_valid,
output reg data_out_valid,
input [511:0] data_in,
output reg [511:0] data_out,
input clk,
input reset
);
always @(posedge clk ) begin
if(reset) begin
data_out <= 'd0;
      data_out_valid<='d0;
    end else begin
      data_out <= data_in+1;
      data_out_valid<= data_in_valid;
    end
  end
endmodule

TestBench:

import cocotb
fromcocotb_bus.drivers import BusDriver
fromcocotb.clock import Clock
fromcocotb.triggers import ClockCycles,RisingEdge,Timer,ReadOnly
fromcocotb.handle import *

@cocotb.test(skip=False)
asyncdef testCaseNormal(dut):
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
dataInvalidSignal=dut.data_in_valid._handle
dataInDataSignal=dut.data_in._handle
cocotb.start_soon(generateClk(dut.clk))
dataInDataSignal.set_signal_val_binstr(0,bin(0)[2:])
dataInvalidSignal.set_signal_val_int(0,0)
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
forindex inrange(1000000):
dut.data_out_valid.value
awaitClockCycles(dut.clk,10)

@cocotb.test(skip=False)
asyncdef testCaseSpeed(dut):
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
dataInvalidSignal=dut.data_in_valid._handle
dataInDataSignal=dut.data_in._handle
cocotb.start_soon(generateClk(dut.clk))
dataInDataSignal.set_signal_val_binstr(0,bin(0)[2:])
dataInvalidSignal.set_signal_val_int(0,0)
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
forindex inrange(1000000):
targetDataSignal.get_signal_val_binstr()
awaitClockCycles(dut.clk,10)

@cocotb.test(skip=False)
asyncdef testCase0(dut):
cocotb.start_soon(Clock(dut.clk,10,'ns').start())
dut.reset.value=1
dut.data_in.value= 0
dut.data_in_valid.value= 0
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
targetSignal=dut.data_out._handle
forindex inrange(100000):
awaitRisingEdge(dut.clk)
ifint(dut.data_out_valid.value) == 1:
sum+= dut.data_out.value
dut.data_in_valid.value= 1
dut.data_in.value= index
awaitClockCycles(dut.clk,100000)

@cocotb.test(skip=False)
asyncdef testCase1(dut):
cocotb.start_soon(Clock(dut.clk,10,'ns').start())
dut.data_in.value= 0
dut.data_in_valid.value= 0
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
forindex inrange(100000):
awaitRisingEdge(dut.clk)
iftargetValueSignal.get_signal_val_long()==1:
sum+= int(targetDataSignal.get_signal_val_binstr(),2)
dut.data_in_valid.value= 1
dut.data_in.value= index
awaitClockCycles(dut.clk,10)


@cocotb.test(skip=False)
asyncdef testCase2(dut):
cocotb.start_soon(Clock(dut.clk,10,'ns').start())
dut.data_in.value= 0
dut.data_in_valid.value= 0
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
forindex inrange(100000):
awaitRisingEdge(dut.clk)
iftargetValueSignal.get_signal_val_long()==1:
sum+= int(targetDataSignal.get_signal_val_binstr(),2)
dut.data_in._handle.set_signal_val_binstr(0,bin(index)[2:])
dut.data_in_valid._handle.set_signal_val_int(0,1)
awaitClockCycles(dut.clk,10)

@cocotb.test(skip=False)
asyncdef testCase3(dut):
cocotb.start_soon(Clock(dut.clk,10,'ns').start())
dut.data_in.value= 0
dut.data_in_valid.value= 0
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
dataInvalidSignal=dut.data_in_valid._handle
dataInDataSignal=dut.data_in._handle
forindex inrange(100000):
awaitRisingEdge(dut.clk)
iftargetValueSignal.get_signal_val_long()==1:
sum+= int(targetDataSignal.get_signal_val_binstr(),2)
dataInDataSignal.set_signal_val_binstr(0,bin(index)[2:])
dataInvalidSignal.set_signal_val_int(0,1)
awaitClockCycles(dut.clk,10)

asyncdef generateClk(clk):
clk._handle.set_signal_val_int(1,0)
whileTrue:
awaitTimer(5, units="ns")
clk._handle.set_signal_val_int(0,0)
awaitTimer(5, units="ns")
clk._handle.set_signal_val_int(0,1)

@cocotb.test(skip=False)
asyncdef testCase4(dut):
targetDataSignal=dut.data_out._handle
targetValueSignal=dut.data_out_valid._handle
dataInvalidSignal=dut.data_in_valid._handle
dataInDataSignal=dut.data_in._handle
cocotb.start_soon(generateClk(dut.clk))
dataInDataSignal.set_signal_val_binstr(0,bin(0)[2:])
dataInvalidSignal.set_signal_val_int(0,0)
dut.reset.value=1
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
forindex inrange(100000):
awaitRisingEdge(dut.clk)
iftargetValueSignal.get_signal_val_long()==1:
sum+= int(targetDataSignal.get_signal_val_binstr(),2)
dataInDataSignal.set_signal_val_binstr(0,bin(index)[2:])
dataInvalidSignal.set_signal_val_int(0,1)
awaitClockCycles(dut.clk,10)

@cocotb.test(skip=False)
asyncdef testCase5(dut):
cocotb.start_soon(generateClk(dut.clk))
dut.reset.value=1
dut.data_in.value= 0
dut.data_in_valid.value= 0
awaitClockCycles(dut.clk,10)
dut.reset.value=0
awaitClockCycles(dut.clk,10)
sum=0
targetSignal=dut.data_out._handle
forindex inrange(100000):
awaitRisingEdge(dut.clk)
ifint(dut.data_out_valid.value) == 1:
sum+= dut.data_out.value
dut.data_in_valid.value= 1
dut.data_in.value= index
awaitClockCycles(dut.clk,100000)
审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 仿真
    +关注

    关注

    50

    文章

    3872

    浏览量

    132161
  • 信号
    +关注

    关注

    11

    文章

    2639

    浏览量

    75388
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66767
  • Simulation
    +关注

    关注

    0

    文章

    12

    浏览量

    8111

原文标题:给仿真加点速

文章出处:【微信号:Spinal FPGA,微信公众号:Spinal FPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    spinalhdl转Verilog可读性 SpinalHDL开发流程

    SpinalHDL是基于Scala全新的硬件描述语言,解决了不少Verilog等传统HDL语言的痛点,可以快速的完成某些IP的开发,和完美的融入现有的开发流程。 诚然SpinalHDL的学习路线
    的头像 发表于 07-27 09:29 858次阅读
    <b class='flag-5'>spinalhdl</b>转Verilog可读性 <b class='flag-5'>SpinalHDL</b>开发流程

    如何提升EMC性能

    在电源模块应用中,EMC 设计往往是重中之重,因为关乎整个用户产品的 EMC 性能。那么如何提升 EMC 性能呢?本文从电源模块的设计与应用角度为您解读。EMC 测试又叫做电磁兼容,描
    发表于 10-29 07:07

    关于SpinalHDL中的验证覆盖率收集简单说明

    在做RTL仿真验证时,覆盖率收集往往是我们在验证中需要注意的地方,本篇就SpinalHDL中的验证覆盖率收集做一个简单说明。sbt配置在SpinalHDL里进行仿真验证时,我们的待测试代码会生
    发表于 06-24 15:56

    SpinalHDL测试平台搭建进行说明

    对于RTL设计而言,仿真验证是上板前必不可少的一环。当我们的代码基于SpinalHDL进行设计时,自然也想通过基于scala来进行测试验证。测试平台该有的样子对于仿真验证而言,简单的仿真对比验证我们
    发表于 07-20 14:38

    SpinalHDL是如何让仿真跑起来的

    时,SpinalHDL会将的逻辑设计转换成Verilog代码,然后将Verilog代码交给仿真器去编译生成可执行二进制文件。而我们由Scala编写的测试代码则会调用仿真器的VPI接口与设计逻辑进行数据交互,其
    发表于 07-25 15:09

    如何在SpinalHDL里启动一个仿真

    使用SpinalHDL已经集成好的接口来搭建仿真环境。SimConfig用于配置仿真环境参数。doSim里构建仿真环境及测试case。SimConfig里的参数配置可分为三类:仿真环境配置。DUT配置及编译。仿真启动
    发表于 07-26 16:59

    SpinalHDL设计错误总结相关资料分享

    1、SpinalHDL设计错误  SpinalHDL编译器会做很多设计检查,来确保生成的VHDL/Verilog是可仿真的可综合的。基本上,SpinalHDL不会生成破损的VHDL/Verilog
    发表于 10-24 15:37

    基于Windows系统的SpinalHDL开发环境搭建步骤

    开发所需软件SpinaHDL环境搭建所需的软件安装包,SpinalHDL是Scala语言的一个库,开发Scala需要使用IDEA软件;JDK17、SDK:Scala2.12.15,SBT1.5.5
    发表于 10-24 15:40

    SpinalHDL中的对应关系及声明形式

    针对SpinalHDL中的两大类型Reg、Wire,来梳理下在SpinalHDL中的对应关系及声明形式。
    的头像 发表于 07-03 11:02 1152次阅读

    SpinalHDL里如何实现Sobel边缘检测

    书接上文,趁着今天休假,采用SpinalHDL做一个小的demo,看看在SpinalHDL里如何优雅的实现Sobel边缘检测。
    的头像 发表于 08-26 08:59 1016次阅读

    SpinalHDL语法篇之Bool类型

    作为SpinalHDL语法篇的第一节,我们也从最简单的开始。
    的头像 发表于 10-31 10:56 943次阅读

    SpinalHDL BlackBox时钟与复位

    SpinalHDL中使用之前已有的Verilog等代码的时候需要将这些代码包在一个BlackBox里面,但是如果这些代码里面有时钟和复位,我们需要怎么将时钟和复位端口和SpinalHDL中已有的时钟域连接起来呢?
    的头像 发表于 05-04 11:13 509次阅读
    <b class='flag-5'>SpinalHDL</b> BlackBox时钟与复位

    SpinalHDL语法之Bool类型

    作为SpinalHDL语法篇的第一节,我们也从最简单的开始。 Bool类型定义
    的头像 发表于 05-05 16:01 390次阅读

    SOLIDWORKS教育版——SIMULATION

    借助快速、易用的CAD 嵌入式分析解决方案 SOLIDWORKS Simulation,工程师和设计人员可对设计性能进行模拟和分析。您可以快速、轻松地采用先进的仿真技术来在设计的同时测试性能
    的头像 发表于 03-04 15:31 188次阅读
    SOLIDWORKS教育版——<b class='flag-5'>SIMULATION</b>

    浅析SpinalHDL中Pipeline中的复位定制

    之前有系列文章介绍了SpinalHDL中Pipeline的使用,最近在一个功能模块中真实的使用了这个lib。
    的头像 发表于 03-17 17:31 559次阅读
    浅析<b class='flag-5'>SpinalHDL</b>中Pipeline中的复位定制