AM解调 FPGA(寻找复刻电赛电赛D题的)

设计平台

  • Quartus II10.3
  • mif产生工具
  • modelsimSE (仿真用)

DDS(直接数字式频率合成器)

从前面的内容可知,我们需要产生一个载波,并且在仿真时,我们还需要一个较低频率的正弦波信号来充当我们的调制信号,但FPGA要怎么去生成一个正弦波呢?答案是利用DDS。

  • DDS是什么呢?

Direct Digital Synthesis,直接数字频率合成器。以这个项目为例,简单来说,就是可以利用FPGA上的资源,来输入一定的频率控制字(控制具体产生频率),从而来生成一定的正弦/余弦波,有些也可以输出相位。

在Quartus II 中 有自带的DDS生成工具(NCO),在IP核向导 MegaWizard Plug-In Manager中可以找到该IP核,如图:

quartus II 中的DDS 内核 (NCO)

但在本次设计中,我们采用写ROM的形式来实现我们的DDS。

  • 如何来写ROM?
    在quartus的ROM设置向导中(只需在上面的图片中搜索rom,选择1port的即可),有以下设置:

ROM_PIC1

ROM_PIC2

其中,

1:

是设置ROM的位宽,以及深度,在这里我们选择8bits的位宽,256bits的深度,这个选项要和待会给ROM初始化文件mif的数据的宽度,深度有关,要和mif文件的内容相匹配

2:

选择block memory的类型。在这里我们不对这些类型进行讨论,选择自动。

3:

选择给输入,输出的时钟是同一个,还是两个不同的时钟。我们选择一个时钟Single clock。

4:

位于ROM Wizard界面的第三页中的Men Init中,这是是添加一个ROM初始化文件xx.mif,以便来初始化ROM中的数据内容。mif文件也有一定的格式,一般可以通过MATLAB,或者其他mif格式生成工具来生成(网上已经有很多关于mif文件生成的代码或者软件,请自行寻找,此处不作介绍)。

mif文件的格式以及部分解释如下:

DEPTH = 256;		//深度
WIDTH = 8;		//宽度
ADDRESS_RADIX = HEX;  //地址输入的数据格式
DATA_RADIX = HEX;        //数据的数据格式
CONTENT 		//初始化开始 格式为: [address] : [ data ];
	BEGIN
0000 : 0080;
0001 : 008C;
0002 : 0098;
0003 : 00A5;
0004 : 00B0;
0005 : 00BC;
...
...
...
...
[final address] : 0073;
END ;		//初始化结束,深度多少,就有多少行初始化描述

完成以上4步之后就可以点击Next按钮,在EDA界面勾选上Generate netlist选项,继续next:

ROM_PIC3

我们可以在这个界面来选择我们最后生成的文件类型,这里我选择勾选inist的文件,方便我们调用,完成后即可点击finish完成ROMIP核的设置

ROM_PIC4

接着在界面左边的Files中就可以找到我们生成的ROM了:

Files中的ROM文件

点开rom.v文件,复制模块开头的端口描述语句,就可以进行调用啦:

module sigan_rom (
	address,
	clock,
	q);

//例化模板:
sigan_rom  yourRomName(
	.address(),//[7:0]
	.clock(),
	.q()//[7:0]
	);

以上,ROM的配置介绍就到此结束。


调制信号发生模块

新建一个项目,选择好自己的器件之后,新建一个Verilog文件,取名为:modelsim_signal_generator(大家可以自己随意取名,知道是什么模块就行)

开写!那我们需要什么内容呢?

我们现在想要得到的是一个比较低频的正弦波调制信号,那我们应该怎么去规定这个频率呢?

回到DDS上,DDS生成一个波形靠的是地址按一定步进变化之后,输出的数据流组成的。我们刚才创建的ROM核也有一个addr的地址输入端,于是,我们自然而然地可以想到**"输入一定步进的地址,输出一定频率的波形"** 。我们把这个一定步进 叫做频率控制字(也有叫相位控制字)。具体的频率控制字(PINC)公式如下,在此不展开详解:

����=�������∗����2����ℎ+2

其中,Fsystem为系统时钟,Fout为DDS输出的正弦波频率,PINC为频率控制字,depth为ROM的深度。

具体代码实现如下:

//module name : modelsim_signal_generator
//module function:generate a signal for simulation
//author: wataru
//2021.11.16
module modelsim_signal_generator(
	input clk,
	input rst,
	output [7:0] signal

);

parameter PINC = 8'd3;//freqCtrlWord;fsys = 50MHZ,fout ~=146_484.375Hz 

reg[7:0] cnt;	//	pinc_cnt,counter
wire[7:0] addr;//Rom address_in 
//addr cnt
always@(posedge clk,posedge rst)begin
	if(rst)begin
		cnt <= 8'd0;
	end// if
	else begin
		cnt <= cnt + PINC; //addr ++
	end//else
end

assign addr = cnt;
//---------------------------
//rom 
sigan_rom  siganRom(
	.address(addr),//[7:0]
	.clock(clk),
	.q(signal)//[7:0]
	);
endmodule//modelsim_signal_generator

同理,我们也可以利用此代码产生一个载波:

//module name :carrier_generator
//module function : carrier generate
//author:wataru
//2021.11.16
module carrier_generator(
	input clk,
	input rst,
	output[7:0] carrier
);


parameter PINC_cARRIER = 8'd42;//freqCtrlWord

reg[7:0] cntCarrier;	//	pinc_cnt
wire[7:0] addrCarrier;//Rom address_in

//addr cnt
always@(posedge clk,posedge rst)begin
	if(rst)begin
		cntCarrier <= 8'd0;
	end// if
	else begin
		cntCarrier <= cntCarrier + PINC_cARRIER;
	end//else
end

assign addrCarrier = cntCarrier;
//-----------------------------
//rom 
sigan_rom  carrierRom(
	.address(addrCarrier),//[7:0]
	.clock(clk),
	.q(carrier)//[7:0]
	);
	
endmodule

至此,我们编写的模拟的调制波,载波已经描述完毕。


AM调制

AM调制可以通过一个乘法器来实现,非常简单。调用乘法器IP核:

LPM_MULT

乘法器模块部分设置,输出为Signed,即有符号数

数据位宽

文件输出选项

完成MULT乘法器创建之后,我们就可以把模块链接起来,创建一个AM调制波啦,顶层代码如下:

//module :top
//module function:only used for simulation
//author:wataru
//2021.11.16
module ammodtest(
						input 		 clk,
						input 		 rst,
						output[7:0] INsignal,
						output[7:0] carrier,
						output[15:0] ammodOut
);

wire[7:0] INsignal_temp;
wire[7:0] carrier_temp;
wire[15:0] ammodOut_temp;
//signal_in
modelsim_signal_generator signalIn(
	.clk(clk),
	.rst(rst),
	.signal(INsignal_temp)//unsigned

);
assign INsignal = INsignal_temp - 8'd127;//因为我的mif文件是无符号数的,要去掉一定的直流分量。
//carrier
carrier_generator carrierGenerator(
	.clk(clk),
	.rst(rst),
	.carrier(carrier_temp)//unsigned
);
assign carrier = carrier_temp - 8'd127;//因为我的mif文件是无符号数的,要去掉一定的直流分量。
//ammod
mult multmod(
	.dataa(INsignal),
	.datab(carrier),
	.result(ammodOut_temp)//[15:0] signed
	);
assign ammodOut = ammodOut_temp ;
endmodule//endmodule

至此,我们的全部模块已经编写完毕,可以编写一个tb文件来进行仿真啦!


仿真

我们这里选择使用quartus II 与modelsim进行联合仿真。这里使用quartus生成tb.vt文件。该文件为testbench文件,操作如下:

操作界面按钮

生成之后,就可以在项目的所在文件夹里面找到一个simulation的文件夹,可以在里面找到一个测试文件。通常该文件名称为[projectName.vt],如图:

文件地址

之后,选择我们的仿真工具,modelsim。(此处请参考网上关于quartus与modelsim联合仿真的教程),选择我们的testbench文件,操作如下:

步骤1

图中的是我的tb文件,请无视,请点击new

简单说一下这个界面:

1、是你的仿真文件的名字;

2、是该仿真文件中你的模块的名字;

3、是仿真文件里面调用的模块的名字,通常默认为i1,

以上3个内容均可以在生成的[projectName.vt]中找到。

设置完毕之后,打开[projectName.vt]进行编写我们所需要的仿真设置,我的改动如下:

//module function :simulation
`timescale 1 ps/ 1 ps //仿真时间,不做处理
module ammodtest_vlg_tst();

//reg eachvec; //有时钟仿真时,请注释掉这一行

reg clk;
reg rst;
// wires                                               
wire [7:0]  INsignal;
wire [15:0]  ammodOut;
wire [7:0]  carrier;
                         
ammodtest i1 (
  
	.INsignal(INsignal),
	.ammodOut(ammodOut),
	.carrier(carrier),
	.clk(clk),
	.rst(rst)
);
initial                                                
begin                                                  
                       
rst = 1;
clk =  0;
#5 rst = 0;                                                      
// --> end                                             
$display("Running testbench");                       
end                                                    
always                                                 
              
begin                                                  
                        
     #10 clk = ~clk;                                                  
//@eachvec;   //有时钟仿真时,请注释掉这一行                                           
                                          
end                                                    
endmodule

完成后,就可以对项目进行综合,综合完毕后请启动仿真:

仿真按钮

之后我们就可以在打开的modelsim的wave窗口中查看我们的波形了!效果如图:

第三个信号为调制信号,第四个信号为已调波,第五个为载波信号

到这里,我们的仿真完毕。


尾言

到这里,我们的AM调制部分就编写完毕,在下板应用时,请把模拟的调制波输入,换成自己的信号输入端,进行管脚绑定之后就可以正常使用了!

相关推荐
乌恩大侠1 小时前
O-RAN Fronthual CU/Sync/Mgmt 平面和协议栈
5g·平面·fpga开发·架构
DS小龙哥15 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师1 天前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战1 天前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩2 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技2 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪2 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望2 天前
EDA技术简介
fpga开发
淘晶驰AK2 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发