本文介绍如何使用Verilog HDL语言实现类似C语言循环结构,以此为基础进一步向同学们介绍Verilog语法
1. C语言循环结构求累加和函数
1.1 待转换的C语言函数–For循环结构求和
#include <stdio.h>
//---------------------------------------------------
//C语言函数:for_loop_sum_c()
//---------------------------------------------------
void for_loop_sum_c (int low,int high, int* sum)
{
int sum_t = 0; //定义局部变量保存累加和
int i; //为for循环定义一个循环变量
for(i = low; i <= high; i++){ //for循环控制结构
sum_t = sum_t + i; //for循环体,可以是多条语句,为简单起见,这里只做累加
}
//输出结果
*sum = sum_t;
}
本例代码C语言函数for_loop_sum_c()
使用for循环结构求从最小值low
到最大值high
之间整数累加和,然后函数使用指针变量int* sum
输出累加结果。
1.2 编写调用函数for_loop_sum_c()的主函数验证函数的正确性
//调用get_loop_sum()函数,输出结果验证函数的功能正确性
void main(){
int low_val = 2;
int high_val = 8;
int sum = 0;
for_loop_sum_c (low_val,high_val,&sum);
printf("sum from %d to %d = %d",low_val,high_val,sum);
return;
}
2. Verilog实现相同功能的模块
为了转换C语言
的循环结构到Verilog
的对应功能模块,我们先分析一下C语言函数for_loop_sum_c()
里面for
循环结构的几个要点:
-
for
循环主要是使用一个循环变量控制的循环的执行次数,本例代码里是int i
; - 循环变量需要被初始化,本例代码里是
i = low
; - 依据循环变量的值判断要继续循环或退出循环,本例代码里是
i <= high
; - 循环变量在每次循环后必须更新为一个新的的值,这样保证有机会退出循环;
- 必须有一个循环体,其内容可以很简单,也可以很复杂,在这里都会被当做一个复合语句对待。本例只简单地求累加和
sum_t = sum_t + i;
这里要特别提醒!!!
很多教程里都会讲到
Verilog
语言里面也有for循环
结构,但Verilog
自带的for
循环结构一般只能用于编写仿真代码,是不可综合的,也就是只能用于仿真模块里initial
块里面,不可以用于编写可综合的硬件模块。
在
Testbench
的initial
块里代码都是类似C语言的结构,即所谓阻塞赋值,在这里Verilog
的for循环
结构与C语言的for循环
结构基本一致,功能也相似。因为仿真代码本质是与C语言类似,是在CPU上运行的代码,而我们要实现的是实际的硬件模块设计,这是与C语言函数的本质区别。
`timescale 1ns/1ps
module for_loop_sum_v (
input clk,
input rst,
input req,
input [7:0] low,
input [7:0] high,
output reg busy,
output reg valid,
output reg [15:0] sum
);
reg [7:0] cnt;
always @(posedge clk) begin
if(rst) cnt <= 8'hff;
else if (req) cnt <= low;
else if (cnt <= high+1) cnt <= cnt + 1;
else cnt <= cnt;
end
always @(posedge clk) begin
if(rst) sum <= 0;
else if(req) sum <= 0;
else if(cnt >= low && cnt <= high)
sum <= sum + cnt;
else sum <= sum;
end
always @(posedge clk) begin
if(rst) valid <= 1'b0;
else if(cnt == high) valid <= 1'b1;
else valid <= 1'b0;
end
always @(*) begin
if(cnt<=high) busy = 1'b1;
else busy = 1'b0;
end
endmodule
3. 编写仿真测试模块
`timescale 1ns / 1ps
module for_loop_sum_v_tb();
//1. 为时序逻辑生成时钟信号clk和复位信号rst
reg clk;
initial begin
clk = 0;
end
always clk = #50 ~clk;
//2. 为待测模块的每个输入信号声明一个reg型变量,包括复位信号rst
reg rst;
reg req;
reg [7:0] low;
reg [7:0] high;
//3. 为待测模块的每个输出信号声明一个wire型变量
wire busy;
wire valid;
wire [15:0] sum;
//4. initial block依序为待测模块的每个输入信号赋值
initial begin
low = 0;
high = 0;
req = 1'b0;
rst = 1;
#300;
rst = 0;
#200;
//将req信号拉高一个时钟,启动待测模块的计算动作
low = 2;
high = 12;
#20;
req = 1'b1;
#100;
req = 1'b0;
//等待待测模块的输出信号valid变为1,表示计算结束
@valid;
#200;
//再次将req信号拉高一个时钟,启动待测模块的下一次计算动作
low = 3;
high = 9;
#20;
req = 1'b1;
#100;
req = 1'b0;
//等待待测模块的输出信号valid变为1,表示计算结束
@valid;
#300;
//结束仿真
$finish;
end
//例化待测试模块
for_loop_sum_v uut(
.clk(clk),
.rst(rst),
.req(req),
.low(low),
.high(high),
.busy(busy),
.valid(valid),
.sum(sum)
);
endmodule