目录
这篇更多的是记录FIFO学习,参考了众多优秀的文章,我都会标记出来,首先是关于同步FIFO的参考搞懂FIFO书写的关键是什么,异步FIFO参加这篇异步FIFO讲解
FIFO 的英文全称是 First In First Out,即先进先出。 FPGA 使用的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。
为了增强对FIFO的认识,用Verilog描述一个FIFO。
FIFO是基于双口RAM衍生出来的,同步FIFO和异步FIFO的区别是主要是双口RAM的两套独立端口是否使用同一时钟,同步FIFO读写受同一时钟控制,其中设计的同步FIFO的信号如下所示。
FIFO大致分为写端和读端,两者都拥有独自的使能信号,以及写数据,这里数据宽度是8Bit,输出端对于FIFO的空满有相应的标志位,以及计数器和读出的数据。

FIFO读写的规则:
FIFO在外部端口上表现没有地址线,因为是第一个到达的数据也将是第一个输出的数据,所以 FIFO缓冲区是一种读/写存储阵列,可自动跟踪数据进入模块的顺序并以相同顺序读出数据。而内部的实现需要两个指针来分别表示读和写的地址,读指针和写指针。初始时读写指针指向第一个存储单元,此时FIFO队列为空,fifo_empty有效,当写入256个数据,也就是写指针重新指向第一个FIFO的存储单元,而计数data_count等于FIFO的深度(0~255,但是写指针总是指向在下一个写的存储单元,所以当写完地址为255,8’b1111 1111后,变为9位的256 = 9‘b1 0000 0000)时,fifo_full有效。由此可知读写指针相同,此时有可能是满状态(写一直有效,写完最后一个255地址后,取低8位= 8’b0000 0000,此时读写指针的地址相同),所以用地址差不能用于判断空满,而是使用计数器来判断,但是计数器要设置比深度的位数多一位 ,这里也就是9位。
上面提到的博文中的这幅图,结合上一段的解释可以帮助理解地址的变化:

其实更加规范的模式是想其中的位宽设置为参数,接下来的代码书写使用参数声明宽度。
//FIFO两条铁律就是不能满写 不能空读
module custom_FIFO(
input wire clk,
input wire rst_n,
input wire wr_en,
input wire [7:0] wr_data,
input wire rd_en,
output wire fifo_full, //空和满都是1有效
output reg [7:0] rd_data,
output wire fifo_empty,
output reg [8:0] data_count
);
reg [7:0] mem [255:0];
reg [7:0] wr_add;
reg [7:0] rd_add;
//FIFO的书写方式应该是分信号进行书写
//wr_add总是指向下一个写的存储单元,在上升沿监测到写地址为255时,实际上的地址值重新因为溢出指向了地址0,此时溢出信号为真,而计数值达到256
//这里的逻辑是读写地址会自动从255变为0
always@(posedge clk)begin
if(rst_n == 1'b0)begin
wr_add <= 8'b0;
end
else if(wr_en == 1'b1 && fifo_full== 1'b0) begin
wr_add <= wr_add + 1'b1;
end
end
// rd_add
always@(posedge clk)begin
if(rst_n == 1'b0)begin
rd_add <= 8'b0;
end
else if(rd_en == 1'b1 && fifo_empty == 1'b0) begin
rd_add <= rd_add + 1'b1;
end
end
//rd_data
always@(posedge clk)begin
if(rst_n == 1'b0)begin
rd_data <= 8'h00;
end
else if(rd_en == 1'b1 && fifo_empty == 1'b0)begin
rd_data <= mem[rd_add];
end
end
//wr_data
always@(posedge clk)begin
if(wr_en == 1'b1 && fifo_full== 1'b0)begin
mem[wr_add] <= wr_data;
end
end
always@(posedge clk)begin
if(rst_n == 1'b0)begin
data_count <= 9'h0;
end
else begin
if(wr_en == 1'b1 && !rd_en && fifo_full== 1'b0)begin //当读写都有效,则不变
data_count <= data_count + 1'b1;
end
else if(rd_en == 1'b1 && wr_en && fifo_empty == 1'b0)begin
data_count <= data_count - 1'b1;
end
end
end
assign fifo_full = (data_count == 9'h100); //数据计数的范围是1-256,当为256时数据满
assign fifo_empty = (data_count == 9'h00);
endmodule
读取的TXT中有259个数据,可以看到第256个数据为8‘h25,仿真文件中将所有的259个数据全部读取到mem中,然后将读到的数据写256到FIFO中。

module tb_custom_FIFO();
reg clk;
reg rst_n;
reg wr_en;
reg [7:0] wr_data;
reg rd_en;
wire fifo_full; //空和满都是1有效
wire [7:0] rd_data;
wire fifo_empty;
wire [8:0] data_count;
reg [7:0] mem [258:0];
always begin
#5 clk = ~clk;
end
initial begin
clk = 0;
rst_n = 1'b1;
#10;
rst_n = 1'b0;
#10 rst_n = 1'b1;
end
initial begin
$readmemh("D:/vivado_pf/Basic_pratice/uart_data.txt",mem);
$display("the file is load to mem");
$display("Read memory1: %h", mem[0]) ;
$display("Read memory2: %h", mem[1]) ;
$display("Read memory3: %h", mem[2]) ;
$display("Read memory4: %h", mem[3]) ;
end
initial begin
wr_en <= 1'b0;
rd_en <= 1'b0;
wr_data <= 8'h00;
#50;
wr_data_to_fifo();
#2620;
rd_en <= 1'b1;
end
//写256个数据到FIFO中
task wr_data_to_fifo(); //任务没有返回值
integer i;
begin
for (i = 0; i < 257; i = i + 1)begin
wr_data = mem[i];
wr_en = 1;
#10; //任务不能出现always语句,但是可以包含时序控制,任务可以没有或者多个输入、输出
end
wr_en = 0;
end
endtask
custom_FIFO custom_FIFO_inst(
.clk(clk),
.rst_n(rst_n),
.wr_en(wr_en),
.wr_data(wr_data),
.rd_en(rd_en),
.fifo_full(fifo_full),
.rd_data(rd_data),
.fifo_empty(fifo_empty),
.data_count(data_count)
);
endmodule
写数据在上升沿前后要有效一段时间,首先是正常工作的仿真,在设置的时候就没有溢出的情况,第一张图是开始写入,第二张图是写入结束。


修改仿真文件,第一张表示溢出写,可以看到,溢出写无效。第二章可以看到读空出两个单元后,写入读写地址相同也就停止了写。到这里同步的FIFO功能就是正确的。


该系统函数可用于计算寻址给定大小的存储器所需的最小地址宽度或表示给定数量的状态所需的最小向量宽度。
//位宽计算函数
function integer clog2 (input integer depth);
begin
for (clog2=0; depth>0; clog2=clog2+1)
depth = depth >>1;
end
endfunction
//$clog2(8) == 3;
使用方法
parameter p_cnt_max = p_rev_time*p_clk_fre*1000_000 - 1; //翻转时间内所需计数的最大值
wire [$clog2(p_cnt_max)-1:0] w_cnt_max;
还是参考博主的讲解,学习这个知识
FPGA中FIFO的实现可以使用分布式资源或者BLOCK RAM,当使用FIFO缓冲空间较小时,我们选择使用Distributed RAM;当使用FIFO缓冲空间较大时,我们选择使用BLOCK RAM资源;这是一般的选择原则。而我们在声明我们的mem类型的变量时,通过添加约束(*ram_style = “distributed”)/(*ram_style = “block”)来指定。
(*ram_style = "distributed"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];
或者:
(*ram_style = "block"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];
————————————————
版权声明:本文为CSDN博主「李锐博恩」的原创文章
原文链接:https://blog.csdn.net/Reborn_Lee/article/details/106619999
这个点我想补充一下,在底层是如何实现的,VIVADO中资源之间的关系,FPGA 主要资源为: CLB、DSP、Block RAM、CMTs、GT以及XADC等等。一个CLB由2个Slice组成,Slice分为SLICEM和SLICEL,一个CLB里最多有一个SLICEM,即一个CLB可由两个SLICEL或一个SLICEL加一个SLICEM组成。SLICEL可用于逻辑,算术运算, SLICEM除了用于逻辑,算术运算外,还可配置成分布式RAM或32位的移位寄存器。
也就是上面同步FIFO中的方式,优点是:逻辑简单,便于理解,且能够获得当前FIFO中的数据量;缺点:资源使用多,代码量大
增加的一位用来表示轮数,如果两个的附加位相同说明处于同一轮,如果附加位不同,则处于不同的轮数,此时,对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。
判空满的逻辑如下图展示更加清晰:

自然二进制数在表示一个连续变化的数值时,可能会有多个位同时发生变化,每个位翻转(变化)的频率是比较高的,这在某些应用场合,如在FPGA内部跨时钟域传输数据时,是十分不利的。
上图展示了格雷码,十进制0-15的格雷码的对应关系,格雷码有两个特性,一个是循环特性,一个是单布特性。
不过格雷码也有一个缺点,那就是相比于自然二进制码来说,它是一种无权码(而自然二进制码实际上是“8421”码,因此很难直接进行比较和数学运算,所以一般都需要将采集到的以格雷码为表示形式的数据先转换成自然二进制码,然后再参与运算。
异步FIFO通过对比读写指针来进行满空判断,但是读写地址属于不同的时钟域,在比较之前需要将读写地址进行同步处理,然后进行判空满操作后才能够读写。这样会存在两个问题:第一个是跨时钟域容易发生亚稳态,导致判断出错。第二个问题是时钟是不同频,或者读快写慢,或者读慢写快,这时候尽管进行了地址同步,也可能有一定的滞后性。以读慢写快为例,在写入端需要判满,因为写的时钟快,所以对于同步后的读端的地址不会遗漏,读的地址只存在等于小于当前读地址,所以满标志只会提前产生。在读端,同步写的地址,写时钟满,必然会遗漏一部分地址,且及有可能写地址小于真实地址,所以,空标志也会提前产生,尽管地址有遗漏,但时对于FIFO的逻辑操作不会产生影响。这里不管是采用二进制编码还是格雷码编码,都会存在同步后的读写地址不符合实际情况,但是依然能够保证FIFO功能的正确性,需要注意gray码只是在相邻两次跳变之间才会出现只有1位数据不一致的情形,超过两个周期则不一定,所有地址总线bus 偏差一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。
可以看到二进制编码的地址跨时钟同步最主要的问题是要消除亚稳态以及减少跳变。binary编码的地址总线在跳变时极易产生毛刺,因为binary编码是多位跳变,在实现电路时不可能做到所有的地址总线等长,address bus 偏差必然存在,而且写地址和读地址分属不同时钟域异步,这样地址总线在进行同步过程中出错不可避免,比如写地址在从0111到1000转换时4条地址线同时跳变,这样读时钟在进行写地址同步后得到的写地址可能是0000-1111的某个值,这个完全不能确定,所以用这个同步后的写地址进行FIFO空判断的时候难免出错。
通过上面的分析可得,异步FIFO采用地址增加额外一位来判满,然后通过将二进制地址转换位格雷码,降低读写地址在连续变换时的位翻转概率,跨时钟域,然后大拍消除亚稳态,然后在转换为格雷码,进行地址比较,判断满空状态。
格雷码解决了一个问题,但同时也带来了另一个问题,即格雷码如何判断空与满?
**对于“空”的判断依然是两者完全相等(**包括增加的位);
对于“满”的判断由于于gray码除了MSB外,具有镜像对称的特点,存储深度为8,最多MSB表示的读写之间之间最大的差距是一轮,所以,格雷码的表示的深度是16。例如,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,但这个判满的条件显然不成立。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
1.wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
2.wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
3.剩下的其余位完全相等。

该过程也称为格雷码的编码,方法是从二进制码的最右边一位(最低位)起,依次将每一位与左边一位进行异或运算,作为对应格雷码该位的值,而最左边一位(最高位)不变。 对应公式如下:
g[n] <= b[n] //其中g[n]代表的是n位的格雷码,b[n]代表的是二进制码
g[n] <= g[n] ^ (g[n}] >> 1)
例如,将自然二进制码“10110”转换为格雷码,可以形象的用下图表示其转换过程:
简单说来,就是对二进制码右移移位,与其本身相异或即可。
该过程也称为格雷码的解码,方法是从格雷码左边第二位(次高位)起,将每一位与其左边一位解码后的值异或,作为该位解码后的值,而最左边一位(最高位)的解码结果就是它本身。对应公式如下:
b[n] <= g[n] //其中g[n]代表的是n位的格雷码,b[n]代表的是二进制码
b[n] <= g[n]^b[n+1] //只需要计算n-1位 的即可
例如,将格雷码“11101”转换为自然二进制码,可以形象的用下图表示其转换过程:
代码实现的过程要注意当二进制的最高位和格雷码的最高位是相同的,不需要计算直接赋值。
module gray2bin # (
parameter N = 4;
)(
input [N-1:0] gray,
output [N-1:0] bin
);
assign bin[N-1] = gray[N-1];
genvar i;
generate
for(i = N-2; i >= 0 ;i++)begin:gray_2_bin
bin[i] <= gray[i] ^ bin[i+1];
end
endgenerate
endmodule

图和代码是对应的,以左边的写端为例输入到fifo_mem的地址为waddr[ADDR_SIZE-1 : 0],waddr[ADDR_SIZE:0] 转换格雷码为waddr_gray,在读端打两拍分为为:wr_gray2,wr_gray2。
`timescale 1ns / 1ps
module asy_FIFO_gray#(
parameter DATA_WIDTH = 8, //数据位宽
parameter DATA_DEPTH = 8 //数据深度
)(
input wire wclk,
input wire wrst_n,
input wire winc, //有效时写入数据
input wire [DATA_WIDTH-1 : 0] wdata,
output wire wfull,
input wire rclk,
input wire rrst_n,
input wire rinc,
output reg [DATA_WIDTH-1 : 0] rdata,
output wire rempty
);
localparam ADDR_SIZE = $clog2(DATA_DEPTH); //ARR_SIZE = 3;
reg [ADDR_SIZE : 0] waddr,raddr; //注意这里的地址宽度[3:0],是5
wire [ADDR_SIZE : 0] waddr_gray, raddr_gray; //转换后的格雷码位宽为[5:0]
reg [ADDR_SIZE : 0] wr_gray1, wr_gray2; //读时钟域———打拍后的格雷码
reg [ADDR_SIZE : 0] rw_gray1, rw_gray2; //写时钟域———打拍后的格雷码
//二进制转换为格雷码
assign waddr_gray = waddr ^ (waddr >> 1) ; //逻辑移位与算术移位的右移符号分别为“>>”和“>>>”,左移同理,逻辑移位不考虑符号位
assign raddr_gray = raddr ^ (raddr >> 1) ; //左移和右移都只补零;算术移位考虑符号位,左移补零,右移补符号位
//将读端的读格雷码地址同步到写端
//主要功能是将读写端的格雷码进行打拍,r_to_w
//首先要注意格雷码的位宽大于地址位宽一位,所以rptr位宽为6,addrsize = 5,[5;0]
//1
always @(posedge wclk or negedge wrst_n)begin
if (!wrst_n)
{rw_gray2,rw_gray1} <= 0;
else
{rw_gray2,rw_gray1} <= {rw_gray1,raddr_gray};
end
//将写端的写格雷码地址同步到读端
//主要功能是将读写端的格雷码进行打拍,w_to_r
//2
always @(posedge rclk or negedge rrst_n)begin
if (!rrst_n)
{wr_gray2,wr_gray1} <= 0;
else
{wr_gray2,wr_gray1} <= {wr_gray1,waddr_gray};
end
//存储器读写,写信号的有效需要判断你,读总是有效的,读出当前地址内的数据
reg [DATA_DEPTH-1 : 0] fifo_mem [DATA_WIDTH-1 : 0];
wire wclken, rclken;
//写端,当写有效时,写入数据
always@(posedge wclk or negedge wrst_n) begin
if(wrst_n == 0) begin
waddr <= 0;
end
else if(wclken) begin
fifo_mem[waddr[ADDR_SIZE-1 : 0]] <= wdata;
waddr <= waddr + 1;
end
end
//读端,当读有效时,读出数据
always@(posedge rclk or negedge rrst_n) begin
if(rrst_n == 0) begin
raddr <= 0;
end
else if(rclken) begin
rdata <= fifo_mem[raddr[ADDR_SIZE-1 : 0]] ;
raddr <= raddr + 1;
end
end
//判满逻辑
//wire wfull;
localparam ADDRSIZE = $clog2(DATA_DEPTH); //ARR_SIZE = 3;
assign wfull = wrst_n? (waddr_gray=={~rw_gray2[ADDRSIZE:ADDRSIZE-1], rw_gray2[ADDRSIZE-2:0]}) : 1'b0;
//判空逻辑
//wire rempty;
assign rempty = rrst_n ? (raddr_gray == wr_gray2): 1'b0;
assign wclken = winc & !wfull;
assign rclken = rinc & !rempty;
endmodule
其中需要注意的有如下几点:
`timescale 1ns / 1ps
module sim_asy_FIFO_gray();
parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 8;
reg wclk;
reg wrst_n;
reg winc; //有效时写入数据
reg [DATA_WIDTH-1 : 0] wdata;
wire wfull;
reg rclk;
reg rrst_n;
reg rinc;
wire [DATA_WIDTH-1 : 0] rdata;
wire rempty;
initial begin
wclk = 0;
forever begin
#5 wclk = ~wclk;
end
end
initial begin
rclk = 0;
forever begin
#10 rclk = ~rclk;
end
end
initial begin
wrst_n = 1;
rrst_n = 1;
winc = 0;
rinc = 0;
#30
wrst_n = 0;
rrst_n = 0;
#30
wrst_n = 1;
rrst_n = 1;
//write data into fifo buffer
@(negedge wclk)
wdata = $random;
winc = 1;
repeat(8) begin
@(negedge wclk)
wdata = $random; // write into fifo 8 datas in all;
end
// read parts
@(negedge wclk)
winc = 0;
@(negedge rclk)
rinc = 1;
repeat(8) begin
@(negedge rclk); // read empty
end
@(negedge rclk)
rinc = 0;
//write full
# 80
@(negedge wclk)
winc = 1;
wdata = $random;
repeat(15) begin
@(negedge wclk)
wdata = $random;
end
@(negedge rclk)
rinc = 1;
repeat(8) begin
@(negedge rclk); // read empty
end
@(negedge rclk)
rinc = 0;
@(negedge wclk)
winc = 0;
#50 $finish;
end
asy_FIFO_gray #(
.DATA_WIDTH(DATA_WIDTH),
.DATA_DEPTH(DATA_DEPTH)
) inst_asy_FIFO_gray (
.wclk(wclk),
.wrst_n(wrst_n),
.winc(winc),
.wdata(wdata),
.wfull(wfull),
.rclk(rclk),
.rrst_n(rrst_n),
.rinc(rinc),
.rdata(rdata),
.rempty(rempty)
);
endmodule


这篇从原理和实现上都进行了记录,可以看到功能实现正确。如果有问题,可以评论交流。
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG