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 ps
module 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产生时钟。(自己修改)
仿真中常用命令:
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)模块代码
reg[1:0] state;
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
(2)状态机正确仿真:
(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
12的时候应该反转为什么延时一个周期呢?
因为是寄存器,等于是一个触发器,Q输出端等于get输入端 在上升沿来的时候
改善方法2:
分频,控制上升沿。
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
同一变量不能同时在两个always块内复制,如分频和状态机的两个always块,复位时复位什么变量由本块内用到的变量决定。
基础十分重要,是FPGA设计的前提。通过以上学习我们学会了状态机 计数器 时钟分频电路的设计。
以上代码中包含了分频,LED状态机等多个功能模块。
下一节我们来讲这个程序改善为:层次化设计、自顶向下的设计模式。