0%

FPGA数字信号处理之典型IP核

本节主要介绍了数字信号处理在FPGA硬件实现中,经常需要使用的IP核(基于vivado的开发环境)。

乘法器IP核的使用

1.IP核的相关配置

image-20231012150732762

image-20231012144456111

image-20231012151357723

2.Testbench

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`timescale 1ns / 1ps

module mulit_ip_tb();
// Inputs
reg clk;
reg sclr;
reg [17:0] a;
reg [17:0] b;

// Outputs
wire [35:0] p;

// Instantiate the Unit Under Test (UUT)
mult_gen_0 uut (
.CLK(clk),
.SCLR(sclr),
.A(a),
.B(b),
.P(p)
);

initial begin
clk = 0; //设置时钟信号初始状态
sclr = 1; //上电后开始复位清零
a = 0; //设置输入信号的初始状态
b = 0; //设置输入信号的初始状态
#100; //等待100ns
sclr = 0; //取消复位清零
end

//生成周期为20ns,频率为50MHz的时钟信号
always #10 clk <= ~clk;

//生成两个输入信号a、b
always @(posedge clk) begin
a <= a + 10 ;
b <= b + 20;
end

endmodule
  • 结果如下:

    image-20231012153823359


ROM IP核的使用

1.IP核的相关配置

image-20231012164231297

image-20231012164449327

image-20231012170057287

image-20231012170346016

2.MATLAB代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fc=3e6; % 信号频率
fs=65e6; % 采样频率
L=1024; % 采样点数
ADC_bit=16; % 采样位数

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 产生信号
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
t=0:1/fs:(L-1)/fs;
noise=randn(1,length(t));
noise=0;
st=sin(2*pi*fc*t)+noise;
y=st/max(abs(st)); % 归一化
yt=round(y*(2^(ADC_bit-1)-1)); % 16bit量化
figure(1);
plot(st);hold on;
figure(2);
plot(y);hold on;
figure(3);
plot(yt);hold on;


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MATLAB生成coe文件
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 在.coe文件中
% 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
% 从第 3 行开始到第最后一行,是这个 L(数据长度为1024)* ADC_bit(16bit) 大小 ROM 的初始化数据。
% 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
fid=fopen('data0.coe','w');
fprintf(fid,'Memory_Initialization_Radix = 2;\r\n'); % 二进制
fprintf(fid,'Memory_Initialization_Vector = \r\n');
for p=1:L
B_s=dec2bin(yt(p)+(yt(p)<0)*2^ADC_bit,ADC_bit);%十进制转二进制
for q=1:ADC_bit % 16位,依次判断这16位的数值
if B_s(q)=='1'
data=1;
else
data=0;
end
fprintf(fid,'%d',data);
end

% 下面if语句的目的
% 每行数字后面用逗号(,),最后一行数字结束用分号(;)
if (p<L)
fprintf(fid,',\r\n');
else
fprintf(fid,';\r\n'); % 分号(;) 结束标志位
end

end
fclose(fid);

3.Testbench

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
`timescale 1ns / 1ps

module rom_ip_tb();
reg sys_clk; //50MHz时钟
reg rst_n; //复位,低电平有效

wire [15:0] rom_data; //ROM读出数据 每个数据有16bit
reg [9:0] rom_addr; //ROM输入地址 1024个数据,需要2^10个地址

//产生ROM地址读取数据
always @ (posedge sys_clk or negedge rst_n)
begin
if(!rst_n)
rom_addr <= 10'd0;
else
rom_addr <= rom_addr+1'b1;
end

//实例化ROM
blk_mem_gen_0 rom_ip_inst
(
.clka (sys_clk ), //inoput clka
.addra (rom_addr ), //input [9:0] addra 1024个数据,需要2^10个地址
.douta (rom_data ) //output [15:0] douta
);

initial begin
sys_clk = 0;
rst_n = 0;
#21 rst_n = 1;
end
always #10 sys_clk = ~sys_clk;

endmodule
  • 结果如下:

    image-20231012172524577

  • 其实结构输出data数据与上升沿并不是齐平的,说明不能在上升沿结束立马得到数据,而是有一定延时

4.Reference


DDS IP核的使用

  • DDS的作用,产生正余弦波

  • 其产生原理如下图,其通过在圆周上不断旋转,在y轴和x轴上的投影构成正余弦波:

    image-20231120165850569

  • 每个时钟周期旋转一格,当在一圈内旋转的格越多时(即把圆分成的份数越多时),精度越高,设格数为$2^B,B为bit数$,那么输出频率$f_{out}$为:

  • 那么在时钟频率和bit数都确定时,如何调整输出频率呢?用通俗易懂的话来说,也就是:虽然圆被分成了很多份,但我读格子的时候,隔几格再读一个数,不就可以改变输出频率了么,设读数的间隔为$F_{cw}$,则输出频率$f_{out}$有:

    • 其实,$\frac{f_{clk}}{2^B}$就相当于数字频率分辨率(类比FFT的采样频率$f_s$)

1.IP核的相关配置

image-20231120171253311

image-20231120171453447

2.Testbench

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
`timescale 1ms / 1ms //这里这里仿真的尺度

module DDS_ip_tb;

reg clk_100Hz;
wire out_valid;
wire [7:0] sin_data;

//产生2Hz的正弦信号
dds_compiler_0 u1(
.aclk(clk_100Hz),
.s_axis_config_tvalid(1'b1),
.s_axis_config_tdata(16'd1311),
.m_axis_data_tvalid(out_valid),
.m_axis_data_tdata(sin_data)
);

initial begin
clk_100Hz = 0;
#100;
end

always #5 clk_100Hz <= ~clk_100Hz;

endmodule
  • 结果如下:

    image-20231120164947730


FIR IP核的使用

1.IP核的相关配置

image-20231121155548381

image-20231121155927847

image-20231121160309324

2.MATLAB代码

  • 注意:下述代码中量化与在FIR滤波器设计这一节中提到的不一致,下述代码中直接乘以$2^{11}$,方便后续右移11将大小还原
  • 下述matlab代码主要用来求滤波系数,其实可以用matlab中filterDesigner可视化的GUI设计滤波器并导出coe系数文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
N=62;                              %滤波器长度
fs=12.5*10^6; %采样频率
fc=10^6; %低通滤波器的3dB截止频率

%采用fir1函数设计FIR滤波器
b=fir1(N-1,fc*2/fs);

%对滤波器系数进行量化
B=12; %量化位宽为12比特
%Q_h=b/max(abs(b)); %系数归一化处理
%Q_h=Q_h*(2^(B-1)-1); %乘以B比特位宽的最大正整数
Q_h=b*(2^11); %乘以B比特位宽的最大正整数
Q_h12=round(Q_h); %四舍五入

ma=max(abs(Q_h12))

%将生成的滤波器系数数据写入FPGA所需的COE文件中
fid=fopen('D:\App_Data_File\Vivado_data\Vivado_project\DSP_design\FIR_design\fir_coefficient.coe','w'); %新建并打开COE文件
fprintf(fid,'radix = 10;\r\n'); %写滤波器系数的进制
fprintf(fid,'coefdata =\r\n'); %写系数数据的标识
fprintf(fid,'%8d\r\n',Q_h12); %写滤波器系数
fprintf(fid,';'); %写分号“;”
fclose(fid); %关闭COE文件

m=sum(abs(Q_h12)) %求滤波器系数绝对值之和

%求滤波器的幅频响应
m_12=20*log10(abs(fft(Q_h12,1024)));
%对幅频响应归一化处理
m_12=m_12-max(m_12);

%设置幅频响应的横坐标单位为MHz
x_f=[0:(fs/length(m_12)):fs/2]/10^6;
%只显示正频率部分的幅频响应
m12=m_12(1:length(x_f));

%绘制幅频响应曲线
plot(x_f,m12);
xlabel('频率(MHz)');ylabel('幅度(dB)');
legend('12bit量化');
grid on;

untitled

3.Testbench

  • FIR_ip_design.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    module FIR_ip_design(
    input rst,
    input clk_data, //12.5MHz
    input clk_fir, //50MHz
    output m_data_tvalid,
    output [19:0] dout,
    output [8:0] dout_normal
    );

    wire [7:0] xin;
    wire s_tready;
    wire signed [23:0] m_tdata;

    fir_ip_test_data u0 (
    .clk(clk_data),
    .dout(xin)
    );

    fir_compiler_0 u1 (
    .aclk(clk_fir), // input clk
    .s_axis_data_tvalid(1'b1),
    .s_axis_data_tready(s_tready),
    .s_axis_data_tdata(xin), // input [7 : 0] din
    .m_axis_data_tvalid(m_data_tvalid),
    .m_axis_data_tdata(m_tdata) // output [23 : 0] dout,因为axi4总线的最小单位是8bit,所以自动补齐到24bit
    );

    assign dout = m_tdata[19:0];
    assign dout_normal = dout[19:11];

    endmodule
  • fir_ip_test_data.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    module fir_ip_test_data(
    input clk, //系统时钟为12.5MHz
    output reg signed [7:0] dout //输出为500kHz和2MHz的叠加信号
    );

    wire signed [7:0] sin500k,sin2m; //实际DDS ip核设置的是7bit,为了使modelsim仿真不报错,设置为8bit

    //生成500kHz的正弦信号
    dds_for_fir_ip u1(
    .aclk(clk),
    .s_axis_config_tvalid(1'b1),
    .s_axis_config_tdata(16'd2621),
    .m_axis_data_tvalid(),
    .m_axis_data_tdata(sin500k)
    );

    //生成2MHz的正弦信号
    dds_for_fir_ip u2 (
    .aclk(clk),
    .s_axis_config_tvalid(1'b1),
    .s_axis_config_tdata(16'd10486),
    .m_axis_data_tvalid(),
    .m_axis_data_tdata(sin2m)
    );

    //求和运算输出频率叠加信号
    always @(posedge clk) begin
    dout <= sin500k + sin2m;
    end

    endmodule
  • FIR_ip_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    module FIR_ip_tb;

    // Inputs
    reg rst;
    reg clk_data;
    reg clk_fir;

    // Outputs
    wire m_data_tvalid;
    wire [19:0] dout;
    wire [8:0] dout_normal;

    // Instantiate the Unit Under Test (UUT)
    FIR_ip_design uut (
    .rst(rst),
    .clk_data(clk_data),
    .clk_fir(clk_fir),
    .m_data_tvalid(m_data_tvalid),
    .dout(dout),
    .dout_normal(dout_normal)
    );

    reg clk_100m;
    initial begin
    // Initialize Inputs
    rst = 1;
    clk_data = 0;
    clk_fir = 0;
    clk_100m = 0;

    // Wait 100 ns for global reset to finish
    #100;
    rst = 0;
    // Add stimulus here
    end

    always #5 clk_100m <= !clk_100m;

    reg [2:0] cn=0;
    always @(posedge clk_100m) begin
    cn <= cn + 1;
    clk_fir <= cn[0]; //50MHz
    clk_data <= cn[2]; //12.5MHz
    end

    endmodule
  • 仿真结果如下:

    image-20231121161030105

    image-20231121205139577

4.Reference


FFT IP核的使用

1.IP核的相关配置

image-20231024141407431

image-20231024142435510

image-20231024143541679

2.MATLAB代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fs=100; % 采样频率
f1 = 10; % 信号频率
f2 = 30;
L=128; % 采样点数
ADC_bit=16; % 采样点数据的位数

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 产生信号
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
t=0:1/fs:(L-1)/fs;
s1 = cos(2*pi*f1*t);
s2 = cos(2*pi*f2*t);
st = 2 + s1 + s2;
y=st/max(abs(st)); % 归一化
yt=round(y*(2^(ADC_bit-1)-1)); % 16bit量化
figure(1);
plot(st);hold on;
figure(2);
plot(y);hold on;
figure(3);
plot(yt);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MATLAB生成TXT文件
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%。
fid=fopen('D:\App_Data_File\Vivado_data\Vivado_project\Zynq7020_Project\FFT_ip\MATLABdata_before_fft.txt','w');
for p=1:L
B_s=dec2bin(yt(p)+(yt(p)<0)*2^ADC_bit,ADC_bit);%十进制转二进制
for q=1:ADC_bit % 16位,依次判断这16位的数值
if B_s(q)=='1'
data=1;
else
data=0;
end
fprintf(fid,'%d',data);
end

if (p<L)
fprintf(fid,'\r\n');
end

end
fclose(fid);

figure(4);
y = fft(yt,L);
y = abs(y);
n = 0:(L-1);
f = n*fs/L;
plot(f,y);

3.Testbench

image-20231024232553967

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
`timescale 1ns / 1ps

module fft_tb2;

reg clk;
reg rst_n;
reg [15:0] dati_in;
reg [15:0] datq_in;
reg [23:0] dati_out;
reg [23:0] datq_out;
reg [15:0] dataI [127:0];
reg [47:0] fft_abs;

reg fft_s_data_tvalid;
wire [31:0] fft_s_data_tdata;
//reg fft_s_data_tlast;
wire fft_s_config_tready;
wire fft_s_data_tready;
wire [47:0] fft_m_data_tdata;
wire fft_m_data_tvalid;
wire fft_m_data_tlast;
wire [7:0] fft_m_data_tuser;
wire fft_event_frame_started;
wire fft_event_tlast_unexpected;
wire fft_event_tlast_missing;
wire fft_event_status_channel_halt;
wire fft_event_data_in_channel_halt;
wire fft_event_data_out_channel_halt;

initial begin
clk=1;
rst_n=0;
//fft_s_data_tlast=1'b0;
fft_s_data_tvalid=1'b0;
dati_in=0;
datq_in=0;
dati_out=0;
datq_out=0;
fft_abs=0;

$readmemb("D:\\App_Data_File\\Vivado_data\\Vivado_project\\Zynq7020_Project\\FFT_ip\\MATLABdata_before_fft.txt",dataI);
#100
rst_n=1;
end

always #5 clk=~clk;
reg[7:0] count=0;

always @(posedge clk) begin
if (fft_s_data_tready) begin
if(count==128) begin
fft_s_data_tvalid=1'b0;
//fft_s_data_tlast=1'b0;
#10000
count=0;
end
else if(count==127)begin
dati_in<= dataI[count];
datq_in<=16'd0;
fft_s_data_tvalid<=1'b1;
//fft_s_data_tlast<=1'b1;
count<=count+1;
end
else begin
dati_in<= dataI[count];
datq_in<=16'd0;
fft_s_data_tvalid=1'b1;
count<=count+1;
//fft_s_data_tlast<=1'b0;
end
end

end

assign fft_s_data_tdata = {dati_in,datq_in}; //尤其注意这里

fft_test u_fft_test(
.clk(clk),
.rst_n(rst_n),
.tvalid_i(fft_s_data_tvalid),
.tdata_i(fft_s_data_tdata),
//.fft_s_data_tlast(fft_s_data_tlast),
.fft_s_config_tready(fft_s_config_tready),
.fft_s_data_tready(fft_s_data_tready),
.fft_m_data_tdata(fft_m_data_tdata),
.fft_m_data_tvalid(fft_m_data_tvalid),
.fft_m_data_tlast(fft_m_data_tlast),
.fft_m_data_tuser(fft_m_data_tuser),
.fft_event_frame_started(fft_event_frame_started),
.fft_event_tlast_unexpected(fft_event_tlast_unexpected),
.fft_event_tlast_missing(fft_event_tlast_missing),
.fft_event_status_channel_halt(fft_event_status_channel_halt),
.fft_event_data_in_channel_halt(fft_event_data_in_channel_halt),
.fft_event_data_out_channel_halt(fft_event_data_out_channel_halt)
);

always @(posedge clk) begin
if(fft_m_data_tvalid) begin
dati_out<=fft_m_data_tdata[23:0];
datq_out<=fft_m_data_tdata[47:24];
end
end

always @(posedge clk) begin
fft_abs<=$signed(dati_out)* $signed(dati_out)+ $signed(datq_out)* $signed(datq_out);
end

endmodule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
`timescale 1ns / 1ps

module fft_test(
input clk,
input rst_n,
input tvalid_i,
input [31:0] tdata_i,
//input fft_s_data_tlast,
output fft_s_config_tready,

output fft_s_data_tready,
output [47:0] fft_m_data_tdata,
output fft_m_data_tvalid,
output fft_m_data_tlast,
output [7:0] fft_m_data_tuser,
output fft_event_frame_started,
output fft_event_tlast_unexpected,
output fft_event_tlast_missing,
output fft_event_status_channel_halt,
output fft_event_data_in_channel_halt,
output fft_event_data_out_channel_halt
);

reg fft_s_data_tvalid=1'b0;
reg [31:0] fft_s_data_tdata=32'd0;
reg fft_s_data_tlast=1'b0;
reg [7:0] count=8'd0;

always @(posedge clk) begin
if(!rst_n) begin
fft_s_data_tvalid<=1'b0;
fft_s_data_tdata<=32'd0;
fft_s_data_tlast<=1'b0;
count<=8'd0;
end
else if (tvalid_i && fft_s_data_tready) begin
if(count==127)begin
fft_s_data_tvalid<=1'b1;
fft_s_data_tlast<=1'b1;
fft_s_data_tdata<=tdata_i;
count<=0;
end
else begin
fft_s_data_tvalid=1'b1;
count<=count+1;
fft_s_data_tlast<=1'b0;
fft_s_data_tdata<=tdata_i;
end
end
else begin
fft_s_data_tvalid<=1'b0;
fft_s_data_tlast<=1'b0;
fft_s_data_tdata<=fft_s_data_tdata;
end
end

xfft_0 u1_fft(
.aclk(clk), // input wire aclk
.aresetn(rst_n), // input wire aresetn
.s_axis_config_tdata(8'd1), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(1'b1), // input wire s_axis_config_tvalid
.s_axis_config_tready(fft_s_config_tready), // output wire s_axis_config_tready
.s_axis_data_tdata(fft_s_data_tdata), // input wire [31 : 0] s_axis_data_tdata
.s_axis_data_tvalid(fft_s_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(fft_s_data_tready), // output wire s_axis_data_tready
.s_axis_data_tlast(fft_s_data_tlast), // input wire s_axis_data_tlast
.m_axis_data_tdata(fft_m_data_tdata), // output wire [47 : 0] m_axis_data_tdata
.m_axis_data_tuser(fft_m_data_tuser), // output wire [7 : 0] m_axis_data_tuser
.m_axis_data_tvalid(fft_m_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tready(1'b1), // input wire m_axis_data_tready
.m_axis_data_tlast(fft_m_data_tlast), // output wire m_axis_data_tlast
.event_frame_started(fft_event_frame_started), // output wire event_frame_started
.event_tlast_unexpected(fft_event_tlast_unexpected), // output wire event_tlast_unexpected
.event_tlast_missing(fft_event_tlast_missing), // output wire event_tlast_missing
.event_status_channel_halt(fft_event_status_channel_halt), // output wire event_status_channel_halt
.event_data_in_channel_halt(fft_event_data_in_channel_halt), // output wire event_data_in_channel_halt
.event_data_out_channel_halt(fft_event_data_out_channel_halt) // output wire event_data_out_channel_halt
);

endmodule
  • 结果如下:

    image-20231024224042607

  • 放大一点看结果的频谱图

    image-20231024224616096

  • 这里的结果和matlab中的一致:

    sinwave_for_fft

4.Reference


Cordic IP核的使用

1.IP核的相关配置

image-20240319191134297

image-20240319192249585

2.Testbench

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
`timescale 1ns / 1ps

module Cordic_ip_tb;

// square Parameters
parameter PERIOD = 10;


// square Inputs
reg clk = 0 ;
reg rst_n = 0 ;
reg data_in_valid = 0 ;
reg [31:0] data_in = 0 ;

// square Outputs
wire data_out_valid ;
wire [15:0] data_out ;


initial
begin
forever #(PERIOD/2) clk=~clk;
end

initial
begin
#52 rst_n = 1;
end

cordic_square square_U1(
.aclk(clk),
.aresetn(rst_n),
.s_axis_cartesian_tvalid(data_in_valid),
.s_axis_cartesian_tdata(data_in), //其实data_in最多31bit,VHL参考模板有误,还是得看IP核是如何设置的
.m_axis_dout_tvalid(data_out_valid),
.m_axis_dout_tdata(data_out)
);

reg [4:0] cnt ;

//----------------------------------------------------------------------
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
data_in_valid <= 0;
data_in <= 0;
cnt <= 0;
end
else if(cnt < 10)
begin
data_in_valid <= 1;
data_in <= {$random} % {8{1'b1}};
cnt <= cnt + 1;
end
else
begin
data_in_valid <= 0;
data_in <= 0;
cnt <= cnt;
end
end
//----------------------------------------------------------------------

endmodule

RF Data Converter IP核的使用

1.IP核的相关配置

image-20240110125626654

image-20240110125946346

image-20240110130310317

  • 最终配置如下:

    image-20240110132214936

2.MATLAB代码

  • 用来生成测试单频信号的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    close all;
    clear all;
    clc;
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % 参数定义
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    fc=156.25e6; % 信号频率
    fs=2.5e9; % 采样频率
    L=16; % 采样点数
    ADC_bit=16; % 采样位数

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % 产生信号
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    t=0:1/fs:(L-1)/fs;
    noise=randn(1,length(t));
    noise=0;
    st=sin(2*pi*fc*t)+noise;
    y=st/max(abs(st)); % 归一化
    yt=round(y*(2^(ADC_bit-1)-1)); % 16bit量化
    figure(1);
    plot(st);
    figure(2);
    plot(y);
    figure(3);
    plot(yt);


    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % MATLAB生成coe文件
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % 在.coe文件中
    % 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
    % 从第 3 行开始到第最后一行,是这个 L(数据长度为1024)* ADC_bit(16bit) 大小 ROM 的初始化数据。
    % 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
    fid=fopen('sin156o25M.coe','w');
    fprintf(fid,'Memory_Initialization_Radix = 2;\r\n'); % 二进制
    fprintf(fid,'Memory_Initialization_Vector = \r\n');
    for p=1:L
    B_s=dec2bin(yt(p)+(yt(p)<0)*2^ADC_bit,ADC_bit);%十进制转二进制
    for q=1:ADC_bit % 16位,依次判断这16位的数值
    if B_s(q)=='1'
    data=1;
    else
    data=0;
    end
    fprintf(fid,'%d',data);
    end

    % 下面if语句的目的
    % 每行数字后面用逗号(,),最后一行数字结束用分号(;)
    if (p<L)
    fprintf(fid,',\r\n');
    else
    fprintf(fid,';\r\n'); % 分号(;) 结束标志位
    end

    end
    fclose(fid);
    • MATLAB生成的波形:

      image-20240110135744248

    • DAC结果:

      image-20240110135807426

3.Python代码

  • 用来处理经过ADC后的数据,画出波形

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import numpy as np
    from matplotlib import pyplot as plt

    def split_string(input_str, chunk_size):
    return [input_str[i:i + chunk_size] for i in range(0, len(input_str), chunk_size)]

    if __name__ == '__main__':

    hex_num = []
    try:
    with open('/media/data/zhl/ila_data_20240108_215000.txt', 'r') as file:
    for line in file:
    number = line.strip()
    hex_num.append(number)

    except FileNotFoundError:
    print('文件不存在')
    except Exception as e:
    print(f'异常:{e}')

    print('文件读取完毕')

    # hex_num = np.array(hex_num)

    int_num = []

    for line in hex_num:
    # 对每一行,先reshape为16*8的样子,然后再转换数据类型
    temp = split_string(line, 4)
    for item in temp:
    int_num.append(int(item, 16))

    for i in range(0, len(int_num)):
    if (int_num[i] & 0x8000) == 0x8000:
    # 补码,取反加一
    int_num[i] = -((int_num[i]-1) ^ 0xFFFF)

    width = 2000
    height = 500
    dpi = 180
    fig = plt.figure(figsize=(width/dpi, height/dpi), dpi=dpi)
    plt.plot(int_num)
    plt.tight_layout()
    plt.show()

    print('ok')
    • ADC结果:

      image-20240110135454794

4.Reference


FIFO IP核的使用

1.IP核的相关配置

image-20240111151849763

image-20240111152255385

  • 且对于Standard FIFO:实际位宽为255,对于First Word Fall Through:实际位宽为257

image-20240111152355346

image-20240111153202181

2.源代码

  • ip_fifo.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    `timescale 1ns / 1ps

    module ip_fifo(
    input sys_clk , // 时钟信号
    input sys_rst_n // 复位信号
    );

    //wire define
    wire fifo_wr_en ; // FIFO写使能信号
    wire fifo_rd_en ; // FIFO读使能信号
    wire [7:0] fifo_din ; // 写入到FIFO的数据
    wire [7:0] fifo_dout ; // 从FIFO读出的数据
    wire almost_full ; // FIFO将满信号
    wire almost_empty ; // FIFO将空信号
    wire fifo_full ; // FIFO满信号
    wire fifo_empty ; // FIFO空信号
    wire [7:0] fifo_wr_data_count ; // FIFO写时钟域的数据计数
    wire [7:0] fifo_rd_data_count ; // FIFO读时钟域的数据计数

    //*****************************************************
    //** main code
    //*****************************************************

    //例化FIFO IP核
    fifo_generator_0 fifo_ip_u1 (
    .wr_clk ( sys_clk ), // input wire wr_clk
    .rd_clk ( sys_clk ), // input wire rd_clk

    .wr_en ( fifo_wr_en ), // input wire wr_en
    .rd_en ( fifo_rd_en ), // input wire rd_en

    .din ( fifo_din ), // input wire [7 : 0] din
    .dout ( fifo_dout ), // output wire [7 : 0] dout

    .almost_full (almost_full ), // output wire almost_full
    .almost_empty (almost_empty ), // output wire almost_empty
    .full ( fifo_full ), // output wire full
    .empty ( fifo_empty ), // output wire empty

    .wr_data_count ( fifo_wr_data_count ), // output wire [7 : 0] wr_data_count
    .rd_data_count ( fifo_rd_data_count ) // output wire [7 : 0] rd_data_count
    );

    //例化写FIFO模块
    fifo_wr u_fifo_wr(
    .clk ( sys_clk ), // 写时钟
    .rst_n ( sys_rst_n ), // 复位信号

    .fifo_wr_en ( fifo_wr_en ) , // fifo写请求
    .fifo_wr_data ( fifo_din ) , // 写入FIFO的数据
    .almost_empty ( almost_empty ), // fifo空信号
    .almost_full ( almost_full ) // fifo满信号
    );

    //例化读FIFO模块
    fifo_rd u_fifo_rd(
    .clk ( sys_clk ), // 读时钟
    .rst_n ( sys_rst_n ), // 复位信号

    .fifo_rd_en ( fifo_rd_en ), // fifo读请求
    .fifo_dout ( fifo_dout ), // 从FIFO输出的数据
    .almost_empty ( almost_empty ), // fifo空信号
    .almost_full ( almost_full ) // fifo满信号
    );

    endmodule
  • fifo_wr.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    `timescale 1ns / 1ps

    module fifo_wr(
    //mudule clock
    input clk , // 时钟信号
    input rst_n , // 复位信号
    //FIFO interface
    input almost_empty, // FIFO将空信号
    input almost_full , // FIFO将满信号
    output reg fifo_wr_en , // FIFO写使能
    output reg [7:0] fifo_wr_data // 写入FIFO的数据
    );

    //reg define
    reg [1:0] state ; //动作状态
    reg almost_empty_d0 ; //almost_empty 延迟一拍
    reg almost_empty_syn ; //almost_empty 延迟两拍
    reg [3:0] dly_cnt ; //延迟计数器
    //*****************************************************
    //** main code
    //*****************************************************

    //因为 almost_empty 信号是属于FIFO读时钟域的
    //所以要将其同步到写时钟域中
    always@( posedge clk ) begin
    if( !rst_n ) begin
    almost_empty_d0 <= 1'b0 ;
    almost_empty_syn <= 1'b0 ;
    end
    else begin
    almost_empty_d0 <= almost_empty ;
    almost_empty_syn <= almost_empty_d0 ;
    end
    end

    //向FIFO中写入数据
    always @(posedge clk ) begin
    if(!rst_n) begin
    fifo_wr_en <= 1'b0;
    fifo_wr_data <= 8'd0;
    state <= 2'd0;
    dly_cnt <= 4'd0;
    end
    else begin
    case(state)
    2'd0: begin
    if(almost_empty_syn) begin //如果检测到FIFO将被读空
    state <= 2'd1; //就进入延时状态
    end
    else
    state <= state;
    end
    2'd1: begin
    if(dly_cnt == 4'd10) begin //延时10拍
    //原因是FIFO IP核内部状态信号的更新存在延时
    //延迟10拍以等待状态信号更新完毕
    dly_cnt <= 4'd0;
    state <= 2'd2; //开始写操作
    fifo_wr_en <= 1'b1; //打开写使能
    end
    else
    dly_cnt <= dly_cnt + 4'd1;
    end
    2'd2: begin
    if(almost_full) begin //等待FIFO将被写满
    fifo_wr_en <= 1'b0; //关闭写使能
    fifo_wr_data <= 8'd0;
    state <= 2'd0; //回到第一个状态
    end
    else begin //如果FIFO没有被写满
    fifo_wr_en <= 1'b1; //则持续打开写使能
    fifo_wr_data <= fifo_wr_data + 1'd1; //且写数据值持续累加
    end
    end
    default : state <= 2'd0;
    endcase
    end
    end

    endmodule
  • fifo_rd.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    module fifo_rd(
    //system clock
    input clk , // 时钟信号
    input rst_n , // 复位信号
    //FIFO interface
    input [7:0] fifo_dout , // 从FIFO读出的数据
    input almost_full ,// FIFO将满信号
    input almost_empty,// FIFO将空信号
    output reg fifo_rd_en // FIFO读使能
    );

    //reg define
    reg [1:0] state ; // 动作状态
    reg almost_full_d0 ; // fifo_full 延迟一拍
    reg almost_full_syn ; // fifo_full 延迟两拍
    reg [3:0] dly_cnt ; //延迟计数器

    //*****************************************************
    //** main code
    //*****************************************************

    //因为 fifo_full 信号是属于FIFO写时钟域的
    //所以要将其同步到读时钟域中
    always@( posedge clk ) begin
    if( !rst_n ) begin
    almost_full_d0 <= 1'b0 ;
    almost_full_syn <= 1'b0 ;
    end
    else begin
    almost_full_d0 <= almost_full ;
    almost_full_syn <= almost_full_d0 ;
    end
    end

    //读出FIFO的数据
    always @(posedge clk ) begin
    if(!rst_n) begin
    fifo_rd_en <= 1'b0;
    state <= 2'd0;
    dly_cnt <= 4'd0;
    end
    else begin
    case(state)
    2'd0: begin
    if(almost_full_syn) //如果检测到FIFO将被写满
    state <= 2'd1; //就进入延时状态
    else
    state <= state;
    end
    2'd1: begin
    if(dly_cnt == 4'd10) begin //延时10拍
    //原因是FIFO IP核内部状态信号的更新存在延时
    //延迟10拍以等待状态信号更新完毕
    dly_cnt <= 4'd0;
    state <= 2'd2; //开始读操作
    end
    else
    dly_cnt <= dly_cnt + 4'd1;
    end
    2'd2: begin
    if(almost_empty) begin //等待FIFO将被读空
    fifo_rd_en <= 1'b0; //关闭读使能
    state <= 2'd0; //回到第一个状态
    end
    else //如果FIFO没有被读空
    fifo_rd_en <= 1'b1; //则持续打开读使能
    end
    default : state <= 2'd0;
    endcase
    end
    end

    endmodule
  • 整体架构:

    image-20240111153246240

  • Vivado中的RTL视图:

    image-20240111153458045

3.Testbench

  • ip_fifo_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    `timescale 1ns / 1ps

    module ip_fifo_tb;

    // Inputs
    reg sys_clk;
    reg sys_rst_n;

    // Instantiate the Unit Under Test (UUT)
    ip_fifo u_ip_fifo (
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n)
    );

    //Genarate the clk
    parameter PERIOD = 20;
    always begin
    sys_clk = 1'b0;
    #(PERIOD/2) sys_clk = 1'b1;
    #(PERIOD/2);
    end

    initial begin
    // Initialize Inputs
    sys_rst_n = 0;
    // Wait 100 ns for global reset to finish
    #100 ;
    sys_rst_n = 1;
    // Add stimulus here

    end

    endmodule
  • 仿真结果:

    image-20240111153831859

    image-20240111154343389

4.Reference


AXI DMA IP核的使用

欢迎来到ssy的世界