zynq7020+axilite+DDS+platform driver+linux+UDP+PC显示波形
本文介绍了基于正点原子7020开发板的DDS信号发生器设计与实现。硬件部分使用AXI-Lite总线控制的DDS IP核,通过MATLAB生成正弦波查找表文件(sin256.mem和sin256.coe)。软件方面开发了Linux驱动程序和应用程序,支持DDS参数配置、数据缓存(输出到文件)和TCP流式传输功能。测试结果显示系统能生成1MHz正弦波,并通过千兆网口实现736Mbps的数据传输速率。文
·
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
更多推荐




所有评论(0)