综合综合/实施

时间:2015-12-02 15:41:26

标签: vhdl state-machine xilinx synthesis xilinx-ise

我试图创建一个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主模块和顶级模块的两个代码块。任何帮助都非常感谢!

enter image description here

顶级

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;

1 个答案:

答案 0 :(得分:2)

scl_internal   <= '1' when SCL = 'Z' else SCL;
scl2x_internal <= '1' when SCL2X = 'Z' else SCL2X;

您无法测试'Z'的相等性,而合成器应该警告这些。仿真可以,但没有硬件块可以通过这种方式进行合成。

I2C信号使用WIRED-OR约定,你驱动'Z'但你必须测试'0''1'具体来说,逻辑高可由'H'或{{ 1}},前者是'1'的弱形式。这意味着'1'的测试可能会合成正确的事情,但模拟将不再有效......

所以正在发生的事情是综合看到了SCL的无效逻辑,它允许它删除该批次。

解决方案:

首先,在您的测试平台中,永久驱动SCL和SDA上的'1'。因为它很弱,(如上拉),'H'可以覆盖它。

其次,要么明确测试'0'

'0'

或明确测试scl_internal <= 0 when SCL = '0' else '1'; scl2x_internal <= '0' when SCL2X = '0' else '1'; 以及'H'

'1'

或使用“to_01xz”功能将scl_internal <= '1' when SCL = 'H' or SCL = '1' else SCL; scl2x_internal <= '1' when SCL2X = 'H' or SCL2X = '1' else SCL2X; 'H'折叠为相同的值

'1'