[libre-riscv-dev] [Bug 64] data handling / io control / data routing API needed

bugzilla-daemon at libre-riscv.org bugzilla-daemon at libre-riscv.org
Tue Apr 30 14:43:47 BST 2019


http://bugs.libre-riscv.org/show_bug.cgi?id=64

--- Comment #45 from Luke Kenneth Casson Leighton <lkcl at lkcl.net> ---
(In reply to Jacob Lifshay from comment #39)

> >  shape() is used to find out, from the *recursively-iterable*
> >  thing-of-Value-derivatives, what size of Signal() would be needed to store
> > it
> >  (as a sequence of bits)
> I think we should call it bitlen or something like that, to me shape implies
> returning the type of the Data rather than the number of bits inside it.

 yes.  agreed.  however...

> In C++ it would be sizeof vs. decltype.

 ...that needs to be a recommendation made to the nmigen developers, to change
 the nmigen API from "shape" to "bitlen".

 the nmoperator.shape function simply follows the nmigen convention: not
 doing so is... well... inadviseable: it will result in "surprises":
 https://en.wikipedia.org/wiki/Least_surprise

 so... i agree that yes, shape is a bad name... unfortunately, the name's
 already been chosen, and it would be required to be documented that the
 functionality is identical despite the name being different.

 the better choice, i feel, is to stick with the [poor] name-choice, "shape()"


> >  cat() is used to flatten() and *de*-flatten() the incoming and outgoing
> >  hierarchy-of-Value-derivatives so that ONLY ONE Queue object is needed.
> I think we should do that inside the Queue implementation (or better yet,
> inside Memory, which is what the original Chisel code does).

 mmmm this is an idea which, initially, i like, however it has knock-on
 consequences.

 the first is: Memory2 (or whatever it is called) now has a critical
 dependence on the nmoperator API, which would be the first "base"
 utility class outside of the Stage/Pipe API to do so.

 i'm... reluctant to set that precedent, particularly given that the API
 would change to no longer specify a width, it would need to specify
 the "spec".  i.e. it would need to be passed an "iospecfn".

 that's a really drastic change in the API, going beyond my comfort level
 for a long-term API


 second: the use of the flattening actually results in some truly
 dreadful graphviz representations, as the data gets jammed into a
 box of bits, then extracted on the other side in a truly awful messy
 way.


 third: the same logic that applies to Memory also applies to Queue.
 Queue's API would need to be changed, and i'm really not comfortable
 with that.

 right now, it's pretty damn obvious.  read the chisel3 code, read
 the comment about the name-changes, set the two pieces of code
 side-by-side and it's damn obvious (if you can read chisel3 that is)
 that the two are directly equivalent.

 that equivalence is DESTROYED by embedding a hard critical dependence
 on the iospecfn (data format) API.


 ... or...

 very simple...

 use what i called FIFOControl... because FIFOControl *is* Queue [with
 the flattening and processing added].

 in an earlier iteration of FIFOControl, this direct one-for-one equivalence
 was much more obvious, because i was able to make the input port DIRECTLY
 equal to a PrevControl, and the output port DIRECTLY equal to a NextControl.

 unfortunately due to the need to process the in/out data, that equivalence
 was broken, and i had to extract the contents of NextControl._connect_in
 and use those contents *in* FIFOControl.

 *grumble*.

 so basically:

 * if you don't want the nmoperator API to be involved, don't want
   the Shape API involved, don't want the pipeline API involved...

   ... use Queue

 * if you *do* want the data to be flattened...

   ... use FIFOControl.  they are EXACTLY and PRECISELY equivalent.
   ABSOLUTELY NO CHANGE IN THE ARGUMENTS OR THE LOGIC HAS BEEN MADE.  

 if this is not understood, see the comments in queue.py:

            din  = enq_data, writable  = enq_ready, we = enq_valid
            dout = deq_data, re = deq_ready, readable = deq_valid


 Memory has problems of its own (can't do length-1 arrays), which
 makes a Memory2 worthwhile considering doing (for different reasons)

 SyncFIFOBuffered should not exist, basically.


> We need at least one more function, create a new Data that shares the type
> of an existing one. I did that using Shape.get(data).create_data(), similar
> to python type(value)(). This is used to create Data instances for internal
> registers and what-not.

 you're thinking of Value.like.  Signal.like.  etc.

 yes, and the nice thing is: whilst Record.like does not exist (and it should),
 nmoperator.like() can substitute for that lack by auto-detecting if it is
 ever passed a Record.

 the only thing is: we'll need to actually write a recursive-Record-walker
 "like" function (as a non-member function).

 basically Record.like is really really simple: grab the fields member,
 and um create a new Record instance using that Record's fields.

 that's it.

 the only thing we have to watch out for is: if we want to support something
 that *derives* from Record, it should have its own "like" function so
 that we can test for it (if hasattr(x, "like")) and call it.

-- 
You are receiving this mail because:
You are on the CC list for the bug.


More information about the libre-riscv-dev mailing list