基于VHDL的8255可编程并行接口电路设计

一.实验题目名称:

8255可编程并行接口电路设计

 

二.实验目的、任务和要求:

实验目的:学习掌握基本的数字系统设计方法,建立自顶向下的设计思维,能够使用VHDL语言编写简单的应用IP核,掌握基本的FPGA编译、烧写步骤。

任务:使用VHDL语言编写一个IP核,实现8255并行接口的功能,能通过仿真并在Spartan-3E Starter Kit开发板上实现

要求:所编写的IP核要能实现8255的全部三种工作方式。由于8255接口众多,应尽可能多得使用板上其他资源,例如串口、LCD、LED等。

 

三.实验系统结构设计分析

1.模块划分思想和方法;

针对8255端口多,逻辑判断复杂,需要分时应用的特点。我们自顶向下设计了两层文件,顶层文件为8255的端口声明及各个模块的端口声明,然后用map将各个模块连接起来

底层是8个子模块的元件定义。我们首先用串口模块将一个从PC机发来的串行数据转换成并行数据存放到数据输出选择模块的DOUT口,至于这个八位数据是输入到控制寄存器还是从PA/PB/PC口输出,就由另一个输入输出逻辑判断模块来控制。逻辑判断模块根据A0-A1,WR,RD,还有控制字来判断三个端口处于什么工作方式,并将数据发送(接收)至A口、B口、C口的缓冲区。最后,我们通过PA输出模块、PA输入模块、PB输出模块、PB输入模块、PC输出模块将缓存区中的数据根据不同的工作方式进行输入输出。

 

2.模块框图和作用;

 

8个模块的作用:

串口通信模块(Rs232RefComp):由于8255端口众多,而fpga板载I/O口不够用,所以采用串口输入的方式来给8255提供所需的数据(D0-D7)。

数据输出选择模块(dout_mux):8255A有3个8位数据端口,即端口A、端口B和端口C,通过数据输出选择模块来最终判断选择哪个端口输出。

数据输入输出逻辑判断模块(cntl_log):8255A的三个端口,还有一个控制寄存器,通过数据输出输入逻辑判断模块来判断8255处于何种工作方式。

PA口输出模块(portaout):用来控制PA的缓存区的八位数据输出到PA口。

PA口输入模块(portain):用来控制PA口读到的数据放到PA的缓存区。

PB口输出模块(portbout):用来控制PB的缓存区的八位数据输出到PB口。

PB口输入模块(portbin):用来控制PB口读到的数据放到PB的缓存区。

PC口输出模块(portcout):用来控制PC口的位输出。

 

3.各模块引脚定义和作用.

 1 串口通信模块

CLK: 时钟(50MHZ),配合波特率接收PC端发来的串行数据

RD: 读信号,始终置0,一直读来自串口的信号

RST:复位端

RXD:接收端,接收来自PC机传出的串行数据

DBIN:并行数据输入端,接收来自PB口的8位输出数据

TXD: 串行输出端,将PB口发出的8位并行数据转换成串行数据发送到上位机

DBOUT: 并行数据输出端,将来自PC机的串行数据转化成8位并行数据输出

 

2 逻辑判断模块

CLK:时钟(50MHZ)

RESET:复位

nCS:片选端

nRD:读寄存器

nWR:写寄存器

A[1-0]:选择端,选择PA/PB/PC/控制寄存器

DIN[7-0]:数据输入端

PCIN[7-0]:PC输入端(用于方式三)

PAEN:PA使能

PBEN:PB使能

Portaoutld:PA口允许输出                                    

Portaread:读PA口            Portbread:读PB口         PCEN:PC使能

Portawrite:写PA口           Portbwrite:写PB口        DOUTSelect:输出选择

Portboutld:PB口允许输出      Controlreg:控制寄存器    

Portcoutld:PC口允许输出

 

3 数据输出选择模块

DOUTSelect:数据输出选择位

Controlreg:控制寄存器

PortAInReg:PA口输入缓存器

PAIN:PA口输入寄存器

PortBInReg:PB口输入缓存器

PBIN:PB口输入寄存器

Portcstatus:PC口状态寄存器

DOUT:数据输出寄存器

 

4 PA口输出模块

CLK:时钟

PortAoutld:PA口允许输出

Reset:复位

DIN:数据输入寄存器

PAOUT:PA口输出寄存器

 

5 PA口输入模块

CLK:时钟

PortAInLd:PA口允许输入

RESET:复位

PAIN:PA口输入寄存器

PortAInReg:PA口输入缓存器

 

6 PB口输出模块

CLK:时钟

PortBoutld:PB口允许输出

Reset:复位

DIN:数据输入寄存器

PBOUT:PA口输出寄存器

 

7 PB口输入模块

CLK:时钟

PortBInLd:PB口允许输入

RESET:复位

PBIN:PB口输入寄存器

PortBInReg:PB口输入缓存器

 

8 PC口输出模块

CLK:时钟

PortARead:读PA口

PortAWrite:写PA口

PortBRead:读PB口

PortBWrite:写PB口

PortCOverride:PC口重载

RESET:复位

DIN:数据输入寄存器

PCIN:PC口输入寄存器

Controlreg:控制寄存器              Portcstatus:PC口状态寄存器

PortCoutld:PC口允许输出           PCOUT:PC口输出寄存器

 

四.实验代码设计以及分析:

1.给出模块层次图;

 

2.按模块完成的代码及注释.

完整源代码下载地址:

http://files.cnblogs.com/yuzeren48/8255%E6%BA%90%E7%A0%81%E5%8A%A0%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.rar

 1串口通信模块

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity Rs232RefComp is
    Port ( 
        --TXD     : out std_logic      := '1';
        RXD     : in  std_logic;                    
        CLK         : in  std_logic;                            --Master Clock
        --DBIN     : in  std_logic_vector (7 downto 0);                --Data Bus in
        DBOUT  : out std_logic_vector (7 downto 0);                --Data Bus out
        RDA        : inout std_logic;                            --Read Data Available
        TBE        : inout std_logic     := '1';                        --Transfer Bus Empty
        RD        : in  std_logic;                                --Read Strobe
        --WR    : in  std_logic;                                --Write Strobe
        PE        : out std_logic;                                   --Parity Error Flag
        FE        : out std_logic;                                --Frame Error Flag
        OE        : out std_logic;                                --Overwrite Error Flag
        RST        : in  std_logic    := '0');                           --Master Reset
end Rs232RefComp;

architecture rtl of Rs232RefComp is
------------------------------------------------------------------------
-- Component Declarations
------------------------------------------------------------------------
------------------------------------------------------------------------
--  Local Type Declarations
------------------------------------------------------------------------
    --Receive state machine
    type rstate is (                      
        strIdle,            --Idle state
        strEightDelay,      --Delays for 8 clock cycles
        strGetData,        --Shifts in the 8 data bits, and checks parity
        strCheckStop        --Sets framing error flag if Stop bit is wrong
    );

    type tstate is (
        sttIdle,            --Idle state
        sttTransfer,        --Move data into shift register
        sttShift            --Shift out data
        );

    type TBEstate is (
        stbeIdle,
        stbeSetTBE,
        stbeWaitLoad,
        stbeWaitWrite
        );
        
------------------------------------------------------------------------
-- Signal Declarations
------------------------------------------------------------------------
    constant baudDivide : std_logic_vector(7 downto 0) := "10100011";     
--Baud Rate dividor, set now for a rate of 9600. 
--Found by dividing 50MHz by 9600 and 16.
    signal rdReg    :  std_logic_vector(7 downto 0) := "00000000";            
--Receive holding register
    signal rdSReg    :  std_logic_vector(9 downto 0) := "1111111111";        
--Receive shift register
    signal tfReg    :  std_logic_vector(7 downto 0);                                --Transfer holding register
    signal tfSReg  :  std_logic_vector(10 downto 0)     := "11111111111";    
--Transfer shift register
    signal clkDiv    :  std_logic_vector(8 downto 0)    := "000000000";        --used for rClk
    signal rClkDiv :  std_logic_vector(3 downto 0)    := "0000";            --used for tClk
    signal ctr    :  std_logic_vector(3 downto 0)    := "0000";            --used for delay times
    signal tfCtr    :  std_logic_vector(3 downto 0)    := "0000";    --used to delay in transfer
    signal rClk    :  std_logic := '0';                                   --Receiving Clock
    signal tClk    :  std_logic;                                      --Transfering Clock
    signal dataCtr :  std_logic_vector(3 downto 0)    := "0000"
--Counts the number of read data bits
    signal parError:  std_logic;                                        --Parity error bit
    signal frameError: std_logic;                                        --Frame error bit
    signal CE        :  std_logic;                                --Clock enable for the latch
    signal ctRst    :  std_logic := '0';
    signal load    :  std_logic := '0';
    signal shift    :  std_logic := '0';
    signal par    :  std_logic;
signal tClkRST    :  std_logic := '0';
    signal rShift    :  std_logic := '0';
    signal dataRST :  std_logic := '0';
    signal dataIncr:  std_logic := '0';
    signal strCur    :  rstate    := strIdle;            --Current state in the Receive state machine
    signal strNext    :  rstate;                           --Next state in the Receive state machine
    signal sttCur  :  tstate := sttIdle;              --Current state in the Transfer state machine
    signal sttNext :  tstate;                        --Next state in the Transfer staet machine
    signal stbeCur :  TBEstate := stbeIdle;
    signal stbeNext:  TBEstate;
    
------------------------------------------------------------------------
-- Module Implementation
------------------------------------------------------------------------
begin
    frameError <= not rdSReg(9);
    parError <= not ( rdSReg(8) xor (((rdSReg(0) xor rdSReg(1)) xor (rdSReg(2) xor rdSReg(3))) xor ((rdSReg(4) xor rdSReg(5)) xor (rdSReg(6) xor rdSReg(7)))) );
    DBOUT <= rdReg;
    --tfReg <= DBIN;
    par <=  not ( ((tfReg(0) xor tfReg(1)) xor (tfReg(2) xor tfReg(3))) xor ((tfReg(4) xor tfReg(5)) xor (tfReg(6) xor tfReg(7))) );

--Clock Dividing Functions--
    process (CLK, clkDiv)                                --set up clock divide for rClk
        begin
            if (Clk = '1' and Clk'event) then
                if (clkDiv = baudDivide) then
                    clkDiv <= "000000000";
                else
                    clkDiv <= clkDiv +1;
                end if;
            end if;
        end process;

    process (clkDiv, rClk, CLK)                         --Define rClk
    begin
        if CLK = '1' and CLK'Event then
            if clkDiv = baudDivide then
                rClk <= not rClk;
            else
                rClk <= rClk;
            end if;
        end if;
    end process;    

    process (rClk)                                  --set up clock divide for tClk
        begin
            if (rClk = '1' and rClk'event) then
                rClkDiv <= rClkDiv +1;
            end if;
        end process;

    tClk <= rClkDiv(3);                            --define tClk

    process (rClk, ctRst)                           --set up a counter based on rClk
        begin
            if rClk = '1' and rClk'Event then
                if ctRst = '1' then
                    ctr <= "0000";
                else
                    ctr <= ctr +1;
                end if;
            end if;
        end process;

    process (tClk, tClkRST)                        --set up a counter based on tClk
        begin
            if (tClk = '1' and tClk'event) then
                if tClkRST = '1' then
                    tfCtr <= "0000";
                else
                    tfCtr <= tfCtr +1;
                end if;
            end if;
        end process;

     --This process controls the error flags--
    process (rClk, RST, RD, CE)
        begin
            if RD = '1' or RST = '1' then
                FE <= '0';
                OE <= '0';
                RDA <= '0';
                PE <= '0';
            elsif rClk = '1' and rClk'event then
                if CE = '1' then
                    FE <= frameError;
                    OE <= RDA;
                    RDA <= '1';     
                    PE <= parError;
                    rdReg(7 downto 0) <= rdSReg (7 downto 0);
                end if;                
            end if;
        end process;

    --This process controls the receiving shift register--
    process (rClk, rShift)
        begin
            if rClk = '1' and rClk'Event then
                if rShift = '1' then
                    rdSReg <= (RXD & rdSReg(9 downto 1));
                end if;
            end if;
        end process;

    --This process controls the dataCtr to keep track of shifted values--
     process (rClk, dataRST)
        begin
            if (rClk = '1' and rClk'event) then
                if dataRST = '1' then
                    dataCtr <= "0000";
                elsif dataIncr = '1' then
                    dataCtr <= dataCtr +1;
                end if;
            end if;
        end process;
      --Receiving State Machine--
    process (rClk, RST)
        begin
            if rClk = '1' and rClk'Event then
                if RST = '1' then
                    strCur <= strIdle;
                else
                    strCur <= strNext;
                end if;
            end if;
        end process;
            
    --This process generates the sequence of steps needed receive the data
    
    process (strCur, ctr, RXD, dataCtr, rdSReg, rdReg, RDA)
        begin   
            case strCur is

                when strIdle =>
                    dataIncr <= '0';
                    rShift <= '0';
                    dataRst <= '0';
                
                    CE <= '0';
                    if RXD = '0' then
                        ctRst <= '1';
                        strNext <= strEightDelay;
                    else
                        ctRst <= '0';
                        strNext <= strIdle;
                    end if;
                
                when strEightDelay => 
                    dataIncr <= '0';
                    rShift <= '0';
                    CE <= '0';

                    if ctr(2 downto 0) = "111" then
                        ctRst <= '1';
                            dataRST <= '1';
                        strNext <= strGetData;
                    else
                        ctRst <= '0';
                        dataRST <= '0';
                        strNext <= strEightDelay;
                    end if;
                
                when strGetData =>    
                    CE <= '0';
                    dataRst <= '0';
                    if ctr(3 downto 0) = "1111" then
                        ctRst <= '1';
                        dataIncr <= '1';
                        rShift <= '1';
                    else
                        ctRst <= '0';
                        dataIncr <= '0';
                        rShift <= '0';        
                    end if;

                    if dataCtr = "1010" then
                        strNext <= strCheckStop;
                    else
                        strNext <= strGetData;
                    end if;

                when strCheckStop =>
                    dataIncr <= '0';
                    rShift <= '0';
                    dataRst <= '0';
                    ctRst <= '0';
                    CE <= '1';
                    strNext <= strIdle;
            end case;
        end process;
end rtl;


.仿真图(输入输出波形)以及分析:

一:设置控制字

如图1所示:

首先将寄存器选择位a[1:0]设置为“11”,在复位信号(reset)到来时,往数据输出寄存器(dout)放入控制字9B,使PA、PB、PC口工作在方式0下

当读取信号(nrd)到来时,将控制字读入控制寄存器

二:选择端口输出

如图2所示:

首先串行数据接收端(uart)接收来自PC机的串行数据(0xCB)并通过串口模块将串行数据转化为8位并行数据

然后将寄存器选择位a[1:0]设置为“00”,表示由PA口输出

当写信号(nwr)到来时,将8位并行数据发送到PA口

若要使用PB口输出,就将将寄存器选择位a[1:0]设置为“01”

当写信号(nwr)到来时,将8位并行数据发送到PB口

                                         图1 设置控制字

                                        图2 选择端口输出

六.实验问题分析和经验总结:

问题一:8255端口众多,而FPGA的板载I/O口有特别少,因而产生两个问题:1、如何将数据输入8255的数据段(D0-D7)。2、如何安排PA、PB、PC口。

解决第一个问题可以有两个办法:1、分时传送,使用四个开关分两次将8为数据传送到数据端。优点:设计简单。缺点:不直观,在开发板上始终只能看到最后四位输入。2、使用串口。优点:界面直观,可以在PC端清楚显示所传送的每一个数据。缺点:设计复杂。

解决第二个问题也有三个办法:1、用LED灯显示8位输出数据。优点:设计简单。缺点:无。2、用LCD显示8位输出数据。优点:界面直观。缺点:编程复杂。3、使用串口输出数据到PC机上显示。优点:界面直观。缺点:编程复杂。

问题二:状态机编写困难,由于串口模块需要与PC机通讯,所以要编写相应的状态机来完成接收和转换的时序,这个是难点

问题三:测试文件难编写。编写测试文件对时序要求很高,8255内部逻辑复杂,时序一点有错结果就不对。

经验总结:

1、采用了VHDL语言作为输入方式并结合FPGA/CPLD,大大缩短了设计周期,提高了设计的可靠性、灵活性,使用户可根据自己的需求,方便、高效地设计出适合的IP核。

2、要善于查阅网上资料。很多模块都有相应的IP核,可以利用网上资源将IP核适当修改然后应用到自己的设计中。

3、要学会自顶向下的设计方法,首先构建一个结构体系,编写一个顶层文件,然后对各个模块分别编写程序,进行仿真,最后编写顶层测试文件,实现全部功能。

4、对开发板的I/O口要尽可能得有效利用。比如按钮的分时复用,对LED的刷新显示

5、充分利用板载资源。如串口、LCD等

6、将FPGA与上位机通讯,能够更加方便对数据的操作,并且直观的显示数据的收发过程

七.参考资料:

[1] 郑群星. Xilinx FPGA 数字电路设计. 科学出版社,2011

[2] 董兰荣. Xilinx FPGA电路设计与实习. 沧海书局,2001

[3] 林国良. VHDL硬件设计语言. 全华图书公司,1996

[4] 萧如宣. VHDL数位系统电路设计. 儒林书局,2000

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章