FPGA 第三次实验


FPGA第三次实验

自己摸索着写的,如果有错误或者疑问一定要及时告诉我。

我写的程序自己感觉仿真没有太大问题了,但是不知道烧录进去会不会出现问题,请大家擦亮眼睛,不要“太相信我”

更新日志

10.22 23:47

第二个程序似乎可以直接原来的课本的程序,只是在配置管脚的时候需要把低四位和高四位分给两个hex按键,所以就不需要再分成两个输入然后再拼接回来了!!!(谢老板的idea,点赞)

10.23 0:18

管脚分配完毕,可以看那两个题目的管脚分配部分,一样,如果有错误请一定要告诉我。

10.24 1:48

将仿真时每次输出上升沿附近会有不确定电平的情况解决,方法只是用源程序重新编译了一下。现在还不清楚这种问题对于实际烧录之后有没有影响。

1

题目及检查要求

题目要求

检查要求

第一题比较简单,只需要将书本上的两个程序块抄上来就行了。不过要注意,一定要把$pulse.vhd$文件设置为顶层文件,这样编译生成的器件才是正确的,另外,设置的高低脉冲的时间不是输入的那个值,而是需要用$0XFF$减去该值

0XFF-输入

程序源码

LCNT8.vhd

LIBRARY IEEE ;
USE IEEE.STD_LOGIC_1164.ALL ;
ENTITY LCNT8 IS                                     -- 8位可自加载加法计数器
PORT ( CLK, LD : IN STD_LOGIC ;                     -- 工作时钟/预置值加载信号
                 D : IN INTEGER RANGE 0 TO 255 ; --8位分频预置数
              CAO : OUT STD_LOGIC ) ;             --计数溢出输出
END LCNT8 ;
ARCHITECTURE behav OF LCNT8 IS
        SIGNAL COUNT : INTEGER RANGE 0 TO 255 ;--8位计数器设置
BEGIN
    PROCESS( CLK )
    BEGIN
        IF CLK'EVENT AND CLK = '1' THEN
            IF LD = '1' THEN COUNT <= D; --LD为高电平时加载预置数
                ELSE  COUNT <= COUNT + 1; --否则继续计数
            END IF;
        END IF;
    END PROCESS;
    PROCESS ( COUNT )
    BEGIN
        IF COUNT = 255 THEN CAO <= '1'; --计数满后,置位溢出位
        ELSE                      CAO <= '0';
        END IF;
    END PROCESS;
END behav;

PULSE.vhd

LIBRARY IEEE ;                        --正负脉宽数控调制信号发生器顶层文件
USE IEEE.STD_LOGIC_1164.ALL ;
ENTITY PULSE IS
    PORT ( CLK : IN STD_LOGIC;                              --计数时钟
             A,B : IN STD_LOGIC_VECTOR(7 DOWNTO 0);  --8位计数预置数
          PSOUT : OUT STD_LOGIC ) ;                      --计数溢出并分频输出
END PULSE;
ARCHITECTURE mixed OF PULSE IS
COMPONENT LCNT8 
        PORT ( CLK,LD : IN STD_LOGIC;
                    D : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
                  CAO : OUT STD_LOGIC );
END COMPONENT;
    SIGNAL CAO1,CAO2 : STD_LOGIC;
    SIGNAL LD1,LD2   : STD_LOGIC;
    SIGNAL PSINT       : STD_LOGIC;
BEGIN
   U1 : LCNT8 PORT MAP( CLK => CLK, LD => LD1,
                                  D => A,  CAO => CAO1 );
   U2 : LCNT8 PORT MAP( CLK => CLK, LD => LD2,
                                  D => B,  CAO => CAO2 );
    PROCESS(CAO1, CAO2)                                            --计数溢出二分频进程
    BEGIN
        IF CAO1 = '1' THEN PSINT <= '0';                        --异步复位
        ELSIF CAO2'EVENT AND CAO2 = '1' THEN PSINT <= '1' ;
        END IF ;
    END PROCESS;
    LD1 <= NOT PSINT ; LD2 <= PSINT ; PSOUT <= PSINT ;
END mixed;

对程序的理解

先说一下第一个文件,在所有的题目里都原封不动地用了$LCNT8.vhd$,也就是每个题目程序都是在运用计数器的基础上实现的。

重点看一下process:

    PROCESS( CLK )
    BEGIN
        IF CLK'EVENT AND CLK = '1' THEN
            IF LD = '1' THEN COUNT <= D; --LD为高电平时加载预置数
                ELSE  COUNT <= COUNT + 1; --否则继续计数
            END IF;
        END IF;
    END PROCESS;

    PROCESS ( COUNT )
    BEGIN
        IF COUNT = 255 THEN CAO <= '1'; --计数满后,置位溢出位
        ELSE                      CAO <= '0';
        END IF;
    END PROCESS;

这两个process的功能如下(我尽量表达的明白一些):

首先要明白这个vhd文件生成的器件是一个计数器,而计数器的计数是由时钟信号CLK驱动的。所以看第一个process,当CLK变化的的时候,检测CLK的高电平,然后如果LD=1的话,意思就是该置数了,否则LD=0的话,执行的操作是计数值+1。

而每次计数值+1之后,都会进入第二个进程,判断COUNT是否是255,这个进程的功能就是当COUNT=255的时候就代表计数器马上就要溢出了,然后就把溢出位置位1,当COUNT≠255的时候,溢出位一直为0。

综上来看,这就是一个典型的计数器,计数到255时溢出,溢出位置1;同时此计数器有置数功能,当LD=1的时候就把D中的数置入,以改变从开始到溢出的时间。

之后LCNT8.vhd程序内容不再专门做阐述

LD是允许置数位,当LD为高电平时就把输入D存的数置入到计数器。功能表现为,当不置数的话,计数器就从0计数到255,十六进制就是0xff;如果LD为高电平允许把D里的数置入之后,计数就是从D里的数开始直到255,比如如果D<=10,LD=1之后,就是从10计数到255,而不是从0到255。

然后就到了重要的地方了,也就是文件pulse.vhd。

首先可以很明显地看出,pulse里用了两个lcnt8计数器,信号对应关系如下:

U1 : LCNT8 PORT MAP( CLK => CLK, LD => LD1,
                     D => A,  CAO => CAO1 );
U2 : LCNT8 PORT MAP( CLK => CLK, LD => LD2,
                     D => B,  CAO => CAO2 );

下面贴一下这个程序里最重要的程序块,也是功能的体现:

    PROCESS(CAO1, CAO2)                                            --计数溢出二分频进程
    BEGIN
        IF CAO1 = '1' THEN PSINT <= '0';                        --异步复位
        ELSIF CAO2'EVENT AND CAO2 = '1' THEN PSINT <= '1' ;
        END IF ;
    END PROCESS;
    LD1 <= NOT PSINT ; LD2 <= PSINT ; PSOUT <= PSINT ;

因为这个程序是用了两个计数器,所以就有两个计数器溢出的信号CAO1,CAO2。如果有一个计数器溢出之后,就会进入PROCESS,然后会检测是哪个计数器溢出的,如果是U1,那么PSINT置为0,信号PSINT的作用可以由最后一行程序体现,先拿U1溢出之后的情况来说,当U1溢出的时候,CAO1=1,然后PSINT会为0,这样的话从最后一行程序可以看出,LD1会为1,LD2,PSOUT会为0,此时到了一个最关键的地方,可以从LCNT8.vhd文件里看出,在LD1为1的时候,U1将会一直置数,计数位COUNT将不会+1,这样的话COUNT将一直为输入的D的内容,从外面来看,这个计数器仿佛静止了,一直卡在COUNT=D处不动,而此时LD2=0,U2还会一直计数,U2的COUNT还会一直+1,直到溢出,在U2溢出之前,输出PSOUT将一直是0。而等到U2溢出之后,CAO2=1,再次进入PROCESS,然后PSINT会变成1,这样情况和刚才的相反,LD1为0,LD2,PSOUT为1,所以U2将会“停止”,U1正常计数,输出PSOUT是高电平。

上面讲的可能会有点抽象,下面举一个例子(假设置数A=0x20,B=0xf0):因为一开始是没有置数的,所以U1和U2是同时从0开始计数,同时溢出,但是由于IF-ELSE函数具有优先级性,同时溢出之后PSINT将为0,此时U1一直置数,不再计数,U2将会继续计数,直到溢出,溢出之后PSINT=1,U2一直置数不再计数,U1从0x20开始计数,高电平持续时间为0xff-0x20。此时才真正意义上地开始高低电平脉冲的循环。U1溢出之后PSINT=0,U1”停止”在0x20,U2从0xf0开始计数,输出POUT=0,即此时输出为低电平,持续时间为0xf0到0xff计数的时间。综合前几句话就可以得出结论,高电平持续时间为0xff-0x20(0xff-A),低电平持续时间为0xff-0xf0(0xff-B)。此段前半部分也是当仿真时一开始会有一段时间输出均为低电平的原因。

仿真结果

用上面的程序仿真的结果如下:

①. A=11110000,B=00000000

image-20201023012644947

②. A=00000000,B=11110000

仿真结果2

2(2)

题目及检查要求

题目要求

检查要求

此题目和第一题的区别只是在于需要把程序烧进板子了,然后调整高低脉宽的输入就不能是8位的,因为板子上没有8位hex的按键,只有4位hex按键,所以我们需要把1个8位输入改成2个四位输入即可。

程序源码

LCNT8.vhd

LIBRARY IEEE ;
USE IEEE.STD_LOGIC_1164.ALL ;
ENTITY LCNT8 IS                            -- 8位可自加载加法计数器
PORT ( CLK, LD : IN STD_LOGIC ;        -- 工作时钟/预置值加载信号
                 D : IN INTEGER RANGE 0 TO 255 ; --8位分频预置数
              CAO : OUT STD_LOGIC ) ;                --计数溢出输出
END LCNT8 ;
ARCHITECTURE behav OF LCNT8 IS
        SIGNAL COUNT : INTEGER RANGE 0 TO 255 ;--8位计数器设置
BEGIN
    PROCESS( CLK )
    BEGIN
        IF CLK'EVENT AND CLK = '1' THEN
            IF LD = '1' THEN COUNT <= D; --LD为高电平时加载预置数
                ELSE            COUNT <= COUNT + 1; --否则继续计数
            END IF;
        END IF;
    END PROCESS;
    PROCESS ( COUNT )
    BEGIN
        IF COUNT = 255 THEN CAO <= '1'; --计数满后,置位溢出位
        ELSE                      CAO <= '0';
        END IF;
    END PROCESS;
END behav;

PULSE.vhd

LIBRARY IEEE ;                        --正负脉宽数控调制信号发生器顶层文件
USE IEEE.STD_LOGIC_1164.ALL ;
ENTITY PULSE IS
    PORT ( CLK : IN STD_LOGIC;                              --计数时钟
          PSOUT : OUT STD_LOGIC;                   --计数溢出并分频输出
              A1 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);   --设置高电平周期低四位
              A2 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);   --设置高电平周期高四位
              B1 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);   --设置低电平周期低四位
              B2 : IN STD_LOGIC_VECTOR(3 DOWNTO 0) ) ;--设置低电平周期高四位    
END PULSE;
ARCHITECTURE mixed OF PULSE IS
COMPONENT LCNT8 
        PORT ( CLK,LD : IN STD_LOGIC;
                        D : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
                     CAO : OUT STD_LOGIC );
END COMPONENT;
    SIGNAL CAO1,CAO2 : STD_LOGIC;
    SIGNAL LD1,LD2   : STD_LOGIC;
    SIGNAL PSINT       : STD_LOGIC;
    SIGNAL AA1,AA2,BB1,BB2 : STD_LOGIC_VECTOR(3 DOWNTO 0);
    SIGNAL AA,BB       : STD_LOGIC_VECTOR(7 DOWNTO 0);
    -- SIGNAL A,B           : STD_LOGIC_VECTOR(7 DOWNTO 0);  --8位计数预置数
BEGIN
    U1 : LCNT8 PORT MAP( CLK => CLK, LD => LD1,
                                  D => AA,  CAO => CAO1 );
   U2 : LCNT8 PORT MAP( CLK => CLK, LD => LD2,
                                  D => BB,  CAO => CAO2 );
    AA1 <= A1; AA2 <= A2; BB1 <= B1; BB2 <= B2;    --将输入转变为信号SIGNAL
    AA <= AA2&AA1;  BB <= BB2&BB1;                   --将输入高四位和低四位拼接成八位
    PROCESS(CAO1, CAO2)                               --计数溢出二分频进程
    BEGIN
            IF CAO1 = '1' THEN PSINT <= '0';       --异步复位
            ELSIF CAO2'EVENT AND CAO2 = '1' THEN PSINT <= '1' ;
            END IF;
    END PROCESS;
    LD1 <= NOT PSINT ; LD2 <= PSINT ; PSOUT <= PSINT ;
END mixed;

在写上面的程序的时候我遇到了一个问题,就是当把两个输入的四位直接拼成八位然后直接赋给输出的话,结果是错误的,后来稍微转变了一下思路,先将四个四位的输入赋给了信号,然后将四位信号进行拼接成八位进行输出之后,问题解决。但是其中的问题究竟出在哪里,还是有些疑惑的。

紧急更新

紧急更新(10.22 23:47)!!!

第二个程序似乎可以直接原来的课本的程序,只是在配置管脚的时候需要把低四位和高四位分给两个hex按键,所以就不需要再分成两个输入然后再拼接回来了!!!(谢老板的idea,点赞)

仿真结果

用上面的程序的仿真结果如下:

①. A=00100000,B=10100000

仿真结果1

②. A=10100000,B=00100000

仿真结果2

管脚分配

利用实验箱结构图NO.1,四个四位输入接在四个hex按键上,输出接在蜂鸣器上,时钟接在CLOCK0,跳线帽接在256HZ。

实验箱结构图NO.1

更:

管脚分配

2(3)

题目及检查要求

题目要求

检查要求

此题目需要在上一问程序的基础上进行写,首先最简单的,把微调后的四个四位信号进行输出显示;然后需要有一个微调按键和初始化按键,因为调的是高电平,并且是每按一次高电平就加宽一点,所以微调按键需要针对A进行调整,并且需要使A减去一个小的数,以满足让高电平加宽一点的条件(对“减去一个数”有疑惑的可以看上面第一个题里的描述)。

程序源码

LCNT8.vhd

LIBRARY IEEE ;
USE IEEE.STD_LOGIC_1164.ALL ;
ENTITY LCNT8 IS                                     -- 8位可自加载加法计数器
PORT ( CLK, LD : IN STD_LOGIC ;                -- 工作时钟/预置值加载信号
                 D : IN INTEGER RANGE 0 TO 255 ; --8位分频预置数
              CAO : OUT STD_LOGIC ) ;                --计数溢出输出
END LCNT8 ;
ARCHITECTURE behav OF LCNT8 IS
        SIGNAL COUNT : INTEGER RANGE 0 TO 255 ;--8位计数器设置
BEGIN
    PROCESS( CLK )
    BEGIN
        IF CLK'EVENT AND CLK = '1' THEN
            IF LD = '1' THEN COUNT <= D; --LD为高电平时加载预置数
                ELSE            COUNT <= COUNT + 1; --否则继续计数
            END IF;
        END IF;
    END PROCESS;
    PROCESS ( COUNT )
    BEGIN
        IF COUNT = 255 THEN CAO <= '1'; --计数满后,置位溢出位
        ELSE                      CAO <= '0';
        END IF;
    END PROCESS;
END behav;

PULSE.vhd

LIBRARY IEEE ;                        --正负脉宽数控调制信号发生器顶层文件
USE IEEE.STD_LOGIC_1164.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY PULSE IS
    PORT ( CLK : IN STD_LOGIC;                              --计数时钟
          PSOUT : OUT STD_LOGIC;                   --计数溢出并分频输出
              A1 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
              A2 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
              B1 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
              B2 : IN STD_LOGIC_VECTOR(3 DOWNTO 0);    
         A1_OUT : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);    
         A2_OUT : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);    
         B1_OUT : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);    
         B2_OUT : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
            KEY : IN STD_LOGIC;
             RST : IN STD_LOGIC );
END PULSE;
ARCHITECTURE mixed OF PULSE IS
COMPONENT LCNT8 
        PORT ( CLK,LD : IN STD_LOGIC;
                        D : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
                     CAO : OUT STD_LOGIC );
END COMPONENT;
    SIGNAL CAO1,CAO2 : STD_LOGIC;
    SIGNAL LD1,LD2   : STD_LOGIC;
    SIGNAL PSINT       : STD_LOGIC;
    SIGNAL AA1,AA2,BB1,BB2 : STD_LOGIC_VECTOR(3 DOWNTO 0);
    SIGNAL AA,BB       : STD_LOGIC_VECTOR(7 DOWNTO 0);
    -- SIGNAL A,B           : STD_LOGIC_VECTOR(7 DOWNTO 0);  --8位计数预置数
BEGIN
    U1 : LCNT8 PORT MAP( CLK => CLK, LD => LD1,
                                  D => AA,  CAO => CAO1 );
   U2 : LCNT8 PORT MAP( CLK => CLK, LD => LD2,
                                  D => BB,  CAO => CAO2 );    
    BB <= BB2&BB1;  AA <= AA2&AA1;                   
    PROCESS(AA1,AA2,BB1,BB2,KEY,RST,CLK)
    BEGIN
        IF RST = '1' THEN  AA1 <= A1; AA2 <= A2; BB1 <= B1; BB2 <= B2;        
        ELSIF KEY'EVENT AND KEY = '1' THEN AA2 <= AA2 - "0001"; BB2 <= BB2 + "0001";
        END IF;
    END PROCESS;

    PROCESS(CAO1,CAO2,KEY)                                            --计数溢出二分频进程
    BEGIN
            IF CAO1 = '1' THEN PSINT <= '0';                        --异步复位
            ELSIF CAO2'EVENT AND CAO2 = '1' THEN PSINT <= '1' ;
            END IF;
    END PROCESS;
    A1_OUT <= AA(3 DOWNTO 0); A2_OUT <= AA(7 DOWNTO 4);
    B1_OUT <= BB(3 DOWNTO 0); B2_OUT <= BB(7 DOWNTO 4);
    LD1 <= NOT PSINT ; LD2 <= PSINT ; PSOUT <= PSINT ;
END mixed;

仿真结果

A=11110000,B=00000000

仿真结果

从上图仿真结果来看,当KEY微调按键每次为上升沿后,高电平的长度都会略微增加,与题意相符

管脚分配

按检查要求里下图的管脚分配情况进行分配即可。

管脚分配

更:

管脚分配


文章作者: Mat Jenin
文章链接: http://matjenin.xyz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mat Jenin !
  目录