Verillog学习笔记——可编程波形发生器

///Verillog学习笔记——可编程波形发生器

Verillog学习笔记——可编程波形发生器

咕咕咕了很久,终于想到要来写第二篇学习笔记了……这次的实验目的是使用DAC0832来产生自定义波形。

概述

首先是这次要用到的两个器件:DAC0832和TL084,通过由这两个器件组成的电路我们就可以达到将数字信号转换为模拟信号的目的,来看一下电路图:

图上标注为DAC0830,这两款芯片使用方式相同
对于电路图的连接不再做过多赘述。我使用了DAC0830的直通模式来进行实验。在正确连接的情况下,FPGA所需要做的就只是按照预期的波形输出对应的电压值。不过要注意的是,DAC0832的反应时间约为1 μs,所以如果你给的数值变化速度高于了这个值,那可能无法看到波形正确显示。不过以这个模块的精度,我想也完全用不上这么精确的波形输入……

接下来就进入最关键的部分:通过FPGA输出波形数值给DAC0830。因为我希望能够输出一个可调的波形,所以我将使用ROM来存储希望显示的波形,存储方式就是在一个波形上等间距地取一定数量的点,然后将这些点按顺序保存到ROM中,这样就只需通过固定频率的时钟按顺序读出这些点,就可以输出我们想要的波形了。当然,这种做法所输出的波形一定是会有失真的,所取的点越密集所产生的失真也越小。同样波形的精确度还受器件以及电路等多种因素的影响,所以想做一个高精度的波形发生器也是件很麻烦的事情……

正片

1.配置ROM

那么接下来就开始正式编写模块了,首先要做的就是将ROM加入工程中。ROM可以直接调用Vivado中的IP,做法很简单,在左侧工具栏选择IP Catalog ,然后搜索ROM,选择结果中的Block Menory Generator双击打开它进入配置页面,然后开始配置需要产生的波形。

这次实验我先来产生一个正弦波,先依据需要对ROM进行配置。

首先是Basic选项,这里只需要做一项改动,将Memory Type修改为Single Port ROM即可,因为我只需要读取预设好的数据,所以其他功能都可以关掉。修改好的配置页面应该是这样的:

然后进入Port A Options,这里需要修改数据位宽以及数据深度,数据位宽按照所能达到的最大值来设置,依照DAC0832的电压转换公式,输出的数据应该在0~256的范围内,所以将数据位宽Port A Width设置为8(最大值为255)。我准备采样4096个点,所以将数据深度Port A Depth设置为4096。还有一个可选项目,可以将Enable Port Type修改为Always Enabled修改完的窗口应该长这样:
我没有修改Enable Port Type

最后一个配置项目,Other Options。这里要配置的东西也很简单,我们只需要配置coe文件就好了,这里既可以直接在Vivado中直接配置,也可以写完之后添加到工程中,下面是配置完成后的截图,接下来讲解如何制作coe文件。

2.制作coe文件

首先来看看coe文件的格式:

memory_initialization_radix=10;
memory_initialization_vector=127,127;

这个文件储存了两个值为127的数。

文件编写十分简单,第一行表示文件采用什么样的进制,十进制就写10,八进制就写8,以此类推。第二行就是你需要储存的数,依次排列,用逗号隔开就行了。

知道了格式后,接下来就是生成数据了,人工计算4096个点显然是不现实的,所以需要借助其他方法来实现,因为前阶段我正好在学MATLAB,所以这次就正好通过MATLAB来实现数据的生成,以下是生成脚本:

clc;
clear;
x=linspace(0,2*pi,4096);            %取0到2π中的4096个点
y=127*sin(x)+127;                   %依据公式计算对应值
output=fopen('output.txt','wt');
for i=1:4095
    fprintf(output,'%.0f,',y(i));   %以整数形式输入文件
end
i=i+1;
fprintf(output,'%.0f;',y(i));       %按照coe文件格式在最后加上分号
fclose(output);

然后将output.txt中的数据粘贴到coe文件的第二行就大功告成了。这里提供一个我生成的coe文件:

下载 “正弦波4096点coe文件” wave_coe_0.coe – 已下载45次 – 14 KB

Vivado还为我们提供了一种更加方便的coe文件编辑方法,只需要在选择coe文件时点击edit选项,就会弹出一个输入coe文件两个参数的窗口,然后直接按照coe文件的格式填写这两个参数就好了,如图:

至此,coe文件的生成就告一段落了。

3.编写主模块代码

最后就是编写主模块的代码了,这一块非常简单,利用一个时钟触发的计数器来循环读取ROM中的数据就可以了,在我的代码中我添加了可以在两种不固波形中切换的功能:

module waveform_generator(
    input clk,select,                 //clk外部时钟,select按键脉冲
    output reg[7:0]wave               //wave波形数值输出
    );
    reg [11:0]counter;                //地址计数器
    reg[1:0]select_reg;               //信号选择
    wire [7:0]ram_out_0;
    wire [7:0]ram_out_1;
    initial begin                      //初始化
        wave=0;
        counter=0;
        select_reg=0;
    end
    always @(posedge clk) begin        //主模块
        counter=counter+1;             //地址计数增加
        case(select_reg)               //选择波形
            0:wave=ram_out_0;          //把ROM波形数值输出
            1:wave=ram_out_1;
            default:wave=0;
        endcase
    end
    always @(posedge select) begin     //信号选择按键脉冲处理
        select_reg=select_reg+1;
        if(select_reg==2)
            select_reg=0;
    end
    blk_mem_gen_0 blk_mem_gen_0(        //调用ROM
        .clka(clk),                     //ROM更新时钟,与主模块时钟相同
        .addra(counter),                //使用地址计数器选择地址
        .douta(ram_out_0),              //输出数值
        .ena(1)                         //使能ROM
    );
     blk_mem_gen_1 blk_mem_gen_1(
        .clka(clk),
        .addra(counter),
        .douta(ram_out_1),
        .ena(1)
    );
endmodule

至此,可编程波形发生器全部完成。

发布者 | 2019-12-17T16:29:30+08:00 十二月 14th, 2019|verillog, 学习笔记|0条评论

关于作者

坚强大概——并不是指的的结果,而是迈向某个目标的过程吧。

发表评论

召唤伊斯特瓦尔