Gemmini测试test文件chisel源码详解(一)
DMACommandTrackerTest.scala
源码如下:
package gemminiimport scala.collection.mutable.ArrayBufferimport chisel3._
import chisel3.iotesters.{ChiselFlatSpec, PeekPokeTester}class DMACommandTrackerTester(c: DMAReadCommandTracker[UInt]) extends PeekPokeTester(c) {case class AllocatedCmd(id: Int, tag: Int, requestsSent: Int)var max_cycles = 100000000var cmdsToAllocate = 100val cmdIdsAllocated: ArrayBuffer[AllocatedCmd] = ArrayBuffer() // TODO use a map insteadwhile ((cmdsToAllocate > 0 || cmdIdsAllocated.size > 0) && max_cycles > 0) {val can_allocate = cmdsToAllocate > 0 && rnd.nextBoolean()poke(c.io.alloc.valid, can_allocate)val tag = rnd.nextInt(100)poke(c.io.alloc.bits.tag, tag)val can_read_cmd = rnd.nextBoolean()poke(c.io.cmd_completed.ready, can_read_cmd)if (rnd.nextBoolean() || cmdIdsAllocated.size == 0) {poke(c.io.request_returned.valid, false)} else {val cmd_buf_id = rnd.nextInt(cmdIdsAllocated.size)val id = cmdIdsAllocated(cmd_buf_id).idval requestsSent = cmdIdsAllocated(cmd_buf_id).requestsSentif (requestsSent < c.nRequests) {poke(c.io.request_returned.valid, true)poke(c.io.request_returned.bits.cmd_id, id)cmdIdsAllocated(cmd_buf_id) = cmdIdsAllocated(cmd_buf_id).copy(requestsSent=requestsSent+1)} else {poke(c.io.request_returned.valid, false)}}val alloc_fire = can_allocate && peek(c.io.alloc.ready) != 0if (alloc_fire) {cmdsToAllocate -= 1val cmd_id = peek(c.io.alloc.bits.cmd_id).toIntassert(!cmdIdsAllocated.exists(_.id == cmd_id), s"$cmd_id already allocated")cmdIdsAllocated += AllocatedCmd(cmd_id, tag, 0)}val cmd_completed_fire = can_read_cmd && peek(c.io.cmd_completed.valid) != 0if (cmd_completed_fire) {val cmd_id = peek(c.io.cmd_completed.bits.cmd_id).toIntval cmd_buf_id = cmdIdsAllocated.zipWithIndex.collectFirst { case (AllocatedCmd(id, _, _), i) if id == cmd_id => i }.getval tag = peek(c.io.cmd_completed.bits.tag).toIntif (cmdIdsAllocated(cmd_buf_id).tag != tag) {println(s"wrong tag, $tag, ${cmdIdsAllocated(cmd_buf_id).tag}")max_cycles = 0}assert(cmdIdsAllocated(cmd_buf_id).tag == tag, "tag is incorrect")assert(cmdIdsAllocated(cmd_buf_id).requestsSent == c.nRequests, "returned after wrong number of requests returned")cmdIdsAllocated.remove(cmd_buf_id)}step(1)max_cycles -= 1}assert(max_cycles > 0, "reached max_cycles")
}class DMACommandTrackerUnitTest extends ChiselFlatSpec {val testerArgs = Array("--backend-name", "treadle",// "--generate-vcd-output", "on","--target-dir", "test_run_dir/dmacommandtracker")behavior of "DMACommandTracker"it should "work" in {chisel3.iotesters.Driver.execute(testerArgs, () => new DMAReadCommandTracker(2, 16, UInt(29.W))) {c => new DMACommandTrackerTester(c)} should be (true)}
}
这段代码是用于测试 DMAReadCommandTracker 模块的功能和正确性。DMAReadCommandTracker 模块是一个用于跟踪 DMA 读取命令的状态的硬件模块,它可以分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。
这段代码包含两个类,DMACommandTrackerTester 和 DMACommandTrackerUnitTest。DMACommandTrackerTester 类是一个用于对 DMAReadCommandTracker 模块进行输入输出操作和断言检查的测试类。
DMACommandTrackerUnitTest 类是一个用于运行 DMACommandTrackerTester 的测试类,它继承自 ChiselFlatSpec 类,并指定了一些测试参数,如后端名称,目标目录等。
这段代码的目的是验证 DMAReadCommandTracker 模块是否能正确地分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。
-
DMACommandTrackerTester 类是一个继承自 PeekPokeTester 的测试器类,它接受一个 DMAReadCommandTracker 类型的参数 c。
- AllocatedCmd 是一个 case class,它用于存储已分配命令的 id、tag 和 requestsSent,id是命令编号、tag是标签(记录版本类型等)、requestsSent记录命令的进度。
- max_cycles 是一个整数变量,它表示最大的执行周期数,用于限制测试的时间。
- cmdsToAllocate 是一个整数变量,它表示要分配的命令数量,初始值为 100。
- cmdIdsAllocated 是一个 ArrayBuffer 类型的变量,它用于存储已分配命令的 AllocatedCmd 对象。
- while 循环是测试的主要逻辑,它在 cmdsToAllocate 大于 0 或 cmdIdsAllocated 不为空,并且 max_cycles 大于 0 的条件下执行。循环中包含以下步骤:
- 随机决定是否可以分配新的命令,并将结果赋值给 can_allocate 变量,然后将其 poke 给 c.io.alloc.valid 输入端口。
- 随机生成一个 0 到 99 之间的整数作为 tag,并将其 poke 给 c.io.alloc.bits.tag 输入端口。
- 随机决定是否可以读取已完成的命令,并将结果赋值给 can_read_cmd 变量,然后将其 poke 给 c.io.cmd_completed.ready 输入端口。
- 如果随机决定不返回请求或 cmdIdsAllocated 为空,则将 false poke 给 c.io.request_returned.valid 输入端口;否则,从 cmdIdsAllocated 中随机选择一个已分配命令,获取其 id 和 requestsSent,如果 requestsSent 小于 c.nRequests,则将 true poke 给 c.io.request_returned.valid 输入端口,并将 id poke 给 c.io.request_returned.bits.cmd_id 输入端口,同时将 cmdIdsAllocated 中对应的 AllocatedCmd 对象的 requestsSent 增加 1;否则,将 false poke 给 c.io.request_returned.valid 输入端口。
- 判断是否发生了 alloc_fire 事件,即 can_allocate 为 true 并且 c.io.alloc.ready 输出端口为 true。如果是,则将 cmdsToAllocate 减 1,并从 c.io.alloc.bits.cmd_id 输出端口读取分配的 cmd_id,断言 cmdIdsAllocated 中不存在相同的 id,并将新建的 AllocatedCmd 对象添加到 cmdIdsAllocated 中。
- 判断是否发生了 cmd_completed_fire 事件,即 can_read_cmd 为 true 并且 c.io.cmd_completed.valid 输出端口为 true。如果是,则从 c.io.cmd_completed.bits.cmd_id 和 c.io.cmd_completed.bits.tag 输出端口读取完成的 cmd_id 和 tag,并在 cmdIdsAllocated 中找到对应的 AllocatedCmd 对象和索引。如果对象中的 tag 不等于输出端口中的 tag,则打印错误信息并将 max_cycles 设为 0;否则,断言对象中的 tag 等于输出端口中的 tag,并断言对象中的 requestsSent 等于 c.nRequests,然后从 cmdIdsAllocated 中移除该对象。
- 调用 step(1) 方法执行一个时钟周期,并将 max_cycles 减 1。
- 在 while 循环结束后,断言 max_cycles 大于 0,表示测试没有超时。
-
DMACommandTrackerUnitTest 类是一个继承自 ChiselFlatSpec 的单元测试类,它定义了一些变量和方法。
- testerArgs 是一个字符串数组,它存储了一些测试参数,如后端名称、目标目录等。
- behavior of “DMACommandTracker” 是一个字符串字面量,它表示要测试的类名。
- it should “work” 是一个方法,它表示要测试的功能。方法中包含以下步骤:
- 调用 chisel3.iotesters.Driver.execute 方法,传入 testerArgs 和一个匿名函数,该函数返回一个新建的 DMAReadCommandTracker 对象,其参数为 2、16 和 UInt(29.W)。
- 在 execute 方法的第二个参数中,传入一个匿名函数,该函数接受一个 DMAReadCommandTracker 类型的参数 c,并返回一个新建的 DMACommandTrackerTester 对象,其参数为 c。
- 在 execute 方法的返回值上调用 should be (true) 方法,断言测试结果为 true。
注释版:
package gemminiimport scala.collection.mutable.ArrayBufferimport chisel3._
import chisel3.iotesters.{ChiselFlatSpec, PeekPokeTester}//测试 DMAReadCommandTracker 模块的功能和正确性。
//DMAReadCommandTracker 模块是一个用于跟踪 DMA 读取命令的状态的硬件模块,它可以分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。
//包含两个类,DMACommandTrackerTester 和 DMACommandTrackerUnitTest。
//DMACommandTrackerTester 类是一个用于对 DMAReadCommandTracker 模块进行输入输出和断言检查的测试类。
//DMACommandTrackerUnitTest 类是一个用于运行 DMACommandTrackerTester 的测试类
//这段代码的目的是验证 DMAReadCommandTracker 模块是否能正确地分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。class DMACommandTrackerTester(c: DMAReadCommandTracker[UInt]) extends PeekPokeTester(c) {
//用于存储已分配命令的 id、tag 和 requestsSent,id是命令编号、tag是标签(记录版本类型等)、requestsSent记录命令的进度。case class AllocatedCmd(id: Int, tag: Int, requestsSent: Int)
//最大的执行周期数var max_cycles = 100000000
//要分配的命令数量,初始值为 100;cmdIdsAllocated存储已分配命令的 AllocatedCmd 对象var cmdsToAllocate = 100val cmdIdsAllocated: ArrayBuffer[AllocatedCmd] = ArrayBuffer() // TODO use a map instead
//循环条件:1.还有剩余的周期数可执行 2.待分配命令数不是0或者已分配命令不为空while ((cmdsToAllocate > 0 || cmdIdsAllocated.size > 0) && max_cycles > 0) {
//当还有命令可以分配的时候,随机决定是否可以分配新的命令,并将结果赋值给 can_allocate 变量,然后将其 poke 给 c.io.alloc.valid 输入端口。val can_allocate = cmdsToAllocate > 0 && rnd.nextBoolean()poke(c.io.alloc.valid, can_allocate)
//随机生成一个 0 到 99 之间的整数作为 tag,并将其 poke 给 c.io.alloc.bits.tag 输入端口。val tag = rnd.nextInt(100)poke(c.io.alloc.bits.tag, tag)
//随机决定是否可以读取已完成的命令,并将结果赋值给 can_read_cmd 变量,然后将其 poke 给 c.io.cmd_completed.ready 输入端口。
//当 can_read_cmd 为 true 时,表示允许读取已完成的命令,即表明模块已准备好接收命令完成的信号。当 can_read_cmd 为 false 时,表示不允许读取已完成的命令,即表明模块不准备接收命令完成的信号。val can_read_cmd = rnd.nextBoolean()poke(c.io.cmd_completed.ready, can_read_cmd)
//如果随机决定不返回请求或 cmdIdsAllocated 为空,则将 false poke 给 c.io.request_returned.valid 输入端口if (rnd.nextBoolean() || cmdIdsAllocated.size == 0) {poke(c.io.request_returned.valid, false)} else {
//从 cmdIdsAllocated 中随机选择一个已分配命令,获取其 id 和 requestsSentval cmd_buf_id = rnd.nextInt(cmdIdsAllocated.size)val id = cmdIdsAllocated(cmd_buf_id).idval requestsSent = cmdIdsAllocated(cmd_buf_id).requestsSent//nRequests 用于表示每个已分配的命令所需的请求次数。在这个上下文中,当已分配的命令的请求次数达到 nRequests 时,该命令将被认为是完成的。
//如果 requestsSent 小于 c.nRequests,则将 true poke 给 c.io.request_returned.valid 输入端口
//将 id poke 给 c.io.request_returned.bits.cmd_id 输入端口,同时将 cmdIdsAllocated 中对应的 AllocatedCmd 对象的 requestsSent 增加 1;否则,将 false poke 给 c.io.request_returned.valid 输入端口。//说白了就是判断这个命令是否完成了,然后作相应操作if (requestsSent < c.nRequests) {poke(c.io.request_returned.valid, true)poke(c.io.request_returned.bits.cmd_id, id)cmdIdsAllocated(cmd_buf_id) = cmdIdsAllocated(cmd_buf_id).copy(requestsSent=requestsSent+1)} else {poke(c.io.request_returned.valid, false)}}
//检查是否可以分配命令,(即 can_allocate 为 true 并且 c.io.alloc.ready 输出端口为 true)
//如果是,则将 cmdsToAllocate 减 1,并从 c.io.alloc.bits.cmd_id 输出端口读取分配的 cmd_id,断言 cmdIdsAllocated 中不存在相同的 id,并将新建的 AllocatedCmd 对象添加到 cmdIdsAllocated 中。val alloc_fire = can_allocate && peek(c.io.alloc.ready) != 0if (alloc_fire) {cmdsToAllocate -= 1val cmd_id = peek(c.io.alloc.bits.cmd_id).toIntassert(!cmdIdsAllocated.exists(_.id == cmd_id), s"$cmd_id already allocated")cmdIdsAllocated += AllocatedCmd(cmd_id, tag, 0)}
//检查是否可以读取完成的命令,(即 can_read_cmd 为 true 且 c.io.cmd_completed.valid 输出端口的值不为 0)
//如果是,则读取id和tag,并在 cmdIdsAllocated 中找到对应的 AllocatedCmd 对象和索引。
//如果原tag与输出端口的tag不同就报错,相同就删除这个命令val cmd_completed_fire = can_read_cmd && peek(c.io.cmd_completed.valid) != 0if (cmd_completed_fire) {val cmd_id = peek(c.io.cmd_completed.bits.cmd_id).toIntval cmd_buf_id = cmdIdsAllocated.zipWithIndex.collectFirst { case (AllocatedCmd(id, _, _), i) if id == cmd_id => i }.getval tag = peek(c.io.cmd_completed.bits.tag).toIntif (cmdIdsAllocated(cmd_buf_id).tag != tag) {println(s"wrong tag, $tag, ${cmdIdsAllocated(cmd_buf_id).tag}")max_cycles = 0}assert(cmdIdsAllocated(cmd_buf_id).tag == tag, "tag is incorrect")assert(cmdIdsAllocated(cmd_buf_id).requestsSent == c.nRequests, "returned after wrong number of requests returned")cmdIdsAllocated.remove(cmd_buf_id)}
//每次循环周期减少step(1)max_cycles -= 1}
//判断有没有超市assert(max_cycles > 0, "reached max_cycles")
}class DMACommandTrackerUnitTest extends ChiselFlatSpec {
//存取测试参数val testerArgs = Array("--backend-name", "treadle",// "--generate-vcd-output", "on","--target-dir", "test_run_dir/dmacommandtracker")
//表示要测试的是 DMACommandTracker 模块behavior of "DMACommandTracker"it should "work" in {chisel3.iotesters.Driver.execute(testerArgs, () => new DMAReadCommandTracker(2, 16, UInt(29.W))) {c => new DMACommandTrackerTester(c)} should be (true)//测试参数与断言测试结果}
}
相关文章:
Gemmini测试test文件chisel源码详解(一)
DMACommandTrackerTest.scala 源码如下: package gemminiimport scala.collection.mutable.ArrayBufferimport chisel3._ import chisel3.iotesters.{ChiselFlatSpec, PeekPokeTester}class DMACommandTrackerTester(c: DMAReadCommandTracker[UInt]) extends Pee…...
RabbitMQ中的手动应答和自动应答
当使用RabbitMQ来处理消息时,消息确认是一个重要的概念。RabbitMQ提供了两种不同的消息确认方式:自动应答(Automatic Acknowledgment)和手动应答(Manual Acknowledgment)。这两种方式适用于不同的应用场景&…...

【C语言】文件的操作与文件函数的使用(详细讲解)
前言:我们在学习C语言的时候会发现在编写一个程序的时候,数据是存在内存当中的,而当我们退出这个程序的时候会发现这个数据不复存在了,因此我们可以通过文件把数据记录下来,使用文件我们可以将数据直接存放在电脑的硬盘…...

ROS-PX4仿真笔记_1
offbord模式测试 rosrun offboard_pkg position stablelize模式 lqr控制器实验 roslaunch px4 fast_test.launch 无人机起飞1.5-2m sh mybot_gazebo.sh#roslaunch px4 fast_racing.launch & sleep 20; roslaunch ego_planner single_run_in_gazebo.launch & sleep 1…...
使用 Python 中的小波变换信号驾驭股票价格的波动
一、简介 股票上涨和下跌,创造出像海浪一样难以预测的模式和走势。然而,就像科学家通过了解下面的水流来预测波浪的运动一样,我们也可以使用类似的工具破译股票市场的一些模式。 通过利用小波变换的力量,我们深入表面,试图揭示驱动股价的深层原因。这段旅程不仅仅涉及数字…...

AndroidStudio模拟器,没有Google Play的就有ROOT权限
正确选择版本 测试 D:\>adb shell emulator64_x86_64:/ $ su emulator64_x86_64:/ #...
复选框 前端代码
表单中复选框选项 <el-form-item label="是否公开:" hidden="true"><input type="checkbox...
每日一练 | 网络工程师软考真题Day41
1、包过滤防火墙对通过防火墙的数据包进行检查,只有满足条件的数据包才能通过,对数据包的检查内容一般不包括 。 A.源地址 B.目的地址 C.协议 D.有效载荷 2、下面关于ARP木马的描述中,错误的…...
vue使用pinia存储数据并保持数据持久化
在Vue中使用Pinia存储数据并保持数据持久化,你可以遵循以下步骤: 安装Pinia:首先,你需要安装Pinia。可以通过npm或yarn来安装它。在终端中运行以下命令: npm install pinia# 或者使用yarn yarn add pinia创建Pinia St…...

k8s - Flannel
1.Flannel概念剖析 Flannel是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,其目的在于帮助每一个使用 Kuberentes 的 CoreOS 主机拥有一个完整的子网。这次的分享内容将从Flannel的介绍、工作原理及安装和配置三方…...

服务器中了balckhoues勒索病毒怎么办?勒索病毒解密,数据恢复
近日,云天数据恢复中心发现,有多位用户的服务器中了一种名为balckhoues的勒索病毒,因为绝大多数用户是第一次遇到这种情况,所以对这种类型的勒索病毒并不是很了解。那接下来我们将对balckhoues勒索病毒做一个分析。 中毒特征 服务…...
react-pdf | Warning: TextLayer styles not found.
问题描述: 使用react-pdf展示pdf,但是报警告,Warning: TextLayer styles not found. 解决方法: <Pageloading{"加载中..."}renderAnnotationLayer{false}renderTextLayer{false}/> 添加属性如上,设…...
vue上传文件MD5加密
1.下载MD5依赖 npm install crypto-js 2.在utils文件夹中新增文件md5方法文件,文件名自定义(fileMd5Sum.js) import CryptoJs from crypto-js export default {// md5值计算fileMd5Sum(file) {let CryptoJS require("crypto-js"…...

vue2 .sync 修饰符
vue2 .sync 修饰符 **创建 工程: H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\13-sync修饰符 vue --version vue create v-sy…...

使用Tensorrt的一般步骤
使用Tensorrt的一般步骤 TensorRT的使用包括两个阶段:build and deployment。 build:该阶段主要完成模型转换(从caffe或TensorFlow到TensorRT),如下图所示,在模型转换时会完成前述优化过程中的层间融合&am…...

uniapp apple 苹果登录 离线本地打包
官方文档 uni-app官网 文档写的不全,没有写离线打包流程 加lib 签名里带 sign in with apple hbuilder开关 代码 测试代码,获取app里所有的provider uni.getProvider({service: oauth,success: function (res) {console.log(res.provider)uni.showT…...

【数据库】Sql Server数据迁移,处理自增字段赋值
给自己一个目标,然后坚持一段时间,总会有收获和感悟! 在实际项目开发中,如果遇到高版本导入到低版本,或者低版本转高版本,那么就会出现版本不兼容无法导入,此时通过程序遍历创建表和添加数据方式…...

JOSEF约瑟 矿用一般型选择性漏电继电器 LXY2-660 Φ45 JKY1-660
系列型号: JY82A检漏继电器 JY82B检漏继电器 JY82-380/660检漏继电器 JY82-IV检漏继电器 JY82-2P检漏继电器 JY82-2/3检漏继电器 JJKY检漏继电器 JD型检漏继电器 JY82-IV;JY82J JY82-II;JY82-III JY82-1P;JY82-2PA;JY82-2PB JJB-380;JJB-380/660 JD-12…...
DHCP自动分配IP原理
DHCP自动分配IP原理 1.采用UDP通信方式 2.服务器IP:255.255.255.255; 服务器端口:67, 设备接收端口:68 3.设备向服务器发送DISCOVER信息 4.设备收到服务器回应,且解析正确 5.设备向服务器发送REQUEST请求消息 6.设备接…...

读书笔记-《ON JAVA 中文版》-摘要26[第二十三章 注解]
文章目录 第二十三章 注解1. 基本语法1.1 基本语法1.2 定义注解1.3 元注解 2. 编写注解处理器2.1 编写注解处理器2.2 注解元素2.3 默认值限制 3. 使用javac处理注解4. 基于注解的单元测试5. 本章小结 第二十三章 注解 注解(也被称为元数据)为我们在代码…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...