目录
微信公众号获取更多FPGA相关源码:
1.if 和 if-else 语句:
- 可以多层嵌套。在嵌套if序列中,else和前面最近的if相关。
- 为提高可读性及确保正确关联,使用begin…end块语句指定其作用域。
描述方式:
if (表达式)
begin
……
end
else
begin
……
end
举例:
always #20
if (index > 0) // 开始外层 if
if (rega > regb) // 开始内层第一层 if
result = rega;
else
result = 0; // 结束内层第一层 if
else
if (index == 0)
begin
$display(" Note : Index is zero");
result = regb;
end
else
$display(" Note : Index is negative");
2.条件语句(case分支语句)
在Verilog中重复说明case项是合法的,因为Verilog的case语句只执行第一个符合项。
case语法:
case <表达式>
<表达式>:赋值语句或空语句;
<表达式>:赋值语句或空语句;
default:赋值语句或空语句;
module compute (result, rega, regb, opcode);
input [7: 0] rega, regb;
input [2: 0] opcode;
output [7: 0] result;
reg [7: 0] result;
always @( rega or regb or opcode)
case (opcode)
3'b000 : result = rega + regb;
3'b001 : result = rega - regb;
3‘b010 : // specify multiple cases with the same result
3'b100 : result = rega / regb;
default : begin
result = 'bx;
$display (" no match");
end
endcase
endmodule
case语句是测试表达式与另外一系列表达式分支是否匹配的一个多路条件语句:
- Case语句进行逐位比较以求完全匹配(包括x和z)。
- Default语句可选,在没有任何条件成立时执行。此时如果未说明default,Verilog不执行任何动作。
- 多个default语句是非法的。
重要内容: - 使用default语句是一个很好的编程习惯,特别是用于检测x和z。
- Casez和casex为case语句的变体,允许比较无关(don‘t-care)值。
- case表达式或case项中的任何位为无关值时,在比较过程中该位不予考虑。
1. 在casez语句中,? 和 z 被当作无关值。
2. 在casex语句中,?,z 和 x 被当作无关值。
3.循环(looping)语句
有四种循环语句:
- repeat:将一块语句循环执行确定次数。
repeat (次数表达式) <语句>
- while:在条件表达式为真时一直循环执行
while (条件表达式) <语句>
- forever:重复执行直到仿真结束
forever <语句> (不可综合)
- for:在执行过程中对变量进行计算和判断,在条件满足时执行
for(赋初值;条件表达式;计算) <语句>
3.1 repeat (次数表达式) 语句举例
// Parameterizable shift and add multiplier
module multiplier( result, op_a, op_b);
parameter size = 8;
input [size:1] op_a, op_b;
output [2* size:1] result;
reg [2* size:1] shift_opa, result;
reg [size:1] shift_opb;
always @( op_a or op_b) begin
result = 0;
shift_opa = op_a; // 零扩展至16位
shift_opb = op_b;
repeat (size) begin
#10 if (shift_opb[1]) result = result + shift_opa;
shift_opa = shift_opa << 1; // Shift left
shift_opb = shift_opb >> 1; // Shift right
end
end
endmodule
在testbench中有时可以这样写:
initial begin
repeat (30) begin
@(posedge CLK);
#1 DATA_IN = $random;
end
end
repeat仿真波形如下:
3.2 while语句
while:只要表达式为真(不为0),则重复执行一条语句(或语句块)。
. . .
reg [7: 0] tempreg;
reg [3: 0] count;
. . .
count = 0;
while (tempreg) // 统计tempreg中 1 的个数
begin
if (tempreg[ 0]) count = count + 1;
tempreg = tempreg >> 1; // Shift right
end
end
. . .
在testbench中有时可以这样写:
initial begin
while (!EMPTY) begin
@(posedge CLK); #1 read_fifo = 1'b1;
end
@(posedge CLK); #1 read_fifo = 1'b0;
end
while仿真波形如下:
3.3 forever语句
- forever:一直执行到仿真结束
- forever应该是过程块中最后一条语句。其后的语句将永远不会执行。
- forever语句不可综合,通常用于test bench描述。
...
reg clk;
initial
begin
clk = 0;
forever
begin
#10 clk = 1;
#10 clk = 0;
end
…
end
...
这种行为描述方式可以非常灵活的描述时钟,可以控制时钟的开始时间及周期占空比。仿真效率也高。
3.4 for语句
for:只要条件为真就一直执行
条件表达式若是简单的与0比较通常处理得更快一些。但综合工具可能不支持与0的比较。
举例说明:
// X检测
for (index = 0; index < size; index = index + 1)
if (val[ index] === 1'bx)
$display (" found an X");
// 存储器初始化; “!= 0”仿真效率高
for (i = size; i != 0; i = i - 1)
memory[ i- 1] = 0;
// 阶乘序列
factorial = 1;
for (j = num; j != 0; j = j - 1)
factorial = factorial * j;
- for:在C语言中,经常用到for循环语句,但在硬件描述语言中for语句的使用较C语言等软件描述语言有较大的区别。
1 for(i=0;i<16;i++)
2 DoSomething();
- 在Verilog中除了在Testbench(仿真测试激励)中使用for循环语句外,在Testbench中for语句在生成激励信号等方面使用较普遍,但在RTL级编码中却很少使用for循环语句。
- 在RTL硬件描述中,遇到类似的算法,推荐的方法是先搞清楚设计的时序要求,做一个reg型计数器。在每个时钟沿累加,并在每个时钟沿判断计数器情况,做相应的处理,能复用的处理模块尽量复用,即使所有的操作不能复用,也采用case语句展开处理。
for的一个举例,做一个求和:
y
(
n
)
=
Σ
x
(
m
)
,
m
=
n
−
M
+
1
,
…
n
,
M
=
16
y(n) =Σx(m),m=n-M+1,…n, M = 16
y(n)=Σx(m),m=n−M+1,…n,M=16
对于C 语言:
1 for(i=0;i<16;i++)
2 y = y + x(i);
对于Verilog语言:
reg[7:0] x;
reg[10:0] y;
reg[3:0] counter;
always @(posedge clk)
if(rst) counter <= 4’b0;
else counter <= counter + 4’b1;
always @(posedge clk)
if(rst) y<= 11’b0;;
else if (counter == 4’b0) y = x;
else y = y + x;
滑动平均又该怎么做呢?前面的文章设计滑动平均,用到了for语句。
基于FPGA的任意点滑动平均(滑动窗长度和数据位宽参数化,例化时参数可设置)
3.5 for 和generate的妙用
genvar <var>;
generate
for (<var>=0; <var> < <limit>; <var>=<var>+1)
begin: <label>
<instantiation>/<alaways>/<assign>
end
endgenerate
例子:将信号延迟128个失踪节拍输出
wire b; reg a[128:1];
always@(posedge clk ) a[1] <=b;
always@(posedge clk ) a[2] <=a[1];
…….
always@(posedge clk ) a[128] <=a[127];
wire b;
reg a[128:1];
always@(posedge clk ) a[1] <=b;
genvar i;
generate
for ( i=1; i< 128; i=i+1)
begin: shift_b
always@(posedge clk ) a[i+1] <=a[i];
end
endgenerate
例子:差分信号输入级处理
genvar i;
generate
for (i=0; i< 16; i=i+1)
begin: gen_diff_label
IBUFDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("TRUE"),
.IOSTANDARD("DEFAULT”))
IBUFDS_inst (
.O(AD_din),
.I(AD_din_p[i]),
.IB(AD_din_n[i]));
end
endgenerate
3.6 disable语句
disable:任何循环都可以用disable命名块终止——仅仿真
initial begin: clock_10ns
forever begin
CLK = 1'b0;
#5 CLK = 1'b1; #5;
end
end
always @(posedge stop_clock)
if (stop_clock)
disable clock_10ns;
4.行为级零延时循环
- 当事件队列中所有事件结束后仿真器向前推进。但在零延时循环中,事件在同一时间片不断加入,使仿真器停滞后那个时间片。
- 在下面的例子中,对事件进行了仿真但仿真时间不会推进。当always块和forever块中没有时序控制时就会发生这种事情。
module comparator( out, in1, in2);
output [1: 0] out;
input [7: 0] in1, in2;
reg [1: 0] out;
always
if (in1 == in2)
out = 2'b00;
else if (in1 > in2)
out = 2'b01;
else
out = 2'b10;
initial
#10 $finish;
endmodule
5.持续赋值(continuous assignment)
- 可以用持续赋值语句描述组合逻辑,代替用门及其连接描述方式。
- 持续赋值在过程块外部使用。
- 持续赋值用于net驱动。
- 持续赋值只能在等式左边有一个简单延时说明
——只限于在表达式左边用#delay形式 - 持续赋值可以是显式或隐含的。
语法:
<assign> [#delay] [strength] <net_name> = <expressions>;
wire out;
assign out = a & b; // 显式
wire inv = ~in; // 隐含
持续赋值的例子
module assigns (o1, o2, eq, AND, OR, even, odd, one, SUM, COUT,
a, b, in, sel, A, B, CIN);
output [7:0] o1, o2;
output [31:0] SUM;
output eq, AND, OR, even, odd, one, COUT;
input a, b, CIN;
input [1:0] sel;
input [7:0] in;
input [31:0] A, B;
wire [7:0] #3 o2; // 没有说明,但设置了延时
tri AND = a& b, OR = a| b; // 两个显示赋值
wire #10 eq = (a == b); // 隐含赋值,并说明了延时
assign o2[7:4] = in[3:0], o2[3:0] = in[7:4]; // 部分选择
tri #5 even = ^in, odd = ~^ in; // 延时,两个赋值
wire one = 1’b1; // 常数赋值
assign {COUT, SUM} = A + B + CIN ; // 给级联赋值
endmodule
in的值赋给o1,但其每位赋值的强度及延迟可能不同。如果o1是一个标量(scalar)信号,则其延迟和前面的条件缓冲器上的门延迟相同。对向量线网(net)的赋值上的延迟情况不同。0赋值使用下降延迟,Z赋值使用关断延迟,所有其他赋值使用上升延迟。
上面的例子显示出持续赋值的灵活性和简单性。持续赋值可以:
- 隐含或显式赋值
- 给任何net类型赋值
- 给矢量net的位或部分赋值
- 设置延时
- 设置强度
- 用级联同时给几个net类变量赋值
- 使用条件操作符
- 使用用户定义的函数的返回值
- 可以是任意表达式,包括常数表达式
使用条件操作符的例子:
module cond_assigns (MUX1, MUX2, a, b, c, d);
output MUX1, MUX2;
input a, b, c, d;
assign MUX1 = sel == 2'b00 ? a :
sel == 2'b01 ? b :
sel == 2'b10 ? c : d;
tri1 MUX2 = sel == 0 ? a : ’bz, MUX2 = sel == 1 ? b : ’bz,
MUX2 = sel == 2 ? c : ’bz, MUX2 = sel == 3 ? d : ’bz;
endmodule
- 从上面的例子可以看出,持续赋值的功能很强。可以使用条件操作符,也可以对一个net多重赋值(驱动)。
- 在任何时间里只有一个赋值驱动MUX2到一个非三态值。如果所有驱动都为三态,则mux2缺省为一个上拉强度的1值。
微信公众号获取更多FPGA相关源码: