找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

543

积分

0

好友

77

主题
发表于 昨天 03:14 | 查看: 1| 回复: 0

在复习硬件编程语言课程时,我梳理了一些之前容易忽略或混淆的Verilog语法细节,特此总结分享,希望能帮助大家查漏补缺。

一、task与function的区别

两者结构相似,但用途和规则有显著不同。

1. task(任务)

任务定义格式如下:

task <任务名>;
  // 端口和类型说明
  // 局部变量声明
  begin
    // 语句
  end
endtask

示例:

task read_memory;
  input [15:0] address;
  output [31:0] data;
  reg [3:0] counter;
  reg [7:0] temp [1:4];
  begin
    for(counter = 1; counter <= 4; counter = counter + 1)
      temp[counter] = mem[address+counter-1];
    data = {temp[1],temp[2],temp[3],temp[4]};
  end
endtask

// 调用示例
module demo_task;
  reg [7:0] mem[127:0];
  reg [15:0] a;
  reg [31:0] b;
  initial begin
    a = 0;
    read_memory(a,b); // 任务调用
    #10;
    a = 10;
    read_memory(a,b);
  end
endmodule

2. function(函数)

函数定义格式如下:

function [返回值位宽] <函数名>;
  // 输入参量和类型说明
  // 局部变量声明
  begin
    // 语句
  end
endfunction

默认返回值类型为reg,也可指定为integerreal

示例:

function [3:0] num_0;
  input [7:0] x;
  reg [3:0] count;
  integer i;
  begin
    count = 0;
    for(i = 0; i < 8; i = i + 1)
      if(x[i] == 1'b0)
        count <= count + 1;
      else
        count <= count;
    num_0 = count; // 赋值给函数名以返回结果
  end
endfunction

// 调用示例
module demo_function(
  input [15:0] num, // 输入序列
  output [15:0] a   // 序列中0的个数
);
  wire [15:0] a;
  assign a = num_0(num); // 函数调用,作为表达式的一部分
endmodule

3. 核心区别总结

特性 function task
调用关系 可调用函数,不可调用任务 可调用函数任务
执行时刻 总是在仿真0时刻开始 可以在非0时刻开始
时序控制 不能包含延时、事件控制语句 可以包含
输入/输出 至少有一个输入,output/inout 可有可无
返回值 一个返回值 返回值
调用形式 不能作为独立语句,是表达式一部分 通过一条独立的语句调用
作用域 可出现在连续赋值和过程块中 只能出现在过程块
中断 不允许disable 可以disable

理解这些区别对于编写正确的硬件描述语言模块至关重要。

二、显示类系统任务:$display,$write, $monitor,$strobe

这些系统任务都用于信号输出显示,但行为各有特点。

  1. $display 与$write

    • $display:输出后自动换行
    • $write:输出后不自动换行,适合在一行内组合多个输出。
  2. $monitor 与$display

    • $monitor:通常在initial块中调用,具备持续监视功能。只要未使用$monitoroff,它就会监控所列信号,任何信号变化都会触发一次全部信号的格式化输出。同一仿真时刻多个信号变化,只打印一次
    • $display不具备监视功能,执行一次输出一次即结束。
  3. $monitor 与$strobe

    • $strobe:在当前时间步的所有操作完成之后才执行输出,常用于查看该时刻的最终稳定值。它同样具有监控功能。$strobeb, $strobeh, $strobeo是其针对不同数制的变体。

示例分析:

initial begin
  a = 0;
  $display(a); // 输出此刻a的值:0
  $strobe(a);  // 将在时间步结束时输出a的最终值
  a = 1;       // a值立即变为1
end

这段代码的输出结果是:

0
1

因为$display立即输出a=0,而$strobe等到initial块内所有赋值完成(a=1)后才输出。

三、仿真控制:$finish 与$stop

这两个系统任务用于控制仿真流程。

  • $finish终止并退出仿真。
  • $stop暂停仿真,通常可在此刻交互式地查看信号状态。

它们都可以带一个可选的整数参数n

$finish(n);
$stop(n);
n值 含义
0 不输出任何信息
1 输出仿真时间和位置
2 输出仿真时间和位置,并附加内存和CPU时间统计

四、随机数生成:$random

$random用于生成随机数,常用于测试激励。

$random % x;      // 产生范围在 (-x+1) 到 (x-1) 之间的随机整数
{$random} % x;    // 产生范围在 0 到 (x-1) 之间的随机整数 (x > 0)

使用{$random}可以确保得到一个正数种子,从而生成非负随机数。

五、时间尺度:$time 与$realtime

这两个系统任务返回当前的仿真时间。

  • $time:返回一个64位整数时间值,按仿真时间单位计算。
  • $realtime:返回一个实型数时间值,精确反映时间尺度。

六、文件读取:$readmemh 与$readmemb

用于从外部文件读取数据并加载到存储器(寄存器数组)中,是搭建测试环境的重要手段。

  • $readmemh:读取十六进制格式的文件。
  • $readmemb:读取二进制格式的文件。

调用格式:

$readmemh(“数据文件.dat”, <存储器名>, <起始地址>, <结束地址>);

文件格式要求

  • 只能包含:空白符、注释行、二进制/十六进制数字。
  • 数字中不能有位宽或格式说明(如8‘hFF是不允许的,应直接写FF)。
  • 可以使用@<十六进制地址>在文件中指定后续数据的加载地址。

示例:
假设有数据文件data.dat,内容为:

@0010 // 从地址0x10开始加载
AA BB
@0020 // 从地址0x20开始加载
CC DD

通过$readmemh(“data.dat”, mem);读取后,存储器mem0x100x11地址将分别存入AABB0x200x21地址将分别存入CCDD

数据文件与存储器加载示意图

熟练掌握这些系统任务能极大提升数字电路仿真与测试的效率。Verilog中用于仿真的语法非常丰富,本文仅对部分易混淆点进行了总结。




上一篇:每日科技前沿动态:AI标准、脑机接口与机器人量产进展
下一篇:服务器安全加固指南:30个常见高危端口风险解析与关闭实践
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-12 08:23 , Processed in 0.080126 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表