视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
用FPGA产生正弦信号
2025-10-02 15:11:20 责编:小OO
文档
用FPGA产生正弦信号

上一篇 / 下一篇 2013-12-23 23:45:42 / 个人分类:设计心得

查看( 2015 ) / 评论( 0 ) / 评分( 0 / 0 )

正弦信号,是一个模拟信号。而FPGA只能产生数字信号。因此需要用DA将数字量转化为模拟量。这里采用modelsim的模拟波形显示,就不需要DA模块了。产生正弦信号的方法有很多,这里用的是查找rom的方法,产生正弦信号。

正弦信号,是一个介于-1和1之间的模拟量。而正弦信号是周期变化的,因此这里只需要将半个正弦信号周期的值存进rom里,其余周期可以根据这个半个周期的值变化可得到。用matlab产生正弦信号的值。以0.01为步长,从0采集到pi/2。共158个点。

因此sin的值是小数,而FPGA是不能表示小数的,因此需要将小数扩大,以整数来表示。此次是以12位二进制来显示这整数。扩大的方法就是乘以2^12即可了。12位二进制数最大能表示2^12-1的值。这里用0表示最小值0,2^12-1表示最大值1.则0到1中间的数,就可以扩大到用0到2^12-1中间的数来表示了。例如,0.3,就可用整数0.3*2^12=1228.8,在向上取整,为1228.当然这样表示会有误差。

这里rom用的是ISE的IP rom。将matlab生成的数据放进rom里面。然后依次读取rom的值,即可生成正弦信号了。

首先是生成rom的初始化文件。Xilinx的rom的初始化文件的后缀为.coe。而这个特殊文件有固定的格式。

第一行的10表示下面的数字是10进制的。。 后面的数据是依次存入rom的值,以逗号分开,最后一个以分号结束。前面两行的内容是固定的。

生成初始化文件的matlab如下所示:

fid = fopen('sin_rom.txt','w');

fprintf(fid,'MEMORY_INITIALIZATION_RADIX = 10;\\n');

fprintf(fid,'MEMORY_INITIALIZATION_VECTOR =\\n');

for i = 0:1:pi/2*100

y = sin(i/100);

rom =floor( y * 2^12);

if i == 157

fprintf(fid,'%d;',rom);

else

fprintf(fid,'%d,',rom);

end

if mod(i,10)==0 && i ~= 0

fprintf(fid,'\\n');

end

end

fclose(fid);

生成的文件是.txt文件。将后缀直接改为.coe即可。然后复制到ISE分工程目录下。

初始化文件生成后,剩下就是编写verilog代码。

首先建一个工程,然后新建一个IP。找到rom,打开。设置按如下设置:

这里是设置位宽,采用的是13位(第一位为符号位,后面12位为数据位)来显示sin的值。因此这里是设置为13.深度是因为要存158个值,所以这里设置为158.

这里将刚刚生成的.coe文件载入,如果没有显示红色,就说明正确,否则错误。错误的原因是数据和设定的深度没有对齐。

接下来直接生成就行了。

接着就是编写verilog代码了。

首次查看用ip生成的rom的HDL例化代码.

sin_rom your_instance_name (

.clka(clka), // input clka

.addra(addra), // input [7 : 0] addra

.douta(douta) // output [12 : 0] douta

);

从上面的程序可看出,只需要给时钟信号,和输入地址,就可以了。输出的就是正弦的数字信号了。分析正弦信号,前1/4个周期,地址从0自加,一直加到157(1/4个周期的点数)。然后再自减,减到0。然后进入到负半周了。大家都知道,负数的表示是以二进制的补码来表示的,即绝对值数的二进制取反在加一。

因此要编写地址自加自减的代码,然后再根据地址的值,判断输出的值是正数,还是负数,负数的话,rom的输出值还要取反加一后再输出。

其代码,如下所示:

module sin_top(

input clk, //输入时钟信号

input [9:0] address, //输入地址信号

output reg [12:0] data_out //输出sin的数字值

);

reg [7:0] add;

wire [12:0] douta;

// 以下是判断地址的值

always@ * begin

if( address <= 157 )

add = address;

else if( address <= 315 )

add = 10'd315 - address;

else if( address <= 473 )

add = address - 10'd316;

else if( address <= 631 )

add = 10'd631 - address;

else

add = 0;

end

//例化之前生成的sin_rom

sin_rom u1_sin_rom (

.clka(clk), // input clka

.addra(add), // input [7 : 0] addra

.douta(douta) // output [12 : 0] douta

);

// 判断输出值是正数还是负数。

always@ * begin

if( address <= 315 )

data_out = douta;

else if( address <= 631 )

data_out = ~douta + 1'b1;

else

data_out = 0;

end

endmodule

程序写好了,剩下就是要仿真了。仿真的testbench,只需要输入时钟和地址信号就可以了。地址信号一直加一,直到不小于631,刚好一个周期结束。就返回0值,在继续自加。

测试代码如下所示:

module sin_top_test;

// Inputs

reg clk;

reg [9:0] address;

// Outputs

wire [12:0] data_out;

// Instantiate the Unit Under Test (UUT)

sin_top uut (

.clk(clk),

.address(address),

.data_out(data_out)

);

always#5 clk = ~clk;

initial begin

// Initialize Inputs

clk = 0;

address = 0;

// Wait 100 ns for global reset to finish

while(1)

begin

@(negedge(clk));

if( address < 631 )

address = address + 1;

else

address = 0;

end

end

endmodule

接着用modelsim仿真。因为有用到xilinx的IP ROM。因此用modelsim单独仿真比较麻烦。因此这里是直接在ISE里面调用modelsim,这样比较简单。

只需在设置器件这里将仿真器设置为modelsim就可以了。

然后就可以直接仿真了。

在modelsim里面

将out的format的格式改为analog。这样一会显示的波形就会以模拟的形式显示了。

仿真后,标准的正弦波形就出来了。是不是很酷啊。哈哈哈

这里采用的modelsim仿真,而不是用的是ISE自带的仿真,因为好像ISE的波形不能以模拟波形显示。

这样产生的正弦波,方法比较简单,但是不能调节,调节的话,就要重新生成rom的值。而且还伴随着有误差。可以增大位数,和减小采样间隔时间来减小误差。下载本文

显示全文
专题