направление оператора в Chisel?

operator-keyword #direction #chisel

#оператор-ключевое слово #направление #chisel

Вопрос:

В 2.6 chiseltest из учебных пособий chisel-bootcamp есть пример создания очереди с использованием Decoupled интерфейса:

 case class QueueModule[T <: Data](ioType: T, entries: Int) extends MultiIOModule {
  val in = IO(Flipped(Decoupled(ioType)))
  val out = IO(Decoupled(ioType))
  out <> Queue(in, entries)
}
 

направление <> оператора в последней строке out <> Queue(in, entries) действительно сбивает меня с толку, поскольку я проверяю <> operator класса DecoupledIO в chisel-api и получаю определение: «Соедините эти данные с этими данными двунаправленно и поэлементно». что означает out , Queue(in, entries) что возврат and должен быть двунаправленным. Тем не менее, я нашел Queue исходный код:

 object Queue
{
  /** Create a queue and supply a DecoupledIO containing the product. */
  @chiselName
  def apply[T <: Data](
      enq: ReadyValidIO[T],
      entries: Int = 2,
      pipe: Boolean = false,
      flow: Boolean = false): DecoupledIO[T] = {
    if (entries == 0) {
      val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits)))
      deq.valid := enq.valid
      deq.bits := enq.bits
      enq.ready := deq.ready
      deq
    } else {
      val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow))
      q.io.enq.valid := enq.valid // not using <> so that override is allowed
      q.io.enq.bits := enq.bits
      enq.ready := q.io.enq.ready
      TransitName(q.io.deq, q)
    }
  }
 

которые возвращаются q.io.deq TransitName методом и q.io.deq определяются следующим образом:

 object DeqIO {
  def apply[T<:Data](gen: T): DecoupledIO[T] = Flipped(Decoupled(gen))
}

/** An I/O Bundle for Queues
  * @param gen The type of data to queue
  * @param entries The max number of entries in the queue.
  */
class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
{ // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.

  /* These may look inverted, because the names (enq/deq) are from the perspective of the client,
   *  but internally, the queue implementation itself sits on the other side
   *  of the interface so uses the flipped instance.
   */
  /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped. */
  val enq = Flipped(EnqIO(gen))
  /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]*/
  val deq = Flipped(DeqIO(gen))
  /** The current amount of data in the queue */
  val count = Output(UInt(log2Ceil(entries   1).W))
}
 

это означает q.io.deq , что он не перевернут DecoupledIO и имеет то же направление интерфейса out . Итак, я действительно хочу знать, как <> это работает в out <> Queue(in, entries) ?

Ответ №1:

Decoupled(data) добавьте протокол квитирования в пакет данных, указанный в параметрах. Если вы объявите этот сигнал, например :

 val dec_data = IO(Decoupled(chiselTypeOf(data)))
 

dec_data объект будет иметь 2 значения квитирования ( ready , valid ) с разными направлениями и одно значение данных.

 myvalue := dec_data.bits
value_is_valid := dec_data.valid  //boolean value in the same direction as data
dec_data.ready := sink_ready_to_receive //boolean value in the oposite data direction
 

Если вы хотите подключиться dec_data к другому пакету DecoupledIO, вы не можете использовать := operator для всего пакета, потому что это однонаправленный operator .
Вы должны выполнять соединение по значению :

 val dec_data_sink = IO(Flipped(Decoupled(chiselTypeOf(data))))

dec_data_sink.bits := dec_data.bits
dec_data_sink.valid := dec_data.valid
dec_data.ready := dec_data_sink.ready
 

С помощью bulk connector <> вы можете избежать этой болезненной связи с :

 dec_data_sink <> dec_data
 

Chisel автоматически соединит правильные сигналы вместе.

Для получения дополнительной документации о соединениях bulks и несвязанном интерфейсе см. Документацию здесь .

Комментарии:

1. но я имею в виду, что я нашел out <> Queue(in, entries) в примере с очередью Queue ‘return, который q.io.deq имеет то же направление, out но не похож на ваш пример, dec_data и dec_data_sink получил другое направление — dec_data развязано и dec_data_sink перевернуто развязано

Ответ №2:

Хорошо, я проверяю Verilog, созданный этим примером:

 module Queue(
  input        clock,
  input        reset,
  output       io_enq_ready,
  input        io_enq_valid,
  input  [8:0] io_enq_bits,
  input        io_deq_ready,
  output       io_deq_valid,
  output [8:0] io_deq_bits
);
......
......
module QueueModule(
  input        clock,
  input        reset,
  output       in_ready,
  input        in_valid,
  input  [8:0] in_bits,
  input        out_ready,
  output       out_valid,
  output [8:0] out_bits
);
  wire  q_clock; // @[Decoupled.scala 296:21]
  wire  q_reset; // @[Decoupled.scala 296:21]
  wire  q_io_enq_ready; // @[Decoupled.scala 296:21]
  wire  q_io_enq_valid; // @[Decoupled.scala 296:21]
  wire [8:0] q_io_enq_bits; // @[Decoupled.scala 296:21]
  wire  q_io_deq_ready; // @[Decoupled.scala 296:21]
  wire  q_io_deq_valid; // @[Decoupled.scala 296:21]
  wire [8:0] q_io_deq_bits; // @[Decoupled.scala 296:21]
  Queue q ( // @[Decoupled.scala 296:21]
    .clock(q_clock),
    .reset(q_reset),
    .io_enq_ready(q_io_enq_ready),
    .io_enq_valid(q_io_enq_valid),
    .io_enq_bits(q_io_enq_bits),
    .io_deq_ready(q_io_deq_ready),
    .io_deq_valid(q_io_deq_valid),
    .io_deq_bits(q_io_deq_bits)
  );
  assign in_ready = q_io_enq_ready; // @[Decoupled.scala 299:17]
  assign out_valid = q_io_deq_valid; // @[cmd2.sc 4:7]
  assign out_bits = q_io_deq_bits; // @[cmd2.sc 4:7]
  assign q_clock = clock;
  assign q_reset = reset;
  assign q_io_enq_valid = in_valid; // @[Decoupled.scala 297:22]
  assign q_io_enq_bits = in_bits; // @[Decoupled.scala 298:21]
  assign q_io_deq_ready = out_ready; // @[cmd2.sc 4:7]
endmodule
 

Я обнаружил, что есть просто входы, соединяющие входы, и выходы, соединяющие выходы между Queue и QueueModule . Насколько я понимаю, существует создание экземпляра Queue модуля в QueueModule , so QueueModule и Queue сопоставление родительских / дочерних модулей, а <> оператор массового подключения соединяет интерфейсы того же пола, что и документация
Итак, я понимаю, что я проигнорировал Queue сам модуль и формат примера:

 case class QueueModule[T <: Data](ioType: T, entries: Int) extends MultiIOModule {
  val in = IO(Flipped(Decoupled(ioType)))
  val out = IO(Decoupled(ioType))
  out <> Queue(in, entries)
}
 

будет соответствовать QueueModule / Queue родительским / дочерним модулям.