基于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.按模块完成的代码及注释.
完整源代码下载地址:
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