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. 本章小结 第二十三章 注解 注解(也被称为元数据)为我们在代码…...
Phi-3-mini-4k-instruct-gguf完整指南:模型原理、部署、调参、运维一体化
Phi-3-mini-4k-instruct-gguf完整指南:模型原理、部署、调参、运维一体化 1. 模型概述 Phi-3-mini-4k-instruct-gguf是微软Phi-3系列中的轻量级文本生成模型GGUF版本。这个模型特别适合处理问答、文本改写、摘要整理和简短创作等任务。相比完整版模型,…...
Leather Dress Collection实战案例:用Leather TankTop Pants生成运动风皮革穿搭图集
Leather Dress Collection实战案例:用Leather TankTop Pants生成运动风皮革穿搭图集 1. 引言:当皮革遇上运动风 想象一下,你正在为一个运动潮牌设计新一季的视觉素材。客户想要一种既酷炫又充满活力的感觉——皮革的质感,运动的…...
【2026年最新600套毕设项目分享】springboot足球训练营系统(14309)
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告/任务书)远程调试控屏包运行一键启动项目&…...
避坑指南:在Windows/Mac上从零部署Grounding DINO和SAM的完整流程(含模型下载、环境配置)
避坑指南:在Windows/Mac上从零部署Grounding DINO和SAM的完整流程 部署多模态AI模型时,90%的失败发生在环境配置阶段。本文将手把手带你避开所有常见陷阱,从模型下载到最终运行,提供双系统兼容的解决方案。不同于常规教程…...
AI辅助开发:描述需求,快马AI自动生成旅行商问题算法与可视化
最近在做一个旅行商问题(TSP)的算法项目,想试试用AI辅助开发能有多高效。结果发现InsCode(快马)平台的AI功能真的帮了大忙,整个过程特别顺畅。这里分享一下我的体验。 需求分析阶段 刚开始我只是简单描述了需求:"需要一个用模拟退火算…...
【TCC从理论到亿级支付系统落地】:7个真实生产环境故障复盘+可直接套用的补偿模板
第一章:TCC分布式事务的核心原理与适用边界TCC(Try-Confirm-Cancel)是一种基于业务层面的柔性事务模型,其核心在于将一个分布式事务拆解为三个明确阶段:资源预留(Try)、最终确认(Con…...
为什么你的Java车载服务在-40℃冷启动失败?温度敏感型ClassLoader加载异常的12小时紧急修复路径
第一章:为什么你的Java车载服务在-40℃冷启动失败?温度敏感型ClassLoader加载异常的12小时紧急修复路径低温环境并非仅影响硬件可靠性——JVM 的类加载机制在极端低温下会触发底层文件系统与内存映射的隐式行为偏移。某车规级 Java 服务在-40℃冷启动时反…...
OpenClaw对接Qwen3-4B实战:5步完成本地模型调用与自动化任务
OpenClaw对接Qwen3-4B实战:5步完成本地模型调用与自动化任务 1. 为什么选择OpenClawQwen3-4B组合 去年冬天第一次听说OpenClaw时,我正被重复性的文件整理工作折磨得焦头烂额。作为一个习惯用脚本解决问题的开发者,我试过各种自动化工具&…...
大疆诉影石创新专利侵权,FTO综合分析筑牢研发风控屏障
3月23日,全球无人机巨头大疆对同行影石创新提起专利权属纠纷诉讼,涉案6项专利聚焦无人机飞行控制、结构设计、影像处理等核心技术领域,这场行业龙头间的知识产权纠纷,成为近日行业关注焦点。职务发明权属成为争议关键本次纠纷由大…...
STM32环境检测系统设计与物联网应用
1. 项目概述这个基于STM32的环境检测系统是我去年为一个工业客户开发的解决方案,经过3个月的迭代优化已经稳定运行了半年多。系统通过多种传感器实时监测环境参数,并将数据上传至OneNet云平台,实现了本地和远程的双重监控。提示:项…...
