综合 Synthesis/Implementation
Synthesised Synthesis/Implementation
我正在尝试创建一个 I2C 总线,但是我遇到了一个非常棘手的问题 - 在实现的映射部分,我收到警告 MapLib:701 - Signal SDA connected to top level port SDA has been removed.
仔细研究后,我发现这是由 I2C Master 本身的先前警告引起的:Xst:1293 - FF/Latch <sda_internal> has a constant value of 1 in block <IIC_MASTER>. This FF/Latch will be trimmed during the optimization process.
仔细查看,似乎整个状态机都被优化掉了 - 这是为什么和我怎么能阻止它?在下面的逻辑模拟中,它按预期工作,我不知道我到底做了什么让它认为整个状态机是多余的!。
我在下面放了我希望看到的图像,然后是描述 I2C 主模块和顶层模块的两个代码块。非常感谢任何帮助!
顶级
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY I2CBus IS
PORT(
SYSCLK_N : IN STD_LOGIC; --system 200MHz differential clock
SYSCLK_P : IN STD_LOGIC;
BTN : IN STD_LOGIC; -- to manually change reset
LED : OUT STD_LOGIC; --to observe reset value
SCL : OUT STD_LOGIC;
SDA : INOUT STD_LOGIC
);
END I2CBus;
ARCHITECTURE behavior OF I2CBus IS
-------------------DECLARE MASTER & SLAVE COMPONENTS------------------------
COMPONENT IIC_MASTER
PORT(SCL : IN STD_LOGIC;
SCL2X : IN STD_LOGIC;
RESET_N : IN STD_LOGIC;
ENA : IN STD_LOGIC;
ADR : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
REG : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
RW : IN STD_LOGIC;
DAT_WR : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
BUSY : OUT STD_LOGIC;
SDA : INOUT STD_LOGIC;
ACK_ERR : BUFFER STD_LOGIC);
END COMPONENT IIC_MASTER;
COMPONENT DCM
PORT(
SYSCLK_P : IN STD_LOGIC; -- CLOCK IN PORTS 200MHZ DIFFERENTIAL
SYSCLK_N : IN STD_LOGIC;
-- CLOCK OUT PORTS
SYSCLK : OUT STD_LOGIC
);
END COMPONENT;
COMPONENT CLK_DIVIDER
GENERIC(INPUT_FREQ : INTEGER;
OUT1_FREQ : INTEGER;
OUT2_FREQ : INTEGER);
PORT(SYSCLK : IN STD_LOGIC;
RESET_N : IN STD_LOGIC;
OUT1 : OUT STD_LOGIC;
OUT2 : OUT STD_LOGIC);
END COMPONENT CLK_DIVIDER;
-------------------I2C MASTER ONLY SIGNALS-----------------
--Inputs
signal reset_n : std_logic; --active high
--Outputs
signal busy : std_logic;
signal ack_err : std_logic;
-----------------------------------------------------------
signal SCL_internal : std_logic;
signal SCL2X_internal : std_logic;
signal sysclk : std_logic;
BEGIN
--------------------I2C_master Instantiation-------------------
master : IIC_Master
port map(
SCL => SCL_internal, --map constant data to send over I2C for now
SCL2X => SCL2X_internal,
RESET_N => RESET_N,
ENA => '1', --hold enable on
ADR => "1011001", --keep address constant
REG => x"AA", --keep target register constant
RW => '0', --always read
DAT_WR => x"77", --keep data to send constant
BUSY => BUSY,
SDA => SDA,
ACK_ERR => ACK_ERR
);
DCM_CLK : DCM
port map(
SYSCLK_P => SYSCLK_P,
SYSCLK_N => SYSCLK_N,
SYSCLK => sysclk --generate 200 MHz clock from system clocks
);
Clk_Div : Clk_Divider --Divide 200MHz clock down to frequencies that can be
generic map( --seen on an oscilloscope
INPUT_FREQ => 200000000, --200 MHz input
OUT1_FREQ => 10, --10 Hz output
OUT2_FREQ => 20 --20 Hz output
)
port map(
SYSCLK => sysclk, --system clock
RESET_N => reset_n, --reset
OUT1 => scl_internal,
OUT2 => scl2x_internal
);
----------------Mappings---------------------------
reset_n <= not BTN; --reset low when button pressed
LED <= not reset_n; --light LED when reset
SCL <= 'Z' when scl_internal = '1' else scl_internal;
end behavior;
I2C 主模块
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity IIC_MASTER IS
PORT(SCL : IN STD_LOGIC; --SCL clock
SCL2X : IN STD_LOGIC; --SCL x2 rate clock
RESET_N : IN STD_LOGIC; --ACTIVE LOW
ENA : IN STD_LOGIC; --ENABLE ACTIVE HIGH
ADR : IN STD_LOGIC_VECTOR(6 DOWNTO 0); --TARGET ADDRESS
REG : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --TARGET REGISTER
RW : IN STD_LOGIC; --READ LOW, WRITE HIGH
DAT_WR : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --DATA TO WRITE TO SLAVE
BUSY : OUT STD_LOGIC; --HIGH WHEN BUSY
SDA : INOUT STD_LOGIC; --SERIAL DATA ON BUS
ACK_ERR : BUFFER STD_LOGIC); --FLAG IF WRONG ACK FROM SLAVE
END IIC_MASTER;
architecture BEHAVIORAL of IIC_MASTER is
type state is (begn, ready, start, command, cmd_ack, reg_cmd, reg_ack, wr, rd, data_ack, stop);
signal i2cstate : state;
signal sda_internal : std_logic := '1'; --internal SDA
signal scl_internal : std_logic;
signal scl2x_internal : std_logic;
signal addr_rw : std_logic_vector(7 downto 0); --latched address (6-0) + rw (7)
signal reg_tx : std_logic_vector(7 downto 0); --latched slave register
signal data_tx : std_logic_vector(7 downto 0); -- data to write to slave
signal bit_cnt : integer range 0 to 7 := 7; --number of bits sent in transaction
begin
------state machine logic & writing to SDA on data clk rising edge-----
state_machine : process(scl2x_internal)
begin
if falling_edge(scl2x_internal) then
if reset_n = '0' then --when reset is low, set to ready i2cstate <= ready; --NOT IN TEMPLATE
busy <= '1'; --set to busy
sda_internal <= '1'; --disable sda to Z
ACK_ERR <= '0'; --clear error/ack flag
bit_cnt <= 7; --reset bit count
i2cstate <= ready;
else
if scl_internal = '0' then --in middle of clock low
case i2cstate is
when begn => i2cstate <= ready; --unconditional transition
when ready => if ENA = '1' then
BUSY <= '1'; --set busy
addr_rw <= ADR & RW; --get address & rw (concatenate)
reg_tx <= REG; --get target register
data_tx <= DAT_WR; --Collect data to write from port
i2cstate <= start; --update state
else
BUSY <= '0'; --set not busy
i2cstate <= ready; --remain ready
end if;
when start =>
BUSY <= '1'; --ensure busy remains on
bit_cnt <= 7; --reset bit counter for bytes
ACK_ERR <= '0';
sda_internal <= addr_rw(bit_cnt); --put first command bit on bus
i2cstate <= command; -- also an unconditional transition
when command =>
if bit_cnt = 0 then
sda_internal <= '1'; --set high for acknowledge
bit_cnt <= 7; --reset bit counter for bytes
i2cstate <= cmd_ack;
else
bit_cnt <= bit_cnt - 1; --decrement bit count
sda_internal <= addr_rw(bit_cnt - 1); --send next address bit on bus
i2cstate <= command; --stay in this state until command is sent
end if;
when cmd_ack =>
sda_internal <= reg_tx(bit_cnt); --write first register bit
i2cstate <= reg_cmd; --go to register sending state
when reg_cmd =>
if bit_cnt = 0 then --register transmitted
sda_internal <= '1'; --release internal sda for acknowledgement
bit_cnt <= 7; --reset bit count
i2cstate <= reg_ack; --go to reg ack
else
bit_cnt <= bit_cnt - 1; --decrement
sda_internal <= reg_tx(bit_cnt - 1); --write next reg bit to bus
i2cstate <= reg_cmd; --keep writing
end if;
when reg_ack =>
if addr_rw(0) = '0' then -- if read/write is high, read
sda_internal <= data_tx(bit_cnt); --write first data bit
i2cstate <= wr; -- go to write state
else --else if low, write
sda_internal <= '1'; --release internal sda for reading
i2cstate <= rd; --go to read state
end if;
when wr =>
BUSY <= '1'; --ensure busy flag still high
if bit_cnt = 0 then --byte transmitted
sda_internal <= '1'; --release internal sda for acknowledgement
bit_cnt <= 7; --reset bit count
BUSY <= '0'; -- data all written, so lower busy to notify others
i2cstate <= data_ack; --go to slav ack
else
bit_cnt <= bit_cnt - 1; --decrement
sda_internal <= data_tx(bit_cnt - 1); --write next bit to bus
i2cstate <= wr; --keep writing
end if;
when rd => null;
when data_ack => --acknowledge write
if ENA = '1' then --continue transaction
BUSY <= '0'; -- accept continue by keeping busy low
addr_rw <= ADR & RW; --fetch next address & command
data_tx <= DAT_WR; --fetch next data to write
reg_tx <= REG; --fetch next register
if addr_rw = ADR & RW and reg_tx = REG then --if the same location
sda_internal <= DAT_WR(bit_cnt); --write first bit of data
i2cstate <= wr; --go to write state
else --continue next transaction with a new read/write or slave
i2cstate <= start; --continue from start
end if;
else
i2cstate <= stop; --transaction done, go to end
sda_internal <= '0';
end if;
when stop =>
BUSY <= '0'; --set not busy
i2cstate <= ready;
end case;
elsif scl_internal = '1' then --in middle of clock high
case i2cstate is
when start => sda_internal <= '0';
when stop => sda_internal <= '1';
when cmd_ack | reg_ack | data_ack =>
if (SDA /= '0' or ACK_ERR = '1') then
ACK_ERR <= '1'; --no acknowledge or prev. error
end if;
when others => null;
end case;
end if;
end if;
end if;
end process;
scl_internal <= '1' when SCL = 'Z' else SCL;
scl2x_internal <= '1' when SCL2X = 'Z' else SCL2X;
SDA <= 'Z' when sda_internal = '1' else '0';
end BEHAVIORAL;
scl_internal <= '1' when SCL = 'Z' else SCL;
scl2x_internal <= '1' when SCL2X = 'Z' else SCL2X;
您不能测试与 'Z'
的相等性,synth 应该警告这些。仿真可以,但是没有硬件块以这种方式供合成使用。
I2C 信号使用 WIRED-OR 约定,您驱动 'Z' 但您必须测试 '0'
或 '1'
具体来说,逻辑高可以表示为 'H'
或 '1'
,前者是 '1'
的弱形式。这意味着 '1'
的测试可能会合成正确的东西,但模拟将不再有效...
所以发生的事情是综合看到 SCL 的无效逻辑,这允许它删除批次。
解决方案:
首先,在您的测试台中,在 SCL 和 SDA 上永久驱动 'H'
。因为它很弱,(像引体向上),'0'
可以覆盖它。
其次,显式测试 '0'
:
scl_internal <= 0 when SCL = '0' else '1';
scl2x_internal <= '0' when SCL2X = '0' else '1';
或显式测试 'H'
以及 '1'
scl_internal <= '1' when SCL = 'H' or SCL = '1' else SCL;
scl2x_internal <= '1' when SCL2X = 'H' or SCL2X = '1' else SCL2X;
或使用"to_01xz"函数将'H'
和'1'
折叠成相同的值
scl_internal <= '1' when to_01xz(SCL) = '1' else SCL;
scl2x_internal <= '1' when to_01xz(SCL2X) = '1' else SCL2X;
我正在尝试创建一个 I2C 总线,但是我遇到了一个非常棘手的问题 - 在实现的映射部分,我收到警告 MapLib:701 - Signal SDA connected to top level port SDA has been removed.
仔细研究后,我发现这是由 I2C Master 本身的先前警告引起的:Xst:1293 - FF/Latch <sda_internal> has a constant value of 1 in block <IIC_MASTER>. This FF/Latch will be trimmed during the optimization process.
仔细查看,似乎整个状态机都被优化掉了 - 这是为什么和我怎么能阻止它?在下面的逻辑模拟中,它按预期工作,我不知道我到底做了什么让它认为整个状态机是多余的!。
我在下面放了我希望看到的图像,然后是描述 I2C 主模块和顶层模块的两个代码块。非常感谢任何帮助!
顶级
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY I2CBus IS
PORT(
SYSCLK_N : IN STD_LOGIC; --system 200MHz differential clock
SYSCLK_P : IN STD_LOGIC;
BTN : IN STD_LOGIC; -- to manually change reset
LED : OUT STD_LOGIC; --to observe reset value
SCL : OUT STD_LOGIC;
SDA : INOUT STD_LOGIC
);
END I2CBus;
ARCHITECTURE behavior OF I2CBus IS
-------------------DECLARE MASTER & SLAVE COMPONENTS------------------------
COMPONENT IIC_MASTER
PORT(SCL : IN STD_LOGIC;
SCL2X : IN STD_LOGIC;
RESET_N : IN STD_LOGIC;
ENA : IN STD_LOGIC;
ADR : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
REG : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
RW : IN STD_LOGIC;
DAT_WR : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
BUSY : OUT STD_LOGIC;
SDA : INOUT STD_LOGIC;
ACK_ERR : BUFFER STD_LOGIC);
END COMPONENT IIC_MASTER;
COMPONENT DCM
PORT(
SYSCLK_P : IN STD_LOGIC; -- CLOCK IN PORTS 200MHZ DIFFERENTIAL
SYSCLK_N : IN STD_LOGIC;
-- CLOCK OUT PORTS
SYSCLK : OUT STD_LOGIC
);
END COMPONENT;
COMPONENT CLK_DIVIDER
GENERIC(INPUT_FREQ : INTEGER;
OUT1_FREQ : INTEGER;
OUT2_FREQ : INTEGER);
PORT(SYSCLK : IN STD_LOGIC;
RESET_N : IN STD_LOGIC;
OUT1 : OUT STD_LOGIC;
OUT2 : OUT STD_LOGIC);
END COMPONENT CLK_DIVIDER;
-------------------I2C MASTER ONLY SIGNALS-----------------
--Inputs
signal reset_n : std_logic; --active high
--Outputs
signal busy : std_logic;
signal ack_err : std_logic;
-----------------------------------------------------------
signal SCL_internal : std_logic;
signal SCL2X_internal : std_logic;
signal sysclk : std_logic;
BEGIN
--------------------I2C_master Instantiation-------------------
master : IIC_Master
port map(
SCL => SCL_internal, --map constant data to send over I2C for now
SCL2X => SCL2X_internal,
RESET_N => RESET_N,
ENA => '1', --hold enable on
ADR => "1011001", --keep address constant
REG => x"AA", --keep target register constant
RW => '0', --always read
DAT_WR => x"77", --keep data to send constant
BUSY => BUSY,
SDA => SDA,
ACK_ERR => ACK_ERR
);
DCM_CLK : DCM
port map(
SYSCLK_P => SYSCLK_P,
SYSCLK_N => SYSCLK_N,
SYSCLK => sysclk --generate 200 MHz clock from system clocks
);
Clk_Div : Clk_Divider --Divide 200MHz clock down to frequencies that can be
generic map( --seen on an oscilloscope
INPUT_FREQ => 200000000, --200 MHz input
OUT1_FREQ => 10, --10 Hz output
OUT2_FREQ => 20 --20 Hz output
)
port map(
SYSCLK => sysclk, --system clock
RESET_N => reset_n, --reset
OUT1 => scl_internal,
OUT2 => scl2x_internal
);
----------------Mappings---------------------------
reset_n <= not BTN; --reset low when button pressed
LED <= not reset_n; --light LED when reset
SCL <= 'Z' when scl_internal = '1' else scl_internal;
end behavior;
I2C 主模块
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity IIC_MASTER IS
PORT(SCL : IN STD_LOGIC; --SCL clock
SCL2X : IN STD_LOGIC; --SCL x2 rate clock
RESET_N : IN STD_LOGIC; --ACTIVE LOW
ENA : IN STD_LOGIC; --ENABLE ACTIVE HIGH
ADR : IN STD_LOGIC_VECTOR(6 DOWNTO 0); --TARGET ADDRESS
REG : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --TARGET REGISTER
RW : IN STD_LOGIC; --READ LOW, WRITE HIGH
DAT_WR : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --DATA TO WRITE TO SLAVE
BUSY : OUT STD_LOGIC; --HIGH WHEN BUSY
SDA : INOUT STD_LOGIC; --SERIAL DATA ON BUS
ACK_ERR : BUFFER STD_LOGIC); --FLAG IF WRONG ACK FROM SLAVE
END IIC_MASTER;
architecture BEHAVIORAL of IIC_MASTER is
type state is (begn, ready, start, command, cmd_ack, reg_cmd, reg_ack, wr, rd, data_ack, stop);
signal i2cstate : state;
signal sda_internal : std_logic := '1'; --internal SDA
signal scl_internal : std_logic;
signal scl2x_internal : std_logic;
signal addr_rw : std_logic_vector(7 downto 0); --latched address (6-0) + rw (7)
signal reg_tx : std_logic_vector(7 downto 0); --latched slave register
signal data_tx : std_logic_vector(7 downto 0); -- data to write to slave
signal bit_cnt : integer range 0 to 7 := 7; --number of bits sent in transaction
begin
------state machine logic & writing to SDA on data clk rising edge-----
state_machine : process(scl2x_internal)
begin
if falling_edge(scl2x_internal) then
if reset_n = '0' then --when reset is low, set to ready i2cstate <= ready; --NOT IN TEMPLATE
busy <= '1'; --set to busy
sda_internal <= '1'; --disable sda to Z
ACK_ERR <= '0'; --clear error/ack flag
bit_cnt <= 7; --reset bit count
i2cstate <= ready;
else
if scl_internal = '0' then --in middle of clock low
case i2cstate is
when begn => i2cstate <= ready; --unconditional transition
when ready => if ENA = '1' then
BUSY <= '1'; --set busy
addr_rw <= ADR & RW; --get address & rw (concatenate)
reg_tx <= REG; --get target register
data_tx <= DAT_WR; --Collect data to write from port
i2cstate <= start; --update state
else
BUSY <= '0'; --set not busy
i2cstate <= ready; --remain ready
end if;
when start =>
BUSY <= '1'; --ensure busy remains on
bit_cnt <= 7; --reset bit counter for bytes
ACK_ERR <= '0';
sda_internal <= addr_rw(bit_cnt); --put first command bit on bus
i2cstate <= command; -- also an unconditional transition
when command =>
if bit_cnt = 0 then
sda_internal <= '1'; --set high for acknowledge
bit_cnt <= 7; --reset bit counter for bytes
i2cstate <= cmd_ack;
else
bit_cnt <= bit_cnt - 1; --decrement bit count
sda_internal <= addr_rw(bit_cnt - 1); --send next address bit on bus
i2cstate <= command; --stay in this state until command is sent
end if;
when cmd_ack =>
sda_internal <= reg_tx(bit_cnt); --write first register bit
i2cstate <= reg_cmd; --go to register sending state
when reg_cmd =>
if bit_cnt = 0 then --register transmitted
sda_internal <= '1'; --release internal sda for acknowledgement
bit_cnt <= 7; --reset bit count
i2cstate <= reg_ack; --go to reg ack
else
bit_cnt <= bit_cnt - 1; --decrement
sda_internal <= reg_tx(bit_cnt - 1); --write next reg bit to bus
i2cstate <= reg_cmd; --keep writing
end if;
when reg_ack =>
if addr_rw(0) = '0' then -- if read/write is high, read
sda_internal <= data_tx(bit_cnt); --write first data bit
i2cstate <= wr; -- go to write state
else --else if low, write
sda_internal <= '1'; --release internal sda for reading
i2cstate <= rd; --go to read state
end if;
when wr =>
BUSY <= '1'; --ensure busy flag still high
if bit_cnt = 0 then --byte transmitted
sda_internal <= '1'; --release internal sda for acknowledgement
bit_cnt <= 7; --reset bit count
BUSY <= '0'; -- data all written, so lower busy to notify others
i2cstate <= data_ack; --go to slav ack
else
bit_cnt <= bit_cnt - 1; --decrement
sda_internal <= data_tx(bit_cnt - 1); --write next bit to bus
i2cstate <= wr; --keep writing
end if;
when rd => null;
when data_ack => --acknowledge write
if ENA = '1' then --continue transaction
BUSY <= '0'; -- accept continue by keeping busy low
addr_rw <= ADR & RW; --fetch next address & command
data_tx <= DAT_WR; --fetch next data to write
reg_tx <= REG; --fetch next register
if addr_rw = ADR & RW and reg_tx = REG then --if the same location
sda_internal <= DAT_WR(bit_cnt); --write first bit of data
i2cstate <= wr; --go to write state
else --continue next transaction with a new read/write or slave
i2cstate <= start; --continue from start
end if;
else
i2cstate <= stop; --transaction done, go to end
sda_internal <= '0';
end if;
when stop =>
BUSY <= '0'; --set not busy
i2cstate <= ready;
end case;
elsif scl_internal = '1' then --in middle of clock high
case i2cstate is
when start => sda_internal <= '0';
when stop => sda_internal <= '1';
when cmd_ack | reg_ack | data_ack =>
if (SDA /= '0' or ACK_ERR = '1') then
ACK_ERR <= '1'; --no acknowledge or prev. error
end if;
when others => null;
end case;
end if;
end if;
end if;
end process;
scl_internal <= '1' when SCL = 'Z' else SCL;
scl2x_internal <= '1' when SCL2X = 'Z' else SCL2X;
SDA <= 'Z' when sda_internal = '1' else '0';
end BEHAVIORAL;
scl_internal <= '1' when SCL = 'Z' else SCL;
scl2x_internal <= '1' when SCL2X = 'Z' else SCL2X;
您不能测试与 'Z'
的相等性,synth 应该警告这些。仿真可以,但是没有硬件块以这种方式供合成使用。
I2C 信号使用 WIRED-OR 约定,您驱动 'Z' 但您必须测试 '0'
或 '1'
具体来说,逻辑高可以表示为 'H'
或 '1'
,前者是 '1'
的弱形式。这意味着 '1'
的测试可能会合成正确的东西,但模拟将不再有效...
所以发生的事情是综合看到 SCL 的无效逻辑,这允许它删除批次。
解决方案:
首先,在您的测试台中,在 SCL 和 SDA 上永久驱动 'H'
。因为它很弱,(像引体向上),'0'
可以覆盖它。
其次,显式测试 '0'
:
scl_internal <= 0 when SCL = '0' else '1';
scl2x_internal <= '0' when SCL2X = '0' else '1';
或显式测试 'H'
以及 '1'
scl_internal <= '1' when SCL = 'H' or SCL = '1' else SCL;
scl2x_internal <= '1' when SCL2X = 'H' or SCL2X = '1' else SCL2X;
或使用"to_01xz"函数将'H'
和'1'
折叠成相同的值
scl_internal <= '1' when to_01xz(SCL) = '1' else SCL;
scl2x_internal <= '1' when to_01xz(SCL2X) = '1' else SCL2X;