verilog数字信号处理---实践1-混频器

目录

1. 实现功能

2. matlab 代码

3. quartus 代码

1)用NCO IP核生成本振信号

2)混频后的信号处理

3)代码分析

4. 激励信号仿真

 1) 从.txt文件中打开din输入信号:

 2) 将nco产生的本振信号及混频后的信号输出到.txt文件中:

 3) 文件读取代码分析

5.  有NCO IP核的modelsim仿真操作及结果

 1)仿真出现错误现象 

 2)正确操作

 3)仿真结果


1. 实现功能

   verilog数字信号处理---实践1-混频器_第1张图片2. matlab 代码

        1)生成sin输入信号,并生成.coe/.txt文件供FPGA读取;

        2)进行上图算法的仿真,生成sin信号,混频、去除直流分量,进行FFT变换,并输出波形;

3. quartus 代码

        1)用NCO IP核生成本振信号

verilog数字信号处理---实践1-混频器_第2张图片

      使用过程会发现两个问题,一是会卡在generation环节,进度不动,二是报error。这个IP核并不是免费的IP核,需要破解。以上两个问题可以参考下面的博客解决:

        (5条消息) Quartus II 13.1 调用NCO IP核无法生成终于搞定了_Mbluer的博客-CSDN博客

参考上面链接重新安装两个java包后,无法启动java程序,并且nco IP核没办法创建。显示如下错误: 查百度,应该是java版本更新后的问题.  重复上面的操作,在安装第二个java64时有提醒检测到旧的java版本,忽略掉。再重新尝试,可以成功打开nco IP核设置。

verilog数字信号处理---实践1-混频器_第3张图片        

     

    IP核破解可以参考这篇博文:(7条消息) QUARTUS II中IP核的调用方法之ip核破解_freedomff的博客-CSDN博客

NCO的输出波形公式如下:

verilog数字信号处理---实践1-混频器_第4张图片

 PIN定义如下:

2)混频后的信号处理

        求均值,滤除直流分量。通过对一个周期波形的8个数据进行求和然后右移3位得到均值,然后用混频后的数据减去均值(直流分量),得到输出结果。

3)代码分析

module Mixer(
	input clk,rst_n,
	input [9:0]din,     //输入单频信号
	output wire [9:0] s_oc,  // 本振信号
	output wire [19:0] dout //混频信号
	//output ldoc,        //本振信号分频后的显示信号
	//output ldmix        //混频信号分频后的显示信号
	);      
	
wire [15:0]	phi_inc_i;
wire clken;
wire [9:0]oc_sin;
assign clken=1'b1;
assign phi_inc_i=16'd8192;  //设置频率为625khz

oc oc(
	.phi_inc_i(phi_inc_i),
	.clk(clk),       //高电平有效的时钟使能信号
	.reset_n(rst_n),  //异步复位,低电平有效
	.clken(clken),    //时钟使能信号,高电平有效
	.fsin_o(oc_sin),  //输出正弦信号
	.out_valid(out_valid));	
reg signed [19:0] mult;   // 转换成有符号数,因为有正负
wire signed[9:0] s_din;
wire signed[9:0]s_oc_sin;
assign s_oc = oc_sin;
assign s_din=din;
assign s_oc_sin=oc_sin;
always @(posedge clk or negedge rst_n)
	if (~rst_n) mult<=20'd0;
	else mult<=s_din*s_oc_sin;	
//求均值 取8个值求平均是因为一个周期的波形是8个数值
reg  signed [19:0]m1,m2,m3,m4,m5,m6,m7;
always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		m1<=20'd0;
		m2<=20'd0;
		m3<=20'd0;
		m4<=20'd0;
		m5<=20'd0;
		m6<=20'd0;
		m7<=20'd0;
		end
	else  begin
		m1<=mult;
		m2<=m1;
		m3<=m2;
		m4<=m3;
		m5<=m4;
		m6<=m5;
		m7<=m6;
		end
	end
wire signed [22:0] madd;
wire signed [19:0] mean,mt;
assign madd=mult+m1+m2+m3+m4+m5+m6+m7;
assign mean=madd[22:3];
//滤除直流分量
assign mt=mult-madd;
assign dout=mt;
endmodule

4. 激励信号仿真

 1) 从.txt文件中打开din输入信号:

//从外部TX文件(SinIn.txt)读入数据作为激励
integer pattern,file;
reg [9:0]stimulus[1:data_num];
initial begin
	//文件仿真在“工程目录\simulation\modelsim”路径下
	file=$fopen("SinIn.txt","r");
	//$fscanf(file,"%d",stimulus);   //不能这么写
	pattern=0;
	repeat(data_num)
		begin
		$fscanf(file,"%d",stimulus[pattern]);  //需要一个个读,如下方的写一样,不能直接将所有的数据一次性读到stimulus中。
			din=stimulus[pattern];
			pattern=pattern+1;
			#(`clk_period);
		end
	$fclose(file);
	end

 2) 将nco产生的本振信号及混频后的信号输出到.txt文件中:

//将仿真数据dout写入到外部txt文件中(out.txt)
 integer file_out;
 initial begin
	file_out=$fopen("out.txt","w");
	if(!file_out)begin
		$display("could not open file!");
		$finish;
		end
	end
 
 wire rst_write;
 wire signed[19:0] dout_s;
 assign dout_s=dout;
 assign rst_write=clk&(rst_n); //产生写入时钟信号,复位状态时不写入数据
 
 always@(posedge rst_write)  $fdisplay(file_out,"%d",dout_s);

//将仿真数据s_oc写入到外部txt文件中(oc.txt)
 integer file_oc;
 initial begin
	file_oc=$fopen("oc.txt");
	if(!file_oc)begin
		$display("could not open file!");
		$finish;
		end
	end
 
 wire signed[9:0] oc_s;
 assign oc_s=s_oc;

 always@(posedge rst_write)  $fdisplay(file_oc,"%d",oc_s);

 3) 文件读取代码分析

        本次代码中,联调matlab和quartus的txt文件中用的都是有符号的十进制数,因此在matlab中生成的sin和处理的数据均是十进制的,因此直接用十进制输出,而在quartus中,用$readmem函数只能直接读取二进制和十六进制的数,因此用了$fscanf函数读取十进制,同时在实践中尝试发现只能按照指针一个个数据读取保存到数组中,不能像$readmemb一次性读取。

$readmemb("文件名",存储单元,文件中要存入存储单元的起始地址,文件中要存入存储单元的终了地址):对于$readmemb系统任务,每个数字必须使二进制数字,对于$readmemh系统任务,每个数字必须是十六进制数。$readmem只能读取二进制和十六进制。

$fscanf(文件指针,读取格式,数组);返回值为1表示成功,读取格式可以为%b,%d,%h(注意,如果读取格式为二进制,则文件中只能识别0 1.)

同时,$fscanf(file,"%d",stimulus);必须一次写一个数,不能这样直接一次性将文件的数据全部写入stimulus中。

 verilog中$readmemb和$readmemh的使用_m0_38037810的博客-CSDN博客

 verilog读入.txt的有符号十进制数,把有符号十进制数写入到.txt文件中_芒果爱火锅的博客-CSDN博客

5.  有NCO IP核的modelsim仿真操作及结果

 1)仿真出现错误现象 

仿真时出现找不大IP核调用的模块文件,显示如下错误:

# ** Error: E:/yanghaizhu/FPGA_PRO/11_mixer/quartus/ip/oc_st.v(135): Module 'asj_dxx' is not defined.
# ** Error: (vopt-7) Failed to open info file "work/_info" in read mode.
# No such file or directory. (errno = ENOENT)
# ** Error: E:/yanghaizhu/FPGA_PRO/11_mixer/quartus/ip/oc_st.v(142): Module 'asj_nco_aprid_dxx' is not defined.
# ** Error: (vopt-7) Failed to open info file "work/_info" in read mode.

主要原因时缺少库,解决方法参考下面博客:

MODELSIM仿真 QUARTUS生成的NCO IP核出现找不到asj等库文件问题的解决方法_little_pants的博客-CSDN博客

 2)正确操作

        首先,quartus下 tool-run trl simulation,打开modelsim-SE;

        然后,library-->work--> 点击oc.v,再点击上面的菜单栏compile,选择oc.vo文件编译。然后接着点击simluate-->start simluate-->在design下,选择work里的tb文件,如下图所示,design unit会出现work.xx名称,

verilog数字信号处理---实践1-混频器_第5张图片

  然后点击library-->add,将本项目modelsim--simulation下的verilog_libs下的6个库依次添加进去,然后点击OK。verilog数字信号处理---实践1-混频器_第6张图片

 编译没有错误,如下图:

verilog数字信号处理---实践1-混频器_第7张图片

 此时需要手动将想要观察的信号添加进波形,然后run-all,结果如下:

 3)仿真结果

verilog数字信号处理---实践1-混频器_第8张图片

问题1:有符号数相乘的结果不对,即得到的dout输出不对。

分析波形如下:mult<=din*s_oc,但是乘积项不是同一时刻的值,有错位。但是乘法运算本身是正确的。 猜测原因是时钟比较大,在时钟锁存数据时就已经完成了assign赋值加非阻塞赋值。

问题2:往.txt中写入的dout数据不对,而写入的oc_s是对的。

这里需要注意,.txt每次运行后都会重新更新,而打开的.txt需要关闭再重新打开,不会再打开的状态下更新。

参考:Verilog学习笔记——有符号数的乘法和加法_DengFengLai123的博客-CSDN博客

你可能感兴趣的:(数字信号处理,quartus,verilog)