综合 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;