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

news2024/11/22 13:34:11

一、前言

        在上期文章中讲述了小助手的系统结构和原理。在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架构上实现的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于前端数据库可视化库的选择,vue3+antd+g2plot录课计划

之前&#xff1a;antdv 现在&#xff1a;g2plot https://g2plot.antv.antgroup.com/manual/introduction 录课内容&#xff1a;快速入门 图表示例&#xff1a; 选择使用比较广泛的示例类型&#xff0c;录课顺序如下&#xff1a; 1、折线图2、面积图3、柱形图4、条形图5、饼…

[Qt] Qt Creator中,新建QT文件时选择界面模版下的各选项

在Qt Creator中&#xff0c;新建文件时选择界面模版下的各选项具有特定的意义&#xff0c;这些选项主要帮助开发者根据项目需求快速生成不同类型的文件。以下是对这些选项的详细解释&#xff1a; 0. Qt Item Model 意义&#xff1a;列表模型是Qt中用于表示和操作数据的强大抽…

Ubuntu下载安装chrome浏览器

方法一&#xff1a;wget下载并安装 1、创建文件夹存安装包 cd /root/Downloads mkdir chrome 2、下载安装包到文件夹内 wget -c https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -P /root/Downloads/chrome 3、安装 cd chrome sudo dpkg -i go…

从0开始的STM32HAL库学习5

旋转编码计数器 修改中断代码 void EXTI0_IRQHandler(void) {/* USER CODE BEGIN EXTI0_IRQn 0 */if(__HAL_GPIO_EXTI_GET_FLAG(PB0_Pin)){if(HAL_GPIO_ReadPin(PB1_GPIO_Port, PB1_Pin)GPIO_PIN_RESET){count--;}/* USER CODE END EXTI0_IRQn 0 */HAL_GPIO_EXTI_IRQHandler…

【论文极速读】 可微分检索索引(Differential Search Index, DSI)

【论文极速读】 可微分检索索引&#xff08;Differential Search Index&#xff0c; DSI&#xff09; FesianXu 20240714 at WeChat Search Team 前言 最近从朋友处得知了DSI这个概念&#xff0c;所谓的可微分检索索引DSI&#xff0c;就是通过语言模型将检索过程中的索引和召回…

virtualbox的ubuntu默认ipv4地址为10.0.2.15的修改以及xshell和xftp的连接

virtualbox安装Ubuntu后&#xff0c;默认的地址为10.0.2.15 我们查看virtualbox的设置发现是NAT 学过计算机网络的应该了解NAT技术&#xff0c;为了安全以及缓解ip使用&#xff0c;我们留了部分私有ip地址。 私有IP地址网段如下&#xff1a; A类&#xff1a;1个A类网段&…

持续学习的综述: 理论、方法与应用(三:泛化分析)

前文连接&#xff1a;持续学习的综述: 理论、方法与应用&#xff08;一&#xff09; 前文连接&#xff1a;持续学习的综述: 理论、方法与应用&#xff08;二&#xff1a;理论基础&#xff09; 泛化分析 目前持续学习的理论研究主要是在增量任务的训练集上进行的&#xff0c;假…

Java面试题:MVCC

MVCC 保证事务的隔离性 排它锁: 一个事务获取了数据行的排他锁,其他事务就不能再获取该行的其他锁 MVCC: 多版本并发控制 维护一个数据的多个版本,使读写不存在冲突 具体实现依靠 隐藏字段 mysql中隐藏了三个隐藏字段 db_trx_id:最近修改事务 db_roll_ptr:指向上一个…

【Linux】Linux必备的基础指令

目录 Linux必备的基础指令一 、 什么是Linux二、 Linux常用命令2.1 ls2.2 pwd2.3 cd2.4 touch2.5 cat2.6 mkdir2.7 rm 三、 Linux重要指令3.1 cp3.2 mv3.3 tail3.4 vim3.5 grep3.6 ps3.7 netstat Linux必备的基础指令 一 、 什么是Linux 系统编程&⽹络编程 Linux⾃⾝提供…

适合创业公司使用的wordpress主题

对于创业公司来说&#xff0c;‌选择一个适合的WordPress主题至关重要&#xff0c;‌它不仅能够提升公司网站的外观和用户体验&#xff0c;‌还能帮助优化搜索引擎排名&#xff0c;‌从而吸引更多的潜在客户。‌以下是一些推荐的WordPress主题&#xff0c;‌特别适合创业公司使…

【vue3-命名规范以及注意事项】

使用多字组件名 使用详细的道具定义props 在提交的代码中&#xff0c;prop定义应该总是尽可能详细&#xff0c;至少指定类型。 在声明期间&#xff0c;道具名应该始终使用camelCase。当在in-DOM模板中使用时&#xff0c;props应该是串式的。单文件组件模板和JSX可以使用keba…

【leetcode】整数反转

给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 1&#xff1a; …

C语言丢失精度 如何实现高精度计算

&#xff08;1&#xff09;int 类型举例 int &#xff1a;占4个字节&#xff0c;也就是32位&#xff0c;及最大值是2^32-11024*1024*1024*4-14294967295 以上说法错误&#xff0c;因为Int是有符号类型整数&#xff0c;所以最高位是符号位&#xff0c;及int的最大值应该是2^31…

[BJDCTF2020]EasySearch1

知识点&#xff1a; 1.swp泄露 2.md5碰撞 3.PHP代码审计 4.SSI代码执行漏洞 // Apache SSI 远程命令执行漏洞复现 看着像sql注入&#xff0c;不过注入无果&#xff0c;扫一下目录试试~ 发现是swp泄露. SWP文件泄露漏洞是指在使用 Vim编辑器 编辑一个文件时&#xff0c;Vim会在…

​前端Vue自定义签到获取积分弹框组件设计与实现

摘要 随着前端技术的不断演进&#xff0c;开发的复杂性日益凸显。传统的整体式开发方式在面临功能迭代和修改时&#xff0c;常常牵一发而动全身&#xff0c;导致开发效率低下和维护成本高昂。组件化开发作为一种解决方案&#xff0c;通过实现模块的独立开发和维护&#xff0c;…

代码随想录(day6)哈希表-求两个数组的交集

什么是红黑树&#xff0c;参考链接【数据结构】史上最好理解的红黑树讲解&#xff0c;让你彻底搞懂红黑树-CSDN博客 题目&#xff1a; 注意&#xff1a;字典用{}符合、元组用()符号、列表用[]符号 table.get(num,0)1的意思 class Solution(object):def intersection(self, n…

夹子音转换器matlab

操作过程点击此处观看 上段时间补习了一下傅里叶变化的知识&#xff0c;突发奇想可以根据此做一款声音转换器&#xff0c;使用工科神器Matlab进行完成&#xff0c;并且开发了可操作界面如下图所示&#xff1a; 功能实现与描述 软件中可以实现声音的录制、回放、文件的保存与…

vmware workstation 虚拟机安装

vmware workstation 虚拟机安装 VMware Workstation Pro是VMware&#xff08;威睿公司&#xff09;发布的一代虚拟机软件&#xff0c;中文名称一般称 为"VMware 工作站".它的主要功能是可以给用户在单一的桌面上同时运行不同的操作系统&#xff0c;它也是可进 行开发…

6-7 宠物领养开发及相关代码

6-7 宠物领养开发及相关代码&#xff08;react区块链实战&#xff09; 来到ant-design https://ant.design/index-cn https://ant.design/components/layout-cn/ 来到布局 选择一个简单的布局即可 会显示出所有的相关界面的代码 根据对应界面的代码在我们的react项目woniu-…

接口基础知识3:详解url

课程大纲 一、定义 URL即访问的链接&#xff0c;是Uniform Resource Locator的缩写&#xff0c;译为"统一资源定位符"。 URL是一种URI&#xff0c;它标识一个互联网资源&#xff0c;并指定对其进行操作或获取该资源的方法。可能通过对主要访问手段的描述&#xff0c…