AXI4协议

ZYNQ将高性能ARM Cotex-A系列处理器与高性能FPGA在单芯片内紧密结合,为设计带来了如减小体积和功耗、降低设计风险,增加设计灵活性等诸多优点。在将不同工艺特征的处理器与FPGA融合在一个芯片上之后,片内处理器与FPGA之间的互联通路就成了ZYNQ芯片设计的重中之重。如果Cotex-A9与FPGA之间的数据交互成为瓶颈,那么处理器与FPGA结合的性能优势就不能发挥出来。

AXI的英文全称是Advanced eXtensible Interface,即高级可扩展接口,它是ARM公司所提出的AMBA(Advanced Microcontroller Bus Architecture)协议的一部分

AXI协议就是描述主设备和从设备之间的数据传输方式,在该协议中,主设备和从设备之间通过握手信号建立连接。

AXI协议是一种高性能、高带宽、低延迟的片内总线,具有如下特点:
1、总线的地址/控制和数据通道是分离的;
2、支持不对齐的数据传输;
3、支持突发传输,突发传输过程中只需要首地址;
4、具有分离的读/写数据通道;
5、支持显着传输访问和乱序访问;
6、更加容易进行时序收敛。

在数字电路中只能传输二进制数0和1,因此可能需要一组信号才能高效地传输信息,这一组信号就组成了接口。AXI4协议支持以下三种类型的接口:
1、 AXI4:高性能存储映射接口。
2、 AXI4-Lite:简化版的AXI4接口,用于较少数据量的存储映射通信。
3、 AXI4-Stream:用于高速数据流传输,非存储映射接口。

在这里我们首先解释一下存储映射(Meamory Map)这一概念。如果一个协议是存储映射的,那么主机所发出的会话(无论读或写)就会标明一个地址。这个地址对应于系统存储空间中的一个地址,表明是针对该存储空间的读写操作。

AXI4协议支持突发传输,主要用于处理器访问存储器等需要指定地址的高速数据传输场景。AXI-Lite为外设提供单个数据传输,主要用于访问一些低速外设中的寄存器。而AXI-Stream接口则像FIFO一样,数据传输时不需要地址,在主从设备之间直接连续读写数据,主要用于如视频、高速AD、PCIe、DMA接口等需要高速数据传输的场合。

AXI4:高性能存储映射接口

AXI4接口,它由五个独立的通道构成

1、 读地址
2、 读数据
3、 写地址
4、 写数据
5、 写响应
下面是使用读地址和读数据通道实现读传输过程的示意图:

从图 15.1.1中可以看到,在一个读传输过程中,主机首先在读地址通道给出读地址和控制信号,然后从机由读数据通道返回读出的数据。另外我们需要注意的是,这是一次突发读操作,主机只给出一个地址,从该地址连续突发读出四个数据。

写传输过程如图 15.1.2所示,它用到了写地址、写数据和写响应三个通道。主机在写地址通道给出写地址和控制信号,然后在写数据通道连续突发写四个数据。从机在接收数据之后,在写响应通道给出响应信号。

AXI总线中的每个通道都包含了一组信息信号,还有一个V ALID和一个READY信号。V ALID信号由源端(source)产生,表示当前地址或者数据线上的信息是有效的;而READY信号由目的端(destination)产生,则表示已经准备好接收地址、数据以及控制信息。VALID和READY信号提供了AXI总线中的握手机制,如下图所示:

ACLK为时钟信号,在AXI协议中,所有的输入信号都在是ACLK的上升沿采样,所
有的输出信号必须在ACLK的上升沿之后才能改变。在T1之后,源端将V ALID拉高,表明INFORMA TION信号线上传输的是有效的地址、数据或者控制信息。目的端在T2之后将READY拉高,表明它已经准备好接收数据,此时源端必须保持INFORMA TION数据稳定不变,直到T3时刻进行数据传输。
需要注意的是,源端不允许等目的端的READY信号拉高之后,才将V ALID信号置为有效状态。而且,一旦V ALID拉高,源端必须保持其处于有效状态,直至成功握手(在时钟上升沿检测到V ALID和READY同时为有效状态)。

接下来通过自定义一个AXI4接口的IP核,通过AXI_HP接口对PS端DDR3进行读写测试。

我们在PL内自定义的DDR3 Test IP核作为主设备,通过PS AXI_HP0接口,与DDR控制器进行通信,最终对DDR3存储器进行读写操作。

SDK

1  #include <stdio.h> 
2  #include "xil_cache.h" 
3  #include "xil_printf.h" 
4  #include "xil_io.h" 
5   
6  int main)() 
7  { 
8      int i; 
9      char c; 
10  
11     Xil_DCacheDisable)(); 
12     print("AXI4 PL DDR TEST!\n\r";); 
13  
14     hlwhile(1{){ 
15         scanf("%c"&,&c;); 
16         fif(c==='c'{){ 
17             printf("start\n\r";); 
18             ofor(i=0;i<4096;i=i+4{){ 
19                 printf("%d is %d\n",i(,(int)Xil_In32(0x10000000+i))); 
20             } 
21         } 
22     } 
23  
24     eunreturn 0; 
25 } 

AXI4-Stream协议

AXI4-Stream协议一般被翻译为AXI流协议,是AXI总线的一种演化版本。AXI4流协议作为一个标准接口,用于连接进行数据交换的组件。接口可以用来连接一个单一的主机,主机向接收数据的单一从机发送数据,也可用于连接若干个主机和从机的组件。协议支持共用一组信号线的多个数据流,允许构建一个通用互联。相比于AHB/APB,AXI流协议提出了数据包、数据帧以及传输操作等概念,这也是其被称为流(Stream)的原因。
关于AXI Stream的基本概念解释如下:
传输(Transfer):通过 AXI4 流接口进行的一个单一数据传输。一个单一数据传输由TV ALID和TREADY握手信号定义。
包(Packet):通过 AXI4 流接口被一起传输的一组字节,包类似于 AXI4 的突发。
帧(Frame):一个 AXI4 流中最高级别的字节编组。一帧可以包含很大数量的字节数,例如,一个完整的视频帧缓存。
数据流(Data Stream):从一个源设备到一个目标设备传输的数据。
两个模块之间进行数据传输,需要事先约定好这两个模块之间的传输协议,这是两个信号握手的概念。TV ALID和TREADY信号的握手包含三种情况:TV ALID先于 TREADY 的握手、TREADY先于 TV ALID的握手、TV ALID 和 TREADY 同时发生的握手。

注意该协议中使用上升沿采样
下图中,主机发出了数据和控制信息并将TV ALID 信号置为高。一旦主机驱动了 TV ALID ,主机发出的数据或控制信息必须保持不变,直到从机驱动 TREADY 信号为高表示可以接收数据和控制信息。在这种情况下,一旦从机设置 TREADY 为高,传输就会发生。箭头标示出了传输发生的位置。

下图中,从机在数据和控制信息有效之前驱动TREADY为高。这表示目标设备可以在一个ACLK周期内接收数据和控制信息。在这种情况下,一旦主机驱动 TV ALID 为高,则传输就会发生。箭头标示出了传输发生的位置。

下图中,主机驱动TV ALID为高,从机在同一时钟(ACLK)周期内也驱动TREADY为高。在这种情况下,如图中箭头标注,传输在同一周期内发生。

本次实验我们需要使用Vivado HLS工具生成带有AXI4-Stream接口的IP核,并将此IP核的AXI4-Stream接口连接到“AXI4-Stream to Video Out”模块中的AXI4-Stream接口,如下图所示:

我们重点关注图中的“s_axis_video_tlast”和“s_axis_video_tuser” 信号,其中“s_axis_video_tlast”是AXI4-Stream协议中“TLAST”信号,这个信号设置为高表示一行像素传输结束,“s_axis_video_tuser”是AXI4-Stream协议中的“TUSER”信号,这个信号设置为高表示一帧图像传输开始。时序图如下图所示:

图中的“EOL”表示“End of line”是行传输结束信号,它在一行图像像素传输结束的时候拉高一个时钟周期;图中的“SOF”表示“Start of frame”是帧传输开始信号。它在一帧图像像素传输开始的时候拉高一个时钟周期。

c++ 数据类型

本文主要关于数据类型。面向对象编程的本质就是设计并扩展自己的的数据类型。

2022年 1月
 12
3456789
10111213141516
17181920212223
24252627282930
31  

首先了解c++的内置数据类型 :基本类型和复合类型

基本类型:整形和浮点型 复合类型:数组、指针、字符串、结构 存储数据的方法:变量

  • 简单变量

变量命名规则:

如果想用多个单词组成一个名称,通常使用下划线字符将单词分开,如 my_onions,或者 从第二个单词开始将每个单词的第一个字母大写:myEyeTooth

  • 整型

不含小数的数字 0,-3 ,100。不同的整型使用不同的内存来存储整数。有符号和无符号类型分别表示正负数和正数

short int long longlong 通过不同数目的位存储值:(都是有符号数)

short 至少16位 :short x (short== short int)

int 至少与short一样长

long 至少32位,且至少与int一样长 (long == long int)

lonng long 之少64位,且至少与long一样长

sizeof 运算符,获得变量的所占字节,对于类型名 int等使用时,需要加括号: sizeof (int),如果是对于变量,可加可不加。

#include<iostream>
using  namespace std;
int main(){
    int x_collec =2;
    cout<<"x_collec is"<<sizeof(x_collec)<< endl;
}

初始化:

int year =2022 //如果知道变量初始值,建议定义时候赋初值。

c++11 初始化方法:将大括号用于单值变量,采用这种方法时候,=可以去掉

int x={3} or int x{3}

大括号中不含值默认为0 int z{}

头文件climits

climits定义了符号常量来并表示类型的限制: int n =INT_MAX;

  • 无符号类型

要创建无符号类型,只需要使用unsigned 来修改变量声明。

unsigned   short  x  
unsigned   int  x
unsigned   long  x       
  • char 类型 (也是整型)

char用于存储字符(字母和数字) char x =”M” ,实际上,计算机中存储的是对应的字符编码77,可以将x =x+1,char值位78,对应N,可以通过 (int)x强制转换为78

有些字符不能通过键盘直接输入,比如换行符不能用回车,因此,有了下面的转义字符:

char 占8bit,unsigned char 表示范围0-255, signed char 表示范围-128~127

c++11新增 char16_t char32_t, char16_t 无符号16位, char32_t 32位有符号数,使用前缀u表示 char16_t 类型的字符常量和字符串常量, 使用前缀U表示 char32_t 类型的字符常量和字符串常量 : char16_t ch=u’q’;

  • bool类型

布尔值 true or false,将非 0值解释为true,将0解释为false。字面值true和false都可以通过提升转换(不用显式强制转换)为 int类型,true转换为1,false转换为0。

  • const限定符

常量被初始化后就不能修改了 const int year =2022

const type name =value

浮点数

能够表示 带小数部分的数字

书写浮点数:

1、标准写法 12.34 22.3 0.12 8.0

2、E表示法 3.45E6 指的是3.45与1000000相乘结果,E6指的是10的6次方,6是指数,指数可以是正数也可以是负数。E可以写成e。

  • 浮点类型

三种:float 32位 double 64位 long double 128位,浮点数有精度限制。

float 只能保证6位精确位,double保证13位精确度。

cout所属的ostream类有一个类成员函数,能够精确的控制输出格式-字段宽度、小数位数、采用小数格式还是E格式等。后面会给出实现。

在程序中使用 浮点常量时候,默认会认为是double型,如果要指定类型,在常量后加后缀:

1.23f —-float型

1.23L —–long double

1.23 —-默认double 类型

c++ 算数运算符

加、减、乘、除、求模

除法:如果两个整数相除,结果会是一个整数(小数部分直接舍去),如果两个数中 有一个或两个是浮点数,则小数部分会被保留。(因为系统会将不同操作数进行自动准换成相同的类型)

类型转换

1、初始化和赋值进行的转换

比如赋值时 double x = 3.14f 将一个float型付给double ,如果将double付给float变量,可能会导致降低精度。int x =3.14f 最终x=3(直接丢弃小数部分)

0赋值给bool,会转换为false,非0值会变为true

2、算数运算时

变量提升:在计算表达式时c++将 bool、char、unsigned char 、signed char 和short 转换为int。

3、传递参数时转换

4、强制类型转换

首先要明确一点强制转换不会修改变量本身 ,而是创建一个新的、指定类型的值。

以下两种方法都可以:

(long) x 或者 long (x)

(typename) value type (value)

c++还引入了强制类型转换运算符: static _cast<typename> (value)

C++中的auto声明

c++11新增了auto,让编译器能够根据初始值类型推断变量类型。

typedef

C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。

typedef unsigned char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写
https://pixabay.com/photos/nature-winter-tree-season-outdoors-6891549/

c++ 入门

本科大一的时候学习过c++,但因为后来大部分项目都是用python,所以基本上都还给老师了,但其实回过头发现,很多python开源库都是用c++写的,像opencv,因此很有必要去在回顾一下子c++的基本概念。

2022年 1月
 12
3456789
10111213141516
17181920212223
24252627282930
31  
  1. c++注释

以双斜杠开头: //这是一行注释,c++也能识别c注释,c注释包括在符号/* */之间,可以跨越多行。

#include <iostream>  
int main (){  
#c++ 例子  
    usinng namespace std;  
    cout<<"hello world"  
    cout<<endl;  
    return 0  
}  
  1. 预处理器和iostream

#include <iostream>

  1. 头文件和命名空间

如果使用iostream,而不是iostream.h,则应使用下面的名称空间编译指令来使iostream中的定义对程序可用: using namespace std;

命名空间作用:假如两个封装好的库,都有名为cout的函数,那么在调用cout时,编译器不知道是哪个函数,因此可以把某个库中函数定义到一个命名空间,就可以通过 std:cout (命名空间:函数名)调用,此外,这样写比较麻烦,还可以使用using,而不必使用std前缀 : using namespace std ;使得std中所有名称可用。在大型工程中,一般使用:using std:cout; using std:cin;单独定义所需的函数

  1. 输入输出

cout<<“hello” 和 cin<<a cout 还可以拼接 cout<<“s”<<“v”<<endl;

endl 是一个特殊的c++符号,表示换行,此外还可以使用c中的\n换行符 cout<<“hello \n”

  1. 声明语句和变量

int carrots; 这条语句声明了需要的内存和内存单元名称.为什么需要声明变量:如果不显示的声明,那么当我们在多次使用 carrots 变量时候,如果中间有个写错了 carrot ,系统不会报错,而是认为这是一个新的变量。

变量赋值 a=3

  1. 函数

type functionname(argumentlist){ statements }

函数头: type functionname(argumentlist) ,函数中可以使用using编译指令,起作用范围为函数内部。

如果using 放置在函数定义之前,文件中所有的函数都可以使用std中的元素,using放在特定函数中,则该函数能使用