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:
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:
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 ;
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