Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inout fix #1046

Merged
merged 20 commits into from
Mar 27, 2023
Merged

Inout fix #1046

merged 20 commits into from
Mar 27, 2023

Conversation

Dolu1990
Copy link
Member

This PR reimplement how Analog are managed in order to allows much more flexibility and also fix existing issues.

Now, analog connections are tracked bit by bit (Island) then aggreged together for the final connection.
All the assignements between analogs are deleted and then regenerated into a solved manner.

This is kind of a big change XD

Copy link
Collaborator

@andreasWallner andreasWallner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First off: thanks for spending the effort on this!

Some good news first: I had a look at my initial issue - that is fixed and Vivado is also happy with the outcome.

I had a bit of a look on the code and noticed a few corner cases

In addition we have a small inconsistency that I did not want to hunt down today (too late ;-)):
We can leave all analog inputs of one signal unconnected

  case class Top() extends Component {
    val y = inout(Analog(Bits(2 bit)))
  }

But once we connect one of them, we have to all?

  case class Top() extends Component {
    val y = inout(Analog(Bits(2 bit)))
    y(0) := True
  }

core/src/main/scala/spinal/core/internals/Phase.scala Outdated Show resolved Hide resolved
core/src/main/scala/spinal/core/internals/Phase.scala Outdated Show resolved Hide resolved
})
for(bitId <- 0 until widthOf(seed)){
val flushes = ArrayBuffer[(AggregateKey, AggregateCtx)]()
val island = bitToIsland(Bit(seed, bitId, c.dslBody))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues here

First
Island building in 341 has a small issue in the sense that an assignment like y(0 downto 0) := sub.x works, while sub.x := y(0 downto 0) doesn't. I tried fixing that carrying along the analog flag when creating the extract, but that breaks if a user calls setAsAnalog after making the extract.

It's not per-se an issue if we have to call setAsAnalog on the extracts, but if one forgets we currently crash here (y(0) will never be added to any island since the target is not analog) with

Exception in thread "main" java.util.NoSuchElementException: key not found: Bit((toplevel/y : inout Bits[2 bits]),0,spinal.core.internals.ScopeStatement@ec38bef)

test code for that

  case class Sub() extends Component {
    val x = inout(Analog(Bits(1 bit)))
    x(0) := True
  }
  case class Top() extends Component {
    val sub = Sub()
    val y = inout(Analog(Bits(2 bit)))
    sub.x := y(0 downto 0)
    y(1) := True
  }

We could make that a bit more understandable if we check here like:

          val island = bitToIsland.get(Bit(seed, bitId, c.dslBody)) match {
            case Some(x) => x
            case None => SpinalError("DISCONTINUITY IN ANALOG CONNECTION ...")
          }

As far as I see it I think we can do either:

  • add in the error check from above (and possibly a explanation to RTD ;-))
  • extend all the extraction functions to carry along the analog attribute (and document the weird corner cases in RTD :-( )
  • I also tried extending the check in 328 to also build the island if the source is analog, but that creates a host of other problems...

So just bite the bullet and require .setAsAnalog()?

Edit: remove the "second" issue, that was due to some local changes...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue you had was that sub.x := y(0 downto 0).setAsAnalog() was required when you tested it.

Now that .setAsAnalog() isn't necessary anymore (today push) it is all good.

Can you confirm ? (as far as i can see)

@Dolu1990
Copy link
Member Author

Dolu1990 commented Mar 1, 2023

First off: thanks for spending the effort on this!

Some good news first: I had a look at my initial issue - that is fixed and Vivado is also happy with the outcome.

I had a bit of a look on the code and noticed a few corner cases

In addition we have a small inconsistency that I did not want to hunt down today (too late ;-)): We can leave all analog inputs of one signal unconnected

  case class Top() extends Component {
    val y = inout(Analog(Bits(2 bit)))
  }

But once we connect one of them, we have to all?

  case class Top() extends Component {
    val y = inout(Analog(Bits(2 bit)))
    y(0) := True
  }

Fixed with 28636e8

@andreasWallner
Copy link
Collaborator

Most of the issues are gone, but the changes now seem to have broken the "normal" generation of a driver for the inout (but only if the pad signal is multi-bit...):

    val x = new Component {
      val read = out Bool()
      val write = in Bool()
      val en = in Bool()
      val pad = inout(Analog(Bits(width bit)))

      for (i <- 0 until width) {
        read := pad(i)
        when(en) {
          pad(i) := write
        }
      }
    }

in which case the current code suspects that MULTIPLE INOUT interconnected in the same component

This time I pushed my test cases (currently w/o real verification) here: https://github.com/andreasWallner/SpinalHDL/tree/inout-fix

@Dolu1990
Copy link
Member Author

Dolu1990 commented Mar 2, 2023

Most of the issues are gone, but the changes now seem to have broken the "normal" generation of a driver for the inout (but only if the pad signal is multi-bit...):

    val x = new Component {
      val read = out Bool()
      val write = in Bool()
      val en = in Bool()
      val pad = inout(Analog(Bits(width bit)))

      for (i <- 0 until width) {
        read := pad(i)
        when(en) {
          pad(i) := write
        }
      }
    }

in which case the current code suspects that MULTIPLE INOUT interconnected in the same component

This time I pushed my test cases (currently w/o real verification) here: https://github.com/andreasWallner/SpinalHDL/tree/inout-fix

Should be fixed with 56b956c

Note :

    val read = out Bits(width bit) //!!!
    val write = in Bool()
    val en = in Bool()
    val pad = inout(Analog(Bits(width bit)))

    for (i <- 0 until width) {
      read(i) := pad(i)
      when(en) {
        pad(i) := write
      }
    }

Copy link
Collaborator

@andreasWallner andreasWallner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the effort!

LGTM - should we also pull add a few testcases
I added a few here: https://github.com/andreasWallner/SpinalHDL/tree/inout-fix - while there is currently no simulation with them, but at least they are testing generation

@Dolu1990
Copy link
Member Author

Dolu1990 commented Mar 7, 2023

should we also pull add a few testcases

It would be great, especialy corner cases.

It was my plan to add some, but currently i'm in a 2 week work trip, so realy busy, i realy lagging behind, trying to catch up emails XD

@andreasWallner
Copy link
Collaborator

I'm so sorry, writing tests it looks like I stumbled over another corner case:

object Blub extends App {
  SpinalVerilog(new Component {
    val pad = inout(Analog(Bits(2 bit)))
    val sliced_sub = new Component {
      val pad = inout(Analog(Bits(1 bit)))
    }
    pad(0 downto 0) <> sliced_sub.pad
  })
}

Which fails with

Static bits extraction (1 downto 0) is outside the range (0 downto 0) of (toplevel/sliced_sub/pad : inout Bits[1 bits]) at
    spinal.tester.scalatest.Blub$.delayedEndpoint$spinal$tester$scalatest$Blub$1(AnalogConnectionTester.scala:52)

Also: something seems to be weird with apply since pad.apply(0 downto 1) <> sliced_sub.pad is not cought early but trips over the dummy error in Phase.scala@326

@Dolu1990
Copy link
Member Author

I'm so sorry, writing tests it looks like I stumbled over another corner case:

object Blub extends App {
  SpinalVerilog(new Component {
    val pad = inout(Analog(Bits(2 bit)))
    val sliced_sub = new Component {
      val pad = inout(Analog(Bits(1 bit)))
    }
    pad(0 downto 0) <> sliced_sub.pad
  })
}

Which fails with

Static bits extraction (1 downto 0) is outside the range (0 downto 0) of (toplevel/sliced_sub/pad : inout Bits[1 bits]) at
    spinal.tester.scalatest.Blub$.delayedEndpoint$spinal$tester$scalatest$Blub$1(AnalogConnectionTester.scala:52)

Also: something seems to be weird with apply since pad.apply(0 downto 1) <> sliced_sub.pad is not cought early but trips over the dummy error in Phase.scala@326

Good catch ^^
Thanks ! I will take a look.

@Dolu1990
Copy link
Member Author

@andreasWallner Hi ^^
I pushed a fix for the two last issue you mentionned.

@andreasWallner
Copy link
Collaborator

@Dolu1990
Thanks!
I pushed my testcases - it would be quite interesting whether they work on e.g. gitlab CI, on my machine the VHDL stuff is doing weird things (i.e. a write & writeEnable are high and the pad does not get driven to 1 after a delay)
Verilog passes.

I initially tried to also test the direction from driving the pad, having writeEnable switched off and checking read - but there cocotb seems to do weird stuff (when trying to deposit/release on the inout signal). I left that testcase in for reference but I could not get it to do something useful, e.g. what cocotb reports as signal levels does not match what ends up in the vcd...

@Dolu1990
Copy link
Member Author

@andreasWallner I pushed a thing to disable VHDL from AnalogConnectionCocotbBoot.
So, the VHDL fail on my machine too.
You think it's because of GHDL/Cocotb issue ? or SpinalHDL VHDL generation issue ?

@andreasWallner
Copy link
Collaborator

andreasWallner commented Mar 25, 2023

I had a look at the generated VHDL and that looks good to me... just to be sure I'll try testing it with xcelium in case I overlooked something, I should find some time to do that today.

Any deposit to a pad signal breaks the analog tests since these
act as forces on the signal. Therefore driving 'z' fixes the pad
level to 'z'. `Release` also does not improve the situation -
so simply never touch those signals.
@andreasWallner
Copy link
Collaborator

andreasWallner commented Mar 26, 2023

@Dolu1990 I had a look - it looks like the issue with the original test cases was that writing the pad signals seems to work like a force, overriding the signal fully, Release also did not help there. I now just removed all the writes to xxx_pad - now they pass...
Working tests are in #1065

@Dolu1990
Copy link
Member Author

Nice :D
So i think all is ready to merge right ?

@andreasWallner
Copy link
Collaborator

@Dolu1990 sounds good to me!

@Dolu1990 Dolu1990 merged commit 601f0e8 into dev Mar 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants