Dummy TestIO in the ChipTop module

269 views
Skip to first unread message

Brendon Chetwynd

unread,
Jan 11, 2022, 1:36:58 PM1/11/22
to Chipyard
I need to create some Test I/O @ the ChipTop module that does not get routed any further in the design, but does not get optimized by the FIRRTL compiler.

I am guessing I should be able to create an IOBinder to accomplish this, but I haven't quite found an example of the appropriate constructs.

Jerry Zhao

unread,
Jan 11, 2022, 1:40:51 PM1/11/22
to chip...@googlegroups.com
On Tue, Jan 11, 2022 at 10:37 AM Brendon Chetwynd <bche...@gmail.com> wrote:
I need to create some Test I/O @ the ChipTop module that does not get routed any further in the design, but does not get optimized by the FIRRTL compiler.

I am guessing I should be able to create an IOBinder to accomplish this, but I haven't quite found an example of the appropriate constructs.

--
You received this message because you are subscribed to the Google Groups "Chipyard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chipyard+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/chipyard/b8d32f38-8e57-4760-b617-848540e4e44dn%40googlegroups.com.

Brendon Chetwynd

unread,
Jan 11, 2022, 1:42:51 PM1/11/22
to Chipyard
Looks like an empty response?

Jerry Zhao

unread,
Jan 11, 2022, 1:43:39 PM1/11/22
to chip...@googlegroups.com
In chipyard/generators/chipyard/src/main/scala/example/GCD.scala, look at the CanHavePeripheryGCDModuleImp trait

You want to write your own trait following this pattern.

trait HasMyTestIO extends LazyModuleImp {

  val test_io = IO(new TestIO)
  dontTouch(test_io)
}

Then in DigitalTop.scala, add the trait to DigitalTopModule.

On Tue, Jan 11, 2022 at 10:37 AM Brendon Chetwynd <bche...@gmail.com> wrote:
I need to create some Test I/O @ the ChipTop module that does not get routed any further in the design, but does not get optimized by the FIRRTL compiler.

I am guessing I should be able to create an IOBinder to accomplish this, but I haven't quite found an example of the appropriate constructs.

Brendon Chetwynd

unread,
Jan 11, 2022, 2:28:42 PM1/11/22
to chip...@googlegroups.com
Thanks, Jerry.

Still navigating the various Traits, classes, etc.

Example code:

import chisel3._
import chisel3.util._
import freechips.rocketchip.diplomacy._

trait TestIO extends Bundle {
  val testio_0 = Input(Bool())
  val testio_1 = Input(Bool())
  val testio_2 = Input(Bool())
  val testio_3 = Input(Bool())
}

trait HasTestIO extends LazyModuleImp {

val test_io = IO(new TestIO)
dontTouch(test_io)
}

Build result:
[error] /home/user1/chipyard/generators/asicblocks/src/main/scala/testIO.scala:25:19: trait TestIO is abstract; cannot be instantiated
[error]         val test_io = IO(new TestIO)
[error]                          ^
[error] one error found



Jerry Zhao

unread,
Jan 11, 2022, 2:30:37 PM1/11/22
to chip...@googlegroups.com
TestIO should not be a trait.

class TestIO extends Bundle {...


Brendon Chetwynd

unread,
Jan 11, 2022, 3:36:17 PM1/11/22
to chip...@googlegroups.com
Jerry.... that solved the problem, now onto IOBinders.

I need to create GenericDigitalGPIOCells for each one, understanding that a lot of IOCell pins will be unused/fixed.

I started by looking at the WithGPIOCells and WithSPIFlashIOCells examples in IOBinders...

Given the following testIO.scala:

import chisel3._
import chisel3.util._
import chisel3.experimental.{Analog, IO}
import freechips.rocketchip.diplomacy._

class TestIO extends Bundle {
  val testio = Vec(4, Analog(1.W)) // Not using Analog(4.W) because we can't connect these to IO cells
}

trait HasTestIO extends LazyModuleImp {
  val testio = IO(new TestIO)
  dontTouch(testio)
}

I was tryin to use the following to instantiate an I/O cep for 

class WithTestIOCells extends OverrideIOBinder({
  (system: HasTestIO) => {
    val (ports2d, cells2d) = system.testio.zipWithIndex.map({ case (testio, i) =>
      testio.zipWithIndex.map({ case (pin, j) =>
        val g = IO(Analog(1.W)).suggestName(s"testio_${i}_${j}")
        val iocell = system.p(IOCellKey).gpio().suggestName(s"iocell_testio_${i}_${j}")
        iocell.io.o := pin.o.oval
        iocell.io.oe := pin.o.oe
        iocell.io.ie := pin.o.ie
        pin.i.ival := iocell.io.i
        iocell.io.pad <> g
        (g, iocell)
      }).unzip
    }).unzip
    val ports: Seq[Analog] = ports2d.flatten
    (ports, cells2d.flatten)
  }
}) // WithTestIOCells

This fails with a whole lot of errors.... the first of which is :

 value zipWithIndex is not a member of myBlocks.testIO.TestIO
[error]     val (ports2d, cells2d) = system.testio.zipWithIndex.map({ case (testio, i) =>

There are some simpler Chisel constructions that I am missing, I am sure.


Michael Etzkorn

unread,
Jan 12, 2022, 1:16:58 PM1/12/22
to Chipyard

So the problem with a fair number of the IOBinder examples is they're using parameterization you'll need to understand and implement into your IO to model your IOBinder after them.

The class IOCells either creates or inherits a function called makeIOs that returns a type with access to zipWithIndex (I still haven't learned much of the scala-land stuff, and haven't had need for level of IO parameterization things like GPIOCells and SPICells use, so I'm not gonna pretend I know how IOCells works). 

The good news is for punching out IO like this that aren't parameterized, you don't need to do anything with the config API (that p(IOCellKey) thing). You also don't use mapping or zip with index. In fact, since your testIO isn't an Option based IO and always included in the system, you should be able to do something as simple as:

class TestIO extends Bundle {
// added _ between test and io to make name look nicer in verilog
  val test_io = Vec(4, Analog(1.W)) // Not using Analog(4.W) because we can't connect these to IO cells
}

trait HasTestIO extends LazyModuleImp {
  val test_io = IO(new TestIO)
}

class TestIOPassthrough extends OverrideIOBinder({
    (system: HasTestIO) => {
        // clones the internal DigitalTopIO to make pins visible to ChipTop
        val test_io_temp = IO(DataMirror.internal.chiselTypeClone[TestIO](system.testio)).suggestName("my_block") // prefix to "test_io"
        test_io_temp <> system.testio // connecting ChipTop to DigitalTop
        (Seq(test_io_temp), Nil)      // return for harness binder
    }
})

The need for dontTouch isn't necessary once the pins are visible to ChipTop. Chipyard's main function will automatically mark ChipTop pins as DontTouch for you.

Michael Etzkorn

unread,
Jan 12, 2022, 1:24:20 PM1/12/22
to Chipyard
As a note, you'll need to add chisel3.experimental.DataMirror and chipyard.iobinders.OverrideIOBinder to your imports. 

Brendon Chetwynd

unread,
Jan 12, 2022, 3:15:37 PM1/12/22
to chip...@googlegroups.com

Michael,

 

That worked as advertised, but is not quite where I need to be.

 

Effectively, I would like to instantiate the following for all the dummy I/O.

 

GenericDigitalGPIOCell iocell_testio_0 (

    .pad(testio_0),

    .i(1’b0),

    .ie(1’b0),

    .o(),

    .oe(1’b1)

  );

 

Per the code you provided, I have a wire @ ChipTop directly passed to DigitalTop, where it goes nowhere.  


Actually, I want all my chip's I/O to use the GenericDigitalGPIOCell buffers, understanding the unidirectional ports, some of the cell's control signals will be fixed.


michael etzkorn

unread,
Jan 13, 2022, 11:02:10 AM1/13/22
to Chipyard
I see. Simplest approach is to declare the GenericDigitalGPIOCell module in your LazyModuleImp and connect your testio to it.

Something like Example within your LazyModuleImp. (GenericDigitalGPIOCell already exists within IOCell.scala. I only added it as a blackbox for the scastie)

Assuming you want to parameterize the # of these, you'll want to use a for loop or something to declare each module and connect one of the io. 

Brendon Chetwynd

unread,
Jul 19, 2023, 8:49:27 AM7/19/23
to Chipyard
As it turns out, I successfully solved this problem using the following construct:

class WithTestIOStubs extends OverrideIOBinder({
  (system: DontTouch) => {
    val ports = IO(new Bundle {
      val io      = Vec(16, Analog(1.W))
      val mode    = Vec(4, Analog(1.W))
    }).suggestName(s"test")

    val iocells = ports.io.zipWithIndex.map { case (pin, i) =>
      val iocell = Module(new GenericDigitalGPIOCell).suggestName(s"iocell_testio_${i}")
      iocell.io.o  := false.B
      iocell.io.oe := true.B
      iocell.io.ie := false.B
      iocell.io.pad <> pin
      iocell
    }
    (Nil, iocells)
  }
})

Things have changed with Chipyard 1.10.0 that results in the IO cells being properly instantiated in ChipTop.sv, but it does not connect the pad to top level I/O.

Here is a snippet of my AbstractConfig:
...
  new chipyard.iobinders.WithDontTouchIOBinders ++
  new chipyard.iobinders.WithTestIOStubs ++
...

Any thoughts would be appreciated.

Jerry Zhao

unread,
Jul 19, 2023, 2:07:16 PM7/19/23
to chip...@googlegroups.com
Note that if you aren't planning on taping out your design, there's no need to use the GenericIOCells, you can just punch through IOs directly. There are a few "PunchThroughIOBinders" that do this.

-Jerry

Brendon Chetwynd

unread,
Jul 19, 2023, 2:28:14 PM7/19/23
to chip...@googlegroups.com
Thanks,  Jerry.   This is a tapeout certain of the design 

Brendon Chetwynd

unread,
Jul 21, 2023, 7:22:30 AM7/21/23
to Chipyard
Let's try this again.  Jerry, this is for a tapeout version of the design... so I need to instantiate stubbed version of GenericIOCells.

- Brendon

Jerry Zhao

unread,
Jul 21, 2023, 2:59:52 PM7/21/23
to chip...@googlegroups.com
Oh sorry, I didn't catch that something was still broken for you.

Can you try this:

```
class WithTestIOStubs extends OverrideIOBinder({
  (system: DontTouch) => {
    val ports = IO(new Bundle {
      val io      = Vec(16, Analog(1.W))
      val mode    = Vec(4, Analog(1.W))
    }).suggestName(s"test")

    val iocells = ports.io.zipWithIndex.map { case (pin, i) =>
      val iocell = Module(new GenericDigitalGPIOCell).suggestName(s"iocell_testio_${i}")
      iocell.io.o  := false.B
      iocell.io.oe := true.B
      iocell.io.ie := false.B
      iocell.io.pad <> pin
      iocell
    }
    (Seq(ports), iocells)
  }
})

```



Reply all
Reply to author
Forward
0 new messages