基于FPGA的ASK信号生成及测量分析技术-西电通院随机信号实验
基于FPGA的ASK信号生成及测量分析技术 ) 随机信号实验选到了这个,就当学 Verilog
了。
(以下内容围绕西电通院随机信号实验:《基于FPGA的ASK信号生成及测量分析技术》展开。
模块框图 2ASK调制电路组成框图
2ASK调制的FPGA程序框图
模块分解 梳理一下:
分频模块
载波产生模块
m序列产生模块
键控开关
DAC输出模块(给了)
拓展输出口(给了)
所以需要编写5个模块。
1. 分频模块设计
2.1.1按原理2.1节设计分频器a(4分频、6分频、10分频等)将系统时钟sys_clk分频,作为载波产生模块的时钟,则载波频率为sys_clk/(分频值a*一个载波周期的存储点数n)(Hz)。(sys_clk=26MHz)
2.1.2按原理2.1节设计分频器b(分频值应设置为上步中a*n的整数倍)将系统时钟分频,作为m序列产生模块的时钟,则基带码元速率为sys_clk/b(bit/s)。
可以知道要设计两个分频模块,其中主频为 26MHz 。
分频模块端口示意图:
clk端为系统时钟信号输入,从out8、out16、out256可分别得到系统时钟的8分频、16分频和256分频信号。
经历了一晚上的折磨,我终于悟出了:所谓X分频,就是把时钟原来每一下变一次,变成现在是每X下变一次。 (有时候很明显的事情就是转不过来弯
所以就可以顺着这个思路编写 fenpin.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 module fenpin ( input wire clk, output reg out8, output reg out16, output reg out256 ); reg [7 :0 ] counter_256;reg [3 :0 ] counter_16;reg [2 :0 ] counter_8;initial begin counter_256 = 8'b0 ; counter_16 = 4'b0 ; counter_8 = 3'b0 ; out8 = 1'b0 ; out16 = 1'b0 ; out256 = 1'b0 ; end always @(posedge clk) begin if (counter_8 < 8 /2 -1 ) begin counter_8 <= counter_8 + 1'b1 ; out8 <= out8; end else begin counter_8 <= 3'b0 ; out8 <= ~out8; end end always @(posedge clk) begin if (counter_16 < 16 /2 -1 ) begin counter_16 <= counter_16 + 1'b1 ; out16 <= out16; end else begin counter_16 <= 4'b0 ; out16 <= ~out16; end end always @(posedge clk) begin if (counter_256 < 256 /2 -1 ) begin counter_256 <= counter_256 + 1'b1 ; out256 <= out256; end else begin counter_256 <= 8'b0 ; out256 <= ~out256; end end endmodule
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 `timescale 1ns/1ns module tb_fenpin();reg clk;wire out8;wire out16;wire out256;initial begin clk = 1'b0 ; end always #10 clk = ~clk;fenpin fenpin_inst ( .clk (clk), .out8 (out8), .out16 (out16), .out256 (out256) ); endmodule
仿真波形:
2. 载波产生模块 载波产生模块示意图:
载波产生模块示意图如上图所示,其中clock为载波采样时钟,q[2:0]为计数器输出,q[7:0]输出为载波信号。载波产生模块由一个计数器和一个ROM构成,其中ROM中存储着一个载波周期的样点值,则计数器的进制设置为一个载波周期包含的样点数。本实验中一个载波周期取八个样点,计数器设置为八进制计数器,ROM和计数器均可使用IP核实现。
结合示意图可以知道,载波产生模块又分为两部分:八进制计数器 和ROM 。分开来写。
1) 八进制计数器 q 的值每个时钟加1,从0加到7。
lpm_counter0.v
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 module lpm_counter0 ( input wire clk, output reg [2 :0 ] q ); initial begin q = 3'b0 ; end always @(posedge clk) begin if (q == 3'b111 ) begin q <= 3'b0 ; end else begin q <= q + 1'b1 ; end end endmodule
testbench
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 `timescale 1ns/1ns module tb_lpm_counter0();reg clk;wire [2 :0 ] q;initial begin clk = 1'b0 ; end always #10 clk = ~clk;lpm_counter0 lpm_counter0_inst ( .clk (clk), .q (q) ); endmodule
仿真波形
2)ROM 使用 IP核 ,照着野火的教程学了学。
46-第二十六讲-ROM-IP核的调用(一)_哔哩哔哩_bilibili
经过大概分析,可以知道本实验使用 单端口ROM ,数据为 8位宽 ,地址为 3位宽 ,至少包含 8个 数据(采样了8个点),使用单时钟。
输出添加一个寄存器会延后两个周期输出。(原来延后一个,经过寄存器再延后一个。)
写操作是时钟的上升沿,读也是时钟的上升沿。
产生 mif 文件 matlab生成.mif文件 产生正弦信号数据_橘子FPGA的博客-CSDN博客_matlab生成正弦信号
本实验中一个载波周期取八个样点。
本次使用 python
进行生成(就8个点手写也行。
根据 .mif
文件的格式一句一句打印出来。
一个普通余弦信号周期为 $2\pi$ ,取样8个点,就是 $cos(2{\pi}\times\frac{x}{8})$ ;
数据位宽为8位,所表示的数据在0~255之间,所以需要将 $cos(2{\pi}\times\frac{x-1}{8})$ 的幅值**-1~+1变化到0~255**。
具体做法是将 $cos(2{\pi}\times\frac{x}{8})\times128+128$ 。就是将原幅值变换至-128~+128,然后加上128,范围变为0~256。
python
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import mathpi = math.pi filename = "sin_8x8.mif" with open (filename,"w+" ,encoding="utf-8" ) as file_object: file_object.write("WIDTH=8;\n" ) file_object.write("DEEPTH=8;\n" ) file_object.write("ADDRESS_RADIX=UNS;\n" ) file_object.write("DATA_RADIX=UNS;\n" ) file_object.write("CONTENT BEGIN\n" ) for i in range (0 ,8 ): x = (int )(math.cos(2 *pi*i/8 )*128 +128 ) if x == 256 : x = x-1 file_object.write(f"{i} :{x} \n" ) file_object.write("END;\n" )
效果(如果发现quartus报错就生成一个标准的mif文件然后把下面的复制进去替换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 WIDTH=8; DEEPTH=8; ADDRESS_RADIX=UNS; DATA_RADIX=UNS; CONTENT BEGIN 0:255 1:218 2:128 3:37 4:0 5:37 6:127 7:218 END;
生成IP核 注意深度选择的时候下拉没有8深度的选择,但是可以手动输入。
)不过我在其他文件调用ip核的时候一直仿真失败emm,所以ip核就直接用了,等我找到问题再看看。
编写代码及仿真 添加IP核,进行仿真。
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 `timescale 1ns/1ns module tb_lpm_rom();reg clk;reg [2 :0 ] address2;wire [7 :0 ] q;initial begin clk = 1'b1 ; address2 = 3'b000 ; end always #10 clk = ~clk;always #20 begin if (address2 == 3'b111 ) begin address2 <= 3'b000 ; end else begin address2 <= address2 + 1'b1 ; end end cos_8x8 cos_8x8_inst ( .address ( address2 ), .clock ( clk ), .q ( q ) ); endmodule
仿真波形
3.m序列产生模块
m序列产生原理及其性质_Angelo_pj的博客-CSDN博客_m序列
【verilog杂谈(一)】 2-16位长度的m序列发生器 - 知乎 (zhihu.com)
要求:按原理2.3节设计m序列产生模块,要求产生不同长度的m序列。
代码根据实验要求修改自参考链接,可以产生2~16位的m序列。
mxulie.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 module mxulie#( parameter len = 4 ) ( input wire clk, output wire m_sequence ); reg [(len-1 ):0 ] Q_r; assign m_sequence = Q_r[(len-1 )]; initial begin Q_r <= ~(0 ); end always @(posedge clk) begin Q_r <= Q_r<<1 ; case (len) 2 : Q_r[0 ] <= Q_r[1 ]^Q_r[0 ]; 3 : Q_r[0 ] <= Q_r[2 ]^Q_r[1 ]; 4 : Q_r[0 ] <= Q_r[3 ]^Q_r[2 ]; 5 : Q_r[0 ] <= Q_r[4 ]^Q_r[2 ]; 6 : Q_r[0 ] <= Q_r[5 ]^Q_r[4 ]; 7 : Q_r[0 ] <= Q_r[6 ]^Q_r[3 ]; 8 : Q_r[0 ] <= Q_r[7 ]^Q_r[5 ]^Q_r[4 ]^Q_r[3 ]; 9 : Q_r[0 ] <= Q_r[8 ]^Q_r[4 ]; 10 : Q_r[0 ] <= Q_r[9 ]^Q_r[6 ]; 11 : Q_r[0 ] <= Q_r[10 ]^Q_r[8 ]; 12 : Q_r[0 ] <= Q_r[11 ]^Q_r[10 ]^Q_r[7 ]^Q_r[5 ]; 13 : Q_r[0 ] <= Q_r[12 ]^Q_r[11 ]^Q_r[9 ]^Q_r[8 ]; 14 : Q_r[0 ] <= Q_r[13 ]^Q_r[12 ]^Q_r[7 ]^Q_r[3 ]; 15 : Q_r[0 ] <= Q_r[14 ]^Q_r[13 ]; 16 : Q_r[0 ] <= Q_r[15 ]^Q_r[14 ]^Q_r[12 ]^Q_r[3 ]; default : Q_r[0 ] <= Q_r[0 ]; endcase end endmodule
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 `timescale 1ns / 1ns module tb_mxulie; parameter M_len = 5 ;reg clk = 0 ;wire m_sequence ;always #10 clk=~clk;mxulie #( .len ( M_len )) mxulie_inst ( .clk (clk), .m_sequence (m_sequence) ); endmodule
仿真波形
4.键控开关
键控开关示意图图上图所示,其中data[7:0]端输入载波信号,gate端输入基带码元,当gate信号为1时,载波信号通过,如果gate信号为0时,载波信号不能通过。模块的输出端q[7:0]输出2ASK已调信号。
lpm_gate.v
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 module lpm_gate( input wire gate, input wire [7 :0 ] data, output reg [7 :0 ] q ); initial begin q <= 8'b0 ; end always @(*) begin if (gate == 1'b1 ) begin q <= data; end else begin q <= 8'b01111111 ; end end endmodule
整合以及整体仿真 将以上各个模块添加进实验给定的模板,并生成原理图,然后连线。
要整体仿真,所以要先把原理图转换为 Verilog
(File -> Create / Update -> Create HDL Design File from Current File…),之后从工程文件移除原理图,设置生成的 verilog
文件为顶层文件进行编译,然后仿真。
通过对比编译出来的 RTL 视图,符合给定框图。
整体仿真,只要注意 clk
, m_squence
, two_ask[7...0]
这几个信号就行。 testbench
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 `timescale 1ns/1ns module tb_sim;reg clk;wire m_squence;wire [7 :0 ] two_ask;initial begin clk = 1'b0 ; end always #10 clk = ~clk;twoask twoask_inst( .clk (clk), .m_squence (m_squence), .two_ask (two_ask) ); endmodule
仿真波形:
管脚分配 管脚分配啥的给的模板文件里已经分配好了,直接用就行。
总结 毕竟是主要是记录 verilog
的一个学习过程,剩下的就不分析了,属于是实验报告里的活。这算是第一次用 verilog
干这种比较综合的活?接触到了一些新玩意,IP核,mif文件之类的。也锻炼了从s一样实验指导手册里提炼信息的能力?
(然后细节和要补充的等后面想起来再说
(我超突然想起来线下验收完忘了拍时域波形