1、硬件搭建

1.1开发板(正点原子7020)

PS端口的千兆网口

1.2 BD

(粉色是控制流,黄色是数据流)

2、AXIlite控制的DDS ip

2.1生成mem文件的m文件(该DDS模块依赖mem文件)

% gen_sin_lut.m
N   = 256;          % LUT depth
AMP = 2^15 - 1;     % Q1.15 peak = 32767

% 生成 int16 正弦
vals = round(sin(2*pi*(0:N-1)/N) * AMP);
vals_i16 = int16(vals);

% 转成 uint16,保持位级两补码(负数会变成 Fxxx)
vals_u16 = typecast(vals_i16, 'uint16');

%% 1) 生成 sin256.mem (给 Verilog $readmemh 使用,每行一个16bit hex)
fid = fopen('sin256.mem','w');
for k = 1:N
    fprintf(fid, '%04X\n', vals_u16(k));
end
fclose(fid);

%% 2) 生成 sin256.coe (给 Vivado ROM/BRAM IP 初始化用)
fid = fopen('sin256.coe','w');
fprintf(fid, 'memory_initialization_radix=16;\n');
fprintf(fid, 'memory_initialization_vector=\n');
for k = 1:N
    if k < N
        fprintf(fid, '%04X,\n', vals_u16(k));
    else
        fprintf(fid, '%04X;\n', vals_u16(k));
    end
end
fclose(fid);

disp('Generated sin256.mem and sin256.coe');

2.2 DDS模块仿真结果

2.3 test Bench(模块的测试文件)

`timescale 1ns / 1ps

module tb_dds;

  localparam AW = 4;
  localparam DW = 32;

  reg               clk;
  reg               rstn;

  // AXI-Lite
  reg  [AW-1:0]     awaddr;
  reg  [2:0]        awprot;
  reg               awvalid;
  wire              awready;

  reg  [DW-1:0]     wdata;
  reg  [DW/8-1:0]   wstrb;
  reg               wvalid;
  wire              wready;

  wire [1:0]        bresp;
  wire              bvalid;
  reg               bready;

  reg  [AW-1:0]     araddr;
  reg  [2:0]        arprot;
  reg               arvalid;
  wire              arready;

  wire [DW-1:0]     rdata;
  wire [1:0]        rresp;
  wire              rvalid;
  reg               rready;

  // AXIS out
  wire [15:0]       m_axis_tdata;
  wire              m_axis_tvalid;
  wire [1:0]        m_axis_tkeep;
  wire              m_axis_tlast;
  reg               m_axis_tready;

  wire fire = m_axis_tvalid && m_axis_tready;

  // ---------------- clock ----------------
  initial clk = 1'b0;
  always #5 clk = ~clk; // 100MHz

  // ---------------- DUT ----------------
  // 注意:你的顶层 dds_axilite_max_fre_50Hz_v1_0 也必须已经加了 tkeep/tlast 端口并往下连到 S00_AXI
  dds_axilite_max_fre_50MHz_v1_0 dut (
    .s00_axi_aclk    (clk),
    .s00_axi_aresetn (rstn),

    .s00_axi_awaddr  (awaddr),
    .s00_axi_awprot  (awprot),
    .s00_axi_awvalid (awvalid),
    .s00_axi_awready (awready),

    .s00_axi_wdata   (wdata),
    .s00_axi_wstrb   (wstrb),
    .s00_axi_wvalid  (wvalid),
    .s00_axi_wready  (wready),

    .s00_axi_bresp   (bresp),
    .s00_axi_bvalid  (bvalid),
    .s00_axi_bready  (bready),

    .s00_axi_araddr  (araddr),
    .s00_axi_arprot  (arprot),
    .s00_axi_arvalid (arvalid),
    .s00_axi_arready (arready),

    .s00_axi_rdata   (rdata),
    .s00_axi_rresp   (rresp),
    .s00_axi_rvalid  (rvalid),
    .s00_axi_rready  (rready),

    .m_axis_tdata    (m_axis_tdata),
    .m_axis_tvalid   (m_axis_tvalid),
    .m_axis_tkeep    (m_axis_tkeep),
    .m_axis_tlast    (m_axis_tlast),
    .m_axis_tready   (m_axis_tready)
  );

  // ---------------- AXI-Lite tasks ----------------
  // 适配你这个从设备模板:AWREADY/WREADY 只会在 AWVALID&WVALID 同时为1 时同周期脉冲
  task axi_write;
    input [AW-1:0] addr;
    input [DW-1:0] data;
    integer tmo;
    begin
      @(posedge clk);
      awaddr  <= addr;
      awprot  <= 3'b000;
      awvalid <= 1'b1;

      wdata   <= data;
      wstrb   <= {DW/8{1'b1}};
      wvalid  <= 1'b1;

      tmo = 0;
      while (!(awready && wready)) begin
        @(posedge clk);
        tmo = tmo + 1;
        if (tmo > 1000) begin
          $display("[%0t] ERROR: axi_write timeout (awready&wready never true)", $time);
          $finish;
        end
      end

      @(posedge clk);
      awvalid <= 1'b0;
      wvalid  <= 1'b0;

      bready <= 1'b1;
      tmo = 0;
      while (!bvalid) begin
        @(posedge clk);
        tmo = tmo + 1;
        if (tmo > 1000) begin
          $display("[%0t] ERROR: axi_write timeout (bvalid never asserted)", $time);
          $finish;
        end
      end
      @(posedge clk);
      bready <= 1'b0;
    end
  endtask

  task axi_read;
    input  [AW-1:0] addr;
    output [DW-1:0] data;
    integer tmo;
    begin
      @(posedge clk);
      araddr  <= addr;
      arprot  <= 3'b000;
      arvalid <= 1'b1;

      tmo = 0;
      while (!arready) begin
        @(posedge clk);
        tmo = tmo + 1;
        if (tmo > 1000) begin
          $display("[%0t] ERROR: axi_read timeout (arready never asserted)", $time);
          $finish;
        end
      end

      @(posedge clk);
      arvalid <= 1'b0;

      rready <= 1'b1; // 提前拉高更稳
      tmo = 0;
      while (!rvalid) begin
        @(posedge clk);
        tmo = tmo + 1;
        if (tmo > 1000) begin
          $display("[%0t] ERROR: axi_read timeout (rvalid never asserted)", $time);
          $finish;
        end
      end

      data = rdata;
      @(posedge clk);
      rready <= 1'b0;
    end
  endtask

  // ---------------- AXIS monitor ----------------
  integer beat_cnt;        // 累计接收的 beat 数(fire 次数)
  integer frame_beat_cnt;  // 当前帧内 beat 计数

  always @(posedge clk) begin
    if (!rstn) begin
      beat_cnt       <= 0;
      frame_beat_cnt <= 0;
    end else if (fire) begin
      beat_cnt <= beat_cnt + 1;

      // 检查 TKEEP(你设计里应恒为 2'b11)
      if (m_axis_tkeep !== 2'b11) begin
        $display("[%0t] WARN: tkeep=%b (expected 11)", $time, m_axis_tkeep);
      end

      // 帧计数 & TLAST 检查
      if (m_axis_tlast) begin
        $display("[%0t] TLAST asserted at beat_cnt=%0d, frame_len_observed=%0d, sample=%0d",
                 $time, beat_cnt, frame_beat_cnt + 1, $signed(m_axis_tdata));
        frame_beat_cnt <= 0;
      end else begin
        frame_beat_cnt <= frame_beat_cnt + 1;
      end
    end
  end

  // ---------------- test sequence ----------------
  reg [31:0] rd;


  initial begin
    // init
    awaddr  = {AW{1'b0}}; awprot  = 3'b000; awvalid = 1'b0;
    wdata   = {DW{1'b0}}; wstrb   = {DW/8{1'b0}}; wvalid  = 1'b0;
    bready  = 1'b0;
    araddr  = {AW{1'b0}}; arprot  = 3'b000; arvalid = 1'b0;
    rready  = 1'b0;

    m_axis_tready = 1'b1;

    // reset
    rstn = 1'b0;
    repeat(20) @(posedge clk);
    rstn = 1'b1;

    // ---------------- Config: 1MHz @100MHz ----------------
    axi_write(4'h4, 32'h028F5C29);   // slv_reg1 FTW
    axi_write(4'h8, 32'h00000000);   // slv_reg2 phase offset
    axi_write(4'hC, 32'h00007FFF);   // slv_reg3 amp

    // 关键:slv_reg0 同时设置 enable 和 frame_len
    // frame_len 放在 [31:16],enable=bit0,hold_reset=bit1
    // 这里设置 frame_len=16,enable=1 -> 0x0010_0001
    axi_write(4'h0, 32'h0010_0001);

    // readback
    axi_read(4'h0, rd); $display("[%0t] reg0 = 0x%08h", $time, rd);
    axi_read(4'h4, rd); $display("[%0t] reg1(FTW) = 0x%08h", $time, rd);
    axi_read(4'h8, rd); $display("[%0t] reg2(PH)  = 0x%08h", $time, rd);
    axi_read(4'hC, rd); $display("[%0t] reg3(AMP) = 0x%08h", $time, rd);

    // 跑一段时间,应该会频繁看到 TLAST(每 16 个 fire 一次)
    repeat(600) @(posedge clk);

    // ---------------- Change to 2MHz ----------------
    axi_write(4'h4, 32'h051EB852);
    axi_read (4'h4, rd);
    $display("[%0t] FTW (expect 2MHz) readback = 0x%08h", $time, rd);

    repeat(600) @(posedge clk);

    // ---------------- Freeze output (FTW=0) ----------------
    axi_write(4'h4, 32'h00000000);
    axi_read (4'h4, rd);
    $display("[%0t] FTW (expect freeze) readback = 0x%08h", $time, rd);

    repeat(200) @(posedge clk);

    $finish;
  end

endmodule

2.4 DDS模块的驱动

2.4.1 使用petalinux提供的内核模板工程创建

petalinux-create -t modules -n dds-drv --enable

2.4.2 驱动编写

2.4.3 单独编译驱动

petalinux-build -c dds-drv

2.4.4 编译结果

输出的第一行就是 上板驱动文件位置(复制 转移到开发板上电启动)

cc@cc-virtual-machine:~/Desktop/petalinux-dds/DDS$ cd /home/cc/Desktop/petalinux-dds/DDS
find build/ -type f -name "*.ko" | grep -E "dds|drv"
build/tmp/sysroots-components/zynq_generic_7z020/dds-drv/lib/modules/6.1.5-xilinx-v2023.1/extra/dds-drv.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/package/lib/modules/6.1.5-xilinx-v2023.1/kernel/drivers/uio/.debug/uio_pdrv_genirq.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/package/lib/modules/6.1.5-xilinx-v2023.1/kernel/drivers/uio/uio_pdrv_genirq.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/packages-split/kernel-dbg/lib/modules/6.1.5-xilinx-v2023.1/kernel/drivers/uio/.debug/uio_pdrv_genirq.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/packages-split/kernel-module-uio-pdrv-genirq-6.1.5-xilinx-v2023.1/lib/modules/6.1.5-xilinx-v2023.1/kernel/drivers/uio/uio_pdrv_genirq.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/image/lib/modules/6.1.5-xilinx-v2023.1/kernel/drivers/uio/uio_pdrv_genirq.ko
build/tmp/work/zynq_generic_7z020-xilinx-linux-gnueabi/linux-xlnx/6.1.5-xilinx-v2023.1+gitAUTOINC+716921b6d7-r0/linux-zynq_generic_7z020-standard-build/drivers/uio/uio_pdrv_genirq.ko

2.5 DDS模块应用层

2.5.1 仿照编写驱动 使用petalinux创建工程

cc@cc-virtual-machine:~/Desktop/petalinux-dds/DDS$ petalinux-create -t apps --template c --name dds-app
INFO: Create apps: dds-app

2.5.2 应用层部分拆解

①支持DDS数据缓存
dds-app --out /mnt/sd/dds.bin --freq 1000000 --amp 1000 --phase 0 --frame 4096 --timeout-ms 2000
生成数据(应用层有点问题,生成数据之后 sync一下,否则会出现dds.bin是空文件的现象)

root@DDS:~# ./dds-app --out /mnt/dds1.bin --freq 1000000 --amp 1000 --phase 0 --frame 4096 --timeout-ms 2000
[INFO] Params:
       DDS dev=/dev/dds0 freq=1000000Hz amp=1000(milli) phase=0deg frame=4096(samples)
       DMA rx_chan=-1 rx_bytes=8192 timeout=2000ms out=/mnt/dds1.bin
[INFO] DDS REG dump (after_config): CTRL=0x10000000 FTW=0x028f5c29 PHOFF=0x00000000 AMP=0x00008000
[INFO] Using RX channel 1 (S2MM)
[INFO] DDS REG dump (after_capture): CTRL=0x10000000 FTW=0x028f5c29 PHOFF=0x00000000 AMP=0x00008000
[INFO] Writing 8192 bytes to /mnt/dds1.bin
[OK] Capture done.
root@DDS:~# sync
分析生成的数据

(解释 峰值不是完全的1Mhz)——(DDS原理是离散100MHZ为2^32份,会不完全整数现象)

(解释 频域有严重的杂波)——(A相位累加器是32bit,但是查找表仅启动高8位,sin256.mem尺寸比较小,查找表需要和mem文件尺寸相照应)

/* dds模块的输出数据逻辑 */	
// Phase accumulator (only advances on real transfer)
	reg [PHASE_W-1:0] phase_acc;
	always @(posedge S_AXI_ACLK) begin
		if (!S_AXI_ARESETN) begin
		phase_acc <= {PHASE_W{1'b0}};
		end else if (hold_reset) begin
		phase_acc <= {PHASE_W{1'b0}};
		end else if (fire) begin
		phase_acc <= phase_acc + ftw;
		end
	end

	// ROM address = MSBs of phase + offset
	wire [PHASE_W-1:0] phase_sum = phase_acc + ph_off;


!!!!在此进取其高8位,低24位数据舍弃,表现出的子峰值是由一定周期性的,不是完全随机!!!!!!!

	wire [LUT_AW-1:0]  lut_addr  = phase_sum[PHASE_W-1 -: LUT_AW]; // top 8 bits

②支持DDS数据流式传输(TCP协议)
        1、开发板的静态IP地址设定(开机就没了)
ip addr add 192.168.10.2/24 dev eth0
ip link set eth0 up
        2、链路测速(Zynq  → PC)
root@DDS:~# iperf3 -s
-----------------------------------------------------------
Server listening on 5201 (test #1)
-----------------------------------------------------------
Accepted connection from 192.168.10.1, port 2212
[  5] local 192.168.10.2 port 5201 connected to 192.168.10.1 port 2213
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  87.4 MBytes   733 Mbits/sec
[  5]   1.00-2.00   sec  87.6 MBytes   735 Mbits/sec
[  5]   2.00-3.00   sec  87.7 MBytes   736 Mbits/sec
[  5]   3.00-4.00   sec  87.8 MBytes   737 Mbits/sec
[  5]   4.00-5.00   sec  87.9 MBytes   737 Mbits/sec
[  5]   5.00-6.00   sec  87.9 MBytes   737 Mbits/sec
[  5]   6.00-7.00   sec  87.9 MBytes   738 Mbits/sec
[  5]   7.00-8.00   sec  87.9 MBytes   737 Mbits/sec
[  5]   8.00-9.00   sec  87.9 MBytes   737 Mbits/sec
[  5]   9.00-10.00  sec  87.9 MBytes   737 Mbits/sec
[  5]  10.00-10.00  sec   172 KBytes   741 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec   878 MBytes   736 Mbits/sec                  receiver
-----------------------------------------------------------
Server listening on 5201 (test #2)
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated
3、启动命令

(pc)启动python监听9000端口

(zynq)

./dds-app config --freq 1000000 --amp 1000 --phase 0 --frame 4096
./dds-app start
./udp-tx --dest-ip 192.168.10.1 --dest-port 9000

        运行期间修改频率

./dds-app set --freq 1500000

        结束

./dds-app stop

4、实机运行结果(频域显示有这个杂散波)

2.5.3 编译结果

cc@cc-virtual-machine:~/Desktop/petalinux-dds/DDS$ petalinux-build -c dds-app
[INFO] Sourcing buildtools
[INFO] Building dds-app
[INFO] Silentconfig project
[INFO] Generating kconfig for rootfs
[INFO] Silentconfig rootfs
[INFO] Generating plnxtool conf file
[INFO] Generating workspace directory
INFO: bitbake dds-app
NOTE: Started PRServer with DBfile: /home/cc/Desktop/petalinux-dds/DDS/build/cache/prserv.sqlite3, Address: 127.0.0.1:38957, PID: 14547
Loading cache: 100% |############################################################################################################| Time: 0:00:02
Loaded 6272 entries from dependency cache.
Parsing recipes: 100% |##########################################################################################################| Time: 0:00:02
Parsing of 4346 .bb files complete (4341 cached, 5 parsed). 6277 targets, 357 skipped, 1 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Initialising tasks: 100% |#######################################################################################################| Time: 0:00:03
Sstate summary: Wanted 11 Local 4 Mirrors 0 Missed 7 Current 219 (36% match, 96% complete)
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 785 tasks of which 768 didn't need to be rerun and all succeeded.
INFO: Failed to copy built images to tftp dir: /tftpboot
[INFO] Successfully built dds-app

2.5.4 可执行文件位置(补充)

PetaLinux/Yocto 启用了 rm_work(或类似清理机制),在打包完成后把 workdir 里的 image/、packages-split/、编译产物都清掉了,只保留 temp/log.* 这些日志脚本。
所以你在 work/.../dds-app/1.0-r0/ 里看不到 dds-app 是正常的

cd /home/cc/Desktop/petalinux-dds/DDS
find build/tmp/deploy/rpm -type f -name "*dds-app*.rpm"

3、petalinux配置(一些相关的开发包)

3.1 petalinux-config -c kernel

无需特殊调整(调试一下这个是否无需密码登录等等,方便进一步调试)

3.2 petalinux-config -c rootfs

①网络相关开发包

②开发板 板上编译

③汇总(不确定哪些有用 相关的全勾选上了)

CONFIG_system-zynq=y
CONFIG_e2fsprogs-mke2fs=y
CONFIG_fpga-manager-script=y
CONFIG_mtd-utils=y
CONFIG_can-utils=y
CONFIG_ethtool=y
CONFIG_nfs-utils=y
CONFIG_openssh=y
CONFIG_openssh-misc=y
CONFIG_openssh-dbg=y
CONFIG_openssh-sshd=y
CONFIG_openssh-keygen=y
CONFIG_openssh-ssh=y
CONFIG_openssh-dev=y
CONFIG_openssh-sftp=y
CONFIG_openssh-sftp-server=y
CONFIG_openssh-scp=y
CONFIG_tcp-wrappers=y
CONFIG_file=y
CONFIG_pciutils=y
CONFIG_strace=y
CONFIG_vim=y
CONFIG_run-postinsts=y
CONFIG_libsocketcan=y
CONFIG_udev-extraconf=y
CONFIG_linux-xlnx-udev-rules=y
CONFIG_gdb=y
CONFIG_gdbserver=y
CONFIG_glib-2.0=y
CONFIG_glib-2.0-utils=y
CONFIG_glib-2.0-dbg=y
CONFIG_glib-2.0-bash-completion=y
CONFIG_glib-2.0-dev=y
CONFIG_glibc=y
CONFIG_glibc-dev=y
CONFIG_glibc-dbg=y
CONFIG_packagegroup-core-boot=y
CONFIG_packagegroup-core-buildessential=y
CONFIG_packagegroup-core-ssh-dropbear=y
CONFIG_packagegroup-core-ssh-dropbear-dev=y
CONFIG_tcf-agent=y
CONFIG_bridge-utils=y
CONFIG_netcat=y
CONFIG_netcat-dbg=y
CONFIG_netcat-dev=y
CONFIG_tcpdump=y
CONFIG_tcpdump-dbg=y
CONFIG_tcpdump-dev=y
CONFIG_u-boot-tools=y
CONFIG_packagegroup-petalinux-networking-stack=y
CONFIG_packagegroup-petalinux-self-hosted=y
CONFIG_imagefeature-ssh-server-openssh=y
CONFIG_imagefeature-hwcodecs=y
CONFIG_imagefeature-debug-tweaks=y
CONFIG_imagefeature-empty-root-password=y
CONFIG_Init-manager-sysvinit=y

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐