异步FIFO学习

这里写自定义目录标题

  • 一、概述
  • 二、异步FIFO的设计基础
    • 2.1 FIFO指针
    • 2.2 格雷码的使用
      • 2.2.1 二进制码存在的问题
      • 2.2.2 格雷码计数器
    • 2.3 空满条件的判断
  • 三、异步FIFO设计实现
    • 3.1 fifo1
    • 3.2 fifomem
    • 3.3 sync_r2w
    • 3.4 sync_w2r
    • 3.5 rptr_empty
    • 3.6 wptr_full

一、概述

在大规模ASIC或FPGA设计中,多时钟系统往往是不可避免的,这样就产生了不同时钟域数据传输的问题,其中一个比较好的解决方案就是使用异步FIFO来作不同时钟域数据传输的缓冲区,这样既可以使相异时钟域数据传输的时序要求变得宽松,也提高了它们之间的传输效率。

异步FIFO是指一种FIFO设计,其中将数据从一个时钟域写入FIFO缓冲区,并从另一个时钟域的同一FIFO缓冲区中读取数据,这两个时钟域彼此异步。使用异步FIFO可以将数据安全地从一个时钟域传递到另一个时钟域。

进行异步FIFO设计的方法有很多,在设计的过程中一些细节没有注意可能会使设计存在问题和缺陷,可能存在问题但仿真时大部分时间也能够正常工作导致这些问题难以检测和调试。所以本文讨论了一种FIFO设计风格和进行异步FIFO设计时必须考虑的重要细节。

进行FIFO设计时的困难与生成FIFO指针以及找到确定FIFO上的满状态和空状态的可靠方法有关

二、异步FIFO的设计基础

2.1 FIFO指针

同步FIFO空满判断的一种实现:读写计数
对于同步FIFO设计(在同一时钟域中进行FIFO缓冲区的读写操作的FIFO),一种实现方式是对FIFO缓冲区的写入和读取次数进行计数(onFIFO写入但不读取), 递减(在FIFO读取但不写入时)或保持(无写入和读取,或同时进行读写操作)FIFO缓冲区的当前填充值。 当FIFO计数器达到预定的全值时FIFO已满,当FIFO计数器为零时FIFO为空。但对于异步FIFO设计,不能使用增减FIFO计数器,因为将需要两个不同的异步时钟控制计数器。

异步FIFO空满判断的实现:读写指针
为了确定异步FIFO设计的满状态和空状态,必须比较写入和读取指针。

在复位时,写指针被设置为零。在FIFO写操作中,写指针所指向的存储位置被写入,然后写指针递增以指向下一个要写的位置。同样,读取指针始终指向要读取的当前FIFO字。 在复位时,两个指针都复位为零,FIFO为空,而读指针指向无效数据(因为FIFO为空并且声明了空标志)。

将第一个数据字写入FIFO后,写指针就会递增,清空标志,并且仍在寻址第一个FIFO存储字内容的读指针会立即将第一个有效字驱动到FIFO数据输出端口,由接收器逻辑读取。读取指针始终指向要读取的下一个FIFO字,这意味着,接收器逻辑不必使用两个时钟周期来读取数据字。(若接收器在读取FIFO数据字之前首先必须增加读取指针,则接收器将计时一次以从FIFO输出数据字,并再次计时以将数据字捕获到接收器中。 这是低效的)

当读写指针相等时,FIFO为空。 当在复位操作期间两个指针都复位为零时,或者当读取指针从FIFO读取了最后一个字时,读指针追上写指针时,就会发生这种情况。当指针再次相等时,即当写指针已经追上了读指针。因此单纯指针相等,不能确定FIFO是空的还是满的用来区分是FIFO空满状态的一种设计技术是为每个指针添加一个额外的位。 当写指针增加到最终FIFO地址之后,写指针将使未使用的MSB递增,同时将其余位设置回零,如图1所示。 使用读取指针完成相同操作。 如果两个指针的MSB不同,则意味着写指针比读指针多绕了一次。 如果两个指针的MSB相同,则意味着两个指针的绕行次数相同。

在这里插入图片描述
使用n位指针(其中n-1位是访问整个FIFO存储缓冲区所需的地址位数),当两个指针(包括MSB)相等时,FIFO为空。 当两个指针(MSB除外)相等时,FIFO已满。

2.2 格雷码的使用

2.2.1 二进制码存在的问题

将二进制计数值从一个时钟域同步到另一个时钟域将存在亚稳态问题,因为一个n-bit计数器的每个位都可以同时更改(例如,二进制数7-> 8的值为0111-> 1000,所有位都更改了)。

解决问题的一种方法:握手信号

解决该问题的一种方法是将周期二进制计数值采样并保存在一个保持寄存器中,然后将异步就绪信号传递到新的时钟域。 识别就绪信号后,接收时钟域将同步的确认信号发送回发送时钟域。 在从接收时钟域接收到确认信号之前,采样指针不得更改。

使用此技术,可以将具有多个更改位的计数值安全地传输到新的时钟域。 收到确认信号后,发送时钟域有权清除就绪信号并重新采样二进制计数值。使用此技术,二进制计数器值会定期进行采样,并且并非所有二进制计数器值都可以传递给新的时钟域。

同步采样的读写指针此时并不一定是实际的读写地址(同步采样指针滞后于实际指针),所以不用担心FIFO上溢或者下溢的情况:
不需要需要担心二进制计数器可能继续增加采样计数器值使得FIFO出现上溢或下溢的情况。当写指针赶上同步采样的读指针时,FIFO已满。 同步采样的读取指针可能无法反映实际读取指针的当前值,但是写入指针将不会尝试计数超过同步读取指针值的值。 不会发生溢出。当读指针赶上同步采样的写指针时,FIFO空。 同步采样的写指针可能无法反映实际写指针的当前值,但读指针将不会尝试计数超过同步写指针值的值。 不会发生下溢。

解决问题的另一种方法:格雷码
见 2.2.2

2.2.2 格雷码计数器

格雷码的特点
1、任何两个相邻编码的差别仅有1位(从一个格雷计数到下一个格雷计数只能改变一位)。
2、格雷码计数器的计数序列为2^n。可以使格雷码计数器计数偶数个序列,但是与这些序列之间的转换通常不像标准格雷码那样简单。

二进制码转格雷码:移位异或
gray[n-1:0] = (bin[n-1:0] >>1) ^ bin[n-1:0]

格雷码转二进制码:
bin[i] = ^(gray[n-1] >>1)

双n位格雷码计数器
为了之后的需要,这里介绍“双n位格雷码计数器”(一个通用的n位格雷码计数器修改第二个MSB以形成一个具有共享LSB的(n-1)位格雷码计数器)

为了理解n-bit格雷码转换到(n-1) bit格雷码的问题,以4位、3位格雷码计数器为例。4位格雷码后半部分的第二个MSB将在4位序列的三个LSB中产生所需的3位格雷码序列。 唯一的一个问题是带有额外MSB的3位格雷码不再是真正的格雷码,因为当序列从7(灰色0100)变为8(〜灰色1000),然后又从15(〜灰色1100)变为0时, (灰色0000),两位在变化,而不是一位。 真正的格雷码在计数之间只改变一位。

实现1:
图3是一种双n位格雷码计数器的框图。格雷码计数器假定寄存器位的输出本身就是格雷码值(ptr,wptr或rptr)。 然后将格雷码的输出传递到格雷二进制转换器(bin),再将其传递给条件二进制值增量器以生成下一个二进制计数值(bnext),然后将其传递给格雷码转换器生成next-Gray-count-value(gnext),该值传递到寄存器输入。

图3框图的上半部分显示了所描述的逻辑流程,而下半部分则显示了与下一节所述的第二个格雷码计数器相关的逻辑。
在这里插入图片描述
该双n位格雷码计数器既生成n位格雷码序列又生成(n-1)位格雷码序列。(n-1)位格雷码通过对n位格雷码的两个MSB进行异或运算以生成用于(n-1)位格雷码的MSB,它与n位格雷码计数器的(n-2)个LSB组合在一起,形成(n-1)位格雷码计数器。

实现2:
FIFO实现使用这种格雷码计数器,该格雷码计数器实际上使用了两组寄存器,从而无需将格雷指针值转换为二进制值。 第二组寄存器(二进制寄存器)还可用于直接寻址FIFO存储器,而无需将内存地址转换为格雷码。 仍需要n位格雷码指针将指针同步到相反的时钟域,但是n-1位二进制指针可用于直接寻址内存。 如果需要,二进制指针还使运行计算更容易以生成“"almost-full”和“almost-empty”的标志位。
在这里插入图片描述
格雷码指针和二进制指针的比较
较格雷码指针,二进制指针的一些优点:
•将多bit 采样到保持寄存器中并使用同步握手控制信号将多位值传递到新的时钟域中的技术可用于跨时钟域传递任何任意多位值。该技术可用于传递FIFO指针或任何多bit值。
•每个同步格雷码指针需要2n个触发器(每个指针位2个)。采样的多位寄存器需要2n + 4个触发器(每个时钟域中每个保持寄存器位1个,2个触发器用于同步就绪位,2个触发器用于同步确认位)。任何一种指针样式经历亚稳态的机会都没有明显的差异。
•采样的多位二进制寄存器允许任意更改指针。格雷码指针只能递增和递减。
•采样的多位寄存器技术允许任意FIFO深度。而格雷码指针需要2^n FIFO深度。如果设计需要至少132个字的FIFO深度,则使用标准格雷码指针将采用256个字的FIFO深度。由于大多数实例化的双端口RAM块的深度都是2的幂,因此这可能不是问题。
•使用二进制指针可以轻松地使用之间的简单二进制算法来计算“almost full”和“almost empty”状态位,

较格雷码指针使用二进制指针的一个缺点是:
•采样并持有二进制FIFO指针,然后在时钟边界上进行握手,可能会使新采样的捕获与接收时钟域至少相隔两个时钟沿。和来自发送时钟域的另外两个时钟边沿。此等待时间通常不是问题,但通常会在断言为full和empty时增加更多悲观,并且可能需要额外的FIFO深度来补偿所增加的悲观情绪。 由于大多数FIFO通常都是用过大的深度指定的,因此不太可能需要额外的寄存器或更大的双端口FIFO缓冲区大小。

在选择实现FIFO设计的方法时,上述比较值得考虑。

2.3 空满条件的判断

在读时钟域中生成空标志,以确保在FIFO缓冲区为空时立即检测到空标志;在写时钟域中将生成满标志,以确保在FIFO缓冲区已满时立即检测到满标志。

2.3.1 空信号生成
如图1所示,当读指针和同步写指针相等时,FIFO为空。

使用比寻址FIFO所需的指针大一位的指针时, 当两个指针的额外位(指针的MSB)相等,则指针的回绕次数相同,加上读指针的其余部分等于同步的写指针,则FIFO为空。

写指针wptr 必须通过sync_w2r模块中的一对同步器寄存器将格雷码写指针同步到读时钟域进行比较。 因为使用格雷码指针一次只有一位改变,所以在时钟域之间同步多位转换是没有问题的。为了有效地记录剩余输出,实际上将同步的写指针与下一个格雷码读指针进行比较。
在这里插入图片描述

2.3.2 满信号生成
在这里插入图片描述
以8位深度的FIFO为例,有效地址为3bit,这里扩展一位来判断空满条件。当FIFO在前7个位置写入数据,然后读相同的七个字来清空FIFO,则两个指针将相等并指向地址Gray-7(FIFO为空)。 在下一个写操作中,写指针将使4位格雷码指针递增(请记住,只有3个LSB用于地址存储器),这使4位指针上的MSB有所不同,但其余写指针位将匹配读指针位,因此将声明FIFO满标志。

出现错误:不仅FIFO未满,而且3个LSB均未更改,这意味着寻址的存储器位置将覆盖最后写入的FIFO存储器位置。
(1、满信号错误;2、100地址位数据覆盖写入)

正确的满信号判断需要满足:
读指针与写指针的高2位不相等,其余位均相等;

在这里插入图片描述

三、异步FIFO设计实现

在这里插入图片描述
根据以上所诉内容,设计可以划分为以下六个Verilog模块:

•fifo1.v --顶层模块以实例化设计中使用的所有其他FIFO模块。如果将此FIFO用作较大的ASIC或FPGA设计的一部分,则可能会舍弃此顶层模块,以允许将其他FIFO模块分组到各自的时钟域中,以改善综合和静态时序分析。
•fifomem.v–这是FIFO存储器缓冲区,可通过写入和读取时钟域进行访问。该缓冲区很可能是实例化的同步双端口RAM。
•sync_r2w.v–同步器模块,用于将读指针同步到写入时钟域。wptr_full模块将使用同步的读取指针来生成FIFO已满条件。
•sync_w2r.v–同步器模块,用于将写指针同步到读时钟域中。rptr_empty模块将使用同步的写指针来生成FIFO空条件。该模块中没有其他逻辑。
•rptr_empty.v–该模块与read-clock domain完全同步,并且包含FIFO读指针和空标志逻辑。
•wptr_full.v–该模块与write-clockdomain完全同步,并且包含FIFO写指针和满标志逻辑

3.1 fifo1

顶层FIFO模块将所有子模块实例化(建议使用端口命名的方式实例化)。

module fifo1 #(parameter DSIZE = 8,     parameter ASIZE = 4)  
(	output [DSIZE-1:0] rdata,   
	output             wfull,   
	output             rempty,   
	input  [DSIZE-1:0] wdata,   
	input              winc, wclk, wrst_n,   
	input              rinc, rclk, rrst_n
	);  
	
wire   [ASIZE-1:0] waddr, raddr;  
wire   [ASIZE:0]   wptr, rptr, wq2_rptr, rq2_wptr;  

sync_r2w      sync_r2w  (
							.wq2_rptr(wq2_rptr), 
							.rptr(rptr),      
							.wclk(wclk), 
							.wrst_n(wrst_n)); 
							 
sync_w2r      sync_w2r  (	.rq2_wptr(rq2_wptr), 
							.wptr(wptr), 
							.rclk(rclk), 
							.rrst_n(rrst_n));  

fifomem #(DSIZE, ASIZE) fifomem (
							.rdata(rdata), 
							.wdata(wdata),  
							.waddr(waddr), 
							.raddr(raddr), 
							.wclken(winc), 
							.wfull(wfull),  
							.wclk(wclk));  
							
rptr_empty #(ASIZE)     rptr_empty   (
							.rempty(rempty), 
							.raddr(raddr),
							.rptr(rptr),
							.rq2_wptr(rq2_wptr),  
							.rinc(rinc), 
							.rclk(rclk),
							.rrst_n(rrst_n)); 
							 
wptr_full  #(ASIZE)     wptr_full  (
							.wfull(wfull), 
							.waddr(waddr), 
							.wptr(wptr), 
							.wq2_rptr(wq2_rptr),      
							.winc(winc), 
							.wclk(wclk),  
							.wrst_n(wrst_n));
endmodule

3.2 fifomem

module fifomem #(parameter  DATASIZE = 8, // Memory data word width		
				parameter  ADDRSIZE = 4) // Number of mem address bits  
(	output [DATASIZE-1:0] rdata,   
	input  [DATASIZE-1:0] wdata,   
	input  [ADDRSIZE-1:0] waddr, raddr,   
	input                 wclken, wfull, wclk);  
`ifdef VENDORRAM    // instantiation of a vendor's dual-port RAM    
	vendor_ram mem (	.dout(rdata), 
						.din(wdata),                    
						.waddr(waddr), 
						.raddr(raddr), 
						.wclken(wclken), 
						.wclken_n(wfull), 
						.clk(wclk));  
`else    // RTL Verilog memory model    
	localparam DEPTH = 1<<ADDRSIZE;    
	reg [DATASIZE-1:0] mem [0:DEPTH-1];   
	 
	assign rdata = mem[raddr];    
	always @(posedge wclk)      
		if (wclken && !wfull) mem[waddr] <= wdata;  
`endif

endmodule

3.3 sync_r2w

同步器模块,通过使用将FIFO写入时钟作为时钟源的一对寄存器,将n位指针从读取时钟域传递到写入时钟域。

module sync_r2w #(parameter ADDRSIZE = 4)  
(	output reg [ADDRSIZE:0] wq2_rptr,   
	input      [ADDRSIZE:0] rptr,   
	input                   wclk, wrst_n);  

reg [ADDRSIZE:0] wq1_rptr;  

always @(posedge wclk or negedge wrst_n)    
if (!wrst_n) 	{wq2_rptr,wq1_rptr} <= 0;    
else         	{wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};

endmodule

3.4 sync_w2r

同步器模块,通过使用将FIFO写入时钟作为时钟源的一对寄存器,将n位指针从写时钟域传递到读时钟域。

module sync_w2r #(parameter ADDRSIZE = 4)  
(	output reg [ADDRSIZE:0] rq2_wptr,   
	input      [ADDRSIZE:0] wptr,   
	input                   rclk, rrst_n);  

reg [ADDRSIZE:0] rq1_wptr;  

always @(posedge rclk or negedge rrst_n)    
if (!rrst_n) 	{rq2_wptr,rq1_wptr} <= 0;    
else        	 {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};

endmodule

3.5 rptr_empty

该模块描述了在读取时钟域内生成的所有FIFO逻辑(同步器除外)。读取指针是双n位格雷码计数器输出。 n位格雷码读指针(rptr)通过sync_r2w模块传递到写时钟域。 (n-1)位二进制读指针(raddr)用于寻址FIFO缓冲区。当下一个rptr值等于同步的wptr值时,FIFO empty信号输出会在下一个rclk上升沿有效。

module rptr_empty #(parameter ADDRSIZE = 4)  
(	output reg                rempty,   
	output     [ADDRSIZE-1:0] raddr,   
	output reg [ADDRSIZE  :0] rptr,   
	input      [ADDRSIZE  :0] rq2_wptr,   
	input                     rinc, rclk, rrst_n);  

reg  [ADDRSIZE:0] rbin;  
wire [ADDRSIZE:0] rgraynext, rbinnext;  
//-------------------  
// GRAYSTYLE2 pointer  
//-------------------  
always @(posedge rclk or negedge rrst_n)    
if (!rrst_n) {rbin, rptr} <= 0;    
else         {rbin, rptr} <= {rbinnext, rgraynext};  

// Memory read-address pointer (okay to use binary to address memory)  
assign raddr     = rbin[ADDRSIZE-1:0];  
assign rbinnext  = rbin + (rinc & ~rempty);  
assign rgraynext = (rbinnext>>1) ^ rbinnext;  

//---------------------------------------------------------------  
// FIFO empty when the next rptr == synchronized wptr or on reset  //---------------------------------------------------------------  
assign rempty_val = (rgraynext == rq2_wptr);  

always @(posedge rclk or negedge rrst_n)    
if (!rrst_n) rempty <= 1'b1;    
else         rempty <= rempty_val;

endmodule

3.6 wptr_full

该模块描述了在写时钟域内生成的所有FIFO逻辑(同步器除外)。写指针是双n位格雷码计数器输出。 n位格雷码写指针(wptr)通过sync_w2r模块传递到读取时钟域。 (n-1)位二进制写指针(waddr)用于寻址FIFO缓冲区。

当下一个格雷码写指针与读时钟域同步过来的读指针相等时,在下一个clk上升沿产生full信号。

module wptr_full #(parameter ADDRSIZE = 4)  
(	output reg                wfull,   
	output     [ADDRSIZE-1:0] waddr,   
	output reg [ADDRSIZE  :0] wptr,   
	input      [ADDRSIZE  :0] wq2_rptr,   
	input                     winc, wclk, wrst_n
	);  

reg  [ADDRSIZE:0] wbin;  
wire [ADDRSIZE:0] wgraynext, wbinnext;  

// GRAYSTYLE2 pointer  
always @(posedge wclk or negedge wrst_n)    
if (!wrst_n) 	{wbin, wptr} <= 0;    
else         	{wbin, wptr} <= {wbinnext, wgraynext};  

// Memory write-address pointer (okay to use binary to address memory)  
assign waddr = wbin[ADDRSIZE-1:0];  
assign wbinnext  = wbin + (winc & ~wfull);  
assign wgraynext = (wbinnext>>1) ^ wbinnext;  

//------------------------------------------------------------------  // Simplified version of the three necessary full-tests:  
// assign wfull_val=((wgnext[ADDRSIZE]    !=wq2_rptr[ADDRSIZE]  ) &&  //                   (wgnext[ADDRSIZE-1]  !=wq2_rptr[ADDRSIZE-1]) &&  //                   (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));  //------------------------------------------------------------------  
assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],                                   wq2_rptr[ADDRSIZE-2:0]});  

always @(posedge wclk or negedge wrst_n)    
if (!wrst_n) wfull  <= 1'b0;    
else         wfull  <= wfull_val;endmodule

endmodule

参考文献:
[1] C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design,” pp. 1–23.

热门文章

暂无图片
编程学习 ·

Linux 练习 - 文本处理三剑客之AWK

1、文件 ip_list.txt 如下格式,请提取 ”.solin.com” 前面的主机名部分并写入到回到该文件中 1 blog.solin.com 2 www.solin.com … 999 study.solin.com [root@centos7 ~]# awk -F "[ .]" {print $2} ip_list.txt >> ip_list.txt2、统计 /etc/fstab 文件中每…
暂无图片
编程学习 ·

二、21【设计模式】之状态模式

今天的博客主题设计模式 ——》 设计模式之状态模式状态模式 SP (State Pattern)定义允许对象在内部状态发生改变时改变它的行为,看起来好像修改了它的类。类的行为是由状态决定的,不同的状态下该类有不同的行为。就是一个对象在其内部改变的时候,它的行为也随之改变。核心…
暂无图片
编程学习 ·

用Python读取pg数据库,准确统计每一张表的数据量,输出中英文表名和数据量

1 前言 在我们工作中,有时候老板关系我们手上到底有多少数据,每一张表中到底有多少数据量,整个库又有多少数据量?要给他一个准确的数据,给出一张详细清单。 在网上遇到的一种做法是使用navicat写SQL语句统计pg_class里面的reltuples这个列数据,但是发现这个数据有很大偏…
暂无图片
编程学习 ·

共识算法 POW/POS

POW/POS在区块链系统中,共识算法是区块链保持数据安全、不可篡改、透明性等特色的关键技术。共识机制是区块链的灵魂,是区块链建立信任的基础。一个区块链项目选择使用何种共识机制,决定了这个项目是否能建立起完善的激励机制,从而起到鼓励更多节点参与到项目中,进而增加系…
暂无图片
编程学习 ·

处理服务器cpu 占用高达99%问题

102服务器cpu 占用高达99%,查看服务百度了下这个服务是 swap分区的作用是当物理内存不足时,会将一部分硬盘当做虚拟内存来使用。 kswapd0 占用过高是因为 物理内存不足,使用swap分区与内存换页操作交换数据,导致CPU占用过高。 所以将服务器升级了内存和cpu 然而 服务器扩容…
暂无图片
编程学习 ·

带你全面了解蓝牙定位原理,蓝牙定位方案种类-新导智能

蓝牙定位服务是蓝牙技能领域增加最快的解决方案,现在已经被越来越多的运用于寻物、室内定位、室内导航以及财物追寻等领域。蓝牙联盟也在不断的迭代蓝牙技能,使得定位技能的精度越来越高,从最早的朴实基于信号强度的Beacon定位,到蓝牙5.1支撑的多天线视点定位等。从方案视点…
暂无图片
编程学习 ·

【阿里云】学生成长计划领取资格考试答案分享

云计算及云服务器入门 刚刚尝试了阿里云的高校学生计划,完成身份和学生认证后出现了需要测试才能领取,没想到凭感觉还拿到了90分,科一科四都能过了哈哈,下面是分享,希望后半年能把这种好资源利用起来,真正学点吃饭的东西,正确答案加粗显示。 选择题单选 1.SQL语言的功能…
暂无图片
编程学习 ·

程序员翻车时的 30 种常见反应!

**软件开发工作充满了挑战性。人无完人,对于程序员来说,写出有 bug 的代码是在所难免的。有些人很淡定,也有一些人会感到生气、沮丧、不安或气馁。在修复 bug 的过程中我们都经历了什么?这个值得我们一探究竟。 本文列出了程序员在修复 bug 时可能会说的一些话或者想法。我…
暂无图片
编程学习 ·

虚拟机VMware安装学习过程中遇到的几个问题

1.在安装VMware的时候刚开始因为版本不足的原因,电脑显示 Failed to initialize ploicy for cpu 后来我把它复制到百度上发现是我电脑版本过高的原因,于是又下载了VMware15.5.1版本 又查找了它的破解版。2.在安装的过程中还出现过屏幕就一个-,然后什么都不出现,于是查找资…
暂无图片
编程学习 ·

flutter textfield设置高度后内容无法居中

Container(height: 50,width: MediaQuery.of(context).size.width * 2 / 3,alignment: Alignment.center,child: TextField(autofocus: true,decoration: InputDecoration(//这行代码是关键,设置这个之后,居中contentPadding: EdgeInsets.all(2),hintText: 请输入手机号,pref…
暂无图片
编程学习 ·

现代开发者必备:5个更流畅、更受欢迎的Python web框架

全文共1837字,预计学习时长9分钟图源:unsplash如今,可供选择的Python web框架有不少,能帮助你更快更轻松地创建web应用。本文就将为大家介绍一些更现代、使用更广泛的web框架。1.FastAPIFastAPI致力于实现轻便和快速,笔者很喜欢它,它的开发速度和简单程度令人欣慰。这对于…
暂无图片
编程学习 ·

数据表的规范

数据库的设计范式 六种范式 1. 第一范式 2. 第二范式 3. 第三范式 4. BCNF 巴斯-科德范式 5. 第四范式 6. 第五范式 完美范式 * 范式设计越高阶,冗余度越低。数据表中的键 1. 超键: 能唯一标识元组的属性集叫超键 2. 候选键:如果超键不包括多余的属性,这个超键就是候选键 …
暂无图片
编程学习 ·

Tomcat 启动控制台乱码

Tomca 启动控制台乱码将tomcat用作web应用服务器,在tomcat的服务迭代中,服务漏洞是不可避免需要升级的来修复漏洞的。在修复漏洞的时候通常需要将原来的webapps下的文件复制到新tomcat中,并替换tomcat/conf/server.xml文件(小版本升级都可以用这种方式)。解决乱码 在tomca…
暂无图片
编程学习 ·

Java实现文件浏览器下载

前言:先说下需求,项目需求是用户一点击一个前端页面的链接就可以下载一个压缩包.因为就1个文件,使用文件管理系统像fastDSF,阿里云的OSS这种没必要,直接放在nginx服务器上的怕不好管理,于是给我限定了把文件打包到部署时候的jar包中并实现浏览器下载. 废话不多说,直接上代码! 1…
暂无图片
编程学习 ·

spring-data-rest快速实现增删改查

spring-data-rest实现增删改查 - 项目相关依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/> </parent>…
暂无图片
编程学习 ·

Java开发面试知识点-长期更新

前言:本节内容长期更新,专门为了扫清盲点复习。采取链接前置,内容后置。内容可能较为杂碎。 参考链接: Java基础知识面试题(2020最新版) 1、Java开发基础面试知识点 2、equals和HashCode深入理解以及Hash算法原理 长期更新1、String、StringBuilder、StringBuffer区别2、…
暂无图片
编程学习 ·

LeetCode 590. N叉树的后序遍历

目录结构1.题目2.题解1.题目给定一个 N 叉树,返回其节点值的后序遍历。例如,给定一个 3叉树 :返回其后序遍历: [5,6,3,2,4,1].说明: 递归法很简单,你可以使用迭代法完成此题吗?来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/n-ary-tree-postorder-traver…