On 6/17/06, Petter Urkedal <[EMAIL PROTECTED]> wrote:
On 2006-06-16, Timothy Miller wrote:
> What should Lesson 4 be about?

My vote would be to introduce sequential blocks, so that we can start
implementing microprocessors ;-).  Okay, CPUs'll wait.

> Have we covered combinatiorial behavioral code well enough?  Do we
> need more of that, or should I move on to sequential?  Or is there
> something more basic I need to cover?

One thing I would like to understand is in which case a 'reg' is a
register.  I think I can make a guess.  You may have skipped explaining
this, because a flip-flop used for the registers needs a clock.  E.g.
since there is no clock involved in

    reg u, v, w;
    always @(x,y) begin
        u = x & y;
        v = x & u;
        w = u & v;
    end

I apologize if I am repeating things already explained.

A 'reg' in Verilog isn't a register.  It's just the signal type that's
on the lhs of a behavioral assignment.

Actual registers (flipflops) are inferred when the sensitivity list
involves a clock edge.


the synthesiser can turn the whole thing into continuous assignments.
Is there anything we can do with combinatorial behavioural blocks that
can't be done with continuous assignments and some patience in unrolling
loops etc?

No.  For that matter, if you had a module that represented a flipflop,
you could do any kind of sequential logic too.  It all turns into
logic gates in the end.  It's just a matter of how convenient it is to
code it.  Imagine doing everything with single-bit wires.  :)

Actually, that's exactly what you get when you get a synthesizer to
produce gate-level Verilog.  A lot of time, especially with ASICs, we
synthesize the gate-level netlist to Verilog, with timing, and then do
a realistic gate-level simulation to look for dynamic timing problems,
etc. (and verify that the synthesizer did the right thing).


However, if we introduce a clock, we have

    reg u, v, w;
    always @(posedge clk) begin
        u = x & y;  // register
        v = x & u;  // register
        w = u & v;  // not a register
    end

This can be transformed to

    reg u, v;
    wire w;
    always @(posedge clk) begin
        u = x & y;
        v = x & u;
    end
    assign w = u & v;

'v' can not be taken out of the behavioural block because it would then
get updated with new values of 'x' before the clock rises.  For 'w' this
is not a problem because it only depends on values which are already
clocked.

Right.  As a rule of thumb, we prefer to use nonblocking assigments in
clocked always blocks.  Blocking assignments are okay, and in most
cases, you get the same result.  You just need to be careful with
them.  (I used to know the semantics for a nonblocking assignment in a
combinatorial always block, but I haven't looked at it in like 6
years, because I've never done it.)

Is this correct?

Yes.

Are there cases where a synthesiser would create
registers in absence of 'posedge' or 'negedge'?

No.  Only level-sensitive latches can be inferred without posedge.

Oh, and manually instantiating a flipflop module.  You can do that.
I'm sure FPGA libraries have models for those.

You'd have something like this:

FF ff0 (.D(x), .ck(clock), .Q(y), .Qbar(), .reset(1'b0), .set(1'b0));

I think I read that is
an error to intentionally leave out variables from the sensitivity list
to condition the whole behavioural block to execute only when some of
the values are changed.  This makes sense, of course.

Yeah.  In a real chip, it's sensitive to everything that's an input.
Leaving someting out will make simulation behavior deviate from
synthesis behavior.

Some minor points.  Verilog accepts a precise syntax for bus constants

    91                  -- 32 bit value
    4'b1001             -- 4 bit value 5 given in binary
    32'h8000_0000       -- 32 bit value in hex (_ is for readability)
    8{1'b0} = 8'b11111111
    4{8'hd0} = 32'hd0d0d0d0 -- (I guess?)

they can be combined with wires and registers in {} constructs:

These are all correct.


    {addr,2'b0}         -- addr padded with two 0-bits on the right

Yup.  One of my favorite things to do.  :)


Another point, it is valid to re-assign variables.  This is useful to
make defaults for variables assigned in conditional code

Yup.  Excellent for preventing latch inference.


    wire[1:0] op;
    wire[15:0] x, y;
    reg[15:0] z;
    always @(op, x, y) begin
        carry = 0; // only default
        case (op)
            OP_ADD: {carry,z} = {1'b0,x} + {1'b0,y};
            OP_SUB: {carry,z} = {borrow,x} - {1'b0,y}; // correct?
            OP_AND: z = x & y;  // carry takes default 0
            OP_NOT: z = ~x;     // carry takes default 0
        endcase
    end

Notice the convenient Verilog feature that {} can be used on the LHS of
an assignment.

OTOH, even if the loop bounds are constant in the following code, I
believe this code does not behave as expected or is even valid:

    wire x[31:0];
    reg y[31:0];
    integer i;
    always @(x) begin
        y = x;
        for (i = 0; i < 8: ++i)
            y = (y ^ (y >> 7)) - ((y >> 11) ^ (key >> 19));
    end

I think it might work fine.  The constant shifts are no-ops.  You'd
string out eight layers of xor and subtract logic, but it would
produce valid logic.


It would be a big opportunity for gate-bloat if it worked as expected.

It _better_ work as expected.  :)


Thanks for the tutorial!


Thank you for the feedback!
_______________________________________________
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