本博客是本人大二上学期数字系统实验硬件描述3的内容,在此记录以防丢失。目录如下:
① 理解IIC总线读取任意地址数据的时序
首先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所示,抓取此时示波器中对应波形,逐条记录说明执行操作的过程。
④ 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;
①用状态机设计控制模块,包括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取反得到。
③ 状态图以及顶层实现如图:
③ 仿真:设置写时钟频率40MHz,读时钟频率10MHz,为了能看清时钟大小关系我将FIFO的容量改为了64,因此只读64bit
(3)仿真验证实现HM62256的读写功能,记录操作步骤和实验结果。
①写入:首先将地址开关设置为A0A3=0000,开关全闭合,数据输入开关设置为A7A0=10101010,开关相间闭合;然后将WE设置为低电平,输出信号OE设置为高电平,控制三态门的开关闭合,写入地址为0x0,数据输入为10101010;可以看到LED灯组显示为暗亮暗亮暗亮暗亮,表示写入数据10101010。
②读取0x0地址:紧接着,依次将WE设置为低电平,三态门控制信号开关打开,LED灯熄灭。最后将输出信号OE设置为低电平,开关闭合的一瞬间可以看到LED显示为暗亮暗亮暗亮暗亮,表示读取成功。
③实验结论:读写操作成功。
(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读写功能验证正确。