暂时未有相关云产品技术能力~
这个作品算是我大学期间的end show吧,其实也算是第一次做那么大的工程,逛博客看到别人在乱发我们的作品,也是无语…但是还是欢迎大家评论交流。版权声明本设计由本人及其团队共同完成,其他出处标注原创,均为faker。自证截图工程文件夹,带制作,可信赖。项目视频该项目参加了2019年全国大学生FPGA创新设计大赛,获得一等奖。视频链接指路。https://www.bilibili.com/video/BV1VJ411R7DE/第一部分 设计概述 /Design Introduction1.1 设计目的随着现代社会的高速发展,其带来的环境压力也越来越大,对于日益剧增的垃圾产量已经成为世界关注的焦点。垃圾分类成为制约我国环保产业发展的瓶颈,也是造成环境污染、资源再利用困难的根源之一,所以推进垃圾分类刻不容缓。然而垃圾分类总是“屡试屡败”工作推进难度巨大,“垃圾分类难”也同样困扰着大众,为了缓解垃圾分类的问题,减轻社会和居民的压力,如果可以实现可回收垃圾二次分类,对生活垃圾自主分类就显得尤为重要。于此,我们决定设计这样一个作品—智能识别自动投递分类垃圾箱,方便垃圾回收和利用。本作品是基于赛灵思公司的 pynq 开发平台设计,同时结合了机械控制和神经网络等知识,拥有可以自主识别垃圾并分类的功能,以 FPGA 作为核心处理器最大程度的实现了垃圾识别与分类。该智能垃圾桶将人工分类垃圾转为智能化,大大节省了人力,物力,降低垃圾分类的烦恼,具有长远的利用价值和非常巨大的市场前景。该作品有两种操作模式,可在图像识别和语音识别两种模式之间自由切换。1.2 应用领域本作品应用前景广泛,智能识别自动分类系统实现用摄像头取代了人类的眼睛,人工智能取代了大脑,电机取代了双手。实用性强且可使用范围广,既推动我国环保事业,又减轻社会对垃圾处理的压力,节约资源降低处理垃圾资金投入。1.3 适用范围有效减少环境污染,提高资源的再利用,可以适用于生活中的方方面面。例如,在家庭中使用,解决居民垃圾分类的烦恼,或是运用各大商场或人流量密集的地方,可以大大缓解垃圾分类的问题,减轻社会环境压力。此外,本作品可适用于可回收垃圾的二次分类,即加大资源的回收利用,有促进社会可持续发展具有一定的市场经济价值。第二部分 系统组成及功能说明 /System Construction & Function Description2.1 系统介绍图 2.1 系统流程图本系统主要由 Xilinx Zynq-7000 系列的 PYNQ-Z2 开发板作为主控中心,主要包含:摄像头采集模块、图像处理模块、实时显示模块、实时数据采集模块、信息提醒发送模块、驱动控制模块组成。总体结构如图 2.1 所示。流程图讲解:主控 PYNQ 开发板主要为提供神经网络的加速识别,搭载 USB 的网络摄像头进行数据的采集和功能参数的显示,同时在这个 ARM+FPGA 平台,为了最大化主控板优势,我们针对软件算法进行了相应的硬件实现进行提速,解决了在有限的资源内实现了资源利用和效率的最大化,同时在 PYNQ 端,为了保证产品的功能完整性,设计了两种识别模式,图像识别模式和语音识别模式。待识别完成后,我们将通过设置的通信协议进行数据的传输,使得辅助开发板可以进行自动投递。辅助开发板主要对各种传感器进行数据采集、数据分析、数据处理、控制设备的驱动、以及串口通信,实现不同开发板之间信息传输。接收所识别垃圾的信息,控制数字舵机的转动,将垃圾投入相应垃圾桶内。通过 FPGA 开发板将温度传感器测得的垃圾桶内温度值、超声波测得的桶内垃圾含量、是否存在有害气体、是否检测到火焰等数据经过处理发送给显示模块,最终在显示终端显示摄像头采集得到的照片、垃圾桶内温度、桶内垃圾含量、系统目前状态等数据。当检测到火焰,有害气体以及满桶警告时,将通过短信模块给管理员发送短信提醒。垃圾桶内部空间大,外部结构稳定,设置安全提示和自我保护功能,超声波和红外传感器实现智能检测桶内环境,遇到特殊情况,系统将自动发送信息至管理员进行相应的处理。2.2 FPGA 开发板神经网络实现:PYNQ辅助开发板:均可。第三部分 完成情况及性能参数 /Final Design & Performance Parameters本系统由摄像头模块、显示器模块、语音识别和语音播报模块、电机驱动模块、传感器等共同组成。具有摄像头拍照识别垃圾种类、实时检测温度、火焰、有害气体、垃圾含量多少、满桶警告、特殊情况短信提醒、语音播报、语音识别以及屏幕实时显示等功能,同时附加了语音识别和语音播报功能增加识别的准确率。市民将垃圾投入到垃圾桶后,桶内的摄像头会对垃圾进行拍照,通过数据库对比识别垃圾的种类,判定结果通过开发板回传至垃圾桶,控制舵机转动隔板完成分类。可实现功能:(1)在图像识别模式下,可以对可回收垃圾进行二次分类回收利用,可识别塑料、金属、玻璃、硬纸板、废纸并进行分类;(2)在语音识别模式下,可以直接对一些生活中常见垃圾进行可回收垃、厨余垃分类圾、其他垃圾、有害垃圾四种分类,投入相应垃圾桶内。目前,两个功能均已实现且可以随意切换使用,各模块之间依功能连接。传感器采集得到的数据经过处理之后发送给显示器模块,在串口屏显示终端显示垃圾桶内垃圾含量、系统安全性以及传感器实时监控环境数据。两种垃圾识别模式:语音识别模式和图像识别模式(1)使用图像识别,可以对可回收垃圾进行二次识别;(2)使用语音识别模式,可以直接对一些生活中常见垃圾进行可回收垃、厨余垃圾、其他垃圾、有害垃圾四种识别。将传感器检测一氧化碳、火焰传感器、红外温度的数据经过分析过后作是否开启的报警提示信号。系统还可以根据垃圾桶内垃圾的含量、是否出现火焰、存在有害气体等,通过短信发送给管理员进行及时处理。摄像头部分在摄像头采集模块中,我们将 python 内部 opencv 模块的图像处理函数加载至摄像头端,当摄像头采集视频流时可在显示屏实时观看所采集到的视频流。摄像头采集效果如下所示:语音识别模块我们可以很简便的配置语音命令,实现语音识别功能。首先需要通过软件设置词条,后将词条烧录至模块中。其次,利用 UART 通信协议发送启动指令驱动语音识别模块,当使用者对模块说话时,模块对说出的话进行识别并且与烧录进去的词条进行对比,若两者一致,则会向开发板发送信号,信号经过处理后控制震动和语音播报的开关。语音播报模块语音模块可以采样简单的io置数的播报模块也可以采样串口控制的模块。通过串口发送指令控制模块外置 SD 卡或 U 盘中预先存储的语音及音乐,串口屏发送过来的指令使用状态机检测,然后触发语音发送指令模块,触发语音播报。完全支持 FAT16、FAT32 文件系统,最大支持 32G 的 U 盘,TF 卡。U 盘和 SD 卡,需要格式化为 FAT16、FAT32 文件系统。通讯格式:起始位 0x7E + len 后面字节个数 + 命令(播放或暂停) + 查询歌曲序号(数据高字节) + 查询数据低字节 + 结束位 0xEF。直接发送的指令, 每发一条正确指令都会返回 “OK”错误返回“err”,修改指令第三、四位十六进制数据,即可播放指定文件夹语音。在此我们采用的时指定文件夹播放的方式进行播放,在外置存储器中新建文件夹,文件夹名必须为 01-99 文件号,内总曲目名字必须改 1-255。建好文件夹并且存储好要播放的语音后,就可以通过开发板发送播报指令,语音模块便开始工作播报语音。若想要播放 01 文件夹中文件名为 008 的语音,则发送指令:7E 04 41 01 08 EF(其中 7E 为起始地址,04 为位长度,41 为指令,01 08 表示 01 文件夹第文件名为 008 的语音)。显示模式多种显示供你选择~~~~通过 pynq 实现对相关资源进行调用进行实现图片的显示功能。舵机驱动模块本系统设计中所使用的数字电机为如图所示。在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率,而不受负载变化的影响,当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一定的角度,它的旋转的角度可以通过控制脉冲的占空比进行改变调整。位置控制是通过发脉冲来控制的,位置控制模式一般是通过外部输入的脉冲的频率来确定转动速度的大小,通过脉冲的个数来确定转动的角度。一个周期是 20MS,一个周期的高脉冲范围在 0 到 2.5MS 其对应的舵机转动角度范围是 0 到 270 度。只要设定相应转动角度对应的计数值,则可以实现相应的角度旋转。其 RTL 如图 3.所示:串口屏显示模块我们在串口屏与 FPGA 信息交互时使用了 UART 通信协议。计算机和外部设备的连接,基本上使用了两类接口:串行接口和并行接口。并行接口是指数 据的各个位同时进行传送,其特点是传输速度块,但当传输距离远、位数又多 时,通信线路变复杂且成本提高。串行通信是指数据一位位地顺序传送,其特点是适合于远距离通信,通信线路简单,只要一对传输线就可以实现双向通信,从而大大降低了成本。当传感器测得数据,会有指令数据通过 UART 通信协议发出,将传感器发出的信息传送给 FPGA 进行综合处理,串口屏会根据接收到的信息输出相应的数值。在这样的信息传输过程中是以 UART 通讯协议进行通信的。我们的作品主要是通过串口屏进行显示,我们采用 UART 通信协议的接受模块接收,根据发来的传感器检测的数据,使 FPGA 中状态机的状态与串口屏的显示界面一致,会向串口屏发送相应的显示信息进行显示,例如垃圾桶内温度、桶内垃圾含量、是否检测到火焰或者有害气体等。本模块负责接收串口屏发来的信息。我们可以在界面的看到垃圾桶内环境监测,环境正常显示绿色的数值,当出现异常时,显示红色的数值,如图 3.6 所示。MCU90615 红外测温模块此 MCU90615 模块通过发送对应的 AT 指令集设置读取数的时间和波特率命令指令:连续输出指令:0xA5+0x45+0xEA----------------温度数据(模块返回数据类型为 0x45)查询输出指令:0xA5+0x15+0xBA ---------------温度数据(模块返回数据类型为 0x45)配置指令:(掉电重启后生效)波特率配置:0xA5+0xAE+0x53 ---------------96000xA5+0xAF+0x54 ---------------115200(默认)上电是否自动发送温度数据配置:0xA5+0x51+0xF6---------------上电后自动输出温度数据0xA5+0x52+0xF7---------------上电后不自动输出温度数据(默认)0xA5+0xAF+0x54 ---------------115200(默认)通过状态机截取对应位数据输出,将数据发送给串口屏,从而完成显示,RTL 图GSM 短信模块SIM800A 模块通过发送和接受国际电信联盟规定的 AT 指令集来进行控制和数据接收,本系统使用了 UART 通信协议的发送模块进行指令的发送。本系统短信模块的功能主要体现在 当垃圾桶的环境异常时,会给管理员及时通知异常情况,以便处理。超声波测距模块该模块我们通过超声波传感器实时的检测垃圾桶内垃圾含量,进行实时数据监测和满桶警报当垃圾桶即将装满时会及时发送短信至垃圾管理员。可以实时在串口屏上显示垃圾桶内垃圾含量,并且当垃圾桶内可用高度小于 5cm 时认定垃圾桶即将装满时会及时并自动发送短信至垃圾管理员。RTL 图如图串口该部分不做赘述。第四部分 总结 /Conclusions4.1 主要创新点(1)在使用 PYNQ 核心板作为主要处理器处理数据时,使用 hls 对卷积和池化层的硬件电路设计,实现了 cnn 卷积神经网络在 pynq 的复现。(2)对于已经训练的垃圾进行识别其准确率可达 90%以上。(3)增加了语音识别和图像识别两种模式,两种操作随意切换,适用于不同场景,对应相同的功能,增加识别精准度。(4)增加自启动功能。(5)使用硬件加速,提高了识别速度。(6)增加卷积和池化的硬件电路设计,增强网络实现的效率。(7)增加传感器进行环境监测,数据实时显示,保证出现异常情况进行报警,数据及时反馈给管理员。(8)利用摄像头采集垃圾的图像后 PYNQ 使用在 PC 端训练并保存的神经网络在该数据集下的权值。4.2 可扩展之处(1)扩大数据集,对不同种类垃圾图片进行收集、训练,增加可识别垃圾种类,提高识别精准度。(2)对于云台旋转的角度精确值可以更为精确,通过对于器件、参数的调整,预计达到肉眼不可见的误差。(3)可以升级使用更加高级的算法和网络使得分类更加的精准和高效。
前言RFSoC中最重要的部分是射频直采ADC和DAC的配置,因此了解内部相关原理结构可以帮助我们更好理解相关功能配置参数含义。本文参考官方手册,主要对RF-DAC 模拟输出进行介绍。RF-DAC简介每个 RF-DAC 块都包含一个完整的时钟支持结构,带有一个 PLL 和必要的同步逻辑。 tile 中的每个 RF-DAC 都有一个高度可配置的 FIFO,允许内部互连逻辑直接高速访问 RF-DAC。某些功能只能在一个 tile 中的 RF-DAC 配对时执行。 偶数 RF-DAC 用于 I 数据路径,奇数 RF-DAC 用于 Q 数据路径。一个 tile 的所有可用内置功能和一个 tile 中的每个 RF-DAC 都是用户可编程的。 Vivado IDE 中的RF 数据转换器内核配置界面和 RFdc 驱动程序 API 可用于配置 RF-DAC 的数字和模拟功能。RF-DAC 模拟输出tile 中的每个 RF-DAC 都有自己的差分模拟电流输出缓冲器/驱动器。 输出电流是互补的; 两个电流之和始终等于 RF-DAC 的满量程电流。 数字输入代码决定了提供给负载的有效差分电流。 差分 RF-DAC 输出通常使用电容器进行交流耦合。 假设满足正确的共模偏置和负载阻抗要求,还支持 RF-DAC 输出直流耦合。发送传递函数当所有数字输入位为高电平时,差分输出提供最大输出电流。输出电流(使用二进制格式)显示在以下等式中。其中:BinDataIn = 14 位数字输入IOUTFS = 满量程输出电流Vcm 是每个 IVOUT leg上的共模电压RF-DAC 输出电流模式(第 1 代/第 2 代)RF-DAC 输出电流是可配置的,可选择在 20 mA 或 32 mA 模式下工作。 在 20 mA 模式下,将 DAC_AVTT 设置为 2.5V,在 32 mA 模式下,将其设置为 3.0V 以保持线性性能。可以在 IP 内核配置屏幕的 Advanced 选项卡中设置输出电流(见下图)。RFdc Driver API Commands (Gen 1/Gen 2)// 获取 RF-DAC Tile1、Block2 的输出电流为 32mA XRFdc_GetOutputCurr(ptr, 1, 2, XRFDC_OUTPUT_CURRENT_32MA);可变输出功率 (VOP) (Gen 3)许多发射器系统包括可变增益放大器 (VGA) 级,允许在模拟域中放大或衰减信号。RF-DAC 模拟电路支持调整输出功率的能力,并与数字控制相结合以实现 RF-DAC 可变输出功率 (VOP) 功能。VOP 详细信息(第 3 代)下图显示了片上 VOP 功能。RF-DAC 的满量程电流可以通过以下方式得出:其中 N = 0, 1, 2, …,受最大满量程电流限制。为了避免增益急剧变化时对无线发射器信号链的潜在危害,API 设计为将一个大的跳跃分成几个小步骤,然后以一定的时间间隔依次更新 VOP。 步长约为当前 VOP 值的 ±10%。启动期间的默认满量程电流由 IP 配置设置。VOP 向前兼容性(第 3 代)在 Gen 1 和 Gen 2 中,只有 20 mA (2.5V) 和 32 mA (3.0V) 模式可用。 Gen 3 IP 中提供了向后兼容模式,以适应现有的硬件设计。 在此模式下配置时,VOP 功能不再可用,RF-DAC 与 Gen 1 和 Gen 2 一样,在 DAC_AVTT 上输出 2.5V 电源时 20 mA 和 3.0V 电源时 32 mA。电源方案由片上所有瓦片共享,因此一个芯片可以使用 VOP 模式或 20/32 mA 模式。 不允许在一个磁贴上启用 VOP,在其他磁贴上启用 20/32 mA 模式。一致的共模电压(第 3 代)每个输出上的共模电压会随着 VOP 的变化而变化。 在需要直流耦合接口的应用中,通常需要固定 Vcm 以匹配外部设备。 以下两个选项可用于一致的共模电压:向前兼容模式:在向前兼容模式下,共模电压与 Gen 1 和 Gen 2 一样固定,VOP 功能在此模式下不可用。DC 耦合模式:RF-DAC DC 耦合模式在启用VOP 功能的同时固定共模电压。 在这种模式下,VOP 范围减小。VOP 更新(第 3 代)实时 VOP 信号接口使 RF-DAC 输出功率能够从 PL 进行调整。 更新输出功率的过程如下所示。当 tile 的 dacX_vop_busy 输出为低电平时,可以将更新输入与所需的 VOP 代码一起置位。 VOP 代码是一个 10 位值,指示所需的满量程电流设置。 通过同时断言它们的更新输入,可以同时更新同一瓦片中的多个切片。VOP 代码应保留在 dacXY_vop_code 输入中,直到该过程完成。 在更新过程和瓦片启动期间断言忙碌信号。当 VOP 更新成功完成时,完成输出 (dacXY_vop_done) 被断言。 两次 VOP 更新之间允许的最大跳变约为当前 VOP 值的 ±10%(以 mA 为单位)。 如果请求更大的跳转,转换器中的寄存器将不会被写入,并且完成信号也不会被断言。 如果需要更大的跳转,API 可用于设置正确的 VOP 值。当多个 RF-DAC 通道同时被修改时,每个通道的 dacXY_vop_done 信号在该通道的 VOP 更新完成时被置位。 dacX_vop_busy 信号保持高电平,直到所有通道都已更新。 下面显示了同时更新转换器 0 中的通道 0 和通道 1 的示例。RFdc 驱动器也可用于更新可变输出功率。 但是,驱动程序和实时信号方法是互斥的。 在使用 VOP 信号接口时,不应使用驱动器来修改输出功率。
前言RFSoC中最重要的部分是射频直采ADC和DAC的配置,因此了解内部相关原理结构可以帮助我们更好理解相关功能配置参数含义。本文参考官方手册,主要对RFSoC ADC的可编程逻辑数据接口、多频带操作、以及奈奎斯特区的操作进行介绍。RF-ADC 可编程逻辑数据接口RF-ADC 块和 PL 之间的数据接口通过使用 AXI4-Stream 协议的并行数据流实现。 这些数据流通过FIFO 输出,在用户应用程序和 RF-ADC 块之间提供灵活的接口。 对于第 3 代,最大接口宽度为 192 位,最多代表 12 个16 位小端字。数据流和相关的 FIFO 具有可配置的字数,可以灵活地在字数和时钟频率之间进行选择,以与 PL 设计接口。 每个 tile 有四个流,命名约定是 mXY_axis,其中 X 代表 RF-ADC tile 编号,Y 代表从该 tile 输出的流 (FIFO)。 下图显示了接口。接口数据格式数据流代表真实数据或 I/Q 数据,具体取决于 RF-ADC 块配置。 对于双 RF-ADC 块,给定的流是实数,I 或 Q。如果 RF-ADC 配置有 I/Q 输出数据,则具有偶数的流代表 I 数据,具有奇数的流代表 Q 数据。 这些双实数和 I/Q 配置显示在 RF-ADC IP 配置中。对于四路 RF-ADC 块,给定的流要么是真实的,要么是 I/Q 交错的。 如果 RF-ADC 配置有 I/Q 输出数据,则流的偶数样本代表 I 数据,奇数样本代表 Q 数据。 这些 Quad real 和 I/Q 配置将在以下部分中进行说明。RF-ADC 接口数据和时钟速率每个通道到 PL 的总数据速率由许多因素决定,RF-ADC 采样率、抽取因子和 I/Q/Real 数据格式。FIFO 通过允许更改每个时钟的字数,提供了一种将此数据速率与 PL 设计的时钟频率相连接的方法。唯一的要求是字数和时钟速率相结合,以匹配 RF-ADC 的输出数据速率和抽取率(如果启用)。一个 tile 中的所有 RF-ADC 共享一个公共接口时钟频率。 这由以下等式显示,其中 2G_IQMode 对于 Quad RF-ADC tile 设置为 2,I/Q 模式启用,否则设置为 1。内核根据 RF-ADC 采样率和数据路径设置自动计算数据速率。因为每个块都有独立的时钟,所以可以在每个块的基础上指定采样率、时钟率、PL 速率和配置。PL 时钟接口所有四个片流的 AXI4-Stream 数据与来自 PL 的时钟同步,PL 的命名约定为 mX_axis_aclk,其中 X 代表 RF-ADC 瓦片编号。 此时钟必须处于 IP 内核配置屏幕上显示的所需 AXI4-Stream 时钟指定的频率。RF-ADC 块还输出可供 PL 使用的时钟。 该输出时钟是 RF-ADC 采样时钟的分频版本,因此对其进行频率锁定。 此时钟具有 clk_adcX 的命名约定,其中 X 表示 RF-ADC 块编号。接口 FIFO 溢出通过接口FIFO 的数据速率必须在 RFADC 块运行期间保持恒定,PL 时钟和 RF-ADC 采样时钟域之间没有频率漂移。 如果这些域之间存在频率不匹配,则可能会发生 FIFO 溢出。 接口 FIFO 具有确定是否发生 FIFO 溢出的内置功能,使用 IP 中断机制将其标记到 PL。溢出有两种类型,实际溢出和边际溢出。 实际溢出表明 FIFO 读/写指针重叠,这意味着数据没有在域之间安全传输,必须采取措施。 边际溢出是一个警告,表示 FIFO 读/写指针接近重叠。 正常操作期间不应发生溢出,如果观察到溢出,则表明 PL/PCB/IP 的时钟基础结构配置不正确。同步FIFO 为 RF-ADC 块提供灵活的数据和时钟接口。 但是,与所有双时钟 FIFO 一样,延迟可能会在一个 tile 和另一个 tile 之间变化。 虽然 tile 中的所有通道都具有相同的延迟,但某些应用可能需要使用多个 RF-ADC tile,并且需要在所有 RF-ADC 通道中匹配延迟。 这些应用程序可以使用多块同步 (MTS) 功能来实现这种块间同步。RF-ADC 多频带操作RF-ADC 可以配置为在多频带模式下操作。 这是输入模拟输入由上变频(混合)到不同载波频率的基带信号组成的地方。多个 DDC 模块用于下变频模拟输入以恢复单独的基带信号。RF-ADC 多频段功能支持以下配置:每对2x 多频段实际数据。 一个 RF-ADC 模拟输入处于活动状态。 两个 RF-ADC 输出均启用。每对2x 多频段I/Q 数据。 两个 RF-ADC 输入都处于活动状态,一个用于 I,一个用于 Q。两个 RF-ADC 输出均启用。每个区块4x 多频段实际数据(仅限四路ADC 区块)。 一个 RF-ADC 模拟输入处于活动状态。 所有 4 个 RF-ADC 输出均启用。每块4x 多频带I/Q 数据(仅限四ADC 块)。 两个 RF-ADC 输入处于活动状态,一个用于 I,一个用于 Q。所有 4 个 RF-ADC 输出均启用。当多频段关闭时,I 和 Q 输入直接通过多频段路由逻辑。当多频段开启时,I 和 Q 输入被路由到 tile 中的多个 DDC 模块。RF-ADC 多频段是通过将一个 RF-ADC 模拟模块的输出路由到多个 RF-ADC DDC 模块来实现的。 每个块处理一个数据带,并且可以从多个载波混合到基带。 下图中的 Quad ADC 块显示了这一点。RF-ADC Tile 0 (Tile_224) 配置为实际输入到 I/Q 输出模式。 ADC0 转换双频信号; ADC1 关闭。 顶部对可以配置为独立的 RF-ADC。 双频输出路由到 ADC0 和 ADC1 的 DDC 模块。 DDC 模块中的混频器可以配置为从输入数据中提取正确的频带。RF-ADC Tile 1 (Tile_225) 配置为 4x 多频段 I/Q 输入到 I/Q 输出模式。 这里 ADC0 承载四波段 I 信号,ADC1 承载 Q 数据。 ADC2 和 ADC3 关闭。 RF-ADC 的输出被路由到所有四个 DDC 模块。 每个 DDC 都可以配置为从所需的频带中提取数据。RF-ADC 奈奎斯特区操作每个 RF-ADC 通道都可以对第一或第二奈奎斯特区的信号进行采样。 为确保 RFADC 性能最佳,RF-ADC 配置设置应指示预期的操作区域。第一奈奎斯特区定义为 0 和 Fs/2 之间的信号。第二奈奎斯特区定义为 Fs/2 和 Fs 之间的信号。只要信号满足 RF-ADC 输入带宽要求,也可以使用其他奈奎斯特区 . 1、3、5、…区称为奇数区,2、4、…区称为偶数区。RF-ADC IP 配置RF-ADC 模块可以配置为多种模式。 Vivado IDE 的 IP 内核配置屏幕上提供了基本配置选项,并且可以使用 RFdc 驱动程序 API 配置高级操作模式。RF-ADC 可用于四路或双路配置,具体取决于 tile。双 RF-ADC 配置选项双 RF-ADC 实输入到实输出下图显示了一个双 RF-ADC,它具有实际数据输入到实际数据输出、1x 抽取、混频器被绕过并以 250 MHz AXI4-Stream 时钟运行。双路 RF-ADC 实输入到 I/Q 输出下图显示了一个双路 RF-ADC,其具有真实数据输入到 I/Q 数据输出、1x 抽取、启用混频器并以 500 MHz AXI4-Stream 时钟运行。
前言RFSoC中最重要的部分是射频直采ADC和DAC的配置,因此了解内部相关原理结构可以帮助我们更好理解相关功能配置参数含义。本文参考官方手册,主要对RFSoC ADC的校准机制进行介绍。RF-ADC 校准机制Zynq UltraScale+ RFSoC 中的每个 RF-ADC 都建立在交错架构中的多个子 RF-ADC 之上。 交织过程的性质要求执行复杂的校准算法,以从 RF-ADC 获得最佳动态范围性能。 RFSoC 中的每个 RF-ADC(双通道或四通道变体)都有一个内置校准过程,包括一个前台校准 (FG CAL) 步骤和一个后台校准 (BG CAL) 步骤。 FG CAL 步骤仅在 RF-ADC 上电状态机(启动初始化)期间执行。 BG CAL 步骤是一个设计为在 RF-ADC 运行期间可选操作的过程。 每个 RF-ADC 的两个校准步骤都是并行且独立地执行的。 下图显示了其中一个 RF-ADC 的框图,其中 N 个子 RF-ADC 中的每一个都有内部校准块。校准子系统由三个主要模块组成,旨在估计和纠正交错子 RF-ADC 的各种缺陷和失配。时间交错偏移校准模块 (Time Interleaved Offset Calibration Block,OCB)该模块校正每个子 RF-ADC 的 DC 偏移。 任何未校正的残余 DC 偏移在 k * Fs/N 处显示为杂散,其中 Fs 是 RF-ADC 的复合采样率,N 是交错在一起的子 RF-ADC 的数量,k = 0, 1, 2, … N。对于 Dual RFADC tile 变体,N 为 8,对于 Quad RF-ADC tile 变体,N 为 4。 该块进一步分为两个子块,称为 OCB1 和 OCB2。 OCB2 仅在 FG CAL 期间运行,而 OCB1 在 FG 和 BG CAL 期间运行。 OCB2 模块在启动时消除子 RF-ADC 的偏移。OCB1 模块在 BG 校准过程中连续消除子 RF-ADC 偏移,而不会影响偏移位置处的输入信号内容。增益校准模块 (Gain Calibration Block,GCB)此模块校正交错子 RF-ADC 之间的增益差异。 任何残余差异都会导致 +/-fin + (k/N) * Fs 处的杂散信号,其中 Fs 是 RFADC 的采样率,N 是子 RF-ADC 的数量,fin 是输入的频率 信号。 GCB 需要存在输入信号。 此输入信号的最小功率为 -40 dBFS。f = k * Fs/(2N) 处的信号仓被忽略,不计入信号功率。 只要输入信号功率下降到 -40 dBFS 以下超过 100 μs,该模块就应该处于冻结模式。时间偏移校准模块 (Time Skew Calibration Block,TSCB)该模块校正交错子 RF-ADC 之间的时间偏移。 任何残留差异都可能导致 +/-fin + (k/N)*Fs 处的杂散信号,其中 Fs 是 RFADC 的采样率,N 是子 RF-ADC 的数量,fin 是输入的频率 信号。 TSCB 要求存在输入信号。 此输入信号的最小功率为 -40 dBFS。只要输入信号功率下降到 -40 dBFS 以下超过 100 μs,该模块就应该处于冻结模式。Foreground校准过程前台校准步骤是 RF-ADC 启动初始化状态机序列的一部分。 这是由启动 IP 管理和执行的。 前台校准的目的是通过 OCB1 和 OCB2 模块为子 RF-ADC 偏移和采样开关的偏斜提供校正。 然后 OCB2 块被冻结,并且在操作模式期间不再更新。 在此过程中,理想情况下,在任何交织偏移位置都应该没有信号能量,即频率 = k*Fs/N。 因此在这个过程中输入应该被静音。 这通常通过在 RF-ADC 启动过程完成后对接收器前端启用进行排序来实现。 在前台校准过程结束时,OCB2 模块被冻结,并且 OCB1、Time Skew 和 Gain Calibration 模块被设置为允许在后台运行。Background校准过程在 RF-ADC 成功启动初始化结束时,启用背景校准。 背景校准的目的是为子 RF-ADC 的环境变化(主要是温度变化)引入的各种偏差和失配提供实时调整。 在后台(实时)运行的块包括 OCB1、GCB 和 TSCB。 如前所述,OCB1 在后台运行以调整由温度变化引入的残余 sub-RFADC 偏移电平,而不会影响输入信号内容。k*Fs/N 处的信号内容不受此块的影响。增益校准模块 (GCB) 和时间偏移校准模块 (TSCB) 分别为子 RF-ADC 增益和时间偏移提供校正。 两个模块都使用并依赖输入信号来估计和校正偏斜。 当输入信号降至 -40 dBFs 以下时,使用该信号进行计算不再有效。 因此这两个块的最佳使用是动态控制这两个块的系数更新(冻结和恢复)。冻结和恢复这两个模块的控制是使用 ADVANCE 控制面板中的 Vivado IP 端口 RF-ADC 校准冻结端口来实现的。 CAL 冻结端口可独立用于每个 RF-ADC,包括冻结控制信号 (int_cal_freeze) 和状态信号 (cal_frozen)。 冻结控制信号上的高电平有效冻结两个块以进行计算和系数更新。 在冻结事件之前计算的系数继续有效,直到下一个启用(解冻)事件,这对应于冻结控制端口上的低信号。 在控制信号电平发生变化时,模块改变状态所需的时间约为 7 μs。 请注意,这明显短于 GCB 和 TSCB 的时间常数。 必须小心确保冻结控制端口的变化速度不会超过 7 μs 间隔。 可以根据应用需要以多种不同方法之一提供冻结控制机制。可以在 PL 中实现一个简单的信号电平检测器来监控 RF-ADC 的数字输出电平并控制背景校准的状态。 通常,RF-ADC 输出绝对值的泄漏积分器和迟滞计数器就足够了。对于在接收器上具有预定占空比的应用,例如 TDD-LTE 无线电,TX/RX 切换信号可以与电平检测器结合使用,以提供对 GCB 和 TSCB 模块的更精确控制。请注意,在 RF-ADC 初始启动时,GCB 和 TSCB 并未处于最佳性能,因为它们都需要存在一些信号来训练系数。在 AutoCal 模式(第 3 代)RF-ADC 采样时钟中,这两个模块的收敛时间常数约为 222 或 223。 对于需要快速收敛的应用,请考虑提供一个训练信号,该信号最终可以在启动状态机完成后立即关闭以训练 GCB 和 TSCB 模块。 这对于输入信号非常突发且在系统启动期间具有低占空比和低功耗的应用特别有用。子 RF-ADC 输出如框图所示,每个子 RF-ADC 都独立地位于单独的数据流中,直到它们被 QMC 模块、复数混频器混合,然后被 DDC 过滤。OCB2、GCB 和 TSCB 系数独立应用于每个子 RF-ADC,如果您将冻结端口永久设置为高电平有效,则可以有效地禁用这些系数。 如果 QMC 模块和 DDC 模块(因此是混频器)被旁路,则子 RF-ADC 数据流将独立输出到逻辑。关键 CAL 特性和指导摘要Fs/N 的输入信号内容,其中 N = 8 和 4 分别用于双路和四路 RF-ADC 块,在 OCB2 的前台校准期间必须静音。 k*Fs/N bin 处的信号分量应小于 -95 dBFs。 当输入信号下降到 -40 dBFs 以下超过 100 μs 时,应将增益和时间偏移校准模块(GCB、TSCB)置于冻结模式。 一个简单的检测器模块可用于管理冷冻端口的动态控制。 当 RF-ADC 以最大采样率的 0.75 倍或更慢的速度运行 Dual 和 Quad 器件时,应禁用抖动模式。 对于适用的系统,在将系统切换到实时操作之前,还可以使用训练信号来校准 GCB 和 TSCB。 如果不使用 DDC 功能,则可在可编程逻辑接口处获得子 RF-ADC 数据输出流。校准模式要达到指定的 RF-ADC 性能,必须根据信号位置正确设置某些校准模式。 可用的校准模式如下:模式 1,针对输入信号优化,位于 0.4 * Fs 到 Fs/2。模式 2,针对输入信号优化,位于 0 到 0.4 * Fs 。上述位置是指混叠后第一奈奎斯特频段的输入信号。AutoCal 模式:此模式在第 3 代中可用。AutoCal 模式无需手动选择模式 1 和模式 2 校准,并优化 RF-ADC 性能,无论信号位置如何。建议在 AutoCal 模式和传统模式 1 和模式 2 之间进行测试,以评估哪种模式能够提供理想的 RF-ADC 性能。 设置为 AutoCal 模式会影响收敛时间。获取/设置校准系数除了自动校准外,所有四个校准模块(OCB1、OCB2、GCB、TSCB)都可用于获取和设置用户系数。 应用程序读回校准解冻时生成的系数,并在需要时恢复它们; 这有助于在输入信号不满足校准要求时保持 RF-ADC 性能。 此功能适用于 IP 向导中的每个 RF-ADC。 启用此功能会增加 IP 的大小。使用 XRFdc_SetCalCoefficients API 恢复校准系数会自动禁用实时校准。 提供 XRFdc_DisableCoefficientsOverride API 以禁用此用户系数覆盖模式并重新启用实时校准。 禁用实时校准时,实时端口校准冻结无效。注意:以下是适用于 Gen 1 和 Gen 2 设备的两个限制。GCB 的用户系数获取/设置仅在 DDC 通道处于 1x(旁路)、NCO 关闭模式时有效,并且每个 AXI4-Stream 的样本对于 Quad RF-ADC 必须是 4 个样本,对于 Quad RF-ADC 必须是 8 个样本 双 RF-ADC校准模块 OCB1 不支持校准系数获取/设置。设置系数 RFdc API 示例以下示例代码显示了 TSCB 的用户系数设置。u32 Status = XRFDC_FAILURE; XRFdc_Calibration_Coefficients Coeffs; //使用下面的样本系数 Coeffs.Coeff0 = 146; Coeffs.Coeff1 = 255; Coeffs.Coeff2 = 255; Coeffs.Coeff3 = 255; Coeffs.Coeff4 = 113; Coeffs.Coeff5 = 255; Coeffs.Coeff6 = 255 Coeffs.Coeff7 = 255; Status = XRFdc_SetCalCoeffients( RFdcInstPtr, Tile, Block, XRFDC_CAL_BLOCK_TSCB, &Coeffs); If (Status != XRFDC_SUCCESS) { /*handle error*/ } referencePG269
带数控振荡器的 RF-ADC 混频器混频器功能具有三种模式:旁路(不混频)、粗混或细混。 精细混频自动启用用于生成载波频率的 NCO。 混频器支持完全正交混频,同时支持实数到 I/Q 和 I/Q 到 I/Q 模式。粗调混频器:粗调混合器允许将数据与 0、Fs/2、Fs/4 或 –Fs/4 的载波混合。选择 0 仅在使用 RFdc 驱动程序 API 时可用。使用 0 载波进行混音会绕过混音器组件。精细混频器:精细混频器允许数据在频率上任意上移或下移。频移量是通过对NCO 中产生的混频器频率进行编程来获得的。 精细混频器还支持 18 位相位调整。可以对 NCO 进行编程以输出 NCO 频率 (Fc) 的 cos、-cos、sin 或 -sin。根据当前混频器模式选择 NCO 输出。I/Q to I/QReal to I/Q当选择负正交时,Q输入是反向的。NCO 阶段可以使用 XRFdc_UpdateEvent 在图块内同步。NCO 相位可以使用外部事件信号(SYSREF 或 MARKER)跨图块同步。为管理潜在的溢出,精细混频器输出包括 3 dBV 衰减,如上图所示。 此衰减与 R2C 模式无关,因此 API 的自动模式选择会根据 RF-ADC 混频器缩放输出因子选择正确的衰减级别(见下表)。 手动选择也是可能的,允许 0 dBV 或 -3 dBV。Tile UsageCoarse MixerAuto Fine MixerIQ (C2C)1 (0 dBV)0.707 (-3 dBV)Real (R2C)1 (0 dBV)0.997 (~0 dBV)混频器设置可以在内核中配置,或者使用 RFdc 驱动 API。 内核用于设置初始混频器设置(例如,混频器类型和混频器模式),RFdc 驱动 API 用于在运行时调整设置。 RFdc 驱动程序 API 和内核都根据提供的采样率和所需频率计算所需的寄存器设置。 下图显示了一个示例配置界面。RF-ADC 混频器 RFdc API 示例相关的 RFdc 驱动 API 函数显示在以下代码中。 此代码说明了 NCO 相位复位功能的使用。 启动时必须使用此函数将精细混频器的相位初始化为有效状态。 请注意,以下代码会重置所有磁贴中的 NCO。XRFdc_Mixer_Settings Mixer_Settings; for(tile=0;tile<4; tile++) { // 确保混合器设置更新使用 Tile 事件 for(block=0; block<2; block++) { XRFdc_GetMixerSettings (ptr, XRFDC_ADC_TILE, tile, block,&Mixer_Settings); Mixer_Settings.EventSource = XRFDC_EVNT_SRC_TILE; //使用XRFDC_EVNT_SRC_TILE事件更新混频器设置 XRFdc_SetMixerSettings (ptr, XRFDC_ADC_TILE, tile, block,&Mixer_Settings); } //重置 Tile0 中两个 DDC 的 NCO 相位(假设两者都处于活动状态) XRFdc_ResetNCOPhase(ptr, XRFDC_ADC_TILE, tile, 0); // DDC Block0 XRFdc_ResetNCOPhase(ptr, XRFDC_ADC_TILE, tile, 1); // DDC Block1 XRFds_UpdateEvent(ptr, XRFDC_ADC_TILE, tile, 1, XRFDC_EVENT_MIXER); //生成tile事件 } NCO 频率转换NCO 处于数字域,其有效频率范围始终为 -Fs/2 至 Fs/2。当 RF-ADC 或 RF-DAC 工作在子采样频率 (Fc>Fs/2) 时,应用程序必须首先计算第一奈奎斯特频带的信号位置,然后设置有效 NCO 值以移位信号。为方便起见,Zynq UltraScale+ RFSoC IP GUI 和 API 支持在 -10 GHz 到 10 GHz 的范围内设置 NCO 频率(RFdc API 对 NCO 范围没有限制)。 Vivado Design Suite 或 RFdc API 自动将第一奈奎斯特区之外的高频转换为有效的 NCO 配置。 这适用于 RF-ADC 和 RF-DAC,并严格遵循采样理论。为避免原始频谱倒置,当所需信号位于偶数奈奎斯特频带时,在下变频中设置正 NCO 频率,或当所需信号位于奇数奈奎斯特频带时,设置负 NCO 频率。 对于上变频,将信号移至偶数奈奎斯特频带时设置负 NCO 频率,将信号移至奇数奈奎斯特频带时设置正频率(此转换仅适用于将 NCO 设置为第一个奈奎斯特频带之外的频率 (±Fs/2) )。NCO 设置示例下图说明了为不同场景设置 NCO,从偶数或奇数 Nyquist 频带进行上变频和下变频。 NCO 可以设置在 ±Fs/2 之内或此范围之外。RF-ADC 抽取滤波器(第 1 代/第 2 代)需要抽取滤波器来实现数字下变频 (DDC) 过程的下采样和滤波部分。 整体滤波器响应由使用的抽取级数决定。 抽取链由三个 FIR 滤波器级组成,可以组合起来实现可变抽取率。 当 FIR 级未使用时,它会自动断电。 抽取过滤器允许创建以下内容(第 1 代/第 2 代):1x:绕过所有过滤器阶段。2x:使用单个阶段进行抽取过滤。4x:使用两级抽取滤波。8x:使用所有三个可用级进行抽取滤波。每个抽取滤波器元件具有不同数量的抽头,阻带衰减和纹波显示在抽取滤波器详细信息中。 抽取滤波器链可以对 I/Q 数据或真实数据进行操作。 未使用的过滤器链断电。考虑到 FIR 滤波器的阶跃响应,每个滤波器级都可能溢出,尤其是当满量程数据在输入上时。 为了检测和保护数据路径免于溢出,每个滤波器级和子相位在输出端都有一个带符号的溢出状态信号和饱和信号。 当不使用过滤级时,该标志被强制为零。 这些标志连接到中断处理中描述的数据路径中断机制。 下图中的多路复用器显示了在 IP 配置中选择的抽取级别以及相应的抽取滤波器块选择。抽取滤波器操作模式(第 1 代/第 2 代)ModeDescriptionQuad and Dual RF-ADC TileOFF整个滤波器被禁用/断电(在禁用 RF-ADC 时适用)1x整个过滤器被绕过2x2x 抽取,80% 奈奎斯特通带4x4x 抽取,80% 奈奎斯特通带8x8x 抽取,80% 奈奎斯特通带80% 奈奎斯特通带为 0.4*Fs。抽取滤波器使用IP 内核用于设置抽取率。 这是在 Vivado IDE 中设置的,因为随着 PL 带宽的变化,抽取率的改变会直接影响物理接口。 启用的过滤器如下图所示。 通过选中 Enable ADC 复选框来启用 RF-ADC。相关 API 命令RFdc 驱动程序 API 可用于使用以下代码获取 IP 内核中设置的抽取率。//获取 Tile0、DDC Block1 的抽取因子 int Tile = 0; u32 Block = 1; u32 Decimation_Factor; if( XRFdc_GetDecimationFactor (ptr, Tile, Block, &DecimationFactor) == XST_SUCCESS) { xil_printf("ADC Tile%1d,%1d Decimation Factor is: %d", Tile, Block,Decimation_Factor); }抽取滤波器详细信息(第 1 代/第 2 代)抽取滤波器链由三个 FIR 滤波器组成:FIR2、FIR1 和 FIR0,可启用它们以每级两倍的因子连续抽取。 滤波器传递函数如下图所示。抽取滤波器的滤波器系数如下表所示。RF-ADC 抽取滤波器(第 3 代)下图显示了第 3 代中的抽取级。有四级抽取滤波器级联; 每个抽取级都可以独立绕过。 FIR1 级包含 3 个抽取滤波器——FIR1a (2x)、FIR1b (3x) 和 FIR1c (5x)——对于指定的配置,只能启用其中一个。 FIR2、FIR3 和 FIR4 模块的抽取因子均为 2。使用滤波器组合,以下显示了所有可能的抽取因子:1x(旁路)、2x、3x、4x、5x、6x、8x、10x、12x、 16x、20x、24x、40x 注:DDC的信号流向为:FIR4->FIR3->FIR2->FIR1。IP 配置和驱动 API 自动选择高阶 FIR 组合。 例如,选择 FIR2 和 FIR1a 进行 4 倍抽取。抽取滤波器详细信息(第 3 代)下面列出了所有抽取滤波器的系数和频率响应图。 这些都是半带滤波器,所以只列出了中心抽头值和前半部分。 N(bit) 是系数的位宽,用于归一化系数。FIR 1a (2x)N(bit) = 17 Center Tap: 65536 First Half: 5,0,-17,0,44,0,-96,0,187,0,-335,0,565,0,-906,0,1401,0,-2112,0,3145,0,-4723,0,7415,0,-13331,0,41526FIR 1b (3x)N(bit) = 19 Center Tap: 174763 2,6,0,-17,-27,0,57,79,0,-143,-187,0,307,385,0,-590,-721,0,1050,1254,0,-1757,-2063,0,2807,3256,0,-4339,-4991,0,6579,7549,0,-9975,-11517,0,15633,18486,0,-27325,-34856,0,71618,144204 FIR 1c (5x)N(bit) = 19 Center Tap: 104858 First Half: 2,0,-4,-10,-13,-11,0,17,35,43,32,0,-47,-91,-107,-78,0,106,198,228,162,0,-210,-386,-437,-304,0,383,693,772,531,0,-652,-1166,-1286,-875,0,1056,1872,2049,1384,0,-1648,-2905,-3163,-2127,0,2514,4421,4804,3227,0,-3819,-6731,-7340,-4956,0,5956,10615,11742,8070,0,-10195,-18821,-21778,-15872,0,24203,52516,79099,98013 FIR 2 (2x) CoefficientsN(bit) = 15 Center Tap: 16384 First Half: -12,0,84,0,-337,0,1008,0,-2693,0,10142FIR 3/4 (2x) CoefficientsFIR 3 和 4 具有相同的系数。N(bit) = 12 Center Tap: 2048 First Half: -6,0,54,0,-254,0,1230抽取滤波器使用IP 内核用于设置抽取率。 这是在 Vivado IDE 中设置的,因为随着 PL 带宽的变化,抽取率的改变会直接影响物理接口。 启用的过滤器如下图所示。 通过选中 Enable ADC 复选框来启用 RF-ADC。相关 API 命令RFdc 驱动程序 API 可用于使用以下代码获取 IP 内核中设置的抽取率。//获取 Tile0、DDC Block1 的抽取因子 int Tile = 0; u32 Block = 1; u32 Decimation_Factor; if( XRFdc_GetDecimationFactor (ptr, Tile, Block, &DecimationFactor) == XST_SUCCESS) { xil_printf("ADC Tile%1d,%1d Decimation Factor is: %d", Tile, Block,Decimation_Factor); } reference1.PG269
前言RFSoC中最重要的部分是射频直采ADC和DAC的配置,因此了解内部相关原理结构可以帮助我们更好理解相关功能配置参数含义。本文参考官方手册,主要对RFSoC ADC的数字数据路径相关功能进行介绍。RF-ADC 数字数据路径Tile 中的 RF-ADC 组件具有集成的 DSP 功能,您可以启用这些功能在将来自 RF-ADC 设备的采样数据传递到 PL 之前对其进行预处理。 不同的 DSP 功能块如下:检测功能:包含一个双级可编程阈值,为内部互连逻辑提供两个标志,并在 RFADC 的绝对输出值大于或小于编程阈值时置位。补偿功能:包含一个正交调制器校正 (QMC) 模块和一个粗延迟调整模块。数字下变频器 (DDC):混频器——粗调(四分之一和半速率)和精细(具有 48 位频率分辨率的 NCO)。Gen 1/Gen 2:信号抽取功能—支持1(旁路)、2、4 或8 抽取。Gen 3:信号抽取功能—支持1(旁路)、2、3、4、5、6、8、12、16、20、24、40 抽取。可以使用或绕过单个、多个或所有 DSP 功能。 某些功能,例如 QMC,需要在 I 和 Q RF-ADC 中激活相同的功能。 偶数 RF-ADC 始终用于 I 数据路径,奇数 RF-ADC 用于 Q 数据路径。 您可以使用 IP 内核实现、配置或修改一个或多个功能的功能。下图显示了 RF-ADC 中的可用功能。RF-ADC 阈值和超范围设置与任何 ADC 一样,输入模拟信号必须保持在 ADC 的满量程范围内,并且处于正确的输入电平。 任何不符合这些条件的信号都会导致数据丢失。 为了帮助防止这种情况,阈值检测器功能可用于调整信号链增益,以将信号保持在理想的满量程范围内。 但是,如果信号确实超出了满量程范围,每个 RF-ADC 通道都具有内置的检测和保护功能,具有过量程和过压功能。过电压和超范围信号提供给 IP 内核上的中断功能,以及 IP 内核上的 RF-ADC 实时信号总线,以便直接访问 PL 设计。Over Range(超出范围)当信号超出 RF-ADC 的满量程输入时,会出现超出范围的情况。 当检测到这种情况时,转换后的数据会饱和(限幅)以限制数据损坏,并且信号由中断机制和通过断言 Over Range 实时输出信号进行标记。 由于 Over Range 事件可以短至一个 RF-ADC 样本,因此输出信号具有粘性。 要清除 Over Range 输出和相关中断,使用 API 中断处理机制。Over Voltage 过压(第 1 代/第 2 代)当信号远远超出正常工作输入范围时,就会出现过压情况。由于输入端电压过高会损坏器件,因此提供了自动保护机制。过压事件会导致输入缓冲器自动关闭以保护它。 过压电路独立监控差分输入的每个信号,并在任何单个输入信号超过最大输入电压或低于 RF-ADC 输入缓冲器的最小输入电压时标记状态。过压功能为 Zynq UltraScale+ RFSoC 数据表中定义的范围内的信号提供保护(DS926)。 超过此最大值的信号是不允许的,必须注意外部,以确保不会将此类电压提供给 RF-ADC 输入。当检测到过压情况时,中断机制和置位过压实时输出信号都会对信号进行标记。 过电压实时输出被异步断言和取消断言,并提供事件的即时通知。 因此,当过压条件不再存在时,过压输出会自行清零。 相关的中断是粘性的,因此需要通过 API 中断处理例程清除。在发生过压事件后,输入缓冲器会自动重新启用,并且 RF-ADC 会像以前一样恢复运行。 但是,应将过压条件视为停止通信并重新启动接收器链的理由。 如果出现过压情况,缓冲器将关闭,由此产生的 RF-ADC 输出数字代码大多只是噪声。 结果,阈值信息变得无关紧要,这会影响 AGC 的实施。 因此,AGC 实施应考虑过电压信号并相应地设置两个阈值检测器,这一点很重要。下图显示了阈值、过量程和过压电平以及随着输入模拟信号的增加这些电平的响应。阈值设置阈值功能无需等待信号通过信号处理模块传播,而是提供传入信号电平的早期指示。 PL 中实现的自动增益控制 (AGC) 可以使用这种信号电平的早期指示。 用于指示输入信号电平的阈值电平是使用 RFdc 驱动程序 API 设置的。当 RF-ADC 采样数据进入数据路径时,会进行阈值监控。 将此数据与用户定义的阈值进行比较。 阈值状态信号被发送到 IP 内核的输出,以指示已超过用户定义的阈值。 每个 RF-ADC 有两个实时超阈值输出信号。 阈值监控电路的模式如下表所示。阈值级别设置为 14 位无符号值,允许 0 到 16383 之间的任何值。 最大值 16383 表示 RF-ADC 满量程输入的绝对值。 32 位可编程延迟对 RF-ADC 样本进行计数。 要将此计数与特定时间相关联,可以使用以下等式:其中对于 Quad RF-ADC tile,Interleaving Factor 为 4,并且为 8 用于双 RF-ADC 模块。延迟也可以用样本而不是 ms 表示,如下所示:清除阈值标志在 Sticky Over 和 Sticky Under 模式下,阈值置位后,可以通过两种方式清除标志; 通过直接发出 clear 命令或使用 AutoClear 功能。每当与该 RF-ADC 关联的 QMC 增益值更新时,AutoClear 模式都会清除阈值。阈值操作示例下图说明了阈值单元操作。 该图显示了三个阈值 a、b 和 c,每个阈值配置为不同的阈值模式。 在硬件中,所有阈值单元都有独立的阈值电平和延迟值。 在此示例中,阈值水平由水平虚线表示,并且所有单元都使用相同的水平进行说明。延迟值设置为 5(延迟值对 Sticky Over 模式没有影响),并且设置了粘性阈值以通过不同的清除模式清除。 以下部分描述了该行为。注意:在下图中,样本由一个 sub-ADC 计数,而不是 RF-ADC。Threshold-A, Sticky Over Mode当第一个样本超过阈值上限时,该标志置位。 在此示例中,使用对 XRFdc_ThresholdStickyClear API 函数的调用来清除标志。Threshold-B, Sticky Under Mode当样本持续低于样本延迟数(示例中为 6)的下限阈值时,该标志置位。 在此示例中,通过更新 QMC 增益值来清除标志,该值是通过调用 XRFdc_SetQMCSettings API 函数发出的,然后是更新事件,该事件应用 QMC 增益更新。Threshold-C, Hysteresis Mode当第一个样本超过上限阈值时,该标志置位。 当样本持续低于样本延迟数的下限阈值时,该标志被清除。阈值 RFdc 驱动程序 API 操作使用 RFdc 驱动程序 API 配置阈值电平和延迟值。// Initial Setup XRFdc_Threshold_Settings Threshold_Settings; Threshold_Settings.UpdateThreshold = XRFDC_UPDATE_THRESHOLD_BOTH; // Setup values for threshold 0 and 1 Threshold_Settings.ThresholdMode[0] = XRFDC_TRSHD_STICKY_UNDER; // Set threshold0 mode to Sticky Under Threshold_Settings.ThresholdUnderVal[0] = 1000; // Measured in 14-bit unsigned LSBs Threshold_Settings.ThresholdAvgVal[0] = 10; // Data must be below lower threshold for 10*8 4 GSPS RF-ADC samples // Write threshold values to the selected Tile / RF-ADC XRFdc_SetThresholdSettings(ptr, Tile, Block, &Threshold_Settings);阈值应用阈值检测器的常见用途是自动增益控制 (AGC) 应用。超量程设置有两种类型的超量程信号可用作可编程逻辑的状态输出,即超电压和超量程。IP 内核使用中断机制公开这些信号,因此任何违规都会立即标记给用户应用程序。 当超出正常或预期的操作水平时触发这些。过压信号只要输入信号超过来自 RF-ADC 输入缓冲器的安全输入范围,就会检测到过压信号。超量程信号当输入信号超过RF-ADC 的± 数字满量程范围时,检测到超量程信号。在RF-ADC 的原始数字输出处测量超量程信号。信号幅度检测器(第 3 代)时间交错式 RF-ADC 依赖于多个背景校准,以最大限度地提高其在工艺变化范围内的性能,并在电压和温度变化范围内保持其性能。 背景校准算法需要输入信号存在一段时间才能收敛并达到最佳性能。 当输入信号不存在时,校准会发散,因此当输入信号恢复时,校准需要一段时间才能重新收敛。如果处理不当,一些实际应用(例如电缆应用和 TDD 无线通信中使用的突发模式信号,或来自雷达应用之类的不可预测信号)会降低校准的准确性。每个 RF-ADC 通道都内置了幅度检测器,用于监控 RF-ADC 数据并检测信号幅度何时低于某个阈值。 信号的开/关状态呈现给可编程逻辑,并可通过实时端口用于冻结或启用背景校准模块。概述幅度检测器模块用于监控 RF-ADC 数据并检测信号幅度何时低于某个阈值。 该模块监视 4 个或 8 个并行输入字。 每个通道监控的输入字数基于 RF-ADC 配置(四通道或双通道块)。下图显示了幅度检测器在 RF-ADC 数据路径上的位置。每个 RF-ADC 通道都有自己的幅度检测器模块。该块的目的是检测信号幅度何时低于或高于某个可编程阈值,并将信号的状态作为实时异步状态输出到可编程逻辑。 结构的输出状态是信号状态。 信号状态被认为是开或关。信号幅度检测器详解幅度检测器由一个输入级、一个泄漏积分器滤波器、一个阈值比较器和两个基于滞后计数器的滤波器实例组成。下图显示了检测器的架构。Input Stage输入级以 T4/T8 时钟速率将 N=4 或 N=8 个输入字作为输入,并在每个 T4/T8 周期输出一个样本。 该模块以以下两种模式之一运行:平均模式:所有子ADC 输入的平均幅度用于检测信号幅度。随机采样:一个随机选择的数据字(子ADC)用于检测信号幅度。泄漏积分器(Leaky Integrator )泄漏积分器用于测量一段时间内的平均信号幅度。 泄漏积分器时间常数可编程从单个 T4/T8 时钟周期到 2 ^16 个时钟周期。泄漏积分器的变换函数如下:其中:y[n] 是泄漏积分器的输出。y[n-1] 是泄漏积分器的最后一个输出。x[n] 是当前输入值; 输入级的输出。2^λ 的输出是时间常数。下表给出了可编程时间常数值:Lambda IndexLambda ValueTime Constant (T4/T8 Cycle)002^0122^2242^4382^84122^125142^146162^167N/AN/A上图中使用了一个高斯分布信号进行说明。 一组基于不同时间常数的曲线显示了泄漏积分器的 20*log10 输出。用户可编程刷新位可用于刷新将值重置为 0 的泄漏积分器。上升和下降时间在输入信号是突发信号或幅度变化剧烈的某些应用中,需要评估泄漏积分器的上升/下降时间。 上升时间可以从它的阶跃响应推导出来; 下降时间可以从它对初始条件的响应中得出。 以下公式分别显示了如何计算上升和下降时间:其中: α = 1 - 1/2^λ考虑到何时泄漏积分器的输出达到上升时间计算最终值的 90%,比最终值低约 1 dB。对于下降时间,使用 1% (-40 dB) 来说明满量程信号下降到 -40 dBFS,这使得下降时间大约是上升时间的两倍。下表显示了基于不同时间常数的参考上升/下降时间:迟滞计数器(Hysteresis Counter)漏滤波器之后的滞后计数器块,由一个阈值比较器和两个计数器组成,它们都是用户可编程的。 迟滞计数器用于进一步过滤来自泄漏积分器的信号幅度,以避免在信号幅度接近阈值电平时发生振荡,并输出稳定状态,ON或OFF。如果信号大于或等于 2 *“高计数器”连续周期数的阈值,则认为该信号为 ON。 以类似的方式,如果信号在 2 *“低计数器”连续周期数内小于“阈值”,则认为该信号为 OFF。如果前一个条件不满足,低计数器和高计数器立即同步复位。 “ON”或“OFF”输出将保持其状态,直到被另一个计数器更改。阈值电平为 15 位无符号值,以下公式将阈值电平转换为满量程对数值:高位计数器和低位计数器均为 17 位 无符号值,虽然高 16 位是用户可编程的,但最低有效位始终为 0。信号检测器示例将阈值设置为 -40 dBFS,以下示例说明了此信号检测器模块在不同时间常数和计数器值下的响应。假设采样率为 4 GHz,插值因子为 8,则每个 T8 为 2 ns。假设 lambda = 8 时的 on 和 off 计数器值为 1000,lambda = 12 时为 300,则上升时间为 (587 或 9429 + 21000 或 2300) * T8,大约为 5.2 µs 或 20 µs; 在本例中,下降时间应考虑从 -39 dBFS 下降到 -41 dBFS,约为 (58 或 942 + 21000 或 2300) * T8,结果为 4.1 µs 或 3.1 µs。下图显示了两个示例,均基于高 PAR(约 12 dB)突发信号,信号幅度约为 -40 dBFS,有一点偏移(1-2 dB)。上图说明了 lambda = 12 和 counter = 300 的响应。上图说明了 lambda = 8 和 counter = 1000 的响应。这些示例显示了此模块根据输入信号幅度给出的正确响应。 您应该为特定应用选择计数器值和适当的时间常数。
7 系列 FPGA 时钟与前几代 FPGA 的区别尽管所有 7 系列器件具有相同的基本架构,但家族之间和家族内的器件之间存在一些架构差异。每个 7 系列 FPGA 在器件的左边缘至少有一个完整的 I/O 列。 GT 可以是 7 系列 FPGA(GTP、GTX 或 GTH)支持的任何一种串行收发器。带有 GT 的设备要么在设备的右边缘有混合的 GT 和 I/O 列(一些 Kintex-7 设备和一些 Artix-7 设备),要么在右边缘有一个完整的 GT 列(一些 Kintex-7 设备和一些 Virtex-7 设备)以及设备右侧的完整 I/O 列。 其他 Virtex-7 器件具有完整的 GT左侧和右侧边缘的列,左侧和右侧具有完整的 I/O 列。Artix-7 200T 设备在时钟列旁边的顶部和底部都有 GTP 收发器。因此,并非 7 系列器件中的所有时钟区域都包含前面图中所示的所有模块。7 系列 FPGA 时钟具有与 Virtex-6 FPGA 相似的结构,并支持许多相同的功能。 但是,对各种时钟元件及其功能存在一些架构差异和修改。 与 Spartan-6 FPGA 相比,架构和功能都发生了一些重大变化。一些 Spartan-6 FPGA 时钟原语不再可用,取而代之的是更强大、更简单的结构。与 Virtex-6 FPGA 的主要区别7 系列 FPGA 的基本 BUFIO 时钟功能没有改变,只是 BUFIO 现在只跨越一个存储体。替。 现在每个银行有四个 BUFIO。 与BUFIO演进类似,BUFR的基本目的并没有改变。 但是,现在 BUFR 仅直接跨越单个时钟区域。 现在每个区域有四个 BUFR 和四个区域时钟(轨道)。7 系列 FPGA 引入了一种新的缓冲器类型:BUFMR/BUFMRCE。 BUFMR/BUFMRCE 驱动相同和垂直相邻区域中的 BUFIO 和/或 BUFR。它们还提供与 Virtex-6 FPGA 中相同的多时钟区域/多存储库时钟路由,支持相同的三个时钟区域/存储库功能。BUFMRCE 具有可选择的同步或异步切换功能。7 系列 FPGA 不再支持 Virtex-6 系列中的全局时钟 (GC) 输入管脚。 每组四个具有时钟功能的时钟输入引脚/对取代了 GC。具有时钟功能的输入引脚的连接性已得到增强,以支持许多以前的 GC 功能。全局时钟多路复用器 BUFGMUX 添加了一个属性 CLK_SEL_TYPE,用于允许两个输入时钟的同步或异步时钟切换(以前只能通过 IGNORE 端口使用)。BUFHCE 具有增强的时钟使能,以允许输入时钟的同步或异步使能。CMT 现在包含一个 MMCM 和一个 PLL(MMCM 的一个子集),而不是两个 MMCM,以及保留的专用存储器接口逻辑。 CMT 列与 CMT 内的 SelectIO列/组相邻,并且具有对 I/O 的专用访问以实现高性能。 全局时钟缓冲器仍然位于由 CMT 驱动的 I/O 列之间的器件垂直中心。不再支持 CMT 内的直接级联。 直接级联到相邻的 CMT 是可能的,但由于资源有限而受到限制。 级联到其他 CMT超出相邻 CMT 会导致源和目标 MMCM/PLL 之间的相位偏移,并且需要特殊的属性设置。小数除法器不再共享输出计数器。 这可以释放这些计数器用于其他用途。 分数计数器增加了静态相移能力。CLOCK_HOLD 功能不再可用。MMCM 支持扩频。与 Spartan-6 FPGA 的主要区别Spartan-6 架构特有的一些 Spartan-6 FPGA 时钟电路拓扑、功能和模块不受支持,并已被 7 系列 FPGA 时钟特性所取代。 7 系列器件不直接支持 DCM_SP、DCM_CLKGEN、BUFIO2、BUFIO2_2CLK、BUFIO2FB、BUFPLL 和 BUFPLL_MCB 等特性和功能。PLL 是 MMCM 的子集,具有相同的性能(除了最小 CLKIN/PFD 和最小/最大 VCO 频率)、一些连接限制和一些减少的功能。 与之前的 Spartan® FPGA PLL 相比,7 系列 FPGA PLL 增加了断电、输入时钟切换和级联到相邻 CMT 的功能。 PLL 与 BUFIO 或 BUFR 没有直接连接。在 7 系列 FPGA 中,BUFIO2 和 BUFIO2_2CLK 原语没有直接替代品。 使用 BUFIO 和 BUFR 与推荐的连接来驱动 ILOGIC 和 OLOGIC。不再支持从 GCLK 到 CMT 和全局时钟缓冲器的 Spartan-6 FPGA BUFIO2 专用输入路由。 要迁移到 7 系列 FPGA,请使用来自 CCIO 引脚的专用输入路由。7 系列 FPGA 中没有与 Spartan-6 FPGA BUFPLL 直接等效的产品。 要进行迁移,请将 BUFIO 和 BUFR 与 ILOGIC 和 OLOGIC 的推荐连接一起使用。 来自 MMCME2 CLKOUT[0:3] 的高性能时钟布线取代了到 BUFPLL 的专用布线。 ISERDES 和 OSERDES 电路基于 Virtex-6 架构。在 7 系列 FPGA 中,不再需要 BUFPLL_MCB 原语。 DDR 存储器接口在 7 系列 FPGA 中具有不同的(软)实现。在 7 系列 FPGA 中,不再需要 BUFIO2FB 原语。 对于 MMCM 和 PLL 反馈连接,CLKFBIN 可以直接连接到全局时钟缓冲器、输入引脚或 CLKFBOUT,具体取决于所使用的反馈。Spartan-6 FPGA 仅支持 BUFH。 7 系列 FPGA BUFHCE 原语增加了禁用时钟的能力,以在由该资源驱动的时钟区域中实现潜在的节能。7 系列 FPGA 新缓冲器 BUFMR/BUFMRCE 在相同和垂直相邻的时钟区域中驱动 BUFIO 和/或 BUFR。 当与 BUFIO 或 BUFR 一起使用时,BUFMR/BUFMRCE 允许 MRCC 输入访问相邻时钟区域中的 BUFIO 和 BUFR。 BUFMRCE 具有可选择的同步或异步切换功能。Spartan-6 FPGA 设计迁移的新原语是BUFR。 当与 BUFIO 结合使用时,BUFR 功能取代了 BUFIO2、BUFIO2_2CLK 和 BUFPLL 功能。 每个时钟区域有四个 BUFR。Spartan-6 FPGA 设计迁移的另一个新原语是 BUFIO。 与 BUFR 一起使用时,BUFIO 功能取代了 BUFIO2、BUFIO2_2CLK 和 BUFPLL 功能。 每个BANK有四个 BUFIO。Spartan-6 架构中没有两个 DCM 和一个 PLL,7 系列 FPGA 使用一个 CMT,其中包含一个 MMCM、一个 PLL 和专用的存储器接口逻辑,这些逻辑是为 Xilinx 保留的。 这些功能现在支持 DCM 及其相关功能。 CMT 位于与 SelectIO 列相邻的单独列中,并具有对 I/O 的专用访问权限。 DCM_SP 和 DCM_CLKGEN 不再可用,它们的功能现在在 MMCM 和 PLL 中得到支持。7 系列 FPGA 不再支持全局时钟 (GCLK) 输入。 现在,每个支持 Spartan-6 FPGA GCLK 引脚功能的每个 bank 中都有四个具有时钟功能的输入引脚。对于 Spartan-6 FPGA 设计人员来说,MMCM 是一个新的功能块。 MMCM 增加了分数除法、精细相移、动态相移、反相时钟输出、CLKOUT6 到 CLKOUT4 级联以及一些其他功能。 到 BUFPLL 的直接布线连接被使用 CLKOUT[0:3] 从 MMCM 到 BUFIO/BUFR 的 HPC 连接所取代。 更广泛的 DRP 也可用。对于通用高速I/O 时钟,不再推荐使用PLL 的CMT 功能。 PLL 没有直接连接到 BUFIO 或 BUFR。 不再支持 CLKOUT0 反馈。 将 MMCM 用于高速 I/O 接口。 级联连接使用有限的 CMT 骨干资源。 还有一种新的断电模式。 完全支持输入时钟切换。 Spartan-6 FPGA 和 7 系列 FPGA 的工作范围不同。 DRP 功能仍然可用。 DRP 功能位置和地址已更改。不再支持 Spartan-6 FPGA DCM_SP。 要迁移到 7 系列 FPGA,请使用 MMCM 和 PLL。7 系列 FPGA 不直接支持 Spartan-6 FPGA DCM_CLKGEN。使用低带宽的 MMCM 或 PLL 进行输入抖动滤波。 M/D 值的动态重新编程也可以使用 MMCM 或 PLL 的 DRP 参考设计来完成。时钟连接总结下表总结了 7 系列 FPGA 的时钟连接。7 系列 FPGA 的时钟差异7 系列 FPGA 系列中的每一个都有一些独特的连接要求。下表按器件系列列出了描述的连接限制。referenceUG472
VAR_LOAD 模式下图显示了 VAR_LOAD 模式下的 IDELAY 时序图。时钟事件 0在 LD 产生脉冲之前,抽头设置和因此 CNTVALUEOUT 处于未知值。时钟事件 1在 C 的上升沿,LD 被检测为高电平,导致输出 DATAOUT 具有由 CNTINVALUE 定义的延迟,并将抽头设置更改为抽头 2。更新 CNTVALUEOUT 以表示新的抽头值。时钟事件 2在 C 的上升沿捕获 CE 和 INC 上的脉冲。这表示增量操作。 输出从抽头 2 到抽头 3 无毛刺地变化。CNTVALUEOUT 被更新以表示新的抽头值。时钟事件 3在 C 的上升沿,LD 被检测为高电平,导致输出 DATAOUT 被 CNTINVALUE 延迟。 CNTVALUEOUT 显示抽头设置的值。输出将无限期地保持在抽头 10,直到 LD、CE 或 INC 引脚上有进一步的活动。递增/递减操作后的稳定性下图显示了响应 INC 和 CE 命令时从抽头 0 变为抽头 1 的延迟线。 显然,当抽头 0 处的数据值与抽头 1 处的数据值不同时,输出必须改变状态。 但是,当抽头 0 和抽头 1 处的数据值相同(例如,两者均为 0 或均为 1)时,从抽头 0 到抽头 1 的转换不会导致输出出现故障或中断。 通过想象接收器数据信号通过 IDELAY 抽头链,可以更好地理解这个概念。 如果抽头 0 和抽头 1 都在接收器数据眼图的中心附近,那么在抽头 0 处采样的数据将与在抽头 1 处采样的数据没有什么不同。在这种情况下,从抽头 0 到抽头 1 的转换不会导致 更改为输出。 为确保这种情况,IDELAY 的递增/递减操作被设计为无毛刺。 同样的解释也适用于图 2-13 中所示的 VAR_LOAD 行为。 然而,VAR_LOAD 确实提供了将延迟更改不止一次的可能性,这可能会导致采样点远离当前眼图中心点。因此,用户可以在实时用户数据通过 IDELAYE2 原语时实时动态调整 IDELAY 抽头设置。 只要当前延迟线值接近接收数据眼图的中间,这些调整就不会中断实时用户数据。当在时钟信号路径中使用 IDELAYE2 原语时,无毛刺行为也适用。 调整抽头设置不会导致输出出现毛刺或中断,前提是延迟线值不在接收到的时钟信号中看到的边沿附近。 在这种情况下,可以调整时钟路径中 IDELAYE2 原语的抽头设置,而不会中断任何时钟管理元素或可能在该时钟上运行的状态机。IDELAYCTRL简介IDELAYCTRL 概述如果实例化 IDELAYE2 或 ODELAYE2 原语,则还必须实例化 IDELAYCTRL 模块。 IDELAYCTRL 模块连续校准其区域内的各个延迟抽头 (IDELAY/ODELAY),以减少工艺、电压和温度变化的影响。 IDELAYCTRL 模块使用用户提供的 REFCLK 校准 IDELAY 和 ODELAY。IDELAYCTRL 原语下图显示了 IDELAYCTRL 原语。IDELAYCTRL 端口RST - 复位复位输入引脚 (RST) 是高电平有效异步复位。 为确保 IDELAY 和 ODELAY 正常运行,IDELAYCTRL 必须在配置后复位并且 REFCLK 信号稳定。 需要一个复位脉冲宽度 、TIDELAYCTRL_RPW。REFCLK - 参考时钟参考时钟 (REFCLK) 为 IDELAYCTRL 提供时间参考,以校准同一区域中的所有 IDELAY 和 ODELAY 模块。 该时钟必须由全局或水平时钟缓冲器(BUFG 或 BUFH)驱动。 REFCLK 必须为 FIDELAYCTRL_REF ± 指定的 ppm 容差 (IDELAYCTRL_REF_PRECISION),以保证指定的 IDELAY 和 ODELAY 分辨率 (TIDELAYRESOLUTION)。 REFCLK 可以直接从用户提供的源或 MMCM 提供,并且必须在全局时钟缓冲器上布线。RDY - 就绪就绪 (RDY) 信号指示何时校准特定区域中的 IDELAY 和 ODELAY 模块。 如果 REFCLK 保持高电平或低电平超过一个时钟周期,则 RDY 信号无效。 如果 RDY 置低,则必须复位 IDELAYCTRL 模块。 实现工具允许 RDY 不连接/忽略。 图 2-15 说明了 RDY 和 RST 之间的时序关系。IDELAYCTRL 时序下表显示了 IDELAYCTRL 开关特性。如图 2-15 所示,7 系列 FPGA IDELAYCTRL RST 为边沿触发信号。IDELAYCTRL 位置IDELAYCTRL 模块存在于每个时钟区域的每个 I/O 列中。 IDELAYCTRL 模块校准其时钟区域内的所有 IDELAYE2 和 ODELAYE2 模块。图 2-16 说明了 IDELAYCTRL 模块的相对位置。referenceUG471
前言本文主要针对Aurora 8B/10B IP的配置界面相关配置项进行简要说明和介绍。自定义配置 Aurora IP简介可以使用 Vivado工具对 Aurora 8B/10B 内核进行定制,以满足各种要求。下图显示了定制 IP 界面的核心选项选项卡,其中包含 Zynq®-7000 和 7 系列器件的默认选项。 左侧显示了当前配置的 Aurora 8B/10B 内核的代表性框图。 右侧包含用户可配置的参数。图 4-2 和图 4-3 显示了 UltraScale器件的核心选项选项卡。7系列Physical Layer7系列Physical Layer可配置选项如下:Lane Width选择内核中使用的收发器的字节宽度。该参数定义收发器的 TXDATA/RXDATA 宽度和用户接口数据总线宽度。 有效值为 2 和 4。默认值:2Line Rate线路速率,在 0.5 (Gb/s) 到 6.6 (Gb/s) 的有效范围内输入线路速率值(千兆位/秒)。该值是通过串行链路传输数据的未编码比特率。 内核的总数据速率为(0.8 x 线路速率)x Aurora 8B/10B 通道。默认值:3.125 Gb/sGT REFCLK (MHz)从下拉列表中选择收发器的参考时钟频率。 参考时钟频率取决于所选的线路速率。 为获得最佳结果,请选择可实际应用于目标器件参考时钟输入的最高速率。默认值:125.000 MHzINIT clk (MHz)在文本框中输入有效的 INIT 时钟频率。默认值:Zynq-7000 和 7 系列器件为 50 MHz,UltraScale 器件为 (line_rate/lane_width)。DRP clk (MHz)在文本框中输入有效的 DRP 时钟频率。 UltraScale 器件的 INIT 时钟和 DRP 时钟频率相同。默认值:50 MHzUltraScale 系列Physical Layer相比7系列Physical Layer可配置选项,UltraScale 可配置的选项增加以下部分:Column Used从下拉列表中选择适当的 GT 列。默认值:RightLanes选择要在核心中使用的通道数。 有效范围取决于所选的目标设备。默认值:1Starting GT Quad从下拉列表中选择起始通道的起始 GT Quad。 内核配置有连续数量的通道,并选择了通道选择选项。默认值:Quad X1Y0Starting GT Lane从下拉列表中选择内核的起始通道。 使用起始 Quad、通道和起始通道,生成具有连续通道数的核心。默认值:X1Y0内核不支持跨 SLR 边界的通道绑定,并且受到 Vivado的限制。GT Refclk Selection从下拉列表中选择 UltraScale 器件收发器的参考时钟源。默认值:Quad X1Y0 的 MGTREFCLK0Generate Aurora without GT此选项仅适用于 UltraScale 和 UltraScale+ 设备。 如果选择此选项,则生成的 Aurora 内核不带 GT,示例设计中提供了 GT。Link LayerDataflow Mode选择 Aurora 8B/10B 内核支持的通道方向选项。单工 Aurora 8B/10B 内核具有一个单向串行端口,可连接到互补的单工 Aurora 8B/10B 内核。 可用的选项是 RX-only Simplex、TX-only Simplex 和 Duplex。默认值:DuplexInterface选择用于内核的数据路径接口类型。 选择成帧以使用允许封装任何长度的数据帧的 AXI4-Stream 接口。 选择 Streaming 以使用简单的 AXI4-Stream 接口通过 Aurora 8B/10B 通道流式传输数据。默认值:FramingFlow ControlFlow Control 选择所需的选项以将流量控制添加到核心。 用户流控制 (UFC) 允许应用程序通过 Aurora 8B/10B 通道发送简短的高优先级消息。本机流量控制 (NFC) 允许全双工接收器调节发送给它们的数据的速率。 立即模式允许在数据帧中插入空闲代码,而完成模式仅在完整数据帧之间插入空闲代码。可用选项如下:NoneUFCImmediate NFCCompletion NFCUFC + Immediate NFCUFC + Completion NFC默认值:无Back Channel选择Back Channel 选项仅用于单工Aurora 内核; 双工 Aurora 内核不需要此选项。可用选项有:SidebandsTimer默认值:SidebandsUse Scrambler/Descrambler使用加扰器/解扰器 选择以将 16 位加扰器/解扰器包含到 Aurora 8B/10B 设计中。默认值:未选中Little Endian Support选择以将所有接口更改为 little endian 格式。 默认情况下,核心使用大端格式。默认值:未选中Error DetectionUse CRC选择以包含用户数据的 CRC。 根据通道宽度 2 或 4,内核分别实现 CRC16 或 CRC32。Debug and ControlAdditional Transceiver Control and Status Ports选择在核心顶层包括收发器控制和状态端口。默认值:未选中Vivado Lab Tools选择以将 Vivado 实验室工具添加到 Aurora 8B/10B 内核。 该选项提供了一个调试界面,可显示 Vivado Logic Analyzer 中的内核状态信号。默认值:未选中C_DOUBLE_GTRXRESET此参数可以在自定义 IP 时使用 TCL 控制台设置为 1。 启用此参数以在由于非常高的 ppm 差异导致频繁的缓冲区上溢/下溢的情况下断言额外的复位。 在 IP 硬件调试期间,如果在 gt_reset_i 置低后看到 RX 电气空闲退出条件,您也可以设置此参数。默认值:0(GUI 上不存在)Shared Logic下图显示了 Customize IP 界面的 Shared Logic 选项卡。选择该选项以在 IP 内核或示例设计中包含收发器通用 PLL 及其逻辑。可用选项:在内核中包含共享逻辑在示例设计中包含共享逻辑默认:在示例设计中包含共享逻辑下图显示了自定义 IP 界面的 GT 选择选项卡。Column/Row Used此选项仅对具有多于一列/行的设备可见。从下拉列表中选择使用的收发器的相应列/行。 使用的列仅对 Virtex-7 和 Kintex-7 设备启用,使用的行仅对 Artix-7 设备启用。默认值:left/topLanes选择要在内核中使用的通道数(收发器)。 有效范围为 1 到 16,取决于所选的目标设备。默认值:1Lane Assignment通道分配,参见上图中信息区域中的图表。两行或四个框代表一个四边形。 每个活动框代表一个可用的收发器。 提供了一个工具提示来指定哪个收发器(例如,GTXE2_CHANNEL_X0Y0)正在硬件中实现。Aurora 8B/10B 内核以递增方式生成收发器布局 (LOC) 约束。 车道编号仅用于启用车道而不是分配车道编号。GT Refclk1 和 GT RefclK2核心生成 单击确定生成核心。 Aurora 8B/10B 内核的模块使用与内核顶层相同的名称写入 Vivado 设计工具项目目录。 有关 example_design 目录和文件的详细信息,请参见第 80 页的输出生成。注:在 IP 集成器中,Aurora 8B/10B 内核按照 IP 集成器指南以长格式设置预期频率值; 但是,内部核心精度与 Vivado IDE 中显示的相同。数据和流控端口分组为AXI4-Stream接口。 其他输入和输出端口被分组为显示接口。对于显示接口中分组的端口,应手动进行连接。GT Refclk1 and GT RefclK2从本节的下拉列表中选择 GTP、GTX 或 GTH Quad 的参考时钟源。默认值:GT REFCLK Source 1:GTPQn/GTXQn/GTHQn;GT REFCLK Source 2:None.n 的值取决于串行收发器(GTX 或 GTH)的位置。referencePG046
写在前面攻其事必先利其器,本文主要翻译节选自UG471,主要对7系列的xilinx FPGA IO资源的buffer源语进行简要说明,了解buffer的基本结构。7 系列 FPGA SelectIO原语Xilinx 软件库包含大量原语,以支持 7 系列 FPGA I/O 原语中可用的各种 I/O 标准。 以下通用原语都可以支持大多数可用的单端 I/O 标准。IBUF(输入缓冲器)IBUF_IBUFDISABLE(具有缓冲器禁用控制的输入缓冲器)IBUF_INTERMDISABLE(具有缓冲器禁用和IN_TERM禁用控制的输入缓冲器)IBUFG(时钟输入缓冲器)IOBUF(双向缓冲器)IOBUF_DCIEN(具有DCI禁用的双向缓冲器 和输入缓冲器禁用)IOBUF_INTERMDISABLE(双向缓冲器,带有 IN_TERM 禁用和输入缓冲器禁用)OBUF(输出缓冲器)OBUFT(三态输出缓冲器)这八个通用原语都可以支持大多数可用的差分 I/O 标准:IBUFDS(差分输入缓冲器)IBUFDS_DIFF_OUT(具有互补输出的差分输入缓冲器)IBUFDS_DIFF_OUT_IBUFDISABLE(具有互补输出和缓冲器禁用的差分输入缓冲器)IBUFDS_DIFF_OUT_INTERMDISABLE(具有互补输出、缓冲器禁用和 IN_TERM 禁用的差分输入缓冲器)IBUFDS_IBUFDISABLE ( 带缓冲器禁用控制的差分输入缓冲器)IBUFDS_INTERMDISABLE(不同 具有缓冲器禁用和 IN_TERM 禁用的 ial 输入缓冲器)IBUFGDS(差分时钟输入缓冲器)IBUFGDS_DIFF_OUT(具有互补输出的差分时钟输入缓冲器)IOBUFDS(差分双向缓冲器)IOBUFDS_DCIEN(具有 DCI 禁用和输入缓冲器禁用的差分双向缓冲器 )IOBUFDS_DIFF_OUT(具有来自输入缓冲器的互补输出的差分双向缓冲器)IOBUFDS_DIFF_OUT_DCIEN(具有来自输入缓冲器的互补输出的差分双向缓冲器,具有 DCI 禁用和输入缓冲器禁用)IOBUFDS_DIFF_OUT_INTERMDISABLE(具有来自输入缓冲器的互补输出的差分双向缓冲器 带 IN_TERM 禁用和缓冲器禁用)IOBUFDS_INTERMDISABLE(带缓冲器禁用和 IN_TERM 禁用的差分双向缓冲器)OBUFDS(差分输出缓冲器)OBUFTDS(差分三态输出缓冲器)IBUF and IBUFG用作 7 系列设备输入的信号必须使用输入缓冲器 (IBUF)。 通用 7 系列 FPGA IBUF 原语如下图所示。IBUF 和 IBUFG 原语是相同的。 当输入缓冲器用作时钟输入时使用 IBUFG。 在赛灵思软件工具中,一个 IBUFG 自动放置在时钟输入位置。IBUF_IBUFDISABLE下图所示的 IBUF_IBUFDISABLE 原语是一个带有禁用端口的输入缓冲器,可在不使用输入时用作额外的节能功能。当 USE_IBUFDISABLE 属性设置为 TRUE 并且 IBUFDISABLE 信号被置为高电平时,IBUF_IBUFDISABLE 原语可以禁用输入缓冲器并强制到结构的 O 输出为逻辑高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,此输入将被忽略并应接地。 此功能可用于在 I/O 空闲时降低功耗。 使用 VREF 电源轨(如 SSTL 和 HSTL)的输入缓冲器从 IBUFDISABLE 设置为 TRUE 中获益最多,因为它们往往具有比非 VREF 标准(如 LVCMOS 和 LVTTL)更高的静态功耗。IBUF_INTERMDISABLE下图中所示的 IBUF_INTERMDISABLE 原语在 HR I/O bank 中可用,它与 IBUF_IBUFDISABLE 原语相似,因为它有一个 IBUFDISABLE 端口,可用于在不使用缓冲区期间禁用输入缓冲区 . IBUF_INTERMDISABLE 原语还有一个 INTERMDISABLE 端口,可用于禁用可选的未校准拆分终端功能。当 USE_IBUFDISABLE 属性设置为 TRUE 并且 IBUFDISABLE 信号置为高电平时,IBUF_INTERMDISABLE 原语可以禁用输入缓冲器并强制到结构的 O 输出为逻辑高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,此输入将被忽略并应接地。 如果 I/O 使用可选的未校准拆分终端功能 (IN_TERM),则只要驱动器处于活动状态(T 为低电平),这些终端脚就会被禁用。 IBUF_INTERMDISABLE 原语还允许在 INTERMDISABLE 信号被置为高电平时禁用终端支路。每当输入空闲时,这些功能可以结合起来降低功耗。 使用 VREF 电源轨(如 SSTL 和 HSTL)的输入缓冲器从 IBUFDISABLE 信号设置为 TRUE 中获益最多,因为它们往往具有比非 VREF 标准(如 LVCMOS 和 LVTTL)更高的静态功耗。IBUFDS and IBUFGDS差分原语对应的用法和规则与单端 SelectIO 原语类似。 差分 SelectIO 基元有两个引脚与器件焊盘相连,以显示差分对中的 P 和 N 通道引脚。 N 通道引脚具有 B 后缀。 IBUFDS 和 IBUFGDS 原语相同,当差分输入缓冲器用作时钟输入时使用 IBUFGDS。下图显示了差分输入缓冲器原语。IBUFDS_DIFF_OUT and IBUFGDS_DIFF_OUT下图显示了具有互补输出(O 和 OB)的差分输入缓冲器原语。 IBUFDS_DIFF_OUT 和 IBUFGDS_DIFF_OUT 原语相同,IBUFGDS_DIFF_OUT 用于时钟输入。 这些原语仅推荐给有经验的赛灵思设计人员使用。IBUFDS_DIFF_OUT_IBUFDISABLE下图中所示的 IBUFDS_DIFF_OUT_IBUFDISABLE 原语是一个差分输入缓冲器,具有互补差分输出和一个禁用端口,可在不使用输入时用作额外的节能功能。当 USE_IBUFDISABLE 属性设置为 TRUE 且 IBUFDISABLE 信号置为高电平时,IBUFDS_DIFF_OUT_IBUFDISABLE 原语可以禁用输入缓冲器并强制 O 和 OB 输出到结构高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,此输入将被忽略并应接地。 此功能可用于在 I/O 空闲时降低功耗。IBUFDS_IBUFDISABLE下图所示的 IBUFDS_IBUFDISABLE 原语是一个差分输入缓冲器,带有一个禁用端口,可在不使用输入时用作额外的节能功能。当 USE_IBUFDISABLE 属性设置为 TRUE 并且 IBUFDISABLE 信号被置为高电平时,IBUFDS_IBUFDISABLE 原语可以禁用输入缓冲器并强制到架构的 O 输出为逻辑高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,此输入将被忽略并应接地。 此功能可用于在 I/O 空闲时降低功耗。IBUFDS_INTERMDISABLE下图中所示的 IBUFDS_INTERMDISABLE 原语在 HR I/O bank 中可用,它与 IBUFDS_IBUFDISABLE 原语类似,因为它有一个 IBUFDISABLE 端口,可用于在缓冲区未运行期间禁用输入缓冲区 用过的。 IBUFDS_INTERMDISABLE 原语还有一个 INTERMDISABLE 端口,可用于禁用可选的未校准拆分终端功能。当 USE_IBUFDISABLE 属性设置为 TRUE 并且 IBUFDISABLE 信号置为高电平时,IBUFDS_INTERMDISABLE 原语可以禁用输入缓冲区并强制 O 输出到结构高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,则 IBUFDISABLE 输入将被忽略并应接地。 如果 I/O 使用可选的未校准拆分终端功能 (IN_TERM),则只要 INTERMDISABLE 信号置为高电平,此原语就会禁用终端支路。 每当输入空闲时,这些功能都可以结合起来降低功耗。IBUFDS_DIFF_OUT_INTERMDISABLE下图中显示的 IBUFDS_DIFF_OUT_INTERMDISABLE 原语在 HR I/O bank 中可用,它与 IBUFDS_IBUFDISABLE 原语相似,因为它有一个IBUFDISABLE 端口,可用于在不使用缓冲区期间禁用输入缓冲区。 IBUFDS_DIFF_OUT_INTERMDISABLE 原语还有一个 INTERMDISABLE 端口,可用于禁用可选的未校准拆分终端功能。当 USE_IBUFDISABLE 属性设置为 TRUE 并且 IBUFDISABLE 信号置为高电平时,IBUFDS_DIFF_OUT_INTERMDISABLE 原语可以禁用输入缓冲区并强制 O 和 OB 输出到结构高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,则 IBUFDISABLE 输入将被忽略并应接地。 如果 I/O 使用可选的未校准拆分终端功能 (IN_TERM),则只要 INTERMDISABLE 信号置为高电平,此原语就会禁用终端支路。每当输入空闲时,这些功能都可以结合起来降低功耗。IOBUF当双向信号需要输入缓冲器和具有高电平有效三态 T 引脚的三态输出缓冲器时,需要 IOBUF 原语。 下图显示了一个通用的 7 系列 FPGA IOBUF。 T 引脚上的逻辑高电平禁用输出缓冲器。IOBUF_DCIEN下图所示的 IOBUF_DCIEN 原语在 HP I/O bank 中可用。 它有一个 IBUFDISABLE 端口,可用于在不使用缓冲区期间禁用输入缓冲区。 IOBUF_DCIEN 原语还有一个 DCITERMDISABLE 端口,可用于手动禁用可选的 DCI 拆分终止功能。当 USE_IBUFDISABLE 属性设置为 TRUE 且 IBUFDISABLE 信号置为高电平时,IOBUF_DCIEN 原语可以禁用输入缓冲器并强制到架构的 O 输出为逻辑高电平。 如果 USE_IBUFDISABLE 设置为 FALSE,此输入将被忽略并应接地。 如果 I/O 使用分离端接 DCI 功能,则只要 DCITERMDISABLE 信号被置为高电平,该原语就会禁用端接支路。 只有三态 DCI I/O 标准可用于双向信号。使用三态 DCI I/O 标准,只要驱动器处于活动状态,DCI 终端支路就会关闭。 IOBUF_DCIEN 原语还允许在 DCITERMDISABLE 信号被置为高电平时禁用终端支路。 只要输入空闲一段时间,这些功能就可以结合起来降低功耗。
Split-Termination DCI(戴维宁等效终端到 VCCO/2)一些 I/O 标准(例如 HSTL 和 SSTL)要求输入终端电阻 ® 连接到 VCCO/2 的 VTT 电压,见下图。分体式终端 DCI 使用两个电阻值 (2R) 两倍的电阻器创建戴维南等效电路。 一个连接到 VCCO,另一个连接到地。 使用这种方法,分离端接 DCI 提供与 VCCO/2 等效的端接。2R 终端电阻由外部参考电阻器设置。 例如,要实现 50Ω 至 VCCO/2 的戴维南等效并联端接电路,需要在 VRN 和 VRP 引脚上使用 100Ω 外部精密电阻。 支持分离端接的 DCI 输入标准如下表所示。下图说明了 7 系列设备中的分离式终端 DCI。VRN/VRP 外部电阻设计移植指南以前具有 DCI 的 Xilinx FPGA 系列使用稍微不同的电路来校准来自放置在 VRN 和 VRP 引脚上的外部参考电阻的分离终端阻抗。 Virtex-6 FPGA DCI 将分离端接电路的每个支路校准为外部电阻值的两倍。 例如,在具有 50Ω 到 VCCO/2 的目标并联终端的 Virtex-6 器件中,VRN 和 VRP 引脚上需要 50Ω 外部电阻器。7 系列 FPGA DCI 校准分离端接电路的每个支路,使其直接等于外部电阻值。 例如,在具有 50Ω 到 VCCO/2 的目标并联终端的 7 系列器件中,VRN 和 VRP 引脚上需要 100Ω 外部电阻器。在为受控阻抗 DCI 和分离端接 DCI 标准选择要在同一 I/O 组(或多个级联 DCI 组)中使用的 VRN 和 VRP 值时,考虑这一点尤为重要。在具有用于 LVDCI_18 输出的 50Ω 目标受控阻抗驱动器和用于 HSTL_I_DCI_18 输入的 50Ω 目标分离终端接收器的 Virtex-6 FPGA 设计中,可以使用 VRN 和 VRP 引脚上的 50Ω 外部电阻器来实现。 将同样的设计移植到 7 系列 FPGA 不会改变 HSTL_I_DCI_18 I/O 标准; 但是,外部电阻器必须更改为 100Ω,并且受控阻抗驱动器更改为 LVDCI_DIV2_18 输出。 这个示例结果是等价的; 但是,电阻值和 I/O 标准都需要更改。 VRN 和 VRP 外部电阻器可以安全地使用 0.05W 或更高的额定功率。DCI and 3-state DCI (T_DCI)SSTL 和 HSTL I/O 标准的 I 类驱动程序版本仅支持单向信令; 它们只能分配给设计中的仅输入或仅输出引脚,而不是双向引脚。 I 类 SSTL 和 HSTL I/O 标准的 DCI 版本仅在输入(而非输出)上具有内部分离终端电阻。 支持双向和单向信令的 SSTL 和 HSTL I/O 标准的 II 类驱动程序版本; 它们可以分配给设计中的输入、输出或双向引脚。 II 类 SSTL 和 HSTL I/O 标准的 DCI 版本始终在输入、输出或双向引脚上存在内部分离终端电阻。下图说明了 7 系列器件内部具有分离终端的驱动程序。当驱动时存在分离终端时,DCI 仅控制终端的阻抗,而不控制驱动器。 然而,许多应用都可以受益于在引脚驱动时关闭分离端接电阻器。 三态 DCI (T_DCI) 标准旨在通过在输出缓冲器驱动时关闭分离终端电阻器来满足这一要求,并在输出处于三态时打开分离终端电阻器(例如当 接收或处于空闲状态)。 T_DCI 标准只能分配给双向引脚。 对于单向输入引脚,可以分配标准的 DCI 版本。 对于单向输出引脚,可以分配非 DCI 或 DCI 版本。具有始终存在的分离端接 DCI 电阻器的 I/O 标准下表所示。仅在三态时具有分离端接 DCI 的 I/O 标准:所有 7 系列设备 DCI I/O 标准:要在 7 系列器件中正确使用 DCI:VCCO 管脚必须根据该 I/O bank 中的 IOSTANDARD 连接到适当的 VCCO 电压。必须在软件中使用正确的 DCI I/O 缓冲器,方法是使用 IOSTANDARD 属性或 HDL 代码中的实例化。DCI 标准要求将外部参考电阻器连接到多用途引脚(VRN 和 VRP)。 需要时,这两个多用途管脚不能用作使用 DCI 的 I/O bank 中的通用 I/O,或级联 DCI 时的主 I/O bank 中的通用 I/O。引脚 VRN 必须通过其参考电阻上拉至 VCCO。 引脚 VRP 必须通过其参考电阻下拉至地。 在从 I/O bank 中级联 DCI 时,此要求有一个例外,因为 VRN 和 VRP 引脚可用作通用 I/O。具有受控阻抗驱动器的 DCI 标准可用于仅输入信号。 对于这种情况,如果这些引脚是给定 I/O bank 中唯一使用 DCI 标准的引脚,则该 bank 不需要将外部参考电阻器连接到 VRP/VRN 引脚。 当这些基于 DCI 的 I/O 标准是 bank 中唯一的标准时,该 bank 中的 VRP 和 VRN 管脚可用作通用 I/O。VRP/VRN 上不需要参考电阻的 DCI 输入如下表所示。应选择外部参考电阻器的值,以提供所需的输出驱动器阻抗或分路终端阻抗。 例如,当使用 LVDCI_15 时,要实现 50Ω 的输出驱动器阻抗,VRN 和 VRP 引脚上使用的外部参考电阻应各为 50Ω。 使用 SSTL15_T_DCI 时,要实现 50Ω Thevenin 等效端接 ® 到 VCCO/2,外部参考电阻应为 100Ω,即 (2R)。 Xilinx 要求在 VRP 和 VRN 引脚上使用完全相同的电阻值,以实现预期的 DCI 行为。遵循 DCI I/O Bank规则:使用 DCI 级联时,VREF 必须与同一 I/O bank 或一组 I/O bank 中的所有输入兼容。VCCO 必须与同一 I/O bank 中的所有输入和输出兼容。分离端接、受控阻抗驱动器和具有半阻抗的受控阻抗驱动器可以共存于同一组中。DCI Usage Examples下图提供了说明使用 HSTL_I_DCI 和 HSTL_II_DCI I/O 标准的示例。下图提供了说明使用 SSTL18_I_DCI 和 SSTL18_II_DCI I/O 标准的示例。HR I/O Bank 中未校准的拆分终端 (IN_TERM)HR I/O bank 有一个可选的片上分离终端特性,与 HP I/O bank 中的三态分离终端 DCI 特性非常相似。 与 HP 组中的三态分离端接 DCI 类似,HR 组中的选项使用两个目标电阻值两倍的内部电阻器创建戴维南等效电路。 一个电阻端接至 VCCO,另一个端接至地,为中点 VCCO/2 提供戴维南等效端接电路。 当输出缓冲器为三态时,输入端和双向引脚上始终存在端接。 然而,这种未经校准的分离式终端选项和三态分离式终端 DCI 之间的一个重要区别是,在使用 DCI 时,该功能不是校准到 VRN 和 VRP 引脚上的外部参考电阻,而是调用没有校准例程的内部电阻来校准补偿温度、过程或电压变化。 此选项的目标戴维南等效电阻值为 40Ω、50Ω 和 60Ω。与 DCI 终端的另一个区别是如何在设计中调用这种未经校准的终端。 虽然通过将 T_DCI I/O 标准分配给 HP I/O bank 中的 I/O 管脚来调用三态分离终端 DCI 选项,但通过将 IN_TERM 约束分配给 I/O 管脚来调用未校准的分离终端选项 HR I/O bank 中的网络。这可以通过多种方式完成,包括在源 HDL 设计中、在 UCF、NCF 或 XCF 文件中,或在 PlanAhead软件中。referenceUG471
SelectIO简介FPGA的SelectIO就是I/O接口以及I/O逻辑的总称。Xilinx SelectIO支持电平标准多,除MIPI C-PHY电平(三电平标准)外,IO能直接对接3.3V以及3.3V以下基本所有电平标准,初步统计支持72种不同电平标准。同时在使用是经常需要根据具体的应用设计进行配置电平标准以及IO配置,本文节选翻译整理自UG471的第一章,对7系列的FPGA的SelectIO资源进行简要介绍以及数控阻抗 (DCI) 技术的介绍。7系列FPGA I/O Bank支持的功能所有7系列FPGA都有可配置的SelectIO驱动器和接收器,支持多种标准接口。强大的功能集包括可编程控制输出强度和压摆率的可编程控制,使用数字控制阻抗的(DCI)的片上终端,以及内部产生参考电压(INTERNAL_VREF)的能力。HR Bank没有DCI。DCI并不适用于HR Bank。除了一些例外情况,每个I/O库包含50个SelectIO引脚。在每个库的最末端的两个引脚只能用于单端I/O标准。剩下的48个的引脚可以使用单端或差分标准,使用两个SelectIO引脚组成的正/负(P/N)对,可用于单端或差分标准。每个SelectIO资源都包含输入、输出和三态驱动器。7系列FPGA的HR和HP I/O Bank支持的功能如下:单端与差分逻辑架构SelectIO引脚可以被配置成各种I/O标准,包括单端和差分。单端I/O标准(如LVCMOS、LVTTL、HSTL、PCI和SSTL)。差分I/O标准(例如,LVDS、Mini_LVDS、RSDS、PPDS、BLVDS和差分HSTL和SSTL)。被配置成不同I/O标准的引脚,端口的输入输出的连接方式也不相同。下图显示了单端(仅)HP I/O Bank(IOB)及其与内部逻辑和器件焊盘的连接。下图显示了常规HP IO Bank及其与内部逻辑和器件焊盘的连接。下图显示了单端(仅)的HR IO Bank及其与内部逻辑和器件焊盘的连接。下图显示了常规的HR IO Bank及其与内部逻辑和器件焊盘的连接。经过对比分析可知,在 HP 和 HR I/O Bank中,单端和常规IO Bank 基本上是等同的,除了单端IOB没有连接来产生差分输出的信号。HR和HP相比,除了缺少DCI片上终端使能控制,其余结构也基本相似。在大多数设备中,单端 IOB是位于每个I/O组两端的两个引脚。组成每组其他48个引脚的常规IOB可以实现单端和差分I/O标准。每个IOB都有一个直接连接到ILOGIC/OLOGIC对,包含数据和三态的输入和输出逻辑资源。每个IOB都有一个与ILOGIC/OLOGIC对的直接连接,包含了IOB的数据和三态控制的输入和输出逻辑资源,用于数据和IOB的三态控制。ILOGIC和OLOGIC都可以被配置为ISERDES和OSERDES。设计时一般准则本节总结了使用7系列FPGA的SelectIO资源进行设计时需要考虑的一般准则。在7系列器件中,一个I/O库由50个IOB组成。Bank的数量取决于器件尺寸和封装引脚。在7系列FPGA概述中,可用的I/O库的总数按器件类型列出。可用的I/O库总数是按器件类型列出的。例如,XC7K325T有10个可用的I/O库。选择IO引脚的电源电压VCCOVCCO电源是7系列I/O电路的主要电源。一个给定的I/O组的所有VCCO引脚必须连接到电路板上相同的外部电源,因此,一个给定的I/O组内的所有I/O必须共享相同的VCCO电平。VCCO电压必须符合分配给I/O组的I/O标准的要求。不正确的VCCO电压会导致功能丧失或设备损坏。在HP I/O Bank中,如果I/O标准电压要求<1.8V,但应用VCCO>2.5V,器件会自动进入过压保护模式。用正确的VCCO电平重新配置器件,就可以恢复正常工作。VREF具有差分输入缓冲器的单端I/O标准需要一个输入参考电压(VREF)。当一个I/O组内需要VREF时,该组的两个多功能VREF引脚必须被用作VREF电源输入。7系列FPGA可以通过启用INTERNAL_VREF约束来选择使用内部产生的参考电压。VCCAUX全局辅助(VCCAUX)电源主要用于为7系列FPGA内部各种块特征的互连逻辑提供电源。在I/O库中,VCCAUX也被用来为一些I/O标准的输入缓冲电路供电。这些包括所有1.8V或以下的单端I/O标准,以及一些2.5V标准(仅HR I/O Bank)。此外,VCCAUX为用于差分和VREF I/O标准的组的差分输入缓冲电路提供电源。VCCAUX_IO辅助I/O(VCCAUX_IO)电源只存在于HP I/O库中,为I/O电路提供电源。Kintex-7和Virtex-7 FPGAs数据表中包含一个 "存储器接口的最大物理接口(PHY)速率 "的表格,其中提到了VCCAUX_IO。该表指出了VCCAUX_IO引脚如何在1.8V(默认)或可选的2.0V下供电,以实现某些类型存储器接口的更高频率性能。尽管该表是为存储器接口设计的,但它也可以根据目标比特率为其他高速单端接口的VCCAUX_IO供电提供指导。**该表不适用于LVDS,因为LVDS使用的驱动电路与单端驱动不同,受VCCAUX_IO电平影响较大。**因此,对于LVDS接口,VCCAUX_IO轨哪个电压水平上供电并不重要。1.8V的默认值提供了较低的功耗,并在I/O中提供了非常接近于相同的性能。当单端驱动器所支持的最快比特率需要略微增加的性能时,可以选择2.0V。I/O 网络和原语有一个称为 VCCAUX_IO 的设计约束,如果要将任何 bank 的 VCCAUX_IO 引脚设置为 2.0V,则应在设计中指定该约束。配置期间和配置之后的I/O状态7 系列 FPGA 具有专用于 I/O bank 0 中包含的配置功能的引脚。Bank 14 和 15 还包含称为多功能或多用途管脚的 I/O 管脚,也可用于配置,但配置后转换为普通 I/O 引脚。 此外,在 SSI 器件中,bank 11、12、17、18、20 和 21 中的引脚具有配置过程中的限制类似于多功能引脚。 然而,在这些引脚bank没有任何配置功能。在 bank 14 和/或 bank 15 是 HR bank 且配置为 VCCO 要求 < 1.8V 的器件中,如果输入连接到 0 或悬空且配置电压 > 2.5V,则在配置期间输入可能会从 0-1-0 转换到互连逻辑。仅HP bank 中可用的DCI数控阻抗 (DCI) 技术介绍随着 FPGA 变得越来越大,系统时钟速度越来越快,PCB 板的设计和制造变得更加困难。 随着边缘速率越来越快,保持信号完整性成为一个关键问题。 PCB 板走线必须正确端接以避免反射或振铃。 为了终止迹线,传统上添加电阻以使输出和/或输入与接收器或驱动器的阻抗匹配到迹线的阻抗。 然而,由于器件 I/O 的增加,在器件引脚附近添加电阻器会增加电路板面积和元件数量,并且在某些情况下可能在物理上是不可能的。 为了解决这些问题并实现更好的信号完整性,Xilinx 开发了数控阻抗 (DCI) 技术。根据 I/O 标准,DCI 可以控制驱动器的输出阻抗,也可以在驱动器和/或接收器上添加一个并行终端,以精确匹配传输线的特性阻抗。 DCI 主动调整 I/O 内部的这些阻抗,以校准放置在 VRN 和 VRP 引脚上的外部精密参考电阻。 这可以补偿由于工艺变化引起的 I/O 阻抗变化。 它还连续调整阻抗以补偿温度和电源电压波动的变化。对于具有受控阻抗驱动器的 I/O 标准,DCI 控制驱动器阻抗以匹配两个参考电阻器,或者对于某些标准,匹配这些参考电阻器值的一半。对于具有受控并行终端的 I/O 标准,DCI 为发送器和接收器提供并行终端。 这消除了板上端接电阻的需要,减少了电路板布线困难和组件数量,并通过消除短截线反射提高了信号完整性。 当终端电阻距离传输线末端太远时,会发生短截线反射。 使用 DCI,端接电阻器尽可能靠近输出驱动器或输入缓冲器,从而消除短截线反射。 DCI 仅在 7 系列 FPGA HP I/O bank 中可用,在 HR I/O bank 中不可用。Xilinx DCIDCI 在每个 I/O bank 中使用两个多用途参考管脚来控制驱动器的阻抗或该 bank 中所有 I/O 的并行终端值。 N 参考引脚 (VRN) 必须通过参考电阻上拉至 VCCO,而 P 参考引脚 (VRP) 必须通过另一个参考电阻下拉至地。 每个参考电阻的值应等于 PCB 板走线的特性阻抗或该值的两倍。要在设计中使用DCI:在 HP I/O bank 中分配 DCI I/O 标准之一。将 VRN 多功能引脚连接到一个精密电阻,该电阻连接到同一组的 VCCO 。将 VRP 多功能引脚连接到接地的精密电阻。以下部分讨论如何确定不同 I/O 标准的 VRN 和 VRP 的精密电阻值。 每个 bank 仅使用一组 VRN 和 VRP 电阻,因此每个 bank 内的所有 DCI 标准必须能够共享相同的外部电阻值。 如果同一 I/O bank 列中的多个 I/O bank 使用 DCI,并且所有这些 I/O bank 使用相同的 VRN/VRP 电阻值,则内部 VRN 和 VRP 节点可以级联,这样只有一对 整个 I/O 列中所有 I/O bank 的管脚都需要连接到精密电阻。 此选项称为 DCI 级联。如果组中未使用 DCI I/O 标准,则这些管脚可用作常规 I/O 管脚。DCI 通过有选择地打开或关闭 I/O 中的晶体管来调整 I/O 的阻抗。 调整阻抗以匹配外部参考电阻。 调整在设备启动序列期间开始。 默认情况下,在阻抗调整过程的第一部分完成之前,DONE 引脚不会变为高电平。DCI 校准可以通过例化 DCIRESET 原语来复位。 在设备运行时将 RST 输入切换到 DCIRESET 原语,重置 DCI 状态机并重新启动校准过程。 在 DCIRESET 模块的 LOCKED 输出被断言之前,所有使用 DCI 的 I/O 都将不可用。 此功能在温度和/或电源电压从器件上电到标称工作条件显着变化的应用中非常有用。对于受控阻抗输出驱动器,可以调整阻抗以匹配参考电阻器或参考电阻器的一半电阻。 对于片上端接,端接总是被调整以匹配参考电阻。对于支持 DCI 控制阻抗驱动器的 I/O 标准,DCI 可以将输出驱动器配置为以下类型:受控阻抗驱动器(源极端接), Controlled Impedance Driver (Source Termination)具有半阻抗(源极端接)的受控阻抗驱动器 ,Controlled Impedance Driver with Half Impedance (Source Termination)对于支持并行端接的 I/O 标准,DCI 创建一个与 VCCO/2 电压电平等效的戴维南电阻或分离端接电阻。 I/O 标准的命名约定增加了:如果 I/O 中始终存在分离终端电阻,则在 I/O 标准名称中添加 DCI,无论该标准是用于输入、输出还是双向端口。I/O 标准名称中的T_DCI,如果仅当输出缓冲器为三态时才存在分离终端电阻。Match_cycle 配置选项Match_cycle 是一个配置选项,可在 FPGA 配置序列结束时选择性地暂停启动序列,直到 DCI 逻辑对外部参考电阻器执行第一次匹配(校准)。 此选项有时也称为 DCI 匹配。DCIUpdateMode 配置选项DCIUpdateMode 是一个配置选项,可以覆盖对 DCI 电路更新与 VRN 和 VRP 参考电阻匹配的阻抗的频率的控制。 此选项默认为AsRequired,但在 Xilinx 实现软件中还有一个可选值 Quiet。**强烈建议将 DCIUpdateMode 选项保持为默认值 AsRequired,以便允许 DCI 电路正常运行。**DCIUpdateMode 配置选项的设置如下:AsRequired:在设备初始化时进行初始阻抗校准,并在整个设备操作期间根据需要进行动态阻抗调整(默认)。Continuous:对于 7 系列 FPGA,此值无效(默认为 AsRequired)。Quiet:阻抗校准仅在器件初始化时进行一次,或者对于包含该原语的设计,每次在 DCIRESET 原语上断言 RST 引脚时进行一次。DCIRESET原语DCIRESET 是 Xilinx 设计原语,它提供了在设计正常运行期间执行 DCI 控制器状态机复位的能力。 除非 DCIUpdateMode 设置为 Quiet或对于下面概述的与使用设置为使用 DCI 的多功能引脚相关的情况,在大多数情况下,设计中不需要此原语。某些 Bank 的特殊 DCI 要求如果 I/O bank 14 或 15(任何设备)或 bank 11、12、17、18、20 和 21(仅限 SSI 设备)中的任何多功能管脚被分配 DCI I /O 标准在用户设计中,DCIRESET 原语也应包含在设计中并使用。 在这种情况下,设计应该脉冲 DCIRESET 的 RST 输入,然后等待 LOCKED 信号被断言,然后再使用符合 DCI 标准的这些引脚上的任何用户输入或输出。 这是必需的,因为这些 I/O 管脚会忽略在正常设备初始化期间发生的初始 DCI 校准。因此,如果未使用 DCIRESET 原语且 DCIUpdateMode 设置为 AsRequired,则在这些引脚变为正常 I/O 引脚之后,在配置结束与 DCI 校准算法更新这些引脚 DCI 设置之间会有一个不确定的延迟。如果不使用 DCIRESET 并且 DCIUpdateMode 设置为 Quiet,则这些引脚将永远不会设置其 DCI 值。 在这种情况下,受控阻抗 DCI I/O 标准(例如 LVDCI_18)会一直表现得好像处于高阻态,而拆分终端 DCI I/O 标准(例如 SSTL15_DCI)会表现得好像没有内部终端电阻。 在设计中包含和使用 DCIRESET 原语允许这些引脚具有 DCI I/O 标准并且可以毫无问题地执行。DCI 级联使用 DCI I/O 标准的 7 系列 FPGA HP I/O bank 可以选择从另一个 HP I/O bank 导出 DCI 阻抗值。 如下图所示,数字控制总线内部分布在整个 bank 中,以控制每个 I/O 的阻抗。对于 DCI 级联,一个 I/O bank(主 bank)必须将其 VRN/VRP 管脚连接到外部参考电阻。 同一 HP I/O bank 列(从库)中的其他 I/O bank 可以使用具有与主库相同阻抗的 DCI 标准,而无需将这些从库上的 VRN/VRP 管脚连接到外部电阻器。 级联组中的 DCI 阻抗控制从 I/O 主组接收。下图显示了对多个 I/O bank 的 DCI 级联支持。 Bank B 是主 I/O bank,而 Bank A 和 C 被认为是从 I/O bank。使用 DCI 级联时的指导原则如下:DCI 级联只能通过 HP I/O bank 列使用。主和从 SelectIO bank 必须都驻留在设备上的同一 HP I/O 列上,并且可以跨越整个列,除非有插入器边界。DCI 级联不能通过采用堆叠硅互连 (SSI) 技术的较大 Virtex-7 器件的中介层边界。 这包括 XC7V2000T 和 XC7VX1140T 器件。主和从I/O bank 必须具有相同的VCCO 和VREF(如果适用)电压。同一 HP I/O 列中不使用 DCI(直通 bank)的 I/O bank 不必遵守 VCCO 和 VREF 电压规则来组合 DCI 设置。所有主bank 和从 bank 都必须满足 DCI I/O bank 兼容性规则(例如,所有主库和从库只允许使用一种使用单一终端类型的 DCI I/O 标准)。要定位位于同一 I/O 列中的 I/O bank,参阅 UG475。有关在设计中实施直流级联的具体信息,参阅UG471的46页的 DCI_CASCADE 约束。Xilinx 建议给未使用的 bank也上电,因为将未使用的 I/O bank 的 VCCO 管脚悬空会降低这些管脚和 bank 中的 I/O 管脚的 ESD 保护水平。 如果 bank 未上电,DCI 仍可通过未上电的 bank 级联。受控阻抗驱动器(源端接)为了优化高速或高性能应用的信号完整性,需要采取额外措施来使驱动器的输出阻抗与传输线和接收器的阻抗相匹配。 理想情况下,驱动器的输出阻抗必须与驱动线的特性阻抗相匹配,否则会由于不连续性而发生反射。 为了解决这个问题,设计人员有时会使用靠近高强度、低阻抗驱动器引脚的外部源串联终端电阻。 选择电阻值,使驱动器的输出阻抗加上源串联终端电阻的电阻之和大致等于传输线的阻抗。DCI 可以提供受控阻抗输出驱动器来消除反射,而无需使用外部源极终端电阻。 阻抗由外部参考电阻器设置,其电阻等于走线阻抗。支持受控阻抗驱动器的 DCI I/O 标准有:LVDCI_15、LVDCI_18、HSLVDCI_15、HSLVDCI_18、HSUL_12_DCI 和 DIFF_HSUL_12_DCI。下图说明了 7 系列器件中的受控阻抗驱动器。具有半阻抗(源极端接)的可控阻抗驱动器DCI 还为驱动器提供了一半的参考电阻器阻抗。 参考电阻值翻倍可将这些电阻的静态功耗降低一半。 支持具有半阻抗的受控阻抗驱动器的 DCI I/O 标准是 LVDCI_DV2_15 和 LVDCI_DV2_18。下图说明了一个 7 系列器件内部具有半阻抗的受控驱动器。 参考电阻 R 必须为 2 × Z0 以匹配 Z0 的阻抗。
FSBL启动准备工作在静态情况下,Boot.BIN启动文件存放在SD卡或QSPI等存储介质中,然后Boot.BIN文件中已经包含了FSBL代码,也就是说FSBL代码已经集成在了Boot.BIN文件中,所以FSBL代码也是存放在Boot .BIN文件中。如果要启动FSBL代码,就需要完成以下几件事情:找到BOOT.BIN文件所在的位置。从BOOT . BIN文件中找到FSBL代码。找到之后将其拷贝到内存当中( ZYNQ片 内RAM 256K字节大小)。运行FSBL代码。而以上这些事情由BootROM来去做。BootROM何为BootROM?BootROM它是一个程序/代码,并且已经固话在ZYNQ芯片内部,用于启动的BOOT程序。BootROM代码存放片内ROM当中,所以叫做BootROM。因为ZYNQ内部包含256K RAM 以及128K ROM。所以BootROM代码可以固化在ROM当中,并且在掉电情况下不会丢失。一般情况下,芯片内部的ROM都是Nor Flash。NOR Flash 的特点是芯片内执行(XIP ,eXecute In Place),这样应用程序可以直接在Flash闪存内运行,不必再把代码读到系统RAM中。BootROM的作用与功能BootROM的主要作用用于引导、启动FSBL代码。它要完成引导、启动FSBL代码的这个任务需要完成以下几件事情(也就是前文提到的FSBL的启动准备工作):找到BOOT.BIN文件所在的位置。从BOOT . BIN文件中找到FSBL代码.找到之后将其拷贝到内存当中(ZYNQ片内RAM 256K字节大小)运行FSBL代码。为了支持上述的操作,所以BootROM程序需要支持以下功能。包含了SD卡或者QSPI等存储设置的驱动程序。BootROM代码支持文件系统操作,至少支持Fat 32这个文件系统。对于ZYNQ平台的嵌入式Linux系统来说,Linux内核由U-Boot引导、启动;U-Boot由FSBL引导、启动;FSBL由ZYNQ片内BootROM引导、启动。SD卡加载方式启动流程在Sd卡启动方式下,BootROM代码的运行流程:初始化MI0引脚,主要配置MIO引脚的物理特性配置寄存器;重点是将MIO40~MIO45复用为SD0外设所对应的CLK/CMD/DATA引脚。初始化SD卡外设,驱动SD卡,可以实现sD卡读写操作。对SD卡读写进行测试。从SD卡文件系统当中读取BOOT. BIN文件,并对BootROM头进行解析。在BOOT.BIN文件前面有一段头部信息,这个头部信息是按照一-定的格式组组织在一起,在这个头部信息当中就包括了fsbl的加载地址、fsbl的 大小以及fsbl在BOOT. BIN文件中的位置偏移量。BootROM代码能够解析这个头部信息得到fsbl代码的大小和位置偏移量以及加载地址之后,BootROM代码就会从BOOT.BIN文件中。将fsbl代码拷贝到RAM内存中,并且跳转到fsbl代码的运行地址去启动fsbl。自此,BootROM就成功启动了FSBL代码了。QSPI加载方式启动流程在QSPI启动方式下,BootROM代码的运行流程:初始化MIO引脚,将相关的MIO引脚复用为QSPI外设所需的引脚功能。初始化QSPI外设,驱动QSPI Flash设备,可以实现QSPI读写操作。对QSPI进行读写测试。从QSPI存储介质中读取BOOT. BIN文件,并对Boot ROM头进行解析。得到fsbl代码的大小和位置偏移量以及加载地址之后,BootROM代码就会从BOOT.BIN文件中。将fsbl代码拷贝到RAM内存中,并且跳转到fsbl代码的运行地址去启动fsbl。不同于SD卡的文件系统的搜索方式,在QSPI启动方式下,BootROM代码首先会从QSPI的0x000000地址去找BOOT . BIN文件,如果找不到那么就去下一个地址0x008000,如果还找不到他又会跳转到下一个地址0x10000,但是搜索范围不能超出QSPI的前面16MB地址空间。BOOT.BIN头BOOT.BIN头是BOOT.BIN文件前面的一段头部数据,并且这个头部数据是按照一定格式组织在一起的, 并且该头部数据能够并BootROM代码所解析。在BOOT.bin文件中从地址0-0x8FF可以分成17个部分,每个部分都有一定的含义。0x000:中断向量表。0x020:固定值0xaa995566(小端)。0x024:固定值0x584c4e58 ASCII: XLNX。0x028:如果是0xa5c3c5a3或者0x3a5c3c5a为加密的。0x02C:BootROM头版本号,不用管。0x030:此参数包含从有效bootrom头开始到fslb/用户代码映像所在位置的字节数,也就是 FSBL/用户代码的地址偏移量。该地址偏移量必须要大于等于0x8C0。0x034:记录fsbl的长度,用于指导BootROM代码拷贝 fsbl 长度。0x038:将FSBL拷贝到OCM的什么位置一般为0x0,加载地址,指导BootROM代码拷贝FSBL到RAM的哪个位置。0x03C:FSBL在OCM中的运行地址一般定义为0x0,运行地址,指导BootROM代码跳转到RAM哪个地址去运行。0x040:记录FSBL的长度。0x044:为固定值0x01。0x048:校验和(将Ox020-0x047之间的数据按32bit长度进行相加,并取反即可!若相加之后的数据大小超过32bit,则取低32bit 数据进行取反)。0x04C-0x097:fsbl/用户代码自定义,不需要的话可以全部填充为0。0x098:image header table位置偏移量。0x09C:partition header table 的所在位置。0x0A0-0x89F:寄存器初始化的参数。0x8C0:FSBL、用户代码必须要等于或高于此地址。简述通过BOOT.BIN头如何找到FSBLBOOT.BIN头部信息当中记录了FSBL代码的位置、大小以及fsbl代码它在RAM内存中的加载地址。0x30地址记录了fsbl代码在BOOT.BIN文件中的位置偏移量,0x34记录了fsbl代码的长度,0x38记录了fsbl代码在SRAM中的加载地址,BootROM代码解析到这些信息之后,就会从FSBL代码的位置偏移量去读取0x34地址中记录的大小,然后把它拷贝到FSBL代码的加载地址中。最后跳转到0x3C地址中记录的FSBL的运行地址中去启动FSBL。简述通过BOOT.BIN如何找到U-Boot和 bitstreamBOOT.BIN文件当中包含了FSBL镜像、u-boot镜像以及bitstream 文件。BootROM代码需要通过解析BOOT.BIN头部信息去找到FSBL。BootROM代码去启动FSBL。FSBL代码运行之后,要负责从 BOOT.BIN文件中找到U-Boot镜像和 bitstream文件,然后把 bitstream文件加载到ZYNQ PL端,然后要启动U-Boot。这里需要涉及到三个数据表:image headelr table;partition header table;image header。Image Headelr Tableimage header table 只有一个,partition header table和 image header是成对出现的。BOOT.BIN 文件中包含了多少个镜像,那么就有多少对partition header table 和 imageheader。0x00:image header table 的版本号;ox04:image header的数量;0x08:第一个Partition Header table的位置偏移量。这里是以word为单位计算的,所以实际的偏移量需要乘上一个4;0x0C:第一个Image Header的位置偏移量。采用了word 度量单位;0x10:header authentication 的偏移量。采用了word为度量单位;0x1C:使用0xFFFFFFFF进行填充,直到整个image header table的大小为64字节。Image Header0x0:下一个limage header 的地址偏移量,如果这里填充为0,则表示这是最后一个image header;0x4:与之相关联的partition header table 的位置偏移量;0x8:该地址总是0;0xC:实际分区计数的值;0x10-N:记录镜像名称。varies:用于填充。Partition Header Table0x0:加密分区的数据长度;单位是字计算时必须要乘上4;0x4:未加密分区的数据长度,如该分区是u-boot,则指示了u-boot的长度,计算同上;0x8:加密+填充+扩展+身份验证的数据总长度;0xC:该分区数据的加载地址,指的是该分区数据需要拷贝到内存的什么位置;0x10:该分区数据的运行地址,指运行该分区代码时需要跳转到那个内存地址;0x14:该分区数据在BOOT.BIN文件中的位置偏移量,拷贝的时候就是从该地址进行拷贝的;0x18:属性位;0x1C: Section计数;0x20:校验和字段的位置;0x24:该partition header table所对应的 image header所在位置。以 word字为单位;0x28:加密相关的字段;0x2C-0x38:未定义;0x3C:校验和。referencesUG585UG821
Flash Settings —> :设置flash有关的配置,工程默认设置了4个分区,可以根据使用情况修改相关配置分区。设置分区中size设置为0x0,则默认把其余分区给分配到该分区下。RTC Settings —>: 设置时钟有关选项。SD/SDIO Settings —>:设置SD和emmc有关。[*] Advanced bootable images storage Settings —> 镜像的存储介质boot image settings —>: 启动文件BOOT.BIN文件相关的设置。可以存储在sd或者flash中。存储在sd下的话,可以对读取BOOT.BIN的镜像名称进行修改。如果设置存储在flash中,配置界面会有变化。可以修改读取的镜像名称,还要设置镜像存储在哪个分区。u-boot env partition settings —>: U-Boot环境变量存储介质设置。和前面相似也可以设置存储flash和sd,默认为flash。flash模式下也需要对存储区进行设置。kernel image settings —>: 镜像内核相关设置,点击该选项后,存储介质中多了一个网络的选项。也就是可以通过网络的模式下载到板卡上。jffs2 rootfs image settings —>:根文件系统,一般在norflash的存储模式下使用。dtb image settings —>: 内核设备树镜像相关的设置。设置存储选项有四种,可以来自boot image也就是image.ub文件,同时支持网络。qspi,sd卡/emmc去存储。DTG Settings —>配置设备树相关。设置设备树的flags和overlay,以及是否启用移除PL端的相关配置设备树。Kernel Bootargs —> :内核的bootargs变量。进入子目录下,有以下选项。generate boot args automatically:配置使能bootargs自动配置。一般情况下保持默认即可。u-boot Configuration —>配置u-boot的相关选项。u-boot config target : 用于配置编译U-Boot时使用的配置文件。netboot:和网络启动有关。**TFTP Serven IP address **: 用于设置TFTP服务器的IP地址。Image Packaging Configuration —>和镜像打包有关配置。Root filesystem type (INITRAMFS) —>:根文件系统设置。Copy final images to tftpboot,编译完petalinux工程之后自动将镜像文件拷贝到tftp服务器的目录,默认使能。同时可以设置tftpboot路径。Firmware Version Configuration —>配置固件版本名称,可任意设置。Yocto Settings —>petalinux底层使用yocto,在yocto的基础上进行进一步封装。
非组合型对于Verilog,数组经常会被用来做数据存储,reg [15:0] RAM [0 : 4095] ; //memory arraySV将Verilog这种声明数组的方式称之为非组合型声明,数组中的成员之间存储数据都是互相独立的。Sv保留了非组合型的数组声明方式,拓展了允许的类型。包括event,logic,bit,byte,int,longint,shortreal和real类型。SV也保留了Verilog索引非组合型数组或者数组片段的能力,这种方式为数组以及数组片段的拷贝带来了方便。声明数组的方式,以下两种皆可logic [31:0] data [1024]; logic [31:0] data [0 :1023];组合型sv将verilog的向量作为组合类型数组声明方式。wire [3:0] select;// 4-bit "packed array"SV也进—步允许多维组合型数组的声明。wire [ 3:0 ][ 7:0 ] data;//packed array也可以用来定义结构体的存储方式。typedef struct packed { logic [ 7:0]crc; logic [ 63:0]data; }data word; data word [7:0] darray; // 1-D packed array非组合型数组初始化时,需要通过‘{}对数组每个维度进行赋值。int d [0:1][0:3] = '{'{7,3,0,5}, '{ 2,0,1,6}};赋值非组合型数组在初始化时,也可以类似结构体初始化,可以通过’{}和default关键字进行初始化。非组合型数组的数据成员或者数组本身均可以为其赋值:byte a [o:3][0:3]; a[1][0]= 8' h5;// assign to one element以下是组合型数组的赋值方法:拷贝对于组合型数组,由于数组会被视为向量,因此当赋值左右两侧操作时大小为度不一致时,也可以做赋值。如果当尺寸不相同时,则会通过截取或者扩展右侧操作数的方式来对左侧操作数赋值。非组合数组,发生数组间拷贝时,要求左右两侧操作数维度和大小必须严格一致。对于非组合数组无法直接赋值给组合型数组,组合型数组也无法直接赋值给非组合数组。foreachsv添加foreach循环来对一位或者多位数组进行循环索引,而不需要指定该数组的维度大小。foreach循环结构中的变量无需声明,变量时只读的,作用域只在此循环结构中。int sum [1:8][1:3] ; foreach (sum[i,j]) sum[i][j]= i +j;//initialize array系统函数sv中提供一些系统函数方便对数组进行操作。$dimensions(array_name):用来返回数组的维度。left(arrayname,dimension):返回指定维度的最左索引值(msb)。类似的,还有{right, low, high}(array_name, dimension)。$size(array_name, dimension):可以返回指定维度的尺寸大小。$increment(array_name, dimension):如果指定维度的最左索引值大于或等于最右索引值,那么返回1,否则返回-1。$bits(expression):可以用来返回数组存储的比特数。动态数组动态数组是个非组合数组。SystemVerilog 提供了动态数组类型,可以在仿真时分配空间或调整宽度,这样在仿真中就可以使用最小的存储量。动态数组在声明时使用空的下标[]。这意味着数组的宽度不在编译时给出,而在程序运行时再指定。数组在最开始时是空的,所以你必须调用new[]操作符来分配空间,同时在方括号中传递数组宽度。可以把数组名传给new[]构造符,并把已有数组的值复制到新数组里。int dyn[],d2[]; //声明动态数组 initial begin dyn=new[5];//分配5个元素 foreach (dyn[j]) dyn[j]=j;//对元素进行初始化 d2= dyn; //复制一个动态数组 d2[o]=5; //修改复制值 end内建方法size()可以返回动态数组的大小。内建方法delete()可以清空动态数组,使其尺寸变为0。动态数组在声明时也可以完成其初始化队列SystemVerilog引进了一种新的数据类型—队列,它结合了链表和数组的优点。队列与链表相似,可以在一个队列中的任何地方增加或删除元素,这类操作在性能上的损失比动态数组小得多,因为动态数组需要分配新的数组并复制所有元素的值。队列与数组相似,可以通过索引实现对任元素的访问,而不需要像链表那样去遍历目标元素之前的所有元素。队列是个组合数组。sv引入了队列类型,它结合了数组和链表的特点。可以在队列的任何位置添加或者删除数据成员。可以通过索引来访问队列的任何一个成员。通过[] 来 声 明 队 列 , 队 列 的 索 引 值 从 0 到 ]来声明队列,队列的索引值从0到]来声明队列,队列的索引值从0到。可以通过内建方法push_back(val)、push_front(val)、pop_back()和pop_front()来顺序添加或者移除并且获得数据成员。insert(pos, val)来在指定位置插入数据成员。delete()来删除所有数据成员。关联数组如果想要在仿真时创建一个大的数组,也许动态数组是一个选择,不过有的时候,我们并不需要那么大的一个数组。由于处理器在访问存储时的访问的随机或者散乱的,这意味在一个测试中,处理器也许只会访问几百个存储地址,而剩下大多数的地址都将被初始化为0并且浪费了仿真时的存储空间。关联数组可以用来存放散列的数据成员。类似python的字典。散列的索引类型除了为整形以外还可以为字符串或者其它类型,而散列存储的数据成员也可以为任意类型。不定长的能用内建方法size()。数组操作缩减方法基本的数组缩减方法是把一个数组缩减成一个值。最常用的缩减方法是sum,它对数组中的所有元素求和。其它的数组缩减方法还有product(积),and(与),or(或)和xor(异或)。定位方法对于非合并数组,可以使用数组定位方法,其返回值将是一个队列而非一个数据成员。使用foreach也可以实现数组的搜索,不过使用find…with则在查找满足条件的数据成员时,更为方便。排序方法可以通过排序方法改变数组中元素的顺序,可以对它们进行正向、逆向或者乱序的排列。References路科验证西电课程PPT《SystemVerilog验证-测试平台编写指南》
流水操作pragma HLS pipeline说明PIPELINE pragma 通过允许并发执行操作来减少函数或循环的启动间隔。流水线函数或循环可以每 N 个时钟周期处理新输入,其中 N 是循环或函数的启动间隔 (II)。 PIPELINE pragma 的默认启动间隔为 1,它在每个时钟周期处理一个新输入。 您还可以通过使用编译指示的 II 选项来指定启动间隔。流水线化循环允许循环的操作以并发方式实现,如下图所示。 在该图中,(A) 显示了默认顺序操作,其中每次输入读取之间有 3 个时钟周期 (II=3),并且在执行最后一次输出写入之前需要 8 个时钟周期。如果 Vivado HLS 无法使用指定的 II 创建设计,它会:发出警告。创建具有尽可能低的 II 的设计。然后,您可以使用警告消息分析此设计,以确定必须采取哪些步骤来创建满足所需启动间隔的设计。语法将pragma放在函数或循环体的C源代码中。#pragma HLS pipeline II=<int> enable_flush rewind其中:II=<int>:指定流水操作的所需启动间隔。Vivado HLS试图满足此要求。基于数据依赖关系,实际结果可能具有更大的启动间隔。默认值II为1。enable_flush:一个可选关键字,用于实现流水,如果流水输入的有效数据变为非活动状态,则该流水线将刷新并清空。此功能仅支持流水线函数:流水线循环不支持此功能。rewind:一个可选关键字,用于启用倒带或连续循环流水,在一个循环迭代结束和下一个迭代开始之间不暂停。只有在顶级函数中有一个单循环(或完美的循环嵌套)时,rewind才有效。此功能仅支持流水线循环:流水线函数不支持此功能。循环前的代码段:被视为初始化。在流水中只执行一次。不能包含任何条件操作(if-else)示例此示例函数foo以1的启动间隔进行流水化:void foo { a, b, c, d} { #pragma HLS pipeline II=1 ... }II的默认值为1,因此本例中不需要特别设置II=1。pragma HLS occurrence说明当流水操作函数或循环时,OCCURRENCE pragma指定区域中的代码执行频率低于封闭函数或循环中的代码。这允许执行频率较低的代码以较慢的速度进行流水线化,并可能在顶级流水操作中共享。要确定OCCURRENCE情况,请执行以下操作:一个循环迭代N次。但是,循环体的一部分由条件语句启用,因此只执行M次,其中N是M的整数倍。条件代码的出现速度比循环体的其余部分慢N/M倍。例如,在执行10次的循环中,循环中的条件语句仅执行2次,occurrence次数为5(或10/2)。使用occurrence指令标识区域,可以使该区域中的函数和循环以比封闭函数或循环慢的更高启动间隔进行流水化操作。语法将pragma放在C源代码中的代码区域内。#pragma HLS occurrence cycle=<int>其中,cycle=<int>:指定occurrence 次数 N/M。N是执行封闭函数或循环的次数。M是执行条件区域的次数。N必须是M的整数倍。示例在此示例中,区域Cond_Region的occurrence 次数为4(它的执行频率比包含它的周围代码的执行频率低四倍):Cond_Region: { #pragma HLS occurrence cycle=4 ... }循环展开pragma HLS unroll说明展开循环以创建多个独立操作而不是单个操作集合。UNROLL 编译指示通过在 RTL 设计中创建循环体的多个副本来转换循环,这允许并行发生一些或所有循环迭代。C/C++ 函数中的循环默认保持滚动。 当循环滚动时,综合为循环的一次迭代创建逻辑,RTL 设计按顺序为循环的每次迭代执行此逻辑。 循环执行循环归纳变量指定的迭代次数。 迭代次数也可能受循环体内部逻辑的影响(例如,中断条件或对循环出口变量的修改)。 使用 UNROLL 编译指示,您可以展开循环以增加数据访问和吞吐量。UNROLL 编译指示允许完全或部分展开循环。 完全展开循环会为每次循环迭代在 RTL 中创建循环体的副本,因此可以同时运行整个循环。 部分展开循环可让您指定因子 N,以创建循环体的 N 个副本并相应地减少循环迭代。 要完全展开循环,必须在编译时知道循环边界。 这不是部分展开所必需的。部分循环展开不需要 N 是最大循环迭代计数的整数因子。 Vivado HLS 添加了退出检查以确保部分展开的循环在功能上与原始循环相同。 例如,给定以下代码:for(int i = 0; i < X; i++) { pragma HLS unroll factor=2 a[i] = b[i] + c[i]; }循环展开 2 倍有效地将代码转换为如下代码,其中使用 break 构造确保功能保持不变,并且循环在适当的点退出:for(int i = 0; i < X; i += 2) { a[i] = b[i] + c[i]; if (i+1 >= X) break; a[i+1] = b[i+1] + c[i+1]; }由于最大迭代次数 X 是一个变量,Vivado HLS 可能无法确定其值,因此会向部分展开的循环添加退出检查和控制逻辑。 但是,如果知道指定的展开因子(在此示例中为 2)是最大迭代次数 X 的整数因子,则skip_exit_check 选项可让您删除退出检查和相关逻辑。 这有助于最小化面积并简化控制逻辑。当使用 DATA_PACK、ARRAY_PARTITION 或 ARRAY_RESHAPE 等编译指示让更多数据在单个时钟周期内访问时,Vivado HLS 会自动展开任何消耗此数据的循环,如果这样做可以提高吞吐量。 该循环可以完全或部分展开,以创建足够的硬件以在单个时钟周期内消耗额外的数据。 此功能是使用 config_unroll 命令控制的。语法将编译指示放在循环体中的 C/C++ 源代码中以展开。#pragma HLS unroll factor=<N> region skip_exit_check其中:factor=<N>:指定一个非零整数,表示请求部分展开。 循环体重复指定的次数,并相应调整迭代信息。 如果未指定 factor=,则循环完全展开。region:一个可选关键字,用于展开指定循环主体(区域)内的所有循环,而不展开封闭循环本身。skip_exit_check:一个可选关键字,仅在使用factor= 指定部分展开 时才适用。 退出检查的消除取决于循环迭代计数是已知还是未知:○ 固定(已知)边界:如果迭代计数是因子的倍数,则不执行退出条件检查。 如果迭代计数不是因子的整数倍,则该工具:防止展开。发出警告,必须执行退出检查才能继续。○ 变量(未知)边界:根据要求移除退出条件检查。 必须确保:变量bounds是指定展开因子的整数倍。实际上不需要退出检查。示例示例1以下示例完全展开函数 foo 中的 loop_1。 将编译指示放在 loop_1 的主体中,如下所示:loop_1: for(int i = 0; i < N; i++) { #pragma HLS unroll a[i] = b[i] + c[i]; }示例2此示例指定展开因子 4 以部分展开函数 foo 的 loop_2,并删除退出检查:void foo (...) { int8 array1[M]; int12 array2[N]; ... loop_2: for(i=0;i<M;i++) { #pragma HLS unroll skip_exit_check factor=4 array1[i] = ...; array2[i] = ...; ... } ... }示例3下面的示例完全展开函数 foo 中 loop_1 内的所有循环,但由于 region 关键字的存在而不是 loop_1 本身:void foo(int data_in[N], int scale, int data_out1[N], int data_out2[N]) { int temp1[N]; loop_1: for(int i = 0; i < N; i++) { #pragma HLS unroll region temp1[i] = data_in[i] * scale; loop_2: for(int j = 0; j < N; j++) { data_out1[j] = temp1[j] * 123; } loop_3: for(int k = 0; k < N; k++) { data_out2[k] = temp1[k] * 456; } } }pragma HLS dependence说明DEPENDENCE 编译指示用于提供附加信息,这些信息可以克服循环携带依赖性并允许循环流水线化(或以较低的间隔流水线化)。Vivado HLS 自动检测依赖性:循环内(循环独立依赖性)。循环的不同迭代之间(循环进位依赖性)。这些依赖关系会影响何时可以调度操作,尤其是在函数和循环流水线期间。循环独立依赖:在同一个循环迭代中访问相同的元素。for (i=0;i<N;i++){ A[i]=x; y=A[i]; }循环进位依赖:在不同的循环迭代中访问相同的元素。for (i=0;i<N;i++) { A[i]=A[i-1]*2; }在某些复杂的场景下,自动依赖分析可能过于保守,无法过滤掉错误的依赖。 在某些情况下,例如变量依赖数组索引,或者需要强制执行外部要求时(例如,两个输入永远不会是同一个索引),依赖分析可能过于保守。 DEPENDENCE pragma 允许您显式指定依赖项并解决错误的依赖项。指定错误的依赖项,而实际上依赖项不是错误的,可能会导致硬件不正确。 在指定依赖项之前,请确保依赖项是正确的(true 或 false)。语法将 pragma 放在定义依赖关系的函数的边界内。#pragma HLS dependence variable=<variable> <class> <type> <direction> distance=<int> <dependent>其中:variable=<variable>:(可选)指定要考虑依赖关系的变量。<class>:可选地指定依赖关系需要澄清的变量类。有效值包括数组或指针。<class> 和 variable= 不需要一起指定,因为您可以在函数内指定一个变量或一组变量。<type>:有效值包括 intra或 inter。 指定依赖是否为:○ intra:同一循环迭代内的依赖。 当依赖 dependency <type> 被指定为intra,并且 <dependent> 为 false 时,Vivado HLS 可以在循环内自由移动操作,增加它们的移动性并潜在地提高性能或面积。 当 <dependent> 指定为 true 时,必须按照指定的顺序执行操作。○ inter:不同循环迭代之间的依赖关系。 这是默认的 <type>。 如果将dependency <type> 指定为inter,并且<dependent> 为false,则在函数或循环已流水线化、循环展开或部分展开时,它允许Vivado HLS 并行执行操作,并在以下情况下阻止此类并发操作 <dependent> 被指定为 true。<direction>:有效值包括 RAW、WAR 或 WAW。 这仅与循环进位相关性相关,并指定相关性的方向:○ RAW(写后读 - 真实相关性) 写指令使用读指令使用的值。○ WAR(Write-After-Read - 反依赖) 读指令得到一个被写指令覆盖的值。○ WAW(Write-After-Write - 输出依赖) 两条写入指令以一定的顺序写入同一个位置。distance=<int>:指定数组访问的迭代间距离。 仅与循环进位依赖关系相关,其中依赖关系设置为 true。<dependent>:指定是否需要强制执行 (true) 或删除 (false) 依赖项。 默认值为true。示例示例1在以下示例中,Vivado HLS 不了解 cols 的值,并且保守地假设写入 buff_A[1] [col] 和读取 buff_A[1][col] 之间始终存在依赖关系 . 在这样的算法中,cols 不太可能为零,但 Vivado HLS 无法对数据依赖性做出假设。 为了克服这个缺陷,您可以使用 DEPENDENCE 编译指示声明循环迭代之间没有依赖关系(在这种情况下,对于 buff_A 和 buff_B)。void foo(int rows, int cols, ...) for (row = 0; row < rows + 1; row++) { for (col = 0; col < cols + 1; col++) { #pragma HLS PIPELINE II=1 #pragma HLS dependence variable=buff_A inter false #pragma HLS dependence variable=buff_B inter false if (col < cols) { buff_A[2][col] = buff_A[1][col]; // read from buff_A[1][col] buff_A[1][col] = buff_A[0][col]; // write to buff_A[1][col] buff_B[1][col] = buff_B[0][col]; temp = buff_A[0][col]; }示例2删除函数 foo 中 loop_1 的相同迭代中 Var1 之间的依赖关系。#pragma HLS dependence variable=Var1 intra false示例3在函数 foo 的 loop_2 中定义对所有数组的依赖,以通知 Vivado HLS 所有读取必须在同一循环迭代中的写入 (RAW) 之后发生。#pragma HLS dependence array intra RAW true循环优化pragma HLS loop_flatten说明允许将嵌套循环展平为单个循环层次结构,从而提高延迟。在RTL实现中,需要一个时钟周期才能从外循环移动到内循环和从内循环移动到外循环。展平嵌套循环可以将其作为单个循环进行优化。这节省了时钟周期,可能允许对循环体逻辑进行更大的优化。将LOOP_FLATTEN应用于循环层次结构中最内层循环的循环体。只有完美和半完美循环可以用这种方式展平:完美的循环:○ 只有最里面的循环具有循环体内容。○ 循环语句之间没有指定逻辑。○ 所有循环边界都是常量。半完美环嵌套:○ 只有最里面的循环具有循环体内容。○ 循环语句之间没有指定逻辑。○ 最外层的循环边界可以是变量。不完美的循环嵌套:当内部循环具有可变边界(或循环体不完全位于内部循环内)时,尝试重新构造代码,或在循环体中展开循环以创建完美的循环嵌套。语法将pragma放在嵌套循环边界内的C源代码中。#pragma HLS loop_flatten off其中,off:是一个可选关键字,用于防止发生展平。可以防止在展平指定位置中的所有其他循环时展平某些循环。示例示例1将函数foo中的loop_1和循环层次结构中它上面的所有(完美或半完美)循环展平为单个循环。将pragma放置在 loop_1的主体中。void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS loop_flatten ... result = a + b; } }示例2防止loop_1中的循环展平:loop_1: for(i=0;i< num_samples;i++) { #pragma HLS loop_flatten off ...pragma HLS loop_merge说明将连续循环合并到单个循环中,以减少总体延迟、增加共享并改进逻辑优化。合并循环可以:减少RTL中在循环体实现之间转换所需的时钟周期数。允许并行实现循环。LOOP_MERGE pragma将寻求合并其放置范围内的所有循环。例如,如果在循环体中应用 LOOP_MERGE pragma,Vivado HLS将指令应用于循环中的任何子循环,但不应用于循环本身。合并循环的规则是:如果循环边界是变量,则它们必须具有相同的值(迭代次数)。如果循环边界为常量,则最大常量值用作合并循环的边界。不能合并具有可变边界和常量边界的循环。要合并的循环之间的代码不能有单侧作用。多次执行此代码应生成相同的结果(允许a=b,不允许a=a+1)。循环包含FIFO读取时无法合并。合并会更改读取的顺序。从FIFO或FIFO接口的读取必须始终按顺序进行。语法将C源代码中的pragma放在所需的代码范围或区域内:#pragma HLS loop_merge force其中,force是一个可选关键字,用于强制合并循环,即使Vivado HLS发出警告。在这种情况下,您必须手动确保合并的循环将正常工作。示例将函数foo中的所有连续循环合并到单个循环中。void foo (num_samples, ...) { #pragma HLS loop_merge int i; ... loop_1: for(i=0;i< num_samples;i++) { ... loop_2 内的所有循环(但不是loop_2本身)通过使用force选项合并。将pragma放置在loop_2的主体中。loop_2: for(i=0;i< num_samples;i++) { #pragma HLS loop_merge force ...pragma HLS loop_tripcount说明TRIPCOUNT指令可应用于循环,以手动指定循环执行的总迭代次数。TRIPCOUNT指令仅用于分析,不影响合成结果。Vivado HLS报告每个循环的总延迟,即执行循环所有迭代的时钟周期数。因此,循环延迟是循环迭代次数或tripcount的函数。tripcount可以是一个常量值。它可能取决于循环表达式中使用的变量值(例如,x<y),也可能取决于循环中使用的控制语句。在某些情况下,Vivado HLS无法确定tripcount,且延迟未知。这包括用于确定tripcount的变量为:输入参数。通过动态操作计算的变量。在循环延迟未知或无法计算的情况下,TRIPCOUNT指令允许您指定循环的最小和最大迭代次数。这使该工具能够分析循环延迟如何影响报告中的总设计延迟,并帮助您确定设计的适当优化。语法将pragma放在循环体中的C源代码中:#pragma HLS loop_tripcount min=<int>max=<int>avg=<int>其中:max=<int>:指定循环迭代的最大次数。min=<int>:指定循环迭代的最小次数。avg=<int>:指定循环迭代的平均次数。示例函数foo中的loop_1指定最小tripcount为12,最大tripcount为16:pragma HLS array_reshape说明将阵列分区与垂直阵列映射相结合。ARRAY_RESHAPE 编译指示结合了 ARRAY_PARTITION 的效果,将数组分解为更小的数组,以及 ARRAY_MAP 的垂直类型的效果,通过增加位宽来连接数组元素。 这减少了消耗的块 RAM 数量,同时提供了分区的主要好处:对数据的并行访问。 此编译指示创建了一个元素更少但位宽更大的新数组,从而允许在单个时钟周期内访问更多数据。参考以下代码:void foo (...) { int array1[N]; int array2[N]; int array3[N]; #pragma HLS ARRAY_RESHAPE variable=array1 block factor=2 dim=1 #pragma HLS ARRAY_RESHAPE variable=array2 cycle factor=2 dim=1 #pragma HLS ARRAY_RESHAPE variable=array3 complete dim=1 ... }ARRAY_RESHAPE 编译指示将数组转换为下图所示的形式:语法将编译指示放在 C 源代码中定义数组变量的函数区域内。#pragma HLS array_reshape variable=<name> <type> factor=<int> dim=<int>其中:<name>:一个必需的参数,指定要重塑的数组变量。<type>:可选地指定分区类型。 默认类型是完整的。 支持以下类型:○ cyclic:循环重塑通过交错原始数组中的元素来创建更小的数组。 例如,如果使用factor=3,则将元素0分配给第一个新数组,将元素1分配给第二个新数组,将元素2分配给第三个新数组,然后再次将元素3分配给第一个新数组 . 最后一个数组是新数组的垂直串联(词串联,以创建更长的词)到单个数组中。○block:块重塑从原始数组的连续块创建更小的数组。 这有效地将数组拆分为 N 个相等的块,其中 N 是由 factor= 定义的整数,然后将 N 个块合并为一个字宽*N 的数组。○complete:完全重塑将数组分解为临时的单个元素,然后将它们重新组合成一个具有更宽单词的数组。 对于一维数组,这相当于创建一个非常宽的寄存器(如果原始数组是 M 位的 N 个元素,则结果是一个 N*M 位的寄存器)。 这是数组整形的默认类型。factor=<int>:指定当前数组除以的数量(或要创建的临时数组的数量)。 factor=2时,数组分成两半,同时将位宽加倍。factor=3时将数组分成三份,位宽为三倍。对于complete的类型分区,不需要指定factor。 对于块和循环分区,需要 factor。dim=<int>:指定要分区的多维数组的哪个维度。 对于具有 N 维的数组,指定为从 0 到 N 的整数:○ 如果使用值 0,则多维数组的所有维度都使用指定的类型和因子选项进行分区。○ 任何非零值仅对指定维度进行分区。 例如,如果使用值 1,则仅对第一个维度进行分区。object:仅与容器数组相关的关键字。 当指定关键字时,ARRAY_RESHAPE 编译指示应用于容器中的对象,重塑容器内对象的所有维度,但保留容器本身的所有维度。当未指定关键字时,编译指示适用于容器数组而不是对象。示例示例1使用块映射将具有 17 个元素的 8 位数组 AB[17] 重新整形(分区和映射)为具有五个元素的新 32 位数组。#pragma HLS array_reshape variable=AB block factor=4factor=4 表示数组应该被分成四份。 因此,17 个元素被重新整形为 5 个元素的数组,位宽是其四倍。 在这种情况下,最后一个元素 AB[17] 被映射到第五个元素的低八位,而第五个元素的其余部分为空。示例2将二维数组 AB[6][4] 重塑为维度 [6][2] 的新数组,其中维度 2 具有两倍的位宽:#pragma HLS array_reshape variable=AB block factor=2 dim=2示例3将函数 foo 中的三维 8 位数组 AB[4][2][2] 重塑为一个新的单元素数组(一个寄存器),128 位宽(4*2*2*8):#pragma HLS array_reshape variable=AB complete dim=0dim=0 表示重塑数组的所有维度。结构体打包pragma HLS data_pack说明将结构的数据字段打包成一个具有更宽字宽的标量。DATA_PACK 编译指示用于将结构体的所有元素打包到单个宽向量中以减少变量所需的内存,同时允许同时读取和写入结构体的所有成员。 生成的新宽字的位对齐可以从结构域的声明顺序推断出来。 第一个字段采用向量的 LSB,结构体的最后一个元素与向量的 MSB 对齐。如果结构包含数组,则 DATA_PACK 编译指示执行与 ARRAY_RESHAPE 编译指示类似的操作,并将重构后的数组与结构中的其他元素组合。 在结构中声明的任何数组都被完全分区并重新整形为一个宽标量,并与其他标量字段打包在一起。 但是,不能使用 DATA_PACK 和 ARRAY_PARTITION 或 ARRAY_RESHAPE 优化结构,因为这些编译指示是互斥的。在对具有大型数组的结构对象使用 DATA_PACK 优化时,应该谨慎行事。 如果一个数组有 4096 个 int 类型的元素,这将导致一个宽度为 4096*32=131072 位的向量(和端口)。 Vivado HLS 可以创建此 RTL 设计,但是逻辑综合不太可能在 FPGA 实现期间对其进行路由。通常,赛灵思建议使用任意精度(或位精度)数据类型。标准 C 类型基于 8 位边界(8 位、16 位、32 位、64 位),但是,在设计中使用任意精度数据类型可让您在 C 代码中指定确切的位大小。 位精确宽度导致硬件运算符更小、更快。 这允许在 FPGA 中放置更多逻辑,并使逻辑以更高的时钟频率执行。 但是,如果需要,DATA_PACK 编译指示还允许您沿 8 位边界对齐打包结构中的数据。如果要使用 AXI4 接口实现结构端口,应该考虑使用 DATA_PACK <byte_pad> 选项来自动将结构的成员元素对齐到 8 位边界。 AXI4-Stream 协议要求 IP 的 TDATA 端口的宽度为 8 的倍数。定义 TDATA 端口宽度不是 8 的倍数的 AXI4-Stream IP 是违反规范的,因此,它是一个 要求将 TDATA 宽度四舍五入为字节倍数。语法将编译指示放在要打包的 struct 变量的定义附近:#pragma HLS data_pack variable=<variable> instance=<name> <byte_pad>其中,variable=<variable>:是要打包的变量。instance=<name>:指定打包后结果变量的名称。 如果未指定 <name>,则使用输入 <variable>。<byte_pad>:可选地指定是否在8 位边界(8 位、16 位、24 位…)上打包数据。 此选项支持的两个值是:○ struct_level:首先打包整个结构,然后向上填充到下一个 8 位边界。○field_level:首先在 8 位边界上填充结构的每个单独元素(字段),然后打包结构。示例示例将具有三个 8 位字段字段(R、G、B)的结构数组 AB[17] 打包成一个新的 24 位 17 元素数组。typedef struct{ unsigned char R, G, B; } pixel; pixel AB[17]; #pragma HLS data_pack variable=AB示例2将具有函数 foo 中的三个 8 位字段(typedef struct {unsigned char R, G, B;} pixel)的结构指针 AB 打包成一个新的 24 位指针。typedef struct{ unsigned char R, G, B; } pixel; pixel AB; #pragma HLS data_pack variable=AB示例3在此示例中,为 rgb_to_hsv 函数的输入和输出参数指定了 DATA_PACK 编译指示,以指示编译器在 8 位边界上打包结构以改进内存访问:void rgb_to_hsv( RGBcolor* in, // Access global memory as RGBcolor structwise HSVcolor* out, // Access Global Memory as HSVcolor structwise int size) { #pragma HLS data_pack variable=in struct_level #pragma HLS data_pack variable=out struct_level ... }
pragma HLS top说明将名称附加到函数,然后可以与 set_top 命令一起使用该名称来合成该函数以及从指定顶级调用的任何函数。 这通常用于在 C/C++ 中合成类的成员函数。在活动解决方案中指定编译指示,然后使用具有新名称的 set_top 命令。语法将编译指示放在 C 源代码中所需位置的边界内。#pragma HLS top name=<string>其中, name=,指定 set_top 命令使用的名称。示例函数 foo_long_name 被指定为顶级函数,并重命名为 DESIGN_TOP。在代码中放置 pragma 之后,set_top 命令仍然必须从 Tcl 命令行发出,或者从 GUI 项目设置中指定的顶层发出。void foo_long_name () { #pragma HLS top name=DESIGN_TOP ... } set_top DESIGN_TOP函数内联pragma HLS inline说明删除作为层次结构中单独实体的函数。 内联后,函数被分解到调用函数中,不再作为 RTL 中单独的层次结构出现。 在某些情况下,内联函数允许函数内的操作与周围的操作更有效地共享和优化。 内联函数不能共享。 这会增加实现 RTL 所需的面积。INLINE 编译指示适用于它定义的范围,具体取决于它的指定方式:INLINE:没有参数,pragma 意味着指定它的函数应该向上内联到任何调用函数或区域。INLINE OFF:指定在其中指定的函数不应向上内联到任何调用函数或区域中。 这将禁用可能自动内联或作为区域或递归的一部分内联的特定函数的内联。INLINE REGION:这将编译指示应用于区域或分配它的函数体。它向下应用,内联区域或函数的内容,但不通过层次结构递归内联。INLINE RECURSIVE:这将编译指示应用于区域或分配它的函数体。它向下应用,递归地内联区域或函数的内容。默认情况下,内联仅在函数层次结构的下一级执行,而不是在子函数上执行。但是,递归选项允许您通过层次结构的级别指定内联。语法将编译指示放在函数体或代码区域内的 C 源代码中。#pragma HLS inline <region | recursive | off>其中,region:可选地指定指定区域(或包含在函数体中)的所有函数都将被内联,适用于该区域的范围。recursive:默认情况下,只执行一级函数内联,指定函数内的函数不内联。 recursive 选项以递归方式内联指定函数或区域内的所有函数。off:禁用函数内联以防止内联指定的函数。 例如,如果在函数中指定了递归,则此选项可以防止特定调用的函数在所有其他函数都被内联时被内联。Vivado HLS 自动内联小函数,使用带有 off 选项的 INLINE pragma 可用于防止这种自动内联。示例示例1本示例内联指定区域内的所有函数,在本例中为 foo_top 的主体,但不内联这些函数内的任何低级函数。void foo_top { a, b, c, d} { #pragma HLS inline region ...示例2以下示例将 foo_top 主体内的所有函数内联,通过函数层次结构向下递归内联,但函数 foo_sub 未内联。 递归编译指示放在函数 foo_top 中。 禁用内联的编译指示放在函数 foo_sub中:foo_sub (p, q) { #pragma HLS inline off int q1 = q + 10; foo(p1,q);// foo_3 ... } void foo_top { a, b, c, d} { #pragma HLS inline region recursive ... foo(a,b);//foo_1 foo(a,c);//foo_2 foo_sub(a,d); ... }INLINE 向下应用于函数 foo_top 的内容,但向上应用于调用 foo_sub 的代码。示例3本示例将 copy_output 函数内联到任何调用 copy_output 的函数或区域中。void copy_output(int *out, int out_lcl[OSize * OSize], int output) { #pragma HLS INLINE // Calculate each work_item's result update location int stride = output * OSize * OSize; // Work_item updates output filter/image in DDR writeOut: for(int itr = 0; itr < OSize * OSize; itr++) { #pragma HLS PIPELINE out[stride + itr] = out_lcl[itr]; }pragma HLS function_instantiate说明FUNCTION_INSTANTIATE指令是一种优化技术,它具有维护函数层次结构的区域优势,但提供了一个额外的强大选项:对函数的特定实例执行有针对性的局部优化。 这可以简化围绕函数调用的控制逻辑,并有可能提高延迟和吞吐量。默认情况下:函数在 RTL 中保留为单独的层次结构块。同一层次结构中的所有函数实例都使用单个 RTL 实现(块)。FUNCTION_INSTANTIATE 编译指示用于为函数的每个实例创建唯一的 RTL 实现,允许根据函数调用对每个实例进行本地优化。 该编译指示利用了当函数被调用时函数的某些输入可能是常量值的事实,并使用它来简化周围的控制结构并生成更小更优化的功能块。char foo_sub(char inval, char incr) { #pragma HLS function_instantiate variable=incr return inval + incr; } void foo(char inval1, char inval2, char inval3, char *outval1, char *outval2, char * outval3) { *outval1 = foo_sub(inval1, 1); *outval2 = foo_sub(inval2, 2); *outval3 = foo_sub(inval3, 3); }如果没有 FUNCTION_INSTANTIATE pragma,代码将导致 foo 中函数的所有三个实例的函数 foo_sub 的单个 RTL 实现。 函数 foo_sub 的每个实例都以相同的方式实现。 这对于函数重用和减少函数的每个实例调用所需的区域来说很好,但意味着函数内部的控制逻辑必须更加复杂,以解决每次调用 foo_sub 的变化。在上面的代码示例中,FUNCTION_INSTANTIATE 编译指示导致函数 foo_sub 的三种不同实现,每种实现都针对 incr 参数独立优化,减少了面积并提高了函数的性能。 在 FUNCTION_INSTANTIATE 优化之后,foo_sub 被有效地转换为三个独立的函数,每个函数都针对 incr 的指定值进行了优化。语法将编译指示放在 C 源代码中所需位置的边界内。#pragma HLS function_instantiate variable=<variable>其中,variable=定义了用作常量的函数参数的必需参数。示例示例1在以下示例中,放置在函数 swInt) 中的 FUNCTION_INSTANTIATE 编译指示允许函数 swInt 的每个实例相对于 maxv 函数参数进行独立优化:void swInt(unsigned int *readRefPacked, short *maxr, short *maxc, short *maxv){ #pragma HLS function_instantiate variable=maxv uint2_t d2bit[MAXCOL]; uint2_t q2bit[MAXROW]; #pragma HLS array partition variable=d2bit,q2bit cyclic factor=FACTOR intTo2bit<MAXCOL/16>((readRefPacked + MAXROW/16), d2bit); intTo2bit<MAXROW/16>(readRefPacked, q2bit); sw(d2bit, q2bit, maxr, maxc, maxv); }接口综合pragma HLS interface描述在基于C 的设计中,所有输入和输出操作都是通过形式函数参数在零时间内执行的。 在 RTL 设计中,这些相同的输入和输出操作必须通过设计接口中的端口执行,并且通常使用特定的 I/O(输入输出)协议进行操作。INTERFACE pragma 指定如何在接口综合期间从函数定义创建 RTL 端口。RTL 实现中的端口源自:任何指定的功能级协议。函数参数。由顶级函数访问并在其作用域外定义的全局变量。函数级协议,也称为块级 I/O 协议,提供信号来控制函数何时开始操作,并指示函数操作何时结束、空闲和准备好接受新输入。 函数级协议的实现:由 值 ap_ctrl_none、ap_ctrl_hs 或 ap_ctrl_chain 指定。 ap_ctrl_hs 块级 I/O 协议是默认的。与函数名称相关联。每个函数参数都可以指定为具有自己的端口级 (I/O) 接口协议,例如有效握手 (ap_vld) 或确认握手 (ap_ack)。 为顶级函数中的每个参数创建端口级接口协议,如果函数返回一个值,则函数返回。 创建的默认 I/O 协议取决于 C 参数的类型。 在使用块级协议启动块操作后,端口级 IO 协议用于对进出块的数据进行排序。如果访问了全局变量,但所有读写操作都是设计本地的,则在设计中创建资源。RTL 中不需要 I/O 端口。 如果预期全局变量是外部源或目标,请以与标准函数参数类似的方式指定其接口。当 INTERFACE pragma 用于子函数时,只能使用 register 选项。子函数不支持 选项。Vivado HLS 会自动确定任何子函数使用的 I/O 协议。 您无法控制这些端口,除非指定端口是否已注册。语法将编译指示放在函数的边界内。#pragma HLS interface <mode> port=<name> bundle=<string> \ register register_mode=<mode> depth=<int> offset=<string> \ clock=<string> name=<string> \ num_read_outstanding=<int> num_write_outstanding=<int> \ max_read_burst_length=<int> max_write_burst_length=<int>其中,:指定函数参数、函数使用的全局变量或块级控制协议的接口协议模式。 模式可以指定为以下之一:port=:指定 INTERFACE pragma 应用的函数参数、函数返回或全局变量的名称。块级 I/O 协议(ap_ctrl_none、ap_ctrl_hs 或 ap_ctrl_chain)可以分配给函数返回值的端口。bundle=:将函数参数分组到 AXI 接口端口中。 默认情况下,Vivado HLS 将所有指定为 AXI4-Lite (s_axilite) 接口的函数参数分组到单个 AXI4-Lite 端口中。 类似地,指定为 AXI4 (m_axi) 接口的所有函数参数都分组到单个 AXI4 端口中。 此选项将具有相同 bundle= 的所有接口端口显式分组到相同的 AXI 接口端口中,并将 RTL 端口命名为 指定的值。register:注册信号和任何相关协议信号的可选关键字,并使信号至少持续到函数执行的最后一个周期。 此选项适用于以下接口模式:○ ap_none ○ ap_ack ○ ap_vld ○ ap_ovld○ ap_hs ○ ap_stable ○ axis ○ s_axiliteregister_mode= :与 register 关键字一起使用,此选项指定寄存器是否放置在正向路径(TDATA 和 TVALID)、反向路径 (TREADY)、两条路径(TDATA、TVALID、 和 TREADY),或者如果没有任何端口信号要寄存(关闭)。 默认 register_mode 是 both。 AXI-Stream(axis)侧通道信号被认为是数据信号,只要寄存TDATA 就会寄存。depth=:指定测试台要处理的最大样本数。 此设置指示 Vivado HLS 为 RTL 协同仿真创建的验证适配器中所需的 FIFO 的最大大小。虽然深度通常是一个选项,但只有m_axi 接口需要配置。offset=:控制 AXI4-Lite (s_axilite) 和 AXI4 (m_axi) 接口中的地址偏移。○ 对于 s_axilite 接口, 指定寄存器映射中的地址。○ 对于 m_axi 接口, 指定以下值之一:direct:生成标量输入偏移端口。slave:生成偏移端口并自动将其映射到 AXI4-Lite 从接口。off:不生成偏移端口。config_interface 命令的 -m_axi_offset 选项全局控制设计中所有 M_AXI 接口的偏移端口。clock=:可选择仅为接口模式 s_axilite 指定。 这定义了用于接口的时钟信号。 默认情况下,AXI-Lite 接口时钟与系统时钟相同。 此选项用于为 AXI-Lite (s_axilite) 接口指定单独的时钟。如果捆绑选项用于将多个顶级函数参数分组到单个 AXI-Lite 接口中,则只需在捆绑成员之一上指定时钟选项。num_read_outstanding=:对于 AXI4 (m_axi) 接口,此选项指定在设计停止之前可以向 AXI4 总线发出多少个读取请求,而没有响应。这意味着设计中的内部存储,大小为 FIFO:num_read_outstanding*max_read_burst_length*word_size。num_write_outstanding=:对于 AXI4 (m_axi) 接口,此选项指定在设计停止之前可以向 AXI4 总线发出多少次写入请求而没有响应。 这意味着设计中的内部存储,大小为 FIFO:num_write_outstanding*max_write_burst_length*word_sizemax_read_burst_length=:对于 AXI4 (m_axi) 接口,此选项指定在突发传输期间读取的最大数据值数。max_write_burst_length=:对于 AXI4 (m_axi) 接口,此选项指定在突发传输期间写入的最大数据值数。name=:此选项用于根据您自己的规范重命名端口。生成的 RTL 端口将使用此名称。示例示例1在此示例中,两个函数参数均使用 AXI4-Stream 接口实现:void example(int A[50], int B[50]) { //Set the HLS native interface types #pragma HLS INTERFACE axis port=A #pragma HLS INTERFACE axis port=B int i; for(i = 0; i < 50; i++){ B[i] = A[i] + 5; } }示例2下面关闭块级 I/O 协议,并赋给函数返回值:#pragma HLS interface ap_ctrl_none port=return函数参数 InData 被指定为使用 ap_vld 接口,并且还指示应该寄存器输入:#pragma HLS interface ap_vld register port=InData这将全局变量 lookup_table 设置为 RTL 设计上的端口,具有 ap_memory 接口:pragma HLS interface ap_memory port=lookup_table示例3本示例定义了顶级转置功能端口的 INTERFACE 标准。 请注意使用 bundle= 选项对信号进行分组。// TOP LEVEL - TRANSPOSE void transpose(int* input, int* output) { #pragma HLS INTERFACE m_axi port=input offset=slave bundle=gmem0 #pragma HLS INTERFACE m_axi port=output offset=slave bundle=gmem1 #pragma HLS INTERFACE s_axilite port=input bundle=control #pragma HLS INTERFACE s_axilite port=output bundle=control #pragma HLS INTERFACE s_axilite port=return bundle=control #pragma HLS dataflowpragma HLS protocol说明protocol pragma将代码的一个区域指定为协议区域,除非代码中明确指定,否则Vivado HLS不会在其中插入时钟操作。协议区域可用于手动指定接口协议,以确保最终设计可连接到具有相同I/O协议的其他硬件块。Vivado HLS不会在操作之间插入任何时钟,包括从函数参数读取或写入的操作,除非在代码中明确指定。因此,RTL遵循读写顺序。可以指定时钟操作:在C语言中,使用ap_wait()语句(包括ap_utils.h)。在C++和SystemC中使用WaIT()语句(包括System.h)进行设计。ap_wait和wait语句对C和C++设计的仿真没有影响。它们仅由Vivado HLS进行解释。要创建C代码的区域,请执行以下操作:将区域用大括号括起来,{},可以选择将其命名以提供标识符。例如,下面定义了一个名为io_section的区域:io_section:{ ... }语法将pragma放置在区域边界内,以定义该区域的协议。#pragma HLS protocol <floating | fixed>其中:floating:协议模式,允许协议区域外的语句与最终RTL中协议区域内的语句重叠。协议区域内的代码保持周期精确,但其他操作可能同时发生。这是默认的协议模式。fixed:协议模式,确保协议区域内外的语句没有重叠。示例本示例将区域 io_section 定义为固定协议区域。 将编译指示放在里面的区域:io_section:{ #pragma HLS protocol fixed ... }任务级流水pragma HLS dataflow说明DATAFLOW pragma 支持任务级流水线,允许函数和循环在其操作中重叠,增加 RTL实现的并发性,并增加设计的整体吞吐量。所有操作都在 C 描述中按顺序执行。 在没有任何限制资源的指令(例如 pragma HLS 分配)的情况下,Vivado HLS 寻求最小化延迟并提高并发性。 但是,数据依赖性可以限制这一点。 例如,访问数组的函数或循环必须在完成之前完成对数组的所有读/写访问。 这可以防止下一个消耗数据的函数或循环开始操作。 DATAFLOW 优化使函数或循环中的操作能够在前一个函数或循环完成其所有操作之前开始操作。当指定 DATAFLOW pragma 时,Vivado HLS 分析顺序函数或循环之间的数据流并创建通道(基于乒乓 RAM 或 FIFO),允许消费者函数或循环在生产者函数或循环完成之前开始操作。这允许函数或循环并行运行,从而减少延迟并提高 RTL 的吞吐量。如果未指定启动间隔(一个函数或循环开始与下一个循环之间的循环数),Vivado HLS 会尝试最小化启动间隔并在数据可用时立即开始操作。config_dataflow 命令指定用于数据流优化的默认内存通道和 FIFO 深度。为了使 DATAFLOW 优化起作用,数据必须从一个任务流向下一个任务。 以下编码风格会阻止 Vivado HLS 执行 DATAFLOW 优化:单一生产者-消费者违规。绕过任务。任务之间的反馈。有条件地执行任务。具有多个退出条件的循环。如果存在这些编码风格中的任何一种,Vivado HLS 会发出一条消息并且不执行 DATAFLOW 优化。最后,DATAFLOW 优化没有分层实现。 如果子函数或循环包含可能受益于 DATAFLOW 优化的其他任务,则必须将优化应用于循环、子函数或内联子函数。语法将编译指示放在区域、函数或循环的边界内的 C 源代码中。#pragma HLS dataflow示例在循环 wr_loop_j 中指定 DATAFLOW 优化。wr_loop_j: for (int j = 0; j < TILE_PER_ROW; ++j) { #pragma HLS DATAFLOW wr_buf_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_buf_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) { #pragma HLS PIPELINE // should burst TILE_WIDTH in WORD beat outFifo >> tile[m][n]; } } wr_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) { #pragma HLS PIPELINE outx[TILE_HEIGHT*TILE_PER_ROW*TILE_WIDTH*i +TILE_PER_ROW*TILE_WIDTH*m+TILE_WIDTH*j+n] = tile[m][n]; } } }pragma HLS stream说明默认情况下,数组变量实现为RAM。顶层函数数组参数实现为RAM接口端口。通用阵列作为RAM实现读写访问。在涉及 DATAFLOW 优化的子函数中,实现了数组参数使用 RAM 乒乓缓冲通道。基于循环的 DATAFLOW 优化中,涉及的数组作为 RAM 实现乒乓缓冲通道。如果存储在阵列中的数据以顺序方式使用或生成,则更有效的通信机制是使用 STREAM pragma 指定的流数据,其中使用 FIFO 而不是 RAM。当顶层函数的参数指定为 INTERFACE 类型 ap_fifo 时,该数组将自动实现为流。语法将编译指示放在 C 源代码中所需位置的边界内。#pragma HLS stream variable=<variable> depth=<int> dim=<int> off其中:variable=:指定要实现为流接口的数组的名称。depth=:仅与DATAFLOW 通道中的数据流相关。 默认情况下,RTL 中实现的 FIFO 深度与 C 代码中指定的数组大小相同。 此选项可让您修改 FIFO 的大小并指定不同的深度。当数组在 DATAFLOW 区域中实现时,通常使用 depth= 选项来减小 FIFO 的大小。 例如,在 DATAFLOW 区域中,当所有循环和函数都以 II=1 的速率处理数据时,不需要大的 FIFO,因为每个时钟周期都会产生和消耗数据。 在这种情况下,可以使用 depth= 选项将 FIFO 大小减少到 1,以显着减少 RTL 设计的面积。config_dataflow -depth 命令提供了流式传输 DATAFLOW 区域中的所有数组的能力。 此处指定的 depth= 选项会覆盖已分配变量的 config_dataflow 命令。dim=:指定要流式传输的数组的维度。 默认值为维度 1。对于具有 N 维的数组,指定为从 0 到 N 的整数。off:禁用流数据。 仅与数据流通道中的数组流相关。config_dataflow -default_channel fifo 命令全局表示设计中所有阵列上的 STREAM 编译指示。 此处指定的 off 选项会覆盖分配变量的 config_dataflow 命令,并恢复使用基于 RAM 乒乓缓冲区的通道的默认值。示例示例1以下示例指定要流式传输的数组 A[10],并作为 FIFO 实现:#pragma HLS STREAM variable=A示例2在此示例中,数组 B 设置为 FIFO 深度为 12 的流数据:#pragma HLS STREAM variable=B depth=12示例3阵列 C 已禁用流式传输。在本例中假定由 config_dataflow 启用:#pragma HLS STREAM variable=C off
写在前面本文记录了HLS的所使用的大部分指令,参考UG1270,对每个指令进行讲解,并有相关示例。内核优化pragma HLS allocation说明指定实例限制以限制已实现内核中的资源分配。这定义并可以限制用于实现特定功能、循环、操作或内核的 RTL 实例和硬件资源的数量。 ALLOCATION pragma 在函数体、循环或代码区域内指定。例如,如果 C 源代码有四个函数 foo_sub 的实例,则 ALLOCATION pragma 可以确保最终 RTL 中只有一个 foo_sub 实例。 C 函数的所有四个实例都使用相同的 RTL 块实现。 这会减少功能使用的资源,但会对性能产生负面影响。C 代码中的操作,例如加法、乘法、数组读取和写入,可以受 ALLOCATION 编译指示的限制。 在综合期间映射到的内核可以以与运算符相同的方式进行限制。 您可以选择限制组合乘法器内核的数量,而不是限制乘法运算的总数,从而强制使用流水线乘法器执行任何剩余的乘法(反之亦然)。ALLOCATION pragma 适用于它在其中指定的范围:函数、循环或代码区域。 但是,您可以使用 config_bind 命令的 -min_op 参数在整个设计中全局最小化运算符。语法将 pragma 放在将应用的函数、循环或区域的主体内。#pragma HLS allocation instances=<list> limit=<value> <type>其中:instances=:指定函数、运算符或内核的名称。limit=:可选地指定要在内核中使用的实例的限制。:指定分配应用于用于创建设计(例如加法器、乘法器、流水线乘法器和块 RAM)的功能、操作或内核(硬件组件)。 类型指定为以下之一:示例示例1给定具有多个函数 foo 实例的设计,此示例将硬件内核的 RTL 中的 foo 实例数限制为 2。#pragma HLS allocation instances=foo limit=2 function示例2将函数 my_func 的实现中使用的乘法器操作数限制为 1。此限制不适用于 my_func 之外的任何乘法器,或可能驻留在 my_func 子函数中的乘法器。void my_func(data_t angle) { #pragma HLS allocation instances=mul limit=1 operation ... }pragma HLS clock说明将命名时钟应用于指定函数。C 和 C++ 设计仅支持单个时钟。 create_clock 指定的时钟周期应用于设计中的所有功能。SystemC 设计支持多个时钟。 可以使用 create_clock 命令指定多个命名时钟,并使用 pragma HLS 时钟应用于各个 SC_MODULE。每个 SC_MODULE 都使用单个时钟进行综合。语法将编译指示放在函数体内的 C 源代码中。#pragma HLS clock domain=<clock> 其中,domain=,指定时钟名称。示例示例1假设一个 SystemC 设计,其中顶层 foo_top 具有时钟端口 fast_clock 和 slow_clock。 但是, foo_top 在其函数中仅使用 fast_clock。 子块 foo_sub 仅使用 slow_clock。在此示例中,在启动 Vivado HLS 工具时指定的 script.tcl 文件中指定了以下 create_clock 命令:create_clock -period 15 fast_clk create_clock -period 60 slow_clk然后在 C 源文件中指定以下编译指示以将时钟分配给指定的函数 foo_sub 和 foo_top:foo_sub (p, q) { #pragma HLS clock domain=slow_clock ... } void foo_top { a, b, c, d} { #pragma HLS clock domain=fast_clock ...pragma HLS expression_balance说明有时,基于 C 的规范是用一系列操作编写的,从而导致 RTL 中的一长串操作。 如果时钟周期较短,这会增加设计中的延迟。默认情况下,Vivado HLS 使用关联和交换属性重新排列操作。这种重新排列创建了一个平衡树,可以缩短链,潜在地以额外硬件为代价减少设计中的延迟。EXPRESSION_BALANCE 编译指示允许在指定范围内禁用或明确启用此表达式平衡。语法将编译指示放在 C 源代码中所需位置的边界内。#pragma HLS expression_balance off其中,off:在此位置关闭表达式平衡。将此选项排除在编译指示之外会启用表达式平衡,这是默认模式。示例示例1此示例在函数 my_Func 中显式启用表达式平衡:void my_func(char inval, char incr) { #pragma HLS expression_balance示例2在函数 my_Func 中禁用表达式平衡:void my_func(char inval, char incr) { #pragma HLS expression_balance offpragma HLS latency说明为函数、循环和区域的完成指定最小或最大延迟值或两者。 延迟定义为产生输出所需的时钟周期数。函数延迟是函数计算所有输出值并返回所需的时钟周期数。 循环延迟是执行循环的所有迭代的周期数。Vivado HLS 始终尝试将设计中的延迟降至最低。 当指定 LATENCY pragma 时,工具行为如下:延迟大于最小值或小于最大值:满足约束。 没有执行进一步的优化。延迟小于最小值:如果 Vivado HLS 可以实现小于最小指定延迟,它会将延迟扩展到指定值,从而可能增加共享。延迟大于最大值:如果 Vivado HLS 无法在最大限制内进行调度,则会增加实现指定约束的工作量。 如果它仍然无法满足最大延迟,它会发出警告,并生成超过最大延迟的最小可实现延迟的设计。语法将pragma放在必须管理延迟的函数、循环或代码区域的边界内。#pragma HLS latency min=<int> max=<int>其中:min=:可选地指定函数、循环或代码区域的最小延迟。max=:可选地指定函数、循环或代码区域的最大延迟。尽管最小值和最大值都是可选的,但必须指定一个。示例示例1函数foo的最小延迟为4,最大延迟为8:int foo(char x, char a, char b, char c) { #pragma HLS latency min=4 max=8 char y; y = x*a+b+c; return y }示例2以下示例中的指定循环_1的最大延迟为12。将pragma放置在循环体中,如图所示:void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS latency max=12 ... result = a + b; } }示例3以下示例通过指定零延迟创建代码区域并对需要在同一时钟周期内更改的信号进行分组:// create a region { } with a latency = 0 { #pragma HLS LATENCY max=0 min=0 *data = 0xFF; *data_vld = 1; }pragma HLS reset说明添加或删除特定状态变量(全局或静态)的重置。复位端口在FPGA中用于在任何时候应用复位信号时将连接到复位端口的寄存器和块RAM恢复为初始值。RTL重置端口的存在和行为由config_RTL配置文件控制。重置设置包括设置重置极性的能力,并指定重置是同步还是异步,但更重要的是,它通过重置选项控制在应用重置信号时重置哪些寄存器。通过 RESET pragma 可以更好地控制复位。 如果变量是静态或全局变量,则 RESET 编译指示用于显式添加重置,或者可以通过关闭编译指示将变量从重置中移除。 当设计中存在静态或全局数组时,这可能特别有用。语法将pragma放在变量生命周期边界内的C源代码中。#pragma HLS reset variable=<a> off其中:variable=:指定应用pragma的变量。off:表示未为指定变量生成重置。示例示例1此示例将reset添加到函数foo中的变量a,即使全局重置设置为none或control:void foo(int in[3], char a, char b, char c, int out[3]) { #pragma HLS reset variable=a示例2即使全局重置设置为state或all,也会从函数foo中的变量a中移除重置。void foo(int in[3],char a,char b,char c,int out[3]){ #pragma HLS reset variable=a offpragma HLS resource说明指定特定的库资源(核心)用于在RTL中实现变量(数组、算术运算或函数参数)。如果未指定资源pragma,Vivado HLS将确定要使用的资源。Vivado HLS使用硬件内核实现代码中的操作。当库中的多个核心可以实现该操作时,可以指定要与资源pragma一起使用的核心。要生成可用核心的列表,请使用list_core命令。list_core命令用于获取库中可用核心的详细信息。list_core只能在Vivado HLS Tcl命令界面中使用,必须使用set_part命令指定Xilinx设备。如果未选择设备,则list_core命令不起任何作用。例如,要指定库中用于实现数组的内存元素,请使用资源pragma。这使您可以控制阵列是作为单端口RAM还是双端口RAM实现。这种用法对于顶级函数接口上的数组非常重要,因为与数组关联的内存类型决定了RTL中所需的端口。可以使用latency=选项指定内核的延迟。对于接口上的块RAM,latency=选项允许您在接口上建模片外非标准SRAM,例如支持延迟为2或3的SRAM。对于内部操作,latency=选项允许使用更多流水线阶段来实现操作。这些额外的流水阶段可以帮助解决RTL合成过程中的时间问题。要使用latency=选项,操作必须具有可用的多级内核。Vivado HLS为所有基本算术运算(加、减、乘、除)、所有浮点运算和所有块RAM提供多级内核。为获得最佳结果,赛灵思建议您对 C 使用 -std=c99,对 C 和 C++ 使用 -fno-builtin。 要指定 C 编译选项,例如 -std=c99,请使用带有 -cflags 选项的 Tcl 命令 add_files。 或者,使用项目设置中的编辑 CFLAG 按钮 对话框。语法将pragma放在定义变量的函数体中的C源代码中。#pragma HLS resource variable=<variable> core=<core> latency=<int>其中,variable=:指定要将资源pragma分配到的数组、算术运算或函数参数的必需参数。core=:指定核心的必需参数,如技术库中所定义。latency=:指定核心的延迟。示例示例1在下面的示例中,指定了一个2级流水线乘法器来实现函数foo的变量c的乘法。由Vivado HLS决定变量d使用哪个内核。int foo (int a, int b) { int c, d; #pragma HLS RESOURCE variable=c latency=2 c = a*b; d = a*c; return d; }示例2在下面的示例中,变量coefs[128]是顶级函数foo_top的参数。此示例指定使用库中的核心RAM_1P实现系数:#pragma HLS resource variable=coeffs core=RAM_1P在RTL中创建的用于访问系数值的端口在RAM_1P核心中定义。
写在前面本文演示如何基于现有Vivado HLS项目创建Tcl命令文件并使用Tcl。创建TCL文件打开Vivado HLS命令提示符。2.在Windows上,在Xilinx Design Tools文件夹下找到 Vivado HLS Command Prompt。在Vivado HLS项目时,Tcl文件将自动保存在项目层次结构中。在HLS界面中,展开solution1中的Constraints文件夹,可以看到script.tcl和directives.tcl脚本。可以双击文件script.tcl查看它。script.tcl:此文件包含用于使用项目设置和运行合成期间指定的文件创建项目的tcl命令。directions.tcl:它包含应用于设计的任何优化。优化可以放在tcl脚本里,也可以放在源文件中。这里可以把led闪烁的tcl脚本和c语言文件复制到新的文件夹下,用于练习使用tcl进行创建工程。并移动到该文件夹下,方便后续操作。cp led_demo\solution1\script.tcl tcluse\run_hls.tcl #复制script.tcl脚本 cp led_demo\top.c tcluse\top.c#复制c语言文件 cd tcluse修改TCL脚本文件项目创建的脚本是依赖于工程的,所以这里要进行修改tcl脚本进行复位设置。将–reset 选项添加到open_project命令中。由于Tcl文件通常在同一个项目上重复运行,因此需要重写任何现有项目信息。在open_solution命令中添加–reset选项,以便在同一解决方案上重新运行Tcl文件时删除任何现有的解决方案信息。删除源命令。如果以前的项目有任何要重用的指令,则可以将该项目中的directives.tcl文件复制到本地路径,也可以将这些指令直接复制到此文件中。添加exit命令。保存文件 。在Vivado HLS命令提示符窗口中,键入vivado_hls –f run_hls.tcl综合完成后,即可得到和GUI操作一样的工程。
写在前面本文是本系列的第二篇,本文主要介绍FPGA常用运算模块-加减法器和乘法器,xilinx提供了相关的IP以便于用户进行开发使用。加减法器模块在xilinx中,有一个IP模块提供加减法运算的功能,概述加法器/减法器IP 提供 LUT 和单个 DSP slice 实现加减法实现。加法器/减法器模块可以创建加法器(A+B)、减法器(A–B) 和可动态配置的加法器/减法器,用于操作有符号或无符号数据。该功能可以在一个单个 DSP slice 或 LUT(但目前不是两者的混合)。该模块可以流水线化。支持fabric实现输入范围从1到256位宽,该IP核支持DSP片实现,输入高达58位。可选进位输入和输出、时钟启用和同步清除、旁路(负载)能力、设置B值为一个常量。IP核图示以及端口介绍如果Constant Input = TRUE and Bypass = FALSE,则B端口不存在。一个用户定义的核心内部常量被应用到B操作数的位置。输出位宽设计Q的值等于AB输入两者之间的最大值。流水线操作加法器/减法器模块可以选择流水线操作用来提高速度。流水线操作由延迟参数控制。将延迟配置设置为自动,以实现最优的流水线速度。将延迟配置设置为手动,以允许在延迟参数中输入有效数量的流水线层级数。DSP片对于DSP片的实现方式,单个的DSP片可以用0、1或2级寄存器进行流水线操作。Latency Configuration = Automatic ,此时优化速度延迟获得最优的流水操作速度;如果Latency = 1, 只有输出寄存器存在。Latency = 2,输出和输入寄存器都存在。Fabric 实现对于使用PFGA的逻辑资源的实现方式,流水线操作是通过将输入总线 分成许多总线片(等于流水线阶段的数量)来实现的。在第一阶段,对每个总线片做尽可能多的工作,将它们加在一起,并存储结果和每个结果的进位输出。在第二阶段,从最低有效位的部分得到的进位被输入到下一个较高有效位的结果中,它产生一个进位被输入到下一个阶段的下一个结果中,直到进位被传播到顶部。因为需要存储的数据较少,所以这比存储每个切片的输入直到生成该切片的进位的更直观的技术更有效。 此外,该设计更小且更易于布线。上电或复位后,流水线模块需要几个时钟周期才使输出变为有效,由延迟控制参数指定。如果在流水线模块上请求旁路,则旁路值会在延迟控制指定的时钟周期数之后出现在输出上。如果同时请求旁路和时钟使能,则必须设置旁路优先级,以便旁路不会覆盖时钟使能。 对于流水线模块,资源使用率大约是非流水线模块的延迟倍数。为了提高时钟速度,流水线导致面积使用量的显着增加。 如果需要延迟但面积比速度更重要,请在此模块的 S 输出中添加一个基于 SRL16 的移位寄存器,以优化面积使用。加法器IP配置加法器IP配置如图所示,在basic界面,可以对IP的实现方式,输入的数据类型,位宽,IP的方式(加法器、减法器、加减法器)、流水操作延迟方式和延时周期,常数输入进行配置。Constant Input and Constant Value :当常量输入为 TRUE 时,端口 B 设置为参数 Constant Value 指定的值。常数值必须是以二进制格式输入且不得超过 B 输入宽度。在大多数情况下指定端口 B 是一个常量时候,会自动创建一个没有端口 B 的模块。但是在当请求旁路功能时,因为需要端口 B 来提供旁路数据。默认是端口 B 提供的端口 B 值。会生成B端口。加减法器的控制配置界面如下,在控制界面可以配置加减法器进位、旁路、复位等控制操作。Carry In :设置为TRUE时,创建一个C_IN端口。这是用于加法器的高电平有效进位端口和用于减法器和加/减法器的可编程(高电平有效/低电平有效,带借入/输出检测)进位端口。Carry Out :当设置为TRUE时,创建一个端口C_OUT。实现了加法器和加减法器的高有效同步进位,以及可编程(设置高低有效,借入/借出检测)的减法器和加减法器中的减法器的借位标志。Bypass :设置为 TRUE 时,创建旁路引脚。激活 BYPASS 引脚设置输出为端口 B 上给定的值。此功能用于创建可加载的计数器和累加器。Bypass and Clock Enable (CE) Priority :该参数控制是否旁路输入由时钟使能限定。当设置为Bypass_Overrides_CE时,BYPASS 信号的激活也使能寄存器。当设置为CE_Overrides_Bypass,寄存器必须有 CE 激活才能加载 B 端口数据。Bypass Sense :控制旁路的敏感电平,是高有效还是低有效,因为高低电平有效在有些时候都能获得更好的效率。Borrow In/Out Sense :当设置为Active_Low时,用于减法的C_IN和C_OUT引脚是低有效的。这符合fabric实现规则,是一个最佳设置。Synchronous Set :指定是否包含 SSET 引脚。 在DSP实现模式下,SSET 引脚无效。Synchronous Init :指定是否包含一个SINIT引脚,当断言时,该引脚同步地将输出值设置为Init value定义的值。如果SINIT存在,那么SSET和SCLR都不存在。 在DSP实现模式下,SINIT引脚是无效的。Init Value :十六进制指定当断言SINIT时输出初始化为指定的值。如果Synchronous Init = false 则忽略。Power on Reset Init Value :指定(十六进制)S寄存器在上电复位时初始化的值。Synchronous Controls and Clock Enable (CE) Priority : 该参数控制SCLR(以及逻辑单元模式下的SSET和SINIT)输入是否由时钟使能限定。当设置为 Sync_Overrides_CE 时,同步控制覆盖 CE 信号。 当设置为 CE_Overrides_Sync 时,控制信号仅在 CE 为高时有效。 请注意,在结构原语上,SCLR 和 SSET 控制覆盖 CE,因此选择 CE_Overrides_Sync 通常会导致额外的逻辑。Sync Set and Clear (Reset) Priority :控制 SCLR 和 SSET 的相对优先级。当设置为 Reset_Overrides_Set 时,SCLR 会覆盖 SSET。默认值是Reset_Overrides_Set,因为这是原语的排列方式。使 SSET 优先需要额外的逻辑。乘法器乘法器IP实现高性能、优化的乘法器方案。可以使用资源和性能权衡选项来为特定的应用程序定制IP。该IP支持输入范围从1到64位,输出从1到128位。所有乘法器都可配置延迟。当使用DSP Slice时,支持对称四舍五入到无限。概述乘法器IP允许设计者精细地构建定点乘法器。可以使用 DSP Slices、Slice 逻辑或组合的方式进行构建乘法器IP,并且针对性能或资源进行了优化的结构。常数系数乘法器也可以使用许多不同的逻辑资源选项来实现。并且可以通过流水线操作层级数量以适应延迟和性能要求。DSP Slice的对称舍入特性可用于并行乘法器。IP核图示以及端口介绍乘法器IP配置乘法器IP的basic配置界面如下:在该界面可以进行配置乘法器的类型,乘数的数据类型,位宽,乘法器的实现方式以及优化方式。Multiplier Type :在并行和常数系数乘法器选项之间进行选择。并行乘法器选项:这些选项只有当选择的乘法器类型是并行乘法器时才可见。Multiplier Construction :选择用于IP实现的结构是LUT还是专用乘法器原语。Optimization Options :DSP48E1 Slice:可以为高达 47x47 的乘法器大小选择速度或面积优化。 速度优化 :充分利用乘法器原语来提供最高性能的实现。面积优化 :混合使用切片逻辑和专用乘法器原语来降低基于 DSP 切片的乘法器利用率,同时仍提供合理的性能。 对于 47x47 以上的尺寸,只允许优化速度。LUT-based multipliers : 区域优化降低了延迟和LUT利用率,可实现的时钟频率为代价。当两个输入操作数都是无符号且两个输入操作数都小于16位时,区域优化是最有效的。Constant-Coefficient Multiplier Options :这些选项只有当选择的乘数类型是常数系数乘法器时才可见。Coefficient :在显示的范围内输入系数的整数值。 支持正系数和负系数。 常量 (B) 端口的输入类型(有符号或无符号)由 Vivado IDE 根据输入的整数常量自动配置。 可以选择 A 端口是有符号的还是无符号的。Memory Options : 选择乘法器是使用分布式内存、块内存还是使用 DSP Slices 来实现。乘法器IP的输出控制配置界面如下:Output Product Range: 根据输入操作数的宽度自动配置输出产品宽度。Use Custom Output Width :如果通过设置 MSB 和 LSB 范围,对输出进行切片。Use Symmetric Rounding :对于基于 DSP Slice 的并行乘法器,如果需要,可以将乘积对称舍入到无穷大。这与MATLAB的 round 函数的行为相同。Pipelining and Control Signals:Pipeline Stages :为乘数实例选择流水线操作的层级。右边的标签提供了关于实现最佳性能的流水操作的最佳数量的反馈。Pipeline Stages = 0 意味着IP是组合的。Pipeline Stages = 1 意味着IP的输出是寄存器型的。Pipeline Stages > 1 使寄存器插入到输入和输出之间,直到最优的流水线操作的层级。添加更多寄存器可以提高可实现的时钟速度,同时增加延迟。流水线操作的层级设置的值大于最优值的值,将导致在输出时添加基于SRL16的移位寄存器,以实现额外的延迟。Clock Enable :选择设计中的所有寄存器是否都具有时钟使能控制。Synchronous Clear :选择是否该设计中的所有寄存器都具有同步复位控制。SCLR/CE Priority :当SCLR和CE引脚同时存在时,可以选择scr和CE的优先级。选择 SCLR overrides CE ,使用的资源最少,实现的性能最好。ReferencesPG192PG108
写在前面承接前文,本文是射频数据转换器IP使用介绍的第二篇,参考PG269的第五章,完成对射频数据转换器(RF Data Converter)IP配置界面的相关参数进行详细的说明,以便于后续开发设计进行查阅。IP基础配置Presets (预配置)Vivado IDE IP核心配置界面提供了一种保存和应用预设配置的方法。从预设菜单,几个固定配置是可用的。4×4: ADC:R2C DAC:C2R : 4个RF-ADC和4个RF-DAC启用。RF-ADC采用real to I/Q模式配置。RF-DAC配置为I/Q to real模式。8×8: ADC:R2C DAC:C2R :8个RF-ADC和8个RF-DAC启用。RF-ADC采用real to I/Q模式配置。RF-DAC配置为I/Q to real模式。ADC:R2C Multi x 2 DAC: C2R Multi x2 :RF-ADC配置在 x2多波段使用实转虚的数据。RF-DAC配置在 x2多波段使用虚转实的数据。ADC:C2C Multi x 2 DAC: C2C Multi x2 :rf - adc配置在x2多频带使用虚到虚的数据。RF-DAC配置在 x2多频带使用虚到虚的数据。DAC: C2R Multi 2x2 :在单个RF-DAC 块上的 2x2多频段使用虚到实的数据。DAC: C2C Multi 2x2 :在单个RF-DAC 块上的 2x2多频段使用虚到虚的数据。DAC: C2C Multi x4 : 在单个 RF-DAC 块上的 1x4 多频段使用虚到实的数据。除了这些固定的预置外,还可以通过选择“保存”来保存当前配置,配置…从预设菜单。保存配置后,可以通过选择Apply configuration …随时重新加载配置。System Configuration (系统配置)预设配置:此菜单允许用户从预定义配置列表中选择,以避免手动输入所有设置。Converter Setup(变频器的设置)转换器设置菜单为设置IP配置提供了高级(Advanced )和简单(Simple)的选项。Advanced : 所有片都是独立配置的。Simple: 用户可以看到单个RF-ADC和RF-DAC块的配置选项。设置好片配置信息后后,通过从给定的片列表中选择片,就可以将RF-ADC配置应用到设备中的任何RF-ADC。类似地,RF-DAC配置可以应用于任何RF-DAC片。可以在不丢失配置信息的情况下从简单设置更改为高级设置。但是,从高级到简单是不可能的。只支持向下兼容。RF-ADC片配置这些参数对片中的每个RF-ADC都是通用的。Link Coupling (链路耦合): 这决定了RF-ADC的输入信号是交流耦合还是直流耦合。典型的应用程序 使用 交流耦合 ,但对于某些应用程序(如Zero IF),也支持直流耦合。当选择直流链路耦合模式时,外部驱动电路的共模电压需要使用外部驱动的VCM引脚(参见RF-ADC模拟输入)与RF-ADC内部的共模电压对齐。 VCM引脚 只能用于 直流耦合模式 ,如果选择交流耦合模式,则可以保持浮动。Converter Band Mode(转换器频带模式): 每个RF-ADC可以独立工作(单频带模式),也可以与其他转换器一起工作(多频带模式)。以下模式可用于RF-ADC。Single: 转换器工作在单频带模式。Multi x2 (pair 01): ADC 0 的输出通过 ADC 0 和 1 的 DDC 通道路由。数字设置,例如混频器模式和类型,可用于每个 DDC 通道。 在 Quad 器件中,tile 中的其他两个 RF-ADC 在单频带模式下运行。Multi x2 (pair 23): 这个选项类似于前面的模式。在这里,ADC 2的输出被路由到DDC通道2和3。瓦片中的其他转换器以单频带模式工作。此选项仅在Quad设备上可用。Multi x2 (both): 来自 ADC 0 的数据为 DDC 通道 0 和 1 提供输入。来自 ADC 2 的数据为 DDC 通道 2 和 3 供电。此选项仅适用于四通道器件。Multi x4: 片中的所有DDC通道都来自ADC 0。此选项仅在Quad设备上可用。Multi Tile Sync(多片同步): 启用后,块包含在多片同步组中。 RFADC Tile 0 必须启用并与被配置为启用此选项的 tile 的转换器 0 一起出现在组中。RF-ADC转换器配置commonEnable ADC(启用ADC): 选择是否启用所选片内的所选转换器。取值为TRUE和FALSE。Invert Q Output (Q逆变输出): 只有在选择I/Q输出数据且开启微调混频器时才能设置。当设置时,混合器的求积输出为负。这允许生成-Q数据。Dither (抖动): 选择所选片是否启用了抖动。抖动应启用,除非采样率低于0.75倍的RF-ADC的最大采样率。Bypass Background Calibration (Gen 1 and Gen 2) 旁路背景校准(第 1 代和第 2 代): 如果选中,则在 IP 逻辑中而不是在 RF-ADC 中实现背景校准逻辑。 该驱动程序可用于将一组固定的校准系数下载到 IP。 此选项仅在 Real input to Real output 模式下可用,并且对抽取模式和每个 AXI4-Stream 周期的样本数有进一步的限制。Enable TDD Real Time Ports (Gen 3)启用 TDD 实时端口(第 3 代): 启用后,tdd_mode 端口将添加到 IP。 这可以通过关闭 RF-ADC 的部分来实现节能。Data SettingsDigital Output Data (数字输出数据): :设置所选片内所选转换器的数据类型。该参数仅在启用转换器时可配置。有效值为Real和I/Q。当转换器0被设置为I/Q时,转换器1也必须被启用;当转换器2被设置为I/Q时,转换器3也必须被启用;否则配置无效。Decimation Mode(抽取模式): 设置所选片内所选转换器的抽取值。 该参数仅在转换器启用时可配置。 从下拉菜单中选择值 1x、2x、3x、4x、5x、6x、8x、10x、12x、16x、20x、24x、40x。 当转换器未启用时,该值为关闭。第 1 代/第 2 代值为 1x、2x、4x 和 8x。Samples per AXI4-Stream Word(每AXI4-Stream的采样字数): 设置每个周期的字数。 当特定转换器已启用时,此参数是可配置的。 有效值介于 1 和 12 之间,可以使用下拉列表进行选择。 取值范围取决于所选的采样率,以将 AXI4-Stream 时钟保持在规范范围内。 所需的 AXI4-Stream 时钟是 IP 核的输入,并显示基于所选总线宽度的值。Observation Channel (Gen 3)(观测通道(第三代)): 启用并设置观测通道。启用ADC观测通道端口:启用给定RF-ADC的tdd_obs端口。还为观测通道添加了一个AXI4-Stream接口。抽取模式:设置所选转换器在所选贴图内的观测通道的抽取值。该参数仅在开启观测通道时可配置Samples per AXI4-Stream Word(每AXI4-Stream的采样字数): 设置每个周期的字数。 当特定转换器的观察通道已启用时,此参数是可配置的。 取值范围取决于所选的采样率,以将 AXI4-Stream 时钟保持在规范范围内。 所需的 AXI4-Stream 时钟是 IP 核的输入,其值基于选定的总线宽度显示。Mixer SettingsMixer Type(混频器类型): 设置要使用的混音器类型。有效的选项有bypassed, coarse, and fine。可选择的选项取决于转换器数字输出数据字段的选择。Mixer Mode(混频器模式): 设置所选转换器的混合器模式。该参数仅在启用转换器时可配置。混频器模式的选择取决于混频器类型和所选数字输出数据的格式。当真实数据输出时,混频器被绕过。当I/Q数据输出混频器可以设置为 real 的I/Q或I/Q到I/Q。Coarse Mixer Frequency (粗混频器频率): 设置粗混频器的频率。 混频器类型选择为粗调时,此参数可配置。有效选项为 Fs/2、Fs/4 和 -Fs/4。Fine Mixer Frequency (细混频器频率): 设置细混频器的频率。此参数仅在选择fine作为混合器类型时有效。有效频率范围为- 10GHz ~ 10GHz。Fine Mixer Phase(细混频器相位) :设置细混频器的相位。此参数仅在选择fine作为混合器类型时有效。有效范围是-180到180。Analog SettingsNyquist Zone(奈奎斯特区): 选择偶数和奇数奈奎斯特区操作。Calibration Mode(校准模式): 根据输入信号的特性选择不同的校准优化方案。自动模式(第3代)适用于所有输入频率。模式1是最佳输入频率Fsamp/2(Nyquist)±10%。模式2是输入频率输出的最佳范围。RF-DAC片配置这些参数对一个tile中的每个RF-DAC都是通用的。Converter Band Mode(转换器频带模式): 每个 RF-DAC 可以独立运行(单频段模式)或与片中的其他转换器一起使用(多频段模式)。 以下模式是可用于 RF-DAC。Single: 转换器工作在单频带模式。Multi x2 (pair 01): DUC通道0和1驱动DAC0的输入。每个DUC通道都有数字设置,如混频器模式和类型。片中的其他两个rf - dac工作在单频模式。Multi x2 (pair 23): 这个选项类似于前面的模式。DAC2是由DUC通道2和3驱动的。片中的其他转换器以单频带模式工作。Multi x2 (both): 之前的多频段选项的组合。 DAC0 转换来自 DUC 通道 0 和 1 的数据。 DAC2 由 DUC 通道 2 和 3 驱动。Multi x4: DAC0接受来自tile中的所有DUC通道的数据。Multi Tile Sync(多片同步): 启用后,块包含在多片同步组中。 RFDACTile 0 必须启用并与被配置为启用此选项的 tile 的转换器 0 一起出现在组中。Variable Output Current (Gen 3)(可变输出电流(第3代)): 当可变输出电流模式启用时,输出电流按照DC和AC Switching中的规定设置。DAC_AVTT必须设置为3.0V。Link Coupling (链路耦合): 这决定了 RF-DAC 的输出信号是交流耦合还是直流耦合。 在直流耦合模式下,共模电压是固定的。 在此模式下,VOP 范围会减小。
pbuf_dechain该函数将第一个pbuf与链中剩余的pbuf分离开。该函数不能在包队列上调用。struct pbuf * pbuf_dechain(struct pbuf *p)pbuf_copy该函数创建pbuf的一个PBUF_RAM类型的副本。该函数只会复制一个包,不会复制整个包队列。err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)pbuf_copy_partial将数据包缓冲区的(部分)内容复制到应用程序提供的缓冲区。u16_t pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)pbuf_skip在 pbuf 的开头跳过一些字节。struct pbuf* pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)pbuf_take将应用程序提供的数据复制到 pbuf 中。该函数只能用于复制相当于buf->tot_len的数据。err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)pbuf_take_at与 pbuf_take() 相同,但将数据放在偏移量处err_t pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)pbuf_coalesce该函数将一个pbuf队列合并为一个单独的pbuf。struct pbuf* pbuf_coalesce(struct pbuf *p, pbuf_layer layer)pbuf_get_at从 pbuf 中的指定位置获取一个字节。u8_t pbuf_get_at(const struct pbuf* p, u16_t offset)pbuf_try_get_at从 pbuf 中的指定位置获取一个字节int pbuf_try_get_at(const struct pbuf* p, u16_t offset)pbuf_put_at将一个字节放到 pbuf 中的指定位置 。void pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data)pbuf_strstr该函数在pbuf p中查找substr。但和strstr()函数不同,不会在遇到第一个’\0’时停止。u16_t pbuf_strstr(struct pbuf* p, const char* substr)pbuf_memcmp将指定偏移量处的 pbuf 内容与内存 s2 进行比较,两者的长度均为 n。u16_t pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n)pbuf_memfind该函数在pbuf p中查找出现的长度为mem_len的mem,查找位置从start_offest开始。u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
TCP/IPTCP/IP 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信。浏览器与服务器都在使用TCP/IP 协议, E-Mail 使用 TCP/IP 协议,电子邮件也通过 TCP/IP 协议来发送和接收邮件。因特网地址是 TCP/IP 协议。因特网地址比如 “42.120.45.233” 就是一个 TCP/IP 协议。TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。在 TCP/IP 内部包含一系列用于处理数据通信的协议:TCP (传输控制协议) - 应用程序之间通信UDP (用户数据报协议) - 应用程序之间的简单通信IP (网际协议) - 计算机之间的通信ICMP (因特网消息控制协议) - 针对错误和状态DHCP (动态主机配置协议) - 针对动态寻址TCP 使用固定的连接TCP 用于应用程序之间的通信。当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方"握手"之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。UDP 和 TCP 很相似,但是更简单,同时可靠性低于 TCP。IPIP 是无连接的,IP 用于计算机之间的通信。IP 是无连接的通信协议。它不会占用两个正在通信的计算机之间的通信线路。这样,IP 就降低了对网络线路的需求。每条线可以同时满足许多不同的计算机之间的通信需要。通过 IP,消息(或者其他数据)被分割为小的独立的包,并通过因特网在计算机之间传送。IP 负责将每个包路由至它的目的地。IP 路由器:当一个 IP 包从一台计算机被发送,它会到达一个 IP 路由器。IP 路由器负责将这个包路由至它的目的地,直接地或者通过其他的路由器。在一个相同的通信中,一个包所经由的路径可能会和其他的包不同。而路由器负责根据通信量、网络中的错误或者其他参数来进行正确地寻址。TCP/IP :TCP/IP 意味着 TCP 和 IP 在一起协同工作。TCP 负责应用软件(比如您的浏览器)和网络软件之间的通信。IP 负责计算机之间的通信。TCP 负责将数据分割并装入 IP 包,然后在它们到达的时候重新组合它们。IP 负责将包发送至接受者。IP地址每个计算机必须有一个 IP 地址才能够连入因特网。每个 IP 包必须有一个地址才能够发送到另一台计算机。IP 地址包含 4 组数字:TCP/IP 使用 4 组数字来为计算机编址。每个计算机必须有一个唯一的 4 组数字的地址。每组数字必须在 0 到 255 之间,并由点号隔开,比如:192.168.1.60。32 比特 = 4 字节TCP/IP 使用 32 个比特来编址。一个计算机字节是 8 比特。所以 TCP/IP 使用了 4 个字节。一个计算机字节可以包含 256 个不同的值,现在,应该知道了为什么 TCP/IP 地址是介于 0 到 255 之间的 4 组数字。IP V6:IPv6 是 “Internet Protocol Version 6” 的缩写,也被称作下一代互联网协议,它是由 IETF 小组(Internet 工程任务组Internet Engineering Task Force)设计的用来替代现行的 IPv4(现行的)协议的一种新的 IP 协议。Internet 的主机都有一个唯一的 IP 地址,IP 地址用一个 32 位二进制的数表示一个主机号码,但 32 位地址资源有限,已经不能满足用户的需求了,因此 Internet 研究组织发布新的主机标识方法,即 IPv6。LWIP简介LWIP 是瑞典计算机科学院(SICS)的 Adam Dunkels 等开发的一个小型开源的 TCP/IP 协议栈,是 TCP/IP的一种实现方式。 LWIP 是轻量级 IP 协议,有无操作系统的支持都可以运行。 LWIP 实现的重点是在保持TCP 协议主要功能的基础上减少对 RAM 的占用,它只需十几 KB 的 RAM 和 40K 左右的 ROM 就可以运行 , 这 使 LWIP 协 议 栈 适 合 在 低 端 的 嵌 入 式 系 统 中 使 用 。LWIP 的主要特性如下:IGMP 协议,用于网络组管理,可以实现多播数据的接收Internet 协议( IP), 包括 IPv4 和 IPv6,支持 IP 分片与重装, 包括通过多个网络接口的数据包转发用于网络维护和调试的 Internet 控制消息协议( ICMP)用户数据报协议( UDP)传输控制协议( TCP)拥塞控制,往返时间( RTT)估计,快速恢复和重传DNS,域名解析SNMP,简单网络管理协议动态主机配置协议( DHCP)以太网地址解析协议( ARP)AUTOIP, IP 地址自动配置PPP,点对点协议,支持 PPPoE编程接口方式lwip(xilinx中的lib)提供二种用户编程接口方式: raw API 和 socket API。Raw API:(低层次的、基于“核”和“回调”的RAW API) 是为高性能和低内存开销而定制的。 这种类型的 API 把网络协议栈和应用程序放在一个进程里,连接网络协议和应用程序的纽带是回调函数, 回调函数实际上是一个普通的 C 函数。为了接收数据,应用程序会首先向协议栈注册一个回调函数,当关联的连接有一个信息到达时,该回调函数就被协议栈调用。这种实现方式即有优点也有缺点。优点是数据的接收和发送不会导致进程的切换,提供了最好的性能,执行速度快,而且消耗的内存资源少;缺点是应用程序无法进行连续运算,因为网络协议的处理和运算是在同一进程中完成的,二者无法并行发生。 Raw API 是资源较少的嵌入式系统的首选方法,也是在没有操作系统的情况下运行 lwIP 时唯一可用的 API。Socket API:(高层次的、基于“顺序”的API:socket API) 提供了一个基于 open-read-write-close 模块的 BSD socket-style 接口, 需要操作系统。 此接口在性能和内存要求方面不如 Raw API 高效, 不适用于小型嵌入式系统,但移植性更好。TCP和UDPlwIP支持的传输层协议包括:UDP:一种没有可靠性机制的无连接socket协议。TCP:一种面向连接的“流”协议。设计程序前,我们首先要选择使用UDP还是TCP。**UDP:**优点是开销更少,设计者自己选择消息大小;缺点是没有提供安全的通信路径,该协议不能通知用户对方是否收到了消息。**TCP:**优点是提供了一个安全的通信路径,当对方成功收到消息时用户会收到通知;缺点是开销更大,还会自动选择消息大小。选定协议后,设计者要决定应用程序如何通过网络传递数据:UDP:确保传递的数据块不会小于网络所允许的最大数据包,比如在标准以太网中,使用udp_send一次发送1472个字节,以最大化一个包中数据字节和报头字节的比,同时最小化网络中包间的间隔。**TCP:**虽然TCP可以将多个tcp_write调用的数据合并到一个包中,但由于这个包被分割到多个pbuf中,可能会降低性能。由于TCP需要将数据包存储起来重新传输,直到远程主机发出应答信号,所以在tcp_write/tcp_output返回后花费几秒的时间。如果要发送小块数据,应该关掉nagle算法,让堆栈立即发送数据,而不是等待更多数据形成更大的数据包后才发送数据。应该避免发送小块数据,总是等待应答会降低性能。TCP编程LWIP配置步骤使用lwIP的程序,无论TCP还是UDP,在进入while(1)循环前,都会有这样一个配置流程:设置开发板MAC地址开启中断系统设置本地IP地址初始化lwIP添加网络接口设置默认网络接口启动网络初始化TCP或UDP连接(自定义函数)TCP编程RAW API函数一览表初始化在使用任何TCP函数前,必须先调用**lwip_init()函数。此后必须每隔TCP_TMR_INTERVAL(通常取250ms)调用一次tcp_tmr()函数。某些版本的lwIP只需要将sys_check_timeouts()**函数添加到主循环中,它会处理栈中所有协议的定时器。Xilinx中还是需要通过配置处理器的定时器来调用tcp_tmr()。TCP连接步骤一个TCP连接由一个协议控制块(Protocol Control Block,PCB)做标识。有两种建立连接的方法。被动连接(监听)方法,相当于作为服务端(server):调用pcb_new创建一个pcb。(可选)调用tcp_arg将应用程序中特定的值于PCB关联在一起。调用tcp_bind函数指定本地IP地址和端口。调用tcp_listen或tcp_listen_with_backlog,这些函数将释放作为参数的PCB,并返回一个更小的监听PCB,如“tcp_new = tcp_listen(tpcb);”。调用tcp_accept指定新连接到来时要调用的函数。主动连接方法,相当于作为客户端(client):调用pcb_new创建一个pcb。(可选)调用tcp_arg将应用程序中特定的值于PCB关联在一起。(可选)调用tcp_bind函数指定本地IP地址和端口调用tcp_connect函数。发送TCP数据在TCP连接上发送数据的步骤如下:调用tcp_sent()函数设置回调函数;调用tcp_sendbuf()函数查找可以发送的最大数据量;调用tcp_write()函数把数据排队;调用tcp_output()函数强制发送数据。接收TCP数据TCP数据接收是基于回调的,当新数据到达时,将调用应用程序指定的回调函数。TCP协议设定了一个窗口(window),该窗口告诉发送主机它可以在连接上发送多少数据。所有连接的窗口大小都是lwipopts.h中设置的TCP_WND值。当应用程序处理了传入的数据后,必须调用tcp_recved()函数,以指示TCP可以增加接收窗口。lwIP 会话建立顺序图由于 raw TCP 实现主要通过回调执行, 因此其操作往往与各个消息的接收和处理密切相关。因此,熟悉底层 TCP 协议是有帮助的。 对于没有 lwIP 使用经验的人来说,有时并不清楚什么时候需要调用什么。下表显示了远程客户端和本地 lwIP tcp 服务器之间交互的顺序图。tcp_write()只对TCP数据进行排队,以便稍后传输,它实际上并没有开始传输。但是如果是在接收回调中使用tcp_write(),如上表中的示例,则不需要调用tcp_output()来传输要发送的数据。如果在接收回调中使用了tcp_output,它不会执行任何操作。当接收回调函数返回时,lwIP栈会自动启动数据的发送,远程客户端的前一个数据包的应答将于第一个传出的数据段相结合。如果在其它地方调用tcp_write,则可能需要调用tcp_output来启动数据传输。下面再给出一个lwIP作为客户端连接远程服务器的序列图:上表中在连接建立前便建立了接收和发送的回调函数,该操作也可以在连接建立后进行。如果连接失败,客户端可以通过tcp_err()设置的回调函数得到失败的通知。references正点原子嵌入式开发教程学会Zynq(11)RAW API的TCP和UDP编程
写在前面本文是本系列专题的第十篇,参考高亚军老师的视频教程以及课程的ppt,主要介绍了接口综合的相关内容。接口综合概述块级接口协议缺省情况下,块级接口协议被添加到设计中这些信号控制块,独立于任何端口级I/O协议。端口级别接口协议默认情况下,按值传递的输入参数和指针实现为简单的连接端口,没有相关的握手信号。如果函数有返回值,则实现输出端口ap返回来提供返回值协议是ap_ctrl_hs作为返回值。块级接口协议块级接口协议只能在指定的函数或函数的返回值。有三种可用的协议:ap_ctrl_hs:将生成Ap_start, ap_ready, ap_idle, ap_done。ap_ctrl_none:没有任何块级I/O协议。ap_ctrl_chain:类似于ap_ctrl_hs,但有一个额外的输入端口ap_continue,它提供从块消费这个块的数据的背压。ap_ctrl_hs 与 ap_ctrl_none比较ap_ctrl_hs 中的hs表示handshake(握手信号),被综合的函数为上相同的函数。如果选择ap_ctrl_hs 接口协议,则会综合出来start、done、idle、ready等接口信号。块级接口协议信号之间的关系ap_start为高,表示块可以开始处理数据。ap_ready为高,表示块已经准备好接收新输入。ap_done为高,表示操作已经完成。端口级别接口协议ap_none:指定端口不添加I/0协议。这个端口的参数被实现为一个没有其他相关信号的数据端口。ap none模式是标量输入的默认模式。ap_stable模式用于只在设备处于重置模式时才会改变的配置输入。ap_ovld与in-out参数一起使用。当将in-out分为单独的输入端口和输出端口时,ap_none应用于输入端口,ap_vld应用于输出端口。ap_ovld:这是指针实参的默认值,可读可写。也可以手动进行设置握手信号,在directive中,如果对in1进行设置ap_vld进行设置,则会综合生成一个输入有效信号。对in2设置ap_ack信号则会综合出一个输入响应信号,如果对信号使用ap_hs,也就是这里的in_out1则输入有效和响应信号都会产生。接口综合支持表在块级协议中,有ap_ctrl_hs、ap_ctrl_none、ap_ctrl_chain三种接口声明类型。在端口协议中,默认情况下会把:输入的标量、指针和参数设置成为ap_none。输出通过return、输出指针和输出的参数则会默认设置为ap_vld。对于输入输出的指针和参数,则会默认设置为ap_ovld。详细分类也可参考下图:接口综合-对数组的处理这里对数组的处理是在顶层形参,如果是子函数的后面会进行讲解如何对数组进行优化。在默认情况下,数组会映射成RAM端口,包含读写地址、使能等信号。在HLS 综合工具中可以进行设置RAM为单端口和双端口。如果不做这样的选择,Vivado HLS会自动分析设计并选择端口数量以最大化数据速率如果指定双端口RAM, Vivado HLS可以确定只需要一个端口,它使用一个单端口,并之前设置的双端口的设置。通过使用directives。可以设置双端口和单端口RAM接口,也可以设置使用FIFO接口。也可以进行分解数组。存储接口ap_memory默认情况下,只要数组在顶级函数上,就会使用ap_memory。无论数组的类型是什么(input, output, in/out), ap_memory都是默认值。将使用RAM接口的IP导出进行设计如下:从上图可以看出,通常这意味着一个外部内存可用来接收来自这个块的数据,并且这个内存应该手动添加。数据的输入也同理。ap_fifo和RAM接口类似,FIFO接口通常也需要使用FIFO进行发送和接收数据。如下图:数组综合小结默认情况下,顶级函数上的数组将被映射到由Vivado HLS决定的单端口或双端口RAM。可以根据数据速率要求对数组进行分区。只要数据流,ap_fifo也是一个选项数组接口。给输入输出端口添加寄存器在输入输出端口添加寄存器是解决时序问题的有效办法,在默认情况下HLS攻击不会进行添加寄存器部分。可以在指令编辑窗口中进行添加寄存器设置。这里可以看出,根据上面的设计,综合后的结果已经生成了寄存器。对于顶级函数,此选项与标量接口ap_none, ap _ack, ap vid, ap_ovld, ap hs相关,并导致信号(任何和协议有关的信号)被寄存并持续到函数执行的最后一个函数的执行周期。该选项需要启用’ap_ctrl_hs’功能协议。这个选项可以在子函数中使用,以重新调整输出和任何控制信号,直到函数执行结束。给设计添加Global CE默认情况下,Vivado HLS默认不会综合生成一个全局时钟。时钟使能阻止所有时钟操作,如果使能信号为低(高时刻有效)此时就可以禁用所有顺序操作。这可以通过选择目标解决方案并单击"solution setting "解决方案设置来实现。控制IO设计的个数这里是一个矩阵加法的示例,我们的目标是让我们的设计有更小的延时,并且有更少的IO。此时使用pipeline指令对for循环进行展开降低了延时,但是把之前设计的单端口RAM优化成了双端口RAM。然后可以进行使用指令对RAM接口进行约束,此时根据综合后的报告显示,相比之前的设计减少了延时,并且保证了IO的个数尽可能少。config_rtlconfig_rtl 可以帮助进行指定状态机的编码方式,同时也可以进行指定复位的属性,是同步复位还是异步复位,是高有效还是低有效。
学习内容本文介绍关于AXI BRAM控制器的相关内容,针对数据量较少、地址不连续、长度不规则的情况,通过 BRAM 来进行数据的交互。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。AXI BRAM控制器简介BRAM控制器可以用于与 AXI 互连和系统主设备的集成,以与本地块 RAM 进行通信。 内核支持到块 RAM 的单次和突发传输,并针对性能进行了优化。AX14或AX14- lite控制器配置中,可以配置到BRAM块的单个端口或到BRAM块的两个端口。通过第二个AX14-Lite控制端口连接,AXI BRAM控制器IP可以在数据路径上配置ECC功能,并通过可用的外部ECC寄存器设置。AXI BRAM Controller IP核的顶级端口连接和主模块如下图所示。展示了AX14-Lite模式下,AXI BRAM核心与BRAM块的连接。可以利用BRAM块的单端口利用率或BRAM块的双端口模式(通过参数设置)。下图展示了为支持AX14接口而生成的HDL核心。对BRAM块的单端口使用可以配置在双端口配置中增强的性能设置。,详细结构框图如下:所有与axis主设备的通信都是通过一个5通道的axis接口进行的。所有写操作都在AXI总线的写地址通道(AW)上启动,该通道指定了写事务的类型和相应的地址信息。写数据通道(W)为单个或突发写操作通信所有写数据。写响应通道(B)用作写操作的握手或响应。在读操作上,当AXI主程序请求读传输时,读地址通道(AR)通信所有地址和控制信息。当可以处理读操作时,AXI从AXI BRAM控制器IP响应读地址通道(AR)。当读取数据可用时,读数据通道®将转换操作的数据和状态。支持内存大小AXI BRAM Controller支持的内存最大为2mbytes(字节大小为8或9),支持的内存宽度和深度如表1-1所示。AXI BRAM Controller IP支持的最小深度为512字节。任何小于512的深度都被调整为512字节。系统框图与工程设计工程功能设计为PS 将串口接收到的数据写入 BRAM,然后从 BRAM 中读出数据,并通过串口打印出来;与此同时, PL 从 BRAM 中同样读出数据,并通过 ILA 来观察读出的数据与串口打印的数据是否一致。系统框图如下:硬件平台搭建新建工程,创建 block design。配置ZYNQ7添加ZYNQ7 IP,对zynq进行初始化配置,勾选配置uart资源,使能clock复位和 M_GP0接口,配置时钟,配置BRAM控制器和BRAM接着配置BRAM控制器,基本是默认配置。配置BRAM连接连线后系统如下,设计读取控制模块首先点击tools创建一个新的IP,选择创建一个AXI4接口的IP。编辑IP名称等信息,设计IP的接口信息,点击finish,完成IP创建。在IP目录下找到自己创建好的IP,右键进行IP的编辑。在顶层进行例化ram接口。在AXI总线协议实现的文件中添加IP的例化,实现AXI-Lite接口的功能进行参数的传递。这里引用正点原子的BRAM的读取模块,bram_rd.vmodule bram_rd( input clk , //时钟信号 input rst_n , //复位信号 input start_rd , //读开始信号 input [31:0] start_addr , //读开始地址 input [31:0] rd_len , //读数据的长度 //RAM端口 output ram_clk , //RAM时钟 input [31:0] ram_rd_data, //RAM中读出的数据 output reg ram_en , //RAM使能信号 output reg [31:0] ram_addr , //RAM地址 output reg [3:0] ram_we , //RAM读写控制信号 output reg [31:0] ram_wr_data, //RAM写数据 output ram_rst //RAM复位信号,高电平有效 ); //reg define reg [1:0] flow_cnt; reg start_rd_d0; reg start_rd_d1; //wire define wire pos_start_rd; //***************************************************** //** main code //***************************************************** assign ram_rst = 1'b0; assign ram_clk = clk ; assign pos_start_rd = ~start_rd_d1 & start_rd_d0; //延时两拍,采start_rd信号的上升沿 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin start_rd_d0 <= 1'b0; start_rd_d1 <= 1'b0; end else begin start_rd_d0 <= start_rd; start_rd_d1 <= start_rd_d0; end end //根据读开始信号,从RAM中读出数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin flow_cnt <= 2'd0; ram_en <= 1'b0; ram_addr <= 32'd0; ram_we <= 4'd0; end else begin case(flow_cnt) 2'd0 : begin if(pos_start_rd) begin ram_en <= 1'b1; ram_addr <= start_addr; flow_cnt <= flow_cnt + 2'd1; end end 2'd1 : begin if(ram_addr - start_addr == rd_len - 4) begin //数据读完 ram_en <= 1'b0; flow_cnt <= flow_cnt + 2'd1; end else ram_addr <= ram_addr + 32'd4; //地址累加4 end 2'd2 : begin ram_addr <= 32'd0; flow_cnt <= 2'd0; end endcase end end endmodule创建引脚接口,选择任意一个BRAM引脚,创建封装设置接口和名称,完成接口映射。然后点击完成IP封装。完成系统设计完成IP的创建后,添加IP,完成连线,整体设计如下图所示:然后在完成综合后进行setup debug ,抓取b端口有关的信号。完成添加DEDUG信号后,进行综合生成bit流,然后导出硬件,launch SDK。SDK软件部分新建应用工程,main.c中输入以下代码:#include "xil_printf.h" #include "stdio.h" #include "xbram_hw.h" #include "ps_pl_rd_ip.h" #include "xparameters.h" #define PL_BRAM_START PS_PL_RD_IP_S00_AXI_SLV_REG0_OFFSET #define PL_BRAM_START_ADDR PS_PL_RD_IP_S00_AXI_SLV_REG1_OFFSET #define PL_BRAM_LEN PS_PL_RD_IP_S00_AXI_SLV_REG2_OFFSET #define PS_PL_BASEADDR XPAR_PS_PL_RD_IP_0_S00_AXI_BASEADDR #define START_ADDR 0 #define BRAM_DATA_BYTE 4 char input_data[1024]; int len_input_data; int main(){ while(1){ int i=0; int wr_cnt=0; printf("ps_pl_bram test\n"); scanf("%s",input_data); len_input_data= strlen(input_data); for(i = START_ADDR*BRAM_DATA_BYTE;i<(START_ADDR + len_input_data)*BRAM_DATA_BYTE;i+=BRAM_DATA_BYTE) { PS_PL_RD_IP_mWriteReg(XPAR_BRAM_0_BASEADDR,i,input_data[wr_cnt]); wr_cnt++; } //配置起始地址 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START_ADDR,START_ADDR*BRAM_DATA_BYTE); //配置读取长度 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_LEN,len_input_data*BRAM_DATA_BYTE); //使能脉冲 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START,1); PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START,0); for(i = START_ADDR*BRAM_DATA_BYTE;i<(START_ADDR + len_input_data)*BRAM_DATA_BYTE;i+=BRAM_DATA_BYTE) { printf("bram address : %d ,read data : %c\n",i/BRAM_DATA_BYTE,PS_PL_RD_IP_mReadReg(XPAR_BRAM_0_BASEADDR,i)); } } }部分代码讲解本次工程比较简单,在while循环中实现了对串口输入的存储和显示打印。运行效果ila抓取数据通过ILA抓取的读取数据和发送写入的数据一致。references正点原子开发视频正点原子嵌入式开发指南UG585PG078
写在前面承接前文:ZYNQ-双核AMP通信(一),前文对双核AMP架构通信的相关内容进行了简单的介绍,本文完成AMP架构通信的程序并验证。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。工程设计CPU0 接收串口的数据,并写入OCM 中,然后利用软件产生中断触发 CPU1; CPU1 接收到中断后,根据从 OCM 中读出的数据并用串口打印,并在控制结束后触发 CPU0 中断,实现了双核 CPU 通信的功能。系统框图硬件平台搭建首先新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。这里添加SPI和SD的资源是为了进行双核的固化程序的验证。硬件系统构建完成如下:然后我们进行generate output product 然后生成HDL封装。这里只用到了MIO引脚,所以不需要进行管脚分配,XADC测量是内部的电压信息,并且使用的是PS_XADC接口。点击导出硬件资源(不包含bit流文件,因为只用到了PS资源),接着launch SDK。SDK软件部分新建应用工程,这里可以先进行创建CPU0的程序,这里和之前配置相同,无需特殊更改。点击NEXT后,完成建立一个空工程。然后修改cpu0的DDR的地址空间,打开src文件夹中的lscript.ld文件,该文件是链接脚本,可以进行配置应用程序的地址空间的大小。在图中可以看到OCM的两块区域的对应的大小和起始地址以及该应用程序的DDR的起始地址和大小。这里修改cpu0占用ddr的一半空间也即把0x1FF00000修改为0x0FF00000。完成修改后ctrl + s 保存即可完成地址映射的修改。同样的操作进行新建xpu1工程创建。处理器选择ps7_1。创建完成后修改ddr的地址映射,这里需要进行简单的计算,cpu0的起始地址为0x100000,大小为0x0FF00000,所以cpu1的起始地址为两者之和,也即为0x1000000所以对cpu1的程序修改地址映射如下图:打开板级支持包的设置界面同时加入AMP的一个交叉编译的宏定义-DUSE_AMP=1,添加在末尾。cpu0程序#include "xparameters.h" #include "xscugic.h" #include "xil_printf.h" #include "xil_exception.h" #include "xil_mmu.h" #include "stdio.h" //宏定义 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID #define SHARE_BASE 0xffff0000 //共享OCM首地址 #define CPU1_COPY_ADDR 0xfffffff0 //存放CPU1应用起始地址的地址 #define CPU1_START_ADDR 0x10000000 //CPU1应用起始地址 #define CPU1_ID XSCUGIC_SPI_CPU1_MASK //CPU1 ID #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15 //"SEV"指令唤醒CPU1并跳转至相应的程序 #define sev() __asm__("sev") //C语言内嵌汇编写法 send event指令 //函数声明 void start_cpu1(); void cpu0_intr_init(XScuGic *intc_ptr); void soft_intr_handler(void *CallbackRef); //全局变量 XScuGic Intc; //中断控制器驱动程序实例 int rec_flag = 0; //接收标志 char char_input='\0'; //CPU0 main函数 int main() { //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(CPU1_COPY_ADDR,0x14de2);//禁用0xfffffff0的Cache属性 //启动CPU1 start_cpu1(); //CPU0中断初始化 cpu0_intr_init(&Intc); while(1){ if(rec_flag == 0){ xil_printf("CPU0:请输入字符\r\n"); scanf("%c",&char_input); if(char_input != 13){ //向共享的地址中写入输入的数据 Xil_Out8(SHARE_BASE,char_input); xil_printf("CPU0: %c\n",char_input) ; //给CPU1触发中断 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU1,CPU1_ID); } rec_flag = 1; } } return 0 ; } //启动CPU1,用于固化程序 void start_cpu1() { //向 CPU1_COPY_ADDR(0Xffffffff0)地址写入 CPU1 的访问内存基地址 Xil_Out32(CPU1_COPY_ADDR, CPU1_START_ADDR); dmb(); //等待内存写入完成(同步) sev(); //通过"SEV"指令唤醒CPU1并跳转至相应的程序 } //CPU0中断初始化 void cpu0_intr_init(XScuGic *intc_ptr) { //初始化中断控制器 XScuGic_Config *intc_cfg_ptr; intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr, intc_cfg_ptr->CpuBaseAddress); //设置并打开中断异常处理功能 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr); Xil_ExceptionEnable(); XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU0, (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr); XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU0); //CPU0软件中断 } //软件中断函数 void soft_intr_handler(void *CallbackRef) { xil_printf("CPU0 : Soft Interrupt from CPU1\n"); rec_flag = 0; }cpu1程序#include "xparameters.h" #include "xscugic.h" #include "xil_printf.h" #include "xil_exception.h" #include "xil_mmu.h" #include "stdio.h" //宏定义 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID #define SHARE_BASE 0xffff0000 //共享OCM首地址 #define CPU0_ID XSCUGIC_SPI_CPU0_MASK //CPU0 ID #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15 //函数声明 void cpu1_intr_init(XScuGic *intc_ptr); void soft_intr_handler(void *CallbackRef); //全局变量 XScuGic Intc; //中断控制器驱动程序实例 int soft_intr_flag = 0; //软件中断的标志 char read_data; //CPU1 main函数 int main() { //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性 //CPU1中断初始化 cpu1_intr_init(&Intc); while(1){ if(soft_intr_flag){ read_data = Xil_In8(SHARE_BASE);//从共享OCM中读出数据 xil_printf("CPU1:%c\n",read_data) ; //给给CPU0触发中断 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU0,CPU0_ID); soft_intr_flag = 0; } } return 0 ; } //CPU1中断初始化 void cpu1_intr_init(XScuGic *intc_ptr) { //初始化中断控制器 XScuGic_Config *intc_cfg_ptr; intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr, intc_cfg_ptr->CpuBaseAddress); //设置并打开中断异常处理功能 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr); Xil_ExceptionEnable(); XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU1, (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr); XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU1); //CPU1软件中断 } //软件中断函数 void soft_intr_handler(void *CallbackRef) { xil_printf("CUP1:Soft Interrupt from CPU0\n") ; soft_intr_flag = 1; }下载程序和之前不同这次需要在config界面进行勾选,确保两个cpu的程序都下载进去。如下图:运行效果固化双核程序创建fsbl程序。点击next,选择FSBL点击finish。选中cpu0的工程,右击创建镜像工程,添加cpu1的elf文件添加完成创建镜像即可。烧录flash操作如下图:references正点原子开发视频UG585xapp1079
学习内容本文首先介绍了双核AMP通信的相关内容,接着进行设计AMP双核通信的工程,完成功能验证。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。双核通信多核处理器从结构上划分:同构多核: 同构多核处理器是指系统中的处理器在结构上是相同的,在软硬件设计上较为简单,通用性高。异构多核: 异构多核处理器是指系统中的处理器在结构上不同的,在特定场合中可以进行加速,提高性能。Xilinx 的 ZYNQ SOC 融合了这两种架构, ZYNQ SOC 芯片包含两个独立的 Cortex-A9 处理器,这两个处理器核在结构上是相同的,同时又包括了可编程的逻辑单元( PL),使得 ZYNQ 整体系统成为了一个异构多核系统,同时具有较高的通用性和性能。多核处理器软件运行方式多核处理器的运行模式有 AMP(非对称多处理)、 SMP(对称多处理)和 BMP(受约束多处理)三种运行模式。AMP(非对称多处理): 多个内核相对独立的运行不同的任务,每个内核相互隔离,可以运行不同的操作系统。SMP(对称多处理): 多个处理器运行一个操作系统,这个操作系统管理多个内核。BMP(受约束多处理): BMP运行和SMP运行模式类似,开发者可以指定将某个任务仅在某个指定内核上执行。下图为SMP和AMP的示意图:AMP非对称多处理运行详细介绍由于AMP多用于裸机开发,在两个CPU上可以运行不同的应用程序,所以本次工程主要使用该方式进行双核通信。对于AMP的相关设计这里可以参考XAPP1079的文档进行设计验证。ZYNQ-7000 AP SoC 提供两个共享通用内存和外设的 Cortex-A9 处理器。 非对称多处理 (AMP) 是一种机制,允许两个处理器运行自己的操作系统或裸机应用程序,并可以通过共享资源松散耦合这些应用程序。在AMP模式中,每个CPU可以访问自己独立的程序任务,但是需要注意的是要防止CPU在共享资源上发生冲突,否则会发生错误。资源分布对于该架构,CPU会有一些私有资源和共享资源,具体如下:私有资源:L1 cachePrivate peripheral interrupts (PPI)(私有外设中断)Memory management unit (MMU)(内存管理单元)Private timers共享资源:Interrupt control distributor (ICD)(中断控制分配器)DDR memory(DDR内存)On-chip memory (OCM)(片上资源)Global timer(全局时钟)Snoop control unit (SCU) and L2 cacheUART0(串口)两个处理器都使用 OCM 来相互通信。 与 DDR 内存相比,OCM 提供了非常高的性能和来自两个处理器的低延迟访问。 通过禁用两个处理器对 OCM 的缓存访问,进一步确保了确定性访问。此设计为防止共享资源出现问题而采取的措施包括:DDR 内存:CPU0 使用 0x00100000 到 0x001FFFFF 的内存。 CPU1 使用从 0x00200000 到 0x002FFFFF 的内存用于其裸机应用程序。L2 缓存:CPU1 不使用L2 缓存。 L2 缓存是共享资源,CPU0 拥有此资源。如果 CPU1 使用 L2 缓存,则需要从 CPU0 请求 L2 缓存刷新和失效,而 CPU0 将执行该操作。包含使 CPU1 能够请求 L2 缓存交互的通信通道超出了本示例设计的范围。ICD:来自PL 内核的中断被路由到CPU1 的PPI 控制器。通过使用 PPI,CPU1 可以自由地处理中断,而无需访问 ICD。定时器:CPU1 使用专用定时器。OCM:每个CPU 都非常小心地处理对OCM 的访问以防止争用。单个 OCM 地址位置用作标志以在两个处理器之间进行通信。 CPU0 在启动 CPU1 之前将标志初始化为 0。当标志为零时,CPU0 拥有 UART。当标志不为零时,CPU1 拥有 UART。只有 CPU0 设置标志,只有 CPU1 清除它。参考设计参考设计为赛灵思的官方文档xapp1079,这个文档设计的AMP架构是运行在ISE软件下,但是对我们进行AMP架构开发依旧有参考意义。参考设计硬件部分PL块包含一个自定义的嵌入式核心,连接到ChipScope分析仪VIO核心的同步输出。VIO核心为用户提供了与ChipScope分析仪硬件交互的机制。在这个设计中,当VIO产生一个脉冲时,自定义核心将一个中断转发给PS Core1_nlRQ引脚。核心还连接到PS主通用端口(M AXI_GP0),通过一个允许CPU0和CPU1访问核心内的控制寄存器的AXI互连。在中断服务程序期间,CPU1访问控制寄存器以清除中断请求(IRQ)。CPU0可以选择使用控制寄存器来创建一个指向CPU1的中断。Core1_nlRQ引脚直接连接到CPU1 PPI块,因此不需要修改共享ICD的配置。还包括一个ChipScope分析仪axis监视器核心,允许用户测量正在服务的IRQ的延迟。参考设计软件设计该软件可以分为三个部分:第一阶段引导加载程序(FSBL)用于CPU0的裸机应用程序用于CPU1的裸机应用程序FSBL总是在CPU0上运行,是PS上电复位后第一个运行的软件应用程序。FSBL负责编程PL,并将应用程序可执行文件和可链接格式(ELF)文件复制到DDR内存。在将应用程序加载到DDR内存后,FSBL然后开始执行加载的第一个应用程序。参考设计让CPU0和CPU1运行它们自己的裸机应用程序代码。CPU0负责初始化共享资源并且启动CPU1。CPU0的应用程序位于从地址0x00100000开始的内存中。链接器脚本用于设置起始地址。CPU0的设计步骤:配置MMU对0xFFFF0000 ~ oxfffffe地址范围内的OCM访问关闭缓存功能。OCM的地址映射是不变的,所以OCM存在于地址0x00000000到0x0002FFFF和地址0xFFFF0000到0XFFFFFFFF。示例设计只使用了高64kb的OCM,因此在地址0xFFFFo000到oxfffffe上禁用缓存。初始化ICD;CPU1开始启动;打印到UART;在OCM中设置一个用作信号量标志的内存位置;等待OCM中用作信号量标志的内存位置被清除。CPUO应用程序无限期地重复步骤3到步骤6。PS启动后,内部 boot ROM完成执行,CPU1被重定向到0xFFFFFE00的OCM中的一小段代码。这段代码是一个连续循环,它等待一个事件,检查地址位置0xFFFFFFF0的非零值,然后继续循环。如果0xFFFFFFF0包含一个非零值,CPU1将跳转到获取的地址。CPU0通过将Ox00200000的值写入地址0xFFFFFFF0,然后运行Set Event (SEV)命令来启动CPU1(两者都运行裸机程序)。SEV唤醒CPU1,从地址0xFFFFFFF0读取值0x00200000,然后跳转到地址0x00200000。FSBL负责将CPU1 ELF放置在0x00200000。CPU1应用程序位于从地址Ox00200000开始的内存中。链接器脚本用于设置起始地址。CPU1应用程序执行以下操作:配置MMU对地址范围为0xFFFF0000 --0XFFFFFFFF的OCM访问关闭缓存功能。OCM的地址映射是不变的,因此OCM存在于地址0x00000000到0x0002FEEF和地址0xFFEF0000到xFFFFFFFE。这个应用程序笔记只使用了OCM的高64kb,所以缓存在地址0xFFFF0000到0XFFFFFFFF上被禁用。初始化PPl中断控制器和中断子系统。等待OCM中用于设置信号量标志的内存位置。打印到UART。打印的字符串的选择取决于中断服务例程是否增加了一个全局变量。如果全局变量irq_count不为零,CPU1将该值设置为零清除OCM中用作信号量标志的内存位置。CPU1应用程序无限期地重复步骤3到步骤5。一些细节启动CPU1CPU 0负责在CPU 1上启动代码执行。BootROM将CPU 1设置为Wait for Event模式。什么都没有启用,只有少数通用寄存器被修改,以将其置于等待WFE指令的状态。CPU0在CPU1上启动应用程序需要少量的协议。CPU 1收到系统事件后,立即读取地址0xFFFFFFF0的内容并跳转到该地址。如果SEV在更新目的地址位置(0xFFEEFFE0)之前发出,CPU1继续处于WFE状态,因为0xFFFFFFF0将WFE指令的地址作为安全网。如果编写0xFFFFFFF0地址的软件无效或指向未初始化的内存,则结果是不可预测的。CPU 1上的初始跳转只支持Arm-32 ISA代码。在跳转的目的地不支持Thumb和Thumb-ll代码。这意味着目的地址必须是32位对齐的,并且必须是有效的Arm-32指令。如果不满足这些条件,结果是不可预测的。CPU0在CPU1上启动应用的步骤如下:将cpu1的应用程序地址写入0xFFFFFFF0。执行SEV指令,唤醒CPU1并跳转到应用程序。地址范围0xFFFFEE00到0xFFFFFFF0是保留的,在第1阶段或以上的应用程序完全可用之前不能使用。在第二个CPU成功启动之前对这些区域的任何访问都会导致不可预测的结果。软件中断前面提到两个处理器都使用 OCM 来相互通信。 而OCM属于两个CPU共用的资源,如果同时调用会出现问题,所以为了防止共享资源出现问题,可以进行使用中断的办法进行控制。也即让CPU0在完成对OCM的相关操作并完成释放OCM后,产生软件中断,CPU1接收到软件中断即可知道CPU0完成对OCM的操作,即可进行CPU1的对OCM的相关操作。每个CPU都有一组私有外设中断(PPls),它们使用存储的reqisters进行私有访问。PPls包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ。软件生成的中断(SGIs)被路由到一个或两个cpu。SGIs是通过写入通用中断控制器(GIC)中的寄存器而生成的。共享外围中断(SPls)是由PS和PL中的各种I/O和内存控制器产生的。它们被路由到其中一个或两个cpu。来自PS外围设备的SPI中断也被路由到PL。通过一个SGI (software generated interrupt,软件生成中断),每个CPU可以中断自己,也可以中断另一个CPU,也可以中断两个CPU。软件生成中断共有16个,如表所示。通过将SG中断号写入ICDSGIR寄存器并指定目标CPU,生成一个SGl。这个写操作通过CPU自己的私有总线进行。每个CPU都有自己的一组SGl寄存器,用于生成16个软件生成的中断中的一个或多个。通过读取ICCIAR(中断确认)寄存器或在ICDICPR(中断清除等待)寄存器的相应位写一个1来清除中断。所有的SGis都是边缘触发。sgl的灵敏度类型是固定的,不能改变;ICDICFRO寄存器是只读的,因为它指定了所有16个sgl的敏感类型。OCMOCM模块包含256kb的RAM和128kb的ROM (BootROM)。它支持两个64位的axis从接口端口,一个专用于通过APU snoop控制单元(SCU)的CPU/ACP访问,另一个由处理系统(PS)和可编程逻辑(PL)中的所有其他总线主共享。BootROM内存只被引导进程使用,用户不可见。OCM分布OCM分布在两个区域,一个是地址的开始端,有连续的3个64k字节的存储空间,另外一个在最后,用户可以根据自己的传输交互的数据量大小进行选择使用哪一块存储空间。程序设计见下篇 ZYNQ-双核AMP通信(二)。
学习内容本文首先介绍Flash和QSPI Flash控制器的相关内容,然后使用 QSPI Flash 控制器,开发板上的 QSPI Flash 进行写、 读操作。通过对比读出的数据是否等于写入的数据, 从而验证读写操作是否正确。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。Flash简介Flash存储器,又叫做闪存,是一种非易失性存储器。具有操作方便读写速度快等优点。一般用于存储操作系统和程序代码,或者用于数据存储。Flash的存储单元组织为块阵列,块是擦除操作的最小单位,擦除操作将块内的所有为置位为1,页是读写操作的基本单位。在对页进行写操作前,要判断该页内所有位是否为1。如果全部为1可以写操作,否则要对整块进行擦除操作(Flash只能从1反转到0)。Flash在内部结构(接口)上主要分为Nor flash 和NAND flash。Nor Flash :写入和擦除的速度低;结构复杂,成本高;存储容量较小;一般用于存储Bootloader以及操作系统或者程序代码,可以在芯片内部直接运行代码。NAND Flash :写入和擦除的速度较快;结构简单,成本低;存储容量较大;一般用于存储材料和数据。Flash在外部接口上主要分为CFI flash 和SPI(STD/Dual/Quad) flash。CFI flash 读写速度快,需要的硬件引脚多且不同容量的硬件不兼容;而SPI(STD/Dual/Quad) flash读写速度慢,需要的硬件引脚少且不同容量的硬件兼容。QSPI Flash简介介绍Quad-SPI闪存控制器是位于PS内的输入/输出外设(IOP)的一部分。它用于访问多位串行闪存设备,以实现高吞吐量和低引脚数应用。控制器可以以三种模式运行:I / O模式,线性寻址模式和传统SPI模式。在I / O模式下,软件与闪存设备协议紧密交互,所以在该模式下需要对协议进行详细了解。软件通过使用四个TXD寄存器将闪存命令和数据写入控制器。软件通过读取RXD寄存器。线性寻址模式使用设备操作的子集来消除I / O模式读取闪存所需的软件开销。也就是说线性寻址模式比IO模式要快的多。线性模式采用硬件来向闪存发出命令,并控制从flash到AXI接口的数据。控制器响应AXI接口上的存储器请求,就好像闪存是ROM存储器。在传统模式下,QSPI控制器充当普通的SPI控制器。控制器可以与一或两个闪存设备接口。可以并行连接两个设备以实现8位性能,也可以以堆叠的4位布局连接以最大程度地减少引脚数。系统框图QSPI Flash 控制器的系统框图如下所示:从上图中可知, QSPI Flash 控制器通过 MIO 与外部 Flash 器件连接,支持三种模式:单个从器件模式、双从器件并行模式和双从器件堆叠模式。双从器件并行模式把每个flash的IO进行单独连接,扩展成8位用于控制对不同flash的访问。而双从器件堆叠模式,使用SS片选信号进行区分flash的使能,所以想扩展 QSPI Flash 的存储容量,可以使用双从器件并行模式。当使用单个设备时,直接存储器读取的地址映射从FC00_0000开始,最大为FCFF_FFFF(16 MB)。 两台设备系统的地址映射取决于存储设备和I / O配置。QSPI Flash 控制器内部框图由上图可知,在线性地址模式下,使用的是AXI的接口进行数据交互的,首先由AXI总线读取到响应的指令,并存到Command FIFO,然后使用AXI to SPI的命令转化器传输到选择器。在IO模式下,使用的是APB接口直接把接收到的信号传输给选择器进行选择。然后选择器进行功能选择后传输到TxFIFO,接着把Tx FIFO的数据进行串行化,然后通过MIO引脚发送。接收部分原理相反,而且可以看出在线性地址模式下,接收信号的转换使用的是数据转换器。所以在线性地址模式下,只支持读取数据,不支持发送数据。流程控制在数据传输期间,I / O模式具有不同的流控制模式。 用户可以在自动和手动模式之间进行选择,该模式由config_reg.MANSTARTEN(Man_start_com)控制。在自动模式下,包括芯片选择控制在内的整个传输过程都在硬件中完成。 无需软件干预。在手动模式下,用户控制数据传输的开始。 在这种情况下,软件要么将整个传输序列写入TxFIFO,要么直到TxFIFO已满。在手动模式下,用户除了控制传输开始之外,还可以选择控制芯片选择。 从命令开始,软件再次将传输序列写入TxFIFO,直到TxFIFO已满。 然后,软件拉高CS,然后手动启动进行硬件管理。编程指南在xilinx给出的UG585指导手册中,对QSPI Flash 控制器编程设计进行了指导开发,用户可以根据自己的设计需要参考指导手册中的编程顺序实现自己的应用功能。启动顺序配置时钟。配置发送/接收信号。(以上两步在vivado底层搭建中已经完成设计)重置控制器。配置控制器。配置控制器该步骤适用于线性寻址和I / O模式。 它配置了控制器的波特率,FIFO,flash模式,时钟相位/极性和对环回延迟进行编程。配置控制器。 写入qspi.Config_reg寄存器。a. 设置波特率[BAUD_RATE_DIV]。b. 选择主模式,[MODE_SEL] = 1。C. 选择flash模式(不是传统SPI),[LEG_FLSH] = 1。d. 选择Little Endian,[endian] = 0。e. 将FIFO宽度设置为32位[FIFO_WIDTH]。F. 设置时钟相位[CLK_PH]和极性[CLK_POL]。启用回送时钟。 如果使用回送时钟,确保将qspi.Config_reg [BAUD_RATE_DIV]设置为0b00,并使用以下设置配置qspi.LPBK_DLY_ADJ(回送延迟调整)寄存器:a. 设置为选择内部时钟。 qspi.LPBK_DLY_ADJ [USE_LPBK] = 1。b. 将时钟延迟设置为0。qspi.LPBK_DLY_ADJ [DLY0] = 0b00。c. 设置时钟延迟1. qspi.LPBK_DLY_ADJ [DLY1] = 0b00。线性寻址模式线性寻址模式下数据读取(存储器读取)的操作顺序如下:将手动启动启用设置为自动模式。 设置qspi.Config_reg [Man_start_en] = 0。使能片选信号。 设置qspi.Config_reg [PCS] = 0。将配置寄存器编程为线性寻址模式。启用控制器。 设置qspi.En_REG [SPI_EN] = 1。从线性地址存储区读取数据。 内存范围取决于大小和设备数量。 范围是从0xFC00_0000到0xFDFF_FFFF。禁用控制器。 设置qspi.En_REG [SPI_EN] = 0。取消使能片选信号。 设置qspi.Config_reg [PCS] = 1。配置I / O模式使用I / O模式进行读取和写入编程步骤如下:启用手动模式。qspi.Config_reg [Man_start_en,Manual_CS] = 1。配置flash设备。 对单个闪存设备使用qspi.LQSPI_CFG寄存器的重置值。 如果是并行双闪存设备,则将1写入TWO_MEM,SEP_BUS位字段。使能片选。 设置qspi.Config_reg [PCS] = 0。启用控制器。 设置qspi.En_REG [SPI_EN] = 1。将字节序列写入闪存。 使用TXD从1到4字节写入TxFIFO寄存器。避免TxFIFO溢出。当TxFIFO为空时,可以写入252个字节。此后,软件可以通过读取qspi.Intr_status_REG [TX_FIFO_full]并等待直到它等于0之后再写入TXD寄存器,来避免TxFIFO溢出。启用中断。写入qspi.Intrpt_en_REG。开始数据传输。设置qspi.Config_reg [Man_start_com] = 1。中断处理程序:在编程/读取操作期间,将所有需要的数据传输到QSPI flash,并传输到Quad-SPI flash。如果执行了读取操作:重新排列READ数据以消除由于空循环而读取的数据。取消使能片选信号。设置QSPI.Config_reg [PCS] = 1。禁用控制器。设置qspi.En_REG [SPI_EN] = 0。I/O模式中断服务程序配置ISR以根据Quad-SPI器件类型处理中断条件。 要从Quad-SPI器件读取数据,最简单的ISR从RxFIFO读取数据并将内容写入TxFIFO。控制器生成系统外设中断(SPI),IRQ ID#51。对以下两种情况进行触发中断。a. 读取传输中断。 RxFIFO不为空中断b. 写传输中断。 TxFIFO未完全中断I/O模式中断中断仅在I / O模式下使用。只要满足任何中断条件,控制器中断就被置为有效。 Quad-SPI中断处理程序检查中断原因。单个中断服务程序可以管理所有中断条件。Rx和Tx的中断处理程序中断处理程序由IRQ ID#51触发。示例读取RxFIFO直到其为空,然后填充TxFIFO。 RxFIFO非空中断状态用于确定是否可以从RxFIFO读取内容。 TxFIFO未满中断指示TxFIFO中是否有空间容纳更多内容。禁用控制器中的所有中断。将qspi.Intrpt_dis_REG [TX_FIFO_not_full,RX_FIFO_full]都设置为1。清除中断。将1s写入中断状态寄存器qspi.Intr_status_REG。清空RxFIFO。检查是否声明了RxFIFO非空中断。如果qspi.Intr_status_REG [RX_FIFO_not_empty] = 1,则RxFIFO中有数据。a. 如果状态为有效,则从RxFIFO读取数据。使用qspi.RX_data_REG寄存器读取数 据。b. 从RxFIFO读取数据并轮询中断状态,直到RxFIFO为空。当qspi.Intr_status_REG [RX_FIFO_not_empty] = 0时,RxFIFO为空。填充TxFIFO。检查TxFIFO未满状态是否有效。如果 qspi.Intr_status_REG [TX_FIFO_not_Full] = 1,则存在要发送到闪存设备的数据(编程和/或读取操作):a. 将数据写入qspi.TXD0寄存器。b. 轮询qspi.Intr_status_REG [TX_FIFO_full] = 1,这表明TX FIFO已满。c. 遵循步骤a和b,直到将所有数据写入TxFIFO或qspi.Intr_status_REG[TX_FIFO_full] = 1。启用中断。将qspi.Intrpt_en_REG [TX_FIFO_not_full,RX_FIFO_full]都设置为1。开始数据传输。设置qspi.Config_reg [MANSTRTEN] = 1。
学习内容本文首先介绍了ZYNQ的XADC的相关内容,并学习使用ZYNQ芯片中的XADC测量芯片内部的温度电压等参数,然后进行串口打印输出。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。XADC介绍简介Xilinx模拟信号转换模块,称为XADC,是一个硬核。它具有JTAG和DRP接口用于访问7系列FPGA中的XADC状态和控制寄存器。Zynq-7000 SoC器件添加了第三个接口,即PS-XADC接口,用于PS软件进行控制XADC。 ZYNQ器件将XADC与可编程逻辑融合,解决了对模拟数据采集和监视要求。XADC具有两个12位的ADC,具有独立的跟踪和保持放大器,模拟多路复用器(最多17个外部模拟输入通道)以及片上散热和片上电压传感器。可以将两个ADC配置为同时采样两个外部输入模拟通道。采样保持放大器支持一系列模拟输入信号类型,包括单端输入,双端输入和差分输入。模拟输入可以支持信号带宽在1M SPS的采样率下为500 KHz。可以使用外部模拟多路复用器来增加支持的外部通道数量,无需额外的封装引脚。XADC可选地使用片上参考电路,从而无需外部有源元件,用于温度和电源轨的基本片上监控。实现12位的ADC的全部性能,建议使用外部1.25V作为参考电压。最新的测量结果(连同最大和最小读数)存储在专用寄存器。用户可以根据自己的需要进行自定义的警报阈值(例如80°C),可以自动指示温度过高事件和不可接受的电源变化,并启动软件控制的系统掉电。控制方式PS端可以通过以下两种方式之一与XADC通信:PS-XADC接口:PS互连上的32位APB从接口,该接口使用FIFO,并进行了串行化。通过PS to PL AXI的主接口,此时要使用AXI XADC Logic IP核来控制XADC。需要注意的是,对PS端控制ADC对性能要求较高的程序,要使用相关连接的逻辑IP连接到M_AXI_GP接口(并行数据路径)。使用PS_XADC接口时,FIFO用于命令和读取数据,以允许软件快速排队命令,而不必等待序列化,但是对于PS to PL AXI主接口访问,数据像PL-JTAG一样被序列化到XADC 中(串行数据路径),相对来说速度慢得多。系统框图PL-JTAG接口和内部PS-XADC接口不能同时使用。 这些接口之间的选择由devcfg.XADCIF_CFG [ENABLE]位控制。 XADC可以进行接口选择,即对(PL-JTAG或PS-XADC)和DRP接口之间进行仲裁选择。下图为XADC的系统框图。由上图可知,XADC是通过硬逻辑实现的,并且位于PL电源域中。 PS-XADC接口是PS的一部分,所以无需编程PL就可以由PS APU访问。 但是必须打开PL的电源以配置PS-XADC接口,使用PL-JTAG或DRP接口以及操作XADC。同时由图上可以清楚看出对于PL-JTAG或PS-XADC这两个接口经过了一个二选一选择器,所以这两个不能同时进行驱动。接口说明XADC在DRP接口与PS-XADC或PL-JTAG接口之间进行仲裁。PS-XADC Interface: PS-XADC接口是PS中运行的软件使用devcfg寄存器配置接口。 软件将命令写入接口,然后将其推入命令FIFO。 这些由DRP命令,地址和数据组成的32位写入被串行化,并以回送路径发送到XADC,该回送路径填充了软件读取的返回读取数据FIFO。DRP Interface: DRP接口是一个并行的16位双向接口,可以使用AXI4-Lite接口通过AXI XADC IP核连接到主机上,以使处理器能够控制XADC。IP内核通过每个AXI4-Lite读/写事务接收16位数据。PL JTAG Interface: XADC使用完整的JTAG接口扩展到DRP接口。 这允许通过现有的片上JTAG基础结构对XADC DRP进行读/写访问。 通过JTAG访问DRP接口不需要实例化。 边界扫描指令(6位指令= 110111)称为XADC_DRP,已添加到7系列FPGA中,允许通过JTAG TAP访问DRP。 所有XADC JTAG指令均为32位宽。PS-XADC接口编程指南本文主要使用的是PS-XADC接口对XADC进行编程控制,下面给出相关操作的编程指导步骤。通过PS-XADC接口初始化XADC对通道和XADC进行复位操作,并刷新FIFO,操作顺序如下:复位串行通讯通道。 先后把1和0到devcfg.XADCIF_MCTL[RESET]寄存器位中;复位XADC。 把16位任意值写入DRP地址0x03(复位寄存器)。接着写08030000h到devcfg.XADCIF_CMDFIFO寄存器中;刷新FIFO。 这里刷新FIFO没有复位信号,而是将15个NOOP写入到FIFO;然后等待命令FIFO清空。 最后一个命令应该是NOOP(虚拟写入),最后读取读取数据FIFO,直到为空。命令准备准备要写入XADC寄存器的数据配置顺序:本示例格式化了用于写入XADC配置寄存器1的数据,以将XADC设置为独立模式。DRP数据。 将XADC设置为独立模式的数据为8000h。DRP地址。 XADC配置寄存器1的地址为0x41。编写命令。 写入操作的命令为0010b。 在XADC配置寄存器1(0x41)中写入8000h的命令为08418000h。准备从XADC寄存器读取的数据配置顺序:本示例格式化了用于读取XADC VCCPAUX状态寄存器0x0E的数据。DRP数据。 数据可以是用于读取操作(0)的任何任意数据。DRP地址。 XADC VCCPAUX状态寄存器的地址为0x0E。编写命令。 读取操作的命令为0001b。 读取XADC VCCPAUX状态寄存器0x0E的命令为040E0000h。读写FIFO数据向XADC写入命令以下编程顺序为写入XADC 电压警报上限阈值寄存器。准备命令。 执行上面的命令准备的部分,写入具有所需阈值的XADC VCCPAUX警报上限阈值寄存器(0x5A)。用数据填充命令FIFO。 将步骤1中格式化的数据写入devcfg.XADCIF_CMDFIFO寄存器。等待命令FIFO变空。 等待,直到devcfg.XADCIF_MSTS [CFIFOE] = 1。从XADC读取VCCPAUX值:从XADC VCCPAUX状态寄存器读取当前VCCPAUX值顺序如下:准备命令。执行上面的命令准备的部分,读取XADC VCCPAUX状态寄存器(0x0E)。将数据写入命令FIFO。将步骤1中格式化的数据写入devcfg.XADCIF_CMDFIFO寄存器。等待命令FIFO变空。等待,直到devcfg.XADCIF_MSTS [CFIFOE] = 1。从读取数据FIFO读取伪数据。读取devcfg.XADCIF_RDFIFO寄存器。格式化数据。执行上面的命令准备的部分,以使其不进行任何操作。将数据写入命令FIFO。将步骤5中的格式化数据写入devcfg.XADCIF_CMDFIFO寄存器。读取读取数据FIFO。读取devcfg.XADCIF_RDFIFO寄存器。中断配置和管理Alarm5(VCCPAUX)本示例将XADC寄存器配置为设置警报阈值,操作模式并启用PS-XADC接口中的警报5(VCCPAUX)中断。准备命令。执行上面的命令准备的部分,写入XADC硬宏警报阈值寄存器(VCCPAUX Upper-0x5A和VCCPAUX具有所需阈值的低0x5E)和XADC Config_Reg1(0x41)来将XADC设置为独立模式。将命令写入命令FIFO。将步骤1中准备的命令写到devcfg.XADCIF_CMDFIFO寄存器。在PS-XADC接口中启用Alarm5中断。写devcfg.XADCIF_INT_MASK[M_ALM] = 7Eh。检查是否触发了Alarm5。 devcfg.XADCIF_INT_STS [M_ALM] = 1的轮询。清除Alarm5中断。写入devcfg.XADCIF_INT_STS [M_ALM] = 1。禁用Alarm0中断。写入devcfg.XADCIF_INT_MASK [M_ALM] = 7Fh通过PS-XADC接口的启动顺序通过PS-XADC接口的启动并设置各种接口参数,并包括中断和数据传输的步骤如下。完成初始化XADC;配置PS-XADC接口:对配置寄存器进行编程。将80001114h写入devcfg.XADCIF_CFG寄存器:使用默认的最小空闲间隙,[IGAP] = 14h(20个串行时钟)。使用默认的XADC串行时钟频率为PCAP_2x时钟频率的1/4,[TCKRATE] = 01。使用默认的FIFO串行读取捕获边沿(上升),[REDGE] = 1。使用默认的FIFO串行写启动边沿(下降),[WEDGE] = 0使用默认的读取数据FIFO阈值级别[DFIFOTH] = 0x0。使用默认的命令FIFO阈值级别,[CFIFOTH] = 0。启用XADC的PS访问。将0x1写入devcfg.XADCIF_CFG [ENABLE]。配置中断:中断用于管理来自XADC和操作FIFO/读取FIFO数据;数据传输到XADC。系统框图根据本次工程,画出相应的系统框图,如下图所示:本次工程,使用了UART和XADC部分,使用XADC对芯片内部的电压和温度进行检测,并用串口打印输出。硬件平台搭建因为不需要使用其他资源,可以在原来的UART测试工程下进行开发搭建,直接保存即可。新建步骤如下,首先新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。这里只要勾选UART资源即可,取消多余资源,然后点击OK。硬件系统构建完成如下:然后我们进行generate output product 然后生成HDL封装。这里用到了UART,是MIO引脚,所以不需要进行管脚分配,XADC测量是内部的电压信息,并且使用的是PS_XADC接口。点击导出硬件资源(可以不包含bit流文件,因为只用到了PS资源),接着launch SDK。SDK软件部分打开SDK后,新建application project。在system.mss中可以打开相关参考文档辅助设计。参考给出的示例,在main.c中写入以下代码:#include "xparameters.h" #include "xadcps.h" #include "stdio.h" #include "xil_printf.h" #include "sleep.h" #define XADC_DEVICE_ID XPAR_XADCPS_0_DEVICE_ID static XAdcPs XAdc_Inst; u32 Temp_RawData; //芯片温度、最大、最小温度 float TempData; float TempmaxData; float TempminData; //内核各部分电压值 float VccPintData; float VccPauxData; float VccPdroData; float VccBramData; void Xadc_init(); void Xadc_test(); int main(){ //初始化XADC Xadc_init(); while(1){ Xadc_test(); sleep(2); } return 0; } void Xadc_init(){ int status; XAdcPs_Config *ConfigPtr; ConfigPtr = XAdcPs_LookupConfig(XADC_DEVICE_ID); XAdcPs_CfgInitialize(&XAdc_Inst, ConfigPtr,ConfigPtr->BaseAddress); //自测 status = XAdcPs_SelfTest(&XAdc_Inst); if (status != XST_SUCCESS) { xil_printf("xadc selftest failed!\n"); } //设置启动模式 XAdcPs_SetSequencerMode(&XAdc_Inst, XADCPS_SEQ_MODE_SAFE); } void Xadc_test(){ Temp_RawData = XAdcPs_GetAdcData(&XAdc_Inst, XADCPS_CH_TEMP); TempData = XAdcPs_RawToTemperature(Temp_RawData); Temp_RawData = XAdcPs_GetMinMaxMeasurement(&XAdc_Inst, XADCPS_MAX_TEMP); TempmaxData = XAdcPs_RawToTemperature(Temp_RawData); Temp_RawData = XAdcPs_GetMinMaxMeasurement(&XAdc_Inst, XADCPS_MIN_TEMP); TempminData = XAdcPs_RawToTemperature(Temp_RawData); Temp_RawData = XAdcPs_GetAdcData(&XAdc_Inst, XADCPS_CH_VCCPINT); VccPintData = XAdcPs_RawToVoltage(Temp_RawData); Temp_RawData = XAdcPs_GetAdcData(&XAdc_Inst, XADCPS_CH_VCCPAUX); VccPauxData = XAdcPs_RawToVoltage(Temp_RawData); Temp_RawData = XAdcPs_GetAdcData(&XAdc_Inst, XADCPS_CH_VCCPDRO); VccPdroData = XAdcPs_RawToVoltage(Temp_RawData); Temp_RawData = XAdcPs_GetAdcData(&XAdc_Inst, XADCPS_CH_VBRAM); VccBramData = XAdcPs_RawToVoltage(Temp_RawData); printf("============================\n"); printf("Current Temperature:%0.4fC\n",TempData); printf("Maximum Temperature:%0.4fC\n",TempmaxData); printf("Minimum Temperature:%0.4fC\n",TempminData); printf(" VccPSint:%0.4fV\n",VccPintData); printf(" VccPSaux:%0.4fV\n",VccPauxData); printf(" VccPSdro:%0.4fV\n",VccPdroData); printf(" VccPSBram:%0.4fV\n",VccBramData); printf("============================\n"); }整体代码比较简单,主要还是使用相关的函数进行开发设计,这里打印了相关温度和电压信息。运行效果在SDK的串口终端中正确显示各个参数信息。ReferenceUG585正点原子开发视频教程
学习内容本文主要介绍关于ZYNQ芯片的串口中断功能,并编写相关读写测试代码,完成串口中断的读写测试。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。UART控制器简介UART控制器是一个全双工异步接收和发送,支持可编程波特率和I/O信号格式。 该控制器可实现奇偶校验自动生成和多主检测模式。UART操作由配置和模式寄存器控制。FIFO、调制解调器信号和其他控制器功能的状态是使用状态、中断状态和调制解调器状态寄存器读取的。UART控制器有独立的RX和TX数据路径。每个路径包括一个64字节的FIFO。 控制器TX和RX FIFO中的数据进行串行化和反串行化,并包括一个模式开关,以支持RXD和TXD信号的各种环回配置。(RXD和TXD使用模式:正常模式、各种环回诊断测试模式)FIFO中断状态位支持轮询或中断驱动的处理程序。 软件使用RX和TX数据端口寄存器读取和写入数据字节。当在类似于调制解调器的应用程序中使用UART时,调制解调器控制模块检测并生成调制解调器握手信号,并且还根据握手协议控制接收和发送器路径。(调制解调器控制信号:CTS, RTS, DSR, DTR, RI和DCD只有在EMIO接口可用)系统框图UART控制器的系统框图如下图所示:在图中,SLCR寄存器(系统级控制寄存器(SLCR)由用于控制PS行为的各种寄存器组成。这些寄存器可以通过中央互连使用加载和存储指令访问。)包括控制位用于UART时钟,复位和MIO-EMIO信号映射。软件可以使用APB 32位的从接口访问UART控制器寄存器。每个控制器的IRQ(中断号为59,82)连接到PS中断控制器,并连接到PL。UART控制器由参考时钟( UART REF_CLK)驱动,同时控制器也需要连接APB 总线时钟( CPU_1x clock), UART REF_CLK 和 CPU_1x clock 都是来自于 PS 时钟子系统。内部框图UART控制器内部框图如下图所示。由上图可知,UART控制器使用PS AXI interconnect 进行数据交互,通过APB Slave接口来接收PS端口的配置信息和一些数据信息。假设要发送数据,则系统先将发生的字符串缓存到TxFIFO下,然后经Transmitter模块实现并转串,如果工作在正常模式下,则数据之间接到MIO/EMIO引脚上,正常向接收设备发送;假设要接收其他设备传来的串口信息,则首先通过串口接收引脚接收串行数据,然后经过receiver模块实现串转并,转换过后存入到RxFIFO下,经过APB从接口传输到PS端,即可对接收数据进行处理。通过控制状态寄存器可以对UART控制器进行控制,而这些引脚只能连接到EMIO。UART控制器内部包括一个中断模块,所以可以和其他模块一样正常接收到来自系统的中断信号。对于UART的参考时钟在控制器内部首先进行了一个八分频,接着再产生波特率时钟。Transmit FIFOTransmit FIFO (Tx FIFO)存储由APB从接口写入的数据,发送模块收到FIFO中的数据后删除FIFO的数据然后进行串并转换,并装入其移位寄存器。TxFIFO的最大数据宽度为8位。数据通过写入TxFIFO寄存器加载到TxFIFO。当数据加载到TxFIFO时,TxFIFO空标志将被清除并保持在这个Low状态,直到TxFIFO中的最后一个字符被删除并加载到发送器移位寄存器中。TxFIFO满中断状态(TFULL)表明TxFIFO已经完全写满了,并且阻止数据被写入到TxFIFO中。如果对TxFIFO执行另一个APB写入操作,则触发溢出,写入数据不会加载到TxFIFO中。发送 FIFO接近满标志(TNFULL)表明在FIFO中没有足够的空间来进行一次程序大小的写入,这是由模式寄存器的WSIZE位控制的。TxFIFO接近满标志(TNFULL)表示TxFIFO中只有字节空闲。可以在TxFIFO填充级别上设置一个阈值触发器(TTRIG)。发射器触发寄存器可以用来设置这个值,这样当TxFIFO填充深度达到设定的阈值时触发可以设置。Receiver FIFOReceiver FIFO和Transmit FIFO类似。RxFIFO存储由接收器串行移位寄存器接收的数据。RxFIFO的最大数据宽度是8位。当数据加载到RxFIFO时,RxFIFO空标志被清除,并且这种状态保持为低,直到RxFIFO中的所有数据通过APB接口传输完毕。空标志重新置位为高,如果接着读FIFO的话,则会从空的RxFIFO读取返回0。RxFIFO满状态(Chnl_int_sts_reg0 [RFUL]和Channel_sts_reg0 [RFUL]位)表明RxFIFO满了,阻止数据被加载到RxFIFO。同时也可以在RxFIFO上设置一个阈值触发器(RTRIG)。接收器触发级别寄存器(Rcvr_FIFO_trigger_level0)可以用来设置这个值,取值范围是1 ~ 63。I / O模式切换这里的模式切换即为内部框图中的 Mode Switch 模块,如下图所示。该模式由mode_reg0 [CHMODE]寄存器设置控制,总共分为四种模式,分别为:正常模式( Normal Mode)、自动回音模式( Automatic Echo Mode)、本地环回模式( Local Loopback Mode)和远程环回模式( Remote Loopback Mode)。正常模式( Normal Mode) :用于标准UART操作,就是发送接收功能。自动回音模式( Automatic Echo Mode) :Automatic Echo Mode模式在RxD上接收数据,模式开关将数据连接到接收端和UARTx_TxD。而PS的TXD端口的数据无法正常发出。本地环回模式( Local Loopback Mode) :本地环回模式不连接到RxD或TxD引脚,而是直接把PS发送的数据传输到PS的接收端。远程环回模式( Remote Loopback Mode) :远程环回模式将RxD信号连接到TxD信号。在这种模式下,控制器不能在TxD上发送任何内容,也不能在RxD上接收任何内容。UART启动顺序UART 的启动顺序如下:复位UART控制器,在PS进行系统复位时进行控制器的复位。配置 IO 引脚信号。配置 UART 参考时钟(可以保护默认)。配置控制器功能( UART 控制器初始化)。配置中断,通过中断来管理 RxFIFO 和 TxFIFO。配置串口模式控制(可选)。管理发送和接收的数据,采用轮询或中断驱动处理两种方式。配置控制器功能步骤在UART控制器中,控制器可以配置字符帧、波特率、FIFO触发级别、Rx超时机制,并使能控制器。所有步骤都必须在复位之后。配置控制器的功能步骤如下:配置 UART 数据帧格式。 数据位长度、停止位、校验方式、 IO 模式等。设置波特率。设置 RxFIFO 触发器等级,可以选择启用或禁用该功能。使能 UART 控制器。配置接收器的超时机制,可以选择启用或禁用该功能。发送数据步骤编写软件程序时,可以通过使用轮训和中断的方式控制RxFIFO和TxFIFO中的数据流。使用轮询方式发送数据顺序检查TxFIFO是否为空。直到uart.Channel sts rego[TEMPTY] =1,执行后续步骤。用数据填充TxFIFO。向uart.TX_RX_FIFO0寄存器写入64字节的数据。向TxFIFO写入数据。有两种方法:方法一: 可以等待 TxFIFO 为空之后再写入 64 个字节,即执行第 2 步;方法一: 可以检测 TxFIFO 是否写满,即不停的读取 TFUL 标志和写单个字节的数据。使用中断方法发送数据的顺序禁用 TxFIFO 空状态中断。写入数据到TxFIFO中。检测TxFIFO是否还有足够的空间容纳数据。重复2和3操作。使能中断。等待TxFIFO为空,然后从步骤 1 重新开始。接收数据步骤使用轮询方式发送数据顺序等待RxFIFO被填满到触发器级别。从RxFIFO读取数据。重复步骤2,直到FIFO为空。设置Rx超时中断状态位时清除。使用中断方法发送数据顺序使能中断。等待RxFIFO被填满到触发级别或Rx超时。从RxFIFO读取数据。重复步骤2和3,直到FIFO为空。如果设置了中断状态位,则清除中断状态位。系统框图这里仅仅使用了UART部分,所以可以利用前文的helloworld工程,不需要进行特殊的配置。通过串口的中断功能把发送的数据再接收到PS端进行一个回环显示。硬件平台搭建新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的uart资源将ZYNQ无用资源进行取消勾选:硬件系统构建完成如下:然后我们进行generate output product 然后生成HDL封装。这里只用到了UART,是MIO引脚,所以不需要进行管脚分配。点击导出硬件资源(可以不包含bit流文件,因为只用到了PS资源),接着launch SDK。SDK软件部分打开SDK后,新建application project。在system.mss中可以打开相关参考文档辅助设计。这里使用串口中断可以参考赛灵思提供的例程代码进行修改设计:这里导入uart_intr_example例程模板在main.c中输入以下代码:#include "xparameters.h" #include "stdio.h" #include "xuartps.h" #include "xuartps_hw.h" #include "xscugic.h" #define UART_0_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID #define INTR_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID #define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR XUartPs Uart_Inst; XScuGic ScuGic_Inst; int uart_init(); void intr_init(XScuGic *intr, XUartPs *uart); void UartIntr_Handler(void *call_back_ref); int main(){ //uart初试化函数 uart_init(); xil_printf("intr\n"); //中断初始化 intr_init(&ScuGic_Inst,&Uart_Inst); while(1); return 0; } //uart初始化 int uart_init(){ XUartPs_Config *UartPs_Cfg; int Status; //查找配置信息 UartPs_Cfg= XUartPs_LookupConfig(UART_0_DEVICE_ID); //对uart控制器进行初始化 XUartPs_CfgInitialize(&Uart_Inst, UartPs_Cfg, UartPs_Cfg->BaseAddress); //检测硬件搭建是否正确 Status = XUartPs_SelfTest(&Uart_Inst); if (Status != XST_SUCCESS) { return XST_FAILURE; } //设置波特率 XUartPs_SetBaudRate(&Uart_Inst,115200); //设置RXFIFO触发阈值 XUartPs_SetFifoThreshold(&Uart_Inst,1); //设置操作模式 XUartPs_SetOperMode(&Uart_Inst, XUARTPS_OPER_MODE_NORMAL); return XST_SUCCESS; } //中断初始化 void intr_init(XScuGic *intr, XUartPs *uart){ XScuGic_Config *IntcConfig; //中断控制器初始化 IntcConfig = XScuGic_LookupConfig(INTR_DEVICE_ID); XScuGic_CfgInitialize(intr,IntcConfig,IntcConfig->CpuBaseAddress); Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *)intr); Xil_ExceptionEnable(); //为中断设置中断处理函数 XScuGic_Connect(intr, UART_INT_IRQ_ID, (Xil_ExceptionHandler) UartIntr_Handler, (void *) uart); //设置触发类型 XUartPs_SetInterruptMask(uart, XUARTPS_IXR_RXOVR); //使能中断 XScuGic_Enable(intr, UART_INT_IRQ_ID); } //中断处理函数 void UartIntr_Handler(void *call_back_ref){ XUartPs *uartinst =(XUartPs *)call_back_ref; u32 read_data = 0; u32 intr_status; //读取中断ID寄存器 intr_status = XUartPs_ReadReg(uartinst->Config.BaseAddress, XUARTPS_IMR_OFFSET);//读取掩码 intr_status &= XUartPs_ReadReg(uartinst->Config.BaseAddress, XUARTPS_ISR_OFFSET);//读取状态 if(intr_status & (u32)XUARTPS_IXR_RXOVR){ read_data = XUartPs_RecvByte(XPAR_PS7_UART_0_BASEADDR);//接收发送的字节 XUartPs_WriteReg(uartinst->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);//清除中断状态 } //设置发送 XUartPs_SendByte(XPAR_PS7_UART_0_BASEADDR,read_data); }部分代码讲解在整体的代码设计中,代码思路如下:初始化UART控制器初始化UART中断编写中断服务函数对于初始化UART部分,在#include "xuartps.h" 头文件中可以找到很多配置的函数,调用即可对uart的波特率,中断触发阈值等参数进行设置。对于中断的配置可以类比GPIO的中断配置函数,先在初始化SGIC,然后进行异常初始化,完成注册异常并使能,接着需要连接SGIC和UART,最后设置中断类型并使能完成中断整体操作配置。在中断服务函数中,实现的功能为回环读写,所以要在中断函数中进行检测中断类型,当进入相应的中断时进行数据的读取,读取到上位机的发送数据,然后清除中断标志,最后把读到的数据进行发送。Reference正点原子视频教程UG585
学习内容本文主要关于SD卡相关内容,然后使用SD卡进行TXT文本读写,并用串口打印出读写数据。开发环境vivado 18.3&SDK,PYNQ-Z2开发板。SD卡简介SD卡(Secure Digital Card):安全数字卡,又叫安全数码卡。有体积小,容量大传输速度快,支持热插拔等特点。在ZYNQ开发板上这里是使用的是TF卡,也就是小卡(micro sd卡)又称TF卡。SD卡是在 MMC 卡( Multimedia Card,多媒体卡)的基础上发展而来,主要增加了两个特色:更高的安全性和更快的读写速度。SD卡的接口与MMC卡是兼容的,支持SD卡的接口大多支持MMC卡。下表为不同类型的 SD 卡采用的协议规范、容量等级及支持的文件系统。SD卡类型协议规范容量等级磁盘格式SDSCSD1.0上限为2GBFAT12, FAT16SDHCSD2.02GB至32GBFAT32SDXCSD3.032GB至2TB( 2048GBexFAT不同协议规范的 SD 卡有着不同速度等级的表示方法。在 SD1.0 规范中(现已不用),使用“ X”表示不同的速度等级;在 SD2.0 规范中,普通卡和高速卡的速率定义为Class2、Class4、Class6 和Class10 四个等级; SD3.0 规范(又称为超高速卡)使用 UHS( Ultra High Speed)表示不同的速度等级。 不同等级的读写速度和应用如下图所示:SD 卡共有 9 个引脚线, 共支持三种传输模式:SPI模式(独立序列输入和序列输出),1位SD模式 (独立指令和数据通道,独有的传输格式), 4位SD模式 (使用额外的针脚以及某些重新设置的针脚。支持四位宽的并行传输)。具体的引脚功能定义如下表所示:MicroSD 卡接口定义以及各引脚功能说明如下图所示。ZYNQ 内部集成了两个 SD 卡控制器,并且 Xilinx SDK 的 standalone 已经移植好了 FATFS( SDK 软件中叫做 xilffs)文件系统,因此在 SDK 中添加 xilffs 库后, 就可以在程序中使用 FATFS 中的 API 函数来操作 SD 卡。SD 卡控制器( SD/SDIO Controller)SD/SDIO控制器与SDIO设备、SD存储卡和MMC卡通信,最多有4条数据线。SD接口可以使用一条 (dat0)或四条(dat0- dat3)行进行数据传输。SDIO接口可以通过MIO多路复用到MIO引脚或通过PL中的EMIO进行lO引脚分配。ZYNQ 中的 SD 卡控制器符合 SD2.0 协议规范, 接口兼容 eMMC、 MMC3.31、 SDIO2.0、 SD2.0、 SPI,支持 SDHC、 SDHS 器件。 SD 卡控制器支持 SDMA(单操作 DMA)、 ADMA1( 4K 边界限制 DMA)和ADMA2( 在 32 位系统中允许任何位置和任意大小)。SD/SDIO控制器由ARM处理器通过AHB总线访问。该控制器还包括一个DMA单元与内部FIFO以满足吞吐量要求。SD/SDIO控制器的框图如下:SD控制器使用两个512字节深度的双端口FIFO执行写和读操作。SD 控制器读写通道采用独立的 512 字节深度的双缓冲 FIFO 执行读和写操作。在写操作时,处理器向其中一个 FIFO 写数据,将另一个 FIFO 的数据写到 SD 总线;在读操作时, SD 总线上的数据向其中一个FIFO 写数据,处理器将数据从另一个 FIFO 读出数据。 SD 卡控制器通过双缓冲机制以保证最大带宽。FATFS文件系统负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。即在磁盘上组织文件的方法。常见的文件系统有FAT/FATFS(FATFS 是一个完全开源免费的 FAT 文件系统模块)、NTFS(基于安全性的文件系统,是Windows NT采用的独特的文件系统结构)、CDFS(CDFS是大部分光盘的文件系统)、exFAT(扩展性的文件系统)。FATFS是一个可以裁剪的文件系统,专门为小型的嵌入式系统而设计。它完全用标准 C语言编写,结构清晰,代码量小,文件系统和IO底层分开,所以具有良好的硬件平台独立性,可以很方便的移植到各种嵌入式处理器中,也很适合新手进行入门学习。支持最多10个逻辑盘符和两级文件夹;可以支持FAT12、FAT16和FAT32,支持多个存储媒介。FATFS 模块的层次结构如下:最顶层:应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用 FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上读/写文件那样简单。中间层 :FATFS 模块,实现了 FAT 文件读/写协议。使用者一般不用修改,使用时将头文件直接包含进去即可。底层接口,包括存储媒介读/写接口( disk I/O)和供给文件创建修改时间的实时时钟。需要根据平台和存储介质编写移植代码。关于文件系统的函数API使用可以通过下面这个网址进行学习入门:FATFS 学习网址Xilinx SDK 的standalone 已经移植好了 FATFS 文件系统,因此在 SDK 中添加 xilffs 库后, 就可以在程序中使用 FATFS 中的 API 函数来操作 SD 卡。系统框图本次工程只使用了PS端的资源,通过 Xilinx SDK 自带的 FATFS 库,完成对 SD卡中 TXT 文本读写的功能,并将读写测试结果通过串口打印出来。系统框图如下:硬件平台搭建新建工程,创建 block design。添加ZYNQ7 IP,对zynq进行初始化配置,勾选sd,uart资源,设置BANK的电平,取消勾选多余资源,点击OK,完成硬件设计。如下图:然后我们进行generate output product 然后生成HDL封装。这里没有进行使用PL资源,也不需要进行综合布局,在导出硬件时也不用包含bit流文件。SDK软件部分打开SDK后,新建application project。选中工程右击选中设置板载支持文件模式。这里选择xilffs模式,点击standalone下的xilffs,可以对文件系统进行配置,这里可以使能长文件名有效,改变勾选为true。完成配置后点击ok。在main.c中输入以下代码:#include "stdio.h" #include "xparameters.h" #include "ff.h" #include "string.h" #define FILE_NAME "vuko.txt" char str_wr[100]= "this is a sd read and write test~"; char str_rd[100]=""; FATFS fs; void sd_mount(); void sd_write_data(char wr_dat[], u32 wr_len); void sd_read_data(char rd_dat[], u32 rd_len); int main(){ u32 len; sd_mount(); len=strlen(str_wr); //写数据 sd_write_data(str_wr,len); //读数据 sd_read_data(str_rd,len); if(strcmp(str_rd,str_wr)==0){ printf("%s\n",str_rd); printf("sd card test success!\n"); } else printf("sd card test fail!\n"); return 0; } //挂载sd卡 void sd_mount(){ FRESULT status; BYTE work[FF_MAX_SS]; //挂载sd卡,注册文件系统对象 status=f_mount(&fs,"",1); if(status != FR_OK){ printf("%d\n",status); printf("It isn't FAT format\n"); f_mkfs("",FM_FAT32,0,work,sizeof work); f_mount(&fs,"",1); } } //写数据 void sd_write_data(char wr_dat[], u32 wr_len){ FIL fil; UINT bw; //创建或者打开文件 f_open(&fil,FILE_NAME,FA_CREATE_ALWAYS | FA_WRITE | FA_READ); //移动读写指针 f_lseek(&fil, 0); //写数据 f_write(&fil,wr_dat,wr_len,&bw); //关闭文件 f_close(&fil); } //读数据 void sd_read_data(char rd_dat[], u32 rd_len){ FIL fil; UINT br; //创建或者打开文件 f_open(&fil,FILE_NAME,FA_READ); //移动读写指针 f_lseek(&fil, 0); //读取数据 f_read(&fil,rd_dat,rd_len,&br); //关闭文件 f_close(&fil); }整体代码思路该工程代码主要实现三个函数,分别是挂载SD卡,读取SD卡和写入SD卡。在main函数中首先完成对SD卡的挂载,然后向指定文件内写入的字符串数据,通过SD卡读取函数把SD卡中指定文件的数据读到数组中,完成数据的读取后将写入的字符串数据和读取出来的字符串数据进行对比,对比一致后,用串口打印测试成功的标识。部分代码讲解对于具体的读写函数的使用,我们可以进行参考下面这个网址进行学习:FATFS 学习网址可以参考对应的读写函数的使用示例进行辅助开发设计。对于写入SD卡函数编写:首先我们要进行创建或者打开文件,这里引入文件的结构体,使用f_open()函数进行打开,将读写的指针移动到到指定位置,这里使用f_lseek(&fil, 0);把光标移动到初始位置。然后使用写入函数进行写入数据f_write(&fil,wr_dat,wr_len,&bw);完成写入后,关闭文件f_close(&fil);读取SD卡函数和写入的思路类似,对于挂载函数来说,使用status=f_mount(&fs,"",1);注册文件系统对象,如果读取返回值不是FAT文件系统,则使用f_mkfs("",FM_FAT32,0,work,sizeof work); 把SD卡进行初始化。运行结果串口数据:Reference正点原子ZYNQ开发视频xilinx UG585
学习内容本文主要介绍关于AXI DMA的IP核相关内容。DMA简介DMA(Direct Memory Access,直接存储器访问)是计算机中一种内存访问技术。它允许某些硬件子系统可以独立地直接读写系统内存,而不需中央处理器( CPU)介入处理。DMA 是用硬件实现存储器与存储器之间或存储器与 I/O 设备之间直接进行高速数据传输。 使用 DMA时, CPU 向 DMA 控制器发出一个存储传输请求, 这样当 DMA 控制器在传输的时候, CPU 执行其它操作,传输操作完成时 DMA 以中断的方式通知 CPU。为了发起传输事务, DMA 控制器必须得到以下数据:源地址 — 数据被读出的地址。目的地址 — 数据被写入的地址。传输长度 — 应被传输的字节数。DMA 存储传输的过程如下:配置用 DMA 传输数据到存储器,处理器发出操作DMA的指令/代码。DMA 控制器把数据从外设传输到存储器或从存储器到存储器,而让 CPU 腾出手来做其它操作。数据传输完成后,向 CPU 发出一个中断信号来通知它 DMA 传输可以关闭了。AXI DMA IP简介ZYNQ 提供了两种 DMA,一种是集成在 PS 中的硬核 DMA,另一种是 PL 中使用的软核 AXI DMA IP。AXI DMA IP提供内存和AX14-Stream接口的目标外设之间的高带宽直接内存访问。它可以选择scatter/gather模式进行数据的传输搬移,可将CPU从数据搬运任务中解放出来。功能框图IP的功能框图如下:由框图可知,通过AXI4-Lite从接口访问初始化、状态并控制和管理寄存器。MM2S接口完成MemoryMap to Stream 的转换,即存储器映射转换到AXI4-Stream接口转换。同理S2MM接口完成Stream to MemoryMap的转换,即AXI4-Stream接口转换到存储器映射转换。图中标注的三个接口只有在S/G模式下才会生成那三个接口进行IP的控制读写以及数据交互。典型设计解读在IP的指导手册中有下图这样的一个控制AXI DMA IP的设计系统。在图中我们可以看到,对于处理器(microblaze),只需要使用少量的控制指令,即可完成高速的多数据传输。处理器(microblaze)通过interconnect互联模块连接到AXI4-Lite接口,进行IP的寄存器配置。在AXI DMA IP的完成数据传输时,通过MM2S_IntrOut,S2MM_IntrOut指示数据传输完成,并发送给处理器进行进一步操作。在图中的系统AXI DMA IP使能了S/G模式,该存储器映射接口通过互联模块连接到DDR控制端口,ctrStrm(控制Stream)、StatusStrm(判断Stream状态)和SG R/W是在SG模式下使用的三个端口。剩下的MM2S和S2MM进行数据的交互和传输。时钟要求AXI DMA IP,对于不同芯片信号的不同速度、等级下的最大时钟频率有不同的要求。在进行设计开发时,要根据设计芯片的具体型号满足时序要求,避免出现时序伪例的现象。对于时钟的输入端口,有以下端口:m_axi_mm2s_aclk for MM2S interfacem_axi_s2mm_aclk for S2MM interfaces_axi_lite_aclk for AXI4-Lite control interfacem_axi_sg_clk for Scatter Gather InterfaceAXI DMA提供两种时钟模式,同步模式和异步模式。同步模式: 所有的逻辑都连接在一个单一的时钟源,m_axi_mm2s_aclk、m_axi_s2mm_aclk、m_axi_sg_clk必须在一个相同的时钟源下,s_axi_lite_aclk可以连接到更低的时钟。异步模式: 所有逻辑都可以是异步的,但是s_axi_lite_aclk必须小于或等于m_axi_sg_aclk的时钟频率, m_axi_sg_aclk必须小于或等于m_axi_mm2s_aclk或m_axi_s2mm_aclk的时钟。在异步模式下,这四类时钟信号所影响到的接口信号,(在SG模式下和单一传输模式)如下图所示:复位axi_resetn信号需要持续至少16个最慢的时钟周期,并且需要和s_axi_lite_aclk保持同步。编程指导在AXI DMA IP中,一共有三个模式可以进行编程配置:Direct Register Mode (Simple DMA)Scatter/Gather ModeCyclic DMA ModeDirect Register Mode (Simple DMA)简单DMA模式,该模式下,提供了在MM2S和S2MM通道上进行简单的DMA传输的配置。只需要较少的FPGA资源,通过访问DMACR、源地址或者目的地址和长度寄存器发起DMA的传输。当传输完成后,如果使能了产生中断输出,那么DMASR寄存器相关联的通道位会有效,即产生中断输出。DMA的MM2S通道启动顺序:设置运行/停止位为1 (MM2S_DMACR.RS=1),启动MM2S通道运行。 停止位(DMASR.Halted)应该取消上拉,表示MM2S通道正在运行。可以通过向MM2S_DMACR.IOC_IrqEn和MM2S_DMACR.Err_IrqEn写入1来启用中断。 当AXI DMA配置为Direct Register 模式时,延迟中断、延迟计数和阈值计数不被使用。向MM2S_SA寄存器写入有效的源地址。 如果AXI DMA配置的地址空间大于32,则对MM2S_SA MSB寄存器进行编程。如果AXI DMA没有配置为数据重新对齐,则必须对齐有效地址,否则将出现未定义的结果。认为对齐是或非对齐是基于数据流的宽度。当在Micro 模式下配置AXI DMA时,要进行指定正确的地址。在Micro 模式下配置AXI DMA时,是不关心4K边界的。例如,如果内存映射数据宽度= 32,则如果数据位于字偏移(32位偏移),即Ox0、0x4、0x8、OxC,等等,则数据对齐。如果DRE使能,并且数据宽度小于128,那么源地址可以是任何字节偏移量。在MM2S LENGTH寄存器中写入要传输的字节数。 写为零的值没有任何效果。如果该值不为零,则MM2S LENGTH决定了从MM2S AXI4接口读取并输出到MM2S AXI4- stream接口传输的数据个数。MM2S_LENGTH寄存器必须最后写入。 所有其他MM2S寄存器都可以按任意顺序写入。在Micro DMA下,此值不能超过[突发长度*(内存映射数据宽度)/8]。DMA的S2MM通道启动顺序:(类似MM2S通道启动顺序)开启/使能S2MM通道如果需要,可以使能中断写一个有效的源地址到S2MM_SA寄存器,如果没有使能DRE功能,在指定起始地址时,要注意字节地址对齐,哪些地址对齐不对齐取决于Stream流的数据位宽。写传输的字节数到LENGTH寄存器。一个长度为0的值是无效的,而一个非0的值将决定存储器映射到Stream流的数据个数。S2MM_LENGTH寄存器必须最后写入。Scatter/Gather ModeS/G模式下,AXI DMA操作需要一个存储DMA操作列表的内存驻留数据结构。这个指令列表被组织成所谓描述符链。每个描述符都有一个指向下一个要处理的描述符的指针。链中的最后一个描述符指向链中的第一个描述符。S/G模式允许一个包被多个描述符描述。此特性的典型用途是允许从内存中的一个位置存储或获取头,并从另一个位置存储有效数据。利用这一点的程序可以提高吞吐量。利用帧起始位(TXSOF)和帧结束位(TXEOF)来描述缓冲区描述符链中的数据包。当DMA获取一个设置了TXSOF位的描述符时,将触发包的开始。包继续获取后续的描述符,直到获取一个设置了TXEOF位的描述符。在接收(S2MM)通道上,当数据包开始被接收时,AXI DMA用RXSOF标记描述符,指示软件与此描述符相关联的数据缓冲区,包含数据包的开始。如果正在接收的包的字节数比描述符中指定的更长,则使用下一个描述符缓冲区来存储接收包的其余部分。这个获取和存储过程继续进行,直到整个接收包被传输完毕。接收包结束时正在处理的描述符被AXI DMA标记为RXEOF=1。这向软件表明,与此描述符相关联的缓冲区包含包的结尾。每个描述符的状态字段包含为特定描述符实际传输的字节数。该软件可以通过从RXSOF描述符遍历描述符链到RXEOF描述符来确定接收包传输的总字节数。Scatter Gather继续获取一个额外的描述符并存储。这个过程在很大程度上改善了DMA性能。S/G模式从设置控制寄存器和描述符指针开始。简单来说,就是把传输的基本参数存储到内存中;这些参数被称为BD(Buffer Descriptor),在工作时,通过SG的接口进行加载和更新BD的状态从而进行对指定的位置的数据进行读写操作。MM2S通道的DMA操作通过以下顺序建立和启动:将起始描述符的地址写入当前描述符寄存器。 如果AXI DMA被配置为大于32的地址空间,那么也对当前描述符的MSB的32位进行编程。设置运行/停止位为1 (MM2S_DMACR.RS=1),启动MM2S通道运行。 停止位(DMASR.Halted)应该取消上拉,表示MM2S通道正在运行。可以通过向MM2S_DMACR.IOC_IrqEn和MM2S_DMACR.Err_IrqEn写入1来启用中断。向尾部描述符寄存器写入一个有效地址。 如果AXI DMA被配置为大于32的地址空间,那么也对尾部描述符的MSB 32位进行编程。写入尾描述符寄存器将触发DMA开始从内存中获取描述符。 在多通道配置的情况下,当数据包到达S2MM通道时开始获取描述符。对获取的描述符进行处理,数据从内存中读取,然后输出到MM2S流通道。S2MM通道的DMA操作通过以下顺序建立和启动:将起始描述符的地址写入当前描述符寄存器。 如果AXI DMA被配置为大于32的地址空间,那么也对当前描述符的MSB 32位进行编程。设置运行/停止位为1 (S2MM_DMACR.RS=1),启动S2MM通道运行 停止位(DMASR.Halted)应该取消上拉,表示MM2S通道正在运行。可以通过向MM2S_DMACR.IOC_IrqEn和MM2S_DMACR.Err_IrqEn写入1来启用中断。向尾部描述符寄存器写入一个有效地址。 如果AXI DMA被配置为大于32的地址空间,那么也对当前描述符的MSB 32位进行编程。写入尾描述符寄存器将触发DMA开始从内存中获取描述符。对获取的描述符进行处理,并将从S2MM流通道接收的任何数据写入内存。Cyclic DMA Mode通过对缓冲区描述符(BD)链设置进行某些更改,AXI DMA可以以循环模式运行。在循环模式下,DMA不中断地获取和处理相同的BDs。DMA继续获取和处理,直到停止或重置为止。为了使循环运行,BD链的设置如下图所示:在这个设置中,Tail BD指向第一个BD, Tail Descriptor(尾描述寄存器)没有任何用途,仅用于触发DMA。遵循在S/G模式中提到的相同编程顺序。确保控制寄存器中的循环位已设置。在编写了Tail Descriptor寄存器之后,DMA开始获取和处理BDs(以环形方式设置),直到DMA停止或重置。ReferencePG021_axi_dma正点原子开发视频
学习内容本文首先进行自定义IP的AXI总线IP的设计,然后在SDK下编写代码进行DDR的读写数据的测试。开发环境vivado 18.3&SDKPYNQ-Z2开发板系统框图首先对本次工程进行简要说明:本次工程使用AXI-Full接口的IP进行DDR的读写测试。在我们的DDR读写IP中,我们把读写完成和读写错误信号关联到PL端的LED上,用于指示DDR读写IP的读写运行状态。然后使用PL部分消抖处理后的按键进行启动AXI总线工作,控制数据写入。通过AXI互联模块连接到AXI_HP0端口,由PS端口进行数据的读取操作,并通过串口进行读写数据的监控。自定义IP设计首先打开Vivado软件,在Tasks这里选择New IP lacation点击next,对IP的信息进行设置,这里我们使用默认配置即可。设置好我们IP要保存的位置。点击Tools中的创建和封装新的IP选项,点击NEXT ,选择我们的封装类型。因为这里我们是直接进行打开IP设计的界面,前两个选项是可以在我们的vivado当前工程下面进行封装设计,这里我们只进行了IP设计没有建立工程,所以前两个选项是无法选中的。我们也可以通过工程界面,进入点击Tools中的创建和封装新的IP选项。这里是用DDR读写IP来做主机,控制数据写入,PS作为从机进行读取IP中写入的数据。可以直接选中进行编辑IP,用户可以根据自己的设计进行修改编辑IP的功能,这里没有对IP进行修改处理,所以可以直接保存选择第一个添加到IP库中即可。若修改相应的逻辑功能打开IP,在对应位置编辑添加代码即可。添加完成综合后对IP进行重新打包。DDR读写IP设计完成,创建的 IP 核将通过 AXI4 Master 端口向 Slave 端指定的 4K 存储空间中连续写入 1024 个数据, 写入的数值从 1 累加到 1024, 每个数据占 32bit。然后进行硬件平台的构建。硬件平台构建首先,添加ZYNQ7 IP核,以及添加已经完成设计的ddr读写IP核。添加用户自定义IP用户自定义的IP可通过以下步骤完成添加。点击Settings,在project settings选择IP,依次点击,在IP库那里点击加号,把对应的IP目录文件夹添加后,点击OK或者Apply即可完成添加,在IP库中就可以找到用户设计的IP。完成IP和ZYNQ7 IP的导入后,如下图:双击打开zynq删除多余的接口,这里只需要保留uart,并打开Slave HP0端口、时钟、复位端口。配置完,选择自动连接接口,完成部分连接设计。整体设计图如下,
学习内容近期设计需要用到AXI总线的IP,所以就对应常用的IP进行简要的说明,本文主要对AXI互联IP进行介绍。基础架构IP基础的IP是用于帮助组装系统的构建块。基础架构IP往往是一个通用IP,它使用通用的AXI4接口在系统中移动或转换数据,而不解释数据。这些基础的IP各自有自己的常用的功能,下面列举出一部分AXI接口的基础构架IP。° AXI Register slices (for pipelining)用于流水线操作。° AXI FIFOs (for buffering/clock conversion)用于缓存和时钟转换。° AXI Interconnect IP and AXI SmartConnect IP (for connecting memory-mapped IP together)用于连接存储器映射的IP互连。° AXI Direct Memory Access (DMA) engines (for memory-mapped to stream conversion)用于存储器映射和数据流接口的转换。° AXI Performance Monitors and Protocol Checkers (for analysis and debug)用于分析仿真。° AXI Verification IP (for simulation-based verification and performance analysis) 用于仿真验证。Xilinx AXI SmartConnect IP and AXI Interconnect IP(AXI互联IP)介绍Xilinx AXI Interconnect IP和AXI SmartConnect IP都可以将一个或多个AXI存储器映射的主设备连接到一个或多个存储器映射的从设备。而使用AXI SmartConnect IP,更紧密地集成到Vivado设计环境中,用户以最小的用户干预自动配置和适应已连接的AXI主从IP。AXI互联IP(AXI SmartConnect IP and AXI Interconnect IP)可以用于所有的存储器映射设计中。在某些情况下,对于高带宽应用程序,使用SmartConnect IP可以提供更好的优化。AXI SmartConnect IP通过综合针对重要接口进行优化的低区域自定义互连,在低延迟下提供最大的系统吞吐量。AXI Interconnect IP(axi_interconnect)可以将一个或多个AXI存储器映射的主设备连接到一个或多个存储器映射的从设备。Interconnect 相对于SmartConnect IP更符合来自ARM的AMBA AXI4规范,包括AXI4-Lite 接口。AXI Interconnect IP和AXI SmartConnect IP仅用于存储器映射传输。AXI4-Stream传输不适用。但可以使用AXI4-Stream Interconnect IP (axis_interconnect)。带有AXI4-Stream接口的IP通常彼此连接到DMA IP或者AXI4-Stream Interconnect IP上。综上:对于中到高性能设计,推荐使用AXI SmartConnect IP,因为它在面积和时间上提供了更好的向上扩展。对于低性能(AX14-Lite)或中小型复杂性设计,AXI Interconnect IP可能更有效的面积。AXI Interconnect IP使用方式对于互联IP的使用,在xilinx的指导手册中提到了下述四种方式。1. Conversion Only(仅转换操作)当一个主设备连接到一个从设备时,AXI Interconnect IP可以执行各种转换和流水线功能。这些操作如下述:数据宽度转换时钟速率转换AXI4-Lite从机自适配AXI4-3从机自适配流水线,如寄存器或数据通道FIFO操作。在这些情况下,AXI Interconnect IP不包含仲裁、解码或路由等逻辑。可能会导致延迟,延迟大小取决于正在执行的转换类型。下图显示了一个转换的示例:2. N-to-1 InterconnectAXI Interconnect IP的一个常见退化配置(或者我翻译为简化配置)是多个主设备为访问一个从设备(通常是一个内存控制器)进行仲裁。在这些情况下,地址解码逻辑可能是不必要的,并且在AXI Interconnect IP被省略(除非需要地址范围验证)。在这种配置下,还可以执行数据宽度和时钟速率 转换等转换功能。N-to-1 AXI互联示例如下图所示:3. 1-to-N InterconnectAXI Interconnect IP的另一种退化配置(简化配置)是当一个主设备(通常是一个处理器)访问多个内存映射的从外围设备时。在这些情况下,仲裁(在地址和写数据路径) 不执行。1 - N互联示例如下图所示:4. N-to-M Interconnect (Sparse Crossbar Mode)AXI Interconnect的N-to-M用例采用共享地址多数据(SAMD)拓扑,稀疏数据交叉连接,单线程写和读地址仲裁,如下图所示:下图展示了稀疏交叉写和读数据路径:根据配置的稀疏连接映射,并行写和读数据通道将每个SI插槽(连接到左边的AXI主机上)连接到它可以访问的所有MI插槽(连接到右边的AXI从机上)。当多个源有数据要发送到不同的目的地时,只要满足AXI排序规则,数据传输就可以独立并发地进行。在所有SI槽(如果> 1)中的写地址通道馈送到一个中心地址仲裁器,它一次授予对一个SI槽的访问权,对于读地址通道也是如此。AXI4-Stream Interconnect Core IP介绍AXI4-Stream Interconnect Core IP(axis_interconnect)将一个或多个AXI4-Stream主设备连接到一个或多个AXI4-Stream从设备。AXI4-Stream Interconnect Core IP 仅用于AXI4-Stream 传输;AXI4存储器映射传输不适用。AXI4-Stream Interconnect Core 内部框图AXI4-Stream Interconnect Core IP由SI、MI和包括它们之间的AXI通道的功能单元组成。SI接受来自连接的主设备的事务请求。MI向从设备发送事务。在中心是交换机,它仲裁和路由连接到SI和MI的各种设备之间的通信。AXI4-Stream Interconnect Core IP还包括位于交换机和每个SI和MI接口之间的其他功能单元,可选择性地执行各种转换和存储功能。该开关有效地将AXI4-Stream Interconnect Core IP从SI相关功能单元(SI半球)和MI相关单元(MI半球)中间分开。这个架构类似于AXI Interconnect IP的架构。AXI4-Stream Interconnect IP使用方式AXI4-Stream Interconnect IP将一个或多个AXI4-Stream主设备连接到一个或多个AXI4-Stream从设备。对于AXI4-Stream Interconnect IP,主要有两种使用方式:流数据路由和交换流多路复用和去多路复用Streaming Data Routing and Switching (Crossbar Mode)流数据路由和交换ax14流互连可以实现N × M全交叉开关,如下图所示。它支持从端仲裁,能够在N个主服务器和M个从服务器之间并行数据传输。解码器和仲裁者服务于主从之间的路由数据传输交互。Stream Multiplexing and De-multiplexing(流多路复用和去多路复用)你可以在Nx1配置中将AXI4-Stream Interconnect IP配置为一起多路传输流,然后配置为1xM来解多路传输流。使用多路复用和多路复用解复用来创建多通道流,其中较小数量的导线可以携带来自多个主从的共享流量。例如,在下面的图中,AX14-Stream互连与AXI虚拟FIFO控制器一起用于从多个端点主从复用和解复用多个流。Reference正点原子讲解视频UG1037Vuko公众号同步更新~欢迎大家关注我的公众号。如果需要工程微信后台留言即可~
写在前面作为一个大四的老油条,两年全国电子设计竞赛参赛省一选手,本科的电赛是没机会参加了,不过手头资料确实不少,刚开始我参加电赛也是机缘巧合,能获奖更是非常幸运,不过怎么说,一分耕耘一分收获,就算大家开始什么都不会,仅仅是听说过这个比赛而突发奇想去参加,只要坚持训练也是可能拿到令你满意的奖项的。在本文的末尾将给出自己调试搜集的一些程序还有资料,希望对准备参赛的同学有所帮助。竞赛介绍电赛是单数年是国赛年双数年是省赛年,3人组队,4天3夜,在电赛之前一般每个学校都会有校赛等等的选拔赛,总的来说电赛分为这几类电源类控制类模拟信号类(信号的产生或测量)仪器仪表综合类(多为通讯,但也可以应用其他方法)注:其中相互参杂情况较多,题目的出现很有很能涉及上面的两类以上电源类需要掌握的东西大致有这几种:AC-DC、DC-AC、DC-DC电路、功率器件、充电管理电路、均流、恒流电路、相关电参数的测量电路、各类保护电路、PFC、单片机输出PWM\SPWM;单片机基础等控制类需要掌握的东西大致有这几种:单片机机及其基础外设、AD、DA、各类传感器、电机、控制算法,PID等、对于机械结构需要有一定的了解等模拟信号类各类放大器、模拟滤波器、震荡电路、阻抗变换电路等基本电路这题难点在于基本放大器的熟悉,能够准确选型。高频、宽带、小信号、高增益等产生特定的信号,应用ADC、DAC、各种关于信号的模块仪器仪表类需要掌握的东西大致有这几种:电子测量基本原理、各个电参数和元器件参数的测量、高速AD、前端信号处理电路、衰减、放大、阻抗变换、混频 等、DFT、FFT等算法综合类其中通信类近年来基本都一题以信号的调制解调为核心、调制解调电路、通信的常用器件:乘法器、PLL等、阻抗变换和阻抗匹配电路一般做题,我们就在模拟类和仪器仪表类里面进行选择,这两个方向的特点就是都基本要用到模拟部分和数字部分(模拟部分负责通常是小信号放大,滤波,数字部分用于指标的测量验证),这类题在比赛的时候不容易拿分,(相对于控制类,有的年份比较简单的控制类,80分都不一定拿到奖),但是比较稳定(一般设计好的电路封箱后不会有什么影响,如果是控制类的话,周围的光线环境有变化,都会影响到传感器的精度,分数起伏大)。不过大家还是要根据兴趣选择适合自己的赛题,把自己会的东西发挥到极致。准备训练一般要参加当年的比赛2-3月份就要有所准备,对于我个人来说,我主要负责的是数字部分,编写单片机测量部分的程序,模拟部分的准备我就不过多展开。大家要合理利用网上的资源,多关注点论坛、培训网站,看看别人的作品的设计方法和思路,针对自己负责的部分进行合理训练。电赛培训网这里是电赛官方提供的学习培训的网站,大家看到好多资料也可以进行学习交流。模拟部分如果没学过模电的话,要在2-3月份之前完成模电部分的学习,并能动手设计,焊接过电路。主要针对学习要熟练掌握:放大器(各种放大器的设计,通常最常用的是运算放大器)、滤波器、移位电路、振荡电路、衰减、滤波、射极跟随器。数字部分我本人的学习历程:51单片机430单片机32单片机FPGA个人建议要根据自己的时间合理分配学习时间和内容,没必要把各个种类是单片机都接触,但是一定要有一种能够熟练应用的,以免比赛时候遇到问题没法解决对于信号类的程序准备主要针对下面几个方面:测信号的频率、幅度测量信号的占空比、相位差测量双路信号的频率、幅度画波形(包括信号波形、频谱图)信号的发生(多用DDS)测量参数的显示(一般用TFT屏或者OLED屏幕)学习建议在学习单片机的过程中主要针对以下几个模块进行:ADC、DAC、时钟、定时器、中断、gpio操作、iic、uart、spi的简单操作,在熟悉了开发流程后,可以有选择的进行上述这些部分的学习,对于应对信号类程序的编写很有帮助。对于单片机部分的准备,信号类的多半就是准备上述的那些程序,通常题目中会有精度的要求,针对精度的大小的需求,我们可以选择一款单片机和FPGA进行相互辅助开发,FPGA参与测量、产生信号等,将数据传输给单片机方便进行拟合和调试。资料分享说了那么多,重头戏来了。针对模拟和数字部分我将会给出网盘资料链接程序设计的码云的仓库链接提供大家学习,每一部分进行一个简单的说明,方便大家查找下载。电赛参考报告这个是最没用的,因为在比赛中基本不会看,这里给出来17年的赛题的报告也只是提供给大家进行学习报告的书写格式,有就行,主要看作品。模拟部分码云上的资料主要存放了一些常用电路的仿真设计,有部分的调试效果不好或者错误请见谅,另外里面包含了电赛综合测评的往年赛题的仿真参考,主要就是我上面提到的那些:放大器、滤波器、移位电路、振荡电路、衰减、滤波、射极跟随器。MSP430部分网盘中资料给出了MSP149的一些学习教程(当时为了学习,花了好几十买的视频,因为已经有开发板,找不到免费好一点的资源)还有一个大佬编写的库函数包,一些430的开发手册啥的,建议学会F149或者F169再进行F5529的学习,(如果没有51基础的话)MSP430学习视频+资料提取码:kekl链接:https://pan.baidu.com/s/1dfir9VUxKxfNklX4K2wBVw 提取码:kekl复制这段内容后打开百度网盘手机App,操作更方便哦码云上面的程序大致也就是测频测幅,因为开发板型号参差不齐,啥都有我主要是用F149和F5529用TFT屏幕做显示,里面还有一些之前学长跟其他老师写的程序(他喜欢用G2和lcd12864做显示)FPGA部分FPGA部分的学习可以根据给出的小梅哥教程简单学习开发流程,能够调试测频模块、测量频率、相位差、占空比就行了,简单的通信可以学习uart iic spiFPGA学习教程-小梅哥提取码:oxwu链接:https://pan.baidu.com/s/1b0Jwws_Qi4dZCXaY6WwTKA提取码:oxwu 复制这段内容后打开百度网盘手机App,操作更方便哦这里电赛的程序也没多少,主要就做了测频测幅,大部分用的产生和测量的模块都是高速的AD DA,建议大家可以选择一个模块多一点的FPGA店家,买个FPGA最小系统板就行(有时候比赛有要求,上面不能有多余外设)32部分32部分主要使用的就是内部DA DA TIMER的资源进行程序开发,前面已经对要学习模块有了一些说明这里不再展开32部分程序资料(只有码云部分)码云电赛程序资料库码云部分的整体连接如下,希望大家好好利用,不浪费东西。(爆肝5小时整理)码云电赛程序资料库包含430、32、FPGA、模拟电路仿真图等资料模块资料模块资料高速AD DA提取码:oxwu链接:https://pan.baidu.com/s/1xaBWldFbXcKskWlh03ILyA提取码:1pkv复制这段内容后打开百度网盘手机App,操作更方便哦写在最后电赛对于我们电子信息相关的专业的学生真的是一次很有意义的体验,四天三夜,大家各自忙于自己负责的部分开发调试,废寝忘食,乐在其中,提供资源也是希望大家在开源中有所收获,有所进步,这才是开源学习交流的意义和精髓。望大家在交流中学习并有所成果,顺利取得电赛佳绩。觉得有用的话希望能一键三连支持一手,码字不易,keep going!
写在前面本文记录总结之前在FPGA分享会中学到的关于赛灵思系列的复位功能操作的设计以及建议,进行分析总结。学习FPGA入门,有一说一我的领路人大部分都是用的intel的芯片下板验证,当时也不理解为啥子要这样复位,写抄就完了甚至感觉还挺有道理哈哈哈哈。但是现在随着学习的深入我渐渐得发现复位还真是门学问。话不多说正文见。总述为什么复位?使用全局复位有利于我们仿真,所有的寄存器都是有初始值的,也可以在任意时刻让你的寄存器恢复初值,所以验证工程师很喜欢这样的设计,但是Xilinx建议的是尽量避免使用全局复位。复位的基本目的是使器件强制进入到可以稳定工作的确定状态。这避免了器件在上电后进入到随机状态导致进入到无法判断的状态(也就是死机了)。在实际设计过程中,设计者必须选择最适合于设计本身的复位方式。xilinx为什么不推荐异步复位?异步重置将不会被添加到数据路径。所以数据路径对于时序分析来说是干净的。电路可以复位与或没有时钟的情况下复位电路。不需要综合指令导致亚稳态异步复位比预期的要复杂得多,异步复位与寄存器工作时钟域没有一定的相位关系,很难确保所有寄存器同时从状态中释放出来。因为内部复位信号的偏差,寄存器A将在当前时钟周期内从复位中释放,C将在下一个时钟周期释放,B难以定义,甚至可能导致亚稳态。简而言之,***亚稳态亚稳态亚稳态!!!***浪费布线资源异步复位信号会占用大量的布线资源是高速设计的必要条件,但我们不能看到它在源代码-占用太多布线资源将减少其他连接的自由。-可能降低系统性能潜在地需要一个更高的设备速度等级。-增加布线时间。浪费Slice资源-有和没有异步复位的寄存器不能被包装在一个Slice-不同异步复位的寄存器不能被包装在一个Slice降低DSP和BRAM性能如图,在XILINX内部 的dsp和bram中,只有同步复位,异步复位是不会包含复位到BRAM/DSP的解决方案不复位同步复位同步复位的好处同步复位为工具提供了更多的灵活性异步复位确实会出现高扇出的情况。Fan-out即扇出,模块直接调用的下级模块的个数,如果这个数值过大的话,在FPGA直接表现为net delay较大,不利于时序收敛。因此,在写代码时应尽量避免高扇出的情况。合成可以选择将控制信号的低扇出同步复位移到数据通路,以释放更多的寄存器。这可以允许将这个寄存器打包到以前不可能的一个slice中可以改善时序以及寄存器密度同步复位让我们的设计稳定从上图我们可以看出,在我们的复位发送故障时,我们的同步复位只能在时钟的边沿触发,这样进而保证了我们系统的稳定性,在复位故障时尽量相对保证系统稳定。复位建议避免复位高复位同步复位不要混合复位可以在寄存器初始化时候直接赋值reg [7:0] code = 8'hff;内部寄存器其实很多情况不需要过分复位,大不了之前的垃圾数据我们不用就行了。重置重置是设计中需要考虑和限制的更常见和重要的控制信号之一。重置可以显著影响设计的性能、面积和功率。模块化复位模块复位可以降低扇出,保证电路的稳定进行。使用指令确保模块复位信号不被综合掉example:(* keep="true" *) reg my_modular_reset1; (* keep="true" *) reg my_modular_reset2; (* keep=“true” *) reg my_modular_reset3; always @(posedge clkA) begin my_modular_reset1 <= synchronized_reset; my_modular_reset2 <= synchronized_reset; my_modular_reset3 <= synchronized_reset; endDSP片采用同步复位DSP芯片比大多数实现的芯片更通用-它可以用于乘数,增加/sub, MACC,计数器(与可编程终端计数),比较器,移位,多路复用器,模式匹配,和许多其他逻辑功能。每个DSP片有效地具有> 310寄存器-没有异步复位使用同步全局重置可以使合成工具更容易地使用DSP切片-异步复位方法将防止工具使用的存储资源在DSP片。
介绍博主之前在全国FPGA竞赛中使用过HLS的部分内容,所以抽一点时间来碎片化学习下HLS的相关知识。简单来说吧,因为传统的开发FPGA流程相比缓慢,不如软件开发的效率高,所以说HLS运营而生, 使用高级语言来进行转换为底层的硬件代码。虽然说是支持高级语言比如说C与C++ 但是最主要的 核心来说, HLS的开发也是要求开发这对代码的背后的硬件与FPGA内部结构相对了解,所以说本文 首先大致介绍了下HLS的相关内容,还有简单介绍了下FPGA简单的内部结构,(后面会更新专门的 内部结构的分析的文章这里就不过多展开,主要针对的是HLS用到的部分做简单说明) 本文内容来源网贴汇集+ 个人理解 + HLS书籍(《FPGA并行编程》)+视频 + 自费视频在传统的FPGA设计流程中,一般是自顶向下的模块化设计,这些模块包括用户自己编写的RTL或者是供应商提供的IP核。而在Xilinx新推出的高生产力设计流程中是以IP为核心的,把所有的模块都看做是IP,封装为IP,最主要的是IP的设计是基于C语言的,最后通过HLS将C语言代码转化为RTL,这能极大的加快设计进程。从这段时间的学习来看,HLS综合出来的电路比我自己写的RTL更省资源,在时序方面可能会差一些,,但它也满足了时序的要求。HLS还提供了非常多的经过深度优化的库,所以我个人觉得HLS综合的电路已经比很多刚接触FPGA不久的工程师要好了,传统的RTL设计可能不会消失,但很可能会越来越少了。HLS在不添加任何优化指令的情况下默认综合电路为最节省资源的情况,即能使用分时复用就使用分时复用,能在一个周期中完成多个操作(满足时钟频率约束的要求)则绝不流水。从这里得到的启发是,设计不要过度的流水,只要能满足时钟频率的需求,我们在一个时钟周期内可以进行多个操作,于是在低频率的系统中,我们可以节省很多的寄存器资源。这样做还有一个好处是我们可以使用assign来完成逻辑设计,然后再对输出进行寄存,可以极大的减少代码量,增加代码的可读性,HLS综合后的RTL代码的风格就是这样的,当然因为命名的原因该代码几乎没有可读性,我们可以不用刻意去读懂它。在学习Vivado HLS工具之前,我们需要了解HLS工具在FPGA开发流程中的作用。Vivado工具的设计理念是以IP为核心的,所有的功能模块都可以看做并且封装成一个IP,最后由Vivado集成,以实现最大化的设计复用。所以Vivado HLS可以看做是一个IP封装工具,它封装的是由C、C++、SystemC或者OpenCL等高级语言实现的功能函数。HLS 完成的工作HLS自动分析一个算法中的潜在并发性HLS自动在需要的路径插入寄存器,并自动选择最理想的时钟HLS自动产生控制数据在一个路径上的出入方向的逻辑HLS自动完成设计部分与系统中的其他接口HLS自动映射数据到存储单元中以平衡资源使用与带宽HLS自动将程序中计算的部分映射到对应逻辑单元,在实现等效运算的前提下选择有效运算大多数HLS工具需要用户提供功能的规范,交互的描述,一个对接的计算设备,和目标优化方向。而对于Vivado HLS来说,用户需要:一个用C/C++/System C编写的函数一个测试平台用于验证结果(C testbench)一个FPGA开发版期望的时钟周期一个简单的实施指导输入程序作出以下规范根据Vivado HLS的使用指南,我们将对我们的输入程序作出以下规范:不使用动态内存分配(不使用malloc(),free(),new和delete())减少使用指针对指针的操作不使用系统调用(例如abort(),exit(),printf()),我们可以在其他代码例如测试平台上使用这些指令,但是综合的时候这些指令会被无视(或直接删掉)减少使用其他标准库里的内容(支持math.h里常用的内容,但还是有一些不兼容)减少使用C++中的函数指针和虚拟函数不使用递归方程精准的表达我们的交互接口当RTL级的设计可用时,大多数HLS工具会进行标准RTL设计流。而在赛灵思Xilinx Vivado设计套装里进行的是逻辑综合,将RTL级设计转换成一个FPGA逻辑部件的连线表,这份连线表不仅包含需要的逻辑部件还包含他们的连接方式。Vivado之后将连线表和目标设备中的可用资源相关联,这个过程被称作布局及布(PAR)。产出的FPGA配置被附在比特流(bitstream)上,用户可以将比特流上传到FPGA以实现想要的功能。比特流实质上是用二进制表示FPGA上每一个可用资源的配置,包括逻辑部件的使用,连线的方式,和片上的内存。大型FPGA例如赛灵思UltraScale FPGA拥有超过十亿个可配置比特,较小的FPGA上也至少有几亿个可配置比特。FPGA构造FPGA是由可编程逻辑矩阵和相连接的内存组成,通常是基于LUT(查找表,就和超市的购物小票相似,每个值都可以被索引到)。下图大致给出了FPGA的内部构造,在可编程的逻辑块中,现在的FPGA也已经高度集成了很多的资源。逻辑单元这部分主要是是实现逻辑运算的部分,基于查找表也就是LUT,实际的FPGA中大部分使用的是4–6位输入的查找表作为运算基础。在逻辑单元里,触发器FF是最近的内存单元,我们可以利用触发器的性质,储存一些少量的数据。存储单元前面提到了使用(FF)触发器可以存储少量的信息,图中也给出了(LUT in slicem)FPGA另外一部分的存储单元是Bram,他是支持多种内存接口,可以配置的随机存储器。总体来说有两个功能:芯片各部分的数据转移存储大的数据集同时FPGA也可以通过驱动外部的存储器来实现上述的功能,这部分为外部存储,简称(外存)相比较来说,触发器有最好的带宽但是存储容量太小;而外存的存储密度高,但是带宽有限,而Bram相当于两者的中间值。根据设计的需要,我们可以配置存储单元为各种形式,种类列举如图:算术逻辑单元因为对于算法的实现来说,说白了也就是进行计算的实现,算法优化的过程就像我们小学初中学习的化简,简便计算等等,所以我们也要对运算逻辑单元有个简单的了解。这里我就不过多展开,大致了解到DSP48中包括的部分即可。加法乘法ALU运算单元选择器设计优化对于HLS来说,优化基本算是他要解决的首要问题,那么我们应该从哪些角度来思考优化呢?在开始讨论怎么去优化之前,我们先要讨论一下判断一个设计特点的标准。计算时间就是一个衡量设计好坏的重要标准。很多人把时钟周期数作为一个同步电路性能的指标,但实际上对于两个使用不同时钟的电路这是不得当的,而时针不同又是HLS下的绝大多数情况。比如说,我们现在已经规定好了VivadoHLS的输入时钟限制,那么工具根据时钟的不同会从同一段代码中产生不同的结构,所以这不是一个很恰当的比较方式。秒数是一个更好的对应比较指标。Vivado HLS工具会提供一个周期数和周期频率的报告,用户可以用此得出某段代码的操作时间。改变时钟频率有时候可以优化设计。Vivado HLS工具把时钟频率作为一个输入,所以改变一个输入可以导致产出的结构完全不同。我们用任务(task)这个术语来表示一个行为的基本单位,用户可以在Vivado HLS中发现与之对应的是调用函数。任务延迟就是任务开始到任务完成中间的这段时间。任务间隔则是任务开始到下一个任务开始之间的这段时间。所有的任务输入,输出和计算的时间都被算在任务延迟里,但是任务的开始并不同于读取输入,同样任务的结束也不等同于写出输出。在很多设计中,数据率是一个很重要的东西,它同时取决于任务间隔和函数参数的多少。两种不同设计的任务间隔和任务延迟,上方的弧线指示的是任务间隔,下方的弧线指示的是任务延迟。左右两个设计的区别在于,左边是流水线(pipeline),右边使用了更顺序化的设计。图表示的是两种设计的实施设想,横向轴是时间轴(从左到右增大),纵向是设计中不同的函数单位。红色表示的是输入有关的操作,橙色表示的是输出有关的操作,正在活跃的运算符用深蓝表示,不活跃的则用浅蓝表示。每一个进入的箭头表示的是一个任务的开始,而出去的箭头表示任务的完成。左侧的图表示的是一个每个周期都执行新任务的结构设计。与之对应的是完全流水(fully-pipelined)结构。右侧表示的则是一个完全不一样的结构,系统每次读取四段输入,处理数据,然后再合成一个4段数据的输出。这种结构的任务延迟和任务间隔是一样的(13个周期),并且每一周期内只有一个任务在执行。这个结构和左边的流水形成了鲜明对比,左边的结构在同一周期内显然有多个任务在执行。HLS中的流水和处理器中的流水概念相似,但是不再使用处理器中操作分5个阶段并把结果写入寄存器堆的方法,Vivado HLS工具构造的是一个只适用于特定板子,可以完成特定程序的电路,所以它能更好的调整流水的阶段数量,初始间隔(连续两组数据提供给流水之间的间隔),函数单位的数量和种类,还有所有部件之间的互联。Vivado HLS工具通过计算一个任务输出到输入之间这个过程需要的寄存器数来决定周期。因此,0周期的任务延迟是可以实现的,也就是组合逻辑下路径上没有任何寄存器。另一个常用的工作是计算输入输出并把结果存到寄存器里,通过这些数据找到路径上的寄存器数。这样的计算有花费很多的周期优化的取舍理解编译器如何工作其实有助于设计师写出更高效的代码,这点对于HLS尤其重要,因为综合电路的构建方式有很多种,只理解它软件流是不够的。比如HLS设计师需要考虑流水,内存排布,I/O接口这些软件设计师不需要考虑的内容。回到编译器,理解它的关键问题在于:这段代码中产生的是什么电路?这个问题的答案分多钟,还和你所用的HLS工具有关。用户可以通过在代码中添加#pagma HLS pipeline来指导Vivado HLS工具产生函数流水结构。复杂的设计在顺序化和并行化的结构之间会有很多取舍的考虑。这些取舍在Vivado HLS中很大程度上取决于设计者的决定和代码内容。处理速率的限制我们看到了很多改变架构会改变任务间隔的例子,这样做通常来讲可以提升处理速率。但是读者需要意识到任何结构的任务间隔都是有一定的限度的。最关键的限制来自于递归和反馈循环,还有一些其他的例如资源限制也很重要。递归(recurrence),这里是指某个部件的计算需要这个部件之前一轮计算的结果, 递归是限制产力的重要因素,即使在流水结构中也是如此[56,43]。分析算法中的递归并产出正确的硬件设计是非常关键的一步,同样,选择一个尽量避免很多递归的算法也是设计中非常关键的一步。递归在很多代码结构中都会出现,它存在于很多顺序化结构中,也有很多会随着改编成流水结构而消失。对于顺序化结构递归有时候不影响处理速率,但是在流水结构中是一个很不理想的状况。另一个影响速率的关键因素就是资源限制,其中一种形式是设计边缘的跳线,因为一个同步电路中的每根跳线在每周期只能传送抓取1个比特的数据。因此,如果 int32_t f(int32_t x)这样形式的函数作为一个单独模块在100MHz的频率和1的任务间隔下运行,它最大的数据处理量就是3.2G比特。另一种资源限制来自于内存,因为大多数内存每周期只支持一定次数的访问。还有一种资源限制来自于用户所给的限制,如果用户规定了在综合中可用的操作数,这其实是给处理率添加了限制条件。代码要求强调重建代码对于高质量设计的重要性,比如在高性能和低使用面积上。对常见的内容提供重建的代码讨论重建对于硬件的影响使用必要的HLS指令以实现最好的设计
前言之前博主在使用modelsim进行仿真的时候是用图形化的界面进行仿真,但是如果仿真任务量很大的话,这就不是一个很好的办法来操作,这样我们TCL的脚本语言进行仿真的优势就出来了,ModelSim的tcl最大的优势就在于它可以让整个仿真自动运行,(方便快捷),下面我就简单的整理下这部分的内容。工作环境Modelsim (版本不限)流程介绍自动完成建库映射库到物理目录编译源代码启动仿真器运行仿真常用语法说明quit -sim退出当前仿真功能,退出当前的工程,跟在modelsim界面的命令行敲入命令的效果一样, 之后就可以创建其他的工程.main clear清除命令行显示信息;在命令行‘敲入这个命令,回车’的效果一样添加显示波形总体语法:add wave <mydesign>/<signal>如果添加的波形不只是顶层模块的,还有顶层下面的例化模块的信号语法:add wave <测试顶层的名字>/<例化子模块的例化名字>/<子模块信号的名字> (可以使用通配符)代码解释-radix约束进制显示,如:binary 、ascii、unsigned、octal、hex-format约束波形为那种类型 包括 logic ;literal; analog-step; analog-interpolated打开波形窗口,输入命令:view wave添加不同类信号之间的分割线语法:add wave -divider {分割线的名字}。信号建组语法:add wave -group <组名> -radix unsigned tb_x/* 意思将tb_x中所有信号分一个组以无符号显示如:add wave -group xx -radix unsigned tb_x/*force-repeat指令指令格式:force 开始时间 开始电平值,结束电平值 忽略时间(即0电平保持时间) -repeat 周期force clk 0 0,1 30 -repeat 100 表示强制clk从0时间单元开始,起始电平为0,结束电平为1, 0电平保持时间为30个默认时间单元,周期为100个默认时间单元,占空比为70%。指令功能:每隔一段的周期重复一定的force命令,用来产生时钟信号,也可用来产生周期的输入信号,如01010101,00110011等。force指令force din 16#40900000 从当前时刻起给din赋值16进制40900000; force bus 16#F @100ns 在100ns时刻给bus赋值16进制F; force clr 1 100 经历100个默认时间单元延迟后为clr赋值1; force clr 1,0 100 表示clr赋值1后,经历100个默认时间单元延迟后为clr赋值为0;run指令指令格式:run timesteps time_unit,timesteps时间步长,time_unit时间单元,可以是fs、ps、ns、us、ms、sec;指令功能:运行(仿真)并指定时间及单元;run 100, 表示运行100个默认时间单元; run 2500ns, 表示运行2500ns; run -all, 表示运行全过程; run -continue, 表示继续运行force-cancel指令指令格式:force-cancel period指令功能:执行period周期时间后取消force命令;force clk 0 0,1 30 -repeat 60-cancel 1000, 表示强制clk从0时间单元开始,直到1000个时间单元结束;view指令指令格式:view 窗口名指令功能:打开Modelsim的窗口view souce,打开源代码窗口;view wave,打开波形窗口;view list,打开列表窗口;view varibles,打开变量窗口;view signals,打开信号窗口;view all,打开所有窗口;为时钟信号添加驱动输入命令:force clk 0 0,1 10 -r 20, 将仿真时钟设为50MHz;(设时间单位为ns)一些常用指令#打开现有工程 project open C:/Users/jayash/Desktop/sim/ImageProcess #新建一个库 vlib my_lib #将其映射到work vmap my_lib work #删除制定库 vmap -del my_lib #添加指定设计文件 project addfile src/Verilog/test.v #编译工程内所有文件 project compileall #编译指定verilog文件 vlog src/Verilog/test.v #编译指定的vhdl文件,同时检查可综合性 vcom –check_synthesis src/video_cap.vhd ##仿真work库下面的test_tb实例,同时调用220model_ver库,不再进行任何优化,仿真分辨率1ns。 vsim –t 1ns –L 220model_ver –gui –novopt work.test_tb #取消warning,例如‘x’,‘u’,‘z’信号的警告,对提高编译速度很有帮助 set StdarithNoWarning 1 #查看object View objects #查看局部变量 View locals #查看source View source #添加模块顶层所有信号到波形图 add wave* #10进制无符号显示 Radix usigned #16进制显示 Radix hex #重新进行仿真 Restart #开始仿真 Run #仿真指定时间 Run 1ms #时钟激励50ns周期 占空比50% Force –repeat 50 clk 0 0,1 25 #指定信号置0 Force rst_n 0 #指定信号置1 Force rst_n 1 #指定信号赋值 Force din_a 123 Force din_b 39流程说明第一步:创建库vlib:创建库到一个物理目录中,也就是创建文件夹目录了。默认在.do文件所 在的文件夹下可以理解库为某一个路径的文件夹,用来存储modelsim的一些数据文件;创建库的格式vlib ,默认库的名字为work代码意思是: 1-在当前路径(.do文件的路径)下,创建lib文件夹;相当于在命令行敲‘vlib lib 回车’ 2-在当前路径的lib文件夹下,创建work文件夹库;相当于在命令行敲‘vlib ./lib/work’第二步:映射逻辑库到物理目录也就是说,在modelsimGUI界面的Library选项卡里面创建子选项, 这个子选项就叫做逻辑库,编译工程之后,得到一堆编译文件,这些文件名就放在这个逻 辑库选项卡里面。但是编译得到的是实体文件,这些文件必须有一个目录存储,因此就需 要把逻辑库映射到物理(文件夹)目录,也就是把那些得到的实体文件放在某一个文件夹 目录(路径当中)这样,就可以在实际文件夹里面查看编译得到的文件内容,而不是单单 从选项卡里面看到名字而已,注意,在映射之前,一定要先创建好对应的物理路径语法为 vmap work(逻辑库名称) (库的路径)代码意思是:在modelsim界面的library选项卡创建一个叫work的选项卡(逻辑库),编译之 后得到的文件名称就在,相应的文件就放在./lib/work这个文件夹里面。当然逻辑库的名字不 一定是work(modelsim默认是work就一般取做work),也可以是top_xxx,如果是top_xxx (vmap work ./lib/work)在modelsim界面的library选项卡创建的选项卡名称就是top_xxx, 但是编译之后得到的文件还是存放到./lib/work的路径文件夹中 vmap work ./work第三步:编译Verilog 源代码将编译得到的信息文件放到④的work逻辑库里面,库名缺省编译到work本地库,文件按顺 序编译。语法为vlog –work(固定格式) work(逻辑库名字) .v .v(要编译的文件:路径/文件)主要是编译设计文件,测试文件,调用的IP核.v文件,相应的库文件,通配符./../xxx/*.v, 要注意编译的顺序,注意,.v文件应该是放在设计或者仿真的文件里面,不要仿真逻辑库 路‘’径里面,逻辑库路径在编译之后会自然得复制过来#下面代码的意思是:编译xxx.v这两个文件,将编译得到的文件与源文件放到 work这个文件夹里面。vlog -work work ./x_tb.v vlog -work work ./RTL/select_test.v #或写成vlog -work work ./RTL/*.v第四步:编译完启动仿真(设置顶层的testbench文件 tb)语法:vsim –lib name>.level design>,-t表示仿真时间单位为ns-novopt表示仿真时无优化+notimingchecks表示无时序检查vsim -voptargs=+acc work.tb work.tb表示对work库中的tb进行仿真,实际相当于在界面操作时,展开work库, 右键—>Simulate without Optimization,启动仿真。代码意思是:优化部分参数(-voptargs=+acc),链接前面建立的work逻辑库, 启动测试逻辑库(work)里面的x_tb文件第五步:设置仿真运行时长语法: run <运行时间> run 1usdo文件模板目前由于信号较少,所以我们就不用对信号进行过多设置,将所有波形进行添加,下面模板即可够用最简洁版:vlib work vmap work vlog "my_logic.v" vlog "tb_my_logic.v" vsim -t 1ns -novopt work.tb_my_logic run -all实用版:#退出当前仿真 quit -sim vlib work vmap work #编译设计文件和仿真文件。 vlog "../Src.v" vlog "../Sim.v" #开始仿真(条件根据需要改变) vsim -t 1ns -voptargs=+acc -novopt work.tb #添加指定信号 #添加顶层所有的信号 # 打开波形窗口 view wave view structure # 打开信号窗口 view signals # 添加波形模板 add wave -divider {波形名字} add wave tb/* .main clear #运行xxms run 100us
图像增强技术 是一大类基本的图像处理技术 , 其目的是对图像进行加工 , 以得到对具体应用来说视觉效果更 “ 好 ” 、 更 “ 有用 ” 的图像 。目标: 改善图像视觉效果,便于观察和分析,便于人工或机器对图像的进一步处理。标准: 相当主观 , 因人而异,没有完全通用的标准,可以有一些相对一致的准则。图像空域增强在图像处理中 , 空域 是指由像素组成的空间 , 也就是图像域空域增强方法指 直接作用于像素改变其特性的增强方法 。 具体的增强操作可仅定义在每个像素位置( (x x, , y y) ) 上 ,此时称为 点操作 ;增强操作还可定义在每个( (x x, , y y) ) 的某个邻域上 , 此时常称为 模板操作或邻域操作。点处理 是作用于单个像素的空间域处理方法,包括图像灰度变换、直方图处理、伪彩色处理等技术;模板处理 是作用于像素邻域的处理方法,包括空域 平滑、空域锐化等技术。图像直接灰度转换灰度变换也被称为图像的点运算(只针对图像的某一像素点)是所有图像处理技术中最简单的技术,其变换形式如下:s=T(r)其中,T是灰度变换函数;r是变换前的灰度;s是变换后的像素。图像灰度变换的有以下作用:改善图像的质量,使图像能够显示更多的细节,提高图像的对比度(对比度拉伸)有选择的突出图 像感兴趣的特征或者抑制图像中不需要的特征可以有效的改变图像的直方图分布,使像素的分布更为均匀线性灰度变换-截取和分段式(一次函数式)线性灰度变换函数是一个线性函数:B=f(A)=kA+b.其中A为输入图像灰度值,B为输出图像灰度,a为线性函数的斜率,c为在Y轴的截距。当a>1时,输入图像的对比度将增大,当a<1时,输出图像的对比度将减小;当a=1时且c≠0时,c的变换仅使所有的灰度值上移或者下移,其效果使整个图像更暗或更亮,当a<0时暗区域将变亮,亮区域将变暗。这种线性变换可能由于像素达到饱和(小于0或者超过255)从而丢失一部分细节;特殊情况a=1,c=0输入图像和输出图像相同;a=-1,c=255输出图像的灰度正好反转。k>1增大图像的对比度,图像的像素值在变换后全部增大,整体效果被增强k=1通过调整b,实现对图像亮度的调整0<k<1图像的对比度被削弱k<0原来图像亮的区域变暗,原来图像暗的区域变亮imadjustJ = imadjust(I)将灰度图像 I 中的亮度值映射到 J 中的新值并使 1% 的数据是在低高强度和饱和,这增加了输出图像 J 的对比度值。此用法相当于 imadjust(I,stretchlim(I))J = imadjust(I,[low_in; high_in],[low_out; high_out])将图像I中的亮度值映射到J中的新值,即将low_in至high_in之间的值映射到low_out至high_out之间的值。low_in 以下与 high_in 以上的值被剪切掉了,也就是说,low_in 以下的值映射到 low_out,high_in 以上的值映射到high_out。它们都可以使用空的矩阵[],默认值是[0 1]。J = imadjust(I,[low_in; high_in],[low_out; high_out],gamma)将图像 I 中的亮度值映射到 J 中的新值,其中 gamma指定描述值I和值J关系的曲线形状。如果gamma小于1,此映射偏重更高数值(明亮)输出,如果gamma大于1,此映射偏重更低数值(灰暗)输出,如果省略此参数,默认为(线性映射)。clc; clear all;close all; img = imread('pout.tif'); o_img = imadjust(img,[0.3 0.6],[0 1]); subplot(1,2,1);imshow(img); subplot(1,2,2);imshow(o_img);效果线性灰度变换—图像反转灰度线性变换最常见的就是图像反转,在灰度图像灰度级范围中,其反转的公式如下所示:其中,表示原始图像的灰度级,表示变换后的灰度级。反转法一:i=imread('reverse.jpg'); %I=im2gray(i); I1=255-i; imshow(I1);反转法二:使用函数:incomplement调用格式:IM2 = imcomplement(IM) 函数功能: 对图像数据进行取反运算(实现底片效果)。参数说明: IM是源图像的数据, IM2是取反后的图像数据。一个简单的例子:X = uint8([ 255 10 75; 44 225 100]); X2 = imcomplement(X) X2 = 0 245 180 211 30 155注意点:图像文件中用uint8来表示256级灰度。 对于真彩色位图, 一个像素用3个uint8分别表示该像素的R、G、B分量。uint8表示的数据范围: 0~255。图像的底片效果便是拿255 减去原图像数据。代码:clc; clear all;close all; img = imread('../img1.jpg'); o_img = imcomplement(img); subplot(1,2,1);imshow(img); subplot(1,2,2);imshow(o_img);非线性变换:图像灰度对数变换一般表示如下所示:其中,表示原始图像的灰度级,表示变换后的灰度级,为常数。假设,下图所示的对数曲线的形状表明,改变换将输入中范围较窄的低灰度值映射为输出中较宽范围的灰度值。相反的,对高的输入灰度值也是如此。我们使用这种类型的变换来扩展图像中暗像素的值,同时压缩更高灰度级的值。反对数变换的作用与此相反。对数变换对数变换主要用于将图像的低灰度值部分扩展,将其高灰度值部分压缩, 以达到强调图像低灰度部分的目的。增加图片细节。对数变换的 实质 : 将一幅窄带低灰度输入图像映射为一宽带输出图像变换方法由下式给出。底数越大,对低灰度部分的强调就越强,对高灰度部分的压缩也就越强。相反的,如果想强调高灰度部分,则用反对数函数就可以了。clc; clear all;close all; img = imread('../gray1.jpg'); D=double(img); D_1 = 10*(log(D+1)); o_img = uint8(D_1); subplot(1,2,1);imshow(img);title("原始图像"); subplot(1,2,2);imshow(o_img);title("输出图像");伽马变换伽玛变换又称为 指数变换 或 幂次变换,是另一种常用的灰度非线性变换。 图像灰度的伽玛变换一般表示如下所示其中,表示原始图像的灰度级,表示变换后的灰度级,和正常数1)当>1时,会拉伸图像中灰度级较高的区域,压缩灰度级较低的部分;2)当<1时,会拉伸图像中灰度级较低的区域,压缩灰度级较高的部分;3)当=1时,该灰度变换是线性的,此时通过线性方式改变原图像。clc; clear all;close all; img = imread('../gray1.jpg'); img =rgb2gray(img); img = double(img); C=1; gamma=0.4; o_img1=C*(img.^gamma); gamma=1; o_img2=C*(img.^gamma); gamma=10; o_img3=C*(img.^gamma); img = uint8(img); o_img1 = uint8(o_img1); o_img2 = uint8(o_img2); o_img3 = uint8(o_img3); subplot(2,2,1);imshow(img);title("原始图像"); subplot(2,2,2);imshow(o_img1);title("输出图像gamma=0.4"); subplot(2,2,3);imshow(o_img2);title("输出图像gamma=1"); subplot(2,2,4);imshow(o_img3);title("输出图像gamma=10");
本文作为本科系统学习数字图像处理的一个开篇吧,之前间断的学习了一些FPGA和matlab的相关知识,正逢校内开设这个课程,近期就更新下关于数字图像的知识吧,由于博主今年要准备考研,所以花在博客上的时间会比较少。数字图像的操作1.灰度或者二值转换为索引图像gray2ind将灰度或二进制图像转换为索引图像[X,cmap] = gray2ind(I,c)[X,cmap] = gray2ind(BW,c)解释:I— 灰度图像—数字数组BW— 二进制图像—数字数组c— 颜色图颜色的数量—正整数下面把图像转换为128,64,16灰度级的索引图片clc; clear all;close all; img=imread('../img1.jpg'); img=rgb2gray(img); figure; imshow(img); title('original'); [x,map]=gray2ind(img,128); figure; imshow(x,map); title('gray_128'); %%%%%%%%%%%重复操作%%%%%%%%%%%%%% [x1,map1]=gray2ind(img,64); figure; imshow(x1,map1); title('gray_64'); [x2,map2]=gray2ind(img,16); figure; imshow(x2,map2); title('gray_16');效果在灰度级不断减小时,图片逐渐有失真的趋势2.索引图像转换为灰度图像ind2grayI = ind2gray(X,cmap)I = ind2gray(X,cmap) 将X带有颜色图 的索引图像转换cmap为灰度图像I。该 ind2gray功能在保持亮度的同时从输入图像中删除色相和饱和度信息。X— 索引图像----数字数组cmap— 颜色图----c-by-3数字数组clc; clear all;close all; load trees img = ind2gray(X,map); figure,imshow(X,map); title('orginal'); figure,imshow(img); title('gray_image');效果3.RGB图像转换为灰度图像rgb2gray将 RGB 图像或颜色图转换为灰度图语法:I = rgb2gray(RGB)newmap = rgb2gray(map)clc; clear all;close all; img=imread('../img1.jpg'); gray=rgb2gray(img); subplot(1,2,1);imshow(img);title('original'); subplot(1,2,2);imshow(gray);title('gray');4.RGB图像转换为索引图像rgb2indrgb2ind函数对于减少RGB图像的颜色数是非常有用的。将真彩色图像转换为索引图像。在matlab命令窗口中键入doc rgb2ind或help rgb2ind可以获得更多关于该函数的帮助信息。该函数将真彩色图像转换为索引图像, 由于RGB图像一个像素占用三个字节, 分别存储R、G、B分量的值,而索引图像一个像素占用一个字节。在将灰度图像转换为索引图像时, 是从3个字节映射到一个字节的关系。 通常有以下三种算法:gray = (R + G + B) / 3,即求得R、G、B三个分量平均值作为索引图像中对应像素点的像素值。gray = min(R, G, B),即以R、G、B三个中最小分量作为索引图像对应像素点的像素值。gray = 0.3 * R + 0.59 * G + 0.11 * B(或者表示为:gray = ((77 * R + 151 * G + 28 * B) >> 8))调用格式:[X,map] = rgb2ind(RGB, n)使用第二种算法把真彩色图像转换为索引图像,其中n指定map中颜色项数, n最大不能超过65536。返回值中map即索引图像的调色板。X = rgb2ind(RGB, map)使用调色板map将真彩色图像转换为索引图像,即在调色板中找到与真彩色图像颜色值最接近的颜色作为转换后的索引图像的像素值。map中颜色项数(即size(map, 1))不能超过65536。[X,map] = rgb2ind(RGB, tol)利用第一种算法把真彩色图像转换为索引图像, map中最多包含(floor(1/tol)+1)^3种颜色, tol必须是介于0.0和1.0之间的数。[…] = rgb2ind(…, dither_option)clc; clear all;close all; img=imread('../lena.jpg'); subplot(2,2,1);imshow(img);title('original'); [x,map]=rgb2ind(img,16); subplot(2,2,2);imshow(x,map);title('最小方差量化抖动'); [x1,map1]=rgb2ind(img,0.5); subplot(2,2,3);imshow(x,map);title('均值化抖动处理'); [x2,map2]=rgb2ind(img,16,'nodither'); subplot(2,2,4);imshow(x,map);title('不抖动');效果5.索引图像转换为RGB图像ind2rgb和ind2gray类似clc; clear all;close all; load trees img = ind2rgb(X,map); figure,imshow(X,map); title('orginal'); figure,imshow(img); title('rgb_image');6.通过阈值转换为二值图像im2bwim2bw使用阈值(threshold)变换法把灰度图像(grayscale image)转换成二值图像。所谓二值图像, 一般意义上是指只有纯黑(0)、纯白(255)两种颜色的图像。 当然, 也可以是其他任意两种颜色的组合。语法BW = im2bw(I, level)BW = im2bw(X, map, level)BW = im2bw(RGB, level)其中level就是设置阈值的。level取值范围[0, 1]clc; clear all;close all; load trees img = im2bw(X,map,0.3); subplot(2,2,1);imshow(X,map);title('orginal'); subplot(2,2,2);imshow(img);title('bw_ind'); rgb=imread('../img1.jpg'); rgb_bw = im2bw(rgb,0.3); subplot(2,2,3);imshow(rgb);title('orginal'); subplot(2,2,4);imshow(rgb_bw);title('bw_rgb');效果
学习内容pyb的一些简单操作。熟悉micropython在pyb上面的应用开发环境putty、PYB nanoMocroPython的启动模式在启动后,MicroPython会先运行 boot.py 文件,加载用户驱动,然后在运行main.py,执行用户程序。可以将用户程序放在main.py中,也可以在main.py中再加载其它的文件。常见故障在使用过程中,我们需要注意下面问题,避免造成文件系统破坏、数据丢失。取下数据线前,需要先U盘那样安全删除硬件,弹出PYBFLASH磁盘,否则可能会造成文件系统破坏,特别在修改了文件或复制新文件到PYBFLASH磁盘后。不要轻易按复位键,这样会造成当前的USB通讯中断。一般的问题,可以通过按下Ctrl-D软复位接近。出厂模式使用时间长了,因为各种原因可能会出现故障,造成无法正常启动,不能进入REPL,文件系统破坏等现象,这时就需要通过出厂模式进行恢复。进入出厂模式的方法是:按下复位键(RESET)的同时,按住用户按键SW。然后保持用户按键不放,释放复位键。这时LED将循环显示:绿-》黄-》绿+黄-》灭等黄绿灯同时亮时,松开用户键,这时黄绿灯会同时快速闪4次然后红灯亮起(这时红绿黄三个灯同时亮)红灯灭,开始进行恢复到出厂状态所有灯都灭,恢复出厂设置完成。恢复出厂设置后,PYBFLASH中的内容会丢失,变为默认文件。升级固件MicroPython的更新速度很快,每次更新都会带来一些新的功能,修正错误。所以掌握 MicroPython 的固件升级方法是有必要的。PYB Nano支持下面几种升级方法:通过DFU模式升级 通过SWD方式升级使用DFU模式,需要安装ST的DfuSe_demo软件(Windows)或者dfu-util(Linux)。使用SWD需要将开发板的SWD接口(PA13/PA14)连接到编程器,通过编程软件下载。REPL 的用法通常调试程序时,都是在 MicroPython 的 REPL (read–eval–print loop,循环交互解释器)环境下运行。在REPL下可以直接输入命令,有内置的解释器执行。如果命令输入正常,就会显示运行结果,否则会给出错误提示。MicroPython支持几个常用的快捷键,如果你熟悉串口终端,会发现它们的习惯是一样的。Ctrl-C,停止正在的程序或终止当前的命令行Ctrl-D,软复位(soft reset)Ctrl-B,显示系统提示Ctrl-E,进入粘贴模式。可以按下Ctrl-C退出粘贴模式,Ctrl-D完成粘贴Tab,键盘上的Tab键,可以补全命令除了Ctrl-C,其它快捷键需要在空命令行下(没有输入任何字符)才能生效。此外,还可以使用上下左右光标键上下键,调出以前输入的命令。MicroPython可以保存最后输入的6条命令左右键,在当前命令行中移动,编辑命令配置终端MicroPython和PC的标准连接是通过USB接口,使用虚拟磁盘和虚拟串口(VCP)方式。其中虚拟串口(在pyboard和STM32上可以同时使用USB虚拟串口和物理串口两种方式)是调试中最常用的方式,无须频繁复制文件避免造成Flash的损耗。在MicroPython上我们使用串口终端软件和MicroPython的REPL进行交互,发送命令。通过串口终端软件,我们可以方便地在 REPL 中输入代码,运行和调试程序,打印结果。开发MicroPython程序时,掌握终端软件的使用是非常有必要的,注意不要使用Windows下的串口调试助手、串口精灵这样的软件,因为它们只适合一般的串口调试,发送数据,但是不方便输入命令,不支持粘贴功能,不能和REPL进行交互操作。这里我用的是putty配置过程接很简单,首先打开设备管理器:这里可以看到对应PYB板子的串口,我们只需要在PUTTY打开这个串口终端即可。这里我的串口是COM20 波特率是115200,记得选择serial。当然为了方便大家可以把这个COM存起来。大功告成!现在只需要打开对应的端口即可使用,效果如图:当然我们也可以在我的电脑板子上的文件:下面就简单操作下板载的函数熟悉下操作吧~PYB库函数操作1.点灯工程师~(点亮,关闭,翻转,闪烁)首先要说明的是可以在终端进行代码操作,也可以将代码输入main.py中,在终端中输入的将不会保存,而在main.py中输入后,保存后按下Ctrl+D后,实现软件复位。将会看到编写的代码实现的操作。import pyb # 点亮操作 pyb.LED(1).on() #点亮 LED1 pyb.LED(2).on() #点亮 LED2 pyb.LED(3).on() #点亮 LED3 pyb.LED(4).on() #点亮 LED4 # 关闭操作 pyb.LED(1).off() #关闭 LED1 pyb.LED(2).off() #关闭 LED2 # 翻转操作 pyb.LED(1).toggle() #翻转 LED1 #闪烁操作 while True: pyb.LED(1).toggle() #翻转 LED1 pyb.delay(200) #延时200ms除了打开、关闭、翻转功能外,部分 LED 还可以控制亮度。在PYB上,LED3和LED4支持亮度调整功能,如可以这样控制LED3的亮度:import pyb pyb.LED(3).intensity(10)亮度的范围为0~255,0最暗(关闭),255最亮。对于那些不支持亮度功能的LED,在设置亮度时,0是关,大于0就是开。LED.intensity([value])读取或者设置LED亮度。亮度的范围是0(熄灭)到255(最亮)。注意:只有LED(3)和LED(4)支持亮度调节功能,它们使用定时器的PWM方式来控制LED的亮度。LED(3)使用定时器2,LED(4)使用定时器3(如果改变了定时器2/3的参数,亮度调整功能会受到影响)。只有在使用了LED.intensity()功能,并且参数在1~254之间时定时器才会自动配置为PWM模式。1.1跑马灯import pyb leds =[pyb.LED(i) for i in range(1,5)] #定义led n=0 while True: n = (n+1)%4 leds[n].on() pyb.delay(200) leds[n].off()1.2.往返式跑马灯import pyb n=1 dn=1 while True: pyb.LED(n).toggle() pyb.delay(200) pyb.LED(n).toggle() n=n+dn if (n>3)or(n<2): dn=-dn;#改变方向2.按键的操作2.1 读取按键键值import pyb sw = pyb.Switch() #定义按键对象 print(sw()) #读取按键状态 如果按下了按键,就返回True,否则返回False。我们还可以定义一个按键的回调函数(类似C语言里的中断函数),当按下按键时将自动执行这个回调函数。下面定义的回调函数中,每当按下一次按键,就将翻转一次LED1。
学习内容PYNQ的串口使用拓展GPIO的配置类比配置别的IO功能开发环境PYNQ 这里我用的是2.3的官方镜像,jupyter-Notebook官方文档参考[https://pynq.readthedocs.io/en/latest/pynq_libraries.html#pynqmicroblaze]1.PYNQ的串口使用拓展前文提到了uart的配置的方式是用的RPI接口,下面我将简单说一下用arduino接口。别的地方的配置和前文的串口的配置相同吧,这里只需要把MBlibrary加载的ip改为arduino,如文:lib = MicroblazeLibrary(base.ARDUINO, ['uart']) device = lib.uart_open(0,1)这里要说明下uart管脚的定义配置,这里是说如何进行编号:给出原理图从右到左 从下到上由0 递增编号AxiGPIOAxiGPIO类提供了从外部通用外围设备(如LED,按钮,使用AXI GPIO控制器IP连接到PL的开关)读取,写入和接收中断的方法。AxiGPIO模块控制PL中AXI GPIO控制器的实例。每个AXI GPIO最多可具有两个通道,每个通道最多具有32个引脚。read()和write()方法用于一个信道(所有的GPIO的)上读取和写入数据。setdirection()并setlength()可以用来配置IP。方向可以是“内”,“外”和“内”。默认情况下,方向是“ inout”。指定“入”或“出”将仅分别允许对IP进行读取和写入,而尝试读取 “出”或写入 “入”将导致错误。可以将长度设置为仅写入较小范围的GPIO。GPIO也可以视为数组。这允许设置特定的位,并且避免使用位掩码。来自AXI GPIO 的中断信号ip2intc_irpt可以直接连接到AXI中断控制器,以在PS中引起中断。有关AsyncIO和中断的更多信息,请参见PYNQ和Asyncio 部分。LED,Switch,Button和RGBLED类扩展了AxiGPIO控制器,并针对相应的外设进行了自定义。这些类期望[led|switch|button|rgbleds]_gpio 在与此类一起使用的覆盖中存在被称为的AXI GPIO实例。例子该示例仅用于说明,以显示如何使用AxiGPIO类。实际上,LED,按钮,开关和RGBLED类可用于扩展AxiGPIO类,这些类应用于覆盖层中的这些外设。加载覆盖后,可以通过将AXI GPIO控制器的名称传递给该类来实例化AxiGPIO实例。加载overlay:from pynq import Overlay from pynq.lib import AxiGPIO ol = Overlay("base.bit")配置ioled_ip = ol.ip_dict['gpio_leds'] switches_ip = ol.ip_dict['gpio_switches'] leds = AxiGPIO(led_ip).channel1 switches = AxiGPIO(switches_ip).channel1简单的读写:mask = 0xffffffff leds.write(0xf, mask) switches.read()使用AXI GPIO作为数组:switches.setdirection("in") switches.setlength(3) switches.read()这种是官方给的AXI GPIO的例子,这里我没有经过实践验证,所以,我这里就不多说明。下面将介绍另外一种方法,和前文应用方式相同MB调用GPIO通样是打开官方的文档查看在MB里有哪些可以进行配置的操作,这里我们可以进行调用GPIO DevicesGPIO devices allow for one or multiple pins to be read and written directly. All of these functions are in gpio.hgpio typeA handle to one or more pins which can be set simultaneously.gpio gpio_open(int pin)Returns a new handle to a GPIO device for a specific pin on the I/O switch. This function can only be called if there is an I/O switch in the design.gpio gpio_open_device(unsigned int device)Returns a handle to an AXI GPIO controller based either on the base address or device index. The handle will allow for all pins on channel 1 to be set simultaneously.gpio gpio_configure(gpio parent, int low, int hi, int channel)Returns a new handle tied to the specified pins of the controller. This function does not change the configuration of the parent handle.void gpio_set_direction(gpio device, int direction)Sets the direction of all pins tied to the specified handle. The direction can either be GPIO_IN or GPIO_OUT.void gpio_write(gpio device, unsigned int value)Sets the value of the output pins represented by the handle. If the handle represents multiple pins then the least significant bit refers to the lowest index pin. Writing to pins configured as input has no effect.unsigned int gpio_read(gpio device)Reads the value of input pins represented by the handle, If the handle represents multiple pins then the least significant bit refers to the lowest index pin. Read from pins configured as output results in 0 being returned.void gpio_close(gpio_device)Returns the specified pins to high-impedance output and closes the device.函数使用这里我们需要用到的是gpio gpio_open(int pin),void gpio_set_direction(gpio device, int direction),void gpio_write(gpio device, unsigned int value),unsigned int gpio_read(gpio device)这些函数比较好理解我就不过多说明了,直接看下配置程序:首先加载官方的overlays,并且把mb.lib包含下:from pynq.overlays.base import BaseOverlay from pynq.lib import MicroblazeLibrary base = BaseOverlay('base.bit') print('finish')打开GPIO#如果用别的接口只需要把这里的RPI改为相应名称。lib = MicroblazeLibrary(base.RPI, ['gpio']) device = lib.gpio_open(14) device1 = lib.gpio_open(15)输入输出配置gpio_set_direction(device, 1)#输入 gpio_set_direction(device1, 0)#输出GPIO写操作#只可以进行写0,写1 gpio_write(device, 1)GPIO读操作#可以用py打印下读到的值 read_num=gpio_write(device1) print(read_num)IO操作的拓展其实可以从前文的uart和本文的gpio操作进行类比在官方文档中的配置,文档中都有详细的说明,只要慢慢啃就可以,这里像别的SPI IIC的io配置都可以类似这更改这里:lib = MicroblazeLibrary(base.接口, ['设备'])即可比如这里如果想利用RPI的iic接口:lib = MicroblazeLibrary(base.RPI, ['iic'])如果想并用gpio和uart那么只需要在list增加即可,如:lib = MicroblazeLibrary(base.RPI, ['gpio','uart'])别的操作大家可以自己尝试,好了就为大家介绍到这里。
PYNQ是赛灵思开发的一个面向创客的板子,其实仔细查下官方的文档操作还是很方便的,但是东西太杂了,,,实在不好找到,下面这篇博客将解决关于PYNQ是uart的使用的部分问题,欢迎大家补充学习内容PYNQ的串口使用开发环境PYNQ 这里我用的是2.3的官方镜像,jupyter-Notebook官方文档参考[https://pynq.readthedocs.io/en/latest/pynq_libraries.html#pynqmicroblaze]在官方文档中可以看到是这样说的:PYNQ库典型的嵌入式系统支持的外围设备的固定组合(例如SPI,IIC,UART,视频,USB)。也可能有一些GPIO(通用输入/输出引脚)可用。GPIO的可用的在CPU基于嵌入式系统的数量通常是有限的,且GPIO也由主CPU控制。作为被管理系统的其余部分的主CPU,GPIO性能通常是有限的。ZYNQ平台通常比一个典型的嵌入式系统提供更多的IO引脚。专用的硬件控制器和附加软处理器可以在PL中实现,并连接到外部接口。这意味着,这些接口性能比其他嵌入式系统高得多。PYNQ在Linux上运行它默认使用以下ZYNQ PS外设:SD卡引导系统和宿主Linux文件系统,以太网连接到笔记本Jupyter,UART的Linux终端的接入和USB。USB端口等标准接口可以用来关断的,现成的USB和其他外围设备连接到该PS ZYNQ在那里他们可以在Python / Linux的控制。该PYNQ图像目前包括最常用的USB摄像头驱动程序,无线外设等标准的USB设备。其他外围设备可以连接到并从ZYNQ PL访问。例如HDMI,音频,按钮,开关,LED,以及通用接口,包括Pmods,和Arduino的。由于PL是可编程的,可以使用之前,其提供了一种用于这些外设或接口控制器的覆盖必须被加载。硬件IP的库包含在Vivado其可用于连接到广泛的接口标准和协议。PYNQ提供一个Python API对一些普通外设,包括视频(HDMI IN和OUT),GPIO装置(按钮,开关,指示灯)和传感器和致动器。该PYNQ API还可以扩展到支持额外的IP。ZYNQ平台通常具有一个或多个报头或接口,其允许外部外围设备的连接,或直接连接到所述ZYNQ PL引脚。关的,现成的外围设备的范围可以连接到PMOD和Arduino的接口。其他外设可以通过适配器连接到这些端口,或用面包板。请注意,虽然外围可以物理地连接到ZYNQ PL销,控制器必须构建覆盖和软件驱动程序提供的,可用于外围之前。该PYNQ库提供支持,为PynqMicroBlaze子系统,使预编译的应用程序被加载,并且新的应用程序进行创建和编译Jupyter。PYNQ还提供了一种覆盖的低电平控制包括存储器映射的IO读取/写入,存储器分配(例如,用于使用由PL主)覆盖的,控制和管理(下载的覆盖,在覆盖读取IP支持),以及PL的低电平控制(下载的比特流)。可以发现在官方的PYNQ Microblaze的库中我们可以找到想要的UART,那么就去这里找找吧在这个库我们发现了嵌入式的常用的一些驱动协议接口的设置函数emmm虽然官方库给出来了,但是由于PYNQ的国内的使用者还算少,所以咱们对于他们官方函数的使用还是不清晰,经过本人的不断的试错和朋友的帮助,现在可以完成在PYNQ进行发和收首先,还是看下官方的说明吧:UART DevicesThis device driver controls a UART master(此设备驱动器控制一个UART主设备)uart typeHandle to a UART master device.(处理UART主设备)头文件查到的是这样的 /* * UART API */ typedef int uart; uart uart_open(unsigned int tx, unsigned int rx)Open a UART device on the specified pins(打开特定管脚的uart设备)uart uart_open_device(unsigned int device)Open a UART device by base address or index(由基地址或索引打开UART设备)void uart_read(uart dev_id, char* read_data, unsigned int length)Read a fixed length of data from the UART(读取数据的固定长度从UART)void uart_write(uart dev_id, char* write_data, unsigned int length)Write a block of data to the UART(写数据到UART块)void uart_close(uart dev_id)Close the handle.(关闭串口)函数讲解这里我们需要用到的是uart uart_open(unsigned int tx, unsigned int rx),void uart_read(uart dev_id, char* read_data, unsigned int length),void uart_write(uart dev_id, char* write_data, unsigned int length)打开串口函数:uart uart_open(unsigned int tx, unsigned int rx)根据自己的需要设置引脚,这里我们随便设置两个引脚:这个引脚的数字具体参考官方的原理图:我这里设置的是14 15是RPIO_14 RPIO_15也就是这里的8和10串口的接收函数:void uart_read(uart dev_id, char* read_data, unsigned int length)dev_id:这里是之前uart type,也就是uart_open()函数的返回值char* read_data:这里是串口接收的数据存储的地址unsigned int length: 接收的数据是固定长度的,这里是定义接收的字符串长度的串口的发送函数:void uart_write(uart dev_id, char* write_data, unsigned int length)dev_id:这里是之前uart type,也就是uart_open()函数的返回值,这里返回值的具体定义还在扒代码。。char* read_data:这里是串口发送的数据存储的地址unsigned int length: 发送的数据是固定长度的,这里是定义发送的字符串长度的代码解读首先加载官方的overlays,并且把mb.lib包含下:from pynq.overlays.base import BaseOverlay from pynq.lib import MicroblazeLibrary base = BaseOverlay('base.bit') print('finish') 打开串口lib = MicroblazeLibrary(base.RPI, ['uart']) device = lib.uart_open(14,15)串口发送测试:list2 = [101] for num in range(10,20): lib.uart_write(device,list2, len(list2)) number=[101,110]这里是我测试的代码,写的可能比较乱,当时只是为测试来着number=[101,110] flag=0 flag1=0 while(1): if(base.buttons[3].read()==1): if(flag==0): print(number) flag=1 #lib.uart_read(device,number,len(number)) lib.uart_write(device,number,len(number)) lib.uart_read(device,number,len(number)) if(base.buttons[1].read()==1&flag1==0): flag1==1 lib.uart_read(device,number,len(number)) print(number) elif(base.buttons[0].read()==1): break base.rgbleds[5].write(2)给出一段只接收的代码:number=[101,110] while(1): lib.uart_read(device,number,len(number))理论上这样就可以读取长度为2的数据了,注意:这里读取到的数据都是数字,如果要对字符串进行操作的话,还需要对存数据的列表进行转换操作,后面给出字符串的转换操作字符串的转换操作可能比较笨,欢迎py大佬指教nit_dat=['0','0','.','0','0','c','m','0','0','.','0','C','0','0','0']#初始化传感器数据 rxd_dat=[ord(i) for i in init_dat]#接收数据缓冲区 show_dat=[chr(i) for i in rxd_dat] #使用列表推导式把列表中的单个元素全部转化为str类型 print(show_dat) finalshow_dat= "".join(show_dat) print(finalshow_dat)暂时先分享这样一个uart使用,后面发掘到别的再继续发文。
train loss与test loss结果分析train loss 不断下降,test loss不断下降,说明网络仍在学习; 在训练过程中loss可能会出现几次震荡现象,但是整体有上升趋势就说明网络还在学习,可以继续训练。train loss 不断下降,test loss趋于不变,说明网络过拟合过拟合最直观的表现就是 training accuracy 特别高,但是testing accuracy 特别低,即两者相差特别大。训练完了以后可以测试一下网络在训练集的正确率,如果和测试集的正确率相差特别大(比如20%),则说明网络过拟合了。出现过拟合的原因(原文链接:https://blog.csdn.net/NIGHT_SILENT/article/details/80795640)1. 训练集的数量级和模型的复杂度不匹配。训练集的数量级要小于模型的复杂度;2. 训练集和测试集特征分布不一致;3. 样本里的噪音数据干扰过大,大到模型过分记住了噪音特征,反而忽略了真实的输入输出间的关系;4. 权值学习迭代次数足够多(Overtraining),拟合了训练数据中的噪声和训练样例中没有代表性的特征。解决办法:即和已有的数据是独立同分布的,或者近似独立同分布的。一般有以下方法:从数据源头采集更多数据复制原有数据并加上随机噪声重采样根据当前数据集估计数据分布参数,使用该分布产生更多数据等train loss 趋于不变,test loss不断下降,说明数据集100%有问题;train loss 趋于不变,test loss趋于不变,说明学习遇到瓶颈,需要减小学习率或批量数目;train loss 不断上升,test loss不断上升,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题
1.PYNQ 简介PYNQ-Z1 开发板支持 PYNQ 项目,这是一个新的开源框架,使嵌入式编程人员能够在无 需设计可编程逻辑电路的情况下即可充分发挥 Xilinx Zynq All Programmable SoC(APSoC) 的功能。与常规方式不同的是,通过 PYNQ,用户可以使用 Python 进行 APSoC 编程,并 且代码可直接在 PYNQ-Z1 上进行开发和测试。通过 PYNQ,可编程逻辑电路将作为硬件 库导入并通过其 API 进行编程,其方式与导入和编程软件库基本相同。PYNQ-Z1 开发板是 PYNQ 开源框架的硬件平台。在 ARM A9 CPU 上运行的软件包括:载有 Jupyter Notebooks 设计环境的网络服务器IPython 内核和程序包LinuxFPGA 的基本硬件库和 API42. Jupyter Notebook 简介Jupyter Notebook 是一个基于浏览器的交互式编程计算环境。在使用 Jupyter Notebook 编 程时,文件里可以包含代码、画图、注释、公式、图片和视频。当 PYNQ 开发板上安装好 镜像文件,就可以在 Jupyter Notebook 里轻松地用 Python 编程,使用硬件库及 Overlay 控制硬件平台及交互。3. 软硬件准备1) 硬件准备 PYNQ-Z1 开发板 以太网线 Micro USB 数据线 空白 Micro SD 卡(最少 8GB 容量)2) 软件准备 电脑上安装有支持 Jupyter 的浏览器提示:以下浏览器的最新稳定版本可支持 Jupyter Notebook*:✓ Chrome✓ Safari✓ Firefox* 主要由 Jupyter Notebook 使用的 WebSockets 和可变沙箱模型决定不支持 Jupyter 的浏览器: Safari,低于版本 5 Firefox,低于版本 6 Chrome,低于版本 13 全部 Opera:CSS 渲染原因导致,但是执行时有可能可以用 Internet Explorer 浏览器,低于版本 10 Internet Explorer 浏览器,版本 10 及以上(同 Opera) 基于 IE 的 360 浏览器* 请注意,Safari 在 HTTPS(SSL 安全加密的超文本链接协议)和不可信证书下无法正 常工作(主要是 WebSockets 无法正常工作) 将空白的 SD 卡插入电脑(最小需 8GB 容量),烧写镜像文件 Windows 系统:使用 win32DiskImager 烧写。Image File 选择下载好的镜像文件。Device 选择 SD 卡的位置,一般会自动分配为 E 盘或 F 盘。 获取镜像文件 下载 PYNQ-Z1 镜像文件http://www.digilent.com.cn/community/411.html并解压 Linux 系统/MacOS:使用系统自带 dd 命令,在不同操作系统上烧写Micro SD 的操作细节,可参考教程 http://pynq.readthedocs.io/en/latest/appendix.html#writing-the-sd-card4. PYNQ-Z1 硬件设置1. 如上图所示,将跳帽插在最上边两个排针上,设置 boot 跳线(板上标记的 JP4)到 SD 位置,选择为从 SD 卡驱动2. 要想通过 Micro USB 线对 PYNQ-Z1 进行供电,需如图所示将跳帽插在的最下边两个 排针上,设置电源跳线(JP5)到 USB 的位置(你也可以使用 12V 外部电源对 PYNQ-6 Z1 进行供电,将跳帽插在的最上边两个排针上,设置电源跳(JP5)到 REG 的位置)3. 将已安装镜像文件的 SD 卡插入 SD 卡槽(如图所示,SD 卡槽在开发板下方右侧边缘)4. 使用 Micro USB 线将 PYNQ 开发板的 PROG UART(J14)接口连接到电脑。这将用来 给 PYNQ 供电以及作为串口通信5. 使用网线将 PYNQ 开发板连接到路由器或电脑(根据网线端口的选择,后续操作会有不同)*6. 将开关拨到 ON 以打开 PYNQ,等待系统启动。大约一分钟后将有两个蓝色的 LED 和 四个黄绿色的 LED 同时闪动,随后蓝色 LED 关闭,四个黄绿色的 LED 灯亮。此时系统 启动完毕。* 关于板载以太网连接的详细说明你可以将 PYNQ-Z1 的以太网接口和以下设备连接: 连接到一个路由器或者交换机上,与你的电脑在同一网络下 直接连在电脑的以太网接口上可以的话,请将你的开发板连接到一个具有以太网访问的网络上。这可以让你更新板子上的软件并可以安装新的软件包。• 连接到网络如果你通过 DHCP 服务器连接到一个局域网络,你的板子会自动获取一个 IP 地址,你必须保证有足够的权限通过网络访问到设备,否则板子可能无法正常访问。路由器/网络交换机(DHCP)将板载以太网接口连接到路由器/交换机上通过浏览器访问 http://pynq:9090更改主机名称(根据自身需求)配置代理(根据自身需求)• 直接连接到电脑此时,你需要一台有以太网接口的电脑,同时你需要拥有配置网络接口的权限。通过直接相连,你就可以访问使用 PYNQ-Z1 了。但是这里需要注意,除非你能将以太网与电脑上具有 Internet 访问的连接进行桥接,否则你的 PYNQ-Z1 是无法访问 Internet 的。在没有 Internet 连接的情况下,你不能更新或者加载新的软件包。直接连接你的电脑(静态 IP)给电脑配置一个静态的 IP将板载以太网接口与电脑的直接相连访问 http://192.168.2.99:9090*如何配置静态 IP 请参见 https://pynq.readthedocs.io/en/latest/appendix.html#assign-your-computer-a-static-ip5. 连接到 Jupyter 进行在线编程如果PYNQ通过网线连接到了路由器,PYNQ将被自动分配地址。打http://pynq:9090,用户名和密码都是 xilinx,输入后即可进入以下界面。如果 PYNQ 通过网线连接到了电脑,需要先设置电脑的 IP 地址,参考https://pynq.readthedocs.io/en/latest/appendix.html#assign-your-computer-a-static-ip,然后再打开 http://192.168.2.99:9090。同样,输入用户名及 密码 xilinx,即可进入以下界面。默认的主机名是 pynq,默认静态 IP 地址是 192.168.2.99。如果你改变了主机名称或者板子上的静态 IP 地址,你需要改变你访问的地址。第一次连接时,电脑会花费几秒钟的时间来确定主机名和 IP 地址。PYNQ 通过 Jupyter Notebook 的形式来提供各种示例文档。你可以以网页形式浏览这些示例项目文档,或者如果你有一个正在运行 PYNQ 镜像的板子,你可以可交互式地查看并运行这些 Notebook 文档。你也可以在 Jupyter 主页上的 Getting_Started 文件夹中找到可以使用的 Notebook 文档。这里也有许多示例文档来展示如何使用各种板载设备。此外,我们还提供了一些样例展示如何使用不同的板载外围设备。目前,所有我们已对所有这些示例文档进行了分类: common: 无针对性 overlay 的示例项目 base: 与 PYNQ-Z1 base overlay 相关的示例项目 logictools: 与 PYNQ-Z1 logictools overlay 相关的示例项目当你打开一个笔记本并作出任何修改,或者执行代码片段,notebook 文档都将会被更改。这就需要你打开一个新的 notebook 时做好备份。如果你需要恢复原始版本,你可以从 PYNQ Github https://github.com/xilinx/pynq 项目页面上下载全部笔记本。在 Running 一栏下,则可以看到正在运行的项目。• 访问板载文件在 PYNQ 板上,运行有一个文件共享服务:Samba。通过它,板子上的主目录可以作为网络驱动器访问,同时你可以将文件在板子和电脑间传递。\\pynq\xilinx # If connected to a Network/Router with DHCP \\192.168.2.99\xilinx # If connected to a Computer with a Static IP或者在 Linux 下:smb://pynq/xilinx # If connected to a Network/Router with DHCP smb://192.168.2.99/xilinx # If connected to a Computer with a Static IP然后会跳出下图:Samba 服务器的用户名和密码都是 xilinx。注意:如果必要,请修改主机名/IP 地址。• 更改 hostname如果你连接在一个其它 PYNQ-Z1 开发板可能已经连接入的网络下,建议你立即更改你的主机名称。通常,这种情况在工作或者校园环境下会比较常见。PYNQ 的默认 hostname是 pynq , 终 端 被 内 嵌 在 Jupyter 中 。 在 Jupyter 的 主 页 pynq:9090 界面 中 打 开New>>Terminal,你将以 root 权限在浏览器中打开一个终端:在 Terminal 里输入以下指令更改 hostname(使用你自己希望给板子设置的主机名来替换 NEW_HOST_NAME 的位置):sudo /home/xilinx/scripts/hostname.sh NEW_HOST_NAME然后需要重启 PYNQ 才能生效(使用新的主机名重新连接):sudo shutdown -r now注意:如果你以 root 权限登录,则不需要使用 sudo。但是如果使用 xilinx 进行登录,sudo 必须添加在这些命令之前。如果你不能访问你的板子,浏览下面的步骤以通过 micro USB 线来打开终端。• 通过 USB 接口连接电脑终端如果你需要修改板载设置,但是无法访问通过 Jupyter 访问终端,你可以借助 USB 接口, 通过电脑终端控制 PYNQ。此时我们需要安装一个终端工具,比如 PuTTY 或者 Tera Term。 为了打开终端,你需要知道开发板所在 COM 端口。在 Windows 上,你可以在控制面板打开 Windows 设备管理器进行查看 打开设备管理器,展开端口项 找到 USB 串口所在 COM 端口,例如 COM5一旦你知道了 COM 端口,打开 PuTTY 并使用下列设置: 选择 Serial 输入 COM 端口号 输入波特率 然后点击 Open 启动在终端窗口中按 Enter(回车)以确保你能看到命令行:xilinnx@pynq:/home/xilinx#12完整的终端设置如下:115200 baud8 data bits1 stop bitNo ParityNo Flow Control在终端中按回车键,出现 xilinx@pynq:~$,即可输入指令控制 pynq。比如输入 hostname查看名称,输入 ifconfig 查看 IP 地址等。• 设置代理如果你的开发板连接到的是使用代理的网络中,你需要在板上设置代理服务器。按照上方教程打开终端,并输入下列命令并将“my_http_proxy:8080”和“my_https_proxy:8080”更改为你自己的设置:set http_proxy=my_http_proxy:8080set https_proxy=my_https_proxy:8080
建立软连接:ln -s 原目录 映射目录删除软连接的方法:sudo rm -rf 映射目录设置VMwave移动硬盘的挂载首先打开VMwave然后设置添加usb控制器,注意自己的电脑配置(本人的是3.0所以这里选择2.0)打开虚拟机后按图步骤进行关联,断开与主机的连接此时就可以在自己的Ubuntu上看到桌面的usb移动磁盘
MATLAB不但擅长於矩阵相关的数值运算,也适合用在各种科学目视表示(Scientific visualization)。下面将介绍MATLAB基本xy平面及xyz空间的各项绘图命令,包含一维曲线及二维曲面的绘制、列印及存档。plot是绘制一维曲线的基本函数,但在使用此函数之前,我们需先定义曲线上每一点的x及y座标。下例可画出一条正弦曲线:close all; x=linspace(0, 2*pi, 100); % 100个点的x座标 y=sin(x); % 对应的y座标 plot(x,y);MATLAB基本绘图函数plot: x轴和y轴均为线性刻度(Linear scale)loglog: x轴和y轴均为对数刻度(Logarithmic scale)semilogx: x轴为对数刻度,y轴为线性刻度semilogy: x轴为线性刻度,y轴为对数刻度画出多条曲线:只需将座标对依次放入plot函数即可:plot(x, sin(x), x, cos(x));若要改变颜色,在座标对后面加上相关字串即可:plot(x, sin(x), 'c', x, cos(x), 'g');若要同时改变颜色及图线型态(Line style),也是在座标对后面加上相关字串即可:plot(x, sin(x), 'co', x, cos(x), 'g*');plot绘图函数的叁数字元 颜色 字元 图线型态 y 黄色 . 点 k 黑色 o 圆 w 白色 x x b 蓝色 + + g 绿色 * * r 红色 - 实线 c 亮青色 : 点线 m 锰紫色 -. 点虚线 -- 虚线 图形完成后,我们可用axis([xmin,xmax,ymin,ymax])函数来调整图轴的范 围: axis([0, 6, -1.2, 1.2]); 此外,MATLAB也可对图形加上各种注解与处理: xlabel('Input Value'); % x轴注解 ylabel('Function Value'); % y轴注解 title('Two Trigonometric Functions'); % 图形标题 legend('y = sin(x)','y = cos(x)'); % 图形注解 grid on; % 显示格线我们可用subplot来同时画出数个小图形於同一个视窗之中:subplot(2,2,1); plot(x, sin(x)); subplot(2,2,2); plot(x, cos(x)); subplot(2,2,3); plot(x, sinh(x)); subplot(2,2,4); plot(x, cosh(x));其他各种二维绘图函数bar 长条图errorbar 图形加上误差范围fplot 较精确的函数图形polar 极座标图hist 累计图rose 极座标累计图stairs 阶梯图stem 针状图fill 实心图feather 羽毛图compass 罗盘图quiver 向量场图当资料点数量不多时,长条图是很适合的表示方式:close all; % 关闭所有的图形视窗 x=1:10; y=rand(size(x)); bar(x,y);如果已知资料的误差量,就可用errorbar来表示:下例以单位标准差来做资料的误差量:x = linspace(0,2*pi,30); y = sin(x); e = std(y)*ones(size(x)); errorbar(x,y,e)对于变化剧烈的函数,可用fplot来进行较精确的绘图,会对剧烈变化处进行较密集的取样如下例:fplot('sin(1/x)', [0.02 0.2]); % [0.02 0.2]是绘图范围若要产生极座标图形,可用polar:theta=linspace(0, 2*pi); r=cos(4*theta); polar(theta, r);对于大量的资料,我们可用hist来显示资料的分布情况和统计特性。下面几个命令可用来验证randn产生的高斯乱数分 :x=randn(5000, 1); % 产生5000个 ?=0,?=1 的高斯乱数 hist(x,20); % 20代表长条的个数rose和hist很接近,只不过是将资料大小视为角度,资料个数视为距离:1. x=randn(1000, 1); 2. rose(x);stairs可画出阶梯图:x=linspace(0,10,50); y=sin(x).*exp(-x/3); stairs(x,y);stems可产生针状图,常被用来绘制数位讯号:1. x=linspace(0,10,50); 2. y=sin(x).*exp(-x/3); 3. stem(x,y);stairs将资料点视为多边行顶点,并将此多边行涂上颜色:x=linspace(0,10,50); y=sin(x).*exp(-x/3); fill(x,y,'b'); % 'b'为蓝色feather将每一个资料点视复数,并以箭号画出:1. theta=linspace(0, 2*pi, 20); 2. z = cos(theta)+i*sin(theta); 3. feather(z);compass和feather很接近,只是每个箭号的起点都在圆点:theta=linspace(0, 2*pi, 20); z = cos(theta)+i*sin(theta); compass(z);画图并标注 clear all;clc;close all; f=@(x)-x.*log(x); s=integral(f,1,2); x=0:0.001:1; y=f(x); plot(x,y,'Linewidth',2);grid on; xlabel('x');ylabel('p'); hold on; p=find(y==max(y)); %text(x(p),y(p),'*','color','r','linewidth',9); text(x(p),y(p),['(',num2str(x(p)),',',num2str(y(p)),')'],'color','r');
AHP (Analytic Hierarchy Process)层次分析法是美国运筹学家T. L. Saaty教授于二十世纪70年代提出的一种实用的多方案或多目标的决策方法,是一种定性与定量相结合的决策分析方法。常被运用于多目标、多准则、多要素、多层次的非结构化的复杂决策问题,特别是战略决策问题,具有十分广泛的实用性。用AHP分析问题大体要经过以下五个步骤:1、建立层次结构模型将决策的目标、考虑的因素(决策准则)和决策对象按它们之间的相互关系分为最高层、中间层和最低层,绘出层次结构图。2、构造判断矩阵在确定各层次各因素之间的权重时,如果只是定性的结果,则常常不容易被别人接受,因而Saaty等人提出:一致矩阵法,即:不把所有因素放在一起比较,而是两两相互比较。对比时采用相对尺度,以尽可能减少性质不同因素相互比较的困难,以提高准确度。3、层次单排序所谓层次单排序是指,对于上一层某因素而言,本层次各因素的重要性的排序。4、判断矩阵的一致性检验所谓一致性是指判断思维的逻辑一致性。如当甲比丙是强烈重要,而乙比丙是稍微重要时,显然甲一定比乙重要。这就是判断思维的逻辑一致性,否则判断就会有矛盾。5、层次总排序确定某层所有因素对于总目标相对重要性的排序权值过程,称为层次总排序。这一过程是从最高层到最底层依次进行的。对于最高层而言,其层次单排序的结果也就是总排序的结果。 层次分析法的优点系统性——将对象视作系统,按照分解、比较、判断、综合的思维方式进行决策——系统分析(与机理分析、测试分析并列);实用性——定性与定量相结合,能处理传统的优化方法不能解决的问题;简洁性——计算简便,结果明确,便于决策者直接了解和掌握。总结层次分析法的基本步骤:角度一:实际问题分解——>多个因素建立——>层次结构确定——>诸因素的相对重要性计算——>权向量判断——>综合决策角度二:建立层次结构模型——>构造判断矩阵——>层次单排序——>一致性检验——>层次总排序。一.指标体系的建立应遵循的原则:指标以少不宜多指标应具有独立性指标应具有代表性 指标可行注意:以上几条原则在解决实际问题是参考,在实际中要灵活考虑应用。需要注意的是,指标体系的确定有很大的主观随意性。虽然指标体系的确定有经验法跟数学方法两种,但多数研究均采用经验确定法。‘因为层次分析法中的主观成分过多,所以最好采用专家调研的办法来尽可能来降低主观成分的影响。专家调研法1、专家调研法是一种常用的方法。即向专家发函,征求其意见。评价者可以根据评价目标及评价对象的特特征,在设计的调查表中列出一系列的评价指标,分别征询专家所涉及的评价指标的意见,然后进行统计处理,并反馈咨询结果,若专家意见趋于集中,则由最后一次确定出具体的评价指标体系。2、专家调研法的特征匿名性 完全消除了专家互相之间的的影响轮间情况反馈 协调人对每一轮的结果做出统计,并将其作为反馈材料发给每一个专家,供下一轮评价时参考结果的统计特性 采用统计法对结果进行处理二、指标权重的确定: 1.指标的权重是指评价过程中其相对重要程度的一种主观客观观测度的反应,指标间的权重差异是由以下三点造成的: 评价者对各指标的重视程度不同,反应评价者的主观差异; 各指标在评价中所起的作用不同,反映各个指标之间的客观差异; 各指标之间的可靠程度不同,反映了各指标所提供的信息的可靠性不同。 2.加权的方法有两种 (1)经验加权法,也称定性加权法。它的优点是有专家直接评估,简便易行。 (2)数学加权法,也称定量加权法。它以经验为基础,数学原理为背景,间接生成,具有较强的科学性。进行归一化处理归一化公式如下:一般来说,以上方法依据专家知识、经验和个人价值观对指标体系进行分析、判断并主观赋权。一般来说,这样所确定的权数能正确反映各指标的重要程度,保证评价结果的准确性。但是为了提高准确性,也可以采用确定权重的层次分析法。该方法对各指标之间重要程度的分析更具有逻辑性,加上数学处理,使得可信度加大,应用范围较广。
面将主要从三方面进行大致讲解,灰色预测概念及原理、灰色预测的分类及求解步骤、灰色预测的实例讲解。一、灰色预测概念及原理:1.概述:关于所谓的“颜色”预测或者检测等,大致分为三色:黑、白、灰,在此以预测为例阐述。其中,白色预测是指系统的内部特征完全已知,系统信息完全充分;黑色预测指系统的内部特征一无所知,只能通过观测其与外界的联系来进行研究;灰色预测则是介于黑、白两者之间的一种预测,一部分已知,一部分未知,系统因素间有不确定的关系。细致度比较:白>黑>灰。2.原理:灰色预测是通过计算各因素之间的关联度,鉴别系统各因素之间发展趋势的相异程度。其核心体系是灰色模型(Grey Model,GM),即对原始数据做累加生成(或者累减、均值等方法)生成近似的指数规律在进行建模的方法。二、灰色预测的分类及求解步骤:1.GM(1,1)与GM(2,1)、DGM、Verhulst模型的分类比较:2.求解步骤思维导图:其中预测过程可能会涉及以下三种序列、白化微分方程、以及一系列检验,由于大致都相同,仅仅是某些使用累加和累减,而另外一些则使用累加、累减和均值三个序列的差别而已。于是下面笔者将对其进行归纳总结再进行绘制思维导图,帮助读者理解。(1)原始序列(参考数据列):(2)1次累加序列(1-AGO):(3)1次累减序列(1-IAGO):(也就是原始序列中,后一项依次减去前一项的值,例如,[x(2)-x(1),x(3-x(2),...,x(n)-x(n-1))]。)(4)均值生成序列:(这是对累加序列"(前一项+后一项)/2"得出的结果。) 求解步骤:三、灰色预测的实例讲解:1.使用GM(1,1)的预测检验“北方某城市1986年-1992年道路噪声交通 平均声级数据:”见下图:x0 = [71.1 72.4 72.4 72.1 71.4 72 71.6]'; %这里是列向量,相当于原始数据中因变量 n = length(x0); lamda = x0(1:n-1)./x0(2:n) %计算级比 range = minmax(lamda') %计算级比的范围 x1 = cumsum(x0) B = [-0.5*(x1(1:n-1)+x1(2:n)),ones(n-1,1)]; %这是构造的数据矩阵B Y = x0(2:n); %数据向量Y u = B\Y %拟合参数u(1)=a,u(2)=b syms x(t) x = dsolve(diff(x)+u(1)*x==u(2),x(0)==x0(1)); %建立模型求解 xt = vpa(x,6) %以小数格式显示微分方程的解 prediction1 = subs(x,t,[0:n-1]); %求已知数据的预测值 prediction1 = double(prediction1); %符号数转换成数值类型,以便做差分运算 prediction = [x0(1),diff(prediction1)] %差分运算,还原数据 epsilon = x0'-prediction %计算残差 delta = abs(epsilon./x0') %计算相对残差 rho = 1-(1-0.5*u(1))/(1+0.5*u(1))*lamda'%计算级比偏差值,u(1)=a2.使用GM(2,1)的MATLAB实例:题目:已知=(41,90,61,78,96,104),试建立GM(2,1)模型。%% -------------2.GM(2,1)预测模型-------------------%% x0 = [41 49 61 78 96 104]; n = length(x0); add_x0 = cumsum(x0);%1次累加序列 minus_x0 = diff(x0)'; %1次累减序列 z = 0.5*(add_x0(2:end)+add_x0(1:end-1))';%计算均值生成序列 B = [-x0(2:end)',-z,ones(n-1,1)]; u = B\minus_x0 %最小二乘法拟合参数 syms x(t) x = dsolve(diff(x,2)+u(1)*diff(x)+u(2)*x == u(3),x(0) == add_x0(1),x(5) == add_x0(6)); %求符号解 xt = vpa(x,6) %显示小数形式的符号解 prediction = subs(x,t,0:n-1); prediction = double(prediction); x0_prediction = [prediction(1),diff(prediction)];%求已知数据点的预测值 x0_prediction = round(x0_prediction) %四舍五入取整数 epsilon = x0-x0_prediction %求残差 delta = abs(epsilon./x0) %求相对误差
1、传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间,无法了解数据未达终点的状态。因此有必要增强网络层提供服务的服务质量。2、引入传输层的原因 面向连接的传输服务与面向连接的网络服务类似,都分为建立连接、数据传输、释放连接三个阶段;编址、寻址、流控制也是类似的。无连接的传输服务与无连接的网络服务也非常类似。一个很显然的问题:既然传输层的服务与网络层的服务如此相似,那么为什么我们还要两个独立的层呢?原因在于:传输层的代码完全运行在用户的机器上,但是网络层主要运行在由承运商控制的路由器上。试想以下几种情况?① 网络层提供的服务不够用;② 频繁的丢失分组;③ 路由器时常崩溃。 用户在网络层上并没有真正的控制权,所以他们不可能用最好的路由器或者在数据链路层上用更好的错误处理机制来解决服务太差的问题。唯一的可能是在网络层之上的另一层中提高服务质量。这就是传输层存在的必要性。 传输层的重要性:不仅仅是另外一层,它是整个协议层次的核心所在。如果没有传输层,那么分层协议的整个概念将变得没有意义。 传输层的任务:在源机器和目标机器之间提供可靠的、性价比合理的数据传输服务,并且与当前使用的物理网络完全独立。3、传输层的功能 数据传送,不关心数据含义,进程间通信。 弥补高层(上3层)要求与网络层(基于下3层)数据传送服务质量间的差异(差错率、差错恢复能力、吞吐率、延时、费用等),对高层屏蔽网络层的服务的差异,提供稳定和一致的界面。4、传输层协议与网络层协议的主要区别 网络层(IP层)提供点到点的连接即提供主机之间的逻辑通信,传输层提供端到端的连接——提供进程之间的逻辑通信。5、传输层的用途 传输层将数据分段,并进行必要的控制,以便将这些片段重组成各种通信流。在此过程中,传输层主要负责:① 跟踪源主机和目的主机上应用程序间的每次通信;② 将数据分段,并管理每个片段;③ 将分段数据重组为应用程序数据流;④ 标识不同的应用程序。6、端口号的概念 运行在计算机中的进程是用进程标识符来标志的。运行在应用层的各种应用进程却不应当让计算机操作系统指派它的进程标识符。这是因为在因特网上使用的计算机的操作系统种类很多,而不同的操作系统又使用不同格式的进程标识符。为了使运行不同操作系统的计算机的应用进程能够互相通信,就必须用统一的方法对TCP/IP体系的应用进程进行标志。解决这个问题的方法就是在运输层使用协议端口号(protocol port number),或通常简称为端口(port)。虽然通信的终点是应用进程,但我们可以把端口想象是通信的终点,因为我们只要把要传送的报文交到目的主机的某一个合适的目的端口,剩下的工作(即最后交付目的进程)就由 TCP来完成。7、传输层的主要协议 TCP/ IP传输层的两个主要协议都是因特网的重要标准,传输控制协议TCP(Transmission Control Protocol)[RFC 768]、用户数据报协议UDP(User Datagram Protocol)[RFC 793]。 传输层的数据流要在网络端点之间建立逻辑连接。如果使用UDP,传输层的首要任务是将数据从源设备传输到目的设备。如果使用TCP,传输层主要通过滑动窗口来提供端到端控制,以及利用确认序列号和确认信息提供可靠性。传输层定义主机应用程序之间端到端的连接。8、TCP&UDP的比较 TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。 UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。 问题1:什么是面向连接、面向无连接? 面向连接举例:两个人之间通过电话进行通信。面向无连接举例:邮政服务,用户把信函放在邮件中期待邮政处理流程来传递邮政包裹。显然,不可达代表不可靠。从程序实现的角度解析面向连接、面向无连接如下: TCP面向连接,UDP面向无连接(在默认的阻塞模式下): 在TCP协议中,当客户端退出程序或断开连接时,TCP协议的recv函数会立即返回不再阻塞,因为服务端自己知道客户端已经退出或断开连接,证明它是面向连接的; 而在UDP协议中,recvfrom这个接收函数将会始终保持阻塞,因为服务端自己不知道客户端已经退出或断开连接,证明它是面向无连接的) TCP通信需要服务器端侦听listen、接收客户端连接请求accept,等待客户端connect建立连接后才能进行数据包的收发(recv/send)工作。 而UDP则服务器和客户端的概念不明显,服务器端即接收端需要绑定端口,等待客户端的数据的到来。后续便可以进行数据的收发(recvfrom/sendto)工作。问题2:什么是面向字节流、面向报文? 面向字节流:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 面向报文:面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。(谢希仁,第五版。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送),即一次发送一个报文)。问题3:在默认的阻塞模式下,为什么说TCP无边界,UDP有边界? 对于TCP协议,客户端连续发送数据,只要服务端的这个函数的缓冲区足够大,会一次性接收过来,即客户端是分好几次发过来,是有边界的,而服务端却一次性接收过来,所以证明是无边界的; 而对于UDP协议,客户端连续发送数据,即使服务端的这个函数的缓冲区足够大,也只会一次一次的接收,发送多少次接收多少次,即客户端分几次发送过来,服务端就必须按几次接收,从而证明,这种UDP的通讯模式是有边界的。问题4:UDP 如何发送大量的数据?如何处理分包? 用 updclient 一次不能发送太大的数据量,不然就会报错:一个在数据报套接字上发送的消息大于内部消息缓冲器或其他一些网络限制,或该用户用于接收数据报的缓冲器比数据报小。但不知道一次到底能发多少字节?如果把一个大的字节数组拆分成若干个字节数组发送,那么接收时如何判断和处理呢? 答:方法很简单:1、在客户端将你要发送的内容(文件什么的都可以)分块,每块内容进行编号,然后发送;2、服务端在接收到你的分块数据以后,根据你的客户端数据内容的编号重新组装;3、一般我们在发送数据的时候,尽量采用比较小的数据块的方式(我的都没有超过1024的),数据块太大的话容易出现发送和接收的数据时间长,匹配出问题。问题5.UDP发包的问题 问:udp发送两次数据,第一次 100字节,第二次200字节,接包方一次recvfrom( 1000 ),收到是 100,还是200,还是300? 答:UDP是数据报文协议,是以数据包方式,所以每次可以接收100,200,在理想情况下,第一次是无论recvfrom多少都是接收到100。当然,可能由于网络原因,第二个包先到的话,有可能是200了。对可能会由于网络原因乱序,所以可能先收到200,所以自定义的udp协议包头里都要加上一个序列号,标识发送与收包对应。问题6.TCP的发包问题 问:同样如果换成tcp,第一次发送 100字节,第二次发送200字节,recv( 1000 )会接收到多少? 答:tcp是流协议,所以recv( 1000 ),会收到300 tcp自己处理好了重传,保证数据包的完整性问题7.有分片的情况下如下处理问:如果MTU是1500,使用UDP发送 2000,那么recvfrom(2000)是收到1500,还是2000? 答:还是接收2000,数据分片由ip层处理了,放到udp还是一个完整的包。接收到的包是由路由路径上最少的MTU来分片,注意转到UDP已经在是组装好的(组装出错的包会经crc校验出错而丢弃),是一个完整的数据包。问题8.分片后的处理 问:如果500那个片丢了怎么办?udp又没有重传答:udp里有个crc检验,如果包不完整就会丢弃,也不会通知是否接收成功,所以UDP是不可靠的传输协议,而且TCP不存在这个问题,有自己的重传机制。在内网来说,UDP基本不会有丢包,可靠性还是有保障。当然如果是要求有时序性和高可靠性,还是走TCP,不然就要自己提供重传和乱序处理( UDP内网发包处理量可以达 7M~10M/s )问题9.不同连接到同一个端口的包处理 问:TCP A -> C发100 B -> C发200 AB同时同一端口 C recv(1000) ,会收到多少? 答:A与C是一个tcp连接,B与C又是另一个tcp连接,所以不同socket,所以分开处理。每个socket有自己的接收缓冲和发送缓冲问题10.什么是TCP粘包 在应用开发过程中,基于TCP网络传输的应用程序有时会出现粘包现象(即发送方发送的若干包数据到接收方接收时粘成一包)。详细解释如下:由于TCP是流协议,对于一个socket的包,如发送10AAAAABBBBB两次,由于网络原因第一次又分成两次发送, 10AAAAAB和BBBB,如果接包的时候先读取10(包长度)再读入后续数据,当接收得快,发送的慢时,就会出现先接收了 10AAAAAB,会解释错误 ,再接到BBBB10AAAAABBBBB,也解释错误的情况。这就是TCP的粘包。 在网络传输应用中,通常需要在网络协议之上再自定义一个协议封装一下,简单做法就是在要发送的数据前面再加一个自定义的包头,包头中可以包含数据长度和其它一些信息,接收的时候先收包头,再根据包头中描述的数据长度来接收后面的数据。详细做法是:先接收包头,在包头里指定包体长度来接收。设置包头包尾的检查位(如群空间0x2开头,0x3结束来检查一个包是否完整)。对于TCP来说:1)不存在丢包,错包,所以不会出现数据出错 2)如果包头检测错误,即为非法或者请求,直接重置即可 为了避免粘包现象,可采取以下几种措施。 一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满; 二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象; 三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。问题11.图解TCP的3次握手建立连接,4次握手释放连接? TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP传输连接的建立和释放是每一次面向连接的通信中必不可少的过程。因此,传输连接就由三个阶段,即:连接建立、数据传送和连接释放。 这里的SYN=SYNchronization,SYN=1,ACK=0,表示连接请求报文段;同意建立连接则SYN=1,ACK=1,连接后所有的ACK=1。三次握手(three-way handshake)方案解决了由于网络层会丢失、存储和重复分组带来的问题。试想不进行三次握手可能出现的问题?如上图所示,如果仅仅是2次握手的话,可能出现的问题如下: Host A发送的数据包由于网络的原因,出现了滞留,即延时到达了HostB。此时,B以为HostA发来了请求,于是就向HostA发送确认报文,以建立连接。而HostA收到报文后,由于其没有向HostB发起建立连接的请求,因此不会理睬HostB的确认,也不会向HostB发送数据。而此时的B认为已经建立起连接了,就等待HostA发送的数据,导致HostB的资源白白浪费!
2022年10月