模拟信号和输入输出

简介

您可以使用 Analog/inout 功能定义三态信号。添加这些功能的原因有:

  • 能够将三态信号添加到顶层(它避免了必须用一些手写的 VHDL/Verilog 包装它们)。

  • 允许定义包含 inout 引脚的黑盒。

  • 能够通过层次结构将黑盒的 inout 引脚连接到顶级 inout 引脚。

由于这些功能只是为了方便而添加的,因此请不要尝试使用三态逻辑的其他花哨的东西。

如果您想对内存映射 GPIO 外设等组件进行建模,请使用 Spinal 标准库中的 TriState/TriStateArray 线束,它抽象了三态驱动程序的本质。

模拟信号

Analog 是一个关键字,它允许将信号定义为模拟信号,在数字世界中可能意味着 0, 1, 或 Z (断开、高阻状态)。

例如:

case class SdramInterface(g : SdramLayout) extends Bundle {
  val DQ    = Analog(Bits(g.dataWidth bits)) // Bidirectional data bus
  val DQM   = Bits(g.bytePerWord bits)
  val ADDR  = Bits(g.chipAddressWidth bits)
  val BA    = Bits(g.bankWidth bits)
  val CKE, CSn, CASn, RASn, WEn  = Bool()
}

输入/出

inout 是允许您将 Analog 信号设置为双向(“in”和“out”)信号的关键字。

例如:

case class SdramInterface(g : SdramLayout) extends Bundle with IMasterSlave {
  val DQ    = Analog(Bits(g.dataWidth bits)) // Bidirectional data bus
  val DQM   = Bits(g.bytePerWord bits)
  val ADDR  = Bits(g.chipAddressWidth bits)
  val BA    = Bits(g.bankWidth bits)
  val CKE, CSn, CASn, RASn, WEn  = Bool()

  override def asMaster() : Unit = {
    out(ADDR, BA, CASn, CKE, CSn, DQM, RASn, WEn)
    inout(DQ) // Set the Analog DQ as an inout signal of the component
  }
}

输入/出包装器

InOutWrapper 是一个工具,允许您将组件的所有 master TriState/TriStateArray/ReadableOpenDrain 线束转换为 inout(Analog(.. .)) 信号。它允许您保持硬件描述不受任何 Analog/inout 事物的影响,然后转换顶层以备综合。

例如:

case class Apb3Gpio(gpioWidth : Int) extends Component {
  val io = new Bundle {
    val gpio = master(TriStateArray(gpioWidth bits))
    val apb  = slave(Apb3(Apb3Gpio.getApb3Config()))
  }
  ...
}

SpinalVhdl(InOutWrapper(Apb3Gpio(32)))

这将生成:

entity Apb3Gpio is
  port(
    io_gpio : inout std_logic_vector(31 downto 0); -- This io_gpio was originally a TriStateArray Bundle
    io_apb_PADDR : in unsigned(3 downto 0);
    io_apb_PSEL : in std_logic_vector(0 downto 0);
    io_apb_PENABLE : in std_logic;
    io_apb_PREADY : out std_logic;
    io_apb_PWRITE : in std_logic;
    io_apb_PWDATA : in std_logic_vector(31 downto 0);
    io_apb_PRDATA : out std_logic_vector(31 downto 0);
    io_apb_PSLVERROR : out std_logic;
    clk : in std_logic;
    reset : in std_logic
  );
end Apb3Gpio;

而不是:

entity Apb3Gpio is
  port(
    io_gpio_read : in std_logic_vector(31 downto 0);
    io_gpio_write : out std_logic_vector(31 downto 0);
    io_gpio_writeEnable : out std_logic_vector(31 downto 0);
    io_apb_PADDR : in unsigned(3 downto 0);
    io_apb_PSEL : in std_logic_vector(0 downto 0);
    io_apb_PENABLE : in std_logic;
    io_apb_PREADY : out std_logic;
    io_apb_PWRITE : in std_logic;
    io_apb_PWDATA : in std_logic_vector(31 downto 0);
    io_apb_PRDATA : out std_logic_vector(31 downto 0);
    io_apb_PSLVERROR : out std_logic;
    clk : in std_logic;
    reset : in std_logic
  );
end Apb3Gpio;

手动驱动模拟线束

如果 Analog 线束没有被驱动,它将默认为高阻态。因此,要手动实现三态驱动程序(以防因某种原因无法使用 InOutWrapper 类型),您必须有条件地驱动信号。

手动将 TriState 信号连接到 Analog 线束:

case class Example extends Component {
  val io = new Bundle {
    val tri = slave(TriState(Bits(16 bits)))
    val analog = inout(Analog(Bits(16 bits)))
  }
  io.tri.read := io.analog
  when(io.tri.writeEnable) { io.analog := io.tri.write }
}