原创 DDS的编写(verilog)

2011-7-26 20:26 6113 11 16 分类: FPGA/CPLD

先说说DDS算法 的基本原理:

DDS是直接式数字频率合成的简称。它在无线通信系统实现中是一门关键技术。其实,DDS在FPGA 中的实现方法说白了就是在定义好的ROM数据中寻址并找到该地址对应的波形数据。在系统参考时钟的驱动下,通过控制频率控制字来驱动相位累加寄存器数值的 变化,从而是在ROM中寻址并找到对应数据,最后输出为波形数据。其中,在每一个系统时钟脉冲来临时,相位累加寄存器都会相应叠加一次频率控制字。其实, 频率控制字的功能就是控制步进长度,也就是形成的波形的频率和系统时钟频率的比例关系,最终可以确定输出波形的频率。比如要输出正弦波,系统参考时钟频率 为20M,输出每位宽度为10bit,则令频率控制字位宽为20bit,f(out)=(频率控制字/2^20)*f(clk)。这样就可以控制输出波形 的频率。

本人用的是QUETUS II开发环境。对于quatus ii 中ROM 可用的数据文件格式为mif。此文件数据可以用MATLAB来产生。

具体产生方法如下:

新建一个.m文件,然后输入:

width=10;//位宽
depth=2^width;//数据深度
t=linspace(0,6.28,depth);
sin_val=sin(t);
cos_val=cos(t);
sin_val=fix(sin_val*(2^width-1)/2+0.5);//四舍五入求结果
cos_val=fix(cos_val*(2^width-1)/2+0.5);

addr=[0:1023];
%存盘
file=fopen('f:/My Works/FPGA Examples/sin.mif','wt');
fprintf(file,'WIDTH=%d;\n',width);
fprintf(file,'DEPTH=%d;\n',depth);
fprintf(file,'ADDRESS_RADIX=UNS;\n');
fprintf(file,'DATA_RADIX=DEC;\n');//这个数据格式最好不要变,不然改动会很麻烦
fprintf(file,'CONTENT BEGIN\n');
for i=1:depth
    fprintf(file,'%d:%d;\n',addr(i),sin_val(i));
end
fprintf(file,'END;\n');
fclose(file);

file=fopen('f:/My Works/FPGA Examples/cos.mif','wt');
fprintf(file,'WIDTH=%d;\n',width);
fprintf(file,'DEPTH=%d;\n',depth);
fprintf(file,'ADDRESS_RADIX=UNS;\n');
fprintf(file,'DATA_RADIX=DEC;\n');
fprintf(file,'CONTENT BEGIN\n');
for i=1:depth
    fprintf(file,'%d:%d;\n',addr(i),cos_val(i));
end
fprintf(file,'END;\n');
fclose(file);

上述程序可以产生可用的mif文件数据,并可以导入ROM中。

最后就是编写verilog代码:

module DDS(clk,rst,
           data,we,ddse,
           sine,cosine
           );
          
input clk;
input rst;
input we;
input ddse;
input[19:0] data;
output[9:0] sine;
output[9:0] cosine;

reg[19:0] ADD_A;
reg[19:0] ADD_B;
reg[9:0] sine_r;
reg[9:0] cosine_r;

wire[9:0] sine_d;
wire[9:0] cosine_d;
wire[9:0] rom_ad;

assign sine = sine_r;
assign cosine = cosine_r;
assign rom_ad = ADD_B[19:10];

always@(posedge clk or negedge rst) begin
          if(!rst) begin ADD_A <= 20'd0;end
                  
          else case(we)
                1'b1: ADD_A <= data;
                1'b0: ADD_A <= 20'd0;
                default: ADD_A <= 20'd0;
               endcase
end

always@(posedge clk or negedge rst) begin
          if(!rst) begin ADD_B <= 20'd0;  end
          else case(ddse)
                1'b1: begin ADD_B <= ADD_A + ADD_B;  end
                1'b0: begin ADD_B <= 20'd0;  end
                default: begin ADD_B <= 20'd0;  end
               endcase
end

always@(posedge clk or negedge rst) begin
          if(!rst) begin sine_r <= 10'd0;
                         cosine_r <= 10'd0;
                   end      
          else case(ddse)
                 1'b1: begin sine_r <= sine_d;
                             cosine_r <= cosine_d;
                       end
                 1'b0: begin sine_r <= 10'd0;
                             cosine_r <= 10'd0;
                       end
                 default: begin sine_r <= 10'd0;
                                cosine_r <= 10'd0;
                          end
               endcase
end

rom_sin rom_sin1(
                 .address(rom_ad),
                 .clock(clk),
                 .q(sine_d)
                 );
                
                
rom_cos rom_cos1(
                 .address(rom_ad),
                 .clock(clk),
                 .q(cosine_d)
                 ); 
                
           
endmodule
20110726202546798.png

从图中可以看到有很多毛刺,但是这是功能仿真,本人初学,不是很懂,希望高手看到能给予帮助与提示。小弟在此多谢!!!!

文章评论5条评论)

登录后参与讨论

用户377235 2014-4-28 21:11

你这个就是门级电路仿真,其实就属于时序仿真了,加入了器件的延迟。

用户1600457 2012-3-23 12:19

你直接例化一个ROM的IP核,里面的初始化数据可以选择你弄好的mif文件

用户1650588 2012-3-20 10:24

小弟刚开始学,看到这篇文章感兴趣了,但是请教一下:你用mtable建立的m文件,产生的mif文件是怎样导入ROM中的啊?

用户1600457 2011-7-29 15:25

是的,功能仿真原本是不该出现毛刺的,我不明白是什么原因,只有时序仿真时候才能出现毛刺,不知是否是我的ROM表里的数据有问题,但是数据保持期间也没错,就是在两个数据之间出现过短时间的跳变,这是什么情况呢

用户1631420 2011-7-29 12:36

从你的仿真图来看,功能仿真不会出现这样的毛刺, 但我在做时序仿真有过类似的毛刺,是布线的延迟造成的数据跳变,都在几ns 范围内,不影响下级建立和保持时间。
相关推荐阅读
用户1600457 2012-04-13 15:53
BOA WEB服务器移植
BOA WEB服务器的移植: 下载源码:最新的为0.94.13,之后再没有更新过 解压后进入文件夹的src/下 ./configure产生Makefile 修改Makefil...
用户1600457 2012-04-09 10:52
【博客大赛】(原创) linux进程间通信之有名管道(FIFO)的简单应用
之前写了无名管道方式的通信,这里就验证下有名管道的通信方式。 有名管道他和普通文件一样可以被读写,而且实际存在于存储设备中,一般也即磁盘,但是有名管道在进程结束后通信的信息就自动消失了,而一般...
用户1600457 2012-04-09 10:51
【博客大赛】(原创)linux进程间通信之无名管道(pipe)的简单应用
最近开始学了linux应用编程,懂了点多进程的基础知识,便跃跃欲试,想立即应用起来,以加深印象,学任何东西重在实践,技术尤为如此。 在linux中创建新的进程的函数为fork(),如果成功返回...
用户1600457 2012-04-08 11:02
【博客大赛】(原创)OK6410按键中断实现
这里本人使用中断形式实现按键的驱动,首先还是先呈上源程序: #include <linux/kernel.h> #include <linux/module.h> ...
用户1600457 2012-04-02 15:55
【博客大赛】(原创)OK6410的第一个驱动程序--点灯
开始自己的嵌入式之旅了。一切神秘的面纱均可以由闪亮的LED揭开。 贴上自己的驱动程序: #include<linux/kernel.h> #include<linux/...
用户1600457 2012-03-23 14:03
nor flash 与 nand flash的比较(转)
NOR和NAND是现在市场上两种主要的非易失闪存技术。Intel于1988年首先开发出NOR Flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面。紧接着,19***,东芝公司发...
我要评论
5
11
关闭 站长推荐上一条 /2 下一条