本文转载自: OpenFPGA微信公众号
绪论
由于某种需求需要生成正弦波,因此使用 C 应用程序中的sin()函数来计算单位圆的幅度值,然后将该幅度值转换为 AD9717 的适当 DAC 代码(当然将每个角度值转换为弧度)。
能够使用DAC生成简单的正弦波,下一个想法就是在 SDR(软件定义无线电)中使用频率调制。
大多数 SDR 设计都有 3 个不同的内部运行频率:一个低基带频率,用于处理来自 ADC/DAC 的数据;一个或多个中间频率,最终基带数据流作为中间步骤提升到该频率;以及最终的 RF 将输入/输出天线的输出频率。显然,最简单的起点是基带,因为它是最低频率,并且是实际模拟数据流从各个数字数据位组合在一起/提取的地方。为了进一步缩小范围,1 和 0 的数字位与某些相关模拟波形相关的确切点被称为符号映射器,它是前面提到的 SDR 基本构建块。
因此,本项目将是一个非常简化的基带符号映射器,用于 FSK 数字调制方案的链(数字到模拟)的发送端。
数字调制
频移键控 (FSK) 是一种数字调制,通过更改频率来表示数据流中的不同位/符号。在其最基本的形式中,一个频率用于表示二进制 1,另一个频率用于表示二进制 0。这种形式的 FSK 被称为二进制 FSK 或 2-FSK。
从上面可以看出,1比特由大约是表示0比特的频率的两倍的频率来表示。对于 ZYNQ+SDR 来说,这意味着它需要能够分别以连续相位输出两个不同的频率。换句话说:定时在这里很重要,可以确保一个频率的相位恰好在最后一个频率的相位停止的地方出现。每个频率的一个完整周期也是每一位完成的。
Vivado 中的逻辑设计
由于本演示中只关注发送器端,为了处理符号映射器逻辑可能在 MM2S 读取中对来自 DDR 内存的数据流施加的背压,将带有 FSK 符号映射器的 sin LUT 放置在 AXIS 数据 FIFO 和 DAC 控制器 IP 之间。这样,FIFO 可以完成从内存的 MM2S 传输,当符号映射器逻辑每比特输出一个周期的相应频率值时,AXI DMA 不会被锁定tdata。
代码如下:
`timescale 1ns / 1ps
module sin_lut(
input clk,
input rst,
input [8:0] sel,
output [15:0] DAC_code
);
reg [15:0] DAC_code;
always @ (posedge clk)
begin
if (rst == 1'b0)
begin
end
else
begin
case (sel)
9'd0 : DAC_code = 16'h0000;
9'd1 : DAC_code = 16'h01C8;
9'd2 : DAC_code = 16'h0390;
9'd3 : DAC_code = 16'h0558;
9'd4 : DAC_code = 16'h0724;
9'd5 : DAC_code = 16'h08EC;
9'd6 : DAC_code = 16'h0AB0;
9'd7 : DAC_code = 16'h0C78;
9'd8 : DAC_code = 16'h0E3C;
9'd9 : DAC_code = 16'h1004;
9'd10 : DAC_code = 16'h11C4;
9'd11 : DAC_code = 16'h1388;
9'd12 : DAC_code = 16'h1548;
9'd13 : DAC_code = 16'h1708;
9'd14 : DAC_code = 16'h18C4;
9'd15 : DAC_code = 16'h1A7C;
9'd16 : DAC_code = 16'h1C38;
9'd17 : DAC_code = 16'h1DEC;
9'd18 : DAC_code = 16'h1FA0;
9'd19 : DAC_code = 16'h2154;
9'd20 : DAC_code = 16'h2304;
9'd21 : DAC_code = 16'h24B0;
9'd22 : DAC_code = 16'h2658;
9'd23 : DAC_code = 16'h2800;
9'd24 : DAC_code = 16'h29A4;
9'd25 : DAC_code = 16'h2B44;
9'd26 : DAC_code = 16'h2CE0;
9'd27 : DAC_code = 16'h2E78;
9'd28 : DAC_code = 16'h3010;
9'd29 : DAC_code = 16'h31A0;
9'd30 : DAC_code = 16'h3330;
9'd31 : DAC_code = 16'h34B8;
9'd32 : DAC_code = 16'h3640;
9'd33 : DAC_code = 16'h37C0;
9'd34 : DAC_code = 16'h3940;
9'd35 : DAC_code = 16'h3AB8;
9'd36 : DAC_code = 16'h3C2C;
9'd37 : DAC_code = 16'h3D9C;
9'd38 : DAC_code = 16'h3F08;
9'd39 : DAC_code = 16'h406C;
9'd40 : DAC_code = 16'h41D0;
9'd41 : DAC_code = 16'h432C;
9'd42 : DAC_code = 16'h4480;
9'd43 : DAC_code = 16'h45D0;
9'd44 : DAC_code = 16'h471C;
9'd45 : DAC_code = 16'h4864;
9'd46 : DAC_code = 16'h49A4;
9'd47 : DAC_code = 16'h4AE0;
9'd48 : DAC_code = 16'h4C14;
9'd49 : DAC_code = 16'h4D44;
9'd50 : DAC_code = 16'h4E6C;
9'd51 : DAC_code = 16'h4F90;
9'd52 : DAC_code = 16'h50AC;
9'd53 : DAC_code = 16'h51C4;
9'd54 : DAC_code = 16'h52D4;
9'd55 : DAC_code = 16'h53DC;
9'd56 : DAC_code = 16'h54E0;
9'd57 : DAC_code = 16'h55DC;
9'd58 : DAC_code = 16'h56D4;
9'd59 : DAC_code = 16'h57C0;
9'd60 : DAC_code = 16'h58A8;
9'd61 : DAC_code = 16'h598C;
9'd62 : DAC_code = 16'h5A64;
9'd63 : DAC_code = 16'h5B38;
9'd64 : DAC_code = 16'h5C04;
9'd65 : DAC_code = 16'h5CC8;
9'd66 : DAC_code = 16'h5D88;
9'd67 : DAC_code = 16'h5E3C;
9'd68 : DAC_code = 16'h5EEC;
9'd69 : DAC_code = 16'h5F94;
9'd70 : DAC_code = 16'h6034;
9'd71 : DAC_code = 16'h60CC;
9'd72 : DAC_code = 16'h6160;
9'd73 : DAC_code = 16'h61E8;
9'd74 : DAC_code = 16'h6268;
9'd75 : DAC_code = 16'h62E4;
9'd76 : DAC_code = 16'h6358;
9'd77 : DAC_code = 16'h63C0;
9'd78 : DAC_code = 16'h6424;
9'd79 : DAC_code = 16'h6480;
9'd80 : DAC_code = 16'h64D4;
9'd81 : DAC_code = 16'h6520;
9'd82 : DAC_code = 16'h6564;
9'd83 : DAC_code = 16'h659C;
9'd84 : DAC_code = 16'h65D0;
9'd85 : DAC_code = 16'h65FC;
9'd86 : DAC_code = 16'h6620;
9'd87 : DAC_code = 16'h663C;
9'd88 : DAC_code = 16'h6650;
9'd89 : DAC_code = 16'h665C;
9'd90 : DAC_code = 16'h6660;
9'd91 : DAC_code = 16'h665C;
9'd92 : DAC_code = 16'h6650;
9'd93 : DAC_code = 16'h663C;
9'd94 : DAC_code = 16'h6620;
9'd95 : DAC_code = 16'h65FC;
9'd96 : DAC_code = 16'h65D0;
9'd97 : DAC_code = 16'h659C;
9'd98 : DAC_code = 16'h6564;
9'd99 : DAC_code = 16'h6520;
9'd100 : DAC_code = 16'h64D4;
9'd101 : DAC_code = 16'h6480;
9'd102 : DAC_code = 16'h6424;
9'd103 : DAC_code = 16'h63C0;
9'd104 : DAC_code = 16'h6358;
9'd105 : DAC_code = 16'h62E4;
9'd106 : DAC_code = 16'h6268;
9'd107 : DAC_code = 16'h61E8;
9'd108 : DAC_code = 16'h6160;
9'd109 : DAC_code = 16'h60CC;
9'd110 : DAC_code = 16'h6034;
9'd111 : DAC_code = 16'h5F94;
9'd112 : DAC_code = 16'h5EEC;
9'd113 : DAC_code = 16'h5E3C;
9'd114 : DAC_code = 16'h5D88;
9'd115 : DAC_code = 16'h5CC8;
9'd116 : DAC_code = 16'h5C04;
9'd117 : DAC_code = 16'h5B38;
9'd118 : DAC_code = 16'h5A64;
9'd119 : DAC_code = 16'h598C;
9'd120 : DAC_code = 16'h58A8;
9'd121 : DAC_code = 16'h57C0;
9'd122 : DAC_code = 16'h56D4;
9'd123 : DAC_code = 16'h55DC;
9'd124 : DAC_code = 16'h54E0;
9'd125 : DAC_code = 16'h53DC;
9'd126 : DAC_code = 16'h52D4;
9'd127 : DAC_code = 16'h51C4;
9'd128 : DAC_code = 16'h50AC;
9'd129 : DAC_code = 16'h4F90;
9'd130 : DAC_code = 16'h4E6C;
9'd131 : DAC_code = 16'h4D44;
9'd132 : DAC_code = 16'h4C14;
9'd133 : DAC_code = 16'h4AE0;
9'd134 : DAC_code = 16'h49A4;
9'd135 : DAC_code = 16'h4864;
9'd136 : DAC_code = 16'h471C;
9'd137 : DAC_code = 16'h45D0;
9'd138 : DAC_code = 16'h4480;
9'd139 : DAC_code = 16'h432C;
9'd140 : DAC_code = 16'h41D0;
9'd141 : DAC_code = 16'h406C;
9'd142 : DAC_code = 16'h3F08;
9'd143 : DAC_code = 16'h3D9C;
9'd144 : DAC_code = 16'h3C2C;
9'd145 : DAC_code = 16'h3AB8;
9'd146 : DAC_code = 16'h3940;
9'd147 : DAC_code = 16'h37C0;
9'd148 : DAC_code = 16'h3640;
9'd149 : DAC_code = 16'h34B8;
9'd150 : DAC_code = 16'h3330;
9'd151 : DAC_code = 16'h31A0;
9'd152 : DAC_code = 16'h3010;
9'd153 : DAC_code = 16'h2E78;
9'd154 : DAC_code = 16'h2CE0;
9'd155 : DAC_code = 16'h2B44;
9'd156 : DAC_code = 16'h29A4;
9'd157 : DAC_code = 16'h2800;
9'd158 : DAC_code = 16'h2658;
9'd159 : DAC_code = 16'h24B0;
9'd160 : DAC_code = 16'h2304;
9'd161 : DAC_code = 16'h2154;
9'd162 : DAC_code = 16'h1FA0;
9'd163 : DAC_code = 16'h1DEC;
9'd164 : DAC_code = 16'h1C38;
9'd165 : DAC_code = 16'h1A7C;
9'd166 : DAC_code = 16'h18C4;
9'd167 : DAC_code = 16'h1708;
9'd168 : DAC_code = 16'h1548;
9'd169 : DAC_code = 16'h1388;
9'd170 : DAC_code = 16'h11C4;
9'd171 : DAC_code = 16'h1004;
9'd172 : DAC_code = 16'h0E3C;
9'd173 : DAC_code = 16'h0C78;
9'd174 : DAC_code = 16'h0AB0;
9'd175 : DAC_code = 16'h08EC;
9'd176 : DAC_code = 16'h0724;
9'd177 : DAC_code = 16'h0558;
9'd178 : DAC_code = 16'h0390;
9'd179 : DAC_code = 16'h01C8;
9'd180 : DAC_code = 16'h0000;
9'd181 : DAC_code = 16'hFE37;
9'd182 : DAC_code = 16'hFC6F;
9'd183 : DAC_code = 16'hFAA7;
9'd184 : DAC_code = 16'hF8DB;
9'd185 : DAC_code = 16'hF713;
9'd186 : DAC_code = 16'hF54F;
9'd187 : DAC_code = 16'hF387;
9'd188 : DAC_code = 16'hF1C3;
9'd189 : DAC_code = 16'hEFFB;
9'd190 : DAC_code = 16'hEE3B;
9'd191 : DAC_code = 16'hEC77;
9'd192 : DAC_code = 16'hEAB7;
9'd193 : DAC_code = 16'hE8F7;
9'd194 : DAC_code = 16'hE73B;
9'd195 : DAC_code = 16'hE583;
9'd196 : DAC_code = 16'hE3C7;
9'd197 : DAC_code = 16'hE213;
9'd198 : DAC_code = 16'hE05F;
9'd199 : DAC_code = 16'hDEAB;
9'd200 : DAC_code = 16'hDCFB;
9'd201 : DAC_code = 16'hDB4F;
9'd202 : DAC_code = 16'hD9A7;
9'd203 : DAC_code = 16'hD7FF;
9'd204 : DAC_code = 16'hD65B;
9'd205 : DAC_code = 16'hD4BB;
9'd206 : DAC_code = 16'hD31F;
9'd207 : DAC_code = 16'hD187;
9'd208 : DAC_code = 16'hCFEF;
9'd209 : DAC_code = 16'hCE5F;
9'd210 : DAC_code = 16'hCCCF;
9'd211 : DAC_code = 16'hCB47;
9'd212 : DAC_code = 16'hC9BF;
9'd213 : DAC_code = 16'hC83F;
9'd214 : DAC_code = 16'hC6BF;
9'd215 : DAC_code = 16'hC547;
9'd216 : DAC_code = 16'hC3D3;
9'd217 : DAC_code = 16'hC263;
9'd218 : DAC_code = 16'hC0F7;
9'd219 : DAC_code = 16'hBF93;
9'd220 : DAC_code = 16'hBE2F;
9'd221 : DAC_code = 16'hBCD3;
9'd222 : DAC_code = 16'hBB7F;
9'd223 : DAC_code = 16'hBA2F;
9'd224 : DAC_code = 16'hB8E3;
9'd225 : DAC_code = 16'hB79B;
9'd226 : DAC_code = 16'hB65B;
9'd227 : DAC_code = 16'hB51F;
9'd228 : DAC_code = 16'hB3EB;
9'd229 : DAC_code = 16'hB2BB;
9'd230 : DAC_code = 16'hB193;
9'd231 : DAC_code = 16'hB06F;
9'd232 : DAC_code = 16'hAF53;
9'd233 : DAC_code = 16'hAE3B;
9'd234 : DAC_code = 16'hAD2B;
9'd235 : DAC_code = 16'hAC23;
9'd236 : DAC_code = 16'hAB1F;
9'd237 : DAC_code = 16'hAA23;
9'd238 : DAC_code = 16'hA92B;
9'd239 : DAC_code = 16'hA83F;
9'd240 : DAC_code = 16'hA757;
9'd241 : DAC_code = 16'hA673;
9'd242 : DAC_code = 16'hA59B;
9'd243 : DAC_code = 16'hA4C7;
9'd244 : DAC_code = 16'hA3FB;
9'd245 : DAC_code = 16'hA337;
9'd246 : DAC_code = 16'hA277;
9'd247 : DAC_code = 16'hA1C3;
9'd248 : DAC_code = 16'hA113;
9'd249 : DAC_code = 16'hA06B;
9'd250 : DAC_code = 16'h9FCB;
9'd251 : DAC_code = 16'h9F33;
9'd252 : DAC_code = 16'h9E9F;
9'd253 : DAC_code = 16'h9E17;
9'd254 : DAC_code = 16'h9D97;
9'd255 : DAC_code = 16'h9D1B;
9'd256 : DAC_code = 16'h9CA7;
9'd257 : DAC_code = 16'h9C3F;
9'd258 : DAC_code = 16'h9BDB;
9'd259 : DAC_code = 16'h9B7F;
9'd260 : DAC_code = 16'h9B2B;
9'd261 : DAC_code = 16'h9ADF;
9'd262 : DAC_code = 16'h9A9B;
9'd263 : DAC_code = 16'h9A63;
9'd264 : DAC_code = 16'h9A2F;
9'd265 : DAC_code = 16'h9A03;
9'd266 : DAC_code = 16'h99DF;
9'd267 : DAC_code = 16'h99C3;
9'd268 : DAC_code = 16'h99AF;
9'd269 : DAC_code = 16'h99A3;
9'd270 : DAC_code = 16'h999F;
9'd271 : DAC_code = 16'h99A3;
9'd272 : DAC_code = 16'h99AF;
9'd273 : DAC_code = 16'h99C3;
9'd274 : DAC_code = 16'h99DF;
9'd275 : DAC_code = 16'h9A03;
9'd276 : DAC_code = 16'h9A2F;
9'd277 : DAC_code = 16'h9A63;
9'd278 : DAC_code = 16'h9A9B;
9'd279 : DAC_code = 16'h9ADF;
9'd280 : DAC_code = 16'h9B2B;
9'd281 : DAC_code = 16'h9B7F;
9'd282 : DAC_code = 16'h9BDB;
9'd283 : DAC_code = 16'h9C3F;
9'd284 : DAC_code = 16'h9CA7;
9'd285 : DAC_code = 16'h9D1B;
9'd286 : DAC_code = 16'h9D97;
9'd287 : DAC_code = 16'h9E17;
9'd288 : DAC_code = 16'h9E9F;
9'd289 : DAC_code = 16'h9F33;
9'd290 : DAC_code = 16'h9FCB;
9'd291 : DAC_code = 16'hA06B;
9'd292 : DAC_code = 16'hA113;
9'd293 : DAC_code = 16'hA1C3;
9'd294 : DAC_code = 16'hA277;
9'd295 : DAC_code = 16'hA337;
9'd296 : DAC_code = 16'hA3FB;
9'd297 : DAC_code = 16'hA4C7;
9'd298 : DAC_code = 16'hA59B;
9'd299 : DAC_code = 16'hA673;
9'd300 : DAC_code = 16'hA757;
9'd301 : DAC_code = 16'hA83F;
9'd302 : DAC_code = 16'hA92B;
9'd303 : DAC_code = 16'hAA23;
9'd304 : DAC_code = 16'hAB1F;
9'd305 : DAC_code = 16'hAC23;
9'd306 : DAC_code = 16'hAD2B;
9'd307 : DAC_code = 16'hAE3B;
9'd308 : DAC_code = 16'hAF53;
9'd309 : DAC_code = 16'hB06F;
9'd310 : DAC_code = 16'hB193;
9'd311 : DAC_code = 16'hB2BB;
9'd312 : DAC_code = 16'hB3EB;
9'd313 : DAC_code = 16'hB51F;
9'd314 : DAC_code = 16'hB65B;
9'd315 : DAC_code = 16'hB79B;
9'd316 : DAC_code = 16'hB8E3;
9'd317 : DAC_code = 16'hBA2F;
9'd318 : DAC_code = 16'hBB7F;
9'd319 : DAC_code = 16'hBCD3;
9'd320 : DAC_code = 16'hBE2F;
9'd321 : DAC_code = 16'hBF93;
9'd322 : DAC_code = 16'hC0F7;
9'd323 : DAC_code = 16'hC263;
9'd324 : DAC_code = 16'hC3D3;
9'd325 : DAC_code = 16'hC547;
9'd326 : DAC_code = 16'hC6BF;
9'd327 : DAC_code = 16'hC83F;
9'd328 : DAC_code = 16'hC9BF;
9'd329 : DAC_code = 16'hCB47;
9'd330 : DAC_code = 16'hCCCF;
9'd331 : DAC_code = 16'hCE5F;
9'd332 : DAC_code = 16'hCFEF;
9'd333 : DAC_code = 16'hD187;
9'd334 : DAC_code = 16'hD31F;
9'd335 : DAC_code = 16'hD4BB;
9'd336 : DAC_code = 16'hD65B;
9'd337 : DAC_code = 16'hD7FF;
9'd338 : DAC_code = 16'hD9A7;
9'd339 : DAC_code = 16'hDB4F;
9'd340 : DAC_code = 16'hDCFB;
9'd341 : DAC_code = 16'hDEAB;
9'd342 : DAC_code = 16'hE05F;
9'd343 : DAC_code = 16'hE213;
9'd344 : DAC_code = 16'hE3C7;
9'd345 : DAC_code = 16'hE583;
9'd346 : DAC_code = 16'hE73B;
9'd347 : DAC_code = 16'hE8F7;
9'd348 : DAC_code = 16'hEAB7;
9'd349 : DAC_code = 16'hEC77;
9'd350 : DAC_code = 16'hEE3B;
9'd351 : DAC_code = 16'hEFFB;
9'd352 : DAC_code = 16'hF1C3;
9'd353 : DAC_code = 16'hF387;
9'd354 : DAC_code = 16'hF54F;
9'd355 : DAC_code = 16'hF713;
9'd356 : DAC_code = 16'hF8DB;
9'd357 : DAC_code = 16'hFAA7;
9'd358 : DAC_code = 16'hFC6F;
9'd359 : DAC_code = 16'hFE37;
9'd360 : DAC_code = 16'h0000;
default : DAC_code = 16'h0000;
endcase
end
end
endmodule
由于 sin LUT 的 case 语句中的选择值是正弦波单位圆的每个 360 度值,因此计数器从 0 到 360 递增的速度最终设置了输出正弦波的频率。
sin LUT 上方的逻辑中需要三个计数器:一个计数器用于对 sin LUT 选择从 0 到 359 的度数进行计数,一个计数器用于计算频率 0 的增量之间的延迟,以及一个计数器用于计算频率 0 的增量之间的延迟。频率 1.
always @ (posedge clk)
begin
if (rst == 1'b0)
begin
degree_cntr <= 9'd0;
degree_cntr_done <= 1'b0;
end
else if (incr_degree_cntr == 1'b1)
begin
if (degree_cntr < unit_circle_deg)
begin
degree_cntr <= degree_cntr + 1;
degree_cntr_done <= 1'b0;
end
else
begin
degree_cntr <= 9'd0;
degree_cntr_done <= 1'b1;
end
end
else
begin
degree_cntr <= degree_cntr;
end
end
always @ (posedge clk)
begin
if (rst == 1'b0)
begin
incr_degree_cntr = 1'b0;
period_cntr <= 3'd0;
end
else
begin
if (period_cntr == period)
begin
incr_degree_cntr = 1'b1;
period_cntr <= 3'd0;
end
else
begin
incr_degree_cntr = 1'b0;
period_cntr <= period_cntr + 1;
end
end
end
always @ (posedge clk)
begin
if (rst == 1'b0)
begin
tdata_sel_cntr <= 5'd0;
end
else
begin
if (degree_cntr_done == 1'b1)
begin
tdata_sel_cntr <= tdata_sel_cntr + 1;
end
else
begin
tdata_sel_cntr <= tdata_sel_cntr;
end
end
end
还需要一个并行到串行转换器,通过 MM2S 传输从 DDR 发出的数据包的 AXI Stream 接口获取 32 位数据,并将其串行化,可以通过 MM2S 传输输出该位各自频率的一个周期。 DAC一次。这就是 2-FSK 的局限性暴露出来的地方:一次只能传输一位。
always @ (tdata_sel_cntr)
begin
case (tdata_sel_cntr)
32'd0 :
begin
current_tx_bit <= tdata[0];
tx_pkt_done <= 1'b0;
end
32'd1 :
begin
current_tx_bit <= tdata[1];
tx_pkt_done <= 1'b0;
end
32'd2 :
begin
current_tx_bit <= tdata[2];
tx_pkt_done <= 1'b0;
end
32'd3 :
begin
current_tx_bit <= tdata[3];
tx_pkt_done <= 1'b0;
end
32'd4 :
begin
current_tx_bit <= tdata[4];
tx_pkt_done <= 1'b0;
end
32'd5 :
begin
current_tx_bit <= tdata[5];
tx_pkt_done <= 1'b0;
end
32'd6 :
begin
current_tx_bit <= tdata[6];
tx_pkt_done <= 1'b0;
end
32'd7 :
begin
current_tx_bit <= tdata[7];
tx_pkt_done <= 1'b0;
end
32'd8 :
begin
current_tx_bit <= tdata[8];
tx_pkt_done <= 1'b0;
end
32'd9 :
begin
current_tx_bit <= tdata[9];
tx_pkt_done <= 1'b0;
end
32'd10 :
begin
current_tx_bit <= tdata[10];
tx_pkt_done <= 1'b0;
end
32'd11 :
begin
current_tx_bit <= tdata[11];
tx_pkt_done <= 1'b0;
end
32'd12 :
begin
current_tx_bit <= tdata[12];
tx_pkt_done <= 1'b0;
end
32'd13 :
begin
current_tx_bit <= tdata[13];
tx_pkt_done <= 1'b0;
end
32'd14 :
begin
current_tx_bit <= tdata[14];
tx_pkt_done <= 1'b0;
end
32'd15 :
begin
current_tx_bit <= tdata[15];
tx_pkt_done <= 1'b0;
end
32'd16 :
begin
current_tx_bit <= tdata[16];
tx_pkt_done <= 1'b0;
end
32'd17 :
begin
current_tx_bit <= tdata[17];
tx_pkt_done <= 1'b0;
end
32'd18 :
begin
current_tx_bit <= tdata[18];
tx_pkt_done <= 1'b0;
end
32'd19 :
begin
current_tx_bit <= tdata[19];
tx_pkt_done <= 1'b0;
end
32'd20 :
begin
current_tx_bit <= tdata[20];
tx_pkt_done <= 1'b0;
end
32'd21 :
begin
current_tx_bit <= tdata[21];
tx_pkt_done <= 1'b0;
end
32'd22 :
begin
current_tx_bit <= tdata[22];
tx_pkt_done <= 1'b0;
end
32'd23 :
begin
current_tx_bit <= tdata[23];
tx_pkt_done <= 1'b0;
end
32'd24 :
begin
current_tx_bit <= tdata[24];
tx_pkt_done <= 1'b0;
end
32'd25 :
begin
current_tx_bit <= tdata[25];
tx_pkt_done <= 1'b0;
end
32'd26 :
begin
current_tx_bit <= tdata[26];
tx_pkt_done <= 1'b0;
end
32'd27 :
begin
current_tx_bit <= tdata[27];
tx_pkt_done <= 1'b0;
end
32'd28 :
begin
current_tx_bit <= tdata[28];
tx_pkt_done <= 1'b0;
end
32'd29 :
begin
current_tx_bit <= tdata[29];
tx_pkt_done <= 1'b0;
end
32'd30 :
begin
current_tx_bit <= tdata[30];
tx_pkt_done <= 1'b0;
end
32'd31 :
begin
current_tx_bit <= tdata[31];
tx_pkt_done <= 1'b1;
end
default :
begin
current_tx_bit <= 1'b0;
tx_pkt_done <= 1'b0;
end
endcase
end
always @ (posedge clk)
begin
if (current_tx_bit == 1'b1)
period <= T1_period;
else
period <= T0_period;
end
最后,AXI Stream 接口只需要围绕上述逻辑,使用从接口接收来自 AXIS FIFO 的数据,并使用主接口将数据输出到 DAC 控制器。
`timescale 1ns / 1ps
module sin_axis(
input clk,
input reset,
input [31:0] s_axis_tdata,
input [3:0] s_axis_tkeep,
input s_axis_tlast,
output reg s_axis_tready,
input s_axis_tvalid,
output reg [31:0] m_axis_tdata,
output reg [3:0] m_axis_tkeep,
output reg m_axis_tlast,
input m_axis_tready,
output reg m_axis_tvalid,
output [2:0] state_reg
);
sin_sm sin_sm_i(
.clk(clk),
.rst(reset),
.tdata_slave(tdata_slave),
.tdata_master(tdata_master),
.tx_pkt_done(tx_pkt_done)
);
reg tlast;
reg [2:0] state_reg;
wire tx_pkt_done;
wire [31:0] tdata_master;
reg [31:0] tdata_slave;
parameter init = 3'd0;
parameter SetSlaveTready = 3'd1;
parameter CheckSlaveTvalid = 3'd2;
parameter ProcessTdata = 3'd3;
parameter CheckTlast = 3'd4;
always @ (posedge clk)
begin
// Default outputs
m_axis_tvalid <= 1'b0;
if (reset == 1'b0)
begin
tlast <= 1'b0;
tdata_slave[31:0] <= 32'd0;
s_axis_tready <= 1'b0;
m_axis_tdata[31:0] <= 32'd0;
m_axis_tkeep <= 4'h0;
m_axis_tlast <= 1'b0;
state_reg <= init;
end
else
begin
case(state_reg)
init : // 0
begin
tlast <= 1'b0;
tdata_slave[31:0] <= 32'd0;
s_axis_tready <= 1'b0;
m_axis_tdata[31:0] <= 32'd0;
m_axis_tkeep <= 4'h0;
m_axis_tlast <= 1'b0;
state_reg <= SetSlaveTready;
end
SetSlaveTready : // 1
begin
s_axis_tready <= 1'b1;
state_reg <= CheckSlaveTvalid;
end
CheckSlaveTvalid : // 2
begin
if (s_axis_tkeep == 4'hf && s_axis_tvalid == 1'b1)
begin
s_axis_tready <= 1'b0;
tlast <= s_axis_tlast;
tdata_slave[31:0] <= s_axis_tdata[31:0];
state_reg <= ProcessTdata;
end
else
begin
tdata_slave[31:0] <= 32'd0;
state_reg <= CheckSlaveTvalid;
end
end
ProcessTdata : // 3
begin
m_axis_tkeep <= 4'hf;
m_axis_tlast <= tlast;
m_axis_tvalid <= 1'b1;
m_axis_tdata[31:0] <= tdata_master[31:0];
if (m_axis_tready == 1'b1 && tx_pkt_done == 1'b1)
begin
state_reg <= CheckTlast;
end
else
begin
state_reg <= ProcessTdata;
end
end
CheckTlast : // 4
begin
if (m_axis_tlast == 1'b1)
begin
state_reg <= init;
end
else if (m_axis_tready == 1'b1)
begin
state_reg <= SetSlaveTready;
end
else
begin
state_reg <= CheckTlast;
end
end
endcase
end
end
endmodule
完整的代码见最后。
Vitis 软件
由于生成正弦波的所有逻辑都是在 Verilog 的 HDL 中处理的,因此 C 代码中唯一剩下的就是控制 MM2S 传输(源文件也附在下面):
int main()
{
init_platform();
XAxiDma_Config *CfgPtr; //DMA configuration pointer
int Status, Index;
u8 *TxBufferPtr;
TxBufferPtr = (u8 *)TX_BUFFER_BASE;
for(Index = 0; Index < MAX_PKT_LEN; Index ++){
TxBufferPtr[Index] = 0x00;
}
CfgPtr = XAxiDma_LookupConfig(DMA_DEV_ID);
if (!CfgPtr) {
xil_printf("No config found for %d\r\n", DMA_DEV_ID);
return XST_FAILURE;
}
Status = XAxiDma_CfgInitialize(&AxiDma, CfgPtr);
if (Status != XST_SUCCESS) {
xil_printf("Initialization failed %d\r\n", Status);
return XST_FAILURE;
}
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
TxBufferPtr[0] = 0xef;
Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
XAxiDma_Reset(&AxiDma);
Status = XAxiDma_MM2Stransfer(&AxiDma,(UINTPTR) TxBufferPtr, MAX_PKT_LEN);
if (Status != XST_SUCCESS){
xil_printf("XAXIDMA_DMA_TO_DEVICE transfer failed...\r\n");
return XST_FAILURE;
}
cleanup_platform();
return 0;
}
测试设备
为了验证模拟输出,将其通道 1 连接到示波器通道 1,并在主机 PC 上启动 WaveForms 来查看它。
然后,在 Vitis 中启动 C 应用程序的调试,并在 MM2S 传输开始之前设置了断点:
由于将DAC设置为低增益模式,因此峰值输出值约为 1.0v。在 WaveForms 的 Scope 选项卡中,为超过 100mV 的上升沿设置电平触发器,然后单击Run 。
得到 1 和 0 两个不同频率值的清晰输出:
正如我上面提到的,这只是符号映射器的一个非常简化的版本,目的是为了以更实用、更实际的方式克服 SDR 设计入门的困难。两个不同频率的周期计数器是查看输出结果的良好起点。
代码
https://github.com/Digilent/vivado-boards
https://github.com/suisuisi/FPGATechnologyGroup/tree/main/simple_2_fsk