Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chisel: mapping separate input and output ports to inout pin

Tags:

chisel

I'm generating Verilog from Chisel 3 source code and mapping the Verilog's top module ports to FPGA pins by using an UCF file.

I have a set of inout pins in my design (SDRAM data pins), which on the Chisel side have to be expressed as separate input and output ports. The problem is, I can't (AFAIK) then map the Verilog input ports and output ports to the same FPGA pin (if I were directly writing Verilog those would be a single inout signal, so that wouldn't be an issue) and I don't know of anyway to coerce Chisel 3 to produce a single Verilog inout port from two input/output Chisel ports.

How is this usually solved in Chisel (3)?

like image 994
Luís Marques Avatar asked Dec 04 '16 17:12

Luís Marques


1 Answers

We are working on some level of support for Verilog inout in Chisel 3, but until that API is fully fleshed out you should write a Verilog wrapper that converts from from inout to an input, output, and some direction.

For example, say I have some Verilog with an inout pin that can be used to set or read from some register:

module Inout(
  input clock,
  input write,
  inout [31:0] data
);

  reg [31:0] value;

  assign data = (write) ? 32'dz : value;

  always @(posedge clock) begin
    if (write)
      value <= data;
    else
      value <= value;
  end
endmodule

With a simple wrapper, I can expose a different interface that does not use inout:

module InoutWrapper(
  input clock,
  input write,
  input [31:0] dataIn,
  output [31:0] dataOut
);
  wire [31:0] bus;

  assign bus = (write)? dataIn : 32'dz;
  assign dataOut = bus;

  Inout mod (
    .clock(clock),
    .write(write),
    .data(bus)
  );
endmodule

This wrapper interface can be used in a Chisel design as a BlackBox:

class InoutWrapper extends BlackBox {
  val io = IO(new Bundle {
    val clock = Input(Clock())
    val write = Input(Bool())
    val dataIn = Input(UInt(32.W))
    val dataOut = Output(UInt(32.W))
  })
}

And here's a bonus simple sanity test to show that it works:

class InoutTester extends BasicTester {

  val mod = Module(new InoutWrapper)

  val (cycle, done) = Counter(true.B, 4)
  when (done) { stop(); stop() }

  mod.io.clock := this.clock // BlackBoxes require explicit clock assignment
  mod.io.write := false.B // default assignments
  mod.io.dataIn := "hdeadbeef".U

  when (cycle === 1.U) {
    mod.io.write := true.B
    mod.io.dataIn := 123.U
  }
  when (cycle === 2.U) {
    assert(mod.io.dataOut === 123.U)
  }
}

If the inout port is at the top of the design, you can create a similar kind of wrapper for the top of your design.

like image 106
Jack Koenig Avatar answered Jan 01 '23 08:01

Jack Koenig