面向小白:Nand2Tetris(计算机系统要素)Hack计算机实现过程2 - 布尔运算

1. 项目概况

1.1 项目背景

上一章实现了基本逻辑门,本章在此基础上设计和构建具有完整功能的算术逻辑单元ALU,实现对数字的算数操作。ALU是整个计算机的核心单元,它执行所有的算数和逻辑操作,因此构建ALU模块是很重要的一步。

1.2 需求清单

使用上一章构建的芯片,依次实现以下芯片:

加法器芯片名功能
半加器HalfAdder实现两个2位二进制数加法
全加器FullAdder实现三个3位二进制数加法
16位加法器Adder实现两个16位二进制数加法
16位增量器Inc16输出16位二进制数+1
算术逻辑单元ALU实现两个16位二进制数置零、取反、相加(Adder)以及布尔运算

2. 芯片实现过程

2.1 HalfAdder

用于实现半加器HalfAdder的芯片描述如下:

信息项描述
芯片名HalfAdder
输入a, b
输出sum, carry
功能sum = LSB of a + b, carry = MSB of a + b

可以使用And和Xor实现:
carry = a  And  b \text{carry}=a\text{ And }b carry=a And b
sum = a  Xor  b \text{sum}=a\text{ Xor }b sum=a Xor b

用HDL实现HalfAdder(直接在已有文件中修改,位置为 .\nand2tetris\projects\02\HalfAdder.hdl):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/02/HalfAdder.hdl
/**
 * Computes the sum of two bits.
 */
CHIP HalfAdder {
    IN a, b;    // 1-bit inputs
    OUT sum,    // Right bit of a + b 
        carry;  // Left bit of a + b

    PARTS:
    Xor (a = a, b = b, out = sum);
    And (a = a, b = b, out = carry);
}

2.2 FullAdder

用于实现全加器FullAdder的芯片描述如下:

信息项描述
芯片名FullAdder
输入a, b, c
输出sum, carry
功能sum = LSB of a + b + c, carry = MSB of a + b + c

可以使用基本逻辑门实现:
carry = ( a  And  b )  Or  ( b  And  c )  Or  ( a  And  c ) \text{carry}=(a\text{ And }b)\text{ Or }(b\text{ And }c)\text{ Or }(a\text{ And }c) carry=(a And b) Or (b And c) Or (a And c)
sum = ( a  Xor  b )  Xor  c \text{sum}=(a\text{ Xor }b)\text{ Xor }c sum=(a Xor b) Xor c
也可以使用半加器实现(这种方法更简单,参考文章):
carry = HalfAdder ( a , b ) [ c a r r y ]  Or HalfAdder ( HalfAdder ( a , b ) , c ) [ c a r r y ] \text{carry}=\text{HalfAdder}(a, b)[carry]\text{ Or }\text{HalfAdder}(\text{HalfAdder}(a, b), c)[carry] carry=HalfAdder(a,b)[carry] Or HalfAdder(HalfAdder(a,b),c)[carry]
sum = HalfAdder ( HalfAdder ( a , b ) , c ) [ s u m ] \text{sum}=\text{HalfAdder}(\text{HalfAdder}(a, b), c)[sum] sum=HalfAdder(HalfAdder(a,b),c)[sum]

用HDL实现FullAdder(直接在已有文件中修改,位置为 .\nand2tetris\projects\02\FullAdder.hdl):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/02/FullAdder.hdl
/**
 * Computes the sum of three bits.
 */
CHIP FullAdder {
    IN a, b, c;  // 1-bit inputs
    OUT sum,     // Right bit of a + b + c
        carry;   // Left bit of a + b + c

    PARTS:
    // 使用基本逻辑门实现:
    // And (a = a, b = b, out = aAndb);
    // And (a = b, b = c, out = bAndc);
    // And (a = a, b = c, out = aAndc);
    // Or (a = aAndb, b = bAndc, out = abOrbc);
    // Or (a = abOrbc, b = aAndc, out = carry);
    // Xor (a = a, b = b, out = aXorb);
    // Xor (a = aXorb, b = c, out = sum);
    
    // 使用半加器实现:
    HalfAdder (a = a, b = b, sum = tempsum, carry = tempcarry);
    HalfAdder (a = tempsum, b = c, sum = sum, carry = tempcarry2);
    Or (a = tempcarry, b = tempcarry2, out = carry);
}

2.3 Add16

用于实现16位加法器Add16的芯片描述如下:

信息项描述
芯片名Add16
输入a[16], b[16]
输出out[16]
功能out = a + b
说明2补码的整数加法,不处理溢出的情况

可以使用半加器和全加器实现:
out[0] = HalfAdder ( a [ 0 ] , b [ 0 ] ) [ s u m ] \text{out[0]}=\text{HalfAdder}(a[0], b[0])[sum] out[0]=HalfAdder(a[0],b[0])[sum]
out[1] = FullAdder ( a [ 1 ] , b [ 1 ] , HalfAdder ( a [ 0 ] , b [ 0 ] ) [ c a r r y ] ) [ s u m ] \text{out[1]}=\text{FullAdder}(a[1], b[1], \text{HalfAdder}(a[0], b[0])[carry])[sum] out[1]=FullAdder(a[1],b[1],HalfAdder(a[0],b[0])[carry])[sum]

out[15] = FullAdder ( a [ 15 ] , b [ 15 ] , HalfAdder ( a [ 14 ] , b [ 14 ] ) [ c a r r y ] ) [ s u m ] \text{out[15]}=\text{FullAdder}(a[15], b[15], \text{HalfAdder}(a[14], b[14])[carry])[sum] out[15]=FullAdder(a[15],b[15],HalfAdder(a[14],b[14])[carry])[sum]

用HDL实现Add16(直接在已有文件中修改,位置为 .\nand2tetris\projects\02\Add16.hdl):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/02/Adder16.hdl
/**
 * 16-bit adder: Adds two 16-bit two's complement values.
 * The most significant carry bit is ignored.
 */
CHIP Add16 {
    IN a[16], b[16];
    OUT out[16];

    PARTS:
    // 使用半加器实现:
    HalfAdder (a = a[0], b = b[0], sum = out[0], carry = carry0);    
    FullAdder (a = a[1], b = b[1], c = carry0, sum = out[1], carry = carry1);
    FullAdder (a = a[2], b = b[2], c = carry1, sum = out[2], carry = carry2);
    FullAdder (a = a[3], b = b[3], c = carry2, sum = out[3], carry = carry3);
    FullAdder (a = a[4], b = b[4], c = carry3, sum = out[4], carry = carry4);
    FullAdder (a = a[5], b = b[5], c = carry4, sum = out[5], carry = carry5);
    FullAdder (a = a[6], b = b[6], c = carry5, sum = out[6], carry = carry6);
    FullAdder (a = a[7], b = b[7], c = carry6, sum = out[7], carry = carry7);
    FullAdder (a = a[8], b = b[8], c = carry7, sum = out[8], carry = carry8);
    FullAdder (a = a[9], b = b[9], c = carry8, sum = out[9], carry = carry9);
    FullAdder (a = a[10], b = b[10], c = carry9, sum = out[10], carry = carry10);
    FullAdder (a = a[11], b = b[11], c = carry10, sum = out[11], carry = carry11);
    FullAdder (a = a[12], b = b[12], c = carry11, sum = out[12], carry = carry12);
    FullAdder (a = a[13], b = b[13], c = carry12, sum = out[13], carry = carry13);
    FullAdder (a = a[14], b = b[14], c = carry13, sum = out[14], carry = carry14);
    FullAdder (a = a[15], b = b[15], c = carry14, sum = out[15], carry = carry);
}

2.4 Inc16

用于实现16位增量器Inc16的芯片描述如下:

信息项描述
芯片名Inc16
输入in[16]
输出out[16]
功能out = in + 1
说明2补码的整数加法,不处理溢出的情况

可以使用16位加法器实现:
out = Add16 ( i n , 0000000000000001 ) \text{out}=\text{Add16}(in,0000000000000001) out=Add16(in,0000000000000001)

用HDL实现Inc16(直接在已有文件中修改,位置为 .\nand2tetris\projects\02\Inc16.hdl):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/02/Inc16.hdl
/**
 * 16-bit incrementer:
 * out = in + 1
 */
CHIP Inc16 {
    IN in[16];
    OUT out[16];

    PARTS:
    // 使用16位加法器实现
    Add16 (a = in, b[0] = true, b[1..15] = false, out = out);
    // Add16 (a = in, b[0] = 1, b[1..15] = 0, out = out); // 错误,只能用true/false表示,不能用1/0
}

2.5 ALU

用于实现算术逻辑单元ALU的芯片描述如下:

信息项描述
芯片名ALU
输入x[16], y[16], //两个16位数据输入
zx, // x输入置零
nx, // x输入取反
zy, // y输入置零
ny, // y输入取反
f, // 功能码:为1则代表Add,为0则代表And
no // out输出取反
输出out[16], // 16位输出
zr, //若out = 0则为True,否则False
ng, //若out < 0则为True,否则False
功能if zx then x = 0 // 16位常量0
if nx then x = !x // 按位取反
if zy then y = 0 // 16位常量0
if ny then y = !y // 按位取反
if f then out = x + y // 2补码的整数加法
else out = x & y // 按位与运算(And)
if no then out = !out // 按位取反
if out = 0 then zr = 1 else zr = 0 // 16位eq.比较
if out < 0 then ng = 1 else ng = 0 // 16位neg.比较
说明不处理溢出的情况

用HDL实现ALU(直接在已有文件中修改,位置为 .\nand2tetris\projects\02\ALU.hdl):

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/02/ALU.hdl
/**
 * ALU (Arithmetic Logic Unit):
 * Computes out = one of the following functions:
 *                0, 1, -1,
 *                x, y, !x, !y, -x, -y,
 *                x + 1, y + 1, x - 1, y - 1,
 *                x + y, x - y, y - x,
 *                x & y, x | y
 * on the 16-bit inputs x, y,
 * according to the input bits zx, nx, zy, ny, f, no.
 * In addition, computes the output bits:
 * zr = (out == 0, 1, 0)
 * ng = (out < 0,  1, 0)
 */
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0        // 16-bit constant
// if (nx == 1) sets x = !x       // bitwise not
// if (zy == 1) sets y = 0        // 16-bit constant
// if (ny == 1) sets y = !y       // bitwise not
// if (f == 1)  sets out = x + y  // integer 2's complement addition
// if (f == 0)  sets out = x & y  // bitwise and
// if (no == 1) sets out = !out   // bitwise not
CHIP ALU {
    IN  
        x[16], y[16],  // 16-bit inputs        
        zx, // zero the x input?
        nx, // negate the x input?
        zy, // zero the y input?
        ny, // negate the y input?
        f,  // compute (out = x + y) or (out = x & y)?
        no; // negate the out output?
    OUT 
        out[16], // 16-bit output
        zr,      // (out == 0, 1, 0)
        ng;      // (out < 0,  1, 0)

    PARTS:
    // if (zx == 1) sets x = 0
    Mux16 (a = x, b = false, sel = zx, out = outzx);
    
    // if (nx == 1) sets x = !x
    Not16 (in = outzx, out = notx);
    Mux16 (a = outzx, b = notx, sel = nx, out = outnx);
    
    // if (zy == 1) sets y = 0
    Mux16 (a = y, b = false, sel = zy, out = outzy);
    
    // if (ny == 1) sets y = !y
    Not16 (in = outzy, out = noty);
    Mux16 (a = outzy, b = noty, sel = ny, out = outny);
    
    // if (f == 1)  sets out = x + y
    Add16 (a = outnx, b = outny, out = xAddy);
    And16 (a = outnx, b = outny, out = xAndy);
    Mux16 (a = xAndy, b = xAddy, sel = f, out = outf);
    
    // if (no == 1) sets out = !out
    //注意: 16位的out不能通过out[0..7]形式来获取片段,只能事先保存在单独的变量中,才能调用
    Not16 (in = outf, out = notout);
    Mux16 (a = outf, b = notout, sel = no, out = out, out = out0);
    And16 (a = out0, b = true, out[0..7] = outPart1, out[8..15] = outPart2, out[15] = flag);
    
    // if (out = 0) sets zr = True
    Or8Way (in = outPart1, out = sel01);
    Or8Way (in = outPart2, out = sel02);
    Or (a = sel01, b = sel02, out = sel0);
    Mux (a = true, b = false, sel = sel0, out = zr);
    
    // if (out < 0) sets ng = True
    Mux (a = false, b = true, sel = flag, out = ng);
}

3. 总结

3.1 遇到的问题和解决办法

(1)构建全加器芯片时,采用的方法不够简洁。
一开始想使用半加器实现,觉得这样应该会比较快,但苦于没有想出具体的实现形式,所以转而用基本逻辑门实现了,写出了自己认为有些笨重的代码。
后来找到别人写过的代码,发现果然用半加器才是最快的!只是自己并没有想到,可以通过比较两次半加运算的输出结果carry位,来巧妙地确定最终输出结果的carry位。
(2)编写完ALU的HDL文件,总是读取失败。
根据Project1的经验,文件载入失败是因为代码词法/语法错误,因此打开文件仔细检查,没有发现因为大意写错的地方。
通过网络搜索找到其他人写的代码,经比较发现,我的代码在获取数组片段时,直接使用了out[0…7]形式,而这种方式在此hdl语言中不允许。参考文章1文章2
因此,借助And门,将数组片段事先保存在单独的变量中,之后调用,终于成功。

3.2 心得体会

ALU对小白来说有些难度,但是自己多看几遍书,尝试一下,也能写对大部分代码,不必过于担心。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值