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

Const Record value? #261

Closed
RobertBaruch opened this issue Oct 28, 2019 · 16 comments
Closed

Const Record value? #261

RobertBaruch opened this issue Oct 28, 2019 · 16 comments
Labels

Comments

@RobertBaruch
Copy link

Suppose I have a Layout with various fields:

class ThingLayout(Layout):
    def __init__(self):
        super().__init__([
            ("field1", unsigned(8)),
            ("field2", unsigned(16)),
            ...
        ])

Now I'd like to construct a Python integer for a specific setting for the fields in the Layout. I know it needs to be 24 bits. How do I construct such a value?

The ultimate goal is to initialize a Memory with lots of these values, then be able to read the memory and load a Record with the value from memory.

@whitequark
Copy link
Contributor

There's no such functionality currently; Records can only be used in HDL. If such functionality existed, it would probably be present as a pair of functions, Layout.pack and Layout.unpack, which would turn a dictionary of ints into an int and vice versa.

@RobertBaruch
Copy link
Author

That sounds useful. As it is now, I'll just make a class that can pack and generate unpack statements during elaborate time.

@RobertBaruch
Copy link
Author

Basically something like this:

class StepStruct(Object):
    def __init__(self, (step, arg1, arg2, next_cycle, next_step)):
        self.step = Const(step)
        self.arg1 = Const(arg1)
        self.arg2 = Const(arg2)
        self.next_cycle = Const(next_cycle)
        self.next_step = Const(next_step, shape=unsigned(10))

    def _as_const(self):
        return Cat(self.step, self.arg1, self.arg2, self.next_cycle,
                   self.next_step)._as_const()

    def uncat(self, m, value, (step, arg1, arg2, next_cycle, next_step)):
        m.d.comb += Cat(step, arg1, arg2, next_cycle, next_step).eq(value)

@whitequark
Copy link
Contributor

I don't understand what's that supposed to do. Also, _as_const is an internal API that may be removed at any time.

@RobertBaruch
Copy link
Author

Can you keep _as_const()? Otherwise how do I get the const value of a Cat of Consts?

@whitequark
Copy link
Contributor

Just like any other function starting with _, it may be changed or removed at any time. Why do you need to get it?

@whitequark
Copy link
Contributor

Just like any other function starting with _, it may be changed or removed at any time.

Also, to be clear—if _as_const is indeed so useful that it needs to be kept, then it should be promoted to a stable public API instead. That's a separate question though, and if that would be to be done, it would be necessary to greatly expand it in scope to support every nMigen AST class and not just two of them at random.

@RobertBaruch
Copy link
Author

Suppose I have a bunch of structured values that I want to put into a memory. Memories can only store values of a given width. Therefore, I need to convert my structured values into a single value of fixed width. Now, I can do that using something like this:

class StepStruct(Object):
    def __init__(self, (step, arg1, arg2, next_cycle, next_step)):
        self.step = Const(step)
        self.arg1 = Const(arg1)
        self.arg2 = Const(arg2)
        self.next_cycle = Const(next_cycle)
        self.next_step = Const(next_step, shape=unsigned(10))
        self.parts = [self.step, self.arg1, self.arg2, self.next_cycle, self.next_step]

    def value(self):
        value = 0
        for part in reversed(self.parts):
            value <<= len(part)
            value |= part.value
        return value

Ok, Cat actually does that for me, but now I have to duplicate the logic because _as_const() is internal and not to be used. In any case, now I can do something like this:

    a = [(Step.X, Arg.A, Arg.B, Cycle.C, 10),
         (Step.Y, Arg.A, Arg.Z, Cycle.D, 20),
         ...
    ]

    mem_init_val = [StepStruct(v).value() for v in a]
    # Now I can finally initialize the memory

Next, I need to take a value out of memory and break it up. That would be very convenient with a Cat also! I could use it easily... but since _as_const() is internal, I have no way of ensuring that the logic I copied above is the same as Cat uses, which means I can't use Cat to do something like this:

    m.d.comb += Cat(step, a1, a2, nc, ns).eq(mem[addr])

So what should I do instead?

@whitequark
Copy link
Contributor

Isn't StepStruct basically a Record? And Records have a Layout, which you can interrogate to find out which part lives at which offset.

@RobertBaruch
Copy link
Author

Hmm, I see where you can get the shape of a Layout's field, but not the offset.

@whitequark
Copy link
Contributor

Hmm, I see where you can get the shape of a Layout's field, but not the offset.

The fields are sequential and without gaps, so you can just sum the widths while you're iterating through the fields.

@RobertBaruch
Copy link
Author

Hmm.... I don't see anything in the code that makes that obvious, so I can't really rely on that. Where in the code is that implemented?

@whitequark
Copy link
Contributor

Hmm.... I don't see anything in the code that makes that obvious, so I can't really rely on that.

This is the basic contract of Record: a Record always acts exactly like a Cat of its fields. Now that you mention it, this is not documented, which is an unfortunate omission. But it's a guarantee it provides.

@RobertBaruch
Copy link
Author

OK, just so I make sure I understand: that means the layout of

Layout([
    ("first", unsigned(8)),
    ("second", unsigned(16))
])

is the same as Cat(first, second), so the low 8 bits is first and the next highest 16 bits is second. Does that sound right?

@whitequark
Copy link
Contributor

Correct. (In fact, a Record could be implemented as a UserValue that lowers to Cat--it predates UserValue so it doesn't, but it doesn't need to be special-cased anymore!)

@RobertBaruch
Copy link
Author

Sounds good, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants