精华内容
下载资源
问答
  • 双向通信

    2021-02-27 18:24:27
    与单向通信相同的是,双向通信的两端也分为initiator和target,但是数据流向在端对端之间是双向的。双向通信中的两端同时扮演着producer和consumer的角色,而initiator作为request发起方,在发起request之后,还会...

    一、概述

    与单向通信相同的是,双向通信的两端也分为initiator和target,但是数据流向在端对端之间是双向的。双向通信中的两端同时扮演着producer和consumer的角色,而initiator作为request发起方,在发起request之后,还会等待response返回。

    UVM双向端口分为以下类型:

    • uvm_blocking_transport_PORT
    • uvm_nonblocking_transport_PORT
    • uvm_transport_PORT
    • uvm_blocking_master_PORT
    • uvm_nonblocking_master_PORT
    • uvm_master_PORT
    • uvm_blocking_slave_PORT
    • uvm_nonblocking_slave_PORT
    • uvm_slave_PORT

    PORT代表了port、export

    二、分类

    在这里插入图片描述
    双向端口按照通信握手方式可以分为:

    • transaction双向通信方式
    • master和slave双向通信方式

    transport端口通过transport()方法,可以在同一方法调用过程中完成REQ和RSP的发出和返回。

    master和slave的通信方式必须分别通过put、get和peek的调用,使用两个方法才可以完成一次握手通信。master端口和slave端口的区别在于,当initiator作为master时,它会发起REQ送至target,而后再从target端获取RSP,当initiator使用slave端口时,它会先从target端获取REQ,而后将RSP送至target端。

    三、transport

    在这里插入图片描述

    transport双向通信方式

    class comp1 extends uvm_component;
    	uvm_blocking_transport_port #(itrans, otrans) bt_port;
    	`uvm_component_utils(comp1)
    	...
    	task run_phase(uvm_phase phase);
    		itrans itr;
    		otrans otr;
    		int trans_num = 2;
    		for(int i = 0; i < trans_num; i++) begin
    			itr = new("itr", this);
    			itr.id = i;
    			itr.data = 'h10 + i;
    			this.bt_port.transport(itr, otr);
    			`uvm_info("TRSPT", $sformatf("put itrans id: 'h%0x, data: 'h%0x | get otrans id: 'h%0x, data: 'h%0x", itr.id, itr.data, otr.id, otr.data), UVM_LOW)
    		end
    	endtask
    endclass
    		
    class comp2 extends uvm_component;
    	uvm_blocking_transport_imp #(itrans, otrans, comp2) bt_imp;
    	`uvm_component_utils(comp2)
    	...
    	task transport(itrans req, output otrans rsp);
    		rsp = new("rsp", this);
    		rsp.id = req.id;
    		rsp.data = req.data << 8;
    	endtask
    endclass
    
    class env1 extends uvm_env;
    	comp1 c1;
    	comp2 c2;
    	`uvm_component_utils(env1)
    	...
    	function void build_phase(uvm_phase phase);
    		super.build_phase(phase);
    		c1 = comp1::type_id::create("c1", this);
    		c2 = comp2::type_id::create("c2", this);
    	endfunction
    	function void connect_phase(uvm_phase phase);
    		super.connect_phase(phase);
    		c1.bt_port.connect(c2.bt_imp);
    	endfunction
    endclass
    

    双向端口处理类似于单向端口例化和连接,不同的只是要求实现对应的双向传输任务task transport(itrans req, output otrans rsp)

    展开全文
  • 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽,并达到实时通讯的目的,最大特点就是:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。 优势及特点: ...

    来源 | https://www.cnblogs.com/echoyya/p/14792788.html

    首先简单说一下常用的http协议的特点:http是客户端/服务器模式中请求-响应所用的协议,在这种模式中,客户端(一般是web浏览器)向服务器提交HTTP请求,服务器响应请求的资源。

    HTTP是半双工协议,也就是说,在同一时刻数据只能是单向流动,客户端向服务器发送请求(单向的),服务器响应请求(单向的)。

    那么如果想要实时通讯,能使服务器实时地将更新的信息传送到客户端,而无需客户端发出请求,目前有以下几种方式:

    1、 polling 轮循

    轮循:客户端和服务器之间会一直进行连接,每隔一段时间就询问一次(setInterval)

    特点:连接数会很多,一个接收,一个发送,而且每次发送请求都会消耗流量,也会消耗CPU的利用率 。

    (由于http数据包的头部数据往往很大,通常有400多个字节,但是真正被服务器需要的数据却很少,有时只有10字节左右,这样的数据包在网路上周期性的传输,难免对网络带宽是一种浪费)

    代码实现:

    index.html:

     <!--轮循 polling -->
      <div id="clock"></div>
      <script>
        let clock = document.getElementById('clock')
        setInterval(() => {
          let xhr = new XMLHttpRequest;
          xhr.open('get','/clock',true)
          xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
              clock.innerText = xhr.responseText  
            }
          }
          xhr.send()
        }, 1000);
    </script>
    
    
    

    app.js: node模拟服务器,运行app.js, http://localhost:3000/访问index.html

    let express = require('express')
    let app = express()
    app.use(express.static(__dirname))
    
    
    app.get('/clock',function(req,res){
      res.send(new Date().toLocaleString())
    })
    
    
    app.listen(3000)
    

    可以看到的效果就是,每隔1秒发送一次请求,服务器返回更新后的信息。

    2、long-polling 长轮循

    长轮循:是对轮循的改良版,客户端发送请求给服务器之后,需要满足一些条件才返回新的数据,反之若没有新数据就一直等待。

    当有新消息时才会返回给客户端,在某种程度上减少了网络带宽和CPU利用率的问题。

    链接会一直保持,直到有数据更新或链接超时,此时服务器不能在发送数据。

    代码实现:

    index.html

     <body>
        <div id="clock"></div>
      </body>
      <script>
        let clock = document.getElementById('clock')
        function send(){
          let xhr = new XMLHttpRequest;
          xhr.open('get','/clock',true)
          xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
              clock.innerText = xhr.responseText  
              send()    // 服务器响应之后,在发送第二次请求
            }
          }
          xhr.send()
        }
        send()
    </script>
    
    
    

    app.js: node模拟服务器,运行app.js, http://localhost:3000/访问index.html

    let express = require('express')
    let app = express()
    app.use(express.static(__dirname))
    
    
    app.get('/clock',function(req,res){
      let $timer = setInterval(() => {
        let date = new Date()
        let seconds = date.getSeconds()
        if(seconds % 5 == 0){     // 需要满足一些条件下,才会进行数据的返回
          res.send(date.toLocaleString())
          clearInterval($timer)   // 清除定时器
        }
      }, 1000);
    })
    
    
    app.listen(3000)
    

    可以看到的效果就是,服务器每隔5秒更新数据。然后在返回给客户端,结束一次请求响应,开始第二次的请求响应,而实际应用场景中,服务器的响应时候,会更长一些。

    3、 iframe 流

    在html 页面嵌入一个隐藏的iframe,将这个iframe的src 属性设为对一个长链接的请求,服务器端就能源源不断的向客户端推送数据

    代码实现:

    index.html

      <style>
        div{
          height: 100px;
          width:230px; 
          border: 1px solid slateblue;
          line-height: 100px;
          text-align: center;
        }
    </style>
      <body>
        <!-- iframe 流 -->
        <div id="clock"></div>
        <iframe src="/clock" frameborder="0"></iframe>
      </body>
      <script>
        let clock = document.getElementById('clock')
        function setTime(st) { 
          clock.innerText = st
        }
    </script>
    
    
    

    app.js :node模拟服务器,运行app.js, http://localhost:3000/访问index.html

    let express = require('express')
    let app = express()
    app.use(express.static(__dirname))
    
    
    app.get('/clock',function(req,res){
     setInterval(() => {
       res.write(`     // 此处 注意不能用上述案例中的send方法,send方法会默认执行end结束,
        <script>
          parent.setTime('${new Date().toLocaleString()}')
        </script>
       `)
     }, 1000);
    })
    
    
    app.listen(3000)
    

    可以看到的效果就是,服务器每隔1秒更新数据。并主动推送给客户端,但是同样存在问题,就是页签上的icon一直处于loading状态,表示响应一直未结束。用户体验并不是很好。

    4、 EventSource 流

    严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。

    也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。

    此时客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

    H5规范中提供了服务端事件EventSource,浏览器创建一个EventSource连接后,便可收到服务端发送的数据,这些数据需要遵守一定的格式,直到服务端或者客户端关闭该流,所以eventSource也叫做SSE(server-send-event)。

    SSE 就是利用这种机制,使用流信息向浏览器推送信息。

    目前除了 IE/Edge,其他浏览器都支持,实现方式对客户端开发人员而言非常简单,只需在浏览器中监听对应的事件即可。

    另外对于服务器,SSE使用的也是HTTP传输协议,这意味着我们不需要一个特殊的协议或者额外的实现就可以使用。

    其实说白了SSE 是单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。

    应用场景:在股票行情、新闻推送的这种只需要服务器发送消息给客户端场景中,显然使用SSE更加合适。

    EventSource的实现同样分为两部分:

    浏览器端:

    1. 在浏览器端创建一个EventSource实例,向服务器发起连接

    2. open:连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数

    3. message:客户端收到服务器发来的数据,就会触发message事件,可以在onmessage属性定义回调函数。

    4. error:如果发生通信错误(如连接中断,服务器返回数据失败),就会触发error事件,可以在onerror属性定义回调函数。

    5. close:用于关闭 SSE 连接。source.close();

    6. 自定义事件:EventSource规范允许服务器端执行自定义事件,客户端监听该事件即可,需要使用addEventListener

    index.html

    <style>
        div{
          border: 1px solid #ce4;
          width: 300px;
          height: 40px;
          padding: 20px;
          margin-bottom: 20px;
        }
        p{
          color: #888;
          font-size: 14px;
        }
    </style>
      <body>
        <p>默认事件message:</p>
        <div id="clock"></div>
        <p>自定义事件 yya:</p>
        <div id="yya"></div>
      </body>
      <script>
        let sse = new EventSource('/clock')
        let clock = document.getElementById('clock')
        let yya = document.getElementById('yya')
    
    
        // 监听连接刚打开时被调用
        sse.onopen = function () {  
          console.log('open');
        }
         // 监听服务器发过来的信息
         sse.onmessage = function (event) { 
          clock.innerText = event.data
        }
        // 监听链接请求失败 关闭流
        sse.onerror = function (event) {  
          console.log('error');
          sse.close();
        }
        // 监听自定义事件, 不能通过on的方式的去绑定
        sse.addEventListener('yya', function (event) {
          yya.innerText = event.data
        }, false);
    </script>
    

    服务器端:

    1. 事件流的对应MIME格式为text/event-stream。

    2. 服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本

    3. 服务端返回数据需要特殊的格式,分为四种消息类型,且消息的每个字段使用"\n"来做分割,

    • Event: 事件类型,支持自定义事件

    • Data: 发送的数据内容,如果数据很长,可以分成多行,用\n结尾,最后一行用\n\n结尾。

    • ID: 每一条事件流的ID,相当于每一条数据的编号,

    • Retry:指定浏览器重新发起连接的时间间隔。在自动重连过程中,之前收到的最后一个ID会被发送到服务端。

    app.js: node模拟服务器,运行app.js, http://localhost:3000/访问index.html

    let express = require('express')
    let app = express()
    app.use(express.static(__dirname))
    
    
    let counter = 0
    app.get('/clock',function(req,res){
      res.header('Content-Type','text/event-stream')
      let $timer = setInterval(() => {
        // 第一种写法
        res.write(`id:${counter++}\nevent:message\ndata:${new Date().toLocaleString()}\n\n`)
    
    
        // 另一种写法
        res.write(`event:yya\n`)     // 触发 自定义事件
        res.write(`data:${counter}\n\n`)
      }, 1000 );
    
    
      res.on('close',function(){
        counter = 0
        clearInterval($timer)
      })
    })
    
    
    app.listen(3000)
    

    实现效果:可以看到已经很好的解决了在iframe流中遗留的问题,也就是页签一直loading的现象。

    5、 websocket

    WebSocket 是H5下一种新的协议,它诞生于2008年,2011年成为国际标准,现在所有浏览器都已支持。

    它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽,并达到实时通讯的目的,最大特点就是:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。

    优势及特点:

    1. 在客户端和服务器之间保有一个持有的连接,两边可以随时给对方发送数据,有很强的实时性;

    2. 属于应用层协议,基于TCP传输协议,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器;

    3. 可以发送文本,也可以支持二进制数据的传输;

    4. 数据格式比较轻量,性能开销小,通信高效;

    5. 没有同源限制,客户端可以与任意服务器通信;

    6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL;

    WebSocket所涉及的内容远不止于此,这里只是抛砖引玉,带大家入门,知道有这么个东西,可以干一件什么样的事儿,要想完全掌握还需要下一番功夫嘞,那么就简单的用代码实现一下双工通讯,并使用序号标识出执行顺序:

     index.html

      <script>
        let socket = new WebSocket('ws://localhost:8888')
        socket.onopen = function () {
          console.log('1. 客户端连接上了服务器',new Date().getTime());
          socket.send('3. 你好')
        }
        socket.onmessage = function (e) {
          console.log('6',e.data);
        }
    </script>
    

    app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

    let express = require('express')
    let app = express()
    app.use(express.static(__dirname))
    
    
    app.listen(3000)
    
    
    let WebSocket = require('ws')
    let wss = new WebSocket.Server({port:8888})
    wss.on('connection',function(ws){
      console.log('2.服务器监听到了客户端的连接',new Date().getTime());
      ws.on('message',function(data){
        console.log('4.客户端发来的消息',data);
        ws.send('5.服务端说:你也好')
      })
    })
    

    因为我在执行的时候,突然有一个疑问,就是客户端连接服务器 和 服务器监听客户端的连接,那个方法先执行,于是做了一个实验,在打印的时候,分别打印出了对应的时间戳,打印结果显示大部分情况是2先执行,个别时候是同时执行,我试了很多次,没有出现1比2先的情况。

    期待有懂的小伙伴评论区指点一二~~~

    以上就是我今天要分享的全部内容!

    学习更多技能

    请点击下方公众号

    展开全文
  • UVM TLM通信之单向通信、双向通信和多向通信单向通信双向通信多向通信 单向通信 单向通信(unidirectional communication)指的是从initiator到target之间的数据流向是单一方向的,或者说initiator和target只能扮演...

    UVM TLM通信之单向通信、双向通信和多向通信

    单向通信

    单向通信(unidirectional communication)指的是从initiator到target之间的数据流向是单一方向的,或者说initiator和target只能扮演producer和consumer中的一个角色。

    在PORT代表了三种端口名:port、export和imp。
    按照UVM端口名的命名规则,它们指出了通信的两个要素:

    • 是否是阻塞的方式(即可以等待延时)
    • 何种通信方法
      在这里插入图片描述
      阻塞传输方式将blocking前缀作为函数名的一部分,而非阻塞方式则名为nonblocking。阻塞端口的方法类型为task,这保证了可以实现事件等待和延时;非阻塞端口的方式类型为function,这确保了方法调用可以立即返回。
      从方法名也可以发现,例如uvm_blocking_put_PORT提供的方法task put()会在数据传送完后返回,uvm_nonblocking_put_PORT对应的两个函数try_put()和can_put()是立刻返回的。

    uvm_put_PORT则分别提供了blocking和nonblocking的方法,这为通讯方式提供了更多选择。blocking阻塞传输的方法包含

    • Put():initiator先生成数据Tt,同时将该数据传送至target。
    • Get():initiator从target获取数据Tt,而target中的该数据Tt则应消耗。
    • Peek():initiator从target获取数据Tt,而target中的该数据Tt还应保留。
      与上述三种任务对应的nonblocking非阻塞方法分别是
    • try_put)
    • can_put()
    • try_get()
    • can_get()
    • try_peek)
    • can_peek()

    这六个非阻塞函数与对应阻塞任务的区别在于,它们必须立即返回,如果try_xxx函数可以发送或者获取数据,那么函数应该返回1,如果执行失败则应返回0。
    或者通过can_xxx函数先试探target是否可以接收数据,如果可以,再通过try_xxx函数发送,提高数据发送的成功率。

    在这里插入图片描述
    代码如下

    class itrans extends uvm _transaction; 
    	int id; 
    	int data; 
    	...
    endclass 
    class otrans extends uvm transaction; 
    	int id; 
    	int data; 
    	...
    endclass 
    class compl extends uvm_component; 
    	uvm_blocking_put_port #(itrans)bp_port; 
    	uvm_nonblocking_get_port #(otrans)nbg_port; 
    	uvm_component_utils(comp1)
    	...
    	task run_phase(uvm_phase phase); 
    		itrans itr; 
    		otrans otr; 
    		int trans_num=2;
    		fork 
    			begin 
    				for(int i=0;i<trans_num;i++) begin 
    				itr=new("itr", this); 
    				itr.id=i; 
    				itr.data='h10+i; 
    				this.bp_port.put(itr);
    				'uvm info("PUT",$sformatf("put itrans_id:'h%0x, data:'h$0x", itr.id,itr.data),UVM_LOW)
    				end
    			end 
    			begin 
    				for(intj=0;j<trans_num;j++) begin 
    					forever begin 
    						if(this.nbg_port.try_get(otr)==1) break; 
    						else #1ns; 
    					end
    					'uvm info("TRYGET", Ssformatf("get otrans id:'h80x, data: h%0x",otr.id,otr.data),UVM_LOW)
    				end
    			end
    		join
    	endtask
    endclass
    
    class comp2 extends uvm component; 
    	uvm_blocking_put_imp#(itrans,comp2) bp_imp; 
    	uvm_nonblocking_get_imp#(otrans,comp2)nbg_imp; 
    	itrans itr_q[$];
    	`uvm_component_utils(comp2)
    	...
    	task put(itrans t); 
    		itr_q.push_back(t); 
    	endtask 
    	function bit try_get (output otrans t); 
    		itrans i; 
    		if(itr_q.size()!=0) begin 
    			i=itr_q.pop front(); 
    			t=new("t", this); 
    			t.id=i.id; 
    			t.data=i.data<<8; 
    			return 1; 
    		end
    		else return 0;
    	endfunction 
    	function bit can_get(); 
    		if(itr_q.size()!=0) return 1; 
    		else return 0; 
    	endfunction 
    endclass 
    class envl extends uvm_env; 
    	comp1 c1; 
    	comp2 c2;
    	`uvm_component_utils(env1)
    	...
    	function void build_phase(uvm_phase phase); 
    		super.build phase(phase); 
    		c1=compl::type_id::create("c1", this); 
    		c2=comp2::type_id::create("c2", this); 
    	endfunction:build_phase 
    	function void connect_phase(uvm_phase phase); 
    		super.connect_phase(phase); 
    		c1.bp_port.connect(c2.bp_imp); 
    		c1.nbg_port.connect(c2.nbg_imp); 
    	endfunction: connect_phase 
    endclass
    

    双向通信

    与单向通信相同的是,双向通信(bidirectional communication)的两端也分为initiator和target,但是数据流向在端对端之间是双向的
    双向通信中的两端同时扮演着producer和consumer的角色,而initiator作为request发起方,在发起request之后,还会等待response返回。

    在这里插入图片描述

    双向端口按照通信握手方式可以分为:

    • transport双向通信方式
    • master和slave双向通信方式

    transpot端口通过transport()方法,可以在同一方法调用过程中完成REQ和RSP的发出和返回。
    master和slave的通信方式必须分别通过put、get和peek的调用,使用两个方法才可以完成一次握手通信。
    master端口和slave端口的区别在于,当initiator作为master时,它会发起REQ送至target端,而后再从target端获取RSP;当initiator使用slave端口时,它会先从target端获取REQ,而后将RSP送至target端。
    对于master端口或者slave端口的实现方式,类似于之前介绍的单向通信方式,只是imp端口所在的组件需要实现的方法更多了。

    在这里插入图片描述

    class compl extends uvm_component; 
    	uvm_blocking_transport_port #(itrans, otrans) bt_port; 
    	`uvm_component_utils(comp1)
    	...
    	task run_phase(uvm phase phase); 
    		itrans itr; 
    		otrans otr; 
    		int trans num=2; 
    		for(int i=0;i<trans_num;i++) begin 
    			itr=new("itr", this); 
    			itr.id=i; 
    			itr.data='h10+i; 
    			this.bt_port.transport(itr,otr); 
    			uvm_info("TRSPT", $sformatf("put itrans id:'h80x, data:'h%0x 
    				| get_otrans id:'h%0x, data:'h%0x",
    					itr.id, itr.data, otr.id, otr.data), UVM LOW)
    		end 
    	endtask 
    endclass
    class comp2 extends uvm_component; 
    	uvm_blocking_transport_imp #(itrans,otraps,comp2) bt_imp; 
    	`uvm_component_utils(comp2)
    	...
    	task tranport(itrans req,output otrans rsp); 
    		rsp=new("rsp", this); 
    		rsp.id=req.id; 
    		rsp.data=req.data<<8; 
    	endtask 
    endclass 
    class envl extends uvm env;
    	compl c1; 
    	comp2 c2; 
    `uvm_component_utils(env1)
    ...
    	function void build_phase(uvm phase phase); 
    		super.build_phase(phase); 
    		c1=compl::type_id::create("c1",this);
    		c2=comp2::type_id::create("c2",this); 
    	endfunction: build_phase 
    	function void connect_phase(uvm_phase phase); 
    		super.connect_phase(phase); 
    		c1.bt_port.connect(c2.bt_imp); 
    	endfunction: connect_phase 
    endclass
    

    1、事先定义initiator和target;2、例化端口;3、在target一侧定义import所对应的方法;4、顶层环境做连接;

    多向通信

    多向通信(multi-directional communication)服务的仍然是两个组件之间的通信,而不是多个组件之间的通信毕竟多个组件的通信仍然可以由基础的两个组件的通信方式来构建
    多向通信指的是,如果initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。
    comp1有两个uvm_blocking_put_port,而comp2有两个uvm_blocking_put_imp端口,我们对于端口例化可以给不同名字,连接也可以通过不同名字来索引,但问题在于comp2中需要实现两个task put(itrans t),又因为不同端口之间要求在imp端口一侧实现专属方法,这就造成了方法命名冲突,即无法在comp2中定义两个同名的put任务。

    UVM通过端口宏声明方式来解决这一问题,它解决问题的核心在于让不同端口对应不同名的任务,这样便不会造成方法名的冲突

    在这里插入图片描述

    `uvm_blocking_put_imp_decl(_p1)
    `uvm_blocking_put_imp_decl(_p2)
    class compl extends uvm_component; 
    	uvm_blocking_put_port #(itrans) bp_port1; 
    	uvm_blocking_put_port #(itrans) bp_port2;
    	`uvm_component_uti1s(comp1)
    	...
    	task run_phase(uvm_phase phase); 
    		itrans itrl, itr2; 
    		int trans_num=2; 
    		fork 
    			for(int i=0;i<trans_num;i++) begin 
    				itrl=new("itrl", this); 
    				itrl.id=i; 
    				itrl.data='h10+i; 
    				this.bp_port1.put(itrl); 
    			end 
    			for(int i=0;i<trans_num;i++) begin 
    				itr2=new("itr2", this); 
    				itr2.id='h10+i; 
    				itr2.data='h20+i; 
    				this.bp_port2.put(itr2); 
    			end 
    		join
    	endtask 
    endclass 
    class comp2 extends uvm_component; 
    	uvm_blocking_put_imp_pl #(itrans, comp2) bt_imp_pl; 
    	uvm_blocking_put_imp_p2 #(itrans, comp2) bt_imp_p2; 
    	itrans itr_q[$]; 
    	semaphore key; 
    	uvm_component_uti1s(comp2)
    	...
    	task put_p1(itrans t); 
    		key.get(); 
    		itr_q.push back(t); 
    		uvm_info("PUTP1", $sformatf("put itrans id:' h%0x, data:'h%0x",t.id,t.data),UVM_LOW)
    		key.put(); 
    	endtask 
    	task put_p2(itrans t); 
    		key.get(); 
    		itr_q.push back(t); 
    		uvm_info("PUTP2",$sformatf("put itrans id:'h%0x, data:'h%0x", t.id,t.data),UVM_LOW)
    		key.put(); 
    	endtask 
    endclass
    class env1 extends uvm_env; 
    	comp1 c1; 
    	comp2 c2; 
    	`uvm_component_uti1s(env1)
    	...
    	function void build_phase(uvm_phase phase); 
    		super.build_phase(phase); 
    		c1=comp1::type_id::create("c1",this);
    		c2=comp2::type_id::create("c2",this); 
    	endfunction: build_phase 
    	function void connect_phase(uvm_phase phase); 
    		super.connect_phase(phase); 
    		c1.bp_port1.connect(c2.bt_imp_p1);
    		c1.bp_port2.connect(c2.bt_imp_p2); 
    	endfunction: connect_phase 
    endclass
    

    其中仿真结果如下所示:

    uvm_test_top.env.c2[PUTP1] put itrans id:'h0, data:'h10
    uvm_test_top.env.c2[PUTP1] put itrans id:'h1, data:'h11
    uvm_test_top.env.c2[PUTP2] put itrans id:'h10, data:'h20
    uvm_test_top.env.c2[PUTP2] put itrans id:'h11, data:'h21
    

    上述代码通过宏uvm_blocking_put_imp_decl声明了两个后缀_p1和_p2。UVM会根据这两个后缀定义两个新的IMP类:
    uvm_blocking_put_imp_p1和uvm_blocking_put_imp_p2,并实例化这两个类:当相连接执行函数时,会自动调用,所以,只要完成后缀的声明,并在方法后面添加上相应的后缀就可以正常工作了

    这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

    展开全文
  • 1、进程:负责读取和分发事件2、应用程序:负责处理输入事件上面这两个进程会涉及哪些双向通信呢:1.进程会发送输入事件2.应用程序会告知事件处理完或APP已关闭这里大家可能会有疑惑,binder系统能否实现上面所说的...

    Android很多地方会涉及到进程间的通信,比如输入系统,那么进程间通信会涉及哪些内容呢?

    1、进程:负责读取和分发事件

    2、应用程序:负责处理输入事件

    上面这两个进程会涉及哪些双向通信呢:

    1.进程会发送输入事件

    2.应用程序会告知事件处理完或APP已关闭

    这里大家可能会有疑惑,binder系统能否实现上面所说的双向通信呢?

    答案是不行,binder分为server和client,每次都由client主动发出请求,server收到请求后进行答复,这样的缺点就是每次请求只能单方发起,server不能主动发送数据给client,这样自然不能称为双向通信。

    所以这里引入一个新的方法,叫“socketpair”

    APP通过socketpair调用得到两个文件句柄,假设这两个文件句柄是fd1和fd2,这两个文件都对应有两个缓冲区(send_buf、rcv_buf),当某个进程或线程通过fd1写到他的send_buf的时候,内核里面的socket就会把send_buf里面的数据写到fd2的rcv_buf里面,另外一个线程或进程就可以读取fd2得到那些数据了,相反同理。

    但是它也有缺点:由于是通过创建文件句柄来访问句柄实现的通信,那么谁可以看到这个句柄呢,只有当前APP创建出来的线程或它创建出来的子进程才能看到这些文件句柄,所以只适用于线程间通信,或者具有亲缘关系(父子进程)的进程间通信。

    那么如果想使用socketpair来实现任意间的进程间的双向通信怎么办?

    假设现在有APP1和APP2,这两个APP想进行任意间的进程通信,那么APP2需要得到APP1的fd2才行,怎么得到呢?可以通过binder通信,把fd2传给APP2,当然在APP2里面它就变为fd3了,这样这个任意的进程APP2就可以通过socketpair来进行通信了。这篇暂不讲解binder的实现方式

    下面讲解一下“Socketpair”的程序及使用:

    #include

    #include

    #include

    #include /* See NOTES */

    #include

    #define SOCKET_BUFFER_SIZE (32768U)

    /* 参考:

    * frameworks\native\libs\input\InputTransport.cpp

    */

    /* 线程1函数实现 */

    void *function_thread1 (void *arg)

    {

    int fd = (int)arg;/* 把文件句柄转换出来 */

    char buf[500];

    int len;

    int cnt = 0;

    while (1)

    {

    /* 向 main线程发出: Hello, main thread */

    len = sprintf(buf, "Hello, main thread, cnt = %d", cnt++);

    write(fd, buf, len);

    /* 读取数据(main线程发回的数据) */

    len = read(fd, buf, 500);

    buf[len] = '\0';

    printf("%s\n", buf);

    /* 延时5秒钟 */

    sleep(5);

    }

    return NULL;

    }

    int main(int argc, char **argv)

    {

    int sockets[2];

    /* 使用 socketpair 得到两个文件句柄到数组sockets */

    socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);

    /* 设置缓冲区, 每个文件句柄对应两个缓冲区,两个文件对应四个 */

    int bufferSize = SOCKET_BUFFER_SIZE;

    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    /* 创建线程1(thread1),并把文件句柄sockets[1]传给子线程thread1 */

    pthread_t threadID;

    pthread_create(&threadID, NULL, function_thread1, (void *)sockets[1]);

    char buf[500];

    int len;

    int cnt = 0;

    int fd = sockets[0];/* 当前main函数的文件句柄是sockets[0] */

    while(1)

    {

    /* 读数据: 读线程1发出的数据 */

    len = read(fd, buf, 500);

    buf[len] = '\0';

    printf("%s\n", buf);

    /* main thread向thread1 发出: Hello, thread1 */

    len = sprintf(buf, "Hello, thread1, cnt = %d", cnt++);

    write(fd, buf, len);

    }

    }

    程序非常简单,先是使用socketpair得到两个文件句柄并设置发送接收缓冲区,然后创建另一个线程,在线程中通过文件句柄读写数据到main线程中,mian也执行同样的操作实现双向通信。

    测试验证:

    90a3c0fec05832e3a9834e584ef8fd72.png

    检查是否存在这两个线程:

    c4521558fcc71ab783b803f71f3224b8.png

    我们还可以修改程序,让应用程序fork出一个子进程,然后让父子进程通过socketpair来实现双向通信,比较简单,这里就不细讲了。

    由于socekpair只适用于线程间通信,或者具有亲缘关系,如果想实现任意的两个进程间的双向通信就需要使用binder系统把fd传给另一个进程,这里简单说下过程,如下:

    使用binder传输文件句柄:

    假设APP1,open某个文件句柄得到fd1

    通过binder驱动根据fd1得到file结构体,即files->fdt->fd[fd1]

    从APP2的files->fdt->fd中取出一个空项,假设是fd2,让fd2指向fd1的file结构体,即files->fdt->fd[fd2]=file

    之后APP1可以通过fd1,APP2可以通过fd2访问同一个文件了

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    展开全文
  • 通信方式

    2021-06-18 01:36:55
    对于点对点之间的通信,按照消息传送的方向与时间关系,通信方式分为单工通信、半双工通信及全双工通信三种。中文名通信方式外文名Communication学科领域工程技术通信方式单工通信编辑语音单工通信(Simplex ...
  • 数据传输类型与通信方式

    千次阅读 2021-06-01 16:20:29
    数据在计算机中是以离散的二进制数字信号表示,但是在数据通信过程中,它是以数字信号方式还是以模拟信号方式表示,主要取决于选用的通信信道所允许传输的信号类型。 如果通信信道不允许直接传输计算机所产生的数字...
  • grpc介绍grpc是谷歌开源的一套基于rpc实现的通讯框架(官网有更完整的定义)。在搞懂grpc之前,首先要弄懂rpc是什么。下面是自己理解的rpc定义,若有不对,望指出:rpc官方称为 远程过程调用 。我这里理解为远程函数...
  • 此文档主要是介绍蓝牙双向通讯sdk的调用方法以及蓝牙双向传输的核心功能逻辑,用户可以根据需要自定义协议进行蓝牙双向通讯 二、SDK接入步骤: 1: 增加sdk库依赖: a)libs文件夹下增加sdk依赖库 b)应用app...
  • 关注+星标公众号,不错过精彩内容素材来源| 网络编排 | strongerHuang早期的物联网是指两个或多个设备之间在近距离内的数据传输,解决物物相连,早期多采用有线方式,比如RS32...
  • 串口通信: 串口通信指串口按位(bit)发送和接收字节。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用...通信从传输方向上可以分为单工通信、半双工通信、全双工通信三类。 一、全双工
  • 进程间通信一级目录二级目录三级目录 一级目录 二级目录 三级目录
  • 端系统及其两种通信方式端系统的概念端系统通信方式——客户服务器方式端系统通信方式——对等方式 端系统的概念 连接在互联网上的所有的主机称为端系统。 端系统通信方式——客户服务器方式 客户服务器方式(C/...
  • 单向板与双向板的受力特点两对边支承的板是单向板,一个方向受弯;而双向板为四边支承,双向受弯。若板两边均布支承,当长边与短边之比小于或等于2时,应按双向板计算。2.连续板的受力特点连续梁、板的受力特点是,...
  • 文章目录前言一、会话描述二、信令信令服务器设计信令协议1....WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer.
  • vue提供了多种通信方法,针对不同的通信需求,选择最合适的通信方式能帮助我们提高开发效率。本文简要介绍了八种通信方式以及适用场景,记录下在学习中的收货。 父子组件间的通信 props和$emit 这是父子组件在传参...
  • 什么是计算机通信计算机通信基本原理并行与串行通信方式异步通信和同步通信 什么是计算机通信 计算机通信是指计算机个计算机之间或计算机与终端之间进行信息传递的方式。 通信中有并行和串行两种方式。在单片机系统...
  • 1.1 进程间通信的定义   进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。...
  • 通信方式分为多种,按照数据传送方式可以分为串行通信与并行通信,按照通信的数据同步方式,可以分为异步通信与同步通信,按照数据的传输方向可以分为单工,半双工和全双工通信。 串行通信与并行通信 串行通信 ...
  • 一、串口通信简介 ...串行通讯可以进一步分为单工、半双工和全双工三种。在串口通信中,常用的协议包括RS-232、RS-422和RS-485。它们的主要区别在于其各自的电平范围不相同。 二、串行和并行通信 刚说到串口通
  • 进程间通信方式主要分为管道、SystemV IPC、 POSIX IPC三大类,管道作为进程间通信的一大重要方式,平时应用当中十分广泛。于是这里就先简单整理了一些关于管道的用法和注意事项。匿名管道管道是UNIX中最古老的进程...
  • 本发明涉及通信技术领域,特别涉及一种利用北斗短报文实现第三方数据双向传输的方法。背景技术:中国北斗卫星导航系统是中国自行研制的全球卫星导航系统,北斗RDSS是北斗系统区别于其他导航系统的特点之一,可以通过...
  • 介绍了单纤双向的技术原理,分析了单纤双向双向双向、无源波分前传方案在光缆建设、运维管理中的优缺点,并对单纤双向在5G前传中的应用提出了建议。
  • FPGA的双向口在FPGA的设计应用中使用及其广泛,如I2C接口中的SDA,3线制的SPI接口中的数据线,传统控制总线中的数据总线,以及内存的访问DDR3/DDR4的数据总线等都是双向访问的。双向访问涉及到的概念比较多,如三态...
  • 概述Linux下进程通讯方式有很多,比较典型的有套接字,平时比较常用的套接字是基于TCP/IP协议的,适用于两台不同主机上两个进程间通信, 通信之前需要指定IP地址. 但是如果同一台主机上两个进程间通信用套接字,还需要...
  • aidl的一些关键字: 1、oneway介绍 oneway 关键字用于修饰远程调用的行为,被oneway修饰了的...in、out、inout表示跨进程通信中数据的流向(基本数据类型默认是in,非基本数据类型可以使用其它数据流向out、inout)。
  • 在进程同步之中,也需要进程间交换一些信息,所以也可以归入进程通信中,但只能称为低级的进程通信。因为进程同步间的消息交换效率低、对用户不透明。 在进程间要传送大量数据的时候,可以使用OS提供的高级通信工具...
  • 按 的特征对通信系统进行分类,可将通信系统分为模拟通信系统与数字通信系统。2.数字传输技术是建立在模拟信号数字化和 技术基础之上。3.在W-CDMA业务信道帧中插入 比特。4.对于STM-1而言,帧长度为 个字节。5....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,739
精华内容 19,895
关键字:

双向通信方式分为