• 从零开始利用MATLAB进行FPGA设计(六)用ADC采集信号教程1


    黑金的教程做的实在太拉闸了,于是自己摸索信号采集模块的使用方法。

    ADC模块:AN9238

    FPGA开发板:AX7020;Xilinx 公司的 Zynq7000 系列的芯片XC7Z020-2CLG400I,400引脚 FBGA 封装。

    往期回顾:

    从零开始利用MATLAB进行FPGA设计(五)详解双口RAM从零开始利用MATLAB进行FPGA设计(四)生成优化HDL代码

    目录

    1.顶层模块设计

    顶层模块接口

    时钟生成

    AD采样模块

    FIFO缓冲区

    ILA实例化

    2.PLL

    IP核设置

    IP实例化

    3.FIFO

    4.ILA

    5.testbench仿真


    1.顶层模块设计

    信号采集程序的顶层模块主要包括时钟生成、AD采样、FIFO缓冲区管理和逻辑分析仪ILA四个部分。通过PLL生成两个时钟信号,通过AD模块将模拟信号转为数字信号,通过FIFO缓冲区对采样数据进行缓存,最后通过FIFO缓冲区对采样数据进行缓存。

    顶层模块接口

    1. module top(
    2. input clk50m, // 系统时钟
    3. input reset_n,
    4. output wire [0:0] clk65m_1, // FPGA输出的时钟信号1,作为AD模块的输入时钟1
    5. output wire [0:0] clk70m_1, // FPGA输出的时钟信号2,作为AD模块的输入时钟2
    6. input wire [11:0] ad1_in, // 离散数据输入到FPGA
    7. input wire [11:0] ad2_in
    8. );

    这个模块有以下输入和输出端口:

    • clk50m: 50MHz系统时钟输入。

    • reset_n: 复位信号,低电平有效。

    • clk65m_1clk70m_1: 由FPGA输出的两个时钟信号,分别用于AD模块的输入时钟。

    • ad1_inad2_in: 12位宽的模拟数据输入信号。

    时钟生成

    1. wire clk65m;
    2. wire clk70m;
    3. assign clk65m_1 = clk65m;
    4. assign clk70m_1 = clk70m;
    5. pll pll_inst (
    6. // Clock out ports
    7. .clk_out1(clk65m), // output clk_out1
    8. .clk_out2(clk70m), // output clk_out2
    9. // Status and control signals
    10. .reset(~reset_n), // input reset
    11. .locked(), // output locked
    12. // Clock in ports
    13. .clk_in1(clk50m) // input clk_in1
    14. );

    通过PLL模块生成两个时钟信号 clk65mclk70m,并将它们分别分配给 clk65m_1clk70m_1

    AD采样模块

    1. wire [11:0] ad_ch1;
    2. wire [11:0] ad_ch2;
    3. v_ad v_ad_inst (
    4. .ad1_clk(clk65m_1), // 65M
    5. .ad2_clk(clk65m_1), // 65M
    6. .ad1_in(ad1_in), // 模拟数据输入到AD模块
    7. .ad2_in(ad2_in),
    8. .ad_ch1(ad_ch1), // AD模块输出数字数据
    9. .ad_ch2(ad_ch2)
    10. );

    这个模块实例化了一个名为 v_ad 的AD采样模块,将两个模拟输入信号 ad1_inad2_in 转换为数字信号 ad_ch1ad_ch2

    FIFO缓冲区

    1. wire wr_en = 1; // 写使能
    2. wire rd_en = 1; // 读使能
    3. wire [11:0] dout; // 读数据
    4. wire full; // 写满信号
    5. wire empty; // 读空信号
    6. wire [9:0] rd_data_count; // 可读数据数量
    7. wire [9:0] wr_data_count; // 已写入的数据数量
    8. fifo_generator_0 fifo_wr_inst (
    9. .rst(~reset_n), // input wire rst
    10. .wr_clk(clk65m), // input wire wr_clk
    11. .rd_clk(clk70m), // input wire rd_clk
    12. .din(ad1_in), // input wire [11:0] din
    13. .wr_en(wr_en), // input wire wr_en
    14. .rd_en(rd_en), // input wire rd_en
    15. .dout(dout), // output wire [11:0] dout
    16. .full(full), // output wire full
    17. .empty(empty), // output wire empty
    18. .rd_data_count(rd_data_count), // output wire [9:0] rd_data_count
    19. .wr_data_count(wr_data_count) // output wire [9:0] wr_data_count
    20. );

    例化了一个FIFO缓冲区,用于缓存AD采样的数据。FIFO的写时钟是 clk65m,读时钟是 clk70m

    ILA实例化

    1. ila_0 ila_0_inst (
    2. .clk(clk50m), // input wire clk
    3. .probe0(ad1_in), // input wire [11:0] probe0
    4. .probe1(ad2_in), // input wire [11:0] probe1
    5. .probe2(ad_ch1), // input wire [11:0] probe2
    6. .probe3(ad_ch2), // input wire [11:0] probe3
    7. .probe4(ad1_in), // input wire [11:0] probe4
    8. .probe5(dout), // input wire [11:0] probe5
    9. .probe6(rd_data_count), // input wire [9:0] probe6
    10. .probe7(wr_data_count), // input wire [9:0] probe7
    11. .probe8(clk65m), // input wire [0:0] probe8
    12. .probe9(clk70m) // input wire [0:0] probe9
    13. );

    接下来对各个模块进行具体分析:

    2.PLL

    IP核设置

    PLL锁相环,是FPGA中的重要资源。一个复杂的FPGA系统往往需要多个不同频率、相位的时钟信号。7 系列的 FPGA 使用了专用的全局(Global)和区域(Regional)IO 和时钟资源来管理设计中各种时钟需求。Clock Management Tiles(CMT)提供了时钟合成(Clock frequency synthesis),倾斜矫正 (deskew),过滤抖动(jitter filtering)功能。

    每个CMTs包含一个MMCM(混合模式始终管理器)和一个PLL,输出需要接到BUFG(全局时钟缓存)或者BUFH(水平时钟缓存器)后使用。

    PLL主要用于频率综合,使用一个PLL从一个输入时钟信号生成多个时钟信号。

    在Project Manager下选择IP catalog,搜索Clocking后选择Clocking Wizard,进行配置如下:

    PLL的输入信号必须来自于普通单端时钟信号!否则在进行编译过程中会出现报错:

    报错:[DRC REQP-1712] Input clock driver: Unsupported PLLE2_ADV connectivity. The signal pll_inst/inst/clk_in1 on the pll_inst/inst/plle2_adv_inst/CLKIN1 pin of pll_inst/inst/plle2_adv_inst with COMPENSATION mode ZHOLD must be driven by a clock capable IO.

    PLLclk_in1source参数修改为Global buffer即可。

    各引脚定义与顶层模块中保持一致。

    IP实例化

    IP Sources界面找到pll.veo文件,文件中是IP的例化模板。

    3.FIFO

    FIFO:First in, First out代表先进的数据先出,后进的数据后出。FIFO本质上是一种RAM,增加了许多功能,但没有地址线,不能进行随即地址读写(但在FIFO内部还是有地址操作)。

    可以把 FIFO 想象成一个水池,写通道即为加水,读通道即为放水,假如不间断的加水和放水,如果加水速度比放水速度快,那么 FIFO 就会有满的时候,如果满了还继续加水就会溢出 overflow,如果放水速度比加水速度快,那么 FIFO 就会有空的时候。

    根据读写时钟,可以分为同步 FIFO(读写时钟相同)和异步 FIFO(读写时钟不同)。本例采用异步 FIFO的控制,其中读时钟为65MHz,写时钟为70MHz。

    FIFO配置如下所示:

    在时钟与及资源类型中选择independent(读写不同)。

    读模式中:

    • standard mode:仅在执行读操作时从FIFO获取数据
    • first word fall through:在不执行读操作时提前从FIFO获取下一个数据,当数据在FIFO中可用时第一个数据自动出现在总线上

    4.ILA

    ILA是vivado内嵌的在线调试逻辑分析仪。

    probe的数量取决于要采样的信号,Sample Data Depth 指的是采样深度,设置的越高,采集的信号越多,同样消耗的资源也会越多。

    probe的宽度取决于采集信号的宽度。

    5.testbench仿真

    编写仿真程序:

    1. module tb_top;
    2. // Input signals
    3. reg clk50m;
    4. reg reset_n;
    5. reg [11:0] ad1_in;
    6. reg [11:0] ad2_in;
    7. // Signals to connect with DUT (Device Under Test)
    8. wire [0:0] clk65m_1;
    9. wire [0:0] clk70m_1;
    10. wire [11:0] ad_ch1;
    11. wire [11:0] ad_ch2;
    12. wire [11:0] dout;
    13. // Instantiate the top module
    14. top top_inst(
    15. .clk50m(clk50m),
    16. .reset_n(reset_n),
    17. .clk65m_1(clk65m_1),
    18. .clk70m_1(clk70m_1),
    19. .ad1_in(ad1_in),
    20. .ad2_in(ad2_in)
    21. );
    22. // Simulate input signals
    23. initial begin
    24. // Initialize inputs
    25. clk50m = 0;
    26. reset_n = 0;
    27. ad1_in = 12'd0;
    28. ad2_in = 12'd0;
    29. // Apply reset
    30. #1000;
    31. reset_n = 1;
    32. end
    33. // Generate clock signal
    34. always #10 clk50m = ~clk50m;
    35. // Generate 1kHz triangle wave for ad1_in
    36. reg [31:0] counter = 0;
    37. always @(posedge clk50m or negedge reset_n) begin
    38. if (!reset_n) begin
    39. ad1_in <= 12'd0;
    40. counter <= 32'd0;
    41. end else begin
    42. if (counter < 1000) begin
    43. ad1_in <= ad1_in + (12'd1);
    44. end else if (counter < 2000) begin
    45. ad1_in <= ad1_in - (12'd1);
    46. end else begin
    47. counter <= 32'd0;
    48. end
    49. counter <= counter + 1;
    50. end
    51. end
    52. // Simulate ad2_in as a simple ramp signal for variety
    53. always @(posedge clk50m or negedge reset_n) begin
    54. if (!reset_n) begin
    55. ad2_in <= 12'd0;
    56. end else begin
    57. ad2_in <= ad2_in + 12'd2;
    58. end
    59. end
    60. // Extract fifo_din and fifo_dout from internal signals
    61. wire [11:0] fifo_din = ad1_in; // Connecting directly to ad1_in for monitoring
    62. wire [11:0] fifo_dout = top_inst.fifo_wr_inst.dout; // Accessing the internal signal dout
    63. // Monitor fifo signals
    64. initial begin
    65. $monitor("At time %t, fifo_din = %d, fifo_dout = %d", $time, fifo_din, fifo_dout);
    66. end
    67. endmodule

    仿真的核心在于通过计数器counter控制生成1kHz的信号,分成上升沿和下降沿两个部分,每个部分500us。

    通过 $monitor 系统任务,实时显示 fifo_dinfifo_dout 信号的值。

    fifo_din 直接连接到 ad1_infifo_dout 通过实例化顶层模块并访问 fifo_wr_instdout 信号进行监控。

    运行仿真查看结果:

    如果希望看到模拟信号则需要更改信号类型:

  • 相关阅读:
    2023.09.10 学习周报
    HTML5高级部分
    C专家编程 第11章 你懂得C,所以C++不再话下 11.18 如果我的目标是那里,我不会从这里起步
    leetcode-495-提莫攻击
    BD就业复习第四天
    [计算机入门] Windows附件程序介绍(办公类)
    【HTTP协议——八股文(下)篇】
    四六级听力考试高频词汇分类记忆-旅游交通类
    面试经典-Spring篇
    CSS的元素显示模式
  • 原文地址:https://blog.csdn.net/WZT725/article/details/139294398