实验通过编写一个DMA读模块获取FM调制的数据源,DMA模块的实现是基于AXI协议。因为在数据的传输中,Xilinx提供的官方DMA IP核在传输完一次突发数据后需要在PS 端重新启动一次都或者写操作,如此的话,在进行大量数据的传输工作时,尤其是对DDR 不同地址区域同时进行读写操作时,IP 核不能有效工作。所以通过创建一个模块用于读取DDR,无需PS端参与即可完成读DDR操作。
AXI_DMA_RD模块突发读时序:
该模块读取的内容是先由PS端提前写入DDR某一地址区间的音频数据,按照AXI突发读时序进行数据读取,同时需要添加FIFO IP核来完成数据位宽转换和跨时钟域处理。为了防止数据丢失,当判断FIFO写入数据计数器为0,才启动一次突发读DDR。
FIFO 读时序:
FM调制模块:
依据调频信号的数学表达式:
实现图中公式高亮部分,需要用到两个ROM IP用于存放正余弦波形,正余弦波形可由matlab获得,以及一个乘法器IP完成调制中的乘法操作。
c = 0:1/1024:1023/1024; %ROM 中有 1024 点
c_rom = fix(1024*sin(2*pi*c));
%c_rom = fix(1024*cos(2*pi*c));
重要参数:调频灵敏度Kf 和 最大频偏频率控制字 W
FM解调模块:
首先用两个寄存器将接收到的 I Q 两路数据进行缓存,再用两个寄存器进行延时一拍,就可以得到 i(i)与 i(i-1),q(i)与 q(i-1),再调用乘法器完成乘法运算,再将乘法器结果进行加减运算,再进行除法,即可得到解调结果。
文章来源:https://www.toymoban.com/news/detail-506661.html
module fm_demod(
input wire sclk,
input wire rst_n,
input wire adc_valid,
input wire [11:0] adc_data_i,
input wire [11:0] adc_data_q,
input wire [31:0] end_count,
output reg dmsout,
output reg [15:0] dmout
);
reg [11:0] i_fm=0,q_fm=0,i_fm_r=0,q_fm_r=0;
reg isin,isin_r;
wire [23:0] cr1,cr2,cj1,cj2;
reg [23:0] cr,cj;
reg flag1=0;
reg [23:0] cjabs=0;
reg [23:0] cj_r,cr_r;
reg flag2;
reg [23:0] dividend=0;
reg [23:0] divisor =1;
reg flag3;
wire div_valid;
wire [39:0] div_dout;
reg [23:0] cr_r3,cj_r3;
wire [23:0] cr_fifo_data,cj_fifo_data;
reg [39:0] angle=0;
reg div_valid_r;
wire [15:0] pi4;
wire [55:0] angle_r;
reg [23:0] cj_r4;
reg [23:0] cj_r5;
reg [55:0] angle_dout;
reg div_valid_rr;
reg angel_dsout;
reg [11:0] cnt_mean;
reg [49:0] dm_sum;
reg [11:0] i_data,q_data;
wire [11:0] i_data_16,q_data_16;
reg adc_valid_r;
// LPF
assign i_data_16 = {i_data[11],i_data[11],i_data[11],i_data[11],i_data[11:4]};
assign q_data_16 = {q_data[11],q_data[11],q_data[11],q_data[11],q_data[11:4]};
always @(posedge sclk) begin
if (rst_n == 1'b0) begin // reset
i_data <= 0;
q_data <= 0;
end
else if (adc_valid == 1'b1) begin
i_data <= {adc_data_i[11],adc_data_i[11],adc_data_i[11],adc_data_i[11],adc_data_i[11:4]} + i_data - i_data_16;
q_data <= {adc_data_q[11],adc_data_q[11],adc_data_q[11],adc_data_q[11],adc_data_q[11:4]} + q_data - q_data_16;
end
end
always @(posedge sclk) begin
adc_valid_r <= adc_valid;
end
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
{i_fm_r,i_fm} <= 'd0;
{q_fm_r,q_fm} <= 'd0;
end
else if(adc_valid_r == 1'b1) begin
{i_fm_r,i_fm} <= {i_fm,i_data};
{q_fm_r,q_fm} <= {q_fm,q_data};
end
end
always @(posedge sclk) begin
{isin_r,isin} <= {isin,adc_valid_r};
end
mult_crcj mult_cr1 (
.CLK(sclk), // input wire CLK
.A(i_fm), // input wire [11 : 0] A
.B(i_fm_r), // input wire [11 : 0] B
.P(cr1) // output wire [23 : 0] P
);
mult_crcj mult_cr2 (
.CLK(sclk), // input wire CLK
.A(q_fm), // input wire [11 : 0] A
.B(q_fm_r), // input wire [11 : 0] B
.P(cr2) // output wire [23 : 0] P
);
mult_crcj mult_cj1 (
.CLK(sclk), // input wire CLK
.A(q_fm), // input wire [11 : 0] A
.B(i_fm_r), // input wire [11 : 0] B
.P(cj1) // output wire [23 : 0] P
);
mult_crcj mult_cj2 (
.CLK(sclk), // input wire CLK
.A(i_fm), // input wire [11 : 0] A
.B(q_fm_r), // input wire [11 : 0] B
.P(cj2) // output wire [23 : 0] P
);
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
cr <= 'd0;
cj <= 'd0;
end
else begin
cr <= cr1+ cr2;
cj <= cj1-cj2;
end
end
always @(posedge sclk) begin
flag1 <= isin_r;
end
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
cjabs <= 'd0;
end
else if (flag1 == 1'b1 && cj[23] == 1'b1 ) begin
cjabs <= (~cj) +1'b1;
end
else if (flag1 == 1'b1 && cj[23] == 1'b0 ) begin
cjabs <= cj;
end
end
always @(posedge sclk) begin
{cj_r,cr_r} <= {cj,cr};
end
always @(posedge sclk) begin
flag2 <= flag1;
end
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
dividend <='d0;
divisor <= 'd1;
end
else if (flag2 == 1'b1 && cr_r[23] == 1'b0) begin
dividend <= cr_r - cjabs;
divisor <= cr_r + cjabs;
end
else if (flag2 == 1'b1 && cr_r[23] == 1'b1) begin
dividend <= cr_r + cjabs;
divisor <= cjabs - cr_r;
end
end
always @(posedge sclk) begin
flag3 <= flag2;
end
div_gen_0 div_gen_0_inst (
.aclk(sclk), // input wire aclk
.s_axis_divisor_tvalid(flag3), // input wire s_axis_divisor_tvalid
.s_axis_divisor_tdata(divisor), // input wire [23 : 0] s_axis_divisor_tdata
.s_axis_dividend_tvalid(flag3), // input wire s_axis_dividend_tvalid
.s_axis_dividend_tdata(dividend), // input wire [23 : 0] s_axis_dividend_tdata
.m_axis_dout_tvalid(div_valid), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata(div_dout) // output wire [39 : 0] m_axis_dout_tdata
);
always @(posedge sclk) begin
cr_r3 <= cr_r;
cj_r3 <= cj_r;
end
fifo_w24x64_r24x64 cr_buffer (
.clk(sclk), // input wire clk
.din(cr_r3), // input wire [23 : 0] din
.wr_en(flag3), // input wire wr_en
.rd_en(div_valid), // input wire rd_en
.dout(cr_fifo_data), // output wire [23 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
fifo_w24x64_r24x64 cj_buffer (
.clk(sclk), // input wire clk
.din(cj_r3), // input wire [23 : 0] din
.wr_en(flag3), // input wire wr_en
.rd_en(div_valid), // input wire rd_en
.dout(cj_fifo_data), // output wire [23 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
angle <='d0;
end
else if(div_valid == 1'b1 && cr_fifo_data == 'd0 && cj_fifo_data == 'd0) begin
angle <='d0;
end
else if (div_valid == 1'b1 && cr_fifo_data[23] == 1'b0) begin
angle <= 40'd65536 - div_dout;
end
else if(div_valid == 1'b1 && cr_fifo_data[23] == 1'b1) begin
angle <= 40'd196608 - div_dout;
end
end
always @(posedge sclk) begin
div_valid_r <= div_valid;
end
assign pi4 =16'd51472;//PI/4 (3.1415926/4)*65536
mult_a40xb16 mult_a40xb16_inst (
.CLK(sclk), // input wire CLK
.A(angle), // input wire [39 : 0] A
.B(pi4), // input wire [15 : 0] B
.P(angle_r) // output wire [55 : 0] P
);
always @(posedge sclk) begin
cj_r4 <= cj_fifo_data;
end
always @(posedge sclk) begin
cj_r5 <= cj_r4;
end
always @(posedge sclk) begin
div_valid_rr <= div_valid_r;
end
always @(posedge sclk) begin
if(rst_n== 1'b0) begin
angle_dout <='d0;
end
else if (div_valid_rr == 1'b1 && cj_r5[23] == 1'b0) begin
angle_dout <= angle_r;
end
else if (div_valid_rr == 1'b1 && cj_r5[23] == 1'b1) begin
angle_dout <= (~angle_r) + 1'b1;
end
end
always @(posedge sclk) begin
angel_dsout <= div_valid_rr;
end
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
cnt_mean <='d0;
end
else if (angel_dsout == 1'b1 && cnt_mean == end_count[12:1]-1) begin
cnt_mean <= 'd0;
end
else if (angel_dsout == 1'b1) begin
cnt_mean <= cnt_mean + 1'b1;
end
end
always @(posedge sclk)begin
if(rst_n == 1'b0) begin
dm_sum <= 'd0;
end
else if (angel_dsout == 1'b1 && cnt_mean == end_count[12:1]-1) begin
dm_sum <='d0;
end
else if (angel_dsout == 1'b1 ) begin
dm_sum <= dm_sum + {{10{angle_dout[55]}},angle_dout[55:16]};
end
end
always @(posedge sclk) begin
if(rst_n == 1'b0) begin
dmout <='d0;
dmsout <= 'd0;
end
else if (angel_dsout == 1'b1 && cnt_mean == end_count[12:1]-1) begin
dmout <= {dm_sum[49],dm_sum[24:10]};
dmsout <= 1'b1;
end
else begin
dmsout <= 1'b0;
dmout <= dmout;
end
end
endmodule
文章来源地址https://www.toymoban.com/news/detail-506661.html
到了这里,关于FM调制解调---FPGA的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!