北京理工大学VHDL实验报告

课程名称:VHDL硬件描述语言                     实验时间:20##年10月

实验报告

姓    名:

学    号:

专    业:信息工程

班    级:05111003


目录

实验一 时钟分频电路... 3

一、实验概述... 3

二、实验目的... 3

三、实验要求... 3

四、VHDL源代码... 3

五、仿真结果... 4

实验二 序列检测器... 5

一、实验概述... 5

二、实验目的... 5

三、实验要求... 5

四、状态转移图... 6

五、实验代码... 6

六、仿真结果... 8

七、实验感悟... 8

实验三 2FSK解调电路... 9

一、自拟题目... 9

二、题目分析... 9

三、方案一的设计、仿真... 10

1.系统级设计及simulink仿真... 10

2.VHDL实现(子模块描述)... 13

3.MATLAB与Modelsim联合仿真... 16

四、结论与感受... 17

五、附录... 18

1. VHDL源代码(部分)... 18

2. 仿真测试源程序... 20

实验一 时钟分频电路

一、实验概述

    按“Modulus”为模,将“ClkIn”时钟脉冲信号分频,在“ClkOut”上输出。如果“Modulus”是偶数,它产生对称的输出波形,否则,其输出波形的高电平宽度大于低电平宽度。(参考讲义4后实例)

二、实验目的

理解Generic语句参数传递的作用。

三、实验要求

1. 编写源程序          2.用VHDL建立测试平台(Test-Bench)

3. 用ModelSim仿真     4.“Modulus”设不同值观察波形结果

四、VHDL源代码

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

entity top is

       generic(Modulus: integer := 3);              -- 用generic语句设置参数Modulus

       port(

              ClkIn : in std_logic;

              Reset : in std_logic;

              ClkOut : out std_logic);

end top;

architecture Behavioral of top is

begin

       process(ClkIn,Reset)

              variable Count : integer range 0 to Modulus-1;   -- 使用参数Modulus

       begin

              if(Reset = '1') then                          -- 异步复位

                     Count := 0;

                     ClkOut <= '0';

              elsif(rising_edge(ClkIn)) then         -- 同步释放

                     if(Count = Modulus-1) then

                            Count := 0;

                     else

                            Count := Count + 1;

                     end if;

                     if(Count >= Modulus / 2) then --注意观察Modulus为奇偶数时的波形

                            ClkOut <= '1';

                     else

                            ClkOut <= '0';

                     end if;

              end if;

       end process;

end Behavioral   

五、仿真结果

ü  Modulus = 3

ü  Modulus = 4

实验二 序列检测器

一、实验概述

序列检测器可用于检测一组或多组由二进制码组成的脉冲序列信号,这在数字通信领域有广泛的应用。

如下图,RST为复位信号,低电平有效。在时钟CLK作用下,串行二进制码数据从DIN输入。D[7..0]为预先设置的码。当序列检测器连续收到一组串行二进制码后,当这组码出现与检测器中预先设置的码相同码时,A输出1,否则输出0。

二、实验目的

掌握有限状态机的描述。

三、实验要求

1.画出状态转移图                2.编写源程序

3.用VHDL建立测试平台TestBench 4.采用ModelSim进行仿真

四、状态转移图

五、实验代码

VHDL源程序(部分代码)

ü  次态逻辑进程

    COM : process(clk,rst,data_buf,seq_buf,current_state)

    begin

       case current_state is

       when idle =>                    --空闲状态

           next_state <= compare;

           Aout_buf <= '0';

       when compare =>                 --比较状态(工作状态)

           next_state <= compare;

           if(data_buf = seq_buf) then

              Aout_buf <= '1';

           else

              Aout_buf <= '0';

           end if;

       end case;

    end process;

ü  状态寄存器进程

    REG : process(clk,rst)

    begin

       if (rst = '0') then             --异步复位

           seq_buf <= "00000000";

           current_state <= idle;

       elsif(rising_edge(clk)) then

           seq_buf <= seq_buf(6 downto 0) & din_buf; -- 输入din串转并

           current_state <= next_state;

       end if;

    end process;

ü  输出进程

    LATCH : process(clk,rst,Aout_buf)

    begin

       if (rst = '0') then

           Aout <= '0';

       elsif(rising_edge(clk)) then

           Aout <= Aout_buf;

       end if;

    end process;

Test-Bench(部分代码)

ü  定义句柄

    file din_vec : text open read_mode is "din.vec";

    file data_pre : text open read_mode is "data_pre.vec";

    file result : text open write_mode is "d_resuly.out";

ü  激励进程

stim_proc: process

    variable din_line : line;       -- 输入比特流(按行计)

    variable din_bit : std_logic;   -- 输入比特流(按比特计)

    variable data_line : line;      -- 预置序列(按行计)

    variable data_byte : std_logic_vector (7 downto 0); --预置序列

    variable out_line : line;       -- 输出结果(端口A的输出)

   begin

       while not endfile(din_vec) loop

           clk <= '0';

           readline(din_vec,din_line);

           read(din_line,din_bit);

           readline(data_pre,data_line);

           read(data_line,data_byte);

           data <= data_byte;

           din <= din_bit;

           wait for 10 ns;

           clk <= '1';

           write(out_line,string'("Time="));  -- 向.out文件中写入结果

           write(out_line,now);

           write(out_line,string'(": The result is : "));

           write(out_line,Aout);

           write(out_line,string'(". (1-->mathc;0-->not match)"));

           writeline(result,out_line);

           wait for 10 ns;

       end loop;

      wait;

   end process;

六、仿真结果

如下图所示(见下页),预置序列“10101010”从初始时刻开始就给到输入端“data”,之后端口“din”的数据利用TEXTIO模式从.vec文件中读取。

经过一段时间,到达时刻后,A端口输出持续一个时钟周期高电平,反观此前8个时钟周期的输入数据din,发现恰好是“10101010”。

    继续观察发现时刻A端口也输出了持续1个时钟周期的高电平,经检验之前8个时钟周期的din也符合“10101010”。

    另一方面,当某8个连续时钟周期的din不满足“10101010”的组合时,A端口的输出均为‘0’(尽管没有穷举非10101010的情况)。因此可以认为该状态机的功能符合设计要求。

Figure 1 Modelsim仿真结果

七、实验感悟

在本次试验中,我学习了有限状态机(Mealy型)的编写方法。

1.画状态转移图

2.选择合适的描述风格(我选择的描述风格是三个进程,分别是次态逻辑进程、状态寄存器进程、输出逻辑进程)。

3.编写VHDL代码。

通过以上若干步骤,我基本掌握了有限状态机的描述。

实验三 2FSK解调电路

一、自拟题目

设计并实现一个2FSK解调器。外部一个2FSK调制的正弦信号,幅度为0~3V,f_1=500Hz,f_2=2KHz.使用ADC采集信号,在FPGA内做2FSK解调(相干或非相干),在示波器上观察输出信号,并且与信号源输入波形对比。

      仿真平台:Matlab和Modelsim。

二、题目分析

       2FSK信号是用载波频率的变化表征被传递信息的状态的,被调制载波的频率随着二进制信号的0、1状态而变化,即载频为代表传“0“,即载频为代表传“1“。

      我们的任务是解调上述2FSK信号,从中获取传输的二进制信息。

解调的方式有相干解调和非相干解调两种。其中相干解调的系统框图如下所示,它是将信号先前值滤波得到两路载波分量,再用乘法器对信号进行非线性变换,混频得到有用的低频分量和多余的高频分量,接着经过低通滤波器后,根据提取到的位同步时钟抽样判决,最终获得解调输出。

图表 2-1 相干解调实现框图

      另一种是非相干解调。在模拟电路中实现非相干解调的方法常常是利用锁相环路的鉴频功能,但在数字电路中,数字鉴频的方法更为可取。

三、方案一的设计、仿真

1.系统级设计及simulink仿真

1.1 设计

2FSK调制信号生成模块:

为了在模型中获得2FSK调制波形,我们设计了下面的模块,两个离散正弦源产生的信号分别与矩形波及其反相信号相乘然后相加。再把它封装成一个Mask,Mask与外部的接口参数一共五项,如下图所示,使用时只须输入下列数据就可以得到想要的2FSK波形。

图表 3-1 调制信号源及其封装效果

2FSK信号解调模块:

该模块中,2FSK调制波形首先分别与两路不同频率的本振信号做乘积混频,得到的结果经过FIR低通滤波器(设计方法及参数见后)滤除高频分量后,进入门限判决模块(内部结构见后),最终从门限判决器的输出端口得到2FSK的解调信号。

图表 3-2 解调系统框图

FIR低通滤波器:

      在MATLAB中调用DSP System工具箱中的Filter Design & Analyze Tool,在下图所示的界面中设计滤波器。考虑到信号经过滤波器后要保持线性相位,以及硬件设备的资源问题,我们采用FIR滤波器;确定参数时,首先考虑调制信号中高频分量为2KHz,为了满足Nyquist采样定理以及考虑到工程实践中的实际效果,我们确定采样频率为10KHz,又由于有用的低频分量频率非常低,且需要滤除1KHz及更高的频率分量,所以我们把通带设为50Hz,阻带频率设为500Hz,最终设计结果如下图所示。把滤波器的系数导出到Workspace中,再赋给simulink中的滤波器模型即可。(此处滤波器为56阶,但在VHDL实现时考虑到复杂程度及资源问题把阶数降到了46阶)。

图表 3-3 FIR滤波器参数设计

1.2 仿真

      假设本振信号的频率非常准确,当2FSK输入信号理想时,得到如下结果:

图表 3-4 理想情况结果

我们只观察1~4图形。图形一是2FSK调制波形,窗口2是与500Hz的本振信号混频后的波形,窗口3是经过FIR低通滤波器后的结果,窗口4是经过门限判决后最后得到的解调信号。当码元频率为时,混频、滤波后其对应的输出是高电平1;与之相反,输出是低电平0。这样的结果符合预期。

2.VHDL实现(子模块描述)

图表 3-6  RTL模型

       输入输出端口定义:

      输入端口:clk——系统总时钟

                    bfsk_in ——基带仿真信号输入

输出端口:bfsk_out——FSK信号解调输出

                   sig_filter——滤波器输出

                 bfsk_in_com——基于仿真信号

                 sig_mul——混频器输出

                 sig_sin_osc——本振信号输出   

2.1 10kHz时钟模块

      此模块完成时钟分频,为后续模块提供统一的采样频率。

2.2 NCO模块

相干解调方法中,如何产生准确本振信号非常重要。

下图是NCO的参数设置界面,在参考多方资料及仿真尝试后,我们确定了如下参数,并生成NCO核及其VHD文件。

图表 3-7 NCO参数设置

      NCO的VHD实体与外部的接口有7个:

           phi_inc_i  : IN STD_LOGIC_VECTOR (31 DOWNTO 0);

       clk : IN STD_LOGIC;

       reset_n    : IN STD_LOGIC;

       clken  : IN STD_LOGIC;

       fsin_o : OUT STD_LOGIC_VECTOR (9 DOWNTO 0);

       fcos_o : OUT STD_LOGIC_VECTOR (9 DOWNTO 0);

       out_valid  : OUT STD_LOGIC

其中输入端口需要的设置是:

(1)给“phi_inc_i”42950的相位步进值。

      (2)给“clk“一个50MHz的时钟。

      (3)“reset_n“、”clken“置高。

实际用到的输出端口是“fsin_o“,它输出的是500Hz的正弦信号。

2.3 乘法器模块

      将2FSK信号与NCO输出信号混频,得到的信号包含直有用的直流信号和无用的高频信号。

2.4FIR数字滤波器

我们按照前文中所述的方法设计出FIR低通滤波器后,按照其系数自己编写了FIR滤波器的VHDL程序。

为了提高运算效率、节省资源,我们采用了“行波流水“的存储方式来实现各级的延时器。即在每个时钟周期将delay_pipeline(0)到delay_pipeline(44)整体移位到delay_pipeline(1)~(45), 然后再把这一时刻的输入信号存放到delay_pipeline(0)当中。这样,Quaturs综合出的电路是46个8位的并行移位寄存器,每个时钟周期进行一次整体移位。

另外,根据DSP所学,FIR数字滤波器具有线性相位的特性,其系统函数的系数是“前后对称“的,我们利用这一点可以将乘法器的个数减半,极大地节省了资源(原理如下图所示)。

http://files.chinaaet.com/images/2011/04/27/12217046853959.bmp

图表 3-8 FIR滤波器框图

      延时器的代码如下:

       PROCESS (clk)

       BEGIN

IF (clk'event AND clk = '1') THEN

                     IF (clk_enable = '1') THEN

                            delay_pipeline(0) <= filter_in;

                            delay_pipeline(1 TO 45) <= delay_pipeline(0 TO 44);

                     END IF;

              END IF;

       END PROCESS;

2.5 门限判决模块

      该模块对滤波器的输出做0、1判决,得到最终的解调输出信号。

3.MATLAB与Modelsim联合仿真

      3.1 仿真过程描述

在仿真阶段,我们利用MATLAB生成bfsk调制信号的数据存放在.vec文本文档中,并用在testbench中书写相关代码,使Modelsim读入这些数据作为系统的输入信号;仿真过程中,由Modelsim实时地导出解调信号的数据,存放在相应.out文本文档中;仿真结束后,用MATLAB读取.out文档中的数据,绘制出解调后的波形。这样就实现了MATLAB和Modelsim的联动仿真,极大提高了仿真、分析的效率。

3.2 仿真结果

图表 3-9  modelsim仿真输出波形

图表 3-10  MATLAB的GUI绘图结果

四、结论与感受

观察图3-9发现,modelsim仿真结果和设计系统时simulink仿真的结果是完全一致的,这表明基于VHDL的程序编写是正确的。  

对比图3-9和图3-10发现, modelsim输出的数据文件被matlab的gui仿真工具读入并且绘图后的结果和modelsim仿真结果是一致的,这表明基于VHDL的textIO仿真是正确的。

经过这次实验,我练习了层次化设计方法、用TEXTIO模式仿真,收获颇丰。

五、附录

1. VHDL源代码(部分)

1.1 顶层文件

       ……

Entity filter_test is

    port (

       clk : in std_logic;

       bfsk_in : in std_logic_vector (7 downto 0);

       bfsk_out : out std_logic;

       bfsk_in_com : out std_logic_vector (7 downto 0);

       sig_sin_osc : out std_logic_vector (7 downto 0);

       sig_mul : out std_logic_vector (7 downto 0);

       sig_filter : out std_logic_vector (7 downto 0)

       );

end filter_test;

architecture behavior of filter_test is

    signal clk_enable : std_logic;

    signal rst_fir : std_logic;

    signal fs_10khz : std_logic;

    signal filter_in : std_logic_vector (7 downto 0);

    signal filter_out : std_logic_vector (7 downto 0);

    signal sin_2khz : std_logic_vector (7 downto 0);

    signal clk_50hz : std_logic;

    signal bfsk_in_com1 : std_logic_vector (7 downto 0) := "00000000";

begin

    clk_enable <= '1';

    rst_fir <= '0';

    bfsk_in_com1 <= bfsk_in - "10000000";

    bfsk_in_com <= bfsk_in_com1;

    u1 : sampling_freq_10khz port map (clk,fs_10khz);

    u2 : sin_osc port map (clk,fs_10khz,sin_2khz);

    sig_sin_osc <= sin_2khz;

    u3 : multiplier port map (fs_10khz,bfsk_in_com1,sin_2khz,filter_in);

    sig_mul <= filter_in;

    u4 : fir_new port map (fs_10khz,clk_enable,rst_fir,filter_in,filter_out

    );

    sig_filter <= filter_out;

    u5 : threshold_decision port map (fs_10khz,filter_out,bfsk_out);

end behavior;

1.2 时钟分频子模块(省略)

1.3 乘法器子模块

       ……

entity multiplier is

    port (

       fs_clk : in std_logic;

       signal1, signal2 : in std_logic_vector (7 downto 0);

       signal3 : out std_logic_vector (7 downto 0)

);

end multiplier;

architecture bh of multiplier is

    signal buf_sig : std_logic_vector (15 downto 0) := "0000000000000000";

begin

    process (fs_clk)

    begin

       if (rising_edge(fs_clk)) then

           buf_sig <= signal1 * signal2;

       end if;

    end process;

    signal3 <= buf_sig(15 downto 8);

end bh;

1.4 FIR滤波器子模块(省略)

1.5 门限判决子模块

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_signed.all;

entity threshold_decision is

    port (

       fs_clk : in std_logic;

       signal_in : in std_logic_vector (7 downto 0);

       signal_out : out std_logic := '0'

    );

end threshold_decision;

architecture bh of threshold_decision is

begin

    process (fs_clk, signal_in)

    begin

       if (rising_edge(fs_clk)) then

           if (signal_in > "11101100") then

              signal_out <= '0';

           else

              signal_out <= '1';

           end if;

       end if;

    end process;

end bh;

1.6 NCO子模块(IP核,省略)

2. 仿真测试源程序

     ……

architecture bhv of filter_test_tb is

    signal clk : std_logic := '1';

    signal bfsk_in : std_logic_vector (7 downto 0);

    signal bfsk_in_com : std_logic_vector (7 downto 0);

    signal bfsk_out : std_logic;

    signal sig_sin_osc : std_logic_vector (7 downto 0);

    signal sig_mul : std_logic_vector (7 downto 0);

    signal sig_filter : std_logic_vector (7 downto 0);

    component filter_test is

       port (

           clk : in std_logic;

           bfsk_in : in std_logic_vector (7 downto 0);

           bfsk_out : out std_logic;

           bfsk_in_com : out std_logic_vector (7 downto 0);

           sig_sin_osc : out std_logic_vector (7 downto 0);

           sig_mul : out std_logic_vector (7 downto 0);

           sig_filter : out std_logic_vector (7 downto 0)

       );

    end component;

   

    file bfsk_in_file : text open read_mode is "bfsk_in.vec";

    file bfsk_in_com_file : text open write_mode is "bfsk_in_com.vec";

    file sig_sin_osc_file : text open write_mode is "sig_sin_osc.out";

    file sig_mul_file : text open write_mode is "sig_mul.out";

    file sig_filter_file : text open write_mode is "sig_filter.out";

    file bfsk_out_file : text open write_mode is "bfsk_out.out";

begin

    i1 : filter_test port map (clk => clk,bfsk_in => bfsk_in,bfsk_out => bfsk_out,bfsk_in_com => bfsk_in_com,sig_filter => sig_filter,sig_mul => sig_mul,sig_sin_osc => sig_sin_osc);

    clock : process

    begin

       wait for 10 ns;

       clk <= not clk;

    end process clock;

    sim_in : process

       variable buf_in : line;

       variable vec_in : std_logic_vector (7 downto 0);

    begin

       while not endfile(bfsk_in_file) loop

           readline (bfsk_in_file, buf_in);

           read(buf_in, vec_in);

           bfsk_in <= vec_in;

           wait for 100000 ns;

       end loop;

       wait;

    end process sim_in;

    sim_out : process

       variable buf_sig_filter, buf_sig_mul, buf_sig_sin_osc, buf_bfsk_out, buf_bfsk_in_com : line;

    begin

           wait for 100000 ns;

          

           write (buf_bfsk_in_com, bfsk_in_com);

           writeline (bfsk_in_com_file, buf_bfsk_in_com);

          

           write (buf_sig_filter, sig_filter);

           writeline (sig_filter_file, buf_sig_filter);

          

           write (buf_sig_mul, sig_mul);

           writeline (sig_mul_file, buf_sig_mul);

          

           write (buf_sig_sin_osc, sig_sin_osc);

           writeline (sig_sin_osc_file, buf_sig_sin_osc);

          

           write (buf_bfsk_out, bfsk_out);

           writeline (bfsk_out_file, buf_bfsk_out); 

    end process sim_out;

end bhv;

相关推荐