Notas del estudio de diseño de IC digital
Problema de sincronización de dominios a través del reloj
2 Multi-bit signal cross-clock domain problem_asynchronous FIFO
2 FIFO asincrónico
- Diagrama esquemático
El diseño de FIFO asincrónico consta principalmente de 5 partes:
- Memoria FIFORAM de puerto 😀 ual para almacenar datos
- sync_r2w: Sincronizador, sincroniza el puntero de datos leídos con el dominio de reloj de escritura
- sync_w2r: Sincronizador, sincroniza el puntero de datos de escritura para leer el dominio de reloj
- wptr_full: Lógica para manejar el puntero de escritura y la señal completa
- rptr_empt: Lógica para procesar punteros de lectura y señales nulas
- Código Verilog
//----TOP module-------------------------------
module asyncfifo_r1#(
parameter ADDRSIZE = 4,
parameter DATASIZE = 8
)
(
//----write signal ----------------------------
input wclk,
input wrst,
input [DATASIZE-1:0] wdata,
input winc,
output wfull,
//----resd signal ----------------------------
input rclk,
input rrst,
input rinc,
output empty,
output [DATASIZE-1:0] rdata
);
wire [ADDRSIZE-1:0] waddr;
wire [ADDRSIZE-1:0] raddr;
wire [ADDRSIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
FIFOMEM_R0 U1 (
.wclk (wclk),
.wdata(wdata),
.rdata(rdata),
.waddr (waddr),
.raddr(raddr),
.wclken(winc),
.wfull (wfull)
);
syn_r2w U2 (
.wclk(wclk),
.rst_n(wrst),
.wq2_rptr(wq2_rptr),
.rptr(rptr)
);
sync_w2r_r1 U3 (
.rclk(rclk),
.rst_n(rrst),
.rq2_wptr(rq2_wptr),
.wptr(wptr)
);
wptr_full_r1 U4(
.wclk(wclk),
.wrst_n(wrst),
.winc(winc),
.wq2_rptr(wq2_rptr),
.wfull(wfull),
.waddr(waddr),
.wptr(wptr)
);
rptr_empty U5(
.rclk(rclk),
.rst_n(rrst),
.rinc(rinc),
.rq2_wptr(rq2_wptr),
.rempty(empty),
.raddr(raddr),
.rptr(rptr)
);
endmodule
1. Memoria FIFO
- En FPGA, podemos usar carnero de bloque o carnero distribuido.
- Código Verilog
module FIFOMEM_R0#(
parameter DATASIZE = 8, // memory data word width
parameter ADDRSIZE = 4 // memory address bits
)
(
input wclk,
input [DATASIZE-1:0] wdata,
input [ADDRSIZE-1:0] waddr,
input [ADDRSIZE-1:0] raddr,
input wclken,
input wfull,
output [DATASIZE-1:0] rdata
);
localparam DEPTH = 1<< ADDRSIZE;
reg [DATASIZE-1:0] mem [0:DEPTH-1];
//read----------------------------------
assign rdata = mem[raddr];
//write---------------------------------
[email protected](posedge wclk)begin
if(wclken && !wfull) //Write is valid and not full
mem[waddr] <= wdata;
end
endmodule
-
Simulación de Modelsim
Nota: La dirección de memoria es una dirección binaria; la dirección para determinar si está lleno o vacío es una dirección de código gris.
2. sync_r2w
- Código Verilog
module syn_r2w#(
parameter ADDRSIZE = 4
)
(
input wclk,
input rst_n,
input [ADDRSIZE:0] rptr,
output reg [ADDRSIZE:0] wq2_rptr
);
reg [ADDRSIZE:0] wq1_rptr;
[email protected](posedge wclk or negedge rst_n)begin
if(!rst_n)begin
wq1_rptr <= 0;
wq2_rptr <= 0;
end else begin
wq1_rptr <= rptr;
wq2_rptr <= wq1_rptr;
end
end
endmodule
- Simulación de Modelsim
3. sync_w2r
- Código Verilog
module sync_w2r_r1#(
parameter ADDRSIZE = 4
)
(
input rclk,
input rst_n,
input [ADDRSIZE:0] wptr,
output reg [ADDRSIZE:0] rq2_wptr
);
reg [ADDRSIZE:0] rq1_wptr;
[email protected](posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rq1_wptr <= 0;
rq2_wptr <= 0;
end else begin
rq1_wptr <= wptr;
rq2_wptr <= rq1_wptr;
end
end
endmodule
- Simulación de Modelsim
4. wptr_full
-
La lógica de generación de señal completa se coloca en el dominio de reloj de escritura. Dado que los bits N-1 inferiores del código gris de N-bit son simétricos, al comparar punteros de lectura y escritura, no basta con juzgar que el bit Nth no es igual y los bits N-1 inferiores son iguales al comparar punteros de lectura y escritura, y se generará una señal completa falsa. Para generar la señal completa correcta, tenemos que juzgar las siguientes tres condiciones al mismo tiempo:
El bit N-1 no es igual
N-2th bits no son iguales
Los bits bajos de N-3 son todos iguales
Al igual que la señal nula, para la salida de registro de la señal completa, podemos utilizar wgraynext en lugar de wptr para el puntero de escritura (wptr es un latido más tarde que wgraynext). -
Código Verilog
module wptr_full_r1#(
parameter ADDRSIZE = 4
)
(
input wclk,
input wrst_n,
input winc,
input [ADDRSIZE:0] wq2_rptr,
output reg wfull,
output [ADDRSIZE-1:0] waddr,
output reg [ADDRSIZE:0] wptr
);
wire wfull_val;
wire [ADDRSIZE:0] wbinnext;
wire [ADDRSIZE:0] wgraynext;
reg [ADDRSIZE:0] wbin;
//----write address---------------//
assign waddr = wbin[ADDRSIZE-1:0];
//----bin2gray--------------------//
assign wbinnext = wbin + (winc & ~wfull);
assign wgraynext = (wbinnext>>1)^ wbinnext;
//----gray pointer----------------//
[email protected](posedge wclk or negedge wrst_n)begin
if(!wrst_n)begin
wptr <= 0;
wbin <= 0;
end else
begin
wptr <= wgraynext;//gray
wbin <= wbinnext;
end
end
//----write full------------------//
assign wfull_val = ((wgraynext[ADDRSIZE] != wq2_rptr[ADDRSIZE]) &&
(wgraynext[ADDRSIZE-1] != wq2_rptr[ADDRSIZE-1]) &&
(wgraynext[ADDRSIZE-2:0] == wq2_rptr[ADDRSIZE-2:0]));
[email protected](posedge wclk or negedge wrst_n)begin
if(!wrst_n)
wfull <= 0;
else
wfull <= wfull_val;
end
endmodule
-
Simulación de Modelsim
5. rptr_empty - La lógica de generación de la señal nula se coloca en el dominio de reloj de lectura. Compare directamente si los punteros de lectura y escritura son iguales (el puntero de lectura se pone al día con el puntero de escritura). Tenga en cuenta que el puntero de escritura aquí es el puntero de escritura sincronizado con el dominio de reloj de lectura. Para la salida de registro de la señal vacía, podemos utilizar el puntero de lectura al comparar
rgraynext en lugar de rptr (rptr es un ritmo más tarde que rgraynext). - Código Verilog
module rptr_empty#(
parameter ADDRSIZE = 4
)
(
input rclk,
input rst_n,
input rinc,
input [ADDRSIZE:0] rq2_wptr,
output reg rempty,
output [ADDRSIZE-1:0] raddr,
output reg [ADDRSIZE:0] rptr //gray
);
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext;
wire [ADDRSIZE:0] rbinnext;
wire rempty_val;
//----gray pointer---------------------------------------//
[email protected](posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rbin <= 0;
rptr <= 0;
end else
begin
rbin <= rbinnext;
rptr <= rgraynext;
end
end
//----memory address-------------------------------------//
assign raddr = rbin[ADDRSIZE-1:0];
//----bin2gray-------------------------------------------//
assign rbinnext = rbin + (rinc & ~rempty);
assign rgraynext = (rbinnext>>1)^rbinnext;
//----FIFO empty when rptr == ayncronized wptr-----------//
assign rempty_val = (rgraynext == rq2_wptr);
[email protected](posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rempty <= 0;
end else
rempty <= rempty_val;
end
endmodule
-
Simulación de Modelsim
[Note]: Notas de estudio personales, si hay algún error, por favor siéntase libre de iluminarme. Esto es educado ~~~
.