记录最简单的Verilog模块所需要的基础知识
#Verilog基本语法
#Verilog模块编写
Verilog基本语法
数据类型
Verilog中没有多种的数据类型,只有数据的进制。其表示方法为
【长度】+'+【进制缩写】+【数据】
例如2'b01表示两位的二进制数01,10'h1f0表示10位16进制数1f0
运算符
和一般编程语言的运算符相差不大。
最常见的一处不同,是在给寄存器等赋值的时候,一般使用<=非阻塞赋值。该赋值操作将在块结束后才进行,这样更符合在进行时序逻辑设计时设计者的思维。而阻塞赋值=,在该语句处立即执行。
声明
output——定义输出
input——输入
inout——输入输出
wire——相当于一个逻辑量bool,一般会生成一个逻辑门,该变量为逻辑门的输出
reg——寄存器变量,相当于一般意义上的变量
net、tri——略
还可以声明数组,例如
reg[7:0] sd; //7位寄存器sd
reg[1:0] cnt[1:0]; //2位寄存器cnt[0]和cnt[1]
Verilog允许不同类型的变量同名,但同名变量之间存在着逻辑关系。例如向同名的reg类型变量赋值,则数据会在同名的output变量中被发送出去。
assign语句
assign语句用于将某个变量置位。该语句的效果是实时的,也就是说任何时候,只要assign中的条件满足,assign的对象就会按要求被置为相应的位。assign可以写在时钟跳变以外,即不依赖于触发器工作,这也是其实时更新的原因。
assign的对象一般有wire,reg,output,例如:
assign oe = (sa==10'h1f5)?0:1;
该语句表明,输出量oe只要在地址总线寻址1f5的时候就被拉低。
always结构语句
always@(posedge clk)
表示在时钟上升沿到来时,其下可以接一系列操作,相当于一系列以clk为时钟源的触发器。
需要注意的是不能有两个always同时控制同一个变量,即同一个触发器不能接入两个时钟源。
条件语句
case(sa)
10'h1f0 : hour_h <= sd_in;
10'h1f1 : hour_l <= sd_in;
10'h1f2 : minute_h <= sd_in;
10'h1f3 : minute_l <= sd_in;
default : ;
endcase
if(!btn_1)btn_cnt[0] <= btn_cnt[0]+1;
其他
在需要在always或if等后方要接语句块进行一系列操作的时候,需要用begin-end将其框起来,相当于大括号
Verilog模块编写
模块开头先声明模块名字和包含的变量,以便其他模块调用
module timer(btn,sa,sd_out,io_w,oe);
括号内需要包含input,output,inout等需要与外部交互的变量名,wire,reg等内部变量不需要。同时这些变量都要出现在.ucf文件中进行管脚定义。然后声明变量。
output oe;
input[9:0] sa;
output[7:0] sd_out;
input btn;
input io_w;
进行逻辑设计
reg[7:0] sd_out; //声明了同名的寄存器变量
wire cs; //wire型的逻辑变量
assign oe = (sa==10'h1f3)?0:1; //访问1f3地址,则使能输出
assign cs = (oe==0)?1:0; //同时拉高cs
always@(posedge io_w)
begin
if(cs&&(!btn))sd_out <= 8'h1; //因为是同名变量,所以向寄存器sd_out赋值,可以控制输出脚sd_out的输出
else sd_out <= 8'h0;
end
在上位机读地址1f3的时候,从数据总线sd_out发送按键输入btn的状态。
最后加一句
endmodule
表示模块的结束。
#在xilinx ise 14.7中进行如上模块编写的时候,会报always@(posedge io_w)错误。原因可能是ise不推荐使用io_w作为触发信号。这个问题可以通过在.ucf文件中加入
NET "io_w" CLOCK_DEDICATED_ROUTE = FALSE;
这样虽然仍然会有警告,但能通过编译。
#有时候尽管编写的模块看起来比较简单,但ise会一直在布线阶段加载,这可能是某一个变量跨了两个块的原因,导致复杂度大大增加(?)。