42
(Sorry... somebody had to do it...)

2006/6/16, Sébastien Bouché <[EMAIL PROTECTED]>:
Answer to the quiz: the carry bit of the f bus.

Selon Timothy Miller <[EMAIL PROTECTED]>:

> I'm making this up as I go along, so I'm not sure if I'm skipping over
> something or not.  Before we go further, I figure we should have a
> short quiz.
>
> Explain this code in detail:
>
> module whatsit(a, b, d, c);
> input [3:0] a, b;
> input d;
> output [3:0] c;
> wire [3:0] e, f;
> assign e = a & b;
> assign f = a + b;
> assign c = d ? e : f;
> endmodule
>
> Also, some information is being lost.  Identify it.
>
>
> Ok, on to the lesson.  Continuous assignments like what we saw are
> powerful but limiting in their forms of expression.  To augment is
> expressiveness, Verilog has what are called behavioral blocks or
> "always" blocks.  Tonight, we'll just cover a handful of things that
> you can do.  Behavioral blocks look a lot more like C programming, and
> they allow you to express algorithms in a way that looks sequential
> (even when it's not in hardware).  In fact, we'll see later how to
> express sequential logic (logic with registers and clock signals).
>
> The syntax for a behavioral block is:
>
> always @(sensitivity_list) <statements>
>
> The sensitivity list must include every signal that is an input to
> anything expressed in the behavioral block.  Doing this incorrectly
> won't affect synthesis, but it will affect simulation.  The
> sensitivity list is a sequence of inputs separated by the keyword
> "or":
>
> always @(a or b or d) ...
>
> Now, signals on the right-hand-side of an expression can be of either
> type wire or of type reg.  But the left-hand-side must how always be
> of type reg:
>
> reg [3:0] c;
> always @(a or b or d) begin
>     c = <something using the inputs>;
> end
>
> Basically the difference between reg and wire is that reg is the type
> on the lhs of an expression in a behavioral block.  You'd think they'd
> make 'reg' mean a register (flipflops), but not necessarily, and this
> is a point of confusion for beginners.
>
> The assignments we're using in this lesson, using the "=" operator,
> are called "blocking" assignments.  For simulation, that means that
> the expression is computed and the signal is assigned its value
> immediately so that the result is available for subsequent statements.
>  We'll explain non-blocking assignments next time.  As a rule of
> thumb, we use blocking assignments in combinatorial logic and
> non-blocking assignments in sequential logic, and we avoid mixing them
> up.
>
> You can express chains of operations to be done with all sorts of
> dependencies between them, and the synthesizer will just combine them
> together, perhaps creating rather long combinatorial chains.  For
> instance:
>
> wire [3:0] a, b, c, d, e;
> reg [3:0] f, g, h, j;
> always @(a or b or c or d or e) begin
>     f = a+b;
>     g = f & c;
>     h = g | d;
>     j = h - e;
> end
>
> And this is equivalent to:
>
> reg [3:0] j = (((a+b) & c) | d) - e;
>
> f, g, and h may or may not exist as identifiable signals once the
> synthesizer is done with this, depending on whether or not some other
> piece of logic uses them.  If, for instance, f is never used anywhere
> else, the synthesizer will give you a warning, saying that it is
> assigned but not used.  That's okay, because its result is used as
> part of the bigger expression.  All it means is that nothing in the
> netlist is going to be named "f".
>
>
> In behavioral blocks, we can now do some more interesting things, like
> if-statements, case-statements, loops, etc.  For instance, to express
> the quiz problem behaviorally, we can do this:
>
> reg [3:0] c;
> always @(a or b or d) begin
>     c = d ? (a & b) : (a + b);
> end
>
> Or we can do it this way:
>
> reg [3:0] c;
> always @(a or b or d) begin
>     if (d) begin
>         c = a & b;
>     end else begin
>         c = a + b;
>     end
> end
>
> In the place of 'd', you can have any expression.  Zero means false
> and any non-zero value is true.
>
> Case statements are just like switch statements in C.  They're a great
> way to express multiplexers over more than two inputs or to select
> between different actions.
>
> The basic syntax is:
>
>     case (selector)
>         option1: <statement>;
>         option2: <statement>;
>         default:  <if nothing else statement>;  // optional
>     endcase
>
>
> Here's an example:
>
> wire [1:0] option;
> wire [7:0] a, b, c, d;
> reg [7:0] e;
> always @(a or b or c or d or option) begin
>     case (option)
>         0: e = a;
>         1: e = b;
>         2: e = c;
>         3: e = d;
>     endcase
> end
>
> This will synthesize to eight 4-to-1 muxes.
>
> You can also do goofy things like this with case statements:
>
> wire [3:0] x;
> always @(...) begin
>     case (1'b1)
>         x[0]: <something1>;
>         x[1]: <something2>;
>         x[2]: <something3>;
>         x[3]: <something4>;
>     endcase
> end
>
> Logically speaking (and literally in simulation), the case statement
> walks down the list of cases and executes the first one that matches.
> So here, if the lowest 1-bit of x is bit 2, then something3 is the
> statement that will get executed (or selected by the logic).
>
> The latter is great for 1-hot encoded state machines which we'll also
> have to cover another time.
>
> Ok, we've seen case and if.  How about a loop.  For synthesis, a loop
> must have a fixed number of iterations, otherwise, it can't map to a
> fixed amount of logic.  A good example perhaps is a priority encoder.
> Given a bit vector, we would like to know the highest bit that is set,
> and we'd like that number to be the output of our block.
>
> wire [15:0] in_data;
> reg [3:0] high_bit;
> reg valid;
> integer i;
> always @(in_data) begin
>     valid = 0;    // Assume invalid by default
>     high_bit = 0;   // Set this to something by default to avoid
> inferring a latch
>     for (i=0; i<16; i=i+1) begin
>         if (in_data[i]) begin
>             valid = 1;  // We found one, so set it valid
>             high_bit = i;  // and note the number
>         end
>     end
> end
>
> Chew on that for a moment before going on.  It's looping over the bits
> from the bottom up.  Whenever it hits a bit, it notes the position.
> When the loop has completed, high_bit will contain the index of the
> highest 1 bit encountered.  If you wanted the lowest bit, you would do
> it this way:
>
> always @(in_data) begin
>     valid = 0;    // Assume invalid by default
>     high_bit = 0;   // Set this to something by default to avoid
> inferring a latch
>     for (i=15; i>=0; i=i-1) begin
>         if (in_data[i]) begin
>             valid = 1;  // We found one, so set it valid
>             high_bit = i;  // and note the number
>         end
>     end
> end
>
> One reason I use an integer for the loop count is just so loops like
> this will work.  regs and wires are unsigned numbers.  Only integer is
> signed.  So when the loop counter rolls around from 0 to -1, only the
> integer will represent it correctly and have the >=0 comparison yield
> false.
>
>
> Ok, I'm going to go out on a limb here.  The next thing I'm going to
> show you is doable with everything we've covered up to this point, but
> it's really advanced for only being the third lesson.  I suggest,
> therefore, that people pick it apart or perhaps suggest that it not be
> included in official lessons if it's a problem.
>
> This list is dedicated to graphics, and so I'd like to show you how we
> can now do something that is fundamental to 2D graphics, which is to
> apply a planemask and a raster operation to two pixel values.  One
> value is the destination pixel that was read from the target location
> in the framebuffer.  The other value is the source pixel that was
> generated from drawing engine logic.  OGA does this logic around about
> where it also does alpha blending between source and dest.
>
> Here are your two input pixel values:
>
> wire [31:0] src;
> wire [31:0] dst;
>
> And here's your planemask.  The planemask is used to select which src
> bits are written out and which dst bits are preserved.  A 1 in the
> planemask means to write the src bit and a 0 means to leave the dst
> bit alone.
>
> wire [31:0] pmsk;
>
> Now, let's combine the pixels:
>
> reg [31:0] result;
> integer i;
> always @(src or dst or pmsk) begin
>     for (i=0; i<32; i=i+1) begin
>         if (pmsk[i]) begin
>             // 1 means to use the src
>             result[i] = src[i];
>         end else begin
>             // 0 means to keep the dst
>             result[i] = dst[i];
>         end
>     end
> end
>
> Here's a functionally equivalent way to express this (but it may or
> may not produce identical logic):
>
> wire [31:0] result = (pmsk & src) | (~pmsk & dst);
>
> Next, there's the raster-op (ROP).  Given any two bits, there are 16
> ways to combine them.  These include the basics like AND, OR, and XOR,
> but also things like src-only (copy), dst-only (no-op), invert (~dst),
> copy-invert (~src), and the degenerate cases clear (0) and set (all 1
> bits).
>
> Since there are 16 ROPs, we can use a 4-bit number to express them.
> Have a look in "/usr/include/X11/X.h" and search for GXcopy.  You'll
> find names for all 16 rops and their numbers.
>
> The easiest way to express this in hardware is to concatenate the two
> bits together into a 2-bit number and use that to mux amongst the four
> ROP bits.  If you don't see this right away, don't fret.  I didn't
> either.  I suggest you play about a bit with ROP numbers and input bit
> values and see it in action.  Also, we're using the mux a bit
> strangely.  Usually, we have inputs to the mux and some number to
> select amongst them on the select input.  In this case, the "inputs"
> are a constant, and the pixel bits are being used as the select
> inputs.  Here's the behavioral logic for that:
>
> wire src_bit, dst_bit;
> wire [3:0] rop;
> reg result_bit;
> always @(src_bit or dst_bit or rop) begin
>     case ({src_bit, dst_bit})
>         0: result = rop[0];
>         1: result = rop[1];
>         2: result = rop[2];
>         3: result = rop[3];
>     endcase
> end
>
> You may have noticed a pattern.  In fact, we can use a bit-select to
> express the same thing more concisely:
>
> wire result_bit = rop[{src_bit, dst_bit}];
>
> Let's try an example.  GXxor is function number 6, which in binary is
> 4'b0110.
>
> If both src_bit and dst_bit are 0's, we want the exclusive or to be 0,
> and so we see that rop[0] is in fact 0.
> If src_bit=0 and dst_bit=1, we want the result to be 1.  Looking at
> rop[2'b01], we do in fact find a 1.
> If src_bit=1 and dst_bit=2, we want the result to be 1.  Looking at
> rop[2], we find a 1, as expected.
> Finally, if they're both 1's, the xor needs to be zero, and we find
> rop[3] to be 0.
>
> Now that we have that out of the way, let's look at how we can combine
> ROPs and planemasks and pixels together in one chunk of logic:
>
> module mask_and_rop(
>     src, dst,
>     pmask, rop,
>     result);
>
> input [31:0] src, dst, pmask;
> input [3:0] rop;
> output [31:0] result;
>
> reg [31:0] result;  // override wireness, making result a reg
>
> integer i;  // loop counter
> always @(src or dst or pmask or rop) begin
>     // Loop over all bits in the pixels
>     for (i=0; i<32; i=i+1) begin
>         if (pmask[i]) begin
>             // If pmask bit is true, we combine src and dst
>             // based on ROP
>             result[i] = rop[{src[i], dst[i]}];
>         end else begin
>             // Otherwise, we preserve the dst bit
>             result[i] = dst[i];
>         end
>     end
> end
>
> endmodule
>
>
>
> We could also make that loop a little more concise like this:
>
> always @(src or dst or pmask or rop) begin
>     for (i=0; i<32; i=i+1) begin
>         result[i] = pmask[i] ? rop[{src[i], dst[i]}] : dst[i];
>     end
> end
>
>
> For completeness, Windows GDI defines something called a ROP4.  A ROP4
> combined four things:
>
> - A source pixel (color)
> - A dest pixel (color)
> - A pattern pixel (color)
> - A mask pixel (monochrome)
>
> Typically, the pattern is 8x8 and logically repeated over the area of
> the drawing surface.  The mask is typically 32x32 (IIRC), and it's
> also repeated.  (Windows doesn't define a planemask, but we have it in
> the hardware for X11.)
>
> Here's the logic to combine these together:
>
> module src_and_dst_and_patt_and_mask_and_planemask_and_rop(
>     src, dst, patt, mask,
>     planemask, rop,
>     result);
>
> input [31:0] src, dst, patt, planemask;
> input mask;
> input [15:0] rop;   // Notice the 16-bit ROP4
> output [31:0] result;
>
> reg [31:0] result;  // override wireness, making result a reg
>
> integer i;  // loop counter
> always @(src or dst or pmask or rop) begin
>     // Loop over all bits in the pixels
>     for (i=0; i<32; i=i+1) begin
>         if (planemask[i]) begin
>             // If planemask bit is true, we combine src and dst
>             // based on ROP
>             result[i] = rop[{mask, patt[i], src[i], dst[i]}];
>         end else begin
>             // Otherwise, we preserve the dst bit
>             result[i] = dst[i];
>         end
>     end
> end
>
> endmodule
>
>
> Turns out to be painfully simple in the end.  :)
> _______________________________________________
> Open-graphics mailing list
> [email protected]
> http://lists.duskglow.com/mailman/listinfo/open-graphics
> List service provided by Duskglow Consulting, LLC (www.duskglow.com)
>


--S�bastien
http://tetsuo3.free.fr
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to