IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核
博客简介 本博客是本人大二上学期数字系统实验硬件描述3的内容,在此记录以防丢失。目录如下:
- IIC串行总线时序分析
- VHDL编程设计专门状态机与2片异步FIFO来实现乒乓操作
- 设计HM62256测试电路并对其仿真验证
- 定制开发一个1-port RAM的IP核
文章图片
首先scl保持高电平,sda下拉,开始start。主机发送器件的7位地址码+写方向“0”(“伪写”),发送完释放SDA线并在SCL线上产生第9个时钟信号。被选中的器件确认是自己的地址后,在SDA线上产生应答信号。然后,主机再发一个字节的要读出器件的存储区的首地址,收到应答后,主机重复一次起始信号发出器件地址和读方向(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,主机都要回复应答信号。当最后一个字节数据读完后,主机返回以“非应答”(高电平)发出终止信号以结束读出操作
② 将所提供hex文件装入单片机中,启动仿真,掌握读写操作。
文章图片
读写操作:
控制右下角的3个按钮来执行读/写操作,第3个按钮控制24C02C存储区的地址,第2个按钮控制将要写入的数据,第1个按钮按下代表执行写操作。数码管高两位代表24C02C存储区的地址,第4和5位代表输入的数据,低2位代表24C02C指定地址上读出的数据。因此,只需要控制按键3指定地址,按键2指定输入数据,最后按动按键1即可在相应地址上存入数据;只需要控制按键3指定地址,即可在数码管的低2位看到数据输出。
③参考IIC debugger中的数据如图C所示,抓取此时示波器中对应波形,逐条记录说明执行操作的过程。
文章图片
- C图是一任意读过程,SDA由高变低,start;首先单片机发送7位地址码1010000+伪写0组成8个字节A0,表示AT24C02C地址。
- AT24C02C确认是自己的地址之后应答;接着单片机收到应答,发送存储区地址01H
- AT24C02C应答,单片机重复一次起始信号并发出器件地址和读方向位(“1”)得到A1
- AT24C02C应答,并且从01H取出数据03H,发送。
- 单片机发出终止信号结束读出操作stop。
设计思路:(具体实现在打包的工程文件中)
首先给随机读的时序划分状态有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;
--当前状态beginsda<=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;
- 顶层图:
文章图片
- 仿真验证:
文章图片
- 波形分析:
首先设置清零,然后EN有效开始随机读时序,sart时sda拉低,接下来发送存储器件地址10100000,选中000号24C02C,最后一位0表示伪写;接下来24C02C确认是自己的地址发送响应,sda拉低;紧接着主机接收到响应后发送字地址00000000;24C02C接收到字地址之后发送响应;接到响应之后主机再次发送存储器件地址10100001,最后一位1表示读取;从机接收到后响应;并且发送字地址00000000的数据11110000;主机成功读取到这个数据F0。最后主机反馈sda为高电平表示不再读取,此时从机也不在发送数据,回到闲置状态,sda和scl拉高。
结论:通过验证,接口电路读取到24C02C地址00H的数据F0,并在data端显示,设计电路时序满足随机读取要求。
②状态机的输出:输出包括Awrreq和Ardreq分别作为A号FIFO的写使能和读使能。当处于A状态时Awrreq=1,Ardreq=0表示写A;当处于B状态时Awrreq=0,Ardreq=1;表示读A;另外对于B号FIFO的使能信号可以对Awrreq和Ardreq取反得到。
③ 状态图以及顶层实现如图:
文章图片
文章图片
文章图片
③ 仿真:设置写时钟频率40MHz,读时钟频率10MHz,为了能看清时钟大小关系我将FIFO的容量改为了64,因此只读64bit
文章图片
- 仿真分析:
在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)HM62256A是32-kword×8静态RAM,引脚有地址A0~14,片选CS低电平有效,WE写低电平有效,OE输出低电平有效
(2)新建Proteus工程,添加器件,并设计好HM62256的功能验证电路:
文章图片
(3)仿真验证实现HM62256的读写功能,记录操作步骤和实验结果。
【IIC总线随机读VHDL实现&FIFO实现乒乓操作&HM62256测试&定制IP核】①写入:首先将地址开关设置为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字节容量。如左下图:
文章图片
(2)分析并说明生成目录下的html波形报告
文章图片
文章图片
① 图一是读取操作的波形,读取发生在使能为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读写功能验证正确。
推荐阅读
- 概率论/统计学|随机变量 的 分布函数 与 概率密度函数 的区别
- 临床统计学学习日志
- Vue组件之事件总线和消息发布订阅详解
- Python【习题】(随机生成激活码、优惠码、验证码)
- 微信内如何防止推广链接被封,微信内随机跳转落地页的实现原理
- Python|Python--随机森林模型
- Qt实战|Qt+OpenCV联合开发(十七)--随机数与随机颜色
- R语言模拟和预测ARIMA模型、随机游走模型RW时间序列趋势可视化
- LeetCode-138-复制带随机指针的链表
- R语言逻辑回归、随机森林、SVM支持向量机预测Framingham心脏病风险和模型诊断可视化