基于FPGA的ASK信号生成及测量分析技术-西电通院随机信号实验
基于FPGA的ASK信号生成及测量分析技术
) 随机信号实验选到了这个,就当学 Verilog
了。
(以下内容围绕西电通院随机信号实验:《基于FPGA的ASK信号生成及测量分析技术》展开。
模块框图
2ASK调制电路组成框图

2ASK调制的FPGA程序框图

模块分解
梳理一下:
- 分频模块
- 载波产生模块
- 八进制计数器
- ROM
- 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
:
module fenpin (
input wire clk,
output reg out8,
output reg out16,
output reg out256
);
// 所谓X分频,就是原来时钟是每一下变一次
// 现在是每X下变一次
// lb(256) = 8
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
// 折中计数到一半就行
// 8分频
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
// 16分频
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
// 256分频
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
`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
:
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
`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核 ,照着野火的教程学了学。
经过大概分析,可以知道本实验使用 单端口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
代码:
import math
pi = 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文件然后把下面的复制进去替换:
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
`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序列产生模块
要求:按原理2.3节设计m序列产生模块,要求产生不同长度的m序列。
代码根据实验要求修改自参考链接,可以产生2~16位的m序列。
mxulie.v
module mxulie#(
parameter len = 4 // parameter range from 2 to 16
)
(
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; // shift reg
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
//~ `New testbench
`timescale 1ns / 1ns
module tb_mxulie;
// M_series Parameters
parameter M_len = 5;
// M_series Inputs
reg clk = 0 ;
// M_series Outputs
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
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; // +127将0变换过去
end
end
endmodule
整合以及整体仿真
将以上各个模块添加进实验给定的模板,并生成原理图,然后连线。
要整体仿真,所以要先把原理图转换为 Verilog
(File -> Create / Update -> Create HDL Design File from Current File…),之后从工程文件移除原理图,设置生成的 verilog
文件为顶层文件进行编译,然后仿真。
通过对比编译出来的 RTL 视图,符合给定框图。

整体仿真,只要注意 clk
, m_squence
, two_ask[7...0]
这几个信号就行。 testbench
如下:
`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一样实验指导手册里提炼信息的能力?
(然后细节和要补充的等后面想起来再说
(我超突然想起来线下验收完忘了拍时域波形
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!