Analog and inout

Introduction

You can define native tristate signals by using the Analog/inout features. These features were added for the following reasons:

  • Being able to add native tristate signals to the toplevel (it avoids having to manually wrap them with some hand-written VHDL/Verilog)

  • Allowing the definition of blackboxes which contain inout pins

  • Being able to connect a blackbox’s inout pin through the hierarchy to a toplevel inout pin.

As those feature were only added for convenience, do not do other fancy stuff with it. If you want to model a component like an memory mapped GPIO peripheral, please use the TriState/TriStateArray bundles from the spinal lib, which keep the true nature of the tristate driver.

Analog

Analog is the keyword which allows a signal to be defined as something … analog, which in the digital world could mean ‘0’, ‘1’, or ‘Z’.

For instance :

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

inout is the keyword which allows you to set an Analog signal as a component inout.

For instance:

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 of the component
  }
}

InOutWrapper

InOutWrapper is a tool which allows you to tranform all master TriState/TriStateArray/ReadableOpenDrain bundles of a component into native inout(Analog(…)) signals. It allows you to keep all your hardware description without any Analog/inout things, and then transform the toplevel to make it synthesis ready.

For instance:

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)))

Will generate:

entity Apb3Gpio is
  port(
    io_gpio : inout std_logic_vector(31 downto 0); -- This io_gpio was originaly 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;

Instead of:

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;

Manually driving Analog bundles

If an Analog bundle is not driven, it will default to being high-Z. Therefore to manually implement a tristate driver (in case the InOutWrapper can’t be used for some reason) you have to conditionally drive the signal. To manually connect a TriState signal to an Analog bundle:

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