Manage high impedance ‘Z’ state with Chisel

In previous (french) article, I described a technic to integrate a Chisel3 component named “TapTempo” in the APF27 board.  This board is made with an i.MX27 CPU and a Spartan3A FPGA.

To communicate with the FPGA, the i.MX27 is plugged on it with a memory bus named «WEIM». But the data signals used in WEIM are also connected on flash nand memory on the board, and this memory contain the Linux kernel and rootfs.

It’s not a problem if the FPGA “release” the data bus in high impedance when not used. Ant it’s the case when we use ctrl signal correctly (with oen signal -> output enable not)

In the first design, as tempo data are only output values read by the CPU, it was declared as “output” in chisel bundle :

class APF27TapTempo extends Module {
val io = IO(new Bundle {
val data = Output(UInt(16.W))

But with this data description, electrical value on physical data bus is continuously held by the FPGA. And when i.MX want to access nand flash memory it can’t. And we cannot start Linux as it require reading value in flash memory.

The solution is to set data bus as bidirectional signal and use WEIM signal oen to enable output on data bus, and when oen is not active the data bus is left on high impedance state ‘Z’.

In chisel, to manage bidirectional signal, we have to use the type “analog” as it :

class APF27TapTempo extends Module {
  val io = IO(new Bundle {
    val oen = Input(Bool())
    val data = Analog(16.W)

And simply deactivate data when oen is ‘1’ ?

when(oen === '1'){
}.otherwize {
   data := taptempovalue

Would be so easy … But no, we can’t do that with Chisel3 🙁

In FPGA or ASIC, the high state value doesn’t exist in fact. All signals under FPGA must be input or output and set to ‘0’ or ‘1’. No other values are actually supported.

Other “high impedance” values like ‘Z’, ‘H’ or ‘L’ can’t be synthesized under the fpga. But these values (mostly ‘Z’ state in fact) can be synthesized on the boundary. If a bidirectional signal going out of FPGA, the xilinx synthesizer can instantiate an IOBUF and manage the high state.

Chisel doesn’t manage this high state. It’s a deliberate choice from chisel developers team to simplify Chisel3. But for some design, like on APF27 we need to implement it.

Verilog manage this kind of signal state, then we can write a verilog code to write ‘Z’ state and include it in our chisel design.

To do this, we use a module chisel type named BlackBox. And as we want to add directly verilog source in the chisel code we will add the Trait HasBlackBoxInline like described bellow :

// import verilog code with HasBlackBoxInline
import chisel3.util.{HasBlackBoxInline, HasBlackBoxResource}
// Describe blackbox with verilog code
class Apf27Bus extends BlackBox with HasBlackBoxInline {
  val io = IO(new Bundle {
    val dataout = Output(UInt(16.W))
    val dataio = Analog(16.W)
    val datain = Input(UInt(16.W))
    val oe = Input(Bool())

    |module Apf27Bus(
    |     output [15:0] dataout,
    |     inout [15:0] dataio,
    |     input [15:0] datain,
    |     input oen);
    |   assign dataio = (oen == 'b0) ? datain : 'bzzzzzzzzzzzzzzzz;
    |   assign dataout = dataio;
// Verilog will be written in separate file named Apf27Bus.v

// Then connect it in "Top" module
  val iobuf = Module(new Apf27Bus) <> := dataout <> io.oen

As dataio is Analog type, we have to declare also analog on top bundle module :

class APF27TapTempo extends Module {
  val io = IO(new Bundle {
    val data = Analog(16.W)
    val oen = Input(Bool())
    val button = Input(Bool())

With this method, we keep all our sources codes in the chisel sources and our high ‘Z’ bidirectional port is correctly synthesized with classical commercials software.

All sources of this project described in this article are available on my github project TapTempoChisel.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *