板级调试小助手(2)ZYNQ自定义IP核构建属于自己的DDS外设
一、前言
在上期文章中讲述了小助手的系统结构和原理。在PYNQ的框架开发中,我们一般可以将PL端当做PS端的一个外设,通过读写寄存器的方式来操作外设的功能,就类似于在开发ARM和DSP中操作外设一样,不同时的是,我们可以通过FPGA定制我们自己需要的外设结构,例如本次文章需要提到的DDS外设。
二、外设功能介绍
在开发外设之前首先要明确这个外设的功能:
1、由于外部电流采用的是八路DA7512单通道DAC所以要实现DDS功能我们就需要再FPGA中搭建一个正弦波发生器,当然正弦波发生器有多种多样的实现方式,可以采用ROM查表的方式,也可以使用DSP实时计算。我这里就直接使用了Vivado提供的DDS IP核了(其本质也是通过ROM查表的方式),IP设置如下图1所示
2、DDS生成的正弦波需要可以控制其频率和初始相位功能;
3、虽然有DDS功能但是也同样要兼容普通DA功能;
4、一共八个通道,所以需要对八个通道的状态进行设置;
综上,我们可以设计出DAC控制寄存器和DAC波形控制器如下图2和图3所示
三、外设搭建
在ZYNQ中,搭建数据传输比较慢的外设时可以使用AXI_GP接口总线;AXI_GP接口总线可以视作为一种寄存器映射总线。最后将总线和逻辑功能封装成一个IP核,如下图4所示(这里就不放具体代码了,详细代码请移步到开源地址)
该模块具有一个AXI_GP接口的从机和七个DAC波形控制器数据,和一个DAC控制器数据,后续的DDS模块就利用这些寄存器数据来对DAC外设控制,DDS顶层代码如下:
//!DAC_DDS顶层,这里用来通过PS端写来的寄存器控制DAC的输出
module dac_dds_Top(input sysClk, //系统时钟input dacClk, //!DAC的时钟,最大为30Minput sysRst,//DAC外设接口input [31:0]Dac_Ch2_reg, //!通道2数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch3_reg, //!通道3数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch4_reg, //!通道4数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch5_reg, //!通道5数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch6_reg, //!通道6数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch7_reg, //!通道7数据寄存器,详细参考寄存器手册input [31:0]Dac_Ch8_reg, //!通道8数据寄存器,详细参考寄存器手册input [31:0]Dac_Ctrl_reg, //!DAC控制寄存器,DAC模式:0=关闭DAC;1=普通DAC输出;2=正弦波输出//DAC7512引脚output [5:0]dac_Clk, //!DAC的时钟output [5:0]dac_Din, //!DAC数据引脚output [5:0]dac_Syn //!DAC同步引脚
);localparam DAC_CHx = 6; //6个通道//系统时钟域的寄存器和网线
wire [31:0]Dac_chx_wire[0:DAC_CHx-1]; //!输入的寄存器线网数组
reg DDS_Run_sysClk[0:DAC_CHx-1]; //!DDS运行-系统时钟域
reg [15:0]DDS_Fre_sysClk[0:DAC_CHx-1]; //!DDS的频率-系统时钟域
reg [15:0]DDS_Phase_sysClk[0:DAC_CHx-1]; //!DDS相位-系统时钟域
reg [15:0]DAC_OutData_sysClk[0:DAC_CHx-1]; //!普通模式下DAC的输出
reg DAC_enable_sysClk[0:DAC_CHx-1]; //!DAC使能信号-系统时钟域
//DAC时钟域的寄存器和网线
wire DDS_Run_dacClk[0:DAC_CHx-1]; //!DDS运行-DAC时钟域
wire [15:0]DDS_Fre_dacClk[0:DAC_CHx-1]; //!DDS的频率-DAC时钟域
wire [15:0]DDS_Phase_dacClk[0:DAC_CHx-1]; //!DDS相位-DAC时钟域
wire [15:0]DDS_Phase_out_dacClk[0:DAC_CHx-1]; //!驱动DDS的相位
wire [15:0]DDS2Dac_dacClk[0:DAC_CHx-1]; //!DDS->DAC
wire [15:0]DAC_OutData_dacClk[0:DAC_CHx-1]; //!普通模式下DAC的输出-DAC时钟域
reg [15:0]DAC_Data[0:DAC_CHx-1]; //!DAC模块的输入数据
wire DAC_enable_dacClk[0:DAC_CHx-1]; //!DAC使能信号-DAC时钟域
genvar i;//!输入寄存器线网整理 这里的数量与 DAC_CHx 有关
assign Dac_chx_wire[0][31:0] = Dac_Ch3_reg;
assign Dac_chx_wire[1][31:0] = Dac_Ch4_reg;
assign Dac_chx_wire[2][31:0] = Dac_Ch5_reg;
assign Dac_chx_wire[3][31:0] = Dac_Ch6_reg;
assign Dac_chx_wire[4][31:0] = Dac_Ch7_reg;
assign Dac_chx_wire[5][31:0] = Dac_Ch8_reg;//! DDS运行控制和DAC使能控制
generatefor(i=0;i<DAC_CHx;i=i+1)begin:AXI_REG_RUNalways @(posedge sysClk)beginif(sysRst)beginDDS_Run_sysClk[i] <= 1'd0;DAC_enable_sysClk[i] <= 1'd0;endelse beginDDS_Run_sysClk[i] <= Dac_Ctrl_reg[(i+2)*4+:4]==4'd2 ? 1'd1 : 1'd0; //DDS使能信号DAC_enable_sysClk[i] <= Dac_Ctrl_reg[(i+2)*4+:4]==4'd0 ? 1'd0 : 1'd1; //DAC使能信号endendend
endgenerate//! DDS频率、相位和普通DAC数据
generatefor(i=0;i<DAC_CHx;i=i+1)begin:AXI_REG_DATAalways @(posedge sysClk)beginif(sysRst)beginDDS_Fre_sysClk[i][15:0] <= 16'd0;DDS_Phase_sysClk[i][15:0] <= 16'd0;DAC_OutData_sysClk[i][15:0] <= 16'd0;endelse beginDDS_Fre_sysClk[i][15:0] <= Dac_chx_wire[i][31:16]; //频率数据DDS_Phase_sysClk[i][15:0] <= Dac_chx_wire[i][15:0]; //相位数据DAC_OutData_sysClk[i][15:0] <= Dac_chx_wire[i][15:0]; //普通DAC数据endendend
endgenerate//!时钟域数据转换--DDS运行信号
generatefor(i=0;i<DAC_CHx;i=i+1)begin:CLOCK_DDS_RUNshift # (.WIDTH(1),.SHIFT_MUN(3))shift_inst (.sysClk(dacClk),.sysRst(sysRst),.inData(DDS_Run_sysClk[i]),.outData(DDS_Run_dacClk[i]));end
endgenerate//!时钟域数据转换--DDS频率
generatefor(i=0;i<DAC_CHx;i=i+1)begin:CLOCK_DDS_FREshift # (.WIDTH(16),.SHIFT_MUN(3))shift_inst (.sysClk(dacClk),.sysRst(sysRst),.inData(DDS_Fre_sysClk[i][15:0]),.outData(DDS_Fre_dacClk[i][15:0]));end
endgenerate//!时钟域数据转换--DDS相位
generatefor(i=0;i<DAC_CHx;i=i+1)begin:CLOCK_DDS_PHASEshift # (.WIDTH(16),.SHIFT_MUN(3))shift_inst (.sysClk(dacClk),.sysRst(sysRst),.inData(DDS_Phase_sysClk[i][15:0]),.outData(DDS_Phase_dacClk[i][15:0]));end
endgenerate//!时钟域数据转换--普通模式下的DAC输出
generatefor(i=0;i<DAC_CHx;i=i+1)begin:CLOCK_OUTDATAshift # (.WIDTH(16),.SHIFT_MUN(3))shift_inst (.sysClk(dacClk),.sysRst(sysRst),.inData(DAC_OutData_sysClk[i][15:0]),.outData(DAC_OutData_dacClk[i][15:0]));end
endgenerate//!时钟域数据转换--DAC使能信号
generatefor(i=0;i<DAC_CHx;i=i+1)begin:CLOCK_DAC_ENABLEshift # (.WIDTH(1),.SHIFT_MUN(3))shift_inst (.sysClk(dacClk),.sysRst(sysRst),.inData(DAC_enable_sysClk[i]),.outData(DAC_enable_dacClk[i]));end
endgenerate//!DDS相位生成
generatefor(i=0;i<DAC_CHx;i=i+1)begin:DdsPhaseDDS_Phase # (.CH_NUM(DAC_CHx))DDS_Phase_u0 (.sysClk(dacClk),.sysRst(sysRst),.Run(DDS_Run_dacClk[i]),.Fre(DDS_Fre_dacClk[i][15:0]),.Phase(DDS_Phase_dacClk[i][15:0]),.DDS_Phase_Data(DDS_Phase_out_dacClk[i][15:0]));end
endgenerate//!DDS输出,该模块输出到DAC
generatefor(i=0;i<DAC_CHx;i=i+1)begin:DDSdds_compiler_0 dds_compiler_u0 (.aclk(dacClk), // input wire aclk.s_axis_phase_tvalid(1'd1), // input wire s_axis_phase_tvalid.s_axis_phase_tdata(DDS_Phase_out_dacClk[i][15:0]), // input wire [15 : 0] s_axis_phase_tdata.m_axis_data_tvalid(), // output wire m_axis_data_tvalid.m_axis_data_tdata(DDS2Dac_dacClk[i][15:0]) // output wire [15 : 0] m_axis_data_tdata);end
endgenerate//!DAC数据控制
generatefor(i=0;i<DAC_CHx;i=i+1)begin:DAC_DATAalways @(posedge dacClk)beginif(sysRst)beginDAC_Data[i][15:0] <= 16'd0;end//关闭了DACelse if(DAC_enable_dacClk[i]==0)beginDAC_Data[i][15:0] <= 16'd0;end//DDS模式else if(DDS_Run_dacClk[i])beginDAC_Data[i][15:0] <= 16'h8000 - DDS2Dac_dacClk[i][15:0];end//普通模式else beginDAC_Data[i][15:0] <= DAC_OutData_dacClk[i][15:0];endendend
endgenerate//!DAC7512驱动
generatefor(i=0;i<DAC_CHx;i=i+1)begin:DACDAC7512 DAC7512_inst (.sysClk(dacClk),.sysRst(sysRst),.Data(DAC_Data[i][15:4]),.Start(DAC_enable_dacClk[i]),.Done(),.dac_Clk(dac_Clk[i]),.dac_Din(dac_Din[i]),.dac_Syn(dac_Syn[i]));end
endgenerate
endmodule
在代码中,DAC使用了30M的时钟,AXI总线是100M时钟,所以控制数据在这里需要做跨时钟域处理,由于是快速时钟到慢速时钟,我们这里对数据进行打拍处理,并且做多周期约束即可,约束如下所示
#创建30M的DAC时钟时钟,主时钟对该时钟做多周期约束。 3个周期
create_clock -period 33.333 -name dacClk -waveform {0.000 16.667} [get_pins design_1_i/clk_wiz_0/clk_30M]
set_multicycle_path -setup -start -from [get_clocks *fpga*] -to [get_clocks *dacClk*] 3
set_multicycle_path -hold -start -from [get_clocks *fpga*] -to [get_clocks *dacClk*] 3
四、结论
在使用PYNQ框架开发ZYNQ时,PL端都可以视作PS端的外设。本文章只介绍了一种使用慢速数据的外设,如果你写的外设是一个需要高速数据传输的,例如视频解码等。就可以考虑使用AXI_HP接口配合PL端的DMA来实现。
下一个一篇文章是讲述小助手项目的OLED显示是如何在PYNQ架构上实现的。
相关文章:
板级调试小助手(2)ZYNQ自定义IP核构建属于自己的DDS外设
一、前言 在上期文章中讲述了小助手的系统结构和原理。在PYNQ的框架开发中,我们一般可以将PL端当做PS端的一个外设,通过读写寄存器的方式来操作外设的功能,就类似于在开发ARM和DSP中操作外设一样,不同时的是,我们可以通…...
vim+cscope+ctags
一、简单安装 1.安装cscope # apt install cscope 2.安装ctags # apt install ctags 3.taglist安装 下载Vim source code browser plugin - Browse /vim-taglist at SourceForge.net,解压和复制文件 # unzip taglist_46.zip# cp doc/taglist.txt /usr/share/…...
Java 8的变革:函数式编程和Lambda表达式探索
文章目录 一、函数接口二、Lambda表达式简介三、Lambda表达式外部参数四、Lambda范例五、Runnable Lambda表达式 一、函数接口 函数接口是一个具有单个抽象方法的接口,接口设计主要是为了支持 Lambda 表达式和方法引用,使得 Java 能更方便地实现函数式编…...
Java集合框架的内部揭秘:List、Set与Map的深潜之旅
Java集合框架是一套强大的工具,为开发者提供了灵活的数据管理方式。本文将深入剖析List、Set和Map的内部机制,通过详细的示例和扩展讨论,带你领略这些数据容器的真谛。 一、List:有序序列的深度剖析 List接口是一个可以包含重复…...
爬虫(二)——爬虫的伪装
前言 本文是爬虫系列的第二篇文章,主要讲解关于爬虫的简单伪装,以及如何爬取B站的视频。建议先看完上一篇文章,再来看这一篇文章。要注意的是,本文介绍的方法只能爬取免费视频,会员视频是无法爬取的哦。 爬虫的伪装 …...
空安全编程的典范:Java 8中的安全应用指南
文章目录 一、Base64 编码解码1.1 基本的编码和解码1.2 URL 和文件名安全的编码解码器1.3 MIME Base64编码和解码 二、Optional类三、Nashorn JavaScript 一、Base64 编码解码 1.1 基本的编码和解码 Base64 编码: 使用 Base64.getEncoder().encodeToString(origin…...
Docker Machine 深入解析
Docker Machine 深入解析 引言 Docker Machine 是 Docker 生态系统中的一个重要工具,它简化了 Docker 容器环境的配置和管理过程。本文将深入探讨 Docker Machine 的概念、功能、使用场景以及如何在实际环境中高效利用它。 什么是 Docker Machine? Docker Machine 是一个…...
20.x86游戏实战-远线程注入的实现
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…...
06MFC之对话框--重绘元文件
文章目录 实现示例展示需要绘制的窗口/位置控件位置更新下一次示例粗细滑动部分更新重绘元文件(窗口变化内容消失)方法一:使用元文件方法二:兼容设备方法三:使用自定义类存储绘图数据除画笔外功能处理画笔功能处理保存前面画的线及色彩实现示例展示 需要绘制的窗口/位置 …...
鼠标的发明和鼠标“变形记”
注:机翻,未校对。 Who Invented the Computer Mouse? 谁发明了电脑鼠标? It was technology visionary and inventor Douglas Engelbart (January 30, 1925 – July 2, 2013) who revolutionized the way computers worked, turning it fr…...
快捷:通过胶水语言实现工作中测试流程并行、加速
通过胶水语言实现工作中测试流程并行、加速 通过胶水语言实现工作中测试流程并行、加速工作场景(背景)问题抽象(挑战)如何做(行动)获得了什么(结果)后记相关资源 通过胶水语言实现工…...
MySQL 和 PostgreSQL,我到底选择哪个?
MySQL 和 PostgreSQL 是两个广泛使用的关系型数据库管理系统(RDBMS)。它们都具有强大的功能和广泛的社区支持,但在某些方面存在一些差异。本文将详细比较 MySQL 和 PostgreSQL,包括它们的特点、性能、扩展性、安全性以及适用场景等…...
Java —— 内部类
Java内部类 1.什么是内部类? 将一个类A定义在另一个类B里面,里面的类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)。 2.为什么需要内部类? 具体来说,当一…...
高职院校人工智能人才培养成果导向系统构建、实施要点与评量方法
一、引言 近年来,人工智能技术在全球范围内迅速发展,对各行各业产生了深远的影响。高职院校作为培养高技能人才的重要基地,肩负着培养人工智能领域专业人才的重任。为了适应社会对人工智能人才的需求,高职院校需要构建一套科学、…...
ffmpeg中的超时控制
在FFmpeg库中,很多函数没有直接的参数可以设置超时。 那么有哪些函数可以通过设置 AVFormatContext 的 interrupt_callback 来实现超时控制? avformat_open_input: 打开输入文件或流。这个函数会阻塞,尤其是在网络流的情况下&…...
搜维尔科技:【研究】触觉技术将在5年内以8种方式改变人们的世界
触觉技术在过去几年中发展迅猛,大大提高了反馈的精确度和真实度。其应用产生了真正的影响,数百家公司和企业都集成了触觉技术来增强培训和研究模拟。 虽然触觉技术主要用于 B2B 层面,但触觉技术可能会彻底改变我们的生活,尤其是通…...
项目收获总结--MyBatis的知识收获
MyBatis的知识收获 一、概述二、获取自动生成的(主)键值三、将sql执行结果封装为目标返回对象的方式和原理四、延迟加载实现原理五、批量插入六、自带分页与分页插件原理七、Mapper(Dao)接口与XML映射文件关系八、模糊查询like语句九、#{}和${}的区别十、二级缓存案例实战 一、…...
数据库管理-第221期 Oracle的高可用-04(20240717)
数据库管理221期 2024-07-17 数据库管理-第221期 Oracle的高可用-04(20240717)1 ADG2 连接配置2.1 TNS2.2 JDBC2.3 JAVA连接池2.3.1 Oracle UCP2.3.2 应用连接池基础配置 总结 数据库管理-第221期 Oracle的高可用-04(20240717) 作…...
navicat15已连接忘记密码
1.导出链接 2.使用文本打开 connections.ncx UserName"root" PasswordXXXX 3.复制加密密码,在线解密 代码在线运行 - 在线工具 php解密代码 <?php class NavicatPassword {protected $version 0;protected $aesKey libcckeylibcckey;protected…...
企业管理必备:学会寻找客户绝佳方法。
无论是日常沟通、工作交流,还是社交娱乐,微信都扮演着重要的角色。而在微信的使用过程中,添加好友是一项基本而重要的操作,但是您真的会添加微信好友吗? 试试这个神器——微信管理系统,下面分享它快速加客…...
昇思25天学习打卡营第29天 | 文本解码原理--以MindNLP为例
今天是29天,学习了文本解码原理--以MindNLP为例。 MindNLP 是一个基于 MindSpore 的开源自然语言处理(NLP)库。它具有以下特点: 支持多种 NLP 任务:如语言模型、机器翻译、问答、情感分析、序列标记、摘要等ÿ…...
元服务体验-服务发现
服务发现,无论线上或线下的方式都可以发现元服务。 线上:基于用户意图。从精准意图的搜索、用户事件触发的推荐到主动探索等场景。用户可以在设备的负一屏、全局搜索、应用市场、桌面等场景发现元服务。 线下:用户在 HarmonyOS Connect标签…...
设计模式学习(二)工厂模式——抽象工厂模式+注册表
设计模式学习(二)工厂模式——抽象工厂模式注册表 前言使用简单工厂改进使用注册表改进参考文章 前言 在上一篇文章中我们提到了抽象工厂模式初版代码的一些缺点:①客户端违反开闭原则②提供方违反开闭原则。本文将针对这两点进行讨论 使用…...
同三维T80004解码器视频使用操作说明书:高清HDMI解码器,高清SDI解码器,4K超清HDMI解码器,双路4K超高清解码器
同三维T80004解码器视频使用操作说明书:高清HDMI解码器,高清SDI解码器,4K超清HDMI解码器,双路4K超高清解码器 同三维T80004解码器系列视频使用操作说明书:高清HDMI解码器,高清SDI解码器,4K超清H…...
Flutter应用开发:掌握StatefulWidget的实用技巧
前言 随着移动应用的日益复杂,状态管理成为了 Flutter 应用开发中的一项重要挑战。 状态,即应用中的可变数据,它驱动着用户界面的渲染和交互。 在 Flutter 这样的声明式 UI 框架中,如何高效、可维护地管理状态,对于…...
SCADA系统在哪些行业中取得了不斐的成绩!
随着技术的发展,SCADA系统已经历了多代的发展。从基于专用计算机和专用操作系统的第一代SCADA系统,到基于通用计算机和通用操作系统的第二代,再到按照开放原则基于分布式计算机网络以及关系数据库技术的第三代,以及现在基于更高技…...
layui 监听弹窗关闭并刷新父级table
记录:easyadmin 监听弹窗关闭并刷新父级table 场景一:在二级页面的table中点击编辑,保存后刷新二级页面的table edit: function () {ea.listen(function (data) {return data;}, function (res) {ea.msg.success(res.msg, function () {var …...
Webpack详解
Webpack Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。它允许开发者将项目中的资源(如 JavaScript、CSS、图片等)视为模块,通过分析和处理这些模块之间的依赖关系,将它们打包…...
杰发科技AC7801 —— __attribute__指定地址存储常量
const uint8_t usFlashInitVal[] __attribute__((at(0x08002800))) {0x55,0x55,0x55,0x55,0x55};//定位在flash中,0x00030000开始的6个字节信息固定 注意7801的地址在8000000之后 如地址选0x00000800烧录时候报错 不知道是不是atclinktool的bug,使用_…...
docker pull 不下来 还是走代理好啊
docker国内镜像源配置及走代理设置_docker 镜像代理-CSDN博客 docker命令走代理 实际测试下来,就算我们修改成功了国内的镜像源,有时候由于国内镜像更新不及时,或者需要拉取的镜像比较冷门,只有域外镜像站才有,那么我…...
域名网站如何做/视频号广告推广
顶帽(Top Hat): 原图像与开操作之间的差值图像,突出原图像中比周围亮的区域 黑帽(Black Hat): 闭操作图像与原图像的差值图像, 突出原图像中比周围暗的区域 形态学梯度(Gradient&…...
湖州 网站建设公司/免费发布产品信息的网站
header("Content-type:text/html; charsetUTF-8");/* ** 类名:YunxinSmsApi* 功能:云信接口请求类* 详细:构造云信短信接口请求,获取远程HTTP数据* 版本:1.3* 日期:2018-05-12* 说明:…...
哪里专业做网站/如何建立网站 个人
pubsub,即订阅发布,在vue和react中常常被用于任意组件间的传值。 例如:订阅报纸: 1,交钱,说好地址,订阅哪一种报纸 2,邮递员送报纸 本文主要简述pubsub的基本使用流程。 1.在终端…...
杭州利兴建设官方网站/网络营销是做什么的工作
目标 定义什么是this关键字理解四种常用的方法去找出关键字this代表什么尽可能的在语句中不要使用thisthis 是什么 JavaScript中的保留关键字通常由函数的调用方式决定(我们称之为执行上下文)可以用四种规则来确定(全局(默认绑定), 对象/隐式, 显式, new…...
丹阳疫情最新消息今天新增/昆明网络推广优化
http://liuzhijun.iteye.com/blog/1872241 virtualenv virtualenv用于创建独立的Python环境,多个Python相互独立,互不影响,它能够: 1. 在没有权限的情况下安装新套件 2. 不同应用可以使用不同的套件版本 3. 套件升级不影响其他应用…...
网站反链怎么做/网站推广方法大全
目录简介ios类库streambuf类库标准I/O流——iostream预定义流对象cincout标准输出流put()write()标准输入流get()getline()read()ignore()gcount()peek()文件流文件流对象的创建文件打开和关闭文本文件的读写二进制文件的读写文件随机读写字符串流简介 I/O流类库是C标准库的重要…...