学习FPGA编程,学习语法是编程的第一步的,今天就侃侃硬件仿C的硬件描述语言---verilog基本语法!
1.模块化编写输入输出接口:
module cmd_ctr (//module 模块名
clk,rst_n,enable,
h2l,data_in,end_cmd);
注意: 模块名与保存文件名cmd_ctr .v必须一致,编程编一个模块除了有你需要输出管脚,输入端通常还需要添加异步复位信号rst_n,同步时钟信号clk,以及开启触发模块工作的enable信号,输出端还有模块工作完成信号end_cmd。
2.各信号类型定义:
input clk;
input rst_n;
input enable;
input h2l;
input[7:0] data_in;
output dataout;
parameter t1ms=20'd49_999;//1000000ns/20ns=50000=1024*1024
reg[19:0] count;
reg iscount;
wire clock;
input与output定义输入输出信号,parameter用于定于参数类型为常数,wire用于信号或者模块之间的连线,reg为寄存器信号,用于暂存数据,区别reg有时是不能替代wire类型的,比如用作下个模块的时钟信号。
3.简单的always编写:
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
rdata<=4'b0001;
end
else if(dataend)
begin
case(datain)
8'h1d:rdata<={rdata[2:0],rdata[3]};
8'h22:rdata<={rdata[0],rdata[3:1]};
8'h14:rdata<={rdata[0],rdata[1],rdata[2],rdata[3]};
endcase
end
跟VHDL一样,verilog也是由一个一个的进程构成,只是构成的方式不同,VHDL由多个process组成,而verilog由多个always组成,而且编写的形式还是比较固定的,当然在编写时一定要有编写C语言的思维,因此一定要会用if,case语句。而且要养成每个case或者if后面一定要紧跟一个begin...........end语句,养成良好的编写习惯。
4.verilog编程两大利器之一:计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
count<=13'd0;
end
else if(count==13'd4999)
begin
count<=13'd0;
end
else
begin
count<=count+1'b1;
end
assign clock=(count==13'd2499)?1'b1:1'b0;
别看着计数器是一个简单的东西,其实,这是做fpga项目人最常用到的利器,比如很多器件(如sdram)开启时启动时间需要200us的开启时间,那么这是时间怎么来,当然是计数器完成,所以计数必定是fpga编程的利器。
5.verilog编程两大利器之二:状态机
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or negedge rst_n) //异步复位
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state) begin //电平触发
next_state = x; //要初始化,使得系统复位后能进入正确的状态
case(current_state)
S1 : if(...)
next_state = S2;
default : next_state = IDLE; //默认是初始化状态
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @ (posedge clk or negedge rst_n)
case(next_state)
S1:
out1 <= 1'b1
S2:
out2 <= 1'b1;
default:... //default的作用是免除综合工具综合出锁存器
endcase
end
状态机在我常写的代码中非常常用,但是由于常常写的很长,所以就贴了贴网上常用的三段式状态机模板,我一向是支持三段式状态机,因为这样很符合思维逻辑性的进程。
6.顶层模块编写:
module topallkey(clk,rst_n,keyin,clock,dataout);
input clk;
input rst_n;
input wire keyin;
output clock;
wire dataend;
output dataout;
wire[7:0] ps2_data;
top_ps2keyboard u3(.clk(clk),
.rst_n(rst_n),
.ps2_keyin(keyin),
.ps2_data(ps2_data),
.ps2_end(dataend),
.clock(clock));
ctr u2(.clk(clk),
.rst_n(rst_n),
.datain(ps2_data),
.dataend(dataend),
.dataout(dataout));
endmodule
以上是将ctr.v模块和top_ps2keyboard.v模块例化成u1和u2模块,.rst_n和.ps2_keyin为模块的引脚,其后面括号里面的信号就是例化模块的引脚,用于连接到整个模块的输出,两个例化模块的连接用wire信号相连。
7.常规问题:
“<=”与“=”的区别,好多书上都说阻塞与不阻塞,其实不必要讲的那么高深,其实就是“<=”需要时钟clk控制,“=”则不需要clk控制,直接可赋值。
clk与rst_n要着有何用,clk是编程fpga心脏跳动的源泉,rst_n是异步复位阻止程序乱拍的信号。
wire与reg区别,wire只是用于连线,reg用于寄存数据,当用于连线时只能用wire,当需要寄存数据时只能用reg。