参数parameter:
parameter可以用标识符定义常量,运用时只使用标识符即可,提高可读性及维护性。
参数的传递:在一个模块中如果有定义参数,在其他模块调用此模块时可以传递参数,并可以修改参数,如下所示,在module后用#()表示。
//定义的模块
module rom #(
parameter depth =16,
parameter width = 8
)
(
input [depth-1:0] addr ,
input [width-1:0] data ,
output result
);
endmodule
//调用模块
module top() ;
wire [31:0] addr ;
wire [15:0] data ;
wire result ;
rom
#(
.depth(32),
.width(16)
)
rom rom_inst
(
.addr(addr) ,
.data(data) ,
.result(result)
);
endmodule
Parameter可以用于模块间的参数传递,而localparam仅用于本模块内使用,不能用于参数传递,而Localparam多用于状态机状态的定义。
变量
wire型
Wire类型变量,也叫网络类型变量,用于结构实体之间的物理连接,,用连续赋值语句assign赋值。
Reg型
Reg 类型变量,也称为寄存器变量,可用来储存值,必须在always语句里使用。也可以生成组合逻辑,,敏感信号没有时钟。
Memory型
可以用memory类型来定义RAM,ROM等存储器,其结构为reg [n-1:0] 存储器名[m-1:0],意义为m个n位宽度的寄存器,m为存储器的深度,n为数据宽度。
赋值运算符
“=”阻塞赋值,”<=”非阻塞赋值。阻塞赋值为执行完一条赋值语句,再执行下一条,可理解为顺序执行,而且赋值是立即执行;非阻塞赋值可理解为并行执行,不考虑顺序,在always块语句执行完成后,才进行赋值。
一般情况下,在时序逻辑电路中使用非阻塞赋值,可避免仿真时出现竞争冒险现象;在组合逻辑中使用阻塞赋值,执行赋值语句后立即改变;在assign语句中必须用阻塞赋值。
if (!a)表示条件为a的值不为1,也就是0。
拼接运算符
“{ }”拼接运算符,将多个信号按位拼接,如{a[3:0], b[2:0]},将a的低4位,b的低3位拼接成7位数据。另外,{n{a[2:0]}}表示将n个a[2:0]拼接,{n{1’b0}}表示n位的0拼接。如{8{1’b0}}表示为8’b0000_0000。
带异步复位的D触发器
异步复位是指独立于时钟,一旦异步复位信号有效,就触发复位操作,用于给信号复位,初始化。
always @(posedge clk or negedge rst)
begin
if (rst == 1'b0)
q <= 0 ;
else
q <= d;
end
带异步复位同步清零的D触发器
异步复位独立于时钟操作,而同步清零则是同步于时钟信号下操作的。不同于异步复位,同步操作不能把信号放到敏感列表里。
always @(posedge clk or negedge rst)
begin
if (rst == 1'b0)
q <= 0 ;
else if (clr == 1'b1)
q <= 0 ;
else
q <= d ;
end
移位寄存器
always @(posedge clk or negedge rst) begin
if (rst == 1'b0)
q <= 0 ;
else
q <= {q[6:0], d} ; //向左移位
//q <= {d, q[7:1]} ; //向右移位
end
单口RAM
单口RAM的写地址与读地址共用一个地址。
伪双口RAM
伪双口RAM的读写地址是独立的,可以随机选择写或读地址,同时进行读写操作。
真双口RAM
真双口RAM有两套控制线,数据线,允许两个系统对其进行读写操作。
单口ROM
ROM是用来存储数据的,可以按照代码形式初始化ROM,但这种方法处理大容量的ROM就比较麻烦,建议用FPGA自带的ROM IP核实现,并添加初始化文件。
有限状态机
在verilog里经常会用到有限状态机,处理相对复杂的逻辑,设定好不同的状态,根据触发条件跳转到对应的状态,在不同的状态下做相应的处理。有限状态机主要用到always及case语句。
Mealy有限状态机,输出不仅与当前状态有关,也与输入信号有关,在RTL中会与输入信号有连接。Moore有限状态机,输出只与当前状态有关,与输入信号无关,输入信号只影响状态的改变,不影响输出。
采用一段式状态机的写法,只用一个always语句,所有的状态转移,判断状态转移条件,数据输出都在一个always语句里,缺点是如果状态太多,会使整段程序显的冗长。采用三段式状态机的写法,状态转移用一个always语句,判断状态转移条件是组合逻辑,采用一个always语句,数据输出也是单独的 always语句,这样写起来比较直观清晰,状态很多时也不会显得繁琐。
采用Moore状态机三段式的写法设计一个8位的移位寄存器:
module top(
input shift_start,
input shift_stop ,
input rst ,
input clk,
input d,
output reg [ 7:0] q
);
parameter Idle = 2'd0 ; // Idle state
parameter Start = 2'dl ; // start state
parameter Run = 2'd2 ; // Run state
parameter Stop = 2'd3 ; // stop state
reg [1:0] current_state ; // statement
reg [1:0] next_state ;
reg [4:0] delay_cnt ; // delay counter
//First part: statement transition
always @(posedge clk or negedge rst)begin
if(!rst)
current_state <= Idle ;
else
current_state <= next_state ;
end
//Second part: combination logic, judge statement transition condition
always @(*)
begin
case (current _state)
Idle: begin
if(shift_start)
next_state <=start ;
else
next_state <=Idle ;
end
start : begin
if(delay cnt == 5'd99)
next_state <=Run ;
else
next_state <=start ;
end
Run: begin
if(shift_stop)
next_state <=stop ;
else
next_state <=Run ;
end
stop:
next_state <=Idle ;
default:
next_state <=Idle ;
endcase
end
//Last part : output data
always @ (posedge clk or negedge rst) begin
if( !rst)
delay_cnt <= 0 ;
else if (current_state == Start)
delay cnt <=delay cnt +1 'b1 ;
else
delay cnt <=0 ;
end
always @(posedge clk or negedge rst) begin
if(!rst)
q <= 0 ;
else if( current_state == Run)
q <={q[6:0], d};
else
q <=0;
end
endmodule
激励文件:
`timescale 1 ns/1 ns
module top_tb ();
reg shift_start ;
reg shift_stop ;
reg rst ;
reg clk:
reg d ;
wire [7:0]a ;
initial
begin
rst = 0 :
clk = 0 ;
d = 0 ;
#200 rst = 1 ;
forever
begin
#( { $random} %100)//产生0~99的随机数
d = ~d ;
end
end
initial
begin
shift_start = 0 ;
shift_stop = 0 ;
#300 shift_start = 1 ;
#1000 shift_start =0 ;
shift_stop =1 ;
#50 shift_stop = 0 ;
end
always #10 clk = ~clk ;
top t0
(
.shift_start (shift_start) ,
.shift_stop (shift_stop) ,
.rst (rst) ,
.clk (clk) ,
.d (d) ,
.q (q)
) ;
endmodule