异步FIFO设计
1 FIFO简介
FIFO的本质是RAM
,具有先进先出的特性。
FIFO的基本使用原则:空时不能读,满时不能写
FIFO的两个重要参数:宽度和深度
FIFO的两种类型:
- 同步FIFO:读写时钟相同,通常用来做数据缓存
- 异步FIFO:读写时钟不同,通常用来做跨时钟域处理
2 异步FIFO设计要点
2.1 空满信号产生的原理
读空信号:
- 复位的时候(读指针和写指针相等),读空信号有效
- 读指针赶上写指针的时候,读空信号有效
写满信号:
- 写指针赶上读指针的时候
2.2 空满指针跨时钟域处理,为什么要使用Gray信号进行同步
空满信号是通过读写指针的比较来产生,但是读指针属于读时钟域,写指针属于写时钟域,读写指针的比较有一个跨时钟域的问题,因此需要对读写指针进行信号的同步:
- 将写指针同步到读时钟域,然后读指针再和同步后的写指针比较,产生读空信号
- 将读指针同步到写时钟域,然后写指针再和同步后的读指针比较,产生写满信号
使用格雷码Gray
表示的读写指针用两级寄存器同步:
同步过程的亚稳态问题是无法解决的,需要保证即使出现亚稳态也不影响设计的功能
- 如果使用二进制编码表示的读写指针进行同步,当出现亚稳态的时候,将会影响设计的功能。
例如现在将写指针同步到读时钟域,去判断读空信号。写指针0111–>1000,同时有4bit数据发生跳变,在亚稳态的影响下,同步后的写指针的数值可能是0000-1111之间的任何一个数值。本来应该判空的,但是现在没有判空,将会读取错误的数据,使得设计出现问题。 - 如果使用格雷码表示的读写指针进行同步,即使出现亚稳态,也不会影响设计的功能。
格雷码的特点是,相邻的编码之间每次只有1bit数据发生变化。那么这1bit数据如果发生亚稳态会影响设计的功能吗?答案是不会。例如现在还是将写指针同步到读时钟域,去判断读空信号。假设格雷码写指针从0000->0001,写指针同步出错,出错的结果只能是0000->0000,即写指针不变。这种结果最多就是让空信号标志在FIFO不是真正空的时候产生,而不会产生空读的情形。只会影响FIFO的效率,而不会影响到FIFO的功能。
所以Gray码保证的是同步后的读写指针即使在出错的情形下依然能够保证FIFO功能的正确性
2.3 设计的时候,读写指针的同步至少需要消耗两个时钟周期,肯定会使得空满信号的判断有延迟,会不会导致设计出错??
假设现在将写指针同步到读时钟域再和读指针比较进行FIFO空状态判断,因为在同步写指针时需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错。
同理将读指针同步到写时钟域再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满,这样更保守,肯定不会出错。
总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,会稍微有性能损失,但是不会出错
2.4 读写时钟不同频,在同步的过程中,可能会有指针遗漏,是否会影响功能?
异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,慢的时钟域同步到快的时钟域不会出现漏掉指针的情况,但是将指针从快的时钟域同步到慢的时钟域时可能会有指针遗漏。
举例:假设读慢写快
进行满标志判断的时候,需要将读指针同步到写时钟域,因为写时钟快,不会发生读指针同步遗漏的情况,但是由于同步需要消耗至少两个时钟周期,导致读指针同步延迟,读时钟域rd_ptr=3,但是写时钟域的sync_rd_ptr=2,这样可能会导致满标志信号提前产生,满并非真满,但是不影响FIFO功能。
进行空标志判断的时候,需要将写指针同步到读时钟域,因为读时钟慢,会发生写指针同步遗漏的情况,不用关心漏掉哪几个,而是担心漏掉的指针是否会对设计产生影响,比如写指针从0写到10,期间读时钟域只同步捕捉到了3、5、8这三个写指针而漏掉了其他指针。当同步到8这个写指针时,真实的写指针可能已经写到10 ,相当于在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去,这样在判断它是不是空的时候会出现不是真正空的情况,漏掉的指针不会对FIFO的逻辑操作产生影响(即不会影响,FIFO在满的时候写数据,空的时候读数据,造成的功能错误),只会影响FIFO的工作效率。
总而言之,实际的读/写指针大于同步之后的读/写指针的数值,这种情况不会影响FIFO的功能,只会影响FIFO的效率。(2.3和2.4)均是这种情况
2.5 二进制转Gray码
原理
:
代码
代码编写思路
大概就是格雷码最高位与二进制码一致,其余各位为二进制码对应位与其相邻的高位异或,如格雷码的第二位为二进制的第二位与二进制码的第三位异或。因此实现代码则为二进制码本身与逻辑右移一位后的二进制码异或即可得。
2.6 Gray码转二进制
原理
代码
代码编写思路
二进制码的最高位与格雷码最高位保持一致,其余通过自生移位与异或得到,如b[2]为格雷码逻辑右移两位(逻辑右移,最高位补0)再自身四位异或,0与任何数异或均为其本身。
2.7 读空的条件是写指针等于读指针,写满的条件也是写指针等于读指针,到底如何区分呢?
二进制码判断空满
:指针的位宽多定义一位
假设要设计深度为 8 的异步FIFO,此时定义读写指针只需要 3 位(2^3=8)就够用了,
但是我们在设计时将指针的位宽设计成 4 位,最高位的作用就是区分是读空还是写满。
具体判断规则如下:
- 当最高位相同,其余位相同认为是读空
- 当最高位不同,其余位相同认为是写满
格雷码判断空满
:指针的位宽多定义一位
在设计中判断是读指针是否等于写指针的时候,用的是读写指针的格雷码形式
具体判断规则如下:
-
当最高位和次高位相同,其余位相同认为是读空
-
当最高位和次高位不同,其余位相同认为是写满
因为格雷码是一种镜像码, 例如读指针指向 8,写指针指向 7 ,我们可以知道此时此刻并不是读空状态也不是写满状态;
(7对应的格雷码0100;8对应得格雷码1100) 但是如果按照二进制码得判断规则,最高位不同,其余位相同,就会误判出现写满信号;因此格雷码判断空满的条件和二进制判断空满的规则不相同。
3 RTL
一个典型的FIFO模块结构
如下所示:
下面是参考上图进行的实现(略有不同):
module async_fifo#(parameter DEPTH = 16,parameter WIDTH = 8
)(input wr_clk ,input wr_rstn ,input wr_en ,input [WIDTH-1:0] wr_data ,input rd_clk ,input rd_rstn ,input rd_en ,output reg [WIDTH-1:0] rd_data ,output rempty ,output wfull
);//------------------------------------------------------------------------------------
// internal vars
//------------------------------------------------------------------------------------reg [WIDTH-1:0] fifo_ram [DEPTH-1:0];reg [$clog2(DEPTH):0] wr_ptr;
reg [$clog2(DEPTH):0] rd_ptr;wire [$clog2(DEPTH):0] wr_ptr_g;
wire [$clog2(DEPTH):0] rd_ptr_g;
reg [$clog2(DEPTH):0] wr_ptr_g_d1;
reg [$clog2(DEPTH):0] rd_ptr_g_d1;
reg [$clog2(DEPTH):0] wr_ptr_g_d2;
reg [$clog2(DEPTH):0] rd_ptr_g_d2;wire [$clog2(DEPTH)-1:0] wr_addr;
wire [$clog2(DEPTH)-1:0] rd_addr;//------------------------------------------------------------------------------------
// wr_ptr && rd_ptr
//------------------------------------------------------------------------------------always@(posedge wr_clk or negedge wr_rstn)beginif(!wr_rstn)wr_ptr <= 0;else if(wr_en && !wfull)wr_ptr <= wr_ptr + 1'b1;
endalways@(posedge rd_clk or negedge rd_rstn)beginif(!rd_rstn)rd_ptr <= 0;else if(rd_en && !rempty)rd_ptr <= rd_ptr + 1'b1;
end//------------------------------------------------------------------------------------
// wfull && rempty
//------------------------------------------------------------------------------------
assign wr_ptr_g = (wr_ptr >> 1) ^ wr_ptr;
assign rd_ptr_g = (rd_ptr >> 1) ^ rd_ptr;always@(posedge rd_clk or negedge rd_rstn )begin//write_ptr_g sync to read domainif(!rd_rstn){wr_ptr_g_d2,wr_ptr_g_d1} <= 0;else {wr_ptr_g_d2,wr_ptr_g_d1} <= {wr_ptr_g_d1,wr_ptr_g};
endalways@(posedge wr_clk or negedge wr_rstn)begin//read_ptr_g sync to write domainif(!wr_rstn){rd_ptr_g_d2,rd_ptr_g_d1} <= 0;else {rd_ptr_g_d2,rd_ptr_g_d1} <= {rd_ptr_g_d1,rd_ptr_g};
endassign rempty = (rd_ptr_g == wr_ptr_g_d2);
assign wfull = wr_ptr_g == {~rd_ptr_g_d2[$clog2(DEPTH):$clog2(DEPTH)-1],rd_ptr_g_d2[$clog2(DEPTH)-2:0]};//------------------------------------------------------------------------------------
// write && read
//------------------------------------------------------------------------------------assign wr_addr = wr_ptr[$clog2(DEPTH)-1:0];
assign rd_addr = rd_ptr[$clog2(DEPTH)-1:0];always@(posedge wr_clk)if(wr_en && !wfull)fifo_ram[wr_addr] <= wr_data;always@(posedge rd_clk)if(rd_en && !rempty)rd_data <= fifo_ram[rd_addr];endmodule
4 Testbench
pattern只写不读、pattern只读不写、pattern写比读快
`timescale 1ns / 1psmodule tb_async_fifo();//---------------------------------------------------parameter WIDTH = 3;
parameter DEPTH = 32;reg wr_clk ;
reg wr_rstn ;
reg wr_en ;
reg [WIDTH-1:0] wr_data ;
reg rd_clk ;
reg rd_rstn ;
reg rd_en ;
wire [WIDTH-1:0] rd_data ;
wire empty ;
wire full ;//---------------------------------------------------initial beginwr_clk = 0;forever #10 wr_clk = ~wr_clk;//50MHz
end
initial beginrd_clk = 0;forever #12.5 rd_clk = ~rd_clk;//40MHz
endinitial beginrd_rstn = 0;wr_rstn = 0;#103 ;rd_rstn = 1;wr_rstn = 1;
end//pattern1
initial beginwr_en = 0;wr_data = 0;#203;wr_en = 1;repeat(32) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;
end//pattern2
initial beginrd_en = 0;#3000;rd_en = 1;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end//pattern3
initial beginrd_en = 0;wr_en = 0;#6000;wr_en = 1;rd_en = 1;repeat(300) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end
//---------------------------------------------------async_fifo#(.DEPTH(DEPTH),.WIDTH(WIDTH)
) u_async_fifo(.wr_clk (wr_clk ),.wr_rstn (wr_rstn ),.wr_en (wr_en ),.wr_data (wr_data ),.rd_clk (rd_clk ),.rd_rstn (rd_rstn ),.rd_en (rd_en ),.rd_data (rd_data ),.empty (empty ),.full (full )
);endmodule
pattern读比写快
`timescale 1ns / 1psmodule tb_async_fifo_w_slow();//---------------------------------------------------parameter WIDTH = 3;
parameter DEPTH = 32;reg wr_clk ;
reg wr_rstn ;
reg wr_en ;
reg [WIDTH-1:0] wr_data ;
reg rd_clk ;
reg rd_rstn ;
reg rd_en ;
wire [WIDTH-1:0] rd_data ;
wire empty ;
wire full ;//---------------------------------------------------initial beginwr_clk = 0;forever #10 wr_clk = ~wr_clk;//50MHz
endinitial beginrd_clk = 0;forever #8 rd_clk = ~rd_clk;//62.5MHz
endinitial beginrd_rstn = 0;wr_rstn = 0;#103 ;rd_rstn = 1;wr_rstn = 1;
end//pattern1
initial beginwr_en = 0;wr_data = 0;#203;wr_en = 1;repeat(32) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;
end//pattern2
initial beginrd_en = 0;#3000;rd_en = 1;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end//pattern3
initial beginrd_en = 0;wr_en = 0;#6000;wr_en = 1;rd_en = 1;repeat(300) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end
//---------------------------------------------------async_fifo#(.DEPTH(DEPTH),.WIDTH(WIDTH)
) u_async_fifo(.wr_clk (wr_clk ),.wr_rstn (wr_rstn ),.wr_en (wr_en ),.wr_data (wr_data ),.rd_clk (rd_clk ),.rd_rstn (rd_rstn ),.rd_en (rd_en ),.rd_data (rd_data ),.empty (empty ),.full (full )
);endmodule
pattern读写一样快
`timescale 1ns / 1psmodule tb_async_fifo_w_equal();//---------------------------------------------------parameter WIDTH = 3;
parameter DEPTH = 32;reg wr_clk ;
reg wr_rstn ;
reg wr_en ;
reg [WIDTH-1:0] wr_data ;
reg rd_clk ;
reg rd_rstn ;
reg rd_en ;
wire [WIDTH-1:0] rd_data ;
wire empty ;
wire full ;//---------------------------------------------------initial beginwr_clk = 0;forever #10 wr_clk = ~wr_clk;//50MHz
endinitial beginrd_clk = 0;#8;forever #10 rd_clk = ~rd_clk;//50MHz
endinitial beginrd_rstn = 0;wr_rstn = 0;#103 ;rd_rstn = 1;wr_rstn = 1;
end//pattern1
initial beginwr_en = 0;wr_data = 0;#203;wr_en = 1;repeat(32) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;
end//pattern2
initial beginrd_en = 0;#3000;rd_en = 1;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end//pattern3
initial beginrd_en = 0;wr_en = 0;#6000;wr_en = 1;rd_en = 1;repeat(300) @(posedge wr_clk)begin#1;wr_data = wr_data + 1;end#1;wr_en = 0;repeat(32) @(posedge rd_clk)beginend#1;rd_en = 0;
end
//---------------------------------------------------async_fifo#(.DEPTH(DEPTH),.WIDTH(WIDTH)
) u_async_fifo(.wr_clk (wr_clk ),.wr_rstn (wr_rstn ),.wr_en (wr_en ),.wr_data (wr_data ),.rd_clk (rd_clk ),.rd_rstn (rd_rstn ),.rd_en (rd_en ),.rd_data (rd_data ),.empty (empty ),.full (full )
);endmodule
参考博客:
https://zhuanlan.zhihu.com/p/551351794
https://blog.csdn.net/ZZ2588/article/details/122347438
https://blog.csdn.net/spx1164376416/article/details/121086907
https://zhuanlan.zhihu.com/p/344170711
https://wuzhikai.blog.csdn.net/article/details/121152844
https://blog.csdn.net/JustinLee2015/article/details/106527301/
https://blog.csdn.net/wuzhikaidetb/article/details/123570799
https://blog.csdn.net/weixin_44348260/article/details/119612998
相关文章:

异步FIFO设计
1 FIFO简介 FIFO的本质是RAM,具有先进先出的特性。 FIFO的基本使用原则:空时不能读,满时不能写 FIFO的两个重要参数:宽度和深度 FIFO的两种类型: 同步FIFO:读写时钟相同,通常用来做数据缓存…...
学习python和anaconda的经验
PYTHON 1 常用命令 1.1 1.1 注释 Python注释多行的方法有以下三种:使用ctrl+/实现多行注释、在每一行的开头使用shift+#键、输入’‘’ ‘’或者"“” “”",将要注释的代码插在中间 1.2 def init( ):函数 区分两个函数: 1.def init(self): 这种形式在__init_…...

【Linux】多线程【上】
文章目录 前言1、Linux线程概念1-1、什么是线程?1-1-1、如何看待页表1-1-2、回顾进程地址空间1-1-3、页表怎么进行虚拟地址到物理地址的映射的?1-1-4、Linux中线程的概念(重点)1-1-5、原生线程库1-1-6、代码测试1-1-7、知识点&…...

生成式人工智能在高等教育 IT 中的作用
作者:Jared Pane 通过将你大学的数据与公共 LLMs 和 Elasticsearch 安全集成来找到你需要的答案。 根据 2023 年 4 月 EDUCAUSE 的一项调查,83% 的受访者表示,生成式人工智能将在未来三到五年内深刻改变高等教育。 学术界很快就询问和想象生…...

黑龙江省DCMM认证、CSMM认证、CMMM认证、知识产权等政策奖励
2023年8月28日 为深入落实党的二十大精神,认真落实省第十三次党代会关于创新龙江建设的部署要求,全面贯彻新发展理念,融入和服务构建新发展格局,实施创新驱动发展战略,着力建设创新龙江,不断塑造振兴发展新…...

腾讯云2023年云服务器优惠活动价格表
腾讯云经常推出各种云产品优惠活动,为了帮助大家更好地了解腾讯云服务器的价格和优惠政策,下面给大家分享腾讯云最新云服务器优惠活动价格表,助力大家轻松上云! 一、轻量应用服务器优惠活动价格表 1、轻量应用服务器:…...

Sleuth--链路追踪
1 链路追踪介绍 在大型系统的微服务化构建中,一个系统被拆分成了许多模块。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上&…...

MyBatis初级
文章目录 一、mybatis1、概念2、JDBC缺点2.1、之前jdbc操作2.2 、原始jdbc操作的分析 3、mybatis的使用3.1、导入maven依赖3.2、新建表3.3、实体类3.4、编写mybatis的配置文件3.5、编写接口 和 映射文件3.6、编写测试类3.7、注意事项 4、代理方式开发5、mybatis和spring整合5.1…...
Spring 学习(二)AOP
一、什么是AOP Aspect Oriented Programming,即面向切面编程。对一个大型项目的代码而言,整个系统要求关注安全检查、日志、事务等功能,这些功能实际上“横跨”多个业务方法。在一般的OOP编程里,需要在每一个业务方法内添加相关非…...

笔记1.1 计算机网络基本概念
计算机网络是通信技术与计算机技术紧密结合的产物 通信系统模型: 计算机网络是一种通信网络 计算机网络是互连的、自洽的计算机集合。 互连:互联互通 自洽:无主从关系 通过交换网络互连主机 Internet:数以百万计的互连的计算设…...

液压切管机配套用液压泵站比例阀放大器
液压切管机配套用液压泵站是液压系统的动力源,可按机械设备工况需要提供一定压力、流量和清洁度的工作介质。它由泵组、油箱组件、控温组件、滤油器组件及蓄能器组件等组合而成,液压泵站主要服务于大型管道工程。...
C++ Primer Plus 第七章笔记
目录 函数基本知识 没有返回值的函数:void函数 有返回值的函数: 函数原型 1.为什么需要函数原型? 2.函数原型的语法 3.函数原型的功能 按值传递函数参数 形参和实参 局部变量 参数问题 使用const指针参数 调用自身的函数…...

常用数据库的 API - 开篇
API API 这个词在大多数人看来可能和 CNS 差不多,前者天天听说就是用不上,后者天天读就是发不了。 不过,通过今天的一个简短介绍,今后 API 这个东西你就用上了,因为在文章最后我将会展示一个最最基础且高频的 API 使…...

C++之生成详细汇编代码(二百一十六)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...

AIGC|当一个程序员学会用AI来辅助编程实践
一、辅助编程 作为主要以 JAVA 语言为核心的后端开发者,其实,早些时间我也用过比如 Codota、Tabnine、Github 的 Copilot、阿里的 AI Coding Assistant 等 IDEA 插件,但是我并没有觉得很惊奇,感觉就是生成一些代码片段罢了&#x…...

9.14号作业
仿照vector手动实现自己的myVector,最主要实现二倍扩容功能 有些功能,不会 #include <iostream>using namespace std; //创建vector类 class Vector { private:int *data;int size;int capacity; public://无参构造Vector(){}//拷贝构造Vector(c…...
【面试题】C/C++ 中指针和引用的区别
指针是一个独立的对象,它可以指向不同的变量或对象,可以重新赋值给其他变量。而引用是已存在的变量的别名,它必须在定义时初始化,并且不能重新绑定到另一个变量。指针可以是空指针(nullptr),它不…...

spring boot 整合多数据源
多数据源产生的场景 一般情况下,不会有多数据源这样的场景出现,但老项目或者特殊需求的项目,可能会有这样的场景 同一个应用需要访问两个数据库不用数据库中间件的读写分离 注入数据源选择的时机 声明两个数据源实例,在getConnect…...

数据集成:数据挖掘的准备工作之一
⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ 🐴作者:秋无之地 🐴简介:CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作,主要擅长领域有:爬虫、后端、大数据…...

xml配置文件密码特殊字符处理
错误姿势: 正确姿势:采取转义符的方式 常用转义符:...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...