題目要求
老師給了我一道題,讓我用Verilog撰寫出來:通過100M時鐘產生3M、5M和20M正弦波,并將產生的三個不同頻率的正弦波加在一起,然后從這個和信號中將20M正弦波提取出來,
我的思路
首先通過DDS分別產生3M、5M、20M正弦波,通過加法器將這三個正弦波加在一起,再通過設計FIR數字濾波器將20M正弦波從和信號中濾出來,整個程序思路還是很清楚的,
DDS原理及代碼
關于DDS原理介紹的文章很多,csdn上就很多的文章,可以參考下,
DDS,即直接數字式頻率合成法,基本思想就是在存盤器中存入正弦波的N個均勻間隔樣值,然后以均勻速度將樣值傳輸到數模轉換器,將其轉換成模擬信號,
這一塊我感覺很多文章寫的不是很清楚,我直接給大家看看課本上關于DDS原理的介紹:


簡單的說就是在存盤器ROM中,每隔k個點取一個值,實作k倍頻,但由于要滿足Nquist定理,所以DDS輸出頻率小于等于時鐘頻率的二分之一,即:fo <= fc/2,
DDS輸出頻率:

M為頻率控制字,N為相位累加器的位寬,
DDS實作程序及代碼
首先我們需要生成ROM存盤單元,可以直接用Verilog撰寫,也可以在MATLAB中生成.coe檔案,通過Vivado呼叫ROM的IP核來實作(我更推薦第二種辦法),
MATLAB生成ROM存盤單元的.coe檔案(.coe檔案google便可明白),波形資料表ROM中存有一個完整周期的正弦波信號,使波形檔案ROM的地址為12位,存盤資料位寬11位,將一個周期的正弦波,沿橫軸等間隔采樣212=4096次,每次采集的信號幅度用11位資料表示,最大值為2048,最小值為0,將采集的4096個資料按順序存入ROM中,然后以相位累加器的輸出為地址,進行訪問,
MATLAB生成ROM存盤單元的.coe檔案:
clear;clc;
depth = 4096; %4096個存盤空間
width = 11; %存盤資料位寬
t = 0:depth-1;
ADC = 2^11 - 1; %直流分量
A = 2^11; %幅度
s = A * sin(2*pi*t/(depth-1)) + ADC;
plot(t,s);
fid = fopen('sine.coe','wt');
fprintf(fid,'MEMORY_INITIALIZATION_RADIX=10;\n');
fprintf(fid,'MEMORY_INITIALIZATION_VECTOR=\n');
for i = 1:depth-1
fprintf(fid,'%d,\n',ceil(s(i)));
end
fprintf(fid,'%d;\n',ceil(s(depth)));
fclose(fid);
在Vivado中呼叫ROM的IP核
點擊IP Catalog

在search中搜索rom,選擇Block Memory Generator

點擊Port A Option進行修改,主要修改Width和Depth的修改,與之前matlab中保持一致,

點擊Other Option

之后一路OK便可,IP核設定完成之后,我們需要在模塊中呼叫,
我們先來寫3M正弦波的產生檔案,
程式中ROM IP核的呼叫方法:點擊IP Source,找到,veo檔案,將框中的代碼復制進DDS_3M檔案中,實作IP核呼叫,

module DDS_3M(
input clk,
input reset,
output [11:0] data
);
parameter fre_word = 32'd128849018; //頻率控制字 fre_word = f_out * 2^N / fclk N為累加器位寬
reg [31:0] addr_sin;
//相位累加器
always @(posedge clk or reset)
begin
if(reset == 0)
addr_sin <= 32'b0;
else
addr_sin <= addr_sin + fre_word;
end
wire [11:0]addra = addr_sin[31:20];
//ROM IP核的呼叫
sin_rom your_instance_name (
.clka(clk), // input wire clk 時鐘
.addra(addra), // input wire [11 : 0] addra 相位累加器輸入給rom的地址
.douta(data) // output wire [11 : 0] douta 從ROM回傳的資料(3M正弦波的采樣點)
);
endmodule
5M、20M正弦波的產生方法與3M完全類似,只是需要改變相位控制字即可,此處不再呈現,
下面的代碼為了讓后續代碼撰寫起來更簡潔
module DDS(
input clk,
input reset,
output [11:0] data_3M,
output [11:0] data_5M,
output [11:0] data_20M
);
DDS_3M DDS_3M_inst(
.clk(clk),
.reset(reset),
.data(data_3M)
);
DDS_5M DDS_5M_inst(
.clk(clk),
.reset(reset),
.data(data_5M)
);
DDS_20M DDS_20M_inst(
.clk(clk),
.reset(reset),
.data(data_20M)
);
endmodule
至此,DDS產生正弦波成功,
正弦波求和
此處非常簡單,我直接上代碼:
需要注意的就是求和之后,和的位數可能會超過之前單個正弦波樣值的12位,所以需要拓寬正弦波和的位寬,
module wave_summation(
input clk,
input reset,
input [11:0] wave_3M,
input [11:0] wave_5M,
input [11:0] wave_20M,
output reg [13:0] wave_sum
);
DDS wave(
.clk(clk),
.reset(reset),
.data_3M(wave_3M),
.data_5M(wave_5M),
.data_20M(wave_20M)
);
always @(posedge clk or reset)
if(reset == 0)
wave_sum = 14'b0;
else
wave_sum = wave_3M + wave_5M + wave_20M;
endmodule
濾波器設計
濾波器的設計是我感覺比較難的一個地方,
先說說我自己遇到的問題:就是DDS模塊和求和模塊中,符號都是unsigned,而在濾波器設計模塊,我按照一篇文章將符號改為signed,導致仿真一直出錯,
濾波器的設計及呼叫我推薦大家這篇文章:https://blog.csdn.net/follow_moon/article/details/80376118
只是其中需要注意的是,在保存.coe檔案之前需要將hn的系數改為無符號數:

注:其他操作與我推薦的博客中的操作來便可,
fir IP核呼叫推薦文章:
https://blog.csdn.net/judas1801/article/details/108506433
這篇文章非常使用,照著設定IP核,便可成功,之后的呼叫方式與ROM的呼叫方式完全一樣,
https://blog.csdn.net/keilzc/article/details/104249702
這篇文章對于fir IP核呼叫的理解有很大幫助,
下面是fir濾波器的代碼:
module bondpass_filter(
input clk,
input [13:0] wave_in,
output [39:0] wave_out,
output m_tvalid,
output s_tready
);
wire [15:0] s_tdata;
assign s_tdata = {2'b0,wave_in}; //s_tdata為16bit位寬,輸入信號wave_in為14bit位寬,通過拼接運算填補高位
fir_compiler_0 your_instance_name (
.aclk(clk),
.s_axis_data_tvalid(1'b1),
.s_axis_data_tready(s_tready), //準備好信號,置1表示FIR可以接收資料輸入
.s_axis_data_tdata(s_tdata), // input wire [15 : 0] s_tdata
.m_axis_data_tvalid(m_tvalid), //FIR輸出資料有效信號,置1表示輸出有效
.m_axis_data_tdata(wave_out) // output wire [39 : 0] m_tdata
);
endmodule
測驗檔案
`timescale 1ns / 1ps
module wave_summation(
input clk,
input reset,
input [11:0] wave_3M,
input [11:0] wave_5M,
input [11:0] wave_20M,
output reg [13:0] wave_sum
);
DDS wave(
.clk(clk),
.reset(reset),
.data_3M(wave_3M),
.data_5M(wave_5M),
.data_20M(wave_20M)
);
always @(posedge clk or reset)
if(reset == 0)
wave_sum = 14'b0;
else
wave_sum = wave_3M + wave_5M + wave_20M;
endmodule
加粗樣式
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280998.html
標籤:其他
上一篇:Matlab-simulink快速入門_3_創建簡單的模型
下一篇:八大排序之快速排序C++詳解
