2. Verilog realiza la lámpara de agua y la compara con el lenguaje C

1. Plataforma de software y plataforma de hardware

plataforma de software:

1. Sistema operativo: Windows-8.1

2. Equipo de desarrollo: ISE14.7

3. Herramienta de simulación: ModelSim-10.4-SE

Plataforma de hardware:

1. Modelo FPGA: XC6SLX45-2CSG324

2. Introducción al principio

Hay 4 luces LED en mi placa de desarrollo, y el diagrama esquemático es el siguiente:
 inserte la descripción de la imagen aquíEn el diagrama esquemático, se puede ver que el LED se enciende solo cuando el pin correspondiente de la FPGA ingresa un nivel bajo y el efecto de la La luz de agua corriente puede hacer que los cuatro pines correspondientes emitan baja potencia a su vez.

3. Objetivos y tareas

Escriba el código Verilog de los cuatro flujos de LED y simule con ModelSim. Después de pasar la simulación, descárguelo a la placa de desarrollo para probarlo. Es necesario que cada LED de la placa de desarrollo esté encendido durante 1 segundo.

4. Ideas de diseño y escritura de código Verilog

Dado que cada LED está encendido durante 1 s, es natural pensar en generar un reloj de 1 s para impulsar la lógica subsiguiente. Con este reloj de 1 s, la salida del LED se puede cambiar al ritmo de este reloj de 1 s. Operación de bit para producir el Efecto de las luces de agua corriente.

1. Lógica de división de frecuencia del reloj 1s

Dado que el reloj principal es de 50 MHz y el período es de 20 ns, el reloj principal de 50 MHz puede controlar un contador. la salida del divisor de frecuencia se invierte Gire y borre el valor de conteo a 0, de modo que la salida del divisor de frecuencia cambie cada 0,5 s y se genere un reloj de 1 s.

El código de Verilog es el siguiente:

//
// 功能:产生1s的时钟
//
always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_cnt_ls        <= 32'd0 ; 
            R_clk_ls_reg    <= 1'b1  ;
        end 
    else if(R_cnt_ls == 32'd24_999_999)
        begin
            R_cnt_ls        <= 32'd0          ;
            R_clk_ls_reg    <= ~R_clk_ls_reg  ;  
        end
    else
        R_cnt_ls <= R_cnt_ls + 1'b1 ;          
end

assign W_clk_ls = R_clk_ls_reg ;

2. Lógica de cambio

Después de tener la señal de reloj de 1 s, bajo el impulso de la señal de reloj de 1 s, el registro LED de salida se desplaza para producir un efecto de canalización.

El código de Verilog es el siguiente:

//
// 功能:对输出寄存器进行移位产生流水效果
//
always @(posedge W_clk_ls or negedge I_rst_n)
begin
    if(!I_rst_n) 
        R_led_out_reg <= 4'b0001 ; 
    else if(R_led_out_reg == 4'b1000)
        R_led_out_reg <= 4'b0001 ;
    else    
        R_led_out_reg <= R_led_out_reg << 1 ;             
end

assign O_led_out = ~R_led_out_reg ;

El diagrama de tiempo de la simulación se muestra en la siguiente figura: inserte la descripción de la imagen aquí
Puede ver que el tiempo es completamente correcto, el siguiente paso es vincular los pines, generar un archivo de bits y descargarlo a la placa de desarrollo para probarlo.

Sexto, pensar más: la diferencia entre la lámpara de agua en lenguaje C y la lámpara de agua Verilog

Después de leer la serie de publicaciones de blog "Verilog Things" en línea, el autor propuso un método de "operación similar a una secuencia". Después de la introducción, descubrí que realmente tiene el sabor de una "secuencia de imitación". Para publicaciones de blog detalladas, consulte la serie de publicaciones de blog del blogger akuei2 en el jardín del blog. Aquí estoy resumiendo, dejando una impresión para el futuro.

El marco general del código para implementar la lámpara de agua en lenguaje C es el siguiente:

mientras(1)

{

1. Deje que se encienda el primer LED y se apaguen los demás;

2. Retraso 1s

3. Deja que el segundo LED se encienda y los demás se apaguen

4. Retraso 1s

5. Deje que el tercer LED se encienda y los demás se apaguen;

6. Retraso 1s

7. Deje que se encienda el 4º LED y se apaguen los demás

8. Retraso 1s

  }

El código en while(1) se ejecuta línea por línea.Después de ejecutar la última línea, regrese a la primera línea para comenzar una nueva ronda de ejecución. Esto crea el efecto del agua corriente.

Al ver esto, alguien debería entender de repente, ¿no es solo una máquina de estado en Verilog? El código Verilog correspondiente también se puede escribir

siempre @(posege I_clk)

comenzar

     case(R_state)

            第1个状态:让第1个LED亮,其他的灭,下一状态是第2个状态;

            第2个状态:延时1s,下一状态是第3个状态;

            第3个状态:让第2个LED亮,其他的灭,下一状态是第4个状态;

            第4个状态:延时1s,下一状态是第5个状态;

            第5个状态:让第3个LED亮,其他的灭,下一状态是第6个状态;

            第6个状态:延时1s,下一状态是第7个状态;

            第7个状态:让第4个LED亮,其他的灭,下一状态是第8个状态;

            第8个状态:延时1s,下一状态是第1个状态;

            default          : ;

     endcase

fin

El código específico es el siguiente:

//
// 功能:“仿顺序操作”
//
always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_state  <= 3'b000 ; 
            R_cnt_ls <= 32'd0  ;
        end
    else
        begin    
            case(R_state)
                C_S0:
                    begin
                        R_led_out_reg <= 4'b0001 ;
                        R_state       <= C_S1    ;  
                    end
                C_S1:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S2  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S2:
                    begin
                        R_led_out_reg <= 4'b0010 ;
                        R_state       <= C_S3    ;  
                    end
                C_S3:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S4  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S4:
                    begin
                        R_led_out_reg <= 4'b0100 ;
                        R_state       <= C_S5    ;  
                    end
                C_S5:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S6  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S6:
                    begin
                        R_led_out_reg <= 4'b1000 ;
                        R_state <= C_S7 ;  
                    end
                C_S7:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S0  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end 
                default: R_state <= 3'b000 ;                                                                               
            endcase 
        end                  
end

assign O_led_out = ~R_led_out_reg ;

inserte la descripción de la imagen aquí El diagrama de tiempo sigue siendo correcto y el efecto de la lámpara de agua corriente se realiza

Siete Resumen

1. La llamada "operación secuencial" es en realidad una máquina de estado, que logra el efecto de "ejecución secuencial" a través de transiciones de estado. Este tipo de pensamiento sigue siendo muy útil al escribir la sincronización de la interfaz más adelante, y puede pensar mucho en ello en el futuro.

2. El while(1) del lenguaje C es similar al siempre @(posege I_clk) del lenguaje Verilog Mientras exista el reloj de la CPU, seguirán ejecutándose. En el libro se dice que el lenguaje C es un lenguaje serial y Verilog es un lenguaje paralelo. De hecho, también puede experimentarlo aquí: solo puede haber una declaración while (1) en lenguaje C, y la CPU se ejecutará después de ingresar while (1). No se puede salir, y puede haber múltiples declaraciones siempre @ (posege I_clk) en Verilog, y cada siempre @ (posege I_clk) se ejecuta al mismo tiempo. Esta es la mayor diferencia entre los dos idiomas

8. Apéndice

1. División de frecuencia 1s para generar el código completo de la lámpara de agua

module led_work_top
(
    input           I_clk       ,
    input           I_rst_n     ,
    output  [3:0]   O_led_out
);

reg  [31:0]  R_cnt_ls      ;
wire         W_clk_ls      ;
reg          R_clk_ls_reg  ;
reg  [3:0]   R_led_out_reg ;

//
// 功能:产生1s的时钟
//
always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_cnt_ls        <= 32'd0 ; 
            R_clk_ls_reg    <= 1'b1  ;
        end 
    else if(R_cnt_ls == 32'd24_999_999)
        begin
            R_cnt_ls        <= 32'd0          ;
            R_clk_ls_reg    <= ~R_clk_ls_reg  ;  
        end
    else
        R_cnt_ls <= R_cnt_ls + 1'b1 ;          
end

assign W_clk_ls = R_clk_ls_reg ;

//
// 功能:对输出寄存器进行移位产生流水效果
//
always @(posedge W_clk_ls or negedge I_rst_n)
begin
    if(!I_rst_n) 
        R_led_out_reg <= 4'b0001 ; 
    else if(R_led_out_reg == 4'b1000)
        R_led_out_reg <= 4'b0001 ;
    else    
        R_led_out_reg <= R_led_out_reg << 1 ;             
end

assign O_led_out = ~R_led_out_reg ;

endmodule

2. "Imitación de operación secuencial" genera el código completo de la lámpara de agua corriente

module led_work_top
(
    input           I_clk         ,
    input           I_rst_n       ,
    output  [3:0]   O_led_out     
);                                
                                  
reg  [31:0]  R_cnt_ls             ;
reg  [3:0]   R_led_out_reg        ;
reg  [2:0]   R_state              ;

parameter    C_CNT_1S =   32'd49_999_999  ;          

parameter    C_S0     =   3'b000  ,
             C_S1     =   3'b001  ,
             C_S2     =   3'b010  ,
             C_S3     =   3'b011  ,
             C_S4     =   3'b100  ,
             C_S5     =   3'b101  ,
             C_S6     =   3'b110  ,
             C_S7     =   3'b111  ;

//
// 功能:仿顺序操作
//
always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_state  <= 3'b000 ; 
            R_cnt_ls <= 32'd0  ;
        end
    else
        begin    
            case(R_state)
                C_S0:
                    begin
                        R_led_out_reg <= 4'b0001 ;
                        R_state       <= C_S1    ;  
                    end
                C_S1:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S2  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S2:
                    begin
                        R_led_out_reg <= 4'b0010 ;
                        R_state       <= C_S3    ;  
                    end
                C_S3:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S4  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S4:
                    begin
                        R_led_out_reg <= 4'b0100 ;
                        R_state       <= C_S5    ;  
                    end
                C_S5:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S6  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end
                C_S6:
                    begin
                        R_led_out_reg <= 4'b1000 ;
                        R_state <= C_S7 ;  
                    end
                C_S7:
                    begin
                        if(R_cnt_ls == C_CNT_1S)
                            begin
                                R_cnt_ls <= 32'd0 ;
                                R_state  <= C_S0  ;
                            end
                        else
                            R_cnt_ls <= R_cnt_ls + 1'b1 ;                
                    end 
                default: R_state <= 3'b000 ;                                                                               
            endcase 
        end                  
end

assign O_led_out = ~R_led_out_reg ;

endmodule

3. El código completo del archivo de registro de prueba.

module tb_led_work_top;

    // Inputs
    reg I_clk;
    reg I_rst_n;

    // Outputs
    wire [3:0] O_led_out;

    // Instantiate the Unit Under Test (UUT)
    led_work_top U_led_work_top (
        .I_clk(I_clk), 
        .I_rst_n(I_rst_n), 
        .O_led_out(O_led_out)
    );

    initial begin
        // Initialize Inputs
        I_clk = 0;
        I_rst_n = 0;

        // Wait 100 ns for global reset to finish
        #100;
        I_rst_n = 1;
        
        // Add stimulus here

    end
    
    always #5 I_clk = ~I_clk ;
      
endmodule

Supongo que te gusta

Origin blog.csdn.net/weixin_45104510/article/details/129425171
Recomendado
Clasificación