赞
踩
从前面的内容可知,我们需要产生一个载波,并且在仿真时,我们还需要一个较低频率的正弦波信号来充当我们的调制信号,但FPGA要怎么去生成一个正弦波呢?答案是利用DDS。
Direct Digital Synthesis,直接数字频率合成器。以这个项目为例,简单来说,就是可以利用FPGA上的资源,来输入一定的频率控制字(控制具体产生频率),从而来生成一定的正弦/余弦波,有些也可以输出相位。
在Quartus II 中 有自带的DDS生成工具(NCO),在IP核向导 MegaWizard Plug-In Manager中可以找到该IP核,如图:
quartus II 中的DDS 内核 (NCO)
但在本次设计中,我们采用写ROM的形式来实现我们的DDS。
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调制可以通过一个乘法器来实现,非常简单。调用乘法器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调制部分就编写完毕,在下板应用时,请把模拟的调制波输入,换成自己的信号输入端,进行管脚绑定之后就可以正常使用了!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。