学长程序阅读

程序分析

程序主要实现以下功能:

  1. 数据采集模拟:通过模拟 ADC 模块生成多路采样数据;
  2. 数据打包处理:将采集数据封装为BLOB格式,便于网络传输;
  3. 网络协议栈:基于 UDP/IP 协议实现数据的高速网络发送与接收;
  4. 物理层接口:通过 SFP 光模块实现光纤通信。

系统工作流程

  1. 初始化阶段:系统上电后,时钟模块生成各频率时钟,PLL 锁定后解除复位;SFP 物理层初始化,准备光纤通信链路。
  2. 数据采集与打包:按下arm按钮后,ad_data_gen生成 32 通道模拟数据;数据经b64_data_gen打包为 64 位帧,再由ad_data_blob_generator封装为 BLOB 格式,添加帧结构与元信息。
  3. 网络传输:BLOB 数据通过blob_fifo_udp_transmit缓存,转换为 UDP 负载;UDP_IP_MAC模块为数据添加 IP 头、UDP 头,通过 SFP 光模块发送至网络;可选UDP_ECHO回环功能,用于验证数据传输的完整性。
  4. 调试与监控:系统内置 ILA 逻辑分析仪(ila_0、ila_2),可抓取关键信号(如 BLOB 数据、UDP 负载)用于调试。

内部模块划分与交互

时钟与复位生成模块

功能:通过 PLL(锁相环)产生多组时钟,满足不同模块的时序需求,如 ADC 采样时钟、网络协议栈时钟等。

数据采集与预处理模块

功能:

  1. ad_data_gen:模拟 32 通道 ADC 采样,生成测试数据;
  2. b64_data_gen:将多通道数据封装为 64 位帧,便于后续处理。

工作流程

ad_data_gen:arm_cap信号高电平到来后,start_ad置1,LED 点亮,开始采集;total_sample_cnt记录已采集的样本数,采样模式为单次模式下,达到total_sample_num后停止,流式模式下,通过stop_trigger强制中断采集流程。

b64_data_gen:模块接收6个32通道16位数据,通过sampling_len实现降采样,例如sample_coef=2时,每 2 个原始样本取 1 个;all_ch_counter根据掩码计算总有效通道数ch_len;之后循环遍历 6 个模块的 32 通道,每次处理 1 个 16 位通道数据,4 次拼接成 64 位帧,b64_cnt跟踪拼接进度,完成 4 次拼接后输出有效 64 位帧;异步 FIFO 实现从 96MHz 到 100MHz 时钟域的转换,read_b64_ready控制数据读出。

BLOB 数据生成模块

功能:
每次输入 4 通道数据,将其封装为 BLOB,若 BLOB 总长度超过 UDP 负载上限,自动计算切片数量和每切片采样点数;之后通过状态机进行切片处理,最终输出 AXI Stream 格式数据。

UDP 网络传输模块

// BLOB数据缓存与UDP传输  
blob_fifo_udp_transmit blob_fifo_udp_transmit_inst(  
    .s_data_slice_axis_tdata(...)  // 输入数据切片  
    .m_udp_payload_axis_tdata(...) // 输出UDP负载数据  
);  

// UDP/IP协议栈  
UDP_IP_MAC #(  
    .LOCAL_IP({8'd192, 8'd168, 8'd1, 8'd10})  // 本地IP地址  
) UDP_IP_MAC_0_inst(...);  

// UDP回环测试(用于调试)  
UDP_ECHO #(  
    .LOCAL_PORT(16'd8010)  // 本地端口号  
) UDP_ECHO_inst(...);  

功能: blob_fifo_udp_transmit将 BLOB 数据通过 FIFO 缓存后,转换为 UDP 负载; UDP_IP_MAC实现 IP 层与 UDP 层协议,支持数据发送与接收; UDP_ECHO实现数据回环,用于测试网络链路连通性。

物理层接口模块

功能:控制 SFP 光模块的电气特性,实现光纤信号的收发,并生成网络物理层所需的时钟。

ad_data_gen

该模块用于模拟产生32通道ADC采集数据。

输入输出信号:

module ad_data_gen(
    input       ad_clk,    // 100mhz时钟
    input       ad_rst,    // 100mhz时钟对应的复位信号,高电平有效

    input       arm_cap,  // 触发信号,该信号的高电平脉冲到来后开始产生模拟采集数据
    input [31:0]sample_rate,  // 32位采样率,设定的是32'd2_000_000,即2M
    input [7:0] stream_mode,  // 工作模式,为0时采样total_sample_num次后就停止采样,不为0时接收到stop_trigger信号后停止采样
    input       stop_trigger,  // 停止触发信号
    input [31:0]total_sample_num,  // 每次采样次数
    output      led_o,  // 工作状态与led关联,led亮起时表明正在模拟产生adc采样数据
    output      start_ad,  // 高电平时表明正在模拟产生adc采集数据

    // sample data output.
    input       ad_data_ready,   // 阅读这个模块,ad_data_ready信号没有用上
    output      ad_data_valid,   // 每次上升沿表示更新一次ad_data_32ch数据
    output      ad_data_32ch    // 输出的32通道数据
    );

系统工作流程:

  1. 系统采样工作由arm_cap信号决定何时开始,程序设定的工作模式stream_mode=0,因此采样total_num次后停止采样。程序中total_num为5,因此一轮采样工作更新5次采样数据ad_data_32ch。
  2. 采样数据每次更新可通过读取ad_data_valid信号来识别,ad_data_valid上升沿时刻更新ad_data_32ch。
  3. 采样数据通过模块LTC2324_CMOS生成,该模块用于模拟ad板卡工作,通过start_ad控制工作时间,通过sample_rate控制采样率,根据输入信号设定的状态产生模拟的adc采集数据。
  4. 系统根据模块LTC2324_CMOS的工作流程,将32通道数据整合成ad_data_32ch。

b64_data_gen

输入输出信号:

module b64_data_gen(
    input clk_100m,    // 100mhz时钟信号
    input clk_384m,    // 384mhz时钟信号,用于b16数据产生
    input clk_96m,     // 96mhz时钟信号,用于b64数据产生
    input rst,         // 复位信号,与384mhz时钟、96mhz时钟关联
    input rst_locked,  // 与100mhz时钟关联的复位信号

    // 复制6次32通道adc采集数据
    input [511:0] module1_data,
    input [511:0] module2_data,
    input [511:0] module3_data,
    input [511:0] module4_data,
    input [511:0] module5_data,
    input [511:0] module6_data,

    input ad_data_valid,  // ad采集数据更新信号
    input [31:0]total_sample_num,  // 总采样数,程序中设置为5次

    // 通过掩码指示32*6个通道数据中,哪些通道数据有效,用于屏蔽不需要的通道,只处理有效通道的数据
    input [31:0] module1_mask,
    input [31:0] module2_mask,
    input [31:0] module3_mask,
    input [31:0] module4_mask,
    input [31:0] module5_mask,
    input [31:0] module6_mask,

    input [7:0] sr_coef,  // 二次调整采样率相关的时间参数,与sample_coef配合使用
    input [7:0] sample_coef,  // 数据采集的下采样比率,用于降低原始采样数据的频率,例如,若sample_coef=4,则每 4 个原始采样点仅保留 1 个,采样率降为原来的 1/4
    //程序中上面两个变量已经设定的值都是1,那就不用进行降采样,直接按照原始采样率即可
    output [7:0] channel_num,  // 显示当前有效通道的数量,由掩码中1计数得到

    output [63:0] b64_data_reg,
    output b64_valid_reg,
    output sample_end_reg,  // 指向新采样周期的开始,用于同步后续处理模块
    output sample_last_reg,  // 标记真正的最后一个有效数据,用于数据帧边界识别。
    output sample_last_last_reg,  // 通知系统 “下一个是最后一个有效数据”,用于提前准备结束逻辑
    
    //ila data,用于ila进行的调试
    output reg [15:0] b16_data,  // 单通道数据,是b64数据生成的中间数据
    output reg b16_data_valid,  //指示单通道数据是否有效
    output reg [63:0] b64_data,
    output reg b64_valid,
    output reg [63:0] b64_data_long,
    output reg b64_valid_long,
    output reg b64_valid_short,
    
    input read_b64_ready,  // ad_data_blob_generator模块输入的信号,用于控制本模块中fifo读出数据
    
    output b64_en_sync_wire,  //高电平时表示正在将数据转换成为4通道64b格式数据
    output [7:0] sampling_cnt_wire,
    output [31:0] total_sample_cnt_wire,  // 总采样计数器,显示已完成的采样点数
    output [63:0] sampling_data_wire,
    output [7:0] down_sample_cnt_wire,  // 下采样分频计数器,显示当前分频计数状态
    output [5:0] module_ch_cnt_wire,  // 模块通道计数器,指示当前处理的通道号
    output [3:0] module_cnt_wire,  // 模块计数器,指示当前处理的模块号
    output [63:0] module_data_wire,
    output [31:0] module_mask_wire,
    output finish_wire,    // 整个采样过程结束标志(所有样本采集完成)
    output sample_end_wire,  // 样本结束标志(单个样本处理完成)
    output sample_end_long_wire    
    );

系统工作流程:

  1. 首先根据降采样倍率相关变量sr_coef、sample_coef,进行降采样操作,如果sr_coef与sample_coef均为1,则无需降采样,直接使用原始采样数据即可。
  2. 每次ad_data_valid高电平到来后,拉高b64_en_sync信号,开始采集数据转换成4通道64b格式数据的工作。
  3. 通过状态机,依次处理6个module的数据,每个module中的单通道数据以及对应的掩码,通过移位操作输入到b16_data与b16_valid中,b16_valid指示该通道数据是否有效。
  4. 再读取b16_data数据流,通过状态机,将有效的b16_data依次拼接成4通道数据b64_data;每完成一次b64_data生成,b64_valid产生一次脉冲信号,且通道计数器ch_cnt等加4,借助于通道计数器生成sample_last_last和sample_last信号。
  5. 每产生一次b64_data,end_sample_cnt计数一次,当产生的b64_data达到降采样后所需的数据量total_sample_num/sample_coef时,拉低b64_en_sync信号,结束工作。
  6. b64格式数据生成后,并不是直接输出,而是将b64_data与其他信号整合成68位数据,当后面的ad_data_blob_generator模块需要b64形式数据时,从fifo读出数据并使用。

ad_data_blob_generator

输入输出信号:

module ad_data_blob_generator #
(
    // maximum UDP payload length
    parameter MAX_PAYLOAD_LENGTH = 40,
    // one data blob's sample length.
    parameter BLOB_SAMPLE_LENGTH = 8,
    // input channel number.
    parameter AD_CHANNEL = 4,
    // input ad data length in bytes
    parameter AD_DATA_LENGTH = 2,
    // input and output data width
    parameter DATA_WIDTH = 64,
    // AXIS output KEEP_WIDTH = DATA_WDITH/8
    parameter KEEP_WIDTH = 8,
    // AXIS user signal width.
    parameter USER_WIDTH = 1,
    // config information request send interval.
    parameter REQUEST_SEND_INTERVAL = {32'd100000000}
)
(
    // 使用100mhz时钟
    input  wire clk,
    input  wire rst,
    
    // request for AD blob generators config signals.
    input  wire config_request_send,
    input  wire config_request_succed,

    // ADC data input.
    input  wire [63:0]           ad_data_4ch,  // 即上面生成的b64_data
    output wire                  ad_blob_ready,
    input                        b64_valid,  // 高电平时可以读b64_data
    input                        sample_end,  // 采样周期结束信号
    input                        sample_last,  // 采样周期内最后一个信号标记
    input                        sample_last_last,  //倒数第二个信号标记
    output                       read_b64_ready,
    output                       read_b64_ready_nxt,
    input                        start_ad,  // 前面ad_data_gen开始产生ADC采集数据

    // AD parameter
    input      [7:0]             job_id,
    input wire [31:0]            sample_rate,  // 采样率
    input wire [7:0]             channel_num,  // 通道数
    input wire [31:0]            sample_point_num,  // 每个 Blob 包含的采样点数
    input wire [7:0]             sample_coef,  // 降采样倍率
    
    // slice and blob output via axis.
    output wire [DATA_WIDTH-1:0] m_data_slice_axis_tdata,
    output wire [KEEP_WIDTH-1:0] m_data_slice_axis_tkeep,
    output wire                  m_data_slice_axis_tvalid,
    input  wire                  m_data_slice_axis_tready,
    output wire                  m_data_slice_axis_tlast,
    output wire [USER_WIDTH-1:0] m_data_slice_axis_tuser,
    
    output wire [7:0]            m_data_slice_sa_len,
    output wire [15:0]           m_data_slice_axis_tlen,
    
    output data_blob_sa_len,
    output data_blob_payload_len,
    output data_slice_number,
    output data_slice_prev_sa_len,
    output data_slice_last_sa_len,
    output data_slice_divisible,
    
    output state_ge_reg,
    output state_ge_next,
    
    output data_blob_cnt,
    output data_blob_cnt_next,
    output data_slice_cnt,
    output data_slice_cnt_next,
    output data_slice_sa_cnt,
    output data_slice_sa_cnt_next,
    output config_request_send_timer,
    output config_request_send_timer_next,
    
    output wire m_data_slice_ready,
    output wire m_data_blob_ready            
 );

工作流程:

1. 模块首先根据配置参数(sample_point_num、MAX_PAYLOAD_LENGTH 等)计算 Blob 和 Slice 的结构。

Blob:

  • sample_point_num为每个 Blob 包含的采样点数
  • data_blob_payload_len 为Blob总长度,等于头部长度 + 采样数据长度;其中,头部占 16 字节(data_slice_header_len=16),采样数据长度为 sample_point_num × 通道数 × 每通道字节数。

Slice分割规则:

  • 若 Blob 总长度 ≤ MAX_PAYLOAD_LENGTH,则 Blob 作为一个 Slice 传输。
  • 否则前 N-1 个 Slice 的采样点数按最大负载计算,最后一个Slice包含余下的data_slice_last_sa_len个采样点,data_slice_number表示Slice 总数。

2. 之后模块通过 4 位状态机 控制 Blob/Slice 的生成流程。

状态机状态设置如下:

localparam [3:0]
    STATE_GE_IDLE        = 4'd0,  // 空闲
    STATE_GE_CONFIG      = 4'd1,  // 配置信息发送
    STATE_GE_CONFIG_LAST = 4'd2,  // 配置信息发送最后一拍
    STATE_GE_HEADER      = 4'd3,  // Blob/Slice 头部生成
    STATE_GE_HEADER_LAST = 4'd8,  // 头部生成分两周期发送
    STATE_GE_SLICE       = 4'd4,  // 采样数据传输(普通 Slice)
    STATE_GE_SLICE_LAST  = 4'd5,  // 采样数据传输(最后一个 Slice)
    STATE_GE_SLICE_END   = 4'd6,  // Slice 传输结束
    STATE_GE_BLOB_END    = 4'd7;  // Blob 传输结束

状态机跳转逻辑正在理解,可以结合龙哥的论文来看。

程序对接思考

目前程序可以考虑将ad_data_gen模块修改为数据解码模块后的数据处理模块,数据解码模块需要根据ad_data_gen的逻辑调整。

原来的数据解码模块是通过fifo依次输出32通道16位数据,如果要对接过来,需要设置逻辑,将单词采样的所有数据组装成32通道的512位数据,组装完成后输出,同时提供ad_data_valid信号表明传输后的采集数据已更新。本质上就是需要将传输后的数据转换成便于打包的形式。

现在还需要注意程序中时钟、采样率等问题,而且最后要做成的目标也得结合龙哥论文、杰哥limfx文章来明确清楚。


本文章使用limfx的vscode插件快速发布