《SystemVerilog验证测试平台编写指南》学习笔记——线程以及线程间的通信(三)

2023-11-04

一、旗语

使用旗语可以实现对同一资源的访问控制,类似于操作系统里面的互斥访问。在SV多个阻塞的线程会以FIFO的方式进行排队。

1、旗语的操作

旗语有三种操作:

  1. 使用new方法可以创建一个带单个或者多个钥匙的旗语
  2. 使用get可以获取一个或多个钥匙
  3. 使用put可以返还一个或多个钥匙

如果试图获取一个旗语而不希望被阻塞可以使用try_get()函数,返回1表示足够多的钥匙,而返回0则表示钥匙不够。

用旗语实现对硬件资源的访问控制

program automatic test(bus_ifc.TB bus);
	semaphore sem;		//创建一个旗语
	initial begin
		sem = new(1);		//分配一个钥匙
		fork
			sequencer();		//产生两个总线事务线程
			sequencer();
		join
	end

	task sequencer;
		repeat($urandom%10)		//随机等待0-9个周期
			@bus.cb;
		sendTrans();		//执行总线事务
	endtask

	task sendTrans;
		sem.get(1);		//获取总线钥匙
		@bus.cb;		//把信号驱动到总线上
		bus.cb.addr <= t.addr;
		...
		sem.put(1);		//处理完成后把钥匙返还
	endtask
endprogram

2、带多个钥匙的旗语

  • 返还的钥匙可以比你取出来的多
  • 测试程序需要获取或者返还钥匙的时候务必谨慎
  • 当仅剩下一把钥匙时,有一个线程请求两把而被阻塞,第二个线程出现请求一把,SV会把第二个请求get(1)排到第一个请求get(2)的前面,先进先出的规则在这里会被忽略掉
  • 如果有多个大小不同的请求混在一起,可以编写一个类来决定优先权

二、信箱

把发生器和驱动器想象成具备自治能力的事务处理器对象,它们通过信道交换数据。每个对象从它的上游对象中得到事务(如果对象本身是发生器,则创建事务 ),进行一些处理,然后把它们传递给夏有对象。这里的信道必须允驱动器和接收器异步操作。从硬件角度出发,对信箱的最简单的理解是把它看成一个具有源端和收端的FIFO。信箱是一种对象,必须调用new函数来进行实例化,实例化时可选择size参数大小,为0或者没有指定,则默认信箱是无限大的。使用put任务可以把数据放入信箱,get可以移除数据。信箱为空时get会阻塞,信箱满时put会阻塞。peek任务可以获取对信箱里数据的拷贝而不移除它。信箱里放的数据可以使单个的值,例如一个整数或者是任意宽度的logic,可以放入句柄但是不能放入对象。
在这里插入图片描述

1、测试平台里的信箱

使用信箱实现对象的交换:Generator类

class Generator;
	Transaction tr;
	mailbox mbx;
	
	function new(mailbox mbx);
		this.mbx = mbx;
	endfunction

	task run(int count);
		repeat(count) begin
			tr = new();
			assert(tr.randomize());
			mbx.put(tr);
		end
	endtask
endclass

使用信箱实现对象的交换:Driver类

class Driver;
	Transaction tr;
	mailbox mbx;

	function new(mailbox mbx);
		this.mbx = mbx;
	endfunction

	task run(int count);
		repeat(count) begin
			mbx.get(tr);
			@(postedge bus.cb.ack);
			bus.cb.kind <= tr.kind;
			...
		end
	endtask
endclass

使用信箱实现对象的交换:程序块

program automatic mailbox_example(bus_if.TB bus, ...);
	'include "transaction.sv"
	'include "generator.sv"
	'include "driver.sv"

	mailbox mbx;		//连接发生器gen和驱动器drv的信箱
	Generator gen;
	Driver drv;
	int count;

	initial begin
		count = $urandom_range(50);
		mbx = new();		//创建信箱
		gen = new(mbx);
		drv = new(mbx);
		fork
			gen.run(count);
			drv.run(count);
		join
	end
endprogram

2、定容信箱

/*
定容信箱在两个线程之间扮演了一个缓冲器的角色
*/
'timescale 1ns/1ns
program automatic bounded;
	mailbox mbx;
	
	initial begin
		mbx = new(1);		//容量为1
		fork
			//生产方线程
			for(int i = 1; i < 4; i++) begin
				$display("Producer:before put(%0d)", i);
				mbx.put(i);
				$display("Producer:after put(%0d)", i);
			end
			//消费方线程
			repeat(4) begin
				int j;
				# 1ns mbx.get(j);
				$display("Consumer:after get(%0d)", j);
			end
		join
	end
endprogram

3、在异步线程间使用信箱通信

在没有同步信号的情况下,可能会导致消费方还没有开始取数的时候,生产方就已经把信箱填满了,这是因为线程在没有碰到阻塞语句之前会一直运行,而生产方恰好没有碰到阻塞语句的话,可能会一口气儿直接把信箱填满了,换句话说,生产方“跑”到了消费方前面,供过于求,我们想要的是生产者和消费者之间最好有一个同步信号,生产者生产了之后,信号会马上通知消费者来“取货”,或者说消费者需要“取货”时,如果信箱里面“没货”,同步信号会立即通知生产者取“生产”,这样可以维持一个动态的平衡。

4、使用定容信箱和探视(peek)来实现线程的同步

消费者使用一个内建的信箱方法peek()来探视信箱里的数据而不将其移除,当消费者处理完数据后,便使用get()移除数据,这使得生产者可以生成一个新的数据。如果消费者使用get()代替peek()来启动循环,那么事务被立刻移除信箱,这样生产者可能会在消费者完成事务的处理之前生成新的数据。

program automatic sync_peek;
	mailbox mbx;
	class Consumer;
		task run();
			int i;
			repeat(3) begin
				mbx.peek(i);		//探视mbx信箱里的整数
				$display("Consumer: after get(%0d)", i);
				mbx.get(i);		//从信箱里移除
			end
		endtask
	endclass : Consumer

	Producer p;
	Consumer c;
	initial begin
		//创建信箱、生产者、消费者
		mbx = new(1);		//容量为1
		p = new();
		c = new();
		fork
			p.run();
			c.run();
		join
	end
endprogram

输出结果:
在这里插入图片描述
可以看出生产者和消费者步调是一致的,但是生产者仍然比消费者提前一个事务的时间,这是因为容量为1的信箱只有在你试图对第二个事务进行put操作时才会发生阻塞。

5、使用信箱和事件来实现线程的同步

可以在生产者把数据放入信箱后使用事件来阻塞它,消费者则在处理完数据后再触发事件。

program automatic mbx_evt;
	mailbox mbx;
	event handshake;

	class Producer;
		task run();
			for(int i = 1; i < 4; i++) begin
				$display("Producer: before put(%0d)", i);
				mbx.put(i);
				@handshake;		//边沿敏感,可以确保生产者在发送完数据后便停止
				$display("Producer: after put(%0d)", i);
			end
		endtask
	endclass

	class Consumer;
		task run;
			int i;
			repeat(3) begin
				mbx.get(i);
				$display("Consumer: after get(%0d)", i);
				-> handshake;		//消费者触发事件,生产者可以继续生产
			end
		endtask
	endclass : Consumer

	Producer p;
	Consumer c;

	initial begin
		mbx = new();
		p = new();
		c = new();
		//使得生产方和消费方并发运行
		fork
			p.run();
			c.run();
		join
	end
endprogram

输出结果:
在这里插入图片描述
在这里插入图片描述

6、使用两个信箱来实现线程的同步

可以再使用一个信箱把消费者的完成信息发回给生产者

program automatic mbx_mbx2;
	mailbox mbx, rtn;
	class Producer;
		task run();
			int k;
			for(int i = 1; i < 4; i++) begin
				$display("Producer: before put(%0d)", i);
				mbx.put(i);
				rtn.get(k);		//生产者从返回的信箱取值,如果可以取得,说明消费者已经完成,如果没有取得值,说明消费者还没有完成事务的处理,生产者则会阻塞
				$display("Producer: after get(%0d)", k);
			end
		endtask
	endclass

	class Consumer;
		task run();
			int i;
			repeat(3) begin
				$display("Consumer: before get");
				mbx.get(i);
				$display("Consumer: after get(%0d)", i);
				rtn.put(-i);		//返回到rtn信箱的信息仅仅是原始整数的一个相反值,可以使用任意值,只要能表示有返回值即可
			end
		endtask
	endclass : Consumer
	
	Producer p;
	Consumer c;

	initial begin
		mbx = new();
		rtn = new();
		p = new();
		c = new();
		fork
			p.run();
			c.run();
		join
	end
endprogram

输出结果:
在这里插入图片描述

7、其他的同步技术

通过变量或者旗语来阻塞线程也同样可以实现握手。事件是最简单的结构,其次是通过变量阻塞。旗语相当于第二个信箱,但没有信息交换。SV中的定容信箱有一个缺点就是无法再生产者放入第一个事务的时候让它阻塞,会一直比消费者提前一个事务的时间。

三、构筑带线程并可实现线程间通信的测试程序

1、基本的事务处理器

分层的环境测试平台:
在这里插入图片描述
处于发生器和驱动器之间的代理

class Agent;
	mailbox gen2agt, agt2drv;
	Transaction tr;
	
	function new(mailbox gen2agt, agt2drv);
		this.gen2agt = gen2agt;
		this.agt2drv = agt2drv;
	endfunction

	task run();
		forever begin;
			gen2agt.get(tr);		//从上游的模块中获取事务
			...
			agt2drv.put(tr);		//把事务发送给下游模块
		end
	endtask
endclass

2、配置类

配置类允许你在每次仿真时对系统的配置进行随机化

配置类

class Config;
	bit[31:0] run_for_n_trans;
	constraint reasonable{
		run_for_n_trans inside {[1:1000]};
		}
endclass

3、环境类

环境类包含了发生器、代理、驱动器、监视器、检验器、记分板,以及它们之间的配置对象和信箱。

环境类

class Environment;
	Generator gen;
	Agent agt;
	Driver drv;
	Monitor mon;
	Checker chk;
	Scoreboard scb;
	Config cfg;
	mailbox gen2agt, agt2drv, mon2chk;

	extern function new();
	extern function void gen_cfg();
	extern function void build();
	extern task run();
	extern task wrap_up();
endclass

function Environment::new();
	cfg = new();
endfunction

function void Environment::gen_cfg();
	assert(cfg.randomize);
endfunction

function void ENvironment::build();
	//初始化信箱
	gen2agt = new();
	agt2drv = new();
	mon2chk = new();
	//初始化事务处理器
	gen = new(gen2agt);
	agt = new(gen2agt, agt2drv);
	drv = new(agt2drv);
	mon = new(mon2chk);
	chk = new(mon2chk);
	scb = new();
endfunction

task Environment::run();
	fork
		gen.run(cfg.run_for_n_trans);
		agt.run();
		drv.run();
		mon.run();
		chk.run();
		scb.run(cfg.run_for_n_trans);
	join
endtask

task Environment::wrap_up();
	fork
		gen.wrap_up();
		agt.wrap_up();
		drv.wrap_up();
		mon.wrap_up();
		chk.wrap_up();
		scb.wrap_up();
	join
endtask

4、测试程序

program automatic test;
	Environment env;
	initial begin
		env = new();
		env.gen_cfg();
		env.build();
		env.run();
		env.wrap_up();
	end
endprogram

四、结束语

你的设计可以用很多并发运行的独立块来建模,所以测试平台也必须能够产生很多激励流并检验并发线程的反应。fork-join、fork-join_none、fork-join_any用于动态创建线程,线程之间可以使用事件、旗语、信箱,以及@事件控制和wait语句来实现通信和同步。disable可以中止线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《SystemVerilog验证测试平台编写指南》学习笔记——线程以及线程间的通信(三) 的相关文章

  • 1334. 阈值距离内邻居最少的城市

    1334 阈值距离内邻居最少的城市 原题链接 完成情况 解题思路 参考代码 Dijkstra Dijkstra 小顶堆 Floyd martix方法 原题链接 1334 阈值距离内邻居最少的城市 https leetcode cn prob
  • 裸机服务器和虚拟机的用途和好处

    裸机服务器 用户可以根据需要自定义存储区域 用户几乎可以在世界的每个角落访问他们的数据 用户还将拥有最高级别的数据加密 只有使用最新技术的用户才能访问 由于这些服务器有专门的用户 因此具有安全性和监管优势 它具有很高的处理能力 用户可以完全

随机推荐

  • Poi版本升级优化

    Poi 3 17前后版本api使用差异 1 升级缘由 最近公司prod环境出现因为Excel文件下载数据量过大导致应用out of memory 然后就需要找到内存溢出的原因及优化方案 经分析 得出以下结论 1 1 事故原因 1 应用场景发
  • 四合天地软件测试系统,GZ-2017025软件测试赛题.-全国职业院校技能大赛.doc

    GZ 2017025软件测试赛题 全国职业院校技能大赛 doc 2017年全国职业院校技能大赛高职组 软件测试 项目竞赛任务书 2017年全国职业院校技能大赛 高职组 软件测试 赛项执委会制 2017年5月 目录 一 赛程说明3 二 竞赛技
  • ElasticSearch启动流程指令及注意事项

    elasticsearch es的集群部署 第一步 创建普通用户 注意 ES不能使用root用户来启动 必须使用普通用户来安装启动 这里我们创建一个普通用户以及定义一些常规目录用于存放我们的数据文件以及安装包等 创建一个es专门的用户 必须
  • 第一个python代码,第一个错误。python是对缩进严格要求的代码。

    在编写第一个条件判断语句的代码中 就遇到了第一个错误 运行py时提示 仔细对照了一下代码 发现原来时缩进格式错误 并很不明显 条件语句的if换行一般是缩进四个空格 但个人觉得以其按四个空格 不如直接按一下tab键来得简洁明了 我两种方法都试
  • SpringCloud 商城系统搭建之Ribbon (基于Ribbon + RestTemplate)

    Spring Cloud 服务调用方式 Spring Cloud有两种服务调用方式 一种是Ribbon RestTemplate 另一种是feign 在这一篇文章首先讲解下基于Ribbon RestTemplate Ribbon简介 Rib
  • 自定义实现nn.CrossEntropyLoss损失函数

    nn CrossEntropyLoss是在PyTorch中常用的交叉熵损失函数 它主要用于解决多分类问题 但也可以用于解决二分类问题 该函数有两个输入参数 第一个参数是网络的最后一层的输出 是一个二维数组 其中每个向量包含不同类别的概率值
  • 蓝桥杯省赛C++A组B组题解整理(第十、九、八、七、六、五、四、三届)

    写在前面的话19 03 24 从第八届蓝桥杯到第十届蓝桥杯 我也是参加了三届蓝桥杯的老学姐啦 更不更新第十届的题解取决于网上有没有流出题目 但是第十届之后的题解将不会再更新了 下面的这些真题大多是我在大一的时候刷的 在大二的时候整理的 在大
  • FM模型

    FM模型 一 FM模型的意义 1 传统模型的缺点 忽略了特征之间的联系 特征高维 稀疏 容易爆炸 2 什么是FM模型 FM就是Factor Machine 因子分解机 FM通过对两两特征组合 引入交叉项特征 提高模型得分 其次是高维灾难 通
  • 5种免费企业邮箱

    现在 企业邮箱的使用越来越普遍 而企业一方面想用 而且又不花钱 这就要求我们这些网管人员好好找一下了 而哪些是免费的而且又好用的呢 以下是公司使用的 网易免费企业邮 贴出来和大家分享 首页 申请成功的企业邮 基本设置 用户数200 最大容量
  • 字节青训营第四课之高性能Go语言发行版优化与落地实践的笔记和总结

    这是字节青训营的第四课 高性能Go语言发行版优化与落地实践的笔记和总结 概览 本节课主要介绍性能优化和实践 性能优化包括自动内存管理 Go内存管理及优化 编译器与静态分析 编译器优化 实践包括字节内部的Balanced GC优化对象分配以及
  • java与以太坊之web3j

    web3j https docs web3j io index html 如何使用Web3j生成私钥和地址 而不只是创建密钥存储JSON文件 https blog csdn net mongo node article details 81
  • 写作——如何制作参考文献

    进入点ScienceDirect WOS Springer输入相关词找到文献后点 cite 选择bibtex就有bib文件内需要的信息 自己加以修改 无论什么网站搜索的信息 都必须经过自己加工整理 bib文件里格式必须保持一致 1 Late
  • 领域驱动设计-贫血模型VS充血模型

    项目实现方式 事务脚本 事务脚本的核心是过程 通过过程的调用来组织业务逻辑 每个过程处理来自表现层的单个请求 大部分业务应用都可以被看成一系列事务 从某种程度上来说 通过事务脚本处理业务 就像执行一条条Sql语句来实现数据库信息的处理 事务
  • SpringCloud微服务项目快速搭建(一)(SpringCloud Netflix,H版-SR3)

    一 前言 Spring Cloud Netflix是Spring Cloud生态系统中的重要组成部分 也是使用Spring Cloud进行微服务架构开发的主要方向之一 Spring Cloud Netflix主要目标是为开发者提供构建分布式
  • Tomcat WebappClassLoader 类加载机制源码分析

    tomcat中的ClassLoader 启动类加载器 BootStrap ClassLoader 引导类装入器是用本地代码实现的类装入器 它负责将 jdk中jre lib下面的核心类库或 Xbootclasspath选项指定的jar包加载到
  • webm格式怎么转换成mp4?四招快速转换!

    众所周知 视频是我们生活工作中的重要内容 也是传播信息 促进交流的关键工具 随着各类短视频平台的兴起和发展 视频技术也在不断创新 目前 视频格式相对较多 而一些社交网站 短视频软件对视频格式有一定的限制 并不是所有的视频格式都能够被支持的
  • Python 流体动力学层流建模

    使用单相层流的有限差分法求解二维 Navier Stokes 方程 纳维 斯托克斯方程 并使用基准盖腔测试验证结果 介绍 在许多自然现象中都可以观察到流体流动 从舒缓的瀑布到计算机键盘上令人恼火的咖啡飞溅 看到损坏的键盘上的咖啡后 您的第一
  • Unity打开出现两个空白错误的解决方法

    直接上结论 将unity和hub全部重装 问题描述 unity上周还用的好好的 这周打开突然console出现了两个空白错误导致无法运行 版本 2019 4 21 网上的方法 重装unity 改中文路径 改C 改Net 改这改哪的 要么看不
  • CVTE校招笔试

    CVTE校招挺注重基础 并且很多ES6语法 1 变量提升 2 考察call apply bind 和ES6双冒号绑定符 fun bun 相当于 bun bind fun fun bun 相当于 bun apply fun 3树结构 4栈结构
  • 《SystemVerilog验证测试平台编写指南》学习笔记——线程以及线程间的通信(三)

    一 旗语 1 旗语的操作 2 带多个钥匙的旗语 二 信箱 1 测试平台里的信箱 2 定容信箱 3 在异步线程间使用信箱通信 4 使用定容信箱和探视 peek 来实现线程的同步 5 使用信箱和事件来实现线程的同步 6 使用两个信箱来实现线程的