chisel快速入门(二)

2023-10-27

        上一篇见此:

chisel快速入门(一)_沧海一升的博客-CSDN博客简单介绍了chisel,使硬件开发者能快速上手chisel。https://blog.csdn.net/qq_21842097/article/details/121415341

十、运行和测试

        现在我们已经定义了模块,我们将讨论如何实际运行并测试电路。Chisel代码可以转换为C++或Verilog。 为了编译电路,我们需要调用chiselMain:

object tutorial {
	def main(args: Array[String]) = {
		chiselMain(args, () => Module(new Mux2())) 
	}	
}

        测试是电路设计的关键部分,因此在 Chisel 中,我们通过使用 Tester 类的子类在 Scala 中提供测试向量来提供一种测试电路的机制:

class Tester[T <: Module] (val c: T, val isTrace: Boolean = true) {
	var t: Int
	val rnd: Random
	def int(x: Boolean): BigInt 
	def int(x: Int): BigInt
	def int(x: Bits): BigInt 
	def reset(n: Int = 1)
	def step(n: Int): Int
	def pokeAt(data: Mem[T], index: Int, x: BigInt) 
	def poke(data: Bits, x: BigInt)
	def poke(data: Aggregate, x: Array[BigInt])
	def peekAt(data: Mem[T], index: Int)
	def peek(data: Bits): BigInt
	def peek(data: Aggregate): Array[BigInt]
	def expect (good: Boolean, msg: String): Boolean 
	def expect (data: Bits, target: BigInt): Boolean
}

        它将tester绑定到模块,并允许用户使用给定的调试协议编写测试。用户会用到一下这些:

  • poke: 设置输入端口以及状态值
  • step: 以一个时间单元执行电路
  • peek: 读取端口和状态值
  • expect: 比较peek获得的值和期望的值

        用户使用如下的方式连接tester和模块:

object chiselMainTest { 
	def apply[T <: Module]
		(args: Array[String], comp: () => T)( 
			tester: T => Tester[T]): T
}

        当- -test作为参数传递给chiselMainTest时,tester实例在独立的进程中运行被测器件(DUT),并连接stdin和stdout,这样调试命令可以发送到DUT,响应也可以从DUT接收,如图所示。

        举例说明:

class Mux2Tests(c: Mux2) extends Tester(c) { 
	val n = pow(2, 3).toInt
	for (s <- 0 until 2) {
		for (i0 <- 0 until 2) { 
			for (i1 <- 0 until 2) {
				poke(c.io.sel, s)
				poke(c.io.in1, i1)
				poke(c.io.in0, i0)
				step(1)
				expect(c.io.out, (if (s == 1) i1 else i0))
			}
		}
	}
}

         使用poke将Mux2的每个输入的分别设置为合适的值。对于这个例子,我们通过硬编码输入到一些已知的值并检查输出是否对应于已知的值来测试Mux2。为此,在每次迭代中,我们生成模块输入,让模拟将这些值分配给我们正在测试的器件c的输入,单步运行电路并对比期望值。最后,简单说明一下如何调用测试器:

chiselMainTest(args + "--test", () => Module(new Mux2())){ 
	c => new Mux2Tests(c)
}

还有其他的一些命令参数:

  • –targetDir 目标路径名前缀
  • –genHarness 生成C++文件
  • –backend v 生成verilog
  • –backend c 生成C++(默认)
  • –vcd 开启vcd打印
  • –debug 把所有的wire放入class文件

十一、状态元素

        Chisel支持的状态元素的最简单形式是上升沿触发寄存器,可以实例化为:

val reg = Reg(next = in)

        该电路具有输出,该输出是前一个时钟周期的输入信号产生的值。注意,我们不必指定Reg的类型,因为它会在实例化时从输入开始自动推断。在当前版本的Chisel中,时钟和复位是全局信号,在需要时可以隐式包含。

        使用寄存器,我们可以快速定义一些有用的电路结构。 例如,当当前值为true且之前的值为false时,上升沿检测器能够获取到布尔信号并输出true,如下所示:

def risingedge(x: Bool) = x && !Reg(next = x)

        计数器是一个重要的时序电路。 如果想构建一个向上计数器,计数到最大值max后回到零:

def counter(max: UInt) = {
	val x = Reg(init = UInt(0, max.getWidth))
	x := Mux(x === max, UInt(0), x + UInt(1))
	x
}

        计数器复位值为0(宽度大到足以容纳max),当电路的全局复位置位时,寄存器将初始化为该值。

        计数器可用于构建很多有用的时序电路。例如,我们可以通过在计数器达到零时输出true来构建脉冲发生器:

def pulse(n: UInt) = counter(n - UInt(1)) === UInt(0)

        然后可以通过切换方波发生器脉冲序列,在每个脉冲上的true和false之间切换: 

// Flip internal state when input true.
def toggle(p: Bool) = {
	val x = Reg(init = Bool(false)) 
	x := Mux(p, !x, x)
	x
}
// Square wave of a given period.
def squareWave(period: UInt) = toggle(pulse(period/2))

1、转发声明

        纯组合电路在节点之间不存在周期,如果检测到这样的周期,则Chisel将报告错误。因为它们不具有周期,所以可以总是以前馈方式构建组合电路,通过添加一些输入从已经定义的节点导出的新节点。

        时序电路在节点之间具有反馈,因此有时需要在生成节点被定义之前输出。因为Scala顺序执行程序语句,所以我们允许数据节点作为wire来提供节点声明,这样可以立即被使用,但其输入将稍后设置。

        如下例所示,在简单的CPU中,我们需要定义pcPlus4和brTarget的线,以便在定义之前引用它们:

val pcPlus4 = UInt()
val brTarget = UInt()
val pcNext = Mux(io.ctrl.pcSel, brTarget, pcPlus4)
val pcReg = Reg(next = pcNext, init = UInt(0, 32)) 
pcPlus4 := pcReg + UInt(4)
...
brTarget := addOut

        接线操作符:=用于在pcReg和addOut定义后连接。

2、条件更新

        在前面使用到寄存器的示例中,我们简单地将组合逻辑块连接到寄存器的输入。当描述状态元素的操作时,指定何时将发生寄存器更新并且用几个单独的语句指明这些更新。

        Chisel以when的形式提供条件更新规则,以支持这种顺序逻辑描述的风格。例如,

val r = Reg(init = UInt(0, 16)) 
when (cond) {
	r := r + UInt(1) 
}

        其中只有在cond为真时,才在当前时钟周期的结尾更新寄存器r。when的参数是返回Bool值。后面的更新块只能包含使用赋值运算符:=,简单表达式和用val定义的命名引线的更新语句。

        在条件更新序列中,条件为真的最近条件更新优先。 例如:

when (c1) { r := UInt(1) } 
when (c2) { r := UInt(2) }

        上述表达式会根据以下真值表更新r:

        条件更新结构可以嵌套,任何给定块在所有外嵌套条件的联合下才能执行。

        条件可以使用when,.elsewhen,.otherwise来链式表达,对应于Scala中的if, else if, else。例如:

when (c1) { u1 } 
.elsewhen (c2) { u2 } 
.otherwise { ud }
// the same as
when (c1) { u1 }
when (!c1 && c2) { u2 } 
when (!(c1 || c2)) { ud }

        Chisel还允许Wire,即一些组合逻辑的输出,成为条件性更新语句的目标,以允许逐步构建复杂的组合逻辑表达式。Chisel不允许不指定组合输出,并且如果组合输出未遇到无条件更新,则报告错误。

3、有限状态机

        在数字设计中有限状态机(FSM)是时序电路常用的类型。简单FSM的例子就是奇偶校验生成器:

class Parity extends Module { 
	val io = new Bundle {
		val in = Bool(dir = INPUT)
		val out = Bool(dir = OUTPUT) 
	}
	val s_even :: s_odd :: Nil = Enum(UInt(), 2) 
	val state = Reg(init = s_even)
	when (io.in) {
		when (state === s_even) { state := s_odd }
		when (state === s_odd) { state := s_even } 
	}
	io.out := (state === s_odd)
}

        其中Enum(Uint(), 2)生成两个UInt数。当io.in为true时更新状态。需要注意的是,FSM的所有机制都建立在寄存器,线和条件更新的基础上。

        下面是一个复杂的FSM例子,这是一个自动售货机接收货币的电路:

class VendingMachine extends Module {
	val io = new Bundle {
		val nickel = Bool(dir = INPUT)
		val dime   = Bool(dir = INPUT)
		val valid  = Bool(dir = OUTPUT)
	}
	val s_idle :: s_5 :: s_10 :: s_15 :: s_ok :: Nil = Enum(UInt(), 5)
	val state = Reg(init = s_idle) 
	when (state === s_idle) {
		when (io.nickel) { state := s_5 }
		when (io.dime) { state := s_10 } 
	}
	when (state === s_5) {
		when (io.nickel) { state := s_10 } 
		when (io.dime) { state := s_15 }
	}
	when (state === s_10) {
		when (io.nickel) { state := s_15 }
		when (io.dime) { state := s_ok } 
	}
	when (state === s_15) {
		when (io.nickel) { state := s_ok } 
		when (io.dime) { state := s_ok }
	}
	when (state === s_ok) {
		state := s_idle
	}
	io.valid := (state === s_ok) 
}

        采用switch风格代码如下:

class VendingMachine extends Module {
	val io = new Bundle {
		val nickle = Bool(dir = INPUT)
		val dime   = Bool(dir = INPUT)
		val valid  = Bool(dir = OUTPUT)
	}
	val s_idle :: s_5 :: s_10 :: s_15 :: s_ok :: Nil = Enum(UInt(), 5)
	val state = Reg(init = s_idle) 
	switch (state) { 
		is (s_idle) {
			when (io.nickel) { state := s_5 }
			when (io.dime) { state := s_10 } 
		} 
		is (s_5) {
			when (io.nickel) { state := s_10 } 
			when (io.dime) { state := s_15 }
		}
		is (s_10) {
			when (io.nickel) { state := s_15 }
			when (io.dime) { state := s_ok } 
		}
		is (s_ok) {
			state := s_idle
		}
	}
	io.valid := (state === s_ok)
}

十二、内存

        Chisel提供了创建只读和读/写存储器的功能。

1、ROM

        用户可以使用Vec定义ROM:

Vec(inits: Seq[T])
Vec(elt0: T, elts: T*)

        其中inits是初始化ROM的初始Data序列。例如,用户可以创建一个初始化为1,2,4,8的小型ROM,并使用计数器作为地址生成器循环访问所有值,如下所示:

val m = Vec(Array(UInt(1), UInt(2), UInt(4), UInt(8))) 
val r = m(counter(UInt(m.length)))

        我们可以使用如下初始化的ROM创建n值正弦查找表:

def sinTable (amp: Double, n: Int) = { 
	val times = Range(0, n, 1).map(i => (i*2*Pi)/(n.toDouble-1) - Pi) 
	val inits = times.map(t => SInt(round(amp * sin(t)), width = 32)) 
	Vec(inits)
}
def sinWave (amp: Double, n: Int) =
	sinTable(amp, n)(counter(UInt(n))

        其中amp用于缩放存储在ROM中的固定点值。

2、Mem

        存储器在Chisel中被给予特殊处理,因为存储器的硬件实现具有许多变化,例如,FPGA存储器与ASIC存储实例化的结果完全不同。Chisel定义了一个内存抽象,可以映射到简单的Verilog行为描述,也可以映射到从代工厂或IP厂商提供的外部内存生成器获得的内存模块实例。

        Chisel通过Mem结构可以支持随机存取存储器。写入Mems是正边沿触发,读取是组合或正边沿触发。

object Mem {
	def apply[T <: Data](type: T, depth: Int,
		seqRead: Boolean = false): Mem
}
class Mem[T <: Data](type: T, depth: Int, seqRead: Boolean = false)
	extends Updateable { 
		def apply(idx: UInt): T
	}

        通过使用UInt索引创建到Mems的端口。具有一个写入端口和两个组合读取端口的32-entry的寄存器堆可以如下表示:

val rf = Mem(UInt(width = 64), 32) 
when (wen) { rf(waddr) := wdata } 
val dout1 = rf(waddr1)
val dout2 = rf(waddr2)

        如果设置了可选参数seqRead,当读地址为Reg时,Chisel将尝试推断顺序读端口。

        单读端口,单写端口SRAM可以描述如下:

val ram1r1w = Mem(UInt(width = 32), 1024, seqRead = true)
val reg_raddr = Reg(UInt())
when (wen) { ram1r1w(waddr) := wdata } 
when (ren) { reg_raddr := raddr }
val rdata = ram1r1w(reg_raddr)

        单端口SRAM可以在读和写条件在链中相同时相互排斥时推断:

val ram1p = Mem(UInt(width = 32), 1024, seqRead = true) 
val reg_raddr = Reg(UInt())
when (wen) { ram1p(waddr) := wdata } 
.elsewhen (ren) { reg_raddr := raddr }
val rdata = ram1p(reg_raddr)

        如果相同的Mem地址在相同的时钟沿上被写入和顺序读取,或者如果顺序读取使能被清除,则读取数据为未定义。

十三、接口和批量连接

        对于更复杂的模块,在定义模块的 IO 时定义和实例化接口类通常很有用。

        首先,接口类促进重用,允许用户以有用的形式一次性捕获所有通用接口。 其次,接口允许用户通过模块之间的批量连接来显着减少布线。 最后,用户可以在一个地方对大型接口进行更改,从而减少添加或删除接口部分时所需的更新次数。

1、端口类、子类和嵌套

        正如我们之前看到的,用户可以通过定义一个继承 Bundle 的类来定义他们自己的接口。

         例如,可以为握手数据定义一个简单的链接,如下所示:

class SimpleLink extends Bundle {
val data = UInt(16, OUTPUT)
val valid = Bool(OUTPUT)
}

        然后我们可以通过使用包继承添加奇偶校验位来扩展 SimpleLink:

class PLink extends SimpleLink {
val parity = UInt(5, OUTPUT)
}

        通常,用户可以使用继承将他们的接口组织成层次结构。 从那里我们可以通过将两个 PLink 嵌套到一个新的 FilterIO 包中来定义过滤器接口:

class FilterIO extends Bundle {
val x = new PLink().flip
val y = new PLink()
}

        其中flip递归地改变Bundle的“相性”,将输入改变为输出和将输出改变为输入。

        我们现在可以通过定义一个过滤器类继承模块来定义一个过滤器:

class Filter extends Module {
val io = new FilterIO()
...
}

        其中io包含了FilterIO。

2、Bundle Vector

        除了单个元素之外,元素Vector可以组成更丰富的分层接口。 例如,为了创建一个带有输入向量的交叉开关,产生一个输出向量,并由 UInt 输入选择,我们使用 Vec 构造函数。

class CrossbarIo(n: Int) extends Bundle {
	val in = Vec.fill(n){ new PLink().flip() } 
	val sel = UInt(INPUT, sizeof(n))
	val out = Vec.fill(n){ new PLink() }
}

        其中Vec用第一个参获取大小,区块返回一个端口作为第二个参数。

3、批量连接

        我们现在可以将两个过滤器组成一个过滤器块,如下所示:

class Block extends Module { 
	val io = new FilterIO()
	val f1 = Module(new Filter()) 
	val f2 = Module(new Filter())
	f1.io.x <> io.x 
	f1.io.y <> f2.io.x 
	f2.io.y <> io.y
}

        其中<>批量连接同级模块之间的相反接口或父/子模块之间的相同接口。批量连接将相同名称的端口彼此连接。在所有连接完成后,Chisel警告用户端口是否只有一个到它们的连接。

4、接口视图

        考虑一个由控制路径和数据路径子模块以及主机和内存接口组成的简单 CPU,如图。

        在这个 CPU 中,我们可以看到控制路径和数据路径各自只连接到一部分指令和数据内存接口。Chisel 允许用户通过部分实现接口来做到这一点。 用户首先定义完整的 ROM 和 Mem 接口如下:

class RomIo extends Bundle { 
	val isVal = Bool(INPUT)
	val raddr = UInt(INPUT, 32) 
	val rdata = UInt(OUTPUT, 32)
}
class RamIo extends RomIo { 
	val isWr = Bool(INPUT)
	val wdata = UInt(INPUT, 32)
}

        现在控制逻辑可以根据这些接口构建接口:

class CpathIo extends Bundle { 
	val imem = RomIo().flip() 
	val dmem = 	RamIo().flip() 
}

        而且控制和数据通路模块可以通过部分地分配来给这个接口来构建,如下所示:

class Cpath extends Module { 
	val io = new CpathIo();
	...
	io.imem.isVal := ...;
	 io.dmem.isVal := ...; 
	 io.dmem.isWr := ...; 
	 ...
}
class Dpath extends Module { 
	val io = new DpathIo(); 
	...
	io.imem.raddr := ...; 
	io.dmem.raddr := ...; 
	io.dmem.wdata := ...;
	...
}

        我们现在可以使用批量连接来连接CPU,就像使用其他bundle一样:

class Cpu extends Module {
	val io = new CpuIo()
	val c = Module(new CtlPath()) 
	val d = Module(new DatPath()) 
	c.io.ctl <> d.io.ctl
	c.io.dat <> d.io.dat 
	c.io.imem <> io.imem
	d.io.imem <> io.imem
	c.io.dmem <> io.dmem
	d.io.dmem <> io.dmem
	d.io.host <> io.host
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

chisel快速入门(二) 的相关文章

  • 芯片面积估计方法

    一 概念 芯片面积的主要涵盖部分分为三部分 IO 芯片的信号及电源pad等 Standard cell 实现芯片的功能逻辑 Macro block 第三方IP PLL DAC POR Memory etc 芯片面积估计就是通过目标工艺的库信
  • 【HDLBits 刷题 12】Circuits(8)Finite State Manchines 27-34

    目录 写在前面 Finite State Manchines 2014 q3c m2014 q6b m2014 q6c m2014 q6 2012 q2fsm 2012 q2b 2013 q2afsm 2013 q2bfsm 写在前面 HD
  • 【从嵌入式视角学习香山处理器】四、Chisel语言基础

    文章目录 一 前言 二 Linux上对scala工程的操作 1 helloworld执行命令 2 有多个工程目录时 需要切换工程 3 编译报错 4 给vscode的scala插件设置JAVA HOME路径 三 ch4 基本组成部分 ch4
  • Chisel 手册 英文版

    Chisel Manual Jonathan Bachrach Huy Vo Krste Asanovi EECS Department UC Berkeley jrb huytbvo krste eecs berkeley edu Apr
  • Chisel实验笔记(一)

    最近在学习Risc v 其中伯克利大学开源了一款兼容Risc v指令集的处理器Rocket 而Rocket处理器是采用Chisel编写的 所以要学习Chisel Chisel的简单介绍如下 Chisel Constructing Hardw
  • 约束综合中的逻辑互斥时钟(Logically Exclusive Clocks)

    注 本文翻译自Constraining Logically Exclusive Clocks in Synthesis 逻辑互斥时钟的定义 逻辑互斥时钟是指设计中活跃 activate 但不彼此影响的时钟 常见的情况是 两个时钟作为一个多路
  • 组合逻辑毛刺消除(竞争冒险)

    一 毛刺产生的原因 信号在 IC FPGA 器件中通过逻辑单元连线时 是存在延时的 延时的大小不仅和连线的长短和逻辑单元的数目有关 而且也和器件的制造工艺 工作环境等有关 因此 信号在器件中传输的时候 所需要的时间是不能精确估计的 当多路信
  • 第二十章 Chisel基础——生成Verilog与基本测试

    经过前三章的内容 读者已经了解了如何使用Chisel构建一个基本的模块 本章的内容就是在此基础上 把一个Chisel模块编译成Verilog代码 并进一步使用Verilator做一些简单的测试 一 生成Verilog 前面介绍Scala的内
  • verdi中如何查看force信号信息

    转载 verdi中如何查看force信号信息 骏的世界 lujun org cn 在仿真中 我们会有对信号进行force的操作 从而实现某些特定的功能 但是在仿真波形中 不能直接从波形上看出 这些信号的驱动 是因为前级电路的驱动 还是因为f
  • 逻辑综合——工艺库

    一 库文件的设置 运行DC时需要用到的库文件有 目标库 target library 链接库 link library 符号库 symbol library 算术运算库 synthetic library 1 目标库 目标库是综合后电路网表
  • 八、RISC-V SoC外设——GPIO接口 代码讲解

    前几篇博文中注释了RISC V的内核CPU部分 从这篇开始来介绍RISC V SoC的外设部分 另外 在最后一个章节中会上传额外添加详细注释的工程代码 完全开源 如有需要可自行下载 目录 0 RISC V SoC注解系列文章目录 1 结构
  • 了解用于函数调用的 auipc+jalr 序列

    我试图阅读RISC V生成的程序集gcc我发现gcc创建序列auipc jalr对于某些函数调用 我不明白它是如何工作的 这是一个简单的例子 考虑以下C源文件 unsigned long id unsigned long x return
  • 如何用GDB调试交叉编译的QEMU程序?

    我在使用 GDB 调试 QEMU 中运行的简单程序时遇到问题 GDB似乎无法找到我在程序中的位置 因为它总是显示 作为我当前的位置 并且它永远不会达到我设置的任何断点 在一个终端中 我运行 QEMU cat add c int main i
  • Verilog 奇怪的仿真结果综合后

    我面临一个奇怪的问题 该代码适用于简单的 ALU 仅将感兴趣的代码粘贴到此处 always posedge clk or posedge rst begin if rst 1 begin mul valid shr 3 b000 end e
  • RISC-V 使用 LUI 和 ADDI 构建 32 位常量

    LUI 加载立即数 用于构建32位常量并使用U型格式 LUI 将 U 立即数放入目标寄存器 rd 的高 20 位 并用零填充最低 12 位 我在手册中找到了这个 但是如果我想将 0xffffffff 移动到寄存器 我需要的所有代码是 LUI
  • Chisel 中的矩阵运算

    Chisel是否支持加法 乘法 转置等矩阵运算 如果没有 实施它们的最佳方法是什么 向量怎么样 Chisel 不支持矩阵运算 它是一种用于编写实现此类操作的硬件生成器的 DSL 有关专用数学硬件生成器的示例 请参阅 Hwacha 硬件矢量单
  • 为什么 RV64 为 32 位操作而不是 64 位操作引入新的操作码

    在浏览 RISC V 规范时 我注意到 64 位版本与 32 位版本的不同之处在于 它 将寄存器扩展至 64 位 更改了指令以作用于整个 64 位范围 添加了执行 32 位操作的新指令 这使得 RV32 代码与 RV64 不兼容 但是 如果
  • 零/符号扩展是无操作的,为什么要为每种大小类型提供指令呢?

    对于 x86 和 x64 编译器生成类似的零 符号扩展 MOVSX 和 MOVZX 扩展本身并不是免费的 但允许处理器执行无序魔法加速 但在 RISC V 上 因此 无符号和有符号 32 位整数之间的转换是无操作 从有符号 32 位整数到有
  • 数组和结构体可以以不同的方式初始化吗?

    我的问题可能看起来很奇怪 事实上 这是上下文 我目前在切换时遇到一个奇怪的问题 关于我正在从事的项目 核心从pullinino到CV32 也发生了一些其他变化 例如关于crt0 如一些数据内存重置 这是一个 真实的 例子 说明了一个非常简单
  • 为什么 RISC-V S-B 和 U-J 指令类型以这种方式编码?

    我正在读一本书 计算机组织与设计RISC V版 我遇到了 S B 和 U J 指令类型的编码 我上面提到的那些类型有奇怪的编码立即字段 S B 类型将直接字段分为两部分 这是有道理的 因为所有指令编码都必须相似 但我无法理解为什么立即字段以

随机推荐

  • 【动态系统的建模与分析】一阶系统的单位阶跃响应+时间常数-笔记

    一个一阶系统 其数学表达 做系统识别 令qin c 去记录高度的变化 可得图像 这个系统的响应为 如果其4s达到稳定时间 则 该系统传递函数为 则
  • vue金额格式化保留两位小数

    Vue filter number function data return data toFixed 2 在需要的地方 integralval 100 number 元
  • Nginx常用模块

    Nginx常用模块 文章目录 Nginx常用模块 1 Nginx常用模块 1 1 Nginx目录索引 下载模块 1 1 1 配置autoindex语法 1 1 2 autoindex配置实例 1 1 3上传资源 1 1 4 autoinde
  • 【嵙大OJ】Problem 1217: 编写函数:浮点数取整 (Append Code)

    Problem A 编写函数 浮点数取整 Append Code Time Limit 1 Sec Memory Limit 2 MB Submit 7000 Solved 2864 Submit Status Web Board Desc
  • 操作系统重要概念——异步性

    在多道程序环境下 允许多个进程并发执行 进程在使用资源时可能需要等待或放弃 进程的执行并不是一气成的 而是以走走停停的形式推进 如下举例 进程以不可预知的速度向前推进 何时执行 何时暂停 何时完成都是未知的 这就造成了系统的异步性
  • UUID 生成(源代码编译)

    根据定义 UUID Universally Unique IDentifier 也称GUID 在时间和空间都是唯一的 为保证空间的唯一性 每个UUID使用了一个48位的值来记录 一般是计算机的网卡地址 为保证时间上的唯一性 每个UUID具有
  • ++ 符号

    关于 符号 i i 刚好最近看到一个关于i i 的测试题就查询资料测试了一下 int i 10 int j 30 i i Console WriteLine i Console WriteLine i i Console WriteLine
  • ajax保存文件对话框_VBA学习笔记34-2:Excel对话框(FileDialog)

    学习资源 Excel VBA从入门到进阶 第34集 by兰色幻想 这篇笔记写对话框的FileDialog对象 开始前先把上篇漏写的改变窗口默认路径补一下 chdrive 盘符 可以改变默认驱动器 chdir 路径 可以改变默认路径 这两个默
  • C#调用C回调函数后,程序奔溃问题

    原始代理声明 delegate void DlgVideoStreamCallBack IntPtr pData int size int height int width IntPtr pUserData C函数导入 DllImport
  • gitlab-ce-10.0.2版本升级到15.4.2

    先看官方升级文档说明 官方文档链接 升级 如果你的gitlab版本是10 0 2 那个要升级到15 4 2 首先得过度如下几个版本 10 1 0 10 2 3 10 8 7 11 3 4 11 11 8 12 0 12 12 1 17 12
  • C语言小游戏-俄罗斯方块

    C语言小游戏 俄罗斯方块 全部代码如下所示 include
  • 第十一章 数据可视化 - 地图可视化

    目录 疫情地图的使用 疫情地图 国内疫情地图 疫情地图 省级疫情地图 疫情地图的使用 第一阶段 第十一章 01 数据可视化案例 地图 基础地图使用 哔哩哔哩 bilibili 演示地图可视化的基本使用 from pyecharts char
  • layui 树型tree组件 回显BUG

    父级回选时 不管子级有没有设置回显 都会导致子级全部勾选 解决办法是 在后端代码添加逻辑 有子树的都不选中 因为选中子树时 父级会跟着选中 所有这是没问题的
  • 图片服务器

    文章目录 一 项目简介 二 功能及场景 三 业务设计 四 数据库设计 准备图片表 准备实体类 五 API设计 常用功能封装 文件上传 文件上传 获取图片列表接口 获取图片内容 删除图片接口 六 项目优化 七 测试 自动化测试 测试用例 一
  • Qt做的俄罗斯方块游戏

    最近一直在用Qt折腾一个简单的俄罗斯方块游戏 期间断断续续经过将近一个月的折腾 终于完成啦 挂在这里 供大家评阅 第一次做游戏 肯定会有很多的不足之处 希望大家指正 其实做这个游戏主要是想学学多层控件的效果 因为以前做过一些简单的应用型软件
  • 华为OD机试 - 最大矩阵和(Java)

    题目描述 给定一个二维整数矩阵 要在这个矩阵中选出一个子矩阵 使得这个子矩阵内所有的数字和尽量大 我们把这个子矩阵称为和最大子矩阵 子矩阵的选取原则是原矩阵中一块相互连续的矩形区域 输入描述 输入的第一行包含2个整数n m 1 lt n m
  • 解决中文乱码用spring经典编码过滤器org.springframework.web.filter.CharacterEncodingFilter

    使用spring的前提下在web xml中配置
  • Dynamics CRM 错误 问题排查

    很多时候在网上或者群里面看到别人的问题 第一反应就是 有没有搞错 这样的问题怎么让人回答呢 发现很多开发 维护根本没有一点点文档钻研精神 对于这些业务系统 第一是看日志记录 第二是看sdk 别的一切都只是浮云 说多了 回头说这个错误问题排查
  • 什么是开尔文测试?什么时候需要采用开尔文接法?

    开尔文 Kelvin 测试就是通常所说的四线测试方式 四线开尔文测试的目的是扣除导线电阻带来的压降 一段30cm长导线的等效电阻大概是十毫欧姆到百毫欧姆 如果通过导线的电流足够大 比如是安培级的话 那么导线两端的压降就达到几十到上百mV 如
  • chisel快速入门(二)

    上一篇见此 chisel快速入门 一 沧海一升的博客 CSDN博客简单介绍了chisel 使硬件开发者能快速上手chisel https blog csdn net qq 21842097 article details 121415341