2023电赛H题

dds compiler ip

设置输出频率(output frequencies)

在这里插入图片描述

  1. configuration options只有这一种写法
  2. parameter selection 同理

不输出相位

在这里插入图片描述
1.

  • 选system Parameter,固定频率
  • 选Hardware Parameter,频率可编程(频率控制字PINC,phase increment)计算公式如下:
    f o u t = P I N C × f c l k 2 B ( θ ) f_{out}=\frac{PINC\times f_{clk}}{2^{B(\theta)}} fout=2B(θ)PINC×fclk
  • 运算示例:
    PINC设置位置如下图
    在这里插入图片描述
    B ( θ ) 如下图,为 16 B(\theta)如下图,为16 B(θ)如下图,为16
    在这里插入图片描述

设计与仿真文件的层次关系

在这里插入图片描述

  • sim_1中的代码文件会随着design source中的添加而自动添加
  • sim中tb是top文件
  • vivado中不能对文件直接重命名,ip核也一样,改名比较麻烦
  • 括号外的名字是代码中的module名,里面是.v文件名

ip核报错声称没有编译

  • 问题描述:…is not compiled in library ‘xil_defaultlib’
  • 解决:右键ip,reset output products\

concurrent assignment

  • 报错:[VRFC 10-3236] concurrent assignment to a non-net ‘clk’ is not permitted [“D:/vivado_prj/fft_verify/fft_verify.srcs/sources_1/new/fft_verify.v”:2]
  • 解决:
    input reg clk 改为:input clk

dds采样频率

暂时认定是系统时钟频率

FFT IP

注意

FFT的仿真时间可能较长,实测接近10ms-10_000us-10_000_000ns,不要看见ns为单位数字很大就觉得时间很长哦。另外,modelsim虽说打开仿真的时间相比vivado自带仿真短非常多,但是开始仿真后的运行时间上并没有这么大的差距。

IP核交互是用AXI-Stream接口

AXI-Stream接口分为主机(master)和从机(slave),只有ready信号和valid信号同时为高时数据才能读写。举个例子,主机检测从机发出的ready信号,当ready为高时将valid信号拉高即可读写。

端口例化

xfft_0 xfft_0_u (
  .aclk(clk_1M),
  .aresetn(rst_n),
  .s_axis_config_tdata(8'd1),                  // input wire [7 : 0] 
  //s_axis_config_tdata=8'd1时进行FFT, 0时做ifft
  .s_axis_config_tvalid(1'd1),                // input wire 
  .s_axis_config_tready(fft_s_config_tready),                // output
  .s_axis_data_tdata(fft_s_data_tdata),                      // input wire [15 : 0]信号输入 [15:8]虚部,[7:0]实部,8实8虚
  .s_axis_data_tvalid(fft_s_data_tvalid),                    //  input,当s_axis_data_tready高电平后,将s_axis_data_tvalid拉高L个周期,输入L个数据进行fft;L是FFT的点数
  .s_axis_data_tready(fft_s_data_tready),                    //output,rst拉高两个时钟周期后,该口给1输出,此时ip核初始化完成,可进行数据输入,注意,这个是输出,ip核自己给信号,不用你给。管valid不管ready
  .s_axis_data_tlast(fft_m_data_tlast),                      // input,数据输入完毕后为1,数据输入结束
  .m_axis_data_tdata(fft_m_data_tdata),                      // output,48bit频谱输出,[18:0]实部[42:24]虚部,19实,19虚
  .m_axis_data_tuser(fft_m_data_tuser),                      // output,[15 : 0],[9:0]有效,[15:10]恒为0
  .m_axis_data_tvalid(fft_m_data_tvalid),                    // output,有输出时为1,无输出为0
  .m_axis_data_tready(1'd1),                    // input,恒为1
  .m_axis_data_tlast(fft_m_data_tlast),                      // output,当fft输出到最后一个结果时拉高一个时钟周期后,拉低
  .event_frame_started(fft_event_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(fft_event_tlast_unexpected),            // output,当数据输入完毕之前s_axis_data_tlast就拉高时给出“不速之客”信号
  .event_tlast_missing(fft_event_tlast_missing),                  // output,当数据输入完毕而s_axis_data_tlast,没有拉高时给last缺失信号
  .event_status_channel_halt(fft_event_status_channel_halt),      // output,状态通道输出不正常时给信号
  .event_data_in_channel_halt(fft_event_data_in_channel_halt),    // output,需要数据输入但未收到时给信号
  .event_data_out_channel_halt(fft_event_data_out_channel_halt)  // output,需要数据输出但没有输出时给信号
); 

输入点数控制代码

always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin//复位
        fft_s_data_tvalid <= 1'b0;
        fft_s_data_tlast  <= 1'b0;
        data_finish_flag  <= 1'b0;
        count <= 16'd0;
    end
    else if (fft_s_data_tready) begin //data_ready高
        if(count == Length) begin//点数计满
            fft_s_data_tvalid <= 1'b0;//无效
            fft_s_data_tlast  <= 1'b1;//结束
            count <= 16'd0;//计数归零
            data_finish_flag <= 1'b1;//结束标志
        end
        else begin//点数未计满
            fft_s_data_tvalid <= 1'b1;//有效
            fft_s_data_tlast  <= 1'b0;//未结束
            count <= count + 1'b1;//计数加一
        end
    end
    else begin//data_ready无效
        fft_s_data_tvalid <= 1'b0;//无效
        fft_s_data_tlast  <= 1'b0;//结束
    end
end

FFT波形分析

  1. 频率理论分辨率:
    F s N \frac{Fs}{N} NFs
  • Fs为输入FFT的时钟频率,决定了输入FFT的采样间隔
  • N为FFT的点数
  • 如果假设FFT结果峰值的最佳位置为 f = F s 4 f=\frac{Fs}{4} f=4Fs F s = 4 × f Fs=4\times f Fs=4×f
  1. FFT ip核需要配置target clock,如下图所示,最低1MHz
    在这里插入图片描述
  2. PLL分频有限制,当input clk为100MHz时:
    在这里插入图片描述
    频率范围是4.687M到800M
    再低的频率只能计数器分频
  3. 1M clk_fft,1024点下,测得100kHz正弦频率为 102.5 k H z = 105 × 102.5kHz=105\times 102.5kHz=105×,此时理论频率分辨率为 1 0 6 ÷ 1024 = 976.5 {10^6\div1024}=976.5 106÷1024=976.5, 频率误差为2.5kHz。理论上的频点应当在

H题解题

看了一篇H题分析特别好的文章,有所感悟:

H题的难点有两个

  • 其一是准确分析得到间隔5kHz的频率,在这个要求下,2.5kHz的误差是无法接受的
  • 其二是准确复现波形,其频率差要求在万分之一以内,只用DDS无法做到,必须要通过计算复现信号与原始信号的相位差,实时进行调整。

问题一:准确识别频率

  1. 思路一:寻找FFT的更准确结果
  • 调整Fs , L , CLK
  • 改变寻找峰值的方法, 尝试通过多点幅值找到更准确的频点
  • 分析现有频点不准的原因, 重点分析头尾部分
  • 分析结果:现有FFT可以满足5kHz间隔的频率分隔,尝试一下50k和100k正弦的混合、分离
  1. 思路二: 在想要的频点上使用DFT, 从而更灵活地选择点数. 要求理解点数和采样率的倍数关系对结果造成的影响, 以及DFT的实际编程实现

50k和100k分离

  1. DDS加法
  2. FFT找最高峰和次高峰
    下图是100k正弦的[47:0]fft_abs(实际上是实数虚数的平方和)
    在这里插入图片描述
  • 尝试截取[40:30],还活着的信号,就是比较大的了,记录每个非零数据以及对应的tuser,搞个3位的数组,每个fft_clk都判断一下。因为关注的频率分量比较少,图省事可以来个数组,后面来的数据就不要了,根据满没满的标志位来判断。如果要“喜新厌旧”,那就搞一个数组构成的移位寄存器,或者FIFO ip,如果数据量比较大的话。
  • 上述方法有个不太通用的问题,就是峰值要多大才算大,要截取的位数不好确定也不好调整
  • 最好的还是设一个最大值判断,再来一个例如8个数的移位寄存,采用FIFO的队列模式,把历史的f_max都记录在内,这样可以随时读取到最大的8个值。其实找最大值的过程本身就是2个数的移位寄存,结合数组和循环的语言,可以更清晰地实现多个数的移位寄存,改写这段代码,替换原代码中找最值和对称取反的部分,只考虑0-511的点位即可。
  • 上面的思路有个问题,就是不能用最大值判断当做移位寄存器的输入,因为前面的较大值会掩盖后面的较小值
  • 首先应当是舍弃一些位数节约资源,本身这个平方和就没必要,直接取abs的和也能体现幅值大小,不过这里要研究一下负数取模怎么写
  • signed值其实就是补码,那么正数的补码不变,负数按位取反+1即可,代码如下:
`timescale 1ns / 1ps
//输入signed数(其实是补码),输出模值
module signed_to_abs(
	input signed [23:0]A ,
	output signed [23:0]B
);
always @(*) begin
    if (A >= 0)
        B = A;     
    else
        B = ~A + 1;  
end	
endmodule
  • 从fft_m_data_tdata里面接出来的低24位是实部,高24位是虚部,这里要注意,xfft的summary界面虽然如下写着只有[18:0]位是实部,但其实[19-23]仍然被当做实部的一部分,在表示负数时也会补上符号位1或者0。因此可以总结一下,ip核中的位数提示了实际有用位数,高位可以随意戒掉,但是不戒掉也没有关系,前提是一定要当做signed数处理
    在这里插入图片描述
    在这里插入图片描述

  • fft_m_data_tvalid为高电平是有输出的时候

  • fft_m_data_tuser从0到 L e n g t h 2 − 1 \frac{Length}{2}-1 2Length1

  • 移位寄存器示例代码:

reg [7:0] shift_reg [0:7];
    integer i;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            for (i = 0; i < 8; i = i + 1) begin
                shift_reg[i] <= 8'b0;
            end
        end else begin
            shift_reg[0] <= data_in;
            for (i = 1; i < 8; i = i + 1) begin
                shift_reg[i] <= shift_reg[i-1];
            end
        end
    end
  • 移位寄存器方法思路:首先将shift_reg初始化为0(最小值),新来的数使用插入排序,遍历过程中,一旦大于当前值[i]就插入。具体如何插入?[idx_max]到[i+1]移位,腾出[i]的位置,之后将新数写入[i].
  • 如何给新数?如何给valid信号:新数从fft_amp输入,以clk_fft为时钟,fft_m_data_tvalid(高电平)结合fft_m_data_tuser为信号

关于for循环问题

  • vivado仿真器可以运行for(integer i…)的写法,modelsim的报错说是只有systemverilog才能这么写,不给仿真
  • 同时, 应当想想for循环在verilog中究竟是展开成全部并行,还是类似状态机顺序执行,实际情况如下:
  • 仿真(Simulation):
    在仿真中,for 循环是顺序执行的。每次仿真时钟周期中,循环体都会按顺序执行。仿真主要用于描述和验证设计的行为,不会自动展开成并行逻辑。
  • 综合(Synthesis):
    综合工具在转换 Verilog 代码为实际硬件时,可能会将 for 循环展开成并行硬件。综合工具会根据循环的内容、迭代次数以及设计的具体需求来决定是否展开以及如何展开。如果循环中的操作是独立的,那么工具可能会将其展开成并行逻辑。这种展开使得每个循环迭代可以在硬件中同时进行,提高处理速度。
  • 总的来说,正确使用for是一个比较省心的方法,可以把问题丢给synthesis.如果不用,自行展开时要考虑是并行,还是线性顺序执行(if else),还是网状状态机执行

关于数组问题

  • 数组是不好当in/out端口的,建议还是展开来写,除非是内部信号可以用一下简化代码

关于signed

  • output signed reg [23:0]B这行代码在vivado和modelsim中都会报错,去掉signed后正常。这个值本身是无符号,这告诉我们非必要不用signed

找多个最大值

在这里插入图片描述

  • 如上图,找最值已经完成,接下来就是要同时存储对应的频点。思路:将每个频率amp和idx绑定操作,sorted变成二维数组,后面一个存储tuser,排序只看amp,移动数时一组一组移动,idx永远跟着amp走
  • 使用结构体组合amp和idx(这个只支持system verilog,哭,该花时间升级工具了)
// 结构体定义
    typedef struct {
        reg [24:0] amp;
        reg [9:0] index;
    } amp_index_t;
  • 数据进来时先创建好struct
  • a<=a没有什么硬件意义,空的else语句可以补血,if后面不是一定要有else
  • 这里有一个问题,一直没考虑过,tuser对应的应该是mdata,但是mdata经过abs运算后有延迟,按理说应该从mdata开始就要和tuser绑定才对
  • 另一种思路就是只处理幅值,得到几个值之后,idx只跟着对应到abs,后面找到amp后,去abs里面搜索这几个值的idx
  • 鉴于之前平方和运算没有时延影响,先这么搞,看看结果准不准
  • 看了眼结果,影响挺大的,idx和amp对不上,没啥用。而且这样采集的前8,很可能还是扎堆在那个最高峰周围的,应该设立一个机制,一定频率内的只能取一个代表,避免泄露出去的频谱一起作妖。后面的工作:1.测试用50k和100k的加法来做。2.idx和amp要想办法对应上。3.要设立上述的代表机制
引用[1]:大二第一次正式组队培训,目为2017年电赛的H(远程幅频特性测试装置)。由于能力不足,出老师将大二组的难度降了一个维度。实际上这是一道高频目(1M-40M),然而我们大二做的仅仅是处理1k-40kHz部分的信号。这道刚拿到手,虽然指导老师讲解了一遍,但是脑子里还是一头雾水,不知从何下手,经过我们一群小伙伴后来的讨论,才逐渐理解了目的意思。从一无所知到有所收获,与一群志同道合的朋友一起进步,一起成长,比起奖项,或许这才是电赛吸引人的地方。由于个人能力有限,还是个敲代码的,对于硬件原理也只是半知半解,故纰漏处望大家指正。[1] 引用[2]:至此关于此硬件部分的内容写的大致完毕了。作为一道高频,这不能通过面包板搭建以及软件仿真方法来调试电路,给硬件整体的电路调试环节带来了很多麻烦,我们小组也是屡屡碰壁,不过最后也是能做出成品,还是有些感慨的,也欢迎大家来讨论。[2] 引用[3]:我们搭的这个玩意主要分为两个部分,电磁炮电路部分和控制电路部分,还有一块电源部分,当然其实测试现场是提供学生电源的,我们纯粹是吃饱了撑的。电磁炮部分:包括两个开关(一个充电,一个放电),一个高压电容,一个升压模块,一个线圈与炮管。控制部分:两个舵机 云台,一块F407(带触控屏),两个继电器,一块OpenMV,一个超声波模块。[3] 根据引用内容,2023电赛H是一道关于远程幅频特性测试装置的目。这是一道高频,但我们大二组只处理了1k-40kHz部分的信号。在解过程中,我们遇到了一些困难,但通过小组的讨论,逐渐理解了目的意思。在硬件部分的设计中,我们分为电磁炮电路部分和控制电路部分,还有一个电源部分。电磁炮部分包括开关、高压电容、升压模块、线圈和炮管。控制部分包括舵机云台、F407开发板、继电器、OpenMV和超声波模块。由于这是一道高频,无法通过面包板搭建和软件仿真来调试电路,给整体的电路调试带来了一些麻烦。但最终我们还是成功完成了装置的制作。欢迎大家一起来讨论这道目。[1][2][3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值