Notas de estudio de front-end de IC digital: implementación Verilog de FIFO (1)

Artículos relacionados

Notas de estudio de front-end de IC digital: LSFR (registro de desplazamiento de retroalimentación lineal)

Notas de estudio de front-end de IC digital: sincronización de señal de dominio de reloj cruzado

Notas de estudio de front-end de IC digital: sincronización de señal y detección de bordes

Notas de estudio de front-end de IC digital: síntesis de pestillo Latch

Notas de estudio de front-end de IC digital: código Gray (incluido el convertidor de código Gray binario implementado por Verilog)

Notas de estudio de front-end de IC digital: implementación Verilog de FIFO (2)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (1)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (2)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (3)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (4)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (5)

Notas de estudio de front-end de IC digital: sondeo de arbitraje (6)

Notas de estudio de front-end de IC digital: algoritmo utilizado menos recientemente (LRU)


Tabla de contenido

1. Introducción

2. Operación FIFO

3. Principio FIFO síncrono

4. Implementación Verilog de FIFO síncrono


1. Introducción

        FIFO significa First in First out, que es una estructura de memoria ampliamente utilizada en el diseño de chips. FIFO consiste en una cola o matriz de celdas de memoria.Como sugiere el nombre, los datos que se escriben primero en la cola también se leen primero. En el diseño de chips, FIFO puede cumplir con los siguientes requisitos:

        (1) Cuando la tasa de datos de entrada no coincide con la tasa de salida, se utiliza como unidad de almacenamiento temporal. Por ejemplo, la CPU puede escribir datos en FIFO primero y luego continuar con otros trabajos, y el dispositivo puede leer fácilmente los datos de FIFO. Otro ejemplo es el controlador de Internet, que almacena los datos recibidos de la red en el FIFO, y el controlador DMA (acceso directo a la memoria) de back-end (ubicado en el circuito de interfaz PCIe o PCI) lee los datos del FIFO, y luego escribe en la memoria del sistema.

        (2) Utilizado para sincronización entre diferentes dominios de reloj. En aplicaciones prácticas, los datos a veces ingresan a otro dominio de reloj desde un dominio de reloj En este momento, FIFO no solo sirve como una unidad de almacenamiento de datos temporal, sino que también desempeña un papel en la sincronización de datos.

        (3) Cuando el ancho de datos de los datos de entrada y los datos de salida no coinciden, el ancho de datos se puede usar para ajustar el circuito.

        Presentaremos estas aplicaciones en detalle más adelante, y ahora daremos la estructura de FIFO y los puertos de entrada y salida, como se muestra en la figura a continuación, un FIFO con un ancho de 6 y una profundidad de 8. Esto significa que hay 8 ubicaciones de memoria en este FIFO y cada ubicación puede almacenar 6 bits de datos. Tenga en cuenta que, de hecho, los punteros de lectura y escritura son invisibles desde el exterior, y el dibujo en la figura es solo para conveniencia de una explicación posterior.

2. Operación FIFO

        Después de restablecer el circuito, el FIFO está vacío y tanto el puntero de escritura como el de lectura apuntan a la misma ubicación, que suele ser 0. Los datos son ingresados ​​por el puerto write_data, controlado por la señal de control write_en, y emitidos por el puerto read_data, controlado por la señal de control read_en. El puntero write_pointer apunta a la siguiente posición vacía posible (la posición después del elemento final de la cola), y el puntero read_pointer apunta a la siguiente posición de lectura (el primer elemento de la cola). Generalmente construimos FIFOs en forma de cola circular, es decir, si continuamos escribiendo, cuando el write_pointer apunta al valor máximo (por ejemplo, 111), si el elemento se vuelve a escribir, el write_pointer volverá a 0. El puntero de lectura también tiene esta naturaleza circular, en otras palabras, el puntero de escritura y el puntero de lectura se mueven de forma circular, con el puntero de escritura a la cabeza y el puntero de lectura a continuación. Las operaciones de escritura y lectura también se denominan operaciones push/pop, put/get o fill/drenaje.

        También podemos realizar operaciones de lectura y escritura al mismo tiempo, porque las dos operaciones usan diferentes punteros, señales de control y buses de datos. Esta operación del FIFO es como un tanque de agua, tiene una entrada para que el agua entre al tanque y una salida para que el agua fluya fuera del tanque. En cualquier momento, el agua puede ser liberada al mismo tiempo.A lo que debemos prestar atención es que, a menos que el FIFO esté vacío o lleno, el puntero de lectura y el puntero de escritura no pueden ser iguales. Necesitamos asegurarnos de que no sucedan dos situaciones, una es escribir datos en un FIFO completo (todas las posiciones tienen datos, no hay posición adicional); la otra es leer datos de un FIFO vacío (no hay datos válidos en el FIFO). Se denominan desbordamiento (overrun) y underflow (underrun) respectivamente. El FIFO genera señales fifo_full y fifo_empty para indicar si el FIFO está lleno o vacío. Cuando el FIFO está lleno, está prohibido escribir datos; cuando el FIFO está vacío, está prohibido continuar leyendo datos.

        Antes de describir en detalle, mejoremos aún más nuestra comprensión a través del diagrama de tiempo, como se muestra en la figura a continuación.

3. Principio FIFO síncrono

         En un FIFO síncrono, se usa un solo reloj para las operaciones de escritura y lectura. El flujo de datos y la lógica de control asociada operan dentro de un dominio de reloj. Los FIFO síncronos se utilizan para almacenar datos temporalmente, donde las operaciones de escritura y lectura pueden ocurrir simultáneamente o en diferentes momentos. Dado que FIFO síncrono solo usa un reloj, su lógica de control es mucho más simple que la de FIFO asíncrono. Algunos puertos de entrada y salida se discutieron anteriormente, y ahora se deben agregar algunas salidas útiles, como fifo_full, fifo_empty, room_disponible, data_disponible. Como se puede ver en el nombre, la señal fifo_full indica que el FIFO está lleno y fifo_empty indica que el FIFO está vacío. Estas dos señales se utilizan como condiciones límite para recordar al circuito externo que no escriba el FIFO completo y lea el FIFO vacío.

        FIFO también puede proporcionar otras señales, como casi_lleno y casi_vacío, que se utilizan para proporcionar información de FIFO lleno y vacío por adelantado. Por ejemplo, cuando quedan de 2 a 3 posiciones vacantes en el FIFO diseñado, la señal de casi_lleno es válida. En este momento, el circuito responsable de la escritura periférica debe considerar dejar de escribir, porque puede pasar un tiempo desde la decisión de detener hasta la señal write_en está desactivada múltiples ciclos de reloj. Si la lógica de escritura espera a que el indicador fifo_full sea válido antes de desactivar la señal write_en, es posible que sea demasiado tarde y que una señal se escriba incorrectamente o se pierda (dependiendo de si existen medidas de seguridad). Almost_empty también funciona de manera similar para evitar el desbordamiento de datos. En comparación con las señalescasi_lleno ycasi_vacío, a veces preferimos usar room_disponible y datos_disponibles para proporcionar información precisa sobre el espacio restante y la profundidad de los datos (el FIFO a continuación de este artículo usa estas dos señales). La lógica de escritura usa room_disponible porque le importa cuánto espacio queda, y la lógica de lectura usa data_disponible porque le importa cuántos datos quedan disponibles para leer.

4. Implementación Verilog de FIFO síncrono

module synch_fifo  #(parameter FIFO_PTR = 4,
                               FIFO_WIDTH = 32,
                               FIFO_DEPTH = 16)
                   (fifo_clk,rstb,
                    fifo_wren,
                    fifo_wrdata,
                    fifo_rden,
                    fifo_rddata,
                    fifo_full,
                    fifo_empty,
                    fifo_room_avail,
                    fifo_data_avail);
//端口声明
//*******************************************************
input                          fifo_clk;
input                          rstb;
input                          fifo_wren;
input   [FIFO_WIDTH - 1 : 0]   fifo_wrdata;
input                          fifo_rden;
input   [FIFO_WIDTH - 1 : 0]   fifo_rddata;
output  reg                    fifo_full;
output  reg                    fifo_empty;
output  reg [FIFO_PTR : 0]     fifo_room_avail;
output  [FIFO_PTR : 0]         fifo_data_avail; 

//变量定义
//*******************************************************
reg [FIFO_PTR-1 : 0]           wr_ptr,wr_ptr_nxt;
reg [FIFO_PTR-1 : 0]           rd_ptr,rd_ptr_nxt;
reg [FIFO_PTR : 0]             num_entries,num_entries_nxt;
reg [FIFO_PTR : 0]             fifo_room_avail;
reg                            fifo_full,fifo_empty;
wire [FIFO_PTR : 0]            fifo_room_avail_nxt;
wire [FIFO_PTR : 0]            fifo_data_avail_nxt;
wire                           fifo_full_nxt,fifo_empty_nxt;

//写指针控制逻辑
//*******************************************************
always@(*) begin
    wr_ptr_nxt = wr_ptr;
    if(fifo_wren) begin
        if(wr_ptr == FIFO_DEPTH - 1)
            wr_ptr_nxt = 0;
        else
            wr_ptr_nxt = wr_ptr + 1;
    end
end

//读指针控制逻辑
//*******************************************************
always@(*) begin
    rd_ptr_nxt = rd_ptr;
    if(fifo_rden) begin
        if(rd_ptr == FIFO_DEPTH - 1)
            rd_ptr_nxt = 0;
        else
            rd_ptr_nxt = rd_ptr + 1;
    end
end

//计算FIFO内的数据量
//*******************************************************
always@(*) begin
    num_entries_nxt = num_entries;
    if(fifo_wren && fifo_rden)
        num_entries_nxt = num_entries;
    else if(fifo_wren)
        num_entries_nxt = num_entries + 1;
    else if(fifo_rden)
        num_entries_nxt = num_entries - 1;
end

//产生提示信号
//*******************************************************
assign fifo_full_nxt = (num_entries_nxt == FIFO_DEPTH);
assign fifo_empty_nxt = (num_entries_nxt == 0);
assign fifo_data_avail = num_entries;
assign fifo_room_avail_nxt = (FIFO_DEPTH - num_entries_nxt);

//寄存器操作
//*******************************************************
always@(posedge fifo_clk or negedge rstb) begin
    if(!rstb) begin
        wr_ptr       <=  0;
        rd_ptr       <=  0;
        num_entries  <=  0;
        fifo_full    <=  0;
        fifo_empty   <=  0;
        fifo_room_avail <=0;
    end
    else begin
        wr_ptr       <=    wr_ptr_nxt;
        rd_ptr       <=    rd_ptr_nxt;
        num_entries  <=    num_entries_nxt;
        fifo_full    <=    fifo_full_nxt;
        fifo_empty   <=    fifo_empty_nxt;
        fifo_room_avail <= fifo_room_avail_nxt;
    end
    
end

//可以自己写一个存储器,也可以直接例化存储器ip(这里选择后者)
//*******************************************************
sram        #(.FIFO_PTR (FIFO_PTR),
              .FIFO_WIDTH (FIFO_WIDTH)) sram_0
            
            (.wrclk(fifo_clk),
             .wren(fifo_wren),
             .wrptr(wr_ptr),
             .wrdata(fifo_wrdata),
             .rdclk(fifo_clk),
             .rden(fifo_rden),
             .rdptr(rd_ptr),
             .rddata(fifo_rddata));

endmodule
            

El contenido anterior proviene de "Tecnología de diseño de sistemas digitales avanzados de Verilog y análisis de ejemplo"

Supongo que te gusta

Origin blog.csdn.net/weixin_45791458/article/details/129936780
Recomendado
Clasificación