news 2026/8/12 17:21:13

SV学习记录(三)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SV学习记录(三)

目录

3.1 过程语句

3.2 task、function以及void函数

3.3 task、function

3.4 子程序参数

C-style

参数方向

高级参数类型

参数缺省值

用名字传递参数

常见错误

3.5 子程序的返回

返回语句

从函数中返回一个数组

3.6 局部数据存储

自动存储

变量初始化

3.7 时间值

时间单位和精度

时间量

时间和变量

$time and $realtime


3.1 过程语句

sv从C中引入了一些特点。

如下,可以在循环中定义循环变量,这在v中好像是不行的。

例 3.1 新的过程语句和操作符 initial begin: example integer array[10], sum, j; ​ // 在 for 语句中声明 i for (int i = 0; i < 10; i++) // i 递增 array[i] = i; ​ // 把数组里的元素相加 sum = array[9]; j = 8; do // do...while 循环 sum += array[j]; // 累加 while (j--); // 判断 j=0 是否成立 ​ $display("Sum=%4d", sum); // %4d-指定宽度 end : example // 结束标识符

还有continue和break,这个v中似乎也有。

例 3.2 在读取文件时使用 break 和 continue initial begin bit [127:0] cmd; int file, c; ​ file = $fopen("commands.txt", "r"); while (!$feof(file)) begin c = $fscanf(file, "%s", cmd); case (cmd) "" : continue; // 空行——跳到本轮循环的末尾 "done" : break; // Done——终止并跳出循环 ... // 此处处理其他命令 endcase // case(cmd) end $fclose(file); end

3.2 task、function以及void函数

v中,task可以消耗时间,但是function不行。function必须有返回值

sv中function可以调用task,但是只能在fork...join_none中。

如果有不消耗时间的task,最好定义为void function,方便复用。

例 3.3 用于调试的 void 函数 function void print_state(...); $display("@@t: state=%s", $time, cur_state.name()); endfunction

3.3 task、function

现在只要有task...endtask和function...endfunction就可以

例 3.5 不带 begin...end 的简单任务 task multiple_lines; $display("First line"); $display("Second line"); endtask : multiple_lines

3.4 子程序参数

C-style

端口声明可以一次声明方向+类型,现在V也行了,估计这书比较老。

例 3.7 C语言风格的子程序参数 task mytask1 (output logic [31:0] x, input logic y); ... endtask

参数方向

如果声明端口的时候忘了加方向和类型,sv中默认为input logic

不建议这么干

例 3.9 带缺省类型的子程序参数 task T3(a, b, output bit [15:0] u, v);

高级参数类型

旧v不能传递数组,现在sv可以

ref类似C中的指针,传的时候不是把整个数组复制过去,而是让它能够拿来使用就可以。如果不写ref,就会复制。

const使得function只能使用它的值而不能修改。

例 3.10 使用 ref 和 const 传递数组 function void print_checksum (const ref bit [31:0] a[]); bit [31:0] checksum = 0; for (int i = 0; i < a.size(); i++) checksum ^= a[i]; $display("The array checksum is %0d", checksum); endfunction

但是ref只能用于带automatic(自动存储)属性的routine

ref参数的第二个好处是在任务里可以修改变量而且修改结果对调用它的函数随时可见。当你有若干并发执行的线程时,这可以给你提供一种简单的信息传递方式

task的data只写了ref没有注明方向,这是因为写ref就表明会对data这个变量直接进行操作。sv中有四个方向:input, output, ref, inout

在initial块中,有两个线程,分别是task和thread,因为了写了ref data,所以两个线程能一起检测到变化。

例 3.11 在多线程间使用 ref ​ task bus_read(input logic [31:0] addr, ref logic [31:0] data); // 请求总线并驱动地址 bus.request = 1'b1; @(posedge bus.grant) bus.addr = addr; ​ // 等待来自存储器的数据 @(posedge bus.enable) data = bus.data; ​ // 释放总线并等待许可 bus.request = 1'b0; @(negedge bus.grant); endtask ​ logic [31:0] addr, data; ​ initial fork bus_read(addr, data); thread2: begin @data; // 在数据变化时触发 $display("Read %h from bus", data); end join

参数缺省值

下面的函数是想输出一个数组(大小不确定)中部分元素的和。

low,high这些在定义的时候给了默认值,如果缺省可以采用默认值。如果传入了用传入值

但是ref不能缺省

例 3.12 带缺省参数值的函数 ​ function void print_checksum(ref bit [31:0] a[], input bit [31:0] low=0, input int high=-1); bit [31:0] checksum=0; ​ if (high==-1||high>=a.size()) high=a.size()-1; for (int i=low;i<=high;i++) checksum+=a[i]; $display("The array checksum is %0d",checksum); endfunction ​ 例 3.13 使用参数的缺省值 ​ print_checksum(a); // a[0:size()-1]中所有元素的校验和——缺省情况 print_checksum(a,2,4); // a[2:4]中所有元素的校验和 ​ print_checksum(a,1); // 从 1 开始 print_checksum(a,2); // a[0:2]中所有元素的校验和 print_checksum(); // 编译错误: a 没有缺省值

用名字传递参数

和v中例化的思想一样,没什么新的。

例 3.14 采用名字进行参数传递 ​ task many (input int a=1, b=2, c=3, d=4); $display("%0d %0d %0d %0d", a, b, c, d); endtask ​ initial begin // a b c d many(6, 7, 8, 9); // 6 7 8 9 指定所有值 many(); // 1 2 3 4 使用缺省值 many(.c(5)); // 1 2 5 4 只指定 c many(6, .d(8)); // 1 6 3 8 混合方式 end

常见错误

如果写task时,省略了后面参数的方向,那么会默认和前一个一样。

对于下面情况,ab都是ref

task sticky (ref int array[50], int a, b); // a 和 b 都是 input

3.5 子程序的返回

返回语句

sv里有return,用法和C类似

例 3.18 在任务中用 return 返回 task load_array(int len, ref int array[]); ​ if (len <= 0) begin $display("Bad len"); return; end ​ // 任务中其余的代码 endtask

从函数中返回一个数组

方法一:定义一个数组类型,在函数声明中使用这个类型

fixed_array5是包含五个int元素的数组类型。

例 3.20 使用 typedef 从函数中返回一个数组 typedef int fixed_array5[5]; fixed_array5 f5;//等价于 int f5[5] ​ function fixed_array5 init(int start);//函数的返回值是int[5] foreach (init[i])//在函数内部,函数名本身就是一个代表“返回值”的隐式局部变量,所以可以用 init[i] = i + start; endfunction ​ initial begin f5 = init(5); foreach (f5[i]) $display("f5[%0d]=%0d", i, f5[i]); end

问题:如果数组太大,可能放不下

方法二:通过ref传递参数

例 3.21 把数组作为 ref 参数传递给函数 function void init(ref int f[5], input int start); foreach (f[i]) f[i] = i + start; endfunction ​ int fa[5]; initial begin init(fa, 5); foreach (fa[i]) $display("fa[%0d]=%0d", i, fa[i]); end

方法三:把函数包装到类中,返回对象的句柄

3.6 局部数据存储

自动存储

意义:程序中的变量在堆栈上动态分配,每次调用都创建全新的副本,互不干扰。如果没有自动存储,并行调用的时候,会出现覆盖问题。

必须显式标注automatic

例 3.22 在 program 块中指定自动存储方式 program automatic test; ​ task wait_for_mem(input [31:0] addr, expect_data, output success); ​ while (bus.addr!==addr) @ (bus.addr); ​ success=(bus.data==expect_data); ​ endtask

变量初始化

问题解释如下:sv中变量默认是静态的,加了automatic才是动态的。对于静态变量,在仿真开始(0s)的时候会初始化这个变量,如果这么写 logic [7:0] local_addr = addr << 2,是把initialize和declaration放到一起了,但是这种语句的initialize会在0s被执行,采集到addr为X赋给local_addr,之后再执行到这个语句的时候,不会再对local_addr赋值,因为它是静态

例 3.23 静态初始化的漏洞(有漏洞版本) program initialization; // 有漏洞的版本 task check_bus; repeat (5) @ (posedge clock); if (bus_cmd == 'READ) begin // 何时对 local_addr 赋初值? logic [7:0] local_addr = addr << 2; // 有漏洞 $display("Local Addr = %h", local_addr); end endtask endprogram

解决方法:program后标注上automatic;或把declaration和initialize分开,logic a; a=b;

3.7 时间值

时间单位和精度

timeunit+timeprecision=timescale

如果已经写了timescale,注意要在模块内部定义timeunit和timeprecision覆盖。

时间量

$timeformat(-9,3,"ns",8);这一句话的理解:-9表示以s为单位,乘10^(-9);3表示小数点后三位;ns表示在数字后面额外打印的内容;8表示输出的最小宽度为8个字符。这句话规定了输出格式。

例 3.26 时间参数和 $timeformat module timing; timeunit 1ns; timeprecision 1ps; initial begin $timeformat(-9,3,"ns",8); #1 $display("%t",$realtime);// 1.000ns #2ns $display("%t",$realtime);// 3.000ns #0.1ns $display("%t",$realtime);// 3.100ns #41ps $display("%t",$realtime);// 3.141ns end endmodule

时间和变量

注意time是64bit整型,所以会舍入。

# rdelay;就算是浮点,在用于时间延迟的时候,也会被舍入。

例 3.27 时间变量及舍入 `timescale 1ps/1ps ​ module ps; ​ initial begin real rdelay = 800fs; // 以 0.800 存储 time tdelay = 800fs; // 舍入后得到 1 $timeformat(-15, 0, "fs", 5); # rdelay; // 时延舍入后得到 1ps $display("%t", rdelay); // "800fs" end ​ endmodule

系统任务$time的返回值是一个根据所在模块的时间精度要求进行舍入的整数,不带小数部分;$realtime返回值带小数部分

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/12 17:19:12

八、Vue组件通信详解

一、vue2组件通信汇总表通信方式适用场景/范围通信方向核心原理与特点Props / $ emit父子组件父传子 / 子传父最核心的单向数据流。父传子用 props&#xff0c;子传父通过 this.$emit 触发自定义事件。v-model父子组件双向绑定语法糖。本质是 props 接收 value&#xff0c;子组…

作者头像 李华
网站建设 2026/8/12 17:18:26

Windows注册表清理指南:彻底删除顽固快捷方式

1. 问题缘起&#xff1a;那些删不掉的“幽灵”快捷方式 你有没有遇到过这种情况&#xff1f;在Windows的文件资源管理器左侧导航栏&#xff0c;或者右键菜单的“发送到”列表里&#xff0c;总是挂着几个早已卸载软件的快捷方式。它们像顽固的“幽灵”一样&#xff0c;既占地方又…

作者头像 李华
网站建设 2026/8/12 17:18:25

苹果评级降至五年最低,折叠屏iPhone能否拯救硬件利润率下滑困局?

苹果评级下调&#xff0c;市场悲观情绪加速聚集 这两日&#xff0c;苹果的情况颇为引人关注&#xff0c;华尔街对它的悲观情绪正加速聚集。继KeyBanc上月下调评级后&#xff0c;杰富瑞又将苹果评级降至“跑输大盘”&#xff0c;目标价下调至263.66美元。目前&#xff0c;给予苹…

作者头像 李华
网站建设 2026/8/12 17:17:49

逆向工程入门:使用x32dbg实战破解CrackMe程序Acid Burn

1. 项目概述&#xff1a;逆向工程入门与CrackMe的独特价值 如果你对软件内部运作机制充满好奇&#xff0c;总想弄清楚一个程序在点击“确定”按钮后到底做了哪些判断&#xff0c;那么逆向工程就是你打开这扇门的钥匙。而CrackMe&#xff0c;正是为学习逆向工程量身定做的“练习…

作者头像 李华
网站建设 2026/8/12 17:16:59

15.RK3588最高运行性能配置

1.查询npu版本rootATK-DLRK3588:cat /sys/kernel/debug/rknpu/version RKNPU driver: v0.9.82.查询npu的负载rootATK-DLRK3588:cat /sys/kernel/debug/rknpu/load NPU load: Core0: 0%, Core1: 0%, Core2: 0%,3.显示或者设置NPU、CPU、DDR运行频率scaling_frequence.shchip…

作者头像 李华
网站建设 2026/8/12 17:15:46

AI技术术语解析:从LLM、RAG到Agent,一文读懂大模型核心概念与应用

1. 引言&#xff1a;当技术黑话成为交流壁垒 最近在几个技术社区和项目讨论区里&#xff0c;我经常看到一种现象&#xff1a;大家讨论AI项目或者分享经验时&#xff0c;会不自觉地蹦出一些“行话”。这些词&#xff0c;圈内人一听就懂&#xff0c;甚至觉得是“专业”的体现&…

作者头像 李华