板级调试小助手(2)ZYNQ自定义IP核构建属于自己的DDS外设

一、前言

        在上期文章中讲述了小助手的系统结构和原理。在PYNQ的框架开发中,我们一般可以将PL端当做PS端的一个外设,通过读写寄存器的方式来操作外设的功能,就类似于在开发ARM和DSP中操作外设一样,不同时的是,我们可以通过FPGA定制我们自己需要的外设结构,例如本次文章需要提到的DDS外设。

二、外设功能介绍

        在开发外设之前首先要明确这个外设的功能:

        1、由于外部电流采用的是八路DA7512单通道DAC所以要实现DDS功能我们就需要再FPGA中搭建一个正弦波发生器,当然正弦波发生器有多种多样的实现方式,可以采用ROM查表的方式,也可以使用DSP实时计算。我这里就直接使用了Vivado提供的DDS IP核了(其本质也是通过ROM查表的方式),IP设置如下图1所示

图1 DDS设置

        2、DDS生成的正弦波需要可以控制其频率和初始相位功能;

        3、虽然有DDS功能但是也同样要兼容普通DA功能;

        4、一共八个通道,所以需要对八个通道的状态进行设置;

        综上,我们可以设计出DAC控制寄存器和DAC波形控制器如下图2和图3所示

图2 DAC控制寄存器
图3 DAC波形控制寄存器

三、外设搭建

        在ZYNQ中,搭建数据传输比较慢的外设时可以使用AXI_GP接口总线;AXI_GP接口总线可以视作为一种寄存器映射总线。最后将总线和逻辑功能封装成一个IP核,如下图4所示(这里就不放具体代码了,详细代码请移步到开源地址)

图4 DDS外设模块

        该模块具有一个AXI_GP接口的从机和七个DAC波形控制器数据,和一个DAC控制器数据,后续的DDS模块就利用这些寄存器数据来对DAC外设控制,DDS顶层代码如下:

//!DAC_DDS顶层,这里用来通过PS端写来的寄存器控制DAC的输出
module dac_dds_Top(
    input  sysClk,                  //系统时钟
    input  dacClk,                  //!DAC的时钟,最大为30M
    input  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使能控制
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:AXI_REG_RUN
        always @(posedge sysClk)begin
            if(sysRst)begin
                DDS_Run_sysClk[i] <= 1'd0;
                DAC_enable_sysClk[i] <= 1'd0;
            end
            else begin
                DDS_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使能信号
            end
        end
    end
endgenerate


//! DDS频率、相位和普通DAC数据
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:AXI_REG_DATA
        always @(posedge sysClk)begin
            if(sysRst)begin
                DDS_Fre_sysClk[i][15:0] <= 16'd0;
                DDS_Phase_sysClk[i][15:0] <= 16'd0;
                DAC_OutData_sysClk[i][15:0] <= 16'd0;
            end
            else begin
                DDS_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数据
            end
        end
    end
endgenerate


//!时钟域数据转换--DDS运行信号
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:CLOCK_DDS_RUN
        shift # (
            .WIDTH(1),
            .SHIFT_MUN(3)
        )
        shift_inst (
            .sysClk(dacClk),
            .sysRst(sysRst),
            .inData(DDS_Run_sysClk[i]),
            .outData(DDS_Run_dacClk[i])
        );
    end
endgenerate



//!时钟域数据转换--DDS频率
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:CLOCK_DDS_FRE
        shift # (
            .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相位
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:CLOCK_DDS_PHASE
        shift # (
            .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输出
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:CLOCK_OUTDATA
        shift # (
            .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使能信号
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:CLOCK_DAC_ENABLE
        shift # (
            .WIDTH(1),
            .SHIFT_MUN(3)
        )
        shift_inst (
            .sysClk(dacClk),
            .sysRst(sysRst),
            .inData(DAC_enable_sysClk[i]),
            .outData(DAC_enable_dacClk[i])
        );
    end
endgenerate



//!DDS相位生成
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:DdsPhase
        DDS_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
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:DDS
      dds_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数据控制
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:DAC_DATA
        always @(posedge dacClk)begin
            if(sysRst)begin
                DAC_Data[i][15:0] <= 16'd0;
            end
            //关闭了DAC
            else if(DAC_enable_dacClk[i]==0)begin
                DAC_Data[i][15:0] <= 16'd0;
            end
            //DDS模式
            else if(DDS_Run_dacClk[i])begin
                DAC_Data[i][15:0] <= 16'h8000 - DDS2Dac_dacClk[i][15:0];
            end
            //普通模式
            else begin
                DAC_Data[i][15:0] <= DAC_OutData_dacClk[i][15:0];
            end
        end
    end
endgenerate



//!DAC7512驱动
generate
    for(i=0;i<DAC_CHx;i=i+1)
    begin:DAC
      DAC7512  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架构上实现的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/799000.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AIGC率超标?掌握论文去AI痕迹的高效策略

随着 AI 技术迅猛发展&#xff0c;各种AI辅助论文写作的工具层出不穷&#xff01; 为了防止有人利用AI工具进行论文代写&#xff0c;在最新的学位法中已经明确规定“已经获得学位者&#xff0c;在获得该学位过程中如有人工智能代写等学术不端行为&#xff0c;经学位评定委员会…

智慧公厕系统实现人性化与节能化的完美结合

在当今社会&#xff0c;科技的飞速发展正不断改变着我们的生活方式&#xff0c;公厕也不例外。智慧公厕系统的出现&#xff0c;不仅提升了人们的使用体验&#xff0c;更实现了人性化与节能化的完美结合&#xff0c;为城市公共服务带来了全新的变革。 一、人性化&#xff0c;是智…

清朝嘉庆二十五年(1820年)地图数据

我们在《中国历史行政区划连续变化数据》一文中&#xff0c;为你分享了中国历史行政区划连续变化地图数据。 现在再为你分享清朝嘉庆二十五年&#xff08;1820年&#xff09;的地图数据&#xff0c;该数据对于研究历史的朋友应该比较有用&#xff0c;请在文末查看领取方式。 …

SpringBoot实战:多表联查

1. 保存和更新公寓信息 请求数据的结构 Schema(description "公寓信息") Data public class ApartmentSubmitVo extends ApartmentInfo {Schema(description"公寓配套id")private List<Long> facilityInfoIds;Schema(description"公寓标签i…

深度学习概览

引言 深度学习的定义与背景 深度学习是机器学习的一个子领域&#xff0c;涉及使用多层神经网络分析和学习复杂的数据模式。深度学习的基础可以追溯到20世纪80年代&#xff0c;但真正的发展和广泛应用是在21世纪初。计算能力的提升和大数据的可用性使得深度学习在许多领域取得…

platformIO STM32 upload-“Failed to init device.”问题解决

因为发现自己的32板子有带自动下载功能&#xff0c;platformIO也支持串口下载&#xff0c;但一直提示这个问题 问题情况 问题解决 把BOOT0接3.3V&#xff0c;BOOT1接GND&#xff0c;再点击下载(之后接回去复位也可以显示) 这是我从一个有相同问题的人从他尝试过的解决方案中…

SadTalker数字人服务器部署

一、单独SadTalker部署 git clone https://github.com/OpenTalker/SadTalker.gitcd SadTalker conda create -n sadtalker python3.8conda activate sadtalkerpip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pyto…

S7-200smart与C#通信

https://www.cnblogs.com/heizao/p/15797382.html C#与PLC通信开发之西门子s7-200 smart_c# s7-200smart通讯库-CSDN博客https://blog.csdn.net/weixin_44455060/article/details/109713121 C#上位机读写西门子S7-200SMART PLC变量 教程_哔哩哔哩_bilibilihttps://www.bilibili…

Windows中安装python/cmd中执行python命令无效

1、问题阐述? 本文章提供非常详细的安装教程。 本文章适合于不会安装python或者安装了python后,在cmd中执行python命令无效的情况。 2、下载python python下载官网地址:Download Python | Python.org 在下面的框子中选择你需要的版本 如果是windows选择如下包 如下版本…

机器人前沿--PalmE:An Embodied Multimodal Language Model 具身多模态大(语言)模型

首先解释这篇工作名称Palm-E&#xff0c;发表时间为2023.03&#xff0c;其中的Palm是谷歌内部在2022.04开发的大语言模型&#xff0c;功能类似ChatGPT&#xff0c;只是由于各种原因没有那样火起来&#xff0c;E是Embodied的首字母&#xff0c;翻译过来就是具身多模态大语言模型…

计算机网络生成树协议介绍与实践

生成树协议 1.环路 二层环路&#xff1a;数据链路层&#xff0c;交换机&#xff08;二层设备&#xff09;通过线路连接环状。即物理成环并且没有开启防环协议。 危害&#xff1a;广播风暴&#xff1a;交换机将未知帧广播&#xff0c;收到后的交换机继续广播&#xff0c;不断…

国产替代正当时,智汇云舟视频孪生加速信创国产化替代

数据安全是国家安全的核心&#xff0c;我国正积极推进计算机软硬件的国产化&#xff0c;以减少对外部技术的依赖&#xff0c;提升国内信息技术产业的竞争力。国产化软硬件的适配不仅能够维护企业机密和客户信任&#xff0c;还能有效防止数据泄露和网络攻击&#xff0c;同时帮助…

vscode编译环境配置-golang

1. 支持跳转 如果单测函数上方不显示run test | debug test&#xff0c;需要安装Code Debugger&#xff08;因为以前的go Test Explorer不再被维护了&#xff09; 2. 单测 指定单个用例测试 go test -v run TestXXXdlv 调试 需要安装匹配的go版本和delve版本&#xff08;如…

OrangePi AIpro在安防领域的深思和实战(旷视科技CNN模型ShuffleNetV1开发案例测试)

一、前言 公司最近有个项目是安防领域的&#xff0c;主要用在边缘结点&#xff0c;虽然已做成形&#xff0c;但是还是存在一些缺陷&#xff0c;例如&#xff1a;算力问题&#xff0c;开发板的成熟问题&#xff0c;已经各种技术的解决方案落地问题。目前我们集成了很多功能&…

MyBatis where标签内嵌foreach标签查询报错‘缺失右括号‘或‘命令未正确结束‘

MyBatis <where>标签内嵌<foreach>标签查询报错’缺失右括号’或’命令未正确结束’ <where>标签内嵌<foreach>标签 截取一段脱敏xml&#xff0c;写明大概意思 <select id"queryLogByIds" resultMap"BaseResultMap">SELE…

巧用 VScode 网页版 IDE 搭建个人笔记知识库!

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 巧用 VScode 网页版 IDE 搭建个人笔记知识库! 描述&#xff1a;最近自己在腾讯云轻量云服务器中部署了一个使用在线 VScode 搭建部署的个人Markdown在线笔记&#xff0c;考虑到在线 VScode 支持终…

昇思25天学习打卡营第7天 | 基于MindSpore的GPT2文本摘要

本次打卡基于gpt2的文本摘要 数据加载及预处理 from mindnlp.utils import http_get# download dataset url https://download.mindspore.cn/toolkits/mindnlp/dataset/text_generation/nlpcc2017/train_with_summ.txt path http_get(url, ./)from mindspore.dataset impor…

代码检查规则语言CodeNavi中代码语句的节点和节点属性

本文分享至华为云社区《CodeNavi 中代码语句的节点和节点属性》。作者&#xff1a;Uncle_Tom 1. 前期回顾 《寻找适合编写静态分析规则的语言》 根据代码检查中的一些痛点&#xff0c;提出了希望寻找一种适合编写静态分析规则的语言。 可以满足用户对代码检查不断增加的各种需求…

c语言中的字符串函数

strstr函数 函数介绍 strstr 用于在一个字符串中查找另一个字符串的首次出现。 我们来看这个函数的参数名字&#xff1a;haysytack&#xff08;干草堆&#xff09;needle&#xff08;针&#xff09;,这个其实就是外国的一句谚语&#xff1a;在干草堆中找一根针&#xff0c;就…

Type-C PD芯片:引领充电技术的新纪元

随着科技的飞速发展&#xff0c;人们对电子设备的依赖日益加深&#xff0c;对充电速度、效率和安全性的要求也越来越高。在这样的背景下&#xff0c;Type-C PD&#xff08;Power Delivery&#xff09;芯片应运而生&#xff0c;以其高效、安全、智能的特点&#xff0c;成为了充电…