跳转至内容

03. Memory

术语

时钟(Clock)基于振荡器在低电平-高电平之间交替变化,提供连续的交变信号序列。两个相邻的上升沿之间的时间间隔称为一个周期(Cycle),周期的倒数即为频率(Frequency)。

触发器(Flip-Flops)计算机中最基本的时序单元。

寄存器(Registers)具有记忆功能的设备,能够存储某一时刻的值。基本设计参数是宽度(Width)即它能够存储的比特位数,比如 16,32 或 64 位。此类寄存器的多位容量通常用字(Word)来表示。

内存(Memories)可以通过将很多寄存器堆叠起来形成随机存取内存(Random Access Memory,RAM)。每个寄存器都有一个地址(Address),可以通过地址访问特定的寄存器。基本设计参数是它的数据宽度(即每个字的宽度)和它的大小(RAM 中字的数量)。

Chip API

通过递归组合逐步构建内存块,一个 w-bit 的寄存器由 w 个 1-bit 寄存器构成,RAM8 内存块由 8 个 w-bit 寄存器构成,RAM64 内存块由 8 个 RAM8 内存块构成,以此类推。

chip
芯片名:  DFF
输入:    int
输出:    out
功能:    out(t) = in(t-1)
说明:    DFF 有现成的内置实现版本,无需自己实现。
chip
芯片名:  Bit
输入:    in, load
输出:    out
功能if load(t-1) then out(t) = in(t-1)
         else out(t) = out(t-1)
chip
芯片名:  Register
输入:    in[16], load
输出:    out[16]
功能if load(t-1) then out(t) = in(t-1)
         else out(t) = out(t-1)
chip
芯片名:  RAMn    // n 和 k 在以下有说明
输入:    in[16], load, address[k]
输出:    out[16]
功能:    out(t) = RAM[address(t)](t)

用于 Hack 平台的具体 RAM 芯片如下:

芯片名           n          k
RAM8            8          3
RAM64          64          6
RAM512        512          9
RAM4K        4096         12
RAM16K      16384         14
chip
芯片名:  PC    // 16-bit 的计数器
输入:    in[16], load, inc, reset
输出:    out[16]
功能if reset(t-1) then out(t) = 0
         else if load(t-1) then out(t) = in(t-1)
         else if inc(t-1) then out(t) = out(t-1) + 1
         else out(t) = out(t-1)

触发器

https://www.codehiddenlanguage.com/Chapter17

Reset-Set Flip-Flop

Level-Triggered D-Type Flip-Flop

Project 03

load 是理解内存芯片的关键:它不是要不要输出,而是要不要在下一个时钟周期更新保存的值。输出一直存在,只是当 load=0 时继续保持旧值。

hdl
/**
 * 1-bit register:
 * If load is asserted, the register's value is set to in;
 * Otherwise, the register maintains its current value:
 * if (load(t)) out(t+1) = in(t), else out(t+1) = out(t)
 */
CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
        Mux(a=feedback, b=in, sel=load, out=next);
        DFF(in=next, out=feedback, out=out);
}
hdl
/**
 * 16-bit register:
 * If load is asserted, the register's value is set to in;
 * Otherwise, the register maintains its current value:
 * if (load(t)) out(t+1) = int(t), else out(t+1) = out(t)
 */
CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
        Bit(in=in[0], load=load, out=out[0]);
        Bit(in=in[1], load=load, out=out[1]);
        Bit(in=in[2], load=load, out=out[2]);
        Bit(in=in[3], load=load, out=out[3]);
        Bit(in=in[4], load=load, out=out[4]);
        Bit(in=in[5], load=load, out=out[5]);
        Bit(in=in[6], load=load, out=out[6]);
        Bit(in=in[7], load=load, out=out[7]);
        Bit(in=in[8], load=load, out=out[8]);
        Bit(in=in[9], load=load, out=out[9]);
        Bit(in=in[10], load=load, out=out[10]);
        Bit(in=in[11], load=load, out=out[11]);
        Bit(in=in[12], load=load, out=out[12]);
        Bit(in=in[13], load=load, out=out[13]);
        Bit(in=in[14], load=load, out=out[14]);
        Bit(in=in[15], load=load, out=out[15]);
}
hdl
/**
 * Memory of eight 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];

    PARTS:
        DMux8Way(in=load, sel=address, a=load0, b=load1, c=load2, d=load3, e=load4, f=load5, g=load6, h=load7);
        Register(in=in, load=load0, out=out0);
        Register(in=in, load=load1, out=out1);
        Register(in=in, load=load2, out=out2);
        Register(in=in, load=load3, out=out3);
        Register(in=in, load=load4, out=out4);
        Register(in=in, load=load5, out=out5);
        Register(in=in, load=load6, out=out6);
        Register(in=in, load=load7, out=out7);
        Mux8Way16(a=out0, b=out1, c=out2, d=out3, e=out4, f=out5, g=out6, h=out7, sel=address, out=out);
}
hdl
/**
 * Memory of sixty four 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];

    PARTS:
        DMux8Way(in=load, sel=address[3..5], a=load0, b=load1, c=load2, d=load3, e=load4, f=load5, g=load6, h=load7);
        RAM8(in=in, load=load0, address=address[0..2], out=out0);
        RAM8(in=in, load=load1, address=address[0..2], out=out1);
        RAM8(in=in, load=load2, address=address[0..2], out=out2);
        RAM8(in=in, load=load3, address=address[0..2], out=out3);
        RAM8(in=in, load=load4, address=address[0..2], out=out4);
        RAM8(in=in, load=load5, address=address[0..2], out=out5);
        RAM8(in=in, load=load6, address=address[0..2], out=out6);
        RAM8(in=in, load=load7, address=address[0..2], out=out7);
        Mux8Way16(a=out0, b=out1, c=out2, d=out3, e=out4, f=out5, g=out6, h=out7, sel=address[3..5], out=out);
}

PC 比普通 Register 多了优先级:resetloadinc、保持不变。写的时候要先想清楚这些控制信号谁覆盖谁。

hdl
/**
 * Memory of 512 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM512 {
    IN in[16], load, address[9];
    OUT out[16];

    PARTS:
        DMux8Way(in=load, sel=address[6..8], a=load0, b=load1, c=load2, d=load3, e=load4, f=load5, g=load6, h=load7);
        RAM64(in=in, load=load0, address=address[0..5], out=out0);
        RAM64(in=in, load=load1, address=address[0..5], out=out1);
        RAM64(in=in, load=load2, address=address[0..5], out=out2);
        RAM64(in=in, load=load3, address=address[0..5], out=out3);
        RAM64(in=in, load=load4, address=address[0..5], out=out4);
        RAM64(in=in, load=load5, address=address[0..5], out=out5);
        RAM64(in=in, load=load6, address=address[0..5], out=out6);
        RAM64(in=in, load=load7, address=address[0..5], out=out7);
        Mux8Way16(a=out0, b=out1, c=out2, d=out3, e=out4, f=out5, g=out6, h=out7, sel=address[6..8], out=out);
}
hdl
/**
 * Memory of 4K 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM4K {
    IN in[16], load, address[12];
    OUT out[16];

    PARTS:
        DMux8Way(in=load, sel=address[9..11], a=load0, b=load1, c=load2, d=load3, e=load4, f=load5, g=load6, h=load7);
        RAM512(in=in, load=load0, address=address[0..8], out=out0);
        RAM512(in=in, load=load1, address=address[0..8], out=out1);
        RAM512(in=in, load=load2, address=address[0..8], out=out2);
        RAM512(in=in, load=load3, address=address[0..8], out=out3);
        RAM512(in=in, load=load4, address=address[0..8], out=out4);
        RAM512(in=in, load=load5, address=address[0..8], out=out5);
        RAM512(in=in, load=load6, address=address[0..8], out=out6);
        RAM512(in=in, load=load7, address=address[0..8], out=out7);
        Mux8Way16(a=out0, b=out1, c=out2, d=out3, e=out4, f=out5, g=out6, h=out7, sel=address[9..11], out=out);
}
hdl
/**
 * Memory of 16K 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM16K {
    IN in[16], load, address[14];
    OUT out[16];

    PARTS:
        DMux4Way(in=load, sel=address[12..13], a=load0, b=load1, c=load2, d=load3);
        RAM4K(in=in, load=load0, address=address[0..11], out=out0);
        RAM4K(in=in, load=load1, address=address[0..11], out=out1);
        RAM4K(in=in, load=load2, address=address[0..11], out=out2);
        RAM4K(in=in, load=load3, address=address[0..11], out=out3);
        Mux4Way16(a=out0, b=out1, c=out2, d=out3, sel=address[12..13], out=out);
}
hdl
/**
 * A 16-bit counter.
 * if      reset(t): out(t+1) = 0
 * else if load(t):  out(t+1) = in(t)
 * else if inc(t):   out(t+1) = out(t) + 1
 * else              out(t+1) = out(t)
 */
CHIP PC {
    IN in[16],inc, load, reset;
    OUT out[16];
    
    PARTS:
        Inc16(in=feedback, out=out1);
        Mux16(a=feedback, b=out1, sel=inc, out=out2);
        Mux16(a=out2, b=in, sel=load, out=out3);
        Mux16(a=out3, b=false, sel=reset, out=next);
        Register(in=next, load=true, out=feedback, out=out);
}