How do I set output flags for ALU in "Nand to Tetris" course?
Asked Answered
S

5

20

Although I tagged this homework, it is actually for a course which I am doing on my own for free. Anyway, the course is called "From Nand to Tetris" and I'm hoping someone here has seen or taken the course so I can get some help. I am at the stage where I am building the ALU with the supplied hdl language. My problem is that I can't get my chip to compile properly. I am getting errors when I try to set the output flags for the ALU. I believe the problem is that I can't subscript any intermediate variable, since when I just try setting the flags to true or false based on some random variable (say an input flag), I do not get the errors. I know the problem is not with the chips I am trying to use since I am using all builtin chips.

Here is my ALU chip so far:

/**
 * The ALU.  Computes a pre-defined set of functions out = f(x,y)
 * where x and y are two 16-bit inputs. The function f is selected 
 * by a set of 6 control bits denoted zx, nx, zy, ny, f, no.
 * The ALU operation can be described using the following pseudocode:
 *     if zx=1 set x = 0       // 16-bit zero constant
 *     if nx=1 set x = !x      // Bit-wise negation
 *     if zy=1 set y = 0       // 16-bit zero constant
 *     if ny=1 set y = !y      // Bit-wise negation
 *     if f=1  set out = x + y // Integer 2's complement addition
 *     else    set out = x & y // Bit-wise And
 *     if no=1 set out = !out  // Bit-wise negation
 *
 * In addition to computing out, the ALU computes two 1-bit outputs:
 *     if out=0 set zr = 1 else zr = 0 // 16-bit equality comparison
 *     if out<0 set ng = 1 else ng = 0 // 2's complement comparison
 */

CHIP ALU {

IN  // 16-bit inputs:
    x[16], y[16],
    // Control bits:
    zx, // Zero the x input
    nx, // Negate the x input
    zy, // Zero the y input
    ny, // Negate the y input
    f,  // Function code: 1 for add, 0 for and
    no; // Negate the out output

OUT // 16-bit output
    out[16],

    // ALU output flags
    zr, // 1 if out=0, 0 otherwise
    ng; // 1 if out<0, 0 otherwise

PARTS:
// Zero the x input
Mux16( a=x, b=false, sel=zx, out=x2 );

// Zero the y input
Mux16( a=y, b=false, sel=zy, out=y2 );

// Negate the x input
Not16( in=x, out=notx );
Mux16( a=x, b=notx, sel=nx, out=x3 );

// Negate the y input
Not16( in=y, out=noty );
Mux16( a=y, b=noty, sel=ny, out=y3 );

// Perform f
Add16( a=x3, b=y3, out=addout );
And16( a=x3, b=y3, out=andout );
Mux16( a=andout, b=addout, sel=f, out=preout );

// Negate the output
Not16( in=preout, out=notpreout );
Mux16( a=preout, b=notpreout, sel=no, out=out );

// zr flag
Or8way( in=out[0..7], out=zr1 );   // PROBLEM SHOWS UP HERE
Or8way( in=out[8..15], out=zr2 );
Or( a=zr1, b=zr2, out=zr );

// ng flag
Not( in=out[15], out=ng );

}

So the problem shows up when I am trying to send a subscripted version of 'out' to the Or8Way chip. I've tried using a different variable than 'out', but with the same problem. Then I read that you are not able to subscript intermediate variables. I thought maybe if I sent the intermediate variable to some other chip, and that chip subscripted it, it would solve the problem, but it has the same error. Unfortunately I just can't think of a way to set the zr and ng flags without subscripting some intermediate variable, so I'm really stuck!

Just so you know, if I replace the problematic lines with the following, it will compile (but not give the right results since I'm just using some random input):

// zr flag
Not( in=zx, out=zr );

// ng flag
Not( in=zx, out=ng );

Anyone have any ideas?

Edit: Here is the appendix of the book for the course which specifies how the hdl works. Specifically look at section 5 which talks about buses and says: "An internal pin (like v above) may not be subscripted".

Edit: Here is the exact error I get: "Line 68, Can't connect gate's output pin to part". The error message is sort of confusing though, since that does not seem to be the actual problem. If I just replace "Or8way( in=out[0..7], out=zr1 );" with "Or8way( in=false, out=zr1 );" it will not generate this error, which is what lead me to look up in the appendix and find that the out variable, since it was derived as intermediate, could not be subscripted.

Shepherd answered 23/2, 2009 at 1:3 Comment(0)
S
5

The solution as Pax suggested was to use an intermediate variable as input to another chip, such as Or16Way. Here is the code after I fixed the problem and debugged:

CHIP ALU {

IN  // 16-bit inputs:
    x[16], y[16],
    // Control bits:
    zx, // Zero the x input
    nx, // Negate the x input
    zy, // Zero the y input
    ny, // Negate the y input
    f,  // Function code: 1 for add, 0 for and
    no; // Negate the out output

OUT // 16-bit output
    out[16],

    // ALU output flags
    zr, // 1 if out=0, 0 otherwise
    ng; // 1 if out<0, 0 otherwise

PARTS:
// Zero the x input
Mux16( a=x, b=false, sel=zx, out=x2 );

// Zero the y input
Mux16( a=y, b=false, sel=zy, out=y2 );

// Negate the x input
Not16( in=x2, out=notx );
Mux16( a=x2, b=notx, sel=nx, out=x3 );

// Negate the y input
Not16( in=y2, out=noty );
Mux16( a=y2, b=noty, sel=ny, out=y3 );

// Perform f
Add16( a=x3, b=y3, out=addout );
And16( a=x3, b=y3, out=andout );
Mux16( a=andout, b=addout, sel=f, out=preout );

// Negate the output
Not16( in=preout, out=notpreout );
Mux16( a=preout, b=notpreout, sel=no, out=preout2 );

// zr flag
Or16Way( in=preout2, out=notzr );
Not( in=notzr, out=zr );

// ng flag
And16( a=preout2, b=true, out[15]=ng );

// Get final output
And16( a=preout2, b=preout2, out=out );
}
Shepherd answered 23/2, 2009 at 3:22 Comment(3)
Bizarre that there's no buffer device and that you have to revert to and16/or16 with identical inputs to emulate this. Real electronics has buffers without wasting gates, perhaps this emulation program should have too.Kaolinite
Yes, there must be some other solution that doesn't require me to make a new chip (I made Or16Way), since the idea is to use only the chips that you build up in the previous projects.Shepherd
you don't have to make a new chip. Just use multiple outputs, like this: Mux16(a=F16, b=NF16, sel=no, out=out, out[15]=ng, out[0..7]=zout1, out[8..15]=zout2); Then use Or16Way on those outputs: Or8Way(in=zout1, out=zr1); Or8Way(in=zout2, out=zr2); Or(a=zr1, b=zr2, out=zr3); Not(in=zr3, out=zr);Locklear
S
24

For anyone else interested, the solution the emulator supports is to use multiple outputs Something like:

Mux16( a=preout, b=notpreout, sel=no, out=out,out=preout2,out[15]=ng);
Suburbicarian answered 23/10, 2009 at 13:44 Comment(0)
L
6

This is how I did the ALU:

CHIP ALU {
IN  // 16-bit inputs:
    x[16], y[16],
    // Control bits:
    zx, // Zero the x input
    nx, // Negate the x input
    zy, // Zero the y input
    ny, // Negate the y input
    f,  // Function code: 1 for add, 0 for and
    no; // Negate the out output
OUT // 16-bit output
    out[16],
    // ALU output flags
    zr, // 1 if out=0, 0 otherwise
    ng; // 1 if out<0, 0 otherwise
PARTS:      
    Mux16(a=x, b=false, sel=zx, out=M16x);
    Not16(in=M16x, out=Nx);
    Mux16(a=M16x, b=Nx, sel=nx, out=M16M16x);

    Mux16(a=y, b=false, sel=zy, out=M16y);
    Not16(in=M16y, out=Ny);
    Mux16(a=M16y, b=Ny, sel=ny, out=M16M16y);

    And16(a=M16M16x, b=M16M16y, out=And16);
    Add16(a=M16M16x, b=M16M16y, out=Add16);
    Mux16(a=And16, b=Add16, sel=f, out=F16);

    Not16(in=F16, out=NF16);
    Mux16(a=F16, b=NF16, sel=no, out=out, out[15]=ng, out[0..7]=zout1, out[8..15]=zout2);

    Or8Way(in=zout1, out=zr1);
    Or8Way(in=zout2, out=zr2);
    Or(a=zr1, b=zr2, out=zr3);
    Not(in=zr3, out=zr);
}
Locklear answered 20/3, 2010 at 21:57 Comment(0)
S
5

The solution as Pax suggested was to use an intermediate variable as input to another chip, such as Or16Way. Here is the code after I fixed the problem and debugged:

CHIP ALU {

IN  // 16-bit inputs:
    x[16], y[16],
    // Control bits:
    zx, // Zero the x input
    nx, // Negate the x input
    zy, // Zero the y input
    ny, // Negate the y input
    f,  // Function code: 1 for add, 0 for and
    no; // Negate the out output

OUT // 16-bit output
    out[16],

    // ALU output flags
    zr, // 1 if out=0, 0 otherwise
    ng; // 1 if out<0, 0 otherwise

PARTS:
// Zero the x input
Mux16( a=x, b=false, sel=zx, out=x2 );

// Zero the y input
Mux16( a=y, b=false, sel=zy, out=y2 );

// Negate the x input
Not16( in=x2, out=notx );
Mux16( a=x2, b=notx, sel=nx, out=x3 );

// Negate the y input
Not16( in=y2, out=noty );
Mux16( a=y2, b=noty, sel=ny, out=y3 );

// Perform f
Add16( a=x3, b=y3, out=addout );
And16( a=x3, b=y3, out=andout );
Mux16( a=andout, b=addout, sel=f, out=preout );

// Negate the output
Not16( in=preout, out=notpreout );
Mux16( a=preout, b=notpreout, sel=no, out=preout2 );

// zr flag
Or16Way( in=preout2, out=notzr );
Not( in=notzr, out=zr );

// ng flag
And16( a=preout2, b=true, out[15]=ng );

// Get final output
And16( a=preout2, b=preout2, out=out );
}
Shepherd answered 23/2, 2009 at 3:22 Comment(3)
Bizarre that there's no buffer device and that you have to revert to and16/or16 with identical inputs to emulate this. Real electronics has buffers without wasting gates, perhaps this emulation program should have too.Kaolinite
Yes, there must be some other solution that doesn't require me to make a new chip (I made Or16Way), since the idea is to use only the chips that you build up in the previous projects.Shepherd
you don't have to make a new chip. Just use multiple outputs, like this: Mux16(a=F16, b=NF16, sel=no, out=out, out[15]=ng, out[0..7]=zout1, out[8..15]=zout2); Then use Or16Way on those outputs: Or8Way(in=zout1, out=zr1); Or8Way(in=zout2, out=zr2); Or(a=zr1, b=zr2, out=zr3); Not(in=zr3, out=zr);Locklear
K
1

Have you tried:

// zr flag
Or8way(
    in[0]=out[ 0], in[1]=out[ 1], in[2]=out[ 2], in[3]=out[ 3],
    in[4]=out[ 4], in[5]=out[ 5], in[6]=out[ 6], in[7]=out[ 7],
    out=zr1);
Or8way(
    in[0]=out[ 8], in[1]=out[ 9], in[2]=out[10], in[3]=out[11],
    in[4]=out[12], in[5]=out[13], in[6]=out[14], in[7]=out[15],
    out=zr2);
Or( a=zr1, b=zr2, out=zr );

I don't know if this will work but it seems to make sense from looking at this document here.

I'd also think twice about using out as a variable name since it's confusing trying to figure out the difference between that and the keyword out (as in "out=...").

Following your edit, if you cannot subscript intermediate values, then it appears you will have to implement a separate "chip" such as IsZero16 which will take a 16-bit value as input (your intermediate out) and return one bit indicating its zero-ness that you can load into zr. Or you could make an IsZero8 chip but you'd have to then call it it two stages as you're currently doing with Or8Way.

This seems like a valid solution since you can subscript the input values to a chip.

And, just looking at the error, this may be a different problem to the one you suggest. The phrase "Can't connect gate's output pin to part" would mean to me that you're unable to connect signals from the output parameter back into the chips processing area. That makes sense from an electrical point of view.

You may find you have to store the output into a temporary variable and use that to both set zr and out (since once the signals have been "sent" to the chips output pins, they may no longer be available).

Can we try:

CHIP SetFlags16 {
    IN  inpval[16];
    OUT zflag,nflag;
    PARTS:
        Or8way(in=inpval[0.. 7],out=zr0);
        Or8way(in=inpval[8..15],out=zr1);
        Or(a=zr0,b=zr1,out=zflag);
        Not(in=inpval[15],out=nflag);
}

and then, in your ALU chip, use this at the end:

// Negate the output
Not16( in=preout, out=notpreout );
Mux16( a=preout, b=notpreout, sel=no, out=tempout );

// flags
SetFlags16(inpval=tempout,zflag=zr,nflag=ng);

// Transfer tempout to out (may be a better way).
Or16(a=tempout,b=tempout,out=out);
Kaolinite answered 23/2, 2009 at 1:16 Comment(4)
The problem is I cannot subscript the variable 'out' at all since it was derived as an intermediate variable. Also, whether you assign each subscript individually or not does not matter. If 'in' has bus size of 8, then the right side of the equation has to be of size 8.Shepherd
Show us the actual error you're getting, @MahlerFive. It may assist.Kaolinite
By the way, the reason I use out as a variable name is because I'm required to calculate out as the output of this ALU chip. By the time I get to the flags, out has already been calculated, and based on out I will set the flags.Shepherd
Thanks, I actually used your suggestions to fix it before you posted your suggested code which looks like it would probably work tooShepherd
R
1

Here's one also with a new chip but it feels cleaner

/**
 * Negator16 - negates the input 16-bit value if the selection flag is lit
 */
CHIP Negator16 {
  IN sel,in[16];
  OUT out[16];

  PARTS:
  Not16(in=in, out=negateIn);
  Mux16(a=in, b=negateIn, sel=sel, out=out);
}

CHIP ALU {
  // IN and OUT go here...
  PARTS:
  //Zero x and y if needed
  Mux16(a=x, b[0..15]=false, sel=zx, out=x1);
  Mux16(a=y, b[0..15]=false, sel=zy, out=y1);

  //Create x1 and y1 negations if needed
  Negator16(in=x1, sel=nx, out=x2);
  Negator16(in=y1, sel=ny, out=y2);

  //Create x&y and x+y
  And16(a=x2, b=y2, out=andXY);
  Add16(a=x2, b=y2, out=addXY);

  //Choose between And/Add according to selection
  Mux16(a=andXY, b=addXY, sel=f, out=res);

  // negate if needed and also set negative flag
  Negator16(in=res, sel=no, out=res1, out=out, out[15]=ng);

  // set zero flag (or all bits and negate)
  Or16Way(in=res1, out=nzr);
  Not(in=nzr, out=zr);
}
Ricotta answered 29/1, 2010 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.