IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核

博客简介

本博客是本人大二上学期数字系统实验硬件描述3的内容,在此记录以防丢失。目录如下:

  • IIC串行总线时序分析
  • VHDL编程设计专门状态机与2片异步FIFO来实现乒乓操作
  • 设计HM62256测试电路并对其仿真验证
  • 定制开发一个1-port RAM的IP核

IIC串行总线时序分析

① 理解IIC总线读取任意地址数据的时序
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第1张图片
首先scl保持高电平,sda下拉,开始start。主机发送器件的7位地址码+写方向“0”(“伪写”),发送完释放SDA线并在SCL线上产生第9个时钟信号。被选中的器件确认是自己的地址后,在SDA线上产生应答信号。然后,主机再发一个字节的要读出器件的存储区的首地址,收到应答后,主机重复一次起始信号发出器件地址和读方向(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,主机都要回复应答信号。当最后一个字节数据读完后,主机返回以“非应答”(高电平)发出终止信号以结束读出操作

② 将所提供hex文件装入单片机中,启动仿真,掌握读写操作。
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第2张图片
读写操作:
控制右下角的3个按钮来执行读/写操作,第3个按钮控制24C02C存储区的地址,第2个按钮控制将要写入的数据,第1个按钮按下代表执行写操作。数码管高两位代表24C02C存储区的地址,第4和5位代表输入的数据,低2位代表24C02C指定地址上读出的数据。因此,只需要控制按键3指定地址,按键2指定输入数据,最后按动按键1即可在相应地址上存入数据;只需要控制按键3指定地址,即可在数码管的低2位看到数据输出。

③参考IIC debugger中的数据如图C所示,抓取此时示波器中对应波形,逐条记录说明执行操作的过程。
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第3张图片

  1. C图是一任意读过程,SDA由高变低,start;首先单片机发送7位地址码1010000+伪写0组成8个字节A0,表示AT24C02C地址。
  2. AT24C02C确认是自己的地址之后应答;接着单片机收到应答,发送存储区地址01H
  3. AT24C02C应答,单片机重复一次起始信号并发出器件地址和读方向位(“1”)得到A1
  4. AT24C02C应答,并且从01H取出数据03H,发送。
  5. 单片机发出终止信号结束读出操作stop。

④ VHDL设计一个基于IIC总线的读取非易失存储器24C02C任意地址数据的接口电路,完成仿真。
设计思路:(具体实现在打包的工程文件中)
首先给随机读的时序划分状态有idle,sart,发送存储器地址状态,接收地址状态,read_data读状态ack_for_read_data,响应读状态,stop停止状态等等;对于每一个状态都与时序意义对应,并且每一个状态的进入都要满足时序要求。然后输入端口有clk时钟,reset,EN,以及存储器地址信号dAddress[2:0];字地址信号dAddress[7:0];对于地址这些并行数据可以通过一个计数变量count来进行计数,每次计数时可以向sda输送一个数据位。

  • 实现如下(代码仍有改进之必要):
library ieee;   
use ieee.std_logic_1164.all;  
use ieee.std_logic_arith.all; 
use ieee.std_logic_unsigned.all;  
entity iicInterface is   
port(
		reset,clk,EN:in std_logic;
		dAddress:in std_logic_vector(2 downto 0);
		wAddress:in std_logic_vector(7 downto 0);
		sda,scl:inout std_logic;
		data:out std_logic_vector(7 downto 0));  
end iicInterface;   
architecture Behavioral of iicInterface is 
signal sdaTemp:std_logic:='1';
signal sclTemp:std_logic:='1';
signal dataTemp:std_logic_vector(7 downto 0):="00000000";
type state is (
				idle,                   		--闲置状态  
				start,                  		--开始
				first_device_addr,       		--存储器地址接收状态    
				ack_for_first_device_addr,    	--响应存储器地址状态    
				word_addr,          			--字地址状态
				ack_for_word_addr,      		--响应字地址状态
				second_device_addr,            	--再次接收存储器地址状态 
				ack_for_second_device_addr,    	--响应再次接收存储器地址状态       
				read_data,             			--读状态
				ack_for_read_data,     			--响应读状态
				stop);  						--停止状态
signal current_state:state;            			--当前状态

begin    

sda<=sdaTemp;
scl<=sclTemp;

process(clk,reset)   
variable count:integer range 0 to 40;  
begin   
if reset='0'then                        		--闲置状态
	sdaTemp<='1';                           
	sclTemp<='1';
	current_state<=idle;
	count:=0;  
elsif clk'event and clk='1' then        		--时钟上升沿
	case current_state is               		--开始状态
		when idle=>
			if(EN='1')then          			
				current_state<=start;   
			else
				current_state<=idle;
			end if;
		
		when start=>							--开始状态sdaTemp拉到低电平	
			sdaTemp<='0';
			count:=0;							
			current_state<=first_device_addr;
		
		when first_device_addr=>           		--存储器地址接收状态 
			count:=count+1;             
			case count is               
				when 1=>						--AT24C系列EEPROM芯片的固定部分为1010
					sdaTemp<='1';
					sclTemp<=not sclTemp;
				when 2=>
					sdaTemp<='0';
					sclTemp<=not sclTemp; 
				when 3=>
					sdaTemp<='1';
					sclTemp<=not sclTemp;  
				when 4=>
					sdaTemp<='0';
					sclTemp<=not sclTemp;  
				when 5=>
					sdaTemp<=dAddress(2);
					sclTemp<=not sclTemp;  
				when 6=>
					sdaTemp<=dAddress(1);
					sclTemp<=not sclTemp; 
				when 7=>
					sdaTemp<=dAddress(0);
					sclTemp<=not sclTemp;  
				when 8=>
					sdaTemp<='0';
					sclTemp<=not sclTemp;				--读写方向为0,伪写
					current_state<=ack_for_first_device_addr;
					count:=0;
				when others=>					--等待响应	
					sdaTemp<='Z';
					sclTemp<='Z';
					current_state<=ack_for_first_device_addr;
					count:=0;
				end case;
		
		when ack_for_first_device_addr=>		--响应存储器地址状态
			sdaTemp<='Z';
			sclTemp<='Z';
			if sda='0' then 
				sclTemp<=not sclTemp;
				current_state<=word_addr;
			end if;

		when word_addr=>                    	--字地址状态
			count:=count+1; 					
			case count is   
				when 1=>                           
					sdaTemp<=wAddress(7); 
					sclTemp<=not sclTemp;    
				when 2=>                            
					sdaTemp<=wAddress(6);
					sclTemp<=not sclTemp;                           
				when 3=>                            
					sdaTemp<=wAddress(5);
					sclTemp<=not sclTemp;  
				when 4=>                            
					sdaTemp<=wAddress(4);
					sclTemp<=not sclTemp;     
				when 5=>                            
					sdaTemp<=wAddress(3);
					sclTemp<=not sclTemp; 
				when 6=>                            
					sdaTemp<=wAddress(2);
					sclTemp<=not sclTemp;  
				when 7=>                            
					sdaTemp<=wAddress(1);
					sclTemp<=not sclTemp;     
				when 8=>                            
					sdaTemp<=wAddress(0);
					sclTemp<=not sclTemp;
					current_state<=ack_for_word_addr;
					count:=0;
				when others=>
					sdaTemp<='Z';
					sclTemp<='Z';
					current_state<=ack_for_word_addr;
					count:=0;
			end case;

		when ack_for_word_addr=>				 --响应字地址 
		sdaTemp<='Z';
		sclTemp<='Z';
		if sda='0' then current_state<=second_device_addr;
			if sclTemp/='Z' then sclTemp<=not sclTemp;
			end if;
		end if;	
	
		when second_device_addr=>           	--再次接收存储器地址状态 
			count:=count+1;             
			case count is               
				when 1=>						--AT24C系列EEPROM芯片的固定部分为1010
					sdaTemp<='1';
					sclTemp<=not sclTemp;  
				when 2=>
					sdaTemp<='0';
					sclTemp<=not sclTemp; 
				when 3=>
					sdaTemp<='1';
					sclTemp<=not sclTemp;  
				when 4=>
					sdaTemp<='0';
					sclTemp<=not sclTemp;  
				when 5=>
					sdaTemp<=dAddress(2);
					sclTemp<=not sclTemp;  
				when 6=>
					sdaTemp<=dAddress(1);
					sclTemp<=not sclTemp; 
				when 7=>
					sdaTemp<=dAddress(0);
					sclTemp<=not sclTemp;  
				when 8=>
					sdaTemp<='1';					--读写方向为1
					sclTemp<=not sclTemp;
					current_state<=ack_for_second_device_addr;
					count:=0;
				when others=>
					sdaTemp<='Z';
					sclTemp<='Z';
					current_state<=ack_for_second_device_addr;
					count:=0;
				end case;
		
		when ack_for_second_device_addr=>		--响应再次接收存储器地址状态 
			sdaTemp<='Z';
			sclTemp<='Z';
			if sda='0' then current_state<=read_data;
				if sclTemp/='Z' then sclTemp<=not sclTemp;
				end if;
			end if;
		when read_data=>
			count:=count+1;  
			case count is 
				when 1=>                            
					dataTemp(7)<=sda;
					sclTemp<=not sclTemp;
				when 2=>                            
					dataTemp(6)<=sda;
					sclTemp<=not sclTemp;                           
				when 3=>                            
					dataTemp(5)<=sda;
					sclTemp<=not sclTemp;
				when 4=>                            
					dataTemp(4)<=sda;
					sclTemp<=not sclTemp;     
				when 5=>                            
					dataTemp(3)<=sda;
					sclTemp<=not sclTemp;
				when 6=>                            
					dataTemp(2)<=sda;
					sclTemp<=not sclTemp; 
				when 7=>                            
					dataTemp(1)<=sda;
					sclTemp<=not sclTemp;    
				when 8=>                            
					dataTemp(0)<=sda;
					sclTemp<=not sclTemp;
					data(7 downto 0)<=dataTemp(7 downto 0);
					current_state<=ack_for_read_data;
					count:=0;
				when others=>
					sdaTemp<='Z';
					sclTemp<='Z';
					current_state<=ack_for_read_data;
					count:=0;
		end case;
		when ack_for_read_data=>						--响应再次接收存储器地址状态 
			if sda='1' then current_state<=stop;	--高电平表示终止
			else current_state<=read_data;
			sclTemp<=not sclTemp;
			end if;
		when stop=>
			current_state<=idle; 
		when others=>						--最小冒险
			current_state<=idle; 			
	end case;  
end if;   
end process;  
end Behavioral; 			
  • 顶层图:
    IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第4张图片
  • 仿真验证:
    IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第5张图片
  • 波形分析:
    首先设置清零,然后EN有效开始随机读时序,sart时sda拉低,接下来发送存储器件地址10100000,选中000号24C02C,最后一位0表示伪写;接下来24C02C确认是自己的地址发送响应,sda拉低;紧接着主机接收到响应后发送字地址00000000;24C02C接收到字地址之后发送响应;接到响应之后主机再次发送存储器件地址10100001,最后一位1表示读取;从机接收到后响应;并且发送字地址00000000的数据11110000;主机成功读取到这个数据F0。最后主机反馈sda为高电平表示不再读取,此时从机也不在发送数据,回到闲置状态,sda和scl拉高。
    结论:通过验证,接口电路读取到24C02C地址00H的数据F0,并在data端显示,设计电路时序满足随机读取要求。

VHDL编程设计专门状态机与2片异步FIFO来实现乒乓操作:

①用状态机设计控制模块,包括A和B两状态,分别表示A号FIFO执行写入和B号FIFO执行写入,状态机输入为两块FIFO的写满信号Awrfull,Bwrfull,当Awrfull有效时表示A号FIFO写满因此从A转换到B状态,Bwrfull有效时表示B号FIFO写满因此从B转换到A状态。
②状态机的输出:输出包括Awrreq和Ardreq分别作为A号FIFO的写使能和读使能。当处于A状态时Awrreq=1,Ardreq=0表示写A;当处于B状态时Awrreq=0,Ardreq=1;表示读A;另外对于B号FIFO的使能信号可以对Awrreq和Ardreq取反得到。
③ 状态图以及顶层实现如图:
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第6张图片IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第7张图片
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第8张图片
③ 仿真:设置写时钟频率40MHz,读时钟频率10MHz,为了能看清时钟大小关系我将FIFO的容量改为了64,因此只读64bit
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第9张图片

  • 仿真分析:
    在01.6us,Awrreq有效,写A读B,将FF00写入A号FIFO,由于B为空所以读取数据显示0000;1.6us3.2us,Bwrreq有效,写B读A,将FF11写入B号FIFO,读取A中数据FF00FF00FF00;3.2us~4.8us,Awrreq有效,写A读B,将FF22写入A号FIFO,读取B中数据FF11FF11FF11FF11……依次类推,周而复始,完成验证。

设计HM62256测试电路并对其仿真验证

  1. 设计HM62256测试电路并对其仿真验证。
    (1)HM62256A是32-kword×8静态RAM,引脚有地址A0~14,片选CS低电平有效,WE写低电平有效,OE输出低电平有效
    (2)新建Proteus工程,添加器件,并设计好HM62256的功能验证电路:

IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第10张图片
(3)仿真验证实现HM62256的读写功能,记录操作步骤和实验结果。

①写入:首先将地址开关设置为A0A3=0000,开关全闭合,数据输入开关设置为A7A0=10101010,开关相间闭合;然后将WE设置为低电平,输出信号OE设置为高电平,控制三态门的开关闭合,写入地址为0x0,数据输入为10101010;可以看到LED灯组显示为暗亮暗亮暗亮暗亮,表示写入数据10101010。

②读取0x0地址:紧接着,依次将WE设置为低电平,三态门控制信号开关打开,LED灯熄灭。最后将输出信号OE设置为低电平,开关闭合的一瞬间可以看到LED显示为暗亮暗亮暗亮暗亮,表示读取成功。

③实验结论:读写操作成功。

定制开发一个1-port RAM的IP核

(1)定制步骤:Tools→ Mega wizard Plug-In Manager→Create a new custom megafunction variation→Installed Plug-Ins→ Memory Compiler→RAM1-port→设置输出q为8位,总容量为32,本应该端口RAM按地址4数据8来定制,但是最小容量是32,所以只能定制地址5数据8,后续只需要使用4位地址→新建mif文件,设置16字节容量。如左下图:
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第11张图片
(2)分析并说明生成目录下的html波形报告
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第12张图片
IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核_第13张图片
① 图一是读取操作的波形,读取发生在使能为0,时钟周期的上升沿。在13时钟上升沿读取地址00的值F0输出,45时钟上升沿读取地址01的值F1输出,同理后续时钟上升沿分别读取F2,F3输出。
② 图二是写操作的波形,wren为高使能并且处于时钟的上升沿时候将输入加载到输出端,在这一周期的下降沿才真正写入RAM。在第二个时钟周期的上升沿将数据输入加载到输出,下降沿的时候才写入Memoriy0,同样分别在第5个和第6个时钟周期的下降沿将输入02和03写入Memoriy2.
(3)仿真验证其读写功能,记录波形图并说明。
在这里插入图片描述
分析:0~600ns设置wren为高电平,执行写入操作。第1个时钟上升沿将F0写到IP的01H地址,第2个时钟上升沿将F1写到IP的02H地址,第5个时钟上升沿将F2写入IP的02H地址;600ns后设置wren信号为低电平,执行读取操作,在650ns,850ns,1050ns上升沿执行读取操作,分别读取地址01H,02H,03H的值F0,F1,F2。
结论:地址01H,02H,03H写入的数据和读取的数据一致,IP读写功能验证正确。

你可能感兴趣的:(数字系统)