写在前面
这个实验原要求是要实现
96
96
96 通道的正交混频变换(后来老师说只要不是单通道都行),因此必须使用
F
I
R
FIR
FIR IP核(手搓FIR一两个通道还行,96通道就太费劲了),所以实验成功的关键就是要确保你的Quartus能够正常的生成 FIR IP核。在所有的破解版中本人实测Quartus 18.0 Prime Standard是唯一能够正常生成IP核不会卡死或生成失败的。Quartus 13.0、Quartus 15.0和Quartus 20.0的破解版均无法正常生成(Quartus 20.0的问题是很多IP核没有密钥授权)。
本实验由于是期末周做的,来不及换Quartus完成了,所以是手搓FIR实现的,仅供参考。
相关参数(实际)
- 直线阵通道数: 2 2 2
- 信号载波频率: 100 k H z 100kHz 100kHz
- 采样率: 400 k H z 400kHz 400kHz
- 低通 F I R FIR FIR 阶数: 15 15 15
- 低通滤波器截止频率: 20 k H z 20kHz 20kHz
实验原理
由于载波频率与采样频率满足
1
:
4
1:4
1:4 的关系,因此,对于
c
o
s
w
t
coswt
coswt 通道,采样点按照以
4
4
4 为周期分别与 {
1
,
0
,
−
1
,
0
1,0,-1,0
1,0,−1,0} 相乘;对于
−
s
i
n
w
t
-sinwt
−sinwt 通道,则为 {
0
,
−
1
,
0
,
1
0,-1,0,1
0,−1,0,1} 相乘,通过观察可以发现,两个通道相加的结果实际上跟一个开关电路差不多,即二选一其中一个通道的输出结果,因此对已调信号再进行混频解调信号时,等价于已调信号采样点与{
1
,
0
,
1
,
0
1,0,1,0
1,0,1,0} 和{
0
,
1
,
0
,
1
0,1,0,1
0,1,0,1}相乘,也就是
s
(
t
)
s(t)
s(t) 与 {
1
,
0
,
1
,
0
1,0,1,0
1,0,1,0} 和{
0
,
1
,
0
,
1
0,1,0,1
0,1,0,1}相乘。
实验过程
1.测试信号生成
按照实验要求,测试信号格式应当为:第一通道第一时刻采样点、第二通道第一时刻采样点、第一通道第二时刻采样点、第二通道第二时刻采样点 ⋯ \cdots ⋯ 这种格式生成。实验中输入的已调信号利用 s i n _ g e n e r a t i o n . c p p sin\_generation.cpp sin_generation.cpp 生成,生成文件为 d a t a s e t . v h dataset.vh dataset.vh。通道波形参数定义如下:
通道一:基带信号为 10 k H z 10kHz 10kHz 正弦信号,相位为 0 0 0 。
通道二:基带信号为 10 k H z 10kHz 10kHz 正弦信号,相位为 π 2 \frac{\pi}{2} 2π 。
各通道信号长度均为 2048 2048 2048,量化精度为 8 b i t 8bit 8bit。
sin_generation.cpp
#include<bits/stdc++.h>
#define pi 3.1415926
using namespace std;
int main(){
float t=0;
int f1=10000;
int f2=10000;
int f=100000;
float fs=400000;
float tmp;
freopen("dataset.vh","w",stdout);
/* for (int i=0; i<114514; i++){
t=(float)i/fs;
tmp=sin(2*pi*f1*t)+sin(2*pi*f2*t);
tmp=tmp*4096;
cout<<(int)tmp<<','<<endl;
}*/
for (int i=0; i<2048; i++)
{
t=i/fs;
for (int j=0; j<2; j++){
if (j==0)
tmp=sin(2*pi*f1*t);//*cos(2*pi*f*t)-sin(2*pi*f1*t)*sin(2*pi*f*t);
else
tmp=sin(2*pi*f2*t+pi/2.0);//*cos(2*pi*f*t)-sin(2*pi*f2*t+pi/2.0)*sin(2*pi*f*t);
tmp=tmp*256;
if (i==2047 && j==1)
cout<<(int)tmp;
else
cout<<(int)tmp<<','<<endl;
}
}
fclose(stdout);
return 0;
}
2.测试平台(fir_test_tb.sv)
测试平台主要包括 t i m e s c a l e timescale timescale 描述、输入输出信号定义,时钟初值设定, . v h .vh .vh 采样文件读取。完整文件见 f i r _ t e s t _ t b . s v fir\_test\_tb.sv fir_test_tb.sv 。
输入输出信号定义
reg clk; //系统工作主频时钟
reg clk_400khz; //400khz采样时钟
reg rst_n; //复位信号线
wire [15:0] xin; //文件采样数据读入
wire read_en; //文件读入使能信号
wire signed [36:0] yout_ch1_real; //通道一实部解调信号FIR输出
wire signed [36:0] yout_ch1_imag; //通道一虚部解调信号FIR输出
wire signed [36:0] yout_ch2_real; //通道二实部解调信号FIR输出
wire signed [36:0] yout_ch2_imag; //通道二虚部解调信号FIR输出
时钟初值与频率设定
initial begin
clk=0;
clk_400khz=0;
#0ns rst_n=0; //reset
#1400ns rst_n=1; //normal
$display("Running testbench");
end
//define of testbench's timescale
always #10 clk=~clk; //50Mhz
always #1250 clk_400khz=~clk_400khz; //400kHz
模块参数传递
//parameters transmission
fir_test u0_fir(
.clk (clk), //模块工作主时钟
.clk_400khz (clk_400khz), //400khz采样时钟
.rst_n (rst_n), //复位信号,用于data_to_file.sv
.xin (xin), //输入采样点信号
.read_en (read_en), //文件读入使能
.yout_ch1_real (yout_ch1_real), //通道一实部FIR输出
.yout_ch1_imag (yout_ch1_imag), //通道一虚部FIR输出
.yout_ch2_real (yout_ch2_real), //通道二实部FIR输出
.yout_ch2_imag (yout_ch2_imag) //通道二虚部FIR输出
);
dataset.vh波形采样文件读取
//read .vh file
reg signed [15:0] ad_data[4096] = {`include "dataset.vh"};
reg [11:0] k = 0;
reg signed [15:0] data = 0;
reg [2:0] kk=0;
always @(negedge clk)
begin
if (read_en) begin //使能数据读入
//kk=kk+1;
if (k<=4096 && kk<2) //3
begin
k<=k+1; //读一个新的采样数据
data<=ad_data[k];
kk<=kk+1;
end
end
else begin
kk<=0;
data<=0;
end
end
assign xin=data;
自定义主逻辑模块(fir_test.sv)
自定义模块共由四个部分组成:
- d a t a _ r e a d _ p r e p a r a t i o n data\_read\_preparation data_read_preparation:数据读入。
- Q u a d r a t u r e M o d u l a t i o n & D e m o d u l a t i o n Quadrature\ Modulation\&Demodulation Quadrature Modulation&Demodulation:正交调制与解调,将输入信号分别与 c o s w t coswt coswt 与 − s i n w t -sinwt −sinwt 混频(两次),得到解调波形。
- P a r a l l e l 15 − o r d e r F I R f o r 2 C h a n n e l s Parallel\ 15-order\ FIR\ for\ 2\ Channels Parallel 15−order FIR for 2 Channels:并行十五阶双通道 F I R FIR FIR 低通滤波器。
- d a t a _ t o _ f i l e : data\_to\_file: data_to_file: M o d e l S i m ModelSim ModelSim 波形数据导出模块。
各模块时序逻辑如下:
data_read_preparation
以主时钟
c
l
k
clk
clk 信号读入测试平台中的
.
v
h
.vh
.vh 文件数据,但要以
400
k
h
z
400khz
400khz 采样速度读取该数据,留出空闲的大量
c
l
k
clk
clk 时钟周期的目的是给未来其他的扩展操作留下足够空间。以下是处理一个各通道一个采样点的时序(读->完成正交变换->FIR低通滤波的)。
新数据点的处理在
c
l
k
_
400
k
h
z
clk\_400khz
clk_400khz 下降沿到达时开始处理,因此首先需要捕获
c
l
k
_
400
k
h
z
clk\_400khz
clk_400khz 下降沿信号再拉高
r
e
a
d
_
e
n
read\_en
read_en 使能新数据的读取。
d
a
t
a
_
c
h
_
r
e
a
l
data\_ch\_real
data_ch_real 与
d
a
t
a
_
c
h
_
i
m
a
g
data\_ch\_imag
data_ch_imag 信号为正交变换处理后的信号(未经过滤波),下降沿捕获的逻辑代码如下所示:
assign negedge_of_clk_400khz=~clk_400khz_a0&clk_400khz_a1;
always @ (posedge clk)
begin
clk_400khz_a0<=clk_400khz;
clk_400khz_a1<=clk_400khz_a0;
end
通道指针 s t e p step step 的迭代比测试文件更新慢半个 c l k clk clk 时钟的好处是可以确保 x i n xin xin 的数据一定能被正确读到。
Quadrature Modulation&Demodulation
载波频率与采样频率满足
1
:
4
1:4
1:4 的关系,因此
c
o
s
w
t
coswt
coswt 变换可以等价为每个
c
l
k
_
400
k
h
z
clk\_400khz
clk_400khz 时钟读到的信号与 {
1
,
0
,
−
1
,
1
1,0,-1,1
1,0,−1,1} 相乘,对于
−
s
i
n
w
t
-sinwt
−sinwt 可以等价为与 {
0
,
−
1
,
0
,
1
0,-1,0,1
0,−1,0,1} 相乘。正交变换的逻辑代码较长,请见
f
i
r
_
t
e
s
t
.
s
v
fir\_test.sv
fir_test.sv。变换后各通道
M
o
d
e
l
S
i
m
ModelSim
ModelSim 波形如下所示。
Parallel 15-order FIR for 2 Channels
由于我电脑的 Q u a r t u s Quartus Quartus 版本问题, F I R I P FIR\ IP FIR IP 核一直无法自动生成成功,因此实验中使用的是自己用 V e r i l o g Verilog Verilog 写的 F I R FIR FIR 。考虑到时序资源的利用率以及未来可能的多通道处理,因此 F I R FIR FIR 滤波器应设计为并行 F I R FIR FIR 滤波器,这样一个 c l k clk clk 周期就能完成所有通道的 F I R FIR FIR 低通滤波输入。由于直接型 F I R FIR FIR 滤波器的抽头系数具有对称性,因此在运算时可以合并处理,这样可以用一半的阶数长度(本实验中是 8 8 8 )完成一次 F I R FIR FIR 低通滤波的所有运算。
本实验使用的十五阶
F
I
R
FIR
FIR 低通滤波器抽头系数由
M
a
t
l
a
b
F
i
l
t
e
r
D
e
s
i
g
n
e
r
Matlab\ Filter\ Designer
Matlab Filter Designer 生成,具体如下:
由于 r e g reg reg 类型变量无法存储小数,因此需要先将生成的抽头系数放大,然后去尾取整,理论上放大倍数越大,能够保留的精度越高,但对内存资源消耗就越大。
最终,低通滤波后的四路信号
M
o
d
e
l
S
i
m
ModelSim
ModelSim 波形如下所示:
3.data_to_file.sv
d
a
t
a
_
t
o
_
f
i
l
e
.
s
v
data\_to\_file.sv
data_to_file.sv 文件是老师提供的,具体的用法是放到
f
i
r
_
t
e
s
t
.
s
v
fir\_test.sv
fir_test.sv 文件内,要输出哪个信号线的波形数据,就例化一个
d
a
t
a
_
t
o
_
f
i
l
e
data\_to\_file
data_to_file 模块。
注意此模块的所有非中文注释部分绝对不可以删掉,这些语句是命令,删掉后会影响模块的正常工作!!
通过
d
a
t
a
_
t
o
_
f
i
l
e
.
s
v
data\_to\_file.sv
data_to_file.sv 的分析可知,文件输出仅在
v
a
l
i
d
valid
valid 有效时成立,因此需要写一个使能输出的逻辑,控制文件的读取。文件读取的时序设计要点是使能应该是在
F
I
R
FIR
FIR 低通滤波结束后,以
400
k
h
z
400khz
400khz 为采样频率记录一次,记录期间使能信号高电平持续长度应为一个
c
l
k
clk
clk 周期。实验中实现的方法是:已知新数据的读入操作是从
c
l
k
_
400
k
h
z
clk\_400khz
clk_400khz 的下降沿到达时刻开始,完成两个通道数据读入、延迟等待、正交变换、
F
I
R
FIR
FIR 低通滤波等操作总的时钟周期开销不超过
15
15
15 个
c
l
k
clk
clk 周期。因此可以在
c
l
k
_
400
k
h
z
clk\_400khz
clk_400khz 上升沿到来时,以一个
c
l
k
clk
clk 周期将各通道
F
I
R
FIR
FIR 输出数据写入文件。上升沿的捕获逻辑与前面下降沿捕获类似。
可以看出,这个逻辑下确实可以有效保证各通道读到的数据是已经经过
F
I
R
FIR
FIR 滤波器的数据。
4.结果验证
结果验证主要是两点: M o d e l S i m ModelSim ModelSim 波形频率与相位差的验证; M o d e l S i m ModelSim ModelSim 归一化波形与仿真归一化波形的比对验证。
ModelSim波形验证
结论:时序正确。
结论:频率正确。
结论:相位差相差 π 2 \frac{\pi}{2} 2π ,正确。
归一化波形数据对比
以通道一实部输出为例,其他三个通道方法相同,结论也相同。
验证方法:
c
m
d
cmd
cmd 控制台命令
f
c
10
k
d
a
t
a
_
w
a
v
e
.
d
a
t
y
o
u
t
_
c
h
1
_
r
e
a
l
.
d
a
t
fc\ 10kdata\_wave.dat\ yout\_ch1\_real.dat
fc 10kdata_wave.dat yout_ch1_real.dat ,比较两个文件是否存在差异,若找不到差异,则两个文件的数据完全一致,即:
M
o
d
e
l
S
i
m
ModelSim
ModelSim 波形数据与仿真结果完全一样。
使用
g
n
u
p
l
o
t
gnuplot
gnuplot 工具直接将上述两个文件的波形绘制出来(此处时域为采样点序号)
对比的仿真数据集文件由
10
k
_
g
e
n
e
r
a
t
i
o
n
.
c
p
p
10k\_generation.cpp
10k_generation.cpp 生成,完全使用
M
o
d
e
l
S
i
m
ModelSim
ModelSim 仿真中的参数与算法。
10k_generation.cpp
#include<bits/stdc++.h>
#define pi 3.1415926
using namespace std;
int main(){
float t=0;
int f1=10000;
int f=100000;
float fs=400000;
float tmp;
freopen("10kdata_wave.dat","w",stdout);
for (int i=0; i<2048; i++){
t=i/fs;
tmp=sin(2*pi*f1*t);
tmp=tmp*256;
//正交变换
/* if (i%4==1 || i%4==3)
tmp=0;
if (i%4==2)
tmp=-tmp;*/
cout<<(int)tmp<<endl;
//cout<<t<<' '<<(int)tmp<<endl;
}
fclose(stdout);
return 0;
}
结论:
M
o
d
e
l
S
i
m
ModelSim
ModelSim 归一化波形与仿真波形完全一致(紫色线条为仿真生成数据,绿色线条为
M
o
d
e
l
S
i
m
ModelSim
ModelSim 生成数据),完全重合,证明了实验结果的正确性。
综合验证
结论:上述仿真代码可以综合,且逻辑门资源利用合理,可下载到开发板上。
工程文件
链接:https://pan.baidu.com/s/1iqAAlNRZaJsezku20_OjKQ?pwd=0x7f
提取码:0x7f