FPGA设计的一个难点,就是设计技巧和规则的掌握,下面来看一个简单例子的实现过程,作为初级入门案例。
一、按键按下实现LED点亮
当LED引脚输出低电平时LED点亮,
(1)代码实现如下
module test(clk,rst_n,led);input clk ; input rst_n ; output reg led ; always @ ( posedge clk or negedge rst_n) begin if(!rst_n) begin led<=0; end else begin led<=1; end end endmodule
复制代码(2)基础语法要点:
在always里面被赋值必须是reg变量。
凡是在时序电路中被赋值的变量 必须是非阻塞赋值。always @ ( posedge clk or negedge rst_n)
凡是在组合电路中被赋值的变量 必须是阻塞赋值always @ ( *)
(3)Modelsim仿真测试文件。`timescale 1 ps/ 1 psmodule test_tb(); reg clk; reg rst_n; wire led; test i1 ( .clk(clk), .led(led), .rst_n(rst_n) ); initial begin $display("Running testbench"); clk=0; rst_n=0; #1000 rst_n=1; end always #10 clk=~clk; endmodule
复制代码- 输入都改为reg(系统产生)
- 输出都改为wire:连线(系统产生)
- 实例化模块并连接导线。(系统产生)
- initial中初始化变量。(自己修改)
- always产生时钟。(自己修改)
(4)Modelsim仿真
仿真中常用命令:
do wave.do
restart
run 0.1ms
(5)如果点亮四个LED呢?
改变led输出变量的位宽:
module test(clk,rst_n,led);input clk ; input rst_n ; output reg [3:0]]led ; always @ ( posedge clk or negedge rst_n) begin if(!rst_n) begin led<=4'b1111; end else begin led<=4'b0000; end end endmodule
复制代码同样改变测试文件位宽
wire [3:0] led ;
二、状态机设计实现流水灯
(1)模块代码
module test(clk,rst_n,led);input clk ; input rst_n ; output reg [3:0]led ; reg[1:0] state; always @ ( posedge clk or negedge rst_n) begin if(!rst_n) begin led<=4'b1111; state<=0; end else begin case(state) 0:begin led<=4'b0111; state<=1; end 1:begin led<=4'b1011; state<=2; end 2:begin led<=4'b1101; state<=3; end 3:begin led<=4'b1110; state<=0; end default: state<=0; endcase end end endmodule
复制代码
(3)设计要点
注意state变量的赋初值。
(4)频率反转太快、看不出LED效果。
改善方法1:
状态机中增加计数器。
module test(clk,rst_n,led);input clk ; input rst_n ; output reg [3:0]led ; reg[1:0] state; reg [3:0] counter; always @ ( posedge clk or negedge rst_n) begin if(!rst_n) begin led<=4'b1111; state<=0; counter<=0; end else begin case(state) 0:begin led<=4'b0111; if(counter<12) counter<=counter+1; else begin counter<=0; state<=1; end end 1:begin led<=4'b1011; if(counter<12) counter<=counter+1; else begin counter<=0; state<=2; end end 2:begin led<=4'b1101; if(counter<12) counter<=counter+1; else begin counter<=0; state<=3; end end 3:begin led<=4'b1110; if(counter<12) counter<=counter+1; else begin counter<=0; state<=0; end end default: state<=0; endcase end end endmodule
复制代码
同一变量不能同时在两个always块内复制,如分频和状态机的两个always块,复位时复位什么变量由本块内用到的变量决定。
基础十分重要,是FPGA设计的前提。通过以上学习我们学会了状态机 计数器 时钟分频电路的设计。
以上代码中包含了分频,LED状态机等多个功能模块。
module test(clk,rst_n,led);input clk ; input rst_n ; output reg [3:0]led ; reg[1:0] state; reg [3:0] counter; reg clk_show; always @ ( posedge clk or negedge rst_n) begin if(!rst_n) begin clk_show<=0; counter<=0; end else if(counter<12) counter<=counter+1; else begin counter<=0; clk_show=~clk_show; end end always @ ( posedge clk_show or negedge rst_n) begin if(!rst_n) begin led<=4'b1111; state<=0; end else begin case(state) 0:begin led<=4'b0111; state<=1; end 1:begin led<=4'b1011; state<=2; end 2:begin led<=4'b1101; state<=3; end 3:begin led<=4'b1110; state<=0; end default: state<=0; endcase end end endmodule 测试: `timescale 1 ps/ 1 ps module test_tb(); // constants // general purpose registers // test vector input registers reg clk; reg rst_n; // wires wire [3:0]led; // assign statements (if any) test i1 ( // port map - connection between master ports and signals/registers .clk(clk), .led(led), .rst_n(rst_n) ); initial begin // code that executes only once // insert code here --> begin // --> end $display("Running testbench"); clk=0; rst_n=0; #1000 rst_n=1; end always #10 clk=~clk; endmodule
复制代码