📄 simple_i2c.vhd
字号:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity simple_i2c is
port (
clk : in std_logic;
ena : in std_logic;
nReset : in std_logic;
clk_cnt : in unsigned(7 downto 0); -- 4x SCL
-- input signals
start,
stop,
read,
write,
ack_in : in std_logic;
Din : in std_logic_vector(7 downto 0);
-- output signals
cmd_ack : out std_logic;
ack_out : out std_logic;
Dout : out std_logic_vector(7 downto 0);
-- i2c signals
SCL : inout std_logic;
SDA : inout std_logic
);
end entity simple_i2c;
architecture structural of simple_i2c is
component i2c_core is
port (
clk : in std_logic;
nReset : in std_logic;
clk_cnt : in unsigned(7 downto 0);
cmd : in std_logic_vector(2 downto 0);
cmd_ack : out std_logic;
busy : out std_logic;
Din : in std_logic;
Dout : out std_logic;
SCL : inout std_logic;
SDA : inout std_logic
);
end component i2c_core;
-- commands for i2c_core
constant CMD_NOP : std_logic_vector(2 downto 0) := "000";
constant CMD_START : std_logic_vector(2 downto 0) := "010";
constant CMD_STOP : std_logic_vector(2 downto 0) := "011";
constant CMD_READ : std_logic_vector(2 downto 0) := "100";
constant CMD_WRITE : std_logic_vector(2 downto 0) := "101";
-- signals for i2c_core
signal core_cmd : std_logic_vector(2 downto 0);
signal core_ack, core_busy, core_txd, core_rxd : std_logic;
-- signals for shift register
signal sr : std_logic_vector(7 downto 0); -- 8bit shift register
signal shift, ld : std_logic;
-- signals for state machine
signal go, host_ack : std_logic;
begin
-- hookup i2c core
u1: i2c_core port map (clk, nReset, clk_cnt, core_cmd, core_ack, core_busy,core_txd, core_rxd, SCL, SDA);
-- generate host-command-acknowledge 主机命令确认
cmd_ack <= host_ack;
-- generate go-signal
go <= (read or write) and not host_ack;
-- assign Dout output to shift-register
Dout <= sr;
-- assign ack_out output to core_rxd (contains last received bit)
ack_out <= core_rxd;
-- generate shift register
shift_register: process(clk)
begin
if (clk'event and clk = '1') then
if (ld = '1') then --load
sr <= din;
elsif (shift = '1') then --shift
sr <= (sr(6 downto 0) & core_rxd);--i2c读出的信号放在后面
end if;
end if;
end process shift_register;
--
-- state machine
--
statemachine : block
type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop);
signal state : states;
signal dcnt : unsigned(2 downto 0);
begin
--
-- command interpreter, translate complex commands into simpler I2C commands
--
nxt_state_decoder: process(clk, nReset, state)
variable nxt_state : states;
variable idcnt : unsigned(2 downto 0);
variable ihost_ack : std_logic;
variable icore_cmd : std_logic_vector(2 downto 0);
variable icore_txd : std_logic;
variable ishift, iload : std_logic;
begin
-- 8 databits (1byte) of data to shift-in/out
idcnt := dcnt;
-- no acknowledge (until command complete)
ihost_ack := '0';
icore_txd := core_txd;
-- keep current command to i2c_core
icore_cmd := core_cmd;
-- no shifting or loading of shift-register
ishift := '0';
iload := '0';
-- keep current state;
nxt_state := state;
case state is
when st_idle =>
if (go = '1') then --go 信号是一个开始信号,起到起始作用
if (start = '1') then
nxt_state := st_start;
icore_cmd := CMD_START;
elsif (read = '1') then --Q:不是必须进入start么?为什么可以不呢?
nxt_state := st_read;
icore_cmd := CMD_READ;
idcnt := "111";
else
nxt_state := st_write;
icore_cmd := CMD_WRITE;
idcnt := "111";
iload := '1';--write状态下进行数据导入
end if;
end if;
when st_start => --Q:如果不进入start直接进入read or write呢?
if (core_ack = '1') then--说明start状态完成了
if (read = '1') then
nxt_state := st_read;
icore_cmd := CMD_READ;
idcnt := "111";
else
nxt_state := st_write;
icore_cmd := CMD_WRITE;
idcnt := "111";
iload := '1';
end if;
end if;
when st_write =>
if (core_ack = '1') then
idcnt := dcnt -1; -- count down Data_counter
icore_txd := sr(7);--要写入的数据 --note
if (dcnt = 0) then --写够8个时
nxt_state := st_ack;
icore_cmd := CMD_READ;--等待读ack?
else
ishift := '1';
-- icore_txd := sr(7);
end if;
end if;
when st_read =>
if (core_ack = '1') then
idcnt := dcnt -1; -- count down Data_counter
ishift := '1';
if (dcnt = 0) then
nxt_state := st_ack;
icore_cmd := CMD_WRITE;--写ack?
icore_txd := ack_in;
end if;
end if;
when st_ack => --在此状态下可能写ack也可能读ack
if (core_ack = '1') then
-- generate command acknowledge signal
ihost_ack := '1'; --说明ack处理结束,有可能读或写
-- Perform an additional shift, needed for 'read' (store last received bit in shift register)
ishift := '1'; --这样吧最后1bit的core_rxd读入sr?
--在写入状态,应该读ack信号时,检查是否为低,表示有正确的ack信号
--if(icore_cmd=CMD_READ and core_txd='0') then --modify
-- check for stop; Should a STOP command be generated ?
if (stop = '1') then
nxt_state := st_stop;
icore_cmd := CMD_STOP;
else
nxt_state := st_idle;
icore_cmd := CMD_NOP;
end if;
--elsif(icore_cmd=CMD_READ and core_txd='1') then
-- nxt_state := st_stop;
-- icore_cmd := CMD_STOP;
--else
-- if (stop = '1') then
-- nxt_state := st_stop;
-- icore_cmd := CMD_STOP;
-- else
-- nxt_state := st_idle;
-- icore_cmd := CMD_NOP;
-- end if;
--end if;
end if;
when st_stop =>
if (core_ack = '1') then
nxt_state := st_idle;
icore_cmd := CMD_NOP;
end if;
when others => -- illegal states
nxt_state := st_idle;
icore_cmd := CMD_NOP;
end case;
-- generate registers
if (nReset = '0') then
core_cmd <= CMD_NOP;
core_txd <= '0';
shift <= '0';
ld <= '0';
dcnt <= "111";
host_ack <= '0';
state <= st_idle;
elsif (clk'event and clk = '1') then
if (ena = '1') then
state <= nxt_state;
dcnt <= idcnt;
shift <= ishift;
ld <= iload;
core_cmd <= icore_cmd;
core_txd <= icore_txd;
host_ack <= ihost_ack;
end if;
end if;
end process nxt_state_decoder;
end block statemachine;
end architecture structural;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -