phanrahan/magma

[IO] Unintuitive behavior with IO objects

Opened this issue · 1 comments

Here's an example of some code that doesn't work because of the immutable nature of IO objects.

class RegisterFile(m.Generator):
    def __init__(self, n: int, T: m.Type, has_read_enable: bool = False):
        addr_len = m.bitutils.clog2(n)
        self.io = m.IO(
            raddr=m.In(m.Bits[addr_len]),
            rdata=m.Out(T),
            wen=m.In(m.Enable),
            waddr=m.In(m.Bits[addr_len]),
            wdata=m.In(T)
        )
        rdata = self.io.rdata
        if has_read_enable:
            self.io += m.IO(ren=m.In(m.Enable))
            rdata_reg = m.Register(T)()
            rdata @= rdata_reg.O

The problem here is that the rdata reference is invalidated when the new IO object is created to add the port.

We should at least raise a useful error here (e.g. "Attempting to wire to an invalidated IO object"), although I think we might consider whether we should make IO object mutable to make this seemingly natural code work.

Is there a strong argument for immutability of IO objects? i.e. is there a class of errors we'd like to avoid that immutability guarantees us against? As it currently stands, immutability actually introduces a new class of possible errors, so we'd want a strong argument for why these errors are useful for users (i.e. how they can avoid users introducing more difficult to fix errors).

I agree; I think immutability only needs to kick in once the circuit definition is finished, i.e. open() should not permit modifying the IO. I can make these changes to allow the IO to be mutable.