verilog基础模块
verilog基础模块包括数据类型,运算符,组合逻辑和时序逻辑四个部分。数据类型包括常量和变量,在常量中有整数,X和Z以及参数。X代表不定制,Z代表高阻值。下划线不具有任何意义,是用来分割位数,提高程序可读性,8‘b0000_如1111。常量中的参数parameter可以用标识符定义常量,运用时只使用标识符即可,提高程序可读性及维护性,如定义parameter width=8;定义寄存器reg[width-1:0]a;即定义了8位宽度的寄存器。
参数传递:在一个模块中如果有定义参数,在其他模块调用此模块时可以传递参数,并可以修改参数,如下所示,在module后用#()表示。
module rom
#(
parameter depth=15,
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),
.witdth(16)
)
r1
(
.addr(addr),
.data(data),
.result(result)
);
endmodule
parameter可以用于模块间的参数传递,而localparam仅用于本模块内使用,不能用于参数传递。localparam多用于状态机状态的定义。
数据类型中的变量有Wire型,Reg型,Memory型。wire类型变量,也叫网络类型变量,用于结构实体之间的物理连接,如门与门之间,不能存储值,用连续赋值语句assign赋值,定义为wire[n-1:0]a;其中n代表位宽,如定义wire;assign a=b;是将b的节点连接到连线a上。Reg类型变量,也称为寄存器变量,可用来储存值,必须在always语句里使用(值变化需时钟)。 其定义为reg[n-1:0]a;表示n位位宽的寄存器,如reg[7:0]a;表示定义8位位宽的寄存器a。也可以生成组合逻辑,如数据选择器,敏感信号没有时钟。可以用memory类型来定义RAM,ROM等存储器,其结构为reg[n-1:0]存储器名[m-1:0],表示为m个n位深度的寄存器。
运算符包括(1)算术运算符(+,-,*,/,%);(2)赋值运算符(=,<=);(3)关系运算符(>,<,>=,<=,==,!=);(4)逻辑运算符(&&,||,!);(5)条件运算符(?:);(6)位运算符(~,|,^,&,^~);(7)移位运算符(<<,>>);(8)拼接运算符({})。
这里面着重探讨一下赋值运算符“=”和“<=”。“=”为阻塞赋值,“<=”非阻塞赋值。阻塞赋值为执行完一条赋值语句,再执行下一条,可理解为顺序执行,而且赋值是立即执行;非阻塞赋值可理解为并行执行,不考虑顺序,在always块语句执行完成后,才进行赋值。一般情况下,在时序逻辑电路中使用非阻塞赋值,可避免仿真时出现竞争冒险现象;在组合逻辑中使用阻塞赋值,执行赋值语句后立即改变;在assign语句中必须用阻塞赋值。
组合逻辑电路的特点时任意时刻的输出仅仅取决于输入信号,输入信号变化,输出立即变化,不依赖于时钟。如与门“&”;或门“|”;非门“~”;异或门“^”;比较器;半加器(半加器和全加器时算术运算电路中的基本单元,由于半加器不考虑从低位来的进位,所以称之为半加器);全加器;乘法器;数据选择器(通过选择信号,选择不同的输入信号输出到输出端);3-8译码器;三态门。
时序逻辑电路在逻辑功能上特点是任意时刻的输出仅仅取决于当前时刻的输入,与电路原来的状态无关。而时序逻辑在逻辑功能上的特点是任意时刻的输出不仅仅取决于当前的输入信号,而且取决于电路原来的状态。如D触发器(D触发器在时钟的上升沿或下降沿存储数据,输出与时钟跳变之前输入信号的状态相同。)两级D触发器,带异步复位的D触发器(异步复位是指独立于时钟,一旦复位信号有效,就触发复位操作。);异步复位同步清零的D触发器;移位寄存器(移位寄存器是指在每个时钟脉冲来时,向左或向右移动一位,由于D触发器的特性,数据输出同步于时钟边沿,每个时钟来临,每个D触发器的输出q等于前一个D触发器输出的值,从而实现移位的功能)。
然后是几位重磅级的时序逻辑电路:
1.单口RAM:单口RAM的写地址与读地址公用一个地址,代码如下,其中reg[7:0]ram[63:0]意思是定义了64个8位宽度的数据。其中定义了addr_reg,可以保持住读地址,延迟一周期之后将数据送出。
module top
(
input[7:0]data,
input[5:0]addr,
input wr,
input clk,
output[7:0] q
);
reg[7:0] ram[63:0]; //declare ram
reg[5:0] addr_reg; //addr register
always @(posedge clk)
begin
if(wr)
ram[addr]<=data;
addr_reg<=addr;
end
assign q=ram[addr_reg];
endmodule
2.伪双口RAM:伪双口RAM的读写地址是独立的,可以随机选择写或读地址,同时进行读写操作。代码如下
module top
(
input[7:0] data,
input[5:0] write_addr,
input[5:0] read_addr,
input wr,
input rd,
input clk,
output reg[7:0] q
);
reg [7:0] ram[63:0]; //declare ram
reg [5:0] addr_reg; //addr register
always @(posedge clk)
begin
if(wr)
ram[write_addr]<=data;
if(rd)
q<=ram[read_addr];
end
endmodule
3.真双口RAM:真双口RAM有两套控制线,数据线,允许两个系统对其进行读写操作,代码入下:
module top
(
input [7:0] data_a,data_b,
input [5:0] addr_a,addr_b,
input wr_a,wr_b,
input rd_a,rd_b,
input clk,
output reg[7:0] q_a,q_b
);
reg[7:0] ram[63:0]; //declare ram
//port A
always @ (posedge clk)
begin
if(wr_a)
begin
ram[addr_a]<=data_a;
q_a<=data_a;
end
if(rd_a)
q_a<=ram[addr_a];
end
//port B
always @ (posedge clk)
begin
if(wr_b)
begin
ram[addr_b]<=data_b;
q_b<=data_b;
end
if(rd_b)
q_b<=ram[addr_b];
end
endmodule
4.单口ROM:ROM是用来存储数据的,可以按照下列代码形式初始化ROM,但这种方法处理大容量的ROM就比较麻烦,建议用FPGA自带的ROM IP核实现,并添加初始化文件。代码实现:
module top
(
input [3:0] addr,
input clk,
output reg [7:0] q
);
reg [7:0] rom [15:0]; //declare rom
always @(addr)
begin
case(addr)
4’d0:rom[addr]=8'd15;
4'd1:rom[addr]=8'd24;
4'd2:rom[addr]=8'd100;
4'd3:rom[addr]=8'd78;
4'd4:rom[addr]=8'd98;
4'd5:rom[addr]=8'd105;
4'd6:rom[addr]=8'd86;
4'd7:rom[addr]=8'd254;
4'd8:rom[addr]=8'd76;
4'd9:rom[addr]=8'd35;
4'd10:rom[addr]=8'd120;
4'd11:rom[addr]=8'd85;
4'd12:rom[addr]=8'd37;
4'd13:rom[addr]=8'd19;
4'd14:rom[addr]=8'd22;
4'd15:rom[addr]=8'd67;
endcase
end
always @(posedge clk)
begin
q<=rom[addr];
end
endmodule
5.有限状态机:在verilog里经常会用到有限状态机,处理相对复杂的逻辑,设定好不同的状态,根据触发条件跳转到对应的状态,在不同的状态下做相应的处理。有限状态机主要用到always及case语句。状态机有两种写法:一段式和三段式。一段式只用一个always语句,所有的状态转移,判断状态转移条件,数据输出都在一个always语句里,缺点是如果状态会多,会使整段程序显得冗长。三段式写法,状态转移用一个always语句,判断状态转移条件式组合逻辑,采用了一个always语句,数据输出也是单独得always语句,这样写起来比较直观清晰,状态很多时也不会显得繁琐。
Mealy有限状态机,输出不仅与当前状态有关,也与输入信号有关,一段式写法如下:
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'd1; //Start state
parameter Run =2'd2; //Run state
parameter Stop =2'd3; //Stop state
reg[1:0] state; //statement
reg[4:0] delay_cnt; //delay counter
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
state<=Idle;
delay_cnt<=0;
q<=0;
end
else
case(state)
Idle: begin
if(shift_start)
state<=Start;
end
Start:begin
if(delay_cnt==5'd99)
begin
delay_cnt<=0;
state<=Run;
end
else
delay_cnt<=delay_cnt+1'b1;
end
Run :begin
if(shift_stop)
state<=Stop;
else
q<={q[6:0],d};
end
Stop:begin
q<=0;
state<=Idle;
end
default :state<=Idle;
endcase
end
endmodule
Moore有限状态机,输出只与当前状态有关,与输入信号无关,输入信号只影响状态得改变,不影响输出,比如对delay_cnt和q得处理,只与state状态有关。三段式写法代码如下
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 State=2'd1; //Start state
parameter Run=2'd2; //Run state
parameter Stop=2'd3; //Stop state
reg[1:0] currernt_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
基础模块finish!