Verilog功能模块——异步FIFO

news2024/10/5 2:09:17
asyncFIFO

前言

FIFO的功能

FIFO在FPGA中应用很多,它主要有以下功能:

  1. 数据缓存,很多时候数据发送速度和数据接收速度并不实时匹配,而在其中插入一个FIFO,来临时存储数据,就能平衡发送和接收速度
  2. 组合与分解数据,FIFO的写入数据位宽和读出数据位宽可以不一致,例如可以16bit写入,8bit读出或者反过来,这就为组合与分解数据提供了方便
  3. 跨时钟域传输数据,这是异步FIFO才有的功能,异步FIFO的读写时钟可以完全独立,所以可以借助异步FIFO来实现跨时钟域传输数据
  4. 标准化接口,因为FIFO的写入与读出接口的控制时序非常简单,所以在模块需要外部数据时可以定义一个FIFO接口,这样数据接口的时序就不言自明了

为什么需要自编FIFO

我很喜欢在自编模块中使用FIFO接口,这样在使用此模块时就不必担心数据输入的时序问题,直接从FIFO中读数据即可。但这也带来了一些问题,如:

  1. 实例化模块时还需要额外实例化FIFO IP核,总是不那么方便,降低了自编模块的通用性,这是最大的问题
  2. 通常我只是需要FIFO这种接口,对于存储深度基本没要求,16的深度已经足够,但一些国产FPGA开发软件中的FIFO IP核最小深度就是512,这无疑造成了存储空间的浪费
  3. 我总是使用FWFT类型的FIFO,所以在实例化FIFO IP核时还必须选择FWFT类型,这容易出错,也造成了自编模块使用的不方便;而且一些国产开发软件还不提供FWFT类型的FIFO,这使得还得额外加一个标准FIFO转FWFT FIFO的模块,这就更不方便了

综上,我觉得有必要使用纯Verilog来实现FWFT FIFO,这样就不需要额外的FIFO IP核了,模块通用性大大提升。

FIFO功能模块分两篇文章,本篇为异步FIFO,另一篇为同步FIFO,传送门:

Verilog功能模块——同步FIFO。

同步FIFO实现起来是异步FIFO的简化版,基本看懂了异步FIFO,同步FIFO自然就懂了。


一. 异步FIFO实现原理

异步FIFO的实现原理有很多文章已经讲过了,这里就不详细讲了,只是简单总结一下,感兴趣的同学可以参考以下文章。

FIFO设计-异步FIFO篇 - 知乎 (zhihu.com)

IC基础(一):异步FIFO原理与代码实现 - 你好24h - 博客园 (cnblogs.com)

ICer必备-异步FIFO设计原理 & RTL模型 - 知乎 (zhihu.com)

1.1 实现原理框图

v2-3d004b628e50ab557ce9c71e4476e460_r

异步FIFO的实现框图就是上面这张,可以看到分为了五个部分:

  1. FIFO Memory,也就是一个双口RAM,或者是寄存器组
  2. FIFO wptr & full,FIFO写指针与满信号
  3. FIFO rptr & empty,FIFO读指针与空信号
  4. sync_r2w,读指针同步到写时钟域
  5. sync_w2r,写指针同步到读时钟域

FIFO空的判断依据:读指针 == 写指针,这意味着写入的数据全部读出,则FIFO为空。

FIFO满的判断依据:写指针 - 读指针 = FIFO深度,

注意:

  1. 读写指针都是循环计数的,即从0计数到FIFO深度-1,然后再回到0
  2. 写指针永远领先于读指针,因为FIFO必须是先写后读,所以当读指针最高位为1,而写指针最高位为0时,只是说明写指针最高位从1再次计数到了0
  3. 设计时会让读写指针的位宽 = FIFO深度对应的位宽 + 1,低位用于确定读写RAM的地址,最高位用于判断写指针是否已经领先读指针一圈了,此时只需要判断写指针最高位不等于读指针,而其余地位相等即可得出写指针 - 读指针 = FIFO深度即FIFO满的结论。

异步FIFO中,读写指针的时间域不同,但空满信号的判断需要比较读写指针的值,这样就需要时间域的切换:

  1. 读指针同步到写时钟域,用于判断FIFO满
  2. 写指针同步到读时钟域,用于判断FIFO空

设计中采用的时间域同步方法是,将读写指针由二进制编码转换为格雷码,再经过两级D触发器,即可同步到对应时间域。

1.2 为什么需要转换为格雷码?

我们考虑一种情况,写指针 = 5’b01111,同步到了读时钟域,这时又写入了一个数据,写指针 = 5‘b10000,我们看到指针的每一位都发生了变化,这时经过两级D触发器,在读时钟域能正确的得到5‘b10000吗? 答案是:很难。因为读写时钟是独立的,写指针在写时钟上升沿变化,对于读时钟来说,这个变化的时刻是随机的,而D触发器要能正确的输出数据,需要输入满足建立时间和保持时间的要求,否则在读时钟域得到的数据就可能是不定态X,这个就是亚稳态。

亚稳态因为D触发器的输入不满足建立时间和保持时间要求而发生,但读写时钟的独立使得我们无法从设计上满足时序要求,所以亚稳态是无法从理论上避免的。而上述的二进制编码的极端情况,需要所有bit位变化,每一位都有发生亚稳态的可能,而只要有一位发生亚稳态,组合得到的多bit数据就是错误的,所以,这种多bit信号的跨时钟域有很大的亚稳态风险。

何为格雷码?

格雷码,Gray code,又称循环码或反射码,在格雷码中,相邻的两个数仅有一位二进制位不同,使得在数字之间的转换只需要进行一次位运算,避免了普通二进制码转换时可能需要多次位运算的情况。

以下表格展示了3为二进制数据的正常编码与格雷码:

二进制码格雷码
000000
001001
010011
011010
100110
101111
110101
111100

亚稳态只在数据变化时发生,不变化也就不存在亚稳态,而相邻的格雷码只会有一位不同,所以使用格雷码就将多bit信号的跨时钟域转换成了单bit信号的跨时钟域,极大的降低了亚稳态发生的概率。理论上,两级D触发器,发生亚稳态的概率就非常低了,能满足实际使用需求。

上面说到,判断写满的逻辑为二进制码写指针最高位不等于二进制码读指针,其余位相等,如写指针000,读指针100 或者写指针001,读指针101;可以发现转换为格雷码后,此逻辑变为:格雷码写指针最高位和次高位不等于格雷码读指针,其余位相等。

1.3 格雷码与二进制码的如何互转?

二进制转格雷码很简单,格雷码 = (二进制码 >> 1)^ 二进制码,即右移一位再异或:

//bin to gray
assign gray = (bin >> 1) ^ bin;

格雷码转二进制稍显复杂:

// Verilog写法 gray to bin
reg [WIDTH-1 : 0] bin;
always @(*) begin : gray2bin
  integer i;
  for (i = 0; i <= WIDTH-1; i = i+1) begin
    bin[i] = ^(gray >> i);
  end
end

// SystemVerilog写法 gray to bin
logic [WIDTH-1 : 0] bin;
always_ff @(*) begin
  for (int i = 0; i <= WIDTH-1; i = i+1) begin
    bin[i] = ^(gray >> i);
  end
end

二. 模块功能框图与信号说明

asyncFIFO

信号说明:

分类信号名称输入/输出说明
参数DATA_WIDTH数据位宽
ADDR_WIDTH地址位宽,FIFO深度=2**ADDR_WIDTH
FWFT_ENFirst word fall-through输出模式使能,高电平有效
FIFO写端口dininputFIFO数据输入
fulloutputFIFO满信号
wr_eninputFIFO写使能
wr_clkinputFIFO写时钟
wr_rstinputFIFO写复位
almost_fulloutputFIFO快满信号,FIFO剩余容量<=1时置高
FIFO读端口doutoutputFIFO数据输出
emptyoutputFIFO空信号
rd_eninputFIFO读使能
rd_clkinputFIFO读时钟
rd_rstinputFIFO读复位
almost_emptyoutputFIFO快空信号,FIFO内数据量<=1时置高

注意:

  1. 信号的命名与Vivado中的FIFO IP核完全一致
  2. 复位均为高电平复位,与Vivado中的FIFO IP核保持一致
  3. 复位为异步复位,写复位和读复位可以公用一个信号,也可以分开
  4. FIFO深度通过ADDR_WIDTH来设置,所以FIFO的深度必然是2的指数,如2、4、8、16等

三. 部分代码展示

//++ 生成读写指针 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg  [ADDR_WIDTH:0] rptr_bin;
always @(posedge rd_clk or posedge rd_rst) begin
  if (rd_rst)
    rptr_bin <= 0;
  else if (rd_en & ~empty)
    rptr_bin <= rptr_bin + 1'b1;
end


reg  [ADDR_WIDTH:0] wptr_bin;
always @(posedge wr_clk or posedge wr_rst) begin
  if (wr_rst)
    wptr_bin <= 0;
  else if (wr_en & ~full)
    wptr_bin <= wptr_bin + 1'b1;
end


wire [ADDR_WIDTH-1:0] raddr = rptr_bin[ADDR_WIDTH-1:0];
wire [ADDR_WIDTH-1:0] waddr = wptr_bin[ADDR_WIDTH-1:0];
//-- 生成读写指针 ------------------------------------------------------------


//++ 二进制编码转换为格雷码 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wire [ADDR_WIDTH:0] rptr_gray = (rptr_bin >> 1) ^ rptr_bin;
wire [ADDR_WIDTH:0] rptr_gray_p1 = ((rptr_bin + 1'b1) >> 1) ^ (rptr_bin + 1'b1);


wire [ADDR_WIDTH:0] wptr_gray = (wptr_bin >> 1) ^ wptr_bin;
wire [ADDR_WIDTH:0] wptr_gray_p1 = ((wptr_bin + 1'b1) >> 1) ^ (wptr_bin + 1'b1);
//-- 二进制编码转换为格雷码 ------------------------------------------------------------


//++ 格雷码的读写指针同步 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg [ADDR_WIDTH:0] rptr_gray_wr_clk_r1;
reg [ADDR_WIDTH:0] rptr_gray_wr_clk_r2;
always @(posedge wr_clk or posedge wr_rst) begin
  if (wr_rst) begin
    rptr_gray_wr_clk_r1 <= 0;
    rptr_gray_wr_clk_r2 <= 0;
  end
  else begin
    rptr_gray_wr_clk_r1 <= rptr_gray;
    rptr_gray_wr_clk_r2 <= rptr_gray_wr_clk_r1;
  end
end


reg [ADDR_WIDTH:0] wptr_gray_rd_clk_r1;
reg [ADDR_WIDTH:0] wptr_gray_rd_clk_r2;
always @(posedge rd_clk or posedge rd_rst) begin
  if (rd_rst) begin
    wptr_gray_rd_clk_r1 <= 0;
    wptr_gray_rd_clk_r2 <= 0;
  end
  else begin
    wptr_gray_rd_clk_r1 <= wptr_gray;
    wptr_gray_rd_clk_r2 <= wptr_gray_rd_clk_r1;
  end
end
//-- 格雷码的读写指针同步 ------------------------------------------------------------

3.1 参考与更改

代码参考了Github上的分享,源链接:dpretet/async_fifo: A dual clock asynchronous FIFO written in verilog, tested with Icarus Verilog (github.com)

基本结构是完全一样的,主要做了以下更改:

  1. 更改了部分信号名,使其更符合FIFO的通常命名,并保持与Vivado FIFO IP核名称一致,例如将winc改为wr_en,wdata改为din

  2. 删除了一些不必要的中间信号,使代码逻辑更加简洁

  3. 将原本5个v文件写成了一个v文件,方便在工程中调用

3.2 “假满”与“假空”

“假满”问题:因为满信号是将读指针同步到写时钟域再与写指针比较产生的,同步会有两个写时钟的延迟,这意味着满信号在产生时,读指针是两个写时钟以前的值,如果同步的过程中又进行了读操作,则在满信号置高时,又读出了若干个值,所以此时FIFO并非真正的满,称为“假满”。假满时不响应外部写入,经过两个写时钟后,新的读指针同步到了写时钟域,full信号会拉低,脱离假满状态,此时如果不读出而是继续写入数据,则full信号会置高,这时的满就是“真满”。

所以“假满”并没有降低FIFO的深度,因为两个写时钟后会拉低full,脱离假满状态,此时可以继续写入,它只是让满信号不那么实时,但以这个full信号作为满信号不会产生任何功能问题。

同理,“假空”是因为空信号是将写指针同步到读时钟域再与读指针比较产生的,同步会有两个读时钟的延迟,如果再这两个读时钟内有若干次写入,则此时产生的empty信号并非真正的空,称为“假空”。再两个读时钟后empty会自动拉低,脱离假空状态。同样假空只是让空信号不那么实时,不会产生任何功能问题。

综上,实际使用时,“假满”和"假空"对FIFO功能没有影响

3.3 之后的功能改进

本FIFO模块未实现位宽变换的功能,即写入数据位宽始终等于读出数据位宽,后续可在此模块基础上改进。


三. 功能仿真

可在两种条件下测试,写时钟频率>读时钟频率;写时钟频率 < 读时钟频率

两种条件下分别比较以下情形中的fifo行为是否与FIFO IP核一致,

情形一:单次写单次读

情形二:写满后再读空

情形三:在读的过程中写,在写的过程中读

判断模块功能正常的依据:

  1. 写入数据是否按顺序正常读出
  2. 空信号和满信号是否正常输出。

为方便比较,编写了顶层文件,实例化了FIFO IP核与自编模块,部分代码如下:

vivado_async_fifo vivado_async_fifo_u0 (
  .wr_clk       (wr_clk                 ), // input wire wr_clk
  .wr_rst       (wr_rst                 ), // input wire wr_rst
  .rd_clk       (rd_clk                 ), // input wire rd_clk
  .rd_rst       (rd_rst                 ), // input wire rd_rst
  .din          (din                    ), // input wire [7 : 0] din
  .wr_en        (wr_en                  ), // input wire wr_en
  .rd_en        (rd_en                  ), // input wire rd_en
  .dout         (vivado_fifo_dout       ), // output wire [7: 0] dout
  .full         (vivado_fifo_full       ), // output wire full
  .almost_full  (vivado_fifo_almost_full), // output wire almost_full
  .empty        (vivado_fifo_empty      ), // output wire empty
  .almost_empty (vivado_fifo_almost_empty)  // output wire almost_empty
);


asyncFIFO #(
  .DATA_WIDTH(DATA_WIDTH),
  .ADDR_WIDTH(ADDR_WIDTH),
  .FWFT_EN(FWFT_EN)
) asyncFIFO_inst (
  .din          (din         ),
  .wr_en        (wr_en       ),
  .full         (full        ),
  .almost_full  (almost_full ),
  .wr_clk       (wr_clk      ),
  .wr_rst       (wr_rst      ),
  .dout         (dout        ),
  .rd_en        (rd_en       ),
  .empty        (empty       ),
  .almost_empty (almost_empty),
  .rd_clk       (rd_clk      ),
  .rd_rst       (rd_rst      )
);

testbench部分代码如下:

// 生成时钟
localparam WCLKT = 2;
initial begin
  wr_clk = 0;
  forever #(WCLKT / 2) wr_clk = ~wr_clk;
end

localparam RCLKT = 6;
initial begin
  rd_clk = 0;
  forever #(RCLKT / 2) rd_clk = ~rd_clk;
end


// 复位块
initial begin
  wr_rst = 1;
  #(WCLKT * 2)
  wr_rst = 0;
end


// 读写使能控制
initial begin
  wr_en = 0;
  rd_en = 0;
  #(WCLKT * 2)
  wait(~full && ~vivado_fifo_full); // 两个FIFO都从复位态恢复时开始写

  // 写入一个数据
  wr_en = 1;
  #(WCLKT * 1)
  wr_en = 0;

  // 读出一个数据
  wait(~empty && ~vivado_fifo_empty);// 两个FIFO都非空时开始读,比较读数据和empty信号是否有差异
  rd_en = 1;
  #(RCLKT * 2)
  rd_en = 0;

  // 写满
  wr_en = 1;
  wait(full && vivado_fifo_full); // 两个FIFO都满时停止写,如果两者不同时满,则先满的一方会有写满的情况发生,但对功能无影响
  // vivado FIFO IP在FWFT模式时, 设定深度16时实际深度为17, 但仿真显示full会在写入15个数据后置高, 过几个时钟后后拉低,
  // 再写入一个数据, full又置高; 然后过几个时钟又拉低, 再写入一个数据置高, 如此才能写入17个数据
  // 所以这里多等待12个wclk周期, 就是为了能真正写满vivado FWFT FIFO
  #(WCLKT * 12)
  wr_en = 0;

  // 读空
  wait(~empty && ~vivado_fifo_empty);
  rd_en = 1;
  wait(empty && vivado_fifo_empty); // 两个FIFO都空时停止读,如果两者不同时空,则先空的一方会有读空的情况发生,但对功能无影响
  rd_en = 0;

  #(RCLKT * 10)
  $stop;
end


// 使用以下代码时,先注释掉上面的读写使能控制initial
// 同时读写
// assign wr_en = ~full || ~vivado_fifo_full; // 未满就一直写
// assign rd_en = ~empty || ~vivado_fifo_empty; // 未空就一直读

always @(posedge wr_clk) begin
  if (wr_rst)
    din <= 0;
  else if (wr_en && ~full && ~vivado_fifo_full)
    din <= din + 1;
end

assign rd_rst = wr_rst;

endmodule

8bit,16深度,FWFT FIFO仿真,波形如下:

可以看到模块输出的自编fifo与vivado fwft fifo的写端口和读端口行为是一致的,只是可能会超前或滞后一定的clk周期。

可以看到empty拉低时,数据已经有效了,所以自编模块实现了FWFT功能,Vivado FIFO的实际深度为17,所以它多读出了一个数据,空信号更晚拉高。

因篇幅问题,其它条件下的仿真不再展示,感兴趣的同学可通过更改testbench自行验证。

  1. 更改读写时钟的频率,上述仿真是读频率大于写
  2. FWFT_EN改为0,注意同步修改Vivado FIFO的配置

四. 工程分享

Verilog功能模块——异步FIFO,Vivado 2021.2工程。

欢迎大家关注我的公众号:徐晓康的博客,回复以下四位数字获取。

8301

建议复制过去不会码错字!

或者在我的码云仓库获取,传送门:

徐晓康/Verilog功能模块 - 码云 - 开源中国 (gitee.com)


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1083809.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是系统架构师?什么是系统架构?

1. 什么是系统架构师&#xff1f; 系统架构设计师(System Architecture Designer&#xff09;是项目开发活动中的关键角色之一。系统架构是系统的一种整体的高层次的结构表示&#xff0c;是系统的骨架和根基&#xff0c;其决定了系统的健壮性和生命周期的长短。 系统架构设计…

允许访问:掌握权限的艺术

回到20世纪&#xff0c;网络攻击更难实施&#xff0c;因为大多数计算机没有联网&#xff0c;互联网并不普及&#xff0c;只有少数人群可以访问计算机&#xff0c;更重要的是&#xff0c;没有足够的动机来进行攻击。 访问控制政策 而今天&#xff0c;情况完全不同。在糟糕的数据…

【特纳斯电子】血氧饱和度监测仪设计-实物设计

视频及资料链接&#xff1a;血氧饱和度监测仪设计-实物设计 - 电子校园网 编号&#xff1a; T5662203M-SW 设计简介&#xff1a; 本设计是基于STM32的血氧饱和度监测仪系统&#xff0c;主要实现以下功能&#xff1a; 1. STM32单片机作为微处理器 2. MAX30102进行心率血氧检…

A股风格因子看板 (2023.10 第03期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格暴露等。 今日为该因子跟踪第03期&#xff0c;指数组合数据截止日2023-09-30&#xff0c;要点如下 近1年A股风格因子检验统…

【环境】ubuntu下anaconda虚拟环境中安装的pytorch终于配置成功了!

前请提要 配置情况&#xff1a;双系统——win10ubuntu20.04&#xff1b;anaconda安装与虚拟环境创建&#xff1a;在安装conda的过程中&#xff0c;我修改了安装路径&#xff08;很清楚的记得&#xff09;&#xff1b;昨晚的状态&#xff1a;通过pytorch中的conda命令&#xff…

Linux磁盘常见知识

目录 一、基础概念 1.1 文件系统类型 1.2 主分区、扩展分区、逻辑分区三者关系 1.3 UUID 1.4 lvm逻辑卷管理系统 二. 常用命令 2.1 查看命令 2.2 分区命令 2.3 格式化命令 1.4 挂载命令 三、扩容根目录 一、基础概念 1.1 文件系统类型 文件系统类型决定了向分区中存放、读取数…

再一次整理一下spring框架步骤

1.pom.xml依赖 2.applicationbean.xml 3.类 小树叶可以跟bean联动起来 不写接口直接写类 实现类 4.测试 两种方法的实现

第二证券:锂矿板块发力走高,江特电机涨停,中矿资源等拉升

锂矿板块12日盘中发力走高&#xff0c;到发稿&#xff0c;江特电机涨停&#xff0c;中矿资源、融捷股份涨超4%&#xff0c;天齐锂业、天华新能、永兴资料、赣锋锂业等涨逾3%。 音讯面上&#xff0c;江特电机“探转采”作业取得开展。 10月9日&#xff0c;国家天然资源部官网闪…

2023年中国鸡蛋市场供需现状、市场规模及产品价格走势分析[图]

鸡蛋具有高营养、易消化、用途广等特点&#xff0c;已成为世界公认的必备优质食材&#xff0c;它同肉品、乳品、蔬菜、粮食一样&#xff0c;是人们日常生活中的重要营养食品。鸡蛋的主要品种包括谷物蛋、绿色蛋、素养蛋、OMG营养蛋等。 蛋鸡是指专门产蛋以供应人们食用和加工蛋…

获央视报道 联通智网科技全面引领车联网发展

当前&#xff0c;我国正由交通大国阔步迈向交通强国&#xff0c;涌现出了一批交通行业的专精特新企业&#xff0c;带动了我国交通行业的蓬勃发展。联通智网科股份有限公司作为中国联通在车联网领域的专精特新企业&#xff0c;一直坚持融合创新并积极探索车联网和智慧交通相关领…

Unity基础课程之物理引擎8-扔保龄球游戏案例(完)

三个脚本&#xff1a; 1.给求添加力 2.分数管理器 3.检测是否发生碰撞 ----------------------------------------------- 脚本源码 1.给求添加力 using System.Collections; using System.Collections.Generic; using UnityEngine;public class RoleControl : MonoBeha…

通讯网关软件022——利用CommGate X2MQTT实现MQTT访问MSSQL服务器

本文介绍利用CommGate X2MQTT实现MQTT访问MS SQL数据库。CommGate X2MQTT是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现上位机通过MQTT来获取MS SQL数据库的数据。 【解决方案】设置网…

EDUSRC-记某擎未授权与sql注入

目录 360天擎 - 未授权与sql注入 信息收集 FOFA语法 鹰图搜索 360天擎未授权访问 - 数据库信息泄露 漏洞复现 修复方案 360天擎终端安全管理系统ccid处SQL注入 漏洞复现 手动测试方法 修复方案 360天擎 - 未授权与sql注入 通常访问的页面如下&#xff0c;存在登录框…

Vue2 router详解

vue2 router详解 一、前端路由的概念与原理1. 什么是路由2. SPA与前端路由3. 什么是前端路由4. 前端路由的工作方式5. 实现简易的前端路由 二、vue-router的基本用法1. 什么是 vue-router2. vue-router 安装和配置的步骤2.2 创建路由模块2.3 导入并挂载路由模块2.4 声明路由链接…

怎么把人声和BGM分开?只想要人声部分~

当我们在听一段人声和背景音混合在一起的视频时&#xff0c;会因为周围环境比较嘈杂就很难听清音频了&#xff0c;这时&#xff0c;我们可以利用音分轨人声分离软件来搞定&#xff0c;快速提取人声部分&#xff01; 第一步&#xff1a;打开【音分轨】APP&#xff0c;进入首页点…

Remix 开发小技巧

文章目录 请求搜索参数重定向进行服务器端分页从 URL 读取查询参数不要覆盖其他查询参数使用提交按钮而不是链接向按钮添加标签 Loaders & Actions 中中止异步调用全局类型及类型安全 请求搜索参数重定向 假设您要确保始终设置特定的搜索参数。为此&#xff0c;您可以首先…

MyBatis基础之结果集映射

基本概念 [!danger] 注意 配置结果集映射&#xff0c;只用看 SQL 执行结果&#xff0c;不看 SQL 语句&#xff01; 在前面的内容中&#xff0c;由于我们的 PO 类的属性名与数据库中表的列名是一致的&#xff0c;因此&#xff0c;在 Mapper.xml 配置文件中&#xff0c;Mybatis 省…

对paddleOCR中的字符识别模型转ONNX

对paddle OCR中的模型转换成ONNX。 转换代码&#xff1a; import os import sys import yaml import numpy as np import cv2 import argparse import paddle from paddle import nnfrom argparse import ArgumentParser, RawDescriptionHelpFormatter import paddle.distribu…

没有执行力,一切都是空谈!如何提高执行力

决定人生高度的并非空谈&#xff0c;而是实干&#xff0c;没有执行力一切都是零。 执行力对于达成目标至关重要。即使将目标细分拆解得再细致&#xff0c;若无法切实执行&#xff0c;一切仍然是徒劳。 一旦制定目标&#xff0c;必须进行层层细分的拆解&#xff0c;包括每日的…

信钰证券:汇金增持提振市场情绪 保险、银行等板块集体拉升

12日&#xff0c;两市股指盘中全线走高&#xff0c;沪指一度克复3100点&#xff0c;上证50指数涨超1%。 稳妥、银行、券商板块团体拉升&#xff0c;到发稿&#xff0c;银行板块方面&#xff0c;瑞丰银行涨约6%&#xff0c;盘中一度涨停&#xff1b;紫金银行、渝农银行、西安银…