AC620FPGA学习笔记——Verilog语法
Verilog语法
对于数字逻辑电路,主要有组合逻辑和时序逻辑
组合逻辑:多路选择器、译码器、加法器、乘法器等等
时序逻辑:计数器
module模块
module mux2(a,b,sel,out);
endmodule
module: 模块声明(表示module到endmodule之间的内容是描述模块的代码)
mux2: 模块名字(自定义,相当于此模块的名称)
(a,b,sel,out): 括号内是端口列表(类似芯片的引脚定义)
端口类型与位宽
module mux2(a,b,sel,out,io);
input a;
input b;
input sel;
output out;
inout io;
endmodule
input a: 表示端口a是输入端口(类似芯片的输入引脚)
output b: 表示端口b是输出端口(类似芯片的输出引脚)
inout io: 表示端口io是双向端口(类似芯片的双向引脚)
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
endmodule
与前两个例子功能相同,只是另一种写法,将端口的输入输出描述放入括号内完成
注:input [7:0] a 表示输入端口是一个8bit的总线,相当于8根导线,并且编号为:7 6 5 4 3 2 1 0
内部信号
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
reg ow;
endmodule
wire oe; 表示名称为oe内部信号(相当于芯片内部的一根导线)
reg ow; 表示名称为ow内部信号(相当于芯片内部的一位寄存器)
注:导线不存在存储功能,只有连接功能,所以对于时序逻辑电路无法使用wire型的信号,而组合逻辑电路无法使用reg型变量,但在Verilog语法中任何处于always中的信号均应为reg型
连续赋值语句
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
assign out = sel ? a : b;
assign oe = sel;
assign io = oe ? out[0] : 1'bz;
endmodule
assign out = xxx: 将等号右边组合逻辑(xxx)的输出与导线out相连接(对于连续赋值语句assign,只能是wire型的信号)
sel ? a : b: 组合逻辑,sel 为1 输出a 反之输出b
1’bz: 一个高阻态(z)信号
4’b1001; 表示4位二进制1001
4’d9; 表示4位十进制9
4’hc; 表示4位十六进制c
32’hff_00_ff_00 表示32位十六进制ff00ff00
位操作
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
assign out = sel ? a : b;
assign oe = sel;
assign io = oe ? out[0] : 1'bz;
wire [2:0]m;
assign m = out[5:3];
//reg [7:0]shift_a;
//shift_a <= {shift_a[0],shift_a[7:1]};
wire [3:0] x;
wire [3:0] y;
wire [7:0] z;
z = {x,y};
z = {2{x}};
endmodule
截取
assign m = out[5:3]; 将out总线编号为:5 4 3的导线连接到总线m上(默认连接到m的 2 1 0号导线上)
移位
shift_a <= {shift_a[0],shift_a[7:1]}; 时序逻辑内的操作,每次右移一位(循环右移)。
将shift_a总线重新拼接
右移0次shift_a的导线编号:7 6 5 4 3 2 1 0
右移1次shift_a的导线编号:0 7 6 5 4 3 2 1
右移2次shift_a的导线编号:1 0 7 6 5 4 3 2
.
.
.
拼接
z = {x,y}; 将x与y总线进行拼接
拼接前: x( 3 2 1 0 ) y( 3 2 1 0 )
拼接后: z( 7 6 5 4 3 2 1 0 )
其中z[7]与x[3]相接
z[3]与y[3]相接,以此类推
z = {2{x}}; 等效于z = {x,x};
同理z = {3{x}};等效于z = {x,x,x};
运算
对于语法来说+ - * / 四则运算是可以通过的
但是在实际应用中由于资源有限,对于乘法和除法尽量使用加减法和移位代替,尤其是除法操作,可以使用IP核
逻辑运算
&& 逻辑与
|| 逻辑或
! 逻辑非
& 按位与
| 按位或
~ 按位非
时序逻辑
module counter(
clk,
en,
clr,
cnt_value
);
input clk; //时钟信号
input en; //使能信号
input clr; //清零信号
output [3:0] cnt_value;
reg [3:0] cnt; //4位计数用寄存器
always@(posedge clk or posedge clr) //clk上升沿触发 或 clr上升沿触发
if(clr)
cnt <= 4'b0;
else if(en) begin
cnt <= cnt + 1'b1;
end
assign cnt_value = cnt;
endmodule
always@(posedge clk or posedge clr) 表示时序逻辑部分,posedge为上升沿,negedge为下降沿
注:同一个信号禁止同时出现上升与下降的触发
Test Bench
用于对编写好的moudle进行功能测试所编写的逻辑,仅用在仿真验证环节
`timescale 1ns/1ns
`define clk_period 10
module counter_tb;
reg clk_t;
reg en_t;
reg clr_t;
wire [7:0]cnt_value_t;
counter counter_1(
.clk(clk_t),
.en(en_t),
.clr(clr_t),
.cnt_value(cnt_value_t[7:4])
);
counter counter_2(
.clk(clk_t),
.en(en_t),
.clr(clr_t),
.cnt_value(cnt_value_t[3:0])
);
initial clk_t = 1; //初始化clk_t的值
always #(`clk_period/2) clk_t = ~clk_t;
//always表示循环
initial begin
en_t = 0;
clr_t = 0;
#(`clk_period*20) //延时
ent_t = 1;
#(`clk_period*200)
ent_t = 0;
#(`clk_period*200)
clr_t = 1;
#(`clk_period*200)
clr_t = 0;
#(`clk_period*200)
$stop;
end
endmodule
timescale 第一个参数表示步进,第二个表示精度,一般写成相同的
`define clk_period 10 重定义,与C语言相同
counter counter_1 类似C++中类的实例化,counter是类名,counter_1是实例化后的名称
随机数
myrand = {$random};
myrand2 = {$random} % 65536; // 0 - 65535
myrand3 = $random % 65536; //-65535 - 65535
花括号类似绝对值
测试Task
task press_key
begin
...
end
end task
类似C语言中的函数,press_key是函数名
调用的时候很简单,如下:
always@(posedge press)
press_key;
循环repeat
repeat(50) begin
myrand = {$random} % 65536;
#myrand key = ~key;
end
循环指定次数,例子中是循环50次