一.實驗題目名稱:
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.按模塊完成的代碼及註釋.
完整源代碼下載地址:
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