最近在复习至芯培训课程的DDS部分,DDS的核心主要包括频率发生器、相位累加器和波形存储器三部分。波形存储器的作用是存储不同相位对应地址的波形值,相位累加器根据不同的相位产生对应的地址,用于选择不同的波形值,频率发生器根据输入频率产生频率控制字,其大小决定了相位累加器地址累加的速度,不同的累加速度决定了输出不同的频率大小。
实现的功能如下:输入有四个按键,分别对应波形选择、幅度调节、频率加和频率减;输出有蜂鸣器、6个动态数码管和输出波形d_wave。当四个按键有任意一个按下时,蜂鸣器响,数码管显示波形、幅度和频率对应的值,d_dds模块输出对应波形。
顶层dds模块包括以下几个部分:按键处理模块(key_ctrl)、脉冲处理模块(flag_ctrl)、信号发生器核心部分(d_dds)、蜂鸣器模块(beep)和6个动态数码管显示模块。顶层RTL视图如下。
dds顶层代码如下:
module dds(
input wire clk,
input wire rst_n,
input wire key_sel,
input wire key_amp,
input wire key_fadd,
input wire key_fsub,
output wire beep,
output wire [9:0] d_wave,
output wire [2:0] sel,
output wire [7:0] seg
);
wire flag_s;
wire flag_a;
wire flag_fa;
wire flag_fs;
wire [1:0] wave_sel;
wire [1:0] wave_a;
wire [19:0] wave_freq;
key_ctrl key_ctrl_inst(
.clk (clk ),
.rst_n (rst_n ),
.key_sel (key_sel ),
.key_amp (key_amp ),
.key_fadd (key_fadd),
.key_fsub (key_fsub),
.flag_s (flag_s ),
.flag_a (flag_a ),
.flag_fa (flag_fa ),
.flag_fs (flag_fs )
);
flag_ctrl flag_ctrl_inst(
.clk (clk ),
.rst_n (rst_n ),
.flag_s (flag_s ),
.flag_a (flag_a ),
.flag_fa (flag_fa ),
.flag_fs (flag_fs ),
.wave_sel (wave_sel ),
.wave_a (wave_a ),
.wave_freq (wave_freq )
);
d_dds d_dds_inst(
.clk (clk ),
.rst_n (rst_n ),
.wave_sel (wave_sel ),
.wave_a (wave_a ),
.wave_freq (wave_freq ),
.d_wave (d_wave )
);
beep beep_inst(
.clk (clk ),
.rst_n (rst_n ),
.flag_s (flag_s ),
.flag_a (flag_a ),
.flag_fa (flag_fa ),
.flag_fs (flag_fs ),
.beep (beep )
);
show_data show_data_inst(
.clk (clk ),
.rst_n (rst_n ),
.wave_sel (wave_sel ),
.wave_a (wave_a ),
.wave_freq (wave_freq ),
.sel (sel ),
.seg (seg )
);
endmodule
按键处理包括两个步骤,消抖和边缘检测,需要注意的点是需要对每一个按键进行这两步操作,生成对应的四个脉冲信号:波形选择、幅度调节、频率加和减。顶层代码如下:
module key_ctrl(
input wire clk,
input wire rst_n,
input wire key_sel,
input wire key_amp,
input wire key_fadd,
input wire key_fsub,
output wire flag_s,
output wire flag_a,
output wire flag_fa,
output wire flag_fs
);
wire key_wave1;
wire key_wave2;
wire key_wave3;
wire key_wave4;
key_filter key_filter_inst1(
.clk (clk),
.rst_n (rst_n),
.key (key_sel),
.key_wave (key_wave1)
);
edge_check edge_check_inst1(
.clk (clk),
.rst_n (rst_n),
.key (key_wave1),
.flag_neg (flag_s),
.flag_pos ()
);
key_filter key_filter_inst2(
.clk (clk),
.rst_n (rst_n),
.key (key_amp),
.key_wave (key_wave2)
);
edge_check edge_check_inst2(
.clk (clk),
.rst_n (rst_n),
.key (key_wave2),
.flag_neg (flag_a),
.flag_pos ()
);
key_filter key_filter_inst3(
.clk (clk),
.rst_n (rst_n),
.key (key_fadd),
.key_wave (key_wave3)
);
edge_check edge_check_inst3(
.clk (clk),
.rst_n (rst_n),
.key (key_wave3),
.flag_neg (flag_fa),
.flag_pos ()
);
key_filter key_filter_inst4(
.clk (clk),
.rst_n (rst_n),
.key (key_fsub),
.key_wave (key_wave4)
);
edge_check edge_check_inst4(
.clk (clk),
.rst_n (rst_n),
.key (key_wave4),
.flag_neg (flag_fs),
.flag_pos ()
);
endmodule
本模块根据四个按键产生的脉冲信号,输出波形选择、幅度和频率的控制信号。需要注意的点是幅度和频率变化只能在选中某一波形的前提条件下进行变化,当切换成其他波形时,要保持上一个波形的设定值不变,初始频率设为50k,按键对应频率加减对应为100Hz。代码如下:
module flag_ctrl(
input wire clk,
input wire rst_n,
input wire flag_s,
input wire flag_a,
input wire flag_fa,
input wire flag_fs,
output reg [1:0] wave_sel,
output reg [1:0] wave_a,
output reg [19:0] wave_freq
);
parameter FREQ = 50_000;
reg [1:0] amp_1;
reg [1:0] amp_2;
reg [1:0] amp_3;
reg [1:0] amp_4;
reg [19:0] freq1;
reg [19:0] freq2;
reg [19:0] freq3;
reg [19:0] freq4;
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
wave_sel <= 0;
else
if (flag_s == 1)
wave_sel <= wave_sel + 1'b1;
else
wave_sel <= wave_sel;
end
//===================== according to wave_sel, adjust amplitude ===========================
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
amp_1 <= 0;
else
if (wave_sel == 2'b00 && flag_a == 1)
amp_1 <= amp_1 + 1'b1;
else
amp_1 <= amp_1;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
amp_2 <= 0;
else
if (wave_sel == 2'b01 && flag_a == 1)
amp_2 <= amp_2 + 1'b1;
else
amp_2 <= amp_2;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
amp_3 <= 0;
else
if (wave_sel == 2'b10 && flag_a == 1)
amp_3 <= amp_3 + 1'b1;
else
amp_3 <= amp_3;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
amp_4 <= 0;
else
if (wave_sel == 2'b11 && flag_a == 1)
amp_4 <= amp_4 + 1'b1;
else
amp_1 <= amp_1;
end
//============== according to wave_sel, adjust frequency ==================================
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
freq1 <= FREQ;
else
if (wave_sel == 2'b00 && flag_fa == 1)
freq1 <= freq1 + 20'd100;
else
if (wave_sel == 2'b00 && flag_fs == 1)
freq1 <= freq1 - 20'd100;
else
freq1 <= freq1;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
freq2 <= FREQ;
else
if (wave_sel == 2'b01 && flag_fa == 1)
freq2 <= freq2 + 20'd100;
else
if (wave_sel == 2'b01 && flag_fs == 1)
freq2 <= freq2 - 20'd100;
else
freq2 <= freq2;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
freq3 <= FREQ;
else
if (wave_sel == 2'b10 && flag_fa == 1)
freq3 <= freq3 + 20'd100;
else
if (wave_sel == 2'b10 && flag_fs == 1)
freq3 <= freq3 - 20'd100;
else
freq3 <= freq3;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
freq4 <= FREQ;
else
if (wave_sel == 2'b11 && flag_fa == 1)
freq4 <= freq4 + 20'd100;
else
if (wave_sel == 2'b11 && flag_fs == 1)
freq4 <= freq4 - 20'd100;
else
freq4 <= freq4;
end
//=================== according wave_sel, output frequency and amplitude ==========================
always @ (*)
case (wave_sel)
2'b00 : begin wave_freq = freq1; wave_a = amp_1; end
2'b01 : begin wave_freq = freq2; wave_a = amp_2; end
2'b10 : begin wave_freq = freq3; wave_a = amp_3; end
2'b11 : begin wave_freq = freq4; wave_a = amp_4; end
endcase
endmodule
本模块包括四个部分,频率发生器、相位累加器、波形存储器和幅度调节器。需要注意的点:频率发生器的控制字word的位宽为什么要是64位,相位累加器的基地址和偏移地址的处理,波形存储器模块在rom中写入.mif文件时特别注意文件路径,应放在工程文件夹目录下,幅度调节器部分应注意对所成的幅值进行+1处理。顶层代码如下:
module d_dds(
input wire clk,
input wire rst_n,
input wire [1:0] wave_sel,
input wire [1:0] wave_a,
input wire [19:0] wave_freq,
output wire [9:0] d_wave
);
wire [31:0] f_word;
wire [9:0] addr_rom1024x8;
wire [7:0] wave;
freq_ctrl freq_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.wave_freq (wave_freq),
.f_word (f_word)
);
addr_ctrl addr_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.wave_sel (wave_sel),
.f_word (f_word),
.addr (addr_rom1024x8)
);
rom1024x8 rom1024x8_inst (
.address (addr_rom1024x8),
.clock (clk),
.q (wave)
);
a_ctrl a_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.wave_a (wave_a),
.wave (wave),
.d_wave (d_wave)
);
endmodule
此模块需要明确的是给什么样的信号蜂鸣器才能发声,本设计使用的蜂鸣器是无源蜂鸣器,给连续高低变化的信号就能发生,当然信号频率有一个范围。需要注意的是按键脉冲信号到来,100ms计数器的启动控制信号的条件是什么,代码如下:
module beep(
input wire clk,
input wire rst_n,
input wire flag_s,
input wire flag_a,
input wire flag_fa,
input wire flag_fs,
output reg beep
);
parameter T = 10_000;
parameter MAX = 5_000_000;
reg [15:0] cnt_10k;
reg wave_10k;
reg [22:0] cnt_100ms;
wire wave_100ms;
wire flag_or;
assign flag_or = (flag_s == 1 || flag_a == 1 || flag_fa == 1 || flag_fs == 1) ? 1'b1 : 1'b0;
//=================== generate 10k wave ===========================================================
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
cnt_10k <= 0;
else
if (cnt_10k < T - 1)
cnt_10k <= cnt_10k + 1'b1;
else
cnt_10k <= 0;
end
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
wave_10k <= 0;
else
if (cnt_10k < T / 2)
wave_10k <= 1'b1;
else
wave_10k <= 1'b0;
end
//================== generate wave_100ms ==========================================================
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
cnt_100ms <= MAX - 1;
else
if ((cnt_100ms == MAX - 1) && flag_or == 1)
cnt_100ms <= 0;
else
if (cnt_100ms < MAX - 1)
cnt_100ms <= cnt_100ms + 1'b1;
else
cnt_100ms <= cnt_100ms;
end
assign wave_100ms = (cnt_100ms != MAX - 1) ? 1'b1 : 1'b0;
//================= generate beep ===============================================================
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
beep <= 1;
else
beep <= ~ (wave_10k && wave_100ms);
end
endmodule
前两个数码管分别显示波形选择和幅度,后四个显示频率值。特别需要注意的是波形选择和幅度信号的位宽,以及频率对应需要bin2bcd模块。代码如下:
module show_data(
input wire clk,
input wire rst_n,
input wire [1:0] wave_sel,
input wire [1:0] wave_a,
input wire [19:0] wave_freq,
output wire [2:0] sel,
output wire [7:0] seg
);
wire [3:0] b_wave_sel;
wire [3:0] b_wave_a;
wire [27:0] bcd;
wire [23:0] show_data;
assign b_wave_sel = wave_sel + 1'b1;
assign b_wave_a = wave_a + 1'b1;
assign show_data = {b_wave_sel, b_wave_a, bcd[23:8]};
bin2bcd # (.WIDTH_BIN(20), .WIDTH_BCD(28)) bin2bcd_inst(
.bin (wave_freq),
.bcd (bcd)
);
seven_tube_drive seven_tube_drive_inst(
.clk (clk),
.rst_n (rst_n),
.show_data (show_data),
.seven_tube_seg (seg),
.seven_tube_sel (sel)
);
endmodule
这个工程不建议对所有模块进行仿真,因为子模块太多,提供一个简单的,不足之处请谅解。代码如下:
`timescale 1ns/1ps
module dds_tb;
reg clk;
reg rst_n;
reg key_sel;
reg key_amp;
reg key_fadd;
reg key_fsub;
wire beep;
wire [9:0] d_wave;
wire [2:0] sel;
wire [7:0] seg;
wire [7:0] addr = dds_inst.d_dds_inst.addr_ctrl_inst.p_addr;
defparam dds_inst.key_ctrl_inst.key_filter_inst1.T_5ms = 10;
defparam dds_inst.key_ctrl_inst.key_filter_inst2.T_5ms = 10;
defparam dds_inst.key_ctrl_inst.key_filter_inst3.T_5ms = 10;
defparam dds_inst.key_ctrl_inst.key_filter_inst4.T_5ms = 10;
defparam dds_inst.beep_inst.T = 10;
defparam dds_inst.beep_inst.MAX = 50;
dds dds_inst(
.clk (clk ),
.rst_n (rst_n ),
.key_sel (key_sel ),
.key_amp (key_amp ),
.key_fadd (key_fadd ),
.key_fsub (key_fsub ),
.beep (beep ),
.d_wave (d_wave ),
.sel (sel ),
.seg (seg )
);
initial clk = 1;
always #10 clk = ~ clk;
initial
begin
rst_n = 0;
key_sel = 1;
key_amp = 1;
key_fadd = 1;
key_fsub = 1;
# 201
rst_n = 1;
task_stop;
key_sel = 0;
task_stop;
key_amp = 0;
task_stop;
key_fadd = 0;
task_stop;
key_fsub = 0;
task_stop;
$stop;
end
task task_stop;
begin
repeat (10)
begin
while (addr != 255) @ (posedge clk);
while (addr != 0) @ (posedge clk);
end
end
endtask
endmodule
本人做得还较为顺利,希望给初学者提供一个参考,详细代码见附件。