Friday, October 3, 2014

SystemVerilog Streaming Operator: Knowing Right from Left

SystemVerilog has this cool feature that is very handy for converting one type of collection of bits into another type of collection of bits.  It's the streaming operator.  Or the streaming concatenation operator.  Or maybe it's the concatenation of streaming expressions (it's also called pack/unpack parenthetically).  Whatever you want to call it, it's nice.  If you have an array of bytes and you want to turn it into an int, or an array of ints that you want to turn into an array of bytes, or if you have a class instance that you want to turn into a stream of bits, then streaming is amazing.  What used to require a mess of nested for-loops can now be done with a concise single line of code.

As nice as it is, getting the hang of the streaming operator is tough.  The SystemVerilog 1800-2012 LRM isn't totally clear (at least to me) on the details of how they work.  The statement from the LRM that really got me was this, "The stream_operator << or >> determines the order in which blocks of data are streamed: >> causes blocks of data to be streamed in left-to-right order, while << causes blocks of data to be streamed in right-to-left order."  You might have some intuitive idea about which end of a stream of bits is on the "right" and which is on the "left" but, I sure didn't.  After looking at the examples of streaming on page 240 of the LRM I thought I had it, and then none of my attempts to write streaming concatenations worked like I thought they should.  Here's why: "right" and "left" are different depending on whether your stream of bits is a packed array or an unpacked array.

As far as I can tell, "right" and "left" are in reference to the literal SystemVerilog code representations of an arrays of bits.  A literal packed array is generally written like this:

bit [7:0] packed_array = 8'b0011_0101;

And packed_array[0] is on the right (that 1 right before the semicolon).  A literal unpacked array is written like this:

bit unpacked_array[] = '{1'b1, 1'b0, 1'b1, 1'b0};

unpacked_array[0] is on the left (the first value after the left curly brace).  I don't know about you, but I'm generally more concerned with actual bit positions, not what is to the right and left in a textual representation of an array, but there you have it.

Once I got that down, I still had problems.  It turns out the results of streaming concatenations will be different depending on the variable you are storing them in.  It's really the same right/left definitions coming into play.  If you are streaming using the right-to-left (<<) operator, the right-most bit of the source will end up in the left-most bit of the destination.  If your destination is a packed array then, just as I explained above, "right" means bit zero and left means the highest index bit.  If, your destination is an unpacked array, your right-most source bit will end up as bit zero of the unpacked array (which is the "right" bit according to the literal representation).

Got all that?  If not, I put a code example on edaplayground that you can run and examine the output of.  The examples are all streaming bits one at a time.  It gets a little harder to wrap your head around what happens when you stream using a slice_size and when your source and/or destinations array is an unpacked array of bytes or ints. I'll write another post explaining some tricks for those next (UPDATE: next post is here).

No comments: