VHDL 中的双向到双向

Bidirectional to bidirectional in VHDL

所以我听说无法在不知道协议(或没有控制线)的情况下通过双向数据端口。 (参见 tie two inout together vhdl

但是,我真的很想这样做,而且我真的很想不必知道协议。所以...

我想从 SIM 智能卡(从 phone)传递数据线。目前一切正常,除了 DATA 线,它是双向的。

我还没有证实这一点,但我的范围界定暗示这条线被拉高了,任何一方都可以根据需要将其拉低。我想利用外部拉高的线。

我想尝试下面注释代码中列出的两个选项:

architecture Behavioral of SIM_Select_Test_A is

begin

process(MOD_CLK, MOD_RST)
begin
    SIM_RST     <= MOD_RST;
    SIM_CLK <= MOD_CLK;
end process;

-- OPTION ONE1
--  process(MOD_CLK, MOD_DATA, SIM_DATA)
--  begin
--      IF MOD_DATA = '0' THEN
--          SIM_DATA <= '0';
--      ELSE
--          IF SIM_DATA = '0' THEN
--              MOD_DATA <= '0';
--          ELSE
--              MOD_DATA <= MOD_DATA;
--              SIM_DATA <= SIM_DATA;
--          END IF;
--      END IF;
--  end process;

-- OPTION 2
--  process(MOD_CLK, MOD_DATA, SIM_DATA)
--  begin
--      IF MOD_DATA = '0' THEN
--          SIM_DATA <= '0';
--      ELSE
--          IF SIM_DATA = '0' THEN
--              MOD_DATA <= '0';
--          ELSE
--              MOD_DATA <= 'Z';
--              SIM_DATA <= 'Z';
--          END IF;
--      END IF;
--  end process;

end Behavioral;

有人可以确认如果我将 SIM_DATA 驱动为低电平,我将不会落入第二个 ELSE 并因此将 MOD_DATA 驱动为低电平(即进入某种循环逻辑)

如果我应该走这条路或者我是否绝对需要了解该协议,请发表任何评论。如果是这样,我想我会开始研究 :(

提前致谢, 库尔特

编辑: 添加我的实体声明:

entity SIM_Select_Test_A is
Port ( SIM_VCC : OUT STD_LOGIC;
       SIM_DATA : inout  STD_LOGIC;
       SIM_RST : out  STD_LOGIC;
       SIM_CLK : out  STD_LOGIC;
          MOD_VCC : in STD_LOGIC;
       MOD_DATA : inout  STD_LOGIC;
       MOD_RST : in  STD_LOGIC;
       MOD_CLK : in  STD_LOGIC);
    attribute bufg : string;
    attribute bufg of MOD_CLK : signal is "CLK";
    attribute bufg of MOD_DATA : signal is "OE";
end SIM_Select_Test_A;

编辑 2: 哇,谢谢这么详细的回复。 是的,我明白你在说什么。我想我希望的是我可以在代码中添加一些智能,以便在 CPLD 控制电线并优先处理 wire1 时意识到这一点。所以你的评论让我正式确定了我的想法,这就是我得到的。为伪代码道歉,但我希望它能让事情更清楚,并且当我在脑海中编译它时它总是工作得很好 :)

IF (wire1 = '0' AND flag = '0') THEN
    wire2 <= '0' <--here wire1 gets priority and wire2 is controlled based on wire1.  My hope is that when it is at this point in the code then it does NOT fall into the else statement.
ELSE  <-- IF (wire1 is NOT low OR there is a flag) THEN check if wire2 is low (which in my head seems slightly different than elsif
    IF (wire2 = '0') THEN
       wire1 <= '0';
       flag  <= '1'; <-- I think this is where there might be a problem.  I am trying to use the flag to tell the outer IF that the CPLD holding wire1 low and to ignore it
    ELSE <--neither are being held low externally
       wire1 <= 'HIZ';
       wire2 <= 'HIZ';
       flag  <= '0';
    END IF
END IF;

今天我会看看是否可以模拟,但感谢您提出的任何意见。

EDIT 此答案仅适用于无法对拦截总线上的数据传输做出任何假设的情况。请也看看 .

即使使用由电阻上拉的漏极开路/集电极开路总线也无法做到这一点。电阻器实际上可以集成到其中一个 IC 中,但它们始终放置在焊盘和 I/O 驱动器之间,因此位于总线上。

拦截看起来像这样:IC1 <--wire1--> FPGA <--wire2--> IC2

假设截获的总线在启动后处于空闲状态,那么两条线都会被电阻拉高。如果此时IC1拉低wire1,FPGA也会拉低wire2。但是现在,FPGA 也会读取 wire2 被拉低,因此也会拉低 wire1。现在,两条线都被 FPGA 下拉。即使 IC1 禁用其输出驱动器,该状态也会保留。

如果你不相信,试试这个 VHDL 代码。

library ieee;
use ieee.std_logic_1164.all;

entity intercept_open_drain_bus is
  port (
    wire1 : inout std_logic;
    wire2 : inout std_logic);
end entity intercept_open_drain_bus;

architecture rtl of intercept_open_drain_bus is
begin
  -- Assume port is pulled-up externally.
  wire1 <= '0' when wire2 = '0' else 'Z';
  wire2 <= '0' when wire1 = '0' else 'Z';
end architecture rtl;

这里是测试平台运行上面的场景:

library ieee;
use ieee.std_logic_1164.all;

entity intercept_open_drain_bus_tb is
end entity intercept_open_drain_bus_tb;

architecture sim of intercept_open_drain_bus_tb is
  signal wire1 : std_logic;
  signal wire2 : std_logic;
begin
  DUT: entity work.intercept_open_drain_bus
    port map (
      wire1 => wire1,
      wire2 => wire2);

  -- external PULLUP resistor
  wire1 <= 'H';
  wire2 <= 'H';

  WaveGen_Proc: process
  begin
    -- both far-end ICs have their outputs disabled
    wire1 <= 'Z';
    wire2 <= 'Z';
    wait for 1 ns; 
    assert (wire1 = 'H') and (wire2 = 'H') report "initial pullup failed." severity error;

    -- IC 1 pulls down wire 1
    wire1 <= '0';
    wait for 1 ns; 
    assert (wire2 = '0') report "passing from wire1 failed." severity error;

    -- IC 1 disables its output again
    wire1 <= 'Z';
    wait for 1 ns; 
    assert (wire1 = 'H') and (wire2 = 'H') report "pullup after transfer failed." severity error;
    wait;
  end process WaveGen_Proc;
end architecture sim;

这是显示最后一个测试用例失败的模拟器输出(在黄色标记的右侧):

如果至少可以对拦截的双向总线的数据传输做出一些假设,则此答案适用。如果不能做出任何假设,那么 仍然成立。

拦截看起来像这样:IC1 <--wire1--> FPGA <--wire2--> IC2

一个解决方案是可能的,如果至少可以做出以下假设:

  • 总线在数据传输IC1-->IC2和IC2-->IC1之间空闲。

  • FPGA 可以由时钟(来自外部振荡器)驱动,该时钟至少比总线信号速率快 10 倍。

该解决方案需要一个有限状态机,其中状态跟踪哪一侧(IC1 或 IC2)实际拉低了线路。如果一个 IC 拉低了他的电线,FPGA 就会拉低另一个。如果 IC 释放导线并且导线被电阻上拉,则 FPGA 也会释放另一根导线。但是现在,我们必须等到另一根线被其自身的电阻上拉后,才能检查该线是否在另一个方向传输数据。

代码如下:

library ieee;
use ieee.std_logic_1164.all;

entity intercept_open_drain_bus2 is
  port (
    clock : in std_logic;
    wire1 : inout std_logic;
    wire2 : inout std_logic);
end entity intercept_open_drain_bus2;

architecture rtl of intercept_open_drain_bus2 is
  type state_t is (IDLE, PULLDOWN1, WAIT1, PULLDOWN2, WAIT2);
  signal state : state_t := IDLE;
begin

  -- Moore outputs from finite state machine
  wire1 <= '0' when state = PULLDOWN1 else 'Z';
  wire2 <= '0' when state = PULLDOWN2 else 'Z';

  -- finite state machine
  process(clock)
  begin
    if rising_edge(clock) then
      case state is
        when IDLE =>
          if wire2 = '0' then
            state <= PULLDOWN1;
          elsif wire1 = '0' then
            state <= PULLDOWN2;
          end if;

        when PULLDOWN1 =>
          if wire2 /= '0' then -- 'H' or '1'
            state <= WAIT1;
          end if;

        when WAIT1 => -- wait until wire1 is pulled-up by the resistor
          if wire1 /= '0' then
            state <= IDLE;
          end if;

        when PULLDOWN2 =>
          if wire1 /= '0' then -- 'H' or '1'
            state <= WAIT2;
          end if;

        when WAIT2 => -- wait until wire2 is pulled-up by the resistor
          if wire2 /= '0' then
            state <= IDLE;
          end if;
      end case;
    end if;
  end process;
end architecture rtl;

这是测试平台:

library ieee;
use ieee.std_logic_1164.all;

entity intercept_open_drain_bus2_tb is
end entity intercept_open_drain_bus2_tb;

architecture sim of intercept_open_drain_bus2_tb is
  signal clock   : std_logic := '1';
  signal wire1   : std_logic;
  signal wire2   : std_logic;
  signal STOPPED : boolean   := false;

begin
  DUT: entity work.intercept_open_drain_bus2
    port map (
      clock => clock,
      wire1 => wire1,
      wire2 => wire2);

  -- 100 MHz FPGA clock from external oscillator
  clock <= not clock after 5 ns when not STOPPED;

  -- external PULLUP resistor
  wire1 <= 'H';
  wire2 <= 'H';

  WaveGen_Proc: process
  begin
    -- both far-end ICs have their outputs disabled
    wire1 <= 'Z';
    wire2 <= 'Z';
    wait until rising_edge(clock);
    assert (wire1 = 'H') and (wire2 = 'H') report "initial pullup failed." severity error;

    -- IC 1 pulls down wire 1 for at least 10 FPGA clock cycles
    wire1 <= '0';
    wait until rising_edge(clock);
    -- wire2 is changing here in the real world
    for i in 2 to 10 loop
      wait until rising_edge(clock);
      assert (wire2 = '0') report "passing from wire1 failed." severity error;
    end loop;

    -- IC 1 disables its output again for at least 10 FPGA clock cycles
    wire1 <= 'Z';
    wait until rising_edge(clock);
    -- wire2 is changing here in the real world
    for i in 2 to 10 loop
      wait until rising_edge(clock);
      assert (wire1 = 'H') and (wire2 = 'H') report "pullup after transfer failed." severity error;
    end loop;

    -- IC 2 pulls down wire 1 for at least 10 FPGA clock cycles
    wire2 <= '0';
    wait until rising_edge(clock);
    -- wire1 is changing here in the real world
    for i in 2 to 10 loop
      wait until rising_edge(clock);
      assert (wire1 = '0') report "passing from wire2 failed." severity error;
    end loop;

    -- IC 2 disables its output again for at least 10 FPGA clock cycles
    wire2 <= 'Z';
    wait until rising_edge(clock);
    -- wire1 is changing here in the real world
    for i in 2 to 10 loop
      wait until rising_edge(clock);
      assert (wire2 = 'H') and (wire1 = 'H') report "pullup after transfer failed." severity error;
    end loop;

    STOPPED <= true;
    wait;
  end process WaveGen_Proc;
end architecture sim;

现在,模拟器输出看起来不错: