一直想写一个axi-vip的理解,但是介于个人水平有限,一直没能做出很好的总结,这个系列的内容将对这方面内容做出一个阐述。另外,个人水平有限,仅参考,有什么问题希望大家能够批评指正,共同进步!

这个vip同样是参考github上的一个项目,这个vip支持如下的特性:

(1)支持axi4、axi4-lite的协议;

(2)地址位宽、数据位宽、ID位宽可以配置;

(3)支持延迟写入数据和响应;

(4)支持间隙写入数据和读取响应;

(5)响应的顺序支持乱序和保序;

(6)支持读操作的interleave

首先分析tvip_axi_item,需要注意的是在这个item中存在一些新的数据类型time。对于SV来说,time数据类型如下,四值逻辑,用来反映当前时间。

除此以外还需要注意数据类型real,用于表示浮点数。

        uvm_event             address_begin_event;time                  address_begin_time;uvm_event             address_end_event;time                  address_end_time;uvm_event             write_data_begin_event;time                  write_data_begin_time;uvm_event             write_data_end_event;time                  write_data_end_time;uvm_event             response_begin_event;time                  response_begin_time;uvm_event             response_end_event;time                  response_end_time;

和configuration相关的约束:

constraint c_valid_id {(id >> this.configuration.id_width) == 0;}constraint c_valid_address {(address >> this.configuration.address_width) == 0;}constraint c_valid_memory_type {if (this.configuration.protocol == TVIP_AXI4LITE) {memory_type == TVIP_AXI_DEVICE_NON_BUFFERABLE;}}constraint c_valid_qos {qos inside {[this.configuration.qos_range[0]:this.configuration.qos_range[1]]};}

上面的约束中需要注意区分<<的作用,<<有两部分作用,一种是在verilog中作为左移右移使用,另一种是在sv中作为流操作符使用。对于前者来说,使用的格式是b=a<<2这种形式,也就是将a左移两位后赋给b;而对于流操作来说使用的格式是h={>>{j}}的方式,也就是将j按照从左到右的打包方式打包。

目前还没有理解 (id >> this.configuration.id_width) == 0的意思,个人认为是创建这么多位的0,也就是>>是当作左移右移符号使用的。

除此以外需要注意和configuration相关的几个约束,第一个busrt传输的长度;第二个burst传输的size;第三个4k边界地址;

constraint c_valid_burst_length {if (this.configuration.protocol == TVIP_AXI4) {burst_length inside {[1:this.configuration.max_burst_length]};}else {burst_length == 1;}}constraint c_valid_burst_size {if (this.configuration.protocol == TVIP_AXI4) {burst_size inside {1, 2, 4, 8, 16, 32, 64, 128};(8 * burst_size) <= this.configuration.data_width;}else {(8 * burst_size) == this.configuration.data_width;}}constraint c_4kb_boundary {((address & `tvip_axi_4kb_boundary_mask(burst_size)) +(burst_length * burst_size)) <= 4096;}

关于中间变量的约束

  constraint c_valid_write_data {solve access_type  before data;solve burst_length before data;(access_type == TVIP_AXI_WRITE_ACCESS) -> data.size() == burst_length;(access_type == TVIP_AXI_READ_ACCESS ) -> data.size() == 0;foreach (data[i]) {(data[i] >> this.configuration.data_width) == 0;}}

这里的strobe是和access_type和burst_length密切相关的。对于strobe信号需要注意的是:字节选通的信号是仅仅在写操作时才会起作用,在读操作的时候时不存在的,因此读操作的时候对strobe的size直接约束为0。随后按照字节选通的方式进行创建位数。

  constraint c_valid_strobe {solve access_type  before strobe;solve burst_length before strobe;(access_type == TVIP_AXI_WRITE_ACCESS) -> strobe.size() == burst_length;(access_type == TVIP_AXI_READ_ACCESS ) -> strobe.size() == 0;foreach (strobe[i]) {(strobe[i] >> this.configuration.strobe_width) == 0;}}

这个约束目前没搞清楚作用。

 constraint c_address_start_delay {`tvip_delay_constraint(start_delay, this.configuration.request_start_delay)}

对于写操作来说,实际上会出现写数据的delay,这里对delay进行约束,这里是指写数据中间可能存在延迟。

  constraint c_write_data_delay {solve access_type, burst_length  before write_data_delay;if (access_type == TVIP_AXI_WRITE_ACCESS) {write_data_delay.size() == burst_length;}else {write_data_delay.size() == 0;}foreach (write_data_delay[i]) {`tvip_delay_constraint(write_data_delay[i], this.configuration.write_data_delay)}}

对于写操作和读操作存在ready的delay,这里对ready的delay进行约束。注意这里对于写操作和读操作的区分,对于写操作来说,其B通道中会存在slave给master回应的过程,这个response收到master给slave的ready信号的影响,这里对于B通道的ready信号制作了一个size。原因在于一个response是表示这个burst传输的响应,因此对于写操作的一个burst操作来说,其size也只为1。而对于读操作来说,其size是burst_length,原因在于这里的ready是针对读操作返回给master的每一个数据的ready。

  constraint c_response_ready_delay {solve access_type, burst_length before response_ready_delay;if (access_type == TVIP_AXI_WRITE_ACCESS) {response_ready_delay.size() == 1;}else {response_ready_delay.size() == burst_length;}foreach (response_ready_delay[i]) {if (access_type == TVIP_AXI_WRITE_ACCESS) {`tvip_delay_constraint(response_ready_delay[i], this.configuration.bready_delay)}else {`tvip_delay_constraint(response_ready_delay[i], this.configuration.rready_delay)}}}

此外需要注意几个函数:

这个函数用来拿到item中的burst的长度。

function int get_burst_length();if ((configuration != null) && (configuration.protocol == TVIP_AXI4LITE)) beginreturn 1;endelse beginreturn burst_length;endendfunction

接下来分析tvip_axi_master_sub_driver:

driver的整体框架如下,也就是主要有三个任务,分别是address_thread、write_data_thread、response_thread,接下来具体分析这三个任务。

protected task main();forkaddress_thread();write_data_thread();response_thread();joinendtask

对于address_thread来说,整体框架如下:

 protected task address_thread();tvip_axi_item item;forever beginget_item_from_queue(address_queue, item);consume_delay(item.start_delay);begin_address(item);drive_address(1, item);wait_for_address_ready();drive_address(0, null);end_address(item);endendtask

get_item_from_queue函数原型如下:

注意在这个函数中下面的if条件语句没有看懂。

protected task get_item_from_queue(input tvip_axi_request_item_queue queue,ref   tvip_axi_item               item);queue.get(item);if (!vif.at_master_cb_edge.triggered) begin@(vif.at_master_cb_edge);endendtask

consume_delay函数原型如下:

protected task consume_delay(int delay);repeat (delay) begin@(vif.master_cb);endendtask

drive_address函数原型如下:

针对写操作:

protected task drive_address(bit           valid,tvip_axi_item item);vif.master_cb.awvalid <= valid;if (valid) beginvif.master_cb.awaddr  <= item.address;vif.master_cb.awid    <= item.id;vif.master_cb.awlen   <= item.get_packed_burst_length();vif.master_cb.awsize  <= item.get_packed_burst_size();vif.master_cb.awburst <= item.burst_type;vif.master_cb.awcache <= item.get_cache();vif.master_cb.awprot  <= item.protection;vif.master_cb.awqos   <= item.qos;endendtask

针对读操作:

protected task drive_address(bit           valid,tvip_axi_item item);vif.master_cb.arvalid <= valid;if (valid) beginvif.master_cb.araddr  <= item.address;vif.master_cb.arid    <= item.id;vif.master_cb.arlen   <= item.get_packed_burst_length();vif.master_cb.arsize  <= item.get_packed_burst_size();vif.master_cb.arburst <= item.burst_type;vif.master_cb.arcache <= item.get_cache();vif.master_cb.arprot  <= item.protection;vif.master_cb.arqos   <= item.qos;endendtask

wait_for_write_data_ready函数原型:

protected task wait_for_write_data_ready();do begin@(vif.master_cb);end while (!vif.master_cb.wready);endtask

wait_for_address_ready任务:

protected task wait_for_address_ready();do begin@(vif.master_cb);end while (!get_address_ready());endtask

这个任务也就是只要get_address_ready函数返回的内容不ready,那么就会一直打拍,get_address_ready任务内容如下:

protected function bit get_address_ready();return vif.master_cb.awready;endfunctionprotected function bit get_address_ready();return vif.master_cb.arready;endfunction

总的来说,整个address_thread的层次结构如下,首先通过get_item_from_queue拿到item,然后根据拿到的item中的相关设定判断是否需要延迟,随后驱动地址信息,最后等aw通道的ready信号拉高;

 对于write_data_thread()任务来说,整体的框架如下:

protected task write_data_thread();tvip_axi_item item;if (is_read_component()) beginreturn;endforever beginget_item_from_queue(write_data_queue, item);for (int i = 0;i < item.get_burst_length();++i) beginconsume_delay(item.write_data_delay[i]);if (i == 0) beginbegin_write_data(item);enddrive_write_data(1, item, i);wait_for_write_data_ready();drive_write_data(0, null, 0);endend_write_data(item);endendtask

get_item_from_queue函数和上面的函数是一样的,对于写操作来说,一个burst传输过程中每一个数据都是有可能存在延迟的,因此这里实际需要对burst传输过程中每一个数据进行delay操作,因此上面会存在for循环加consume_delay的操作。

drive_write_data函数原型如下,给数据和strobe,这里的strobe是每个数据都有相应的strobe,数据的最后一拍给wlast。

protected virtual task drive_write_data(bit           valid,tvip_axi_item item,int           index);vif.master_cb.wvalid  <= valid;if (valid) beginvif.master_cb.wdata <= item.data[index];vif.master_cb.wstrb <= item.strobe[index];if (configuration.protocol == TVIP_AXI4) beginvif.master_cb.wlast <= index == (item.get_burst_length() - 1);endendendtask

wait_for_write_data_ready函数原型如下:

protected task wait_for_write_data_ready();do begin@(vif.master_cb);end while (!vif.master_cb.wready);endtask

最后一部分是response_thread,在了解response_thread之前需要了解一个别的类tvip_axi_payload_store,这个类主要实现的功能如下:

*************************************tvip_axi_payload_store*********************************************

内部定义了item,data,strobe,response的队列,除此以外还存在一些任务,具体任务如下:

store_write_data,如果是写操作的化,会把这个函数输入的写数据和strobe推到内部定义的data和strobe里;
store_response,将这个函数输入的response存到自己的队列中,如果是读操作,还会将这个函数输入的数据推到相应的队列中;

接下来分析pack的几个任务,在分析之前需要需要分析item中的几个函数:主要有put_data、put_response、put_strobe三个函数,这三个函数的结构均如下(也就是将输入的response给到外面的response):

function void put_response(const ref tvip_axi_response response[$]);this.response = new[response.size()];foreach (response[i]) beginthis.response[i]  = response[i];endendfunction

接下来看:pack_write_data和pack_response。对于pack_write_data任务来说,是调用item中的put_data和put_strobe任务,这个item就是tvip_axi_payload_store中定义的item。对于pack_response是调用item中的put_response,如果是读操作的化,还会调用item中的put_data任务。

最后看两个任务,分别是get_stored_write_data_count和get_stored_response_count任务,这两个任务内部定义的data和response的size。

*************************************tvip_axi_payload_store*********************************************

在分析response_thread的任务之前,先分析一个sample_response任务,这个任务

protected task sample_response(input tvip_axi_id id,ref   bit         busy);tvip_axi_payload_store  store;store = response_stores[id][0];store.store_response(get_response_status(), get_response_data());if (get_response_last()) beginstore.pack_response();end_response(store.item);void'(response_stores[id].pop_front());busy  = 0;endendtask

也就是说response_thread主要包含的内容是把外部的response_stores给到内部的store,内部的store调用store_response将相关的数据和response存储到store内部中,如果最后一个数据,需要调用store的pack_response,也就是最后一笔数据的时候,将store中华所有的response整合一下到item中去。

这个函数任务的任务如下:

protected function tvip_axi_response get_response_status();return vif.master_cb.bresp;endfunctionprotected function tvip_axi_response get_response_status();return vif.master_cb.rresp;endfunctionprotected function tvip_axi_data get_response_data();return '0;endfunctionprotected function tvip_axi_data get_response_data();return vif.master_cb.rdata;endfunctionprotected function logic get_response_last();return '1;endfunctionprotected function logic get_response_last();return (configuration.protocol == TVIP_AXI4LITE) || vif.master_cb.rlast;endfunction

最后看下整个response_thread任务所执行的内容:

protected task response_thread();bit         busy;tvip_axi_id id;tvip_axi_id current_id;int         delay;forever beginwait_for_response_valid();//等待B通道的valid信号id  = get_response_id();//拿到ID信号if ((!busy) || (id != current_id)) begin//一开始循环过程中busy为0,因此执行下面语句,下次循环的时候不会执行这个if语句if (is_valid_response(id)) begin//response_stores是否存在对应id的responsebusy        = 1;//把busy变为1,id变为相同的idcurrent_id  = id;if (!response_stores[current_id][0].item.response_began()) beginbegin_response(response_stores[current_id][0].item);endendelse beginbusy  = 0;`uvm_warning("UNEXPECTED_RESPONSE", $sformatf("unexpected response: id %h", id))continue;endenddelay = get_response_ready_delay(current_id);//获得delay;if (default_response_ready) begin//一开始的一拍是ready的;sample_response(current_id, busy);//调用sample_response;if (delay > 0) begin//delay大于0,拉低ready,延迟几拍;drive_response_ready(0);consume_delay(delay);drive_response_ready(1);endendelse beginconsume_delay(delay);//一开始的一拍是不ready的,直接延迟一拍,再继续考虑后续延迟情况drive_response_ready(1);consume_delay(1);sample_response(current_id, busy);drive_response_ready(0);endendendtask

对于整个任务的框架就是等B通道valid,拿到id,第一次循环的时候执行上面的循环语句,变化busy状态和id号;获取delay;根据第一拍是否有delay进行延迟,最后调用sample_response任务,这个任务结束的时候会将busy置为0,为下一次循环做准备。

AMBA总线-结合axi-vip对axi4协议的理解1相关推荐

  1. AHB、APB、AXI三种协议对比分析(AMBA总线)

    一.AMBA概述  AMBA (Advanced Microcontroller Bus Architecture) 高级处理器总线架构  AHB (Advanced High-performance ...

  2. AMBA总线协议AHB、APB、AXI对比分析

    一.AMBA概述 AMBA (Advanced Microcontroller Bus Architecture) 高级处理器总线架构 AHB (Advanced High-performance B ...

  3. AMBA总线协议的学习-AHB,ASB,APB三种总线以及AXI接口

    一.AMBA概述 AMBA (Advanced Microcontroller Bus Architecture) 高级处理器总线架构 AHB (Advanced High-performance B ...

  4. AMBA总线概述——AHB、APB、AXI

    参考:https://blog.csdn.net/burningCky/article/details/109630018 https://blog.csdn.net/bleauchat/articl ...

  5. AMBA总线协议AHB、APB

    一.什么是AMBA总线 AMBA总线规范是ARM公司提出的总线规范,被大多数SoC设计采用,它规定了AHB (Advanced High-performance Bus).ASB (Advanced ...

  6. AMBA总线协议 之 APB总线协议

    AMBA总线协议概念: AMBA(Advanced Microcontroller Bus Architecture) 总线是由ARM公司提出的一种开放性的片上总线标准,它独立于处理器和工艺技术,具有 ...

  7. AMBA总线协议(一)——一文看懂APB总线协议

    0.AMBA总线概括 AMBA AHB 总线协议介绍请点击以下链接: AMBA总线协议(二)一文看懂AMBA2 AHB2与AMBA3 AHB-Lite总线协议的区别 AMBA总线协议(三)--一文看懂 ...

  8. AMBA总线—AHB总线协议详解

    文章目录 一.AMBA总线介绍 1.1.AMBA发展史 1.2.典型的AMBA系统 二.AHB总线(宏观构造) 2.1.AHB总线组成 2.2.AHB总线组成互连 2.3.AHB操作概述 2.4.AH ...

  9. AMBA总线协议之AHB学习记录(1)—ahb_bus(附verilog代码)

    目录 0.前言 1.AHB简介 2.ahb_bus实现(verilog) 3.总结反思 & 后面学习计划 0.前言 前段时间粗略过了一下riscv指令集相关内容,并对开源项目tinyriscv ...

最新文章

  1. 【Codeforces 738D】Sea Battle(贪心)
  2. java wed登录面 代码_java web 登录界面
  3. 设计模式(八)代理模式(结构型)
  4. android使用bintray发布aar到jcenter
  5. K8s中Pod健康检查源代码分析
  6. 百度地图2021十一大数据:全国高速拥堵里程超7000公里
  7. 阿里巴巴北京总部鸟瞰图曝光:今天又是想去阿里上班的一天!
  8. Python内置函数any()、map()组合运用案例一则
  9. android消息处理机制原理解析
  10. JavaSE集合练习题
  11. 偏心率计算公式matlab,结构计算整体指标(3)——扭转位移比及楼层偏心率
  12. 整个人麻掉!这竟然是一家可以养老的互联网大厂...
  13. xdm,外包能干吗?实在是....
  14. 基于Go语言Beego+Layui的OA办公系统
  15. 原生HTML:img 相关属性详解(alt属性,onerror事件,以及其他基本属性),css中的object-fit
  16. 挺着肚皮的小淘气 蒙语版铃声 挺着肚皮的小淘气 蒙语版手机铃...
  17. python绘制正六边形
  18. video/audio标签倍速播放
  19. 百度AIStudio之PaddleHub创意赛:你的名字,你的Face
  20. 2002~2018PJM每小时功率消耗文本数据集(145366行数据,具有明显的季节特性,单位为MW)

热门文章

  1. 跨境电商支付方式和如何玩转跨境支付
  2. python编译器报错:“RecursionError: maximum recursion depth exceeded in comparison”解决方案
  3. 拿来就能用的五个前端表白特效(免费)
  4. vue-cli4引入Element Plus 插件
  5. Python实现数据分析(八)数据清洗(DataClean)
  6. PAT 1002 写出这个数 (20分)(Java)
  7. [柒穆雨]学习ps有什么用?
  8. ICLR 2022 | 商汤提出cosFormer:在注意力中重新思考Softmax
  9. tomcat 日志报错 java.lang.UnsupportedClassVersionError: com/wlt/controller/IndexController
  10. Oracle表空间时间点恢复技术TSPITR