基於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

 

 

 

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