首頁 / 程式人雜誌 / 2015年7月號

Nand2Tetris 第三週 -- 自製記憶元件

記憶元件部份包含『單位元記憶、16位元暫存器、程式計數器 PC、RAM、ROM』等等,我們的 verilog 版模組與測試程式如下所示。

程式模組: memory.v

/* nand2tetris 的要求應該用下列方式實作,但是由於這樣元件太多會導致 icarus 編譯失敗,
出現下列訊息:

D:\Dropbox\cccweb\db\n2t>iverilog ram_test.v -o ram_test

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

所以我們在記憶體容量大的時候改用 verilog 的陣列型寫法,這樣就不會當機了。 */
`include "alu.v"

module DFF (input in, clock, load, output out);
  reg q;
    assign out = q;
  always @(posedge clock) begin
      if (load) q = in;
  end
endmodule

module Bit(input in, clock, load, output out);
    DFF dff1(in, clock, load, out);
endmodule

module Register(input[15:0] in, input clock, load, output[15:0] out);
  Bit g01(in[15], clock, load, out[15]);
  Bit g02(in[14], clock, load, out[14]);
  Bit g03(in[13], clock, load, out[13]);
  Bit g04(in[12], clock, load, out[12]);
  Bit g05(in[11], clock, load, out[11]);
  Bit g06(in[10], clock, load, out[10]);
  Bit g07(in[9],  clock, load, out[9]);
  Bit g08(in[8],  clock, load, out[8]);
  Bit g09(in[7],  clock, load, out[7]);
  Bit g10(in[6],  clock, load, out[6]);
  Bit g11(in[5],  clock, load, out[5]);
  Bit g12(in[4],  clock, load, out[4]);
  Bit g13(in[3],  clock, load, out[3]);
  Bit g14(in[2],  clock, load, out[2]);
  Bit g15(in[1],  clock, load, out[1]);
  Bit g16(in[0],  clock, load, out[0]);
endmodule

module PC(input[15:0] in, input clock, load, inc, reset, output[15:0] out);
  wire[15:0] if1, if2, if3, oInc, o;

  Or g1(load, inc, loadInc);
  Or g2(loadInc, reset, loadIncReset);

  Inc16 inc1(o, oInc);
  And16 g3(o, o, out);

  Mux16 g4(o,   oInc,  inc,   if1);
  Mux16 g5(if1, in,    load,  if2);
  Mux16 g6(if2, 16'b0, reset, if3);

  Register reg1(if3, clock, loadIncReset, o);
endmodule

module RAM8(input[15:0] in, input clock, load, input[2:0] address, output[15:0] out);
  wire[15:0] o0,o1,o2,o3,o4,o5,o6,o7;

  DMux8Way g0(1'b1, address, E0, E1, E2, E3, E4, E5, E6, E7);

  And a0(load, E0, L0); Register r0(in, clock, L0, o0);
  And a1(load, E1, L1); Register r1(in, clock, L1, o1);
  And a2(load, E2, L2); Register r2(in, clock, L2, o2);
  And a3(load, E3, L3); Register r3(in, clock, L3, o3);
  And a4(load, E4, L4); Register r4(in, clock, L4, o4);
  And a5(load, E5, L5); Register r5(in, clock, L5, o5);
  And a6(load, E6, L6); Register r6(in, clock, L6, o6);
  And a7(load, E7, L7); Register r7(in, clock, L7, o7);

  Mux8Way16 g1(o0, o1, o2, o3, o4, o5, o6, o7, address, out);
endmodule

module RAM64(input[15:0] in, input clock, load, input[5:0] address, output[15:0] out);
  wire[15:0] o0,o1,o2,o3,o4,o5,o6,o7;

  DMux8Way g0(1'b1, address[5:3], E0, E1, E2, E3, E4, E5, E6, E7);

  And a0(load, E0, L0); RAM8 m0(in,  clock, L0, address[2:0], o0);
  And a1(load, E1, L1); RAM8 m1(in,  clock, L1, address[2:0], o1);
  And a2(load, E2, L2); RAM8 m2(in,  clock, L2, address[2:0], o2);
  And a3(load, E3, L3); RAM8 m3(in,  clock, L3, address[2:0], o3);
  And a4(load, E4, L4); RAM8 m4(in,  clock, L4, address[2:0], o4);
  And a5(load, E5, L5); RAM8 m5(in,  clock, L5, address[2:0], o5);
  And a6(load, E6, L6); RAM8 m6(in,  clock, L6, address[2:0], o6);
  And a7(load, E7, L7); RAM8 m7(in,  clock, L7, address[2:0], o7);

  Mux8Way16 g1(o0, o1, o2, o3, o4, o5, o6, o7, address[5:3], out);
endmodule

module ROM32K(input[14:0] address, output[15:0] out);
  reg[15:0] m[0:2**14-1];

  assign out = m[address];
endmodule

module RAM8K(input[15:0] in, input clock, load, input[12:0] address, output[15:0] out);
  reg[15:0] m[0:2**12-1];

  assign out = m[address];

  always @(posedge clock) begin
    if (load) m[address] = in;
  end
endmodule

module RAM16K(input[15:0] in, input clock, load, input[13:0] address, output[15:0] out);
  reg[15:0] m[0:2**13-1];

  assign out = m[address];

  always @(posedge clock) begin
    if (load) m[address] = in;
  end
endmodule

測試程式: ram_test.v

`include "memory.v"

module main;
reg[15:0] in;
reg       load, clock;
reg[13:0] address;
wire[15:0] out;

RAM16K m(in, clock, load, address, out);

initial
begin
  clock=0;
  $monitor("%4dns in=%d clock=%d load=%d address=%d out=%d", $stime, in, clock, load, address, out);
    #10 in=3; load=1; address=5;
    #10 load=0; 
    #10 $finish;
end

always #2 begin
  clock=~clock;
end

endmodule

測試結果

D:\Dropbox\cccweb\db\n2t>iverilog ram_test.v -o ram_test

D:\Dropbox\cccweb\db\n2t>vvp ram_test
   0ns in=    x clock=0 load=x address=    x out=    x
   2ns in=    x clock=1 load=x address=    x out=    x
   4ns in=    x clock=0 load=x address=    x out=    x
   6ns in=    x clock=1 load=x address=    x out=    x
   8ns in=    x clock=0 load=x address=    x out=    x
  10ns in=    3 clock=1 load=1 address=    5 out=    3
  12ns in=    3 clock=0 load=1 address=    5 out=    3
  14ns in=    3 clock=1 load=1 address=    5 out=    3
  16ns in=    3 clock=0 load=1 address=    5 out=    3
  18ns in=    3 clock=1 load=1 address=    5 out=    3
  20ns in=    3 clock=0 load=0 address=    5 out=    3
  22ns in=    3 clock=1 load=0 address=    5 out=    3
  24ns in=    3 clock=0 load=0 address=    5 out=    3
  26ns in=    3 clock=1 load=0 address=    5 out=    3
  28ns in=    3 clock=0 load=0 address=    5 out=    3
  30ns in=    3 clock=1 load=0 address=    5 out=    3

另外我們針對 PC 這個元件也單獨進行測試,測試程式與結果如下。

測試程式: PC_test.v

`include "memory.v"

module main;
reg[15:0] in;
reg load, inc, reset, clock;
wire[15:0] out;

PC pc(in, clock, load, inc, reset, out);

initial
begin
  clock = 0;
  $monitor("%4dns clock=%d in=%d reset=%d inc=%d load=%d out=%d", $stime, clock, in, reset, inc, load, out);
    inc = 0; load = 0; reset=0; in=7;
    #10 reset=1; inc=1; 
    #10 reset=0;
    #10 reset=0;
    #30 inc = 0; load=1;
    #30 load = 0; inc=1;
    #30 $finish;
end

always #2 begin
  clock = clock + 1;
end

測試結果

D:\Dropbox\cccweb\db\n2t>iverilog pc_test.v -o pc_test

D:\Dropbox\cccweb\db\n2t>vvp pc_test
   0ns clock=0 in=    7 reset=0 inc=0 load=0 out=    x
   2ns clock=1 in=    7 reset=0 inc=0 load=0 out=    x
   4ns clock=0 in=    7 reset=0 inc=0 load=0 out=    x
   6ns clock=1 in=    7 reset=0 inc=0 load=0 out=    x
   8ns clock=0 in=    7 reset=0 inc=0 load=0 out=    x
  10ns clock=1 in=    7 reset=1 inc=1 load=0 out=    x
  12ns clock=0 in=    7 reset=1 inc=1 load=0 out=    x
  14ns clock=1 in=    7 reset=1 inc=1 load=0 out=    0
  16ns clock=0 in=    7 reset=1 inc=1 load=0 out=    0
  18ns clock=1 in=    7 reset=1 inc=1 load=0 out=    0
  20ns clock=0 in=    7 reset=0 inc=1 load=0 out=    0
  22ns clock=1 in=    7 reset=0 inc=1 load=0 out=    1
  24ns clock=0 in=    7 reset=0 inc=1 load=0 out=    1
  26ns clock=1 in=    7 reset=0 inc=1 load=0 out=    2
  28ns clock=0 in=    7 reset=0 inc=1 load=0 out=    2
  30ns clock=1 in=    7 reset=0 inc=1 load=0 out=    3
  32ns clock=0 in=    7 reset=0 inc=1 load=0 out=    3
  34ns clock=1 in=    7 reset=0 inc=1 load=0 out=    4
  36ns clock=0 in=    7 reset=0 inc=1 load=0 out=    4
  38ns clock=1 in=    7 reset=0 inc=1 load=0 out=    5
  40ns clock=0 in=    7 reset=0 inc=1 load=0 out=    5
  42ns clock=1 in=    7 reset=0 inc=1 load=0 out=    6
  44ns clock=0 in=    7 reset=0 inc=1 load=0 out=    6
  46ns clock=1 in=    7 reset=0 inc=1 load=0 out=    7
  48ns clock=0 in=    7 reset=0 inc=1 load=0 out=    7
  50ns clock=1 in=    7 reset=0 inc=1 load=0 out=    8
  52ns clock=0 in=    7 reset=0 inc=1 load=0 out=    8
  54ns clock=1 in=    7 reset=0 inc=1 load=0 out=    9
  56ns clock=0 in=    7 reset=0 inc=1 load=0 out=    9
  58ns clock=1 in=    7 reset=0 inc=1 load=0 out=   10
  60ns clock=0 in=    7 reset=0 inc=0 load=1 out=   10
  62ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  64ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  66ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  68ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  70ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  72ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  74ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  76ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  78ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  80ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  82ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  84ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  86ns clock=1 in=    7 reset=0 inc=0 load=1 out=    7
  88ns clock=0 in=    7 reset=0 inc=0 load=1 out=    7
  90ns clock=1 in=    7 reset=0 inc=1 load=0 out=    7
  92ns clock=0 in=    7 reset=0 inc=1 load=0 out=    7
  94ns clock=1 in=    7 reset=0 inc=1 load=0 out=    8
  96ns clock=0 in=    7 reset=0 inc=1 load=0 out=    8
  98ns clock=1 in=    7 reset=0 inc=1 load=0 out=    9
 100ns clock=0 in=    7 reset=0 inc=1 load=0 out=    9
 102ns clock=1 in=    7 reset=0 inc=1 load=0 out=   10
 104ns clock=0 in=    7 reset=0 inc=1 load=0 out=   10
 106ns clock=1 in=    7 reset=0 inc=1 load=0 out=   11
 108ns clock=0 in=    7 reset=0 inc=1 load=0 out=   11
 110ns clock=1 in=    7 reset=0 inc=1 load=0 out=   12
 112ns clock=0 in=    7 reset=0 inc=1 load=0 out=   12
 114ns clock=1 in=    7 reset=0 inc=1 load=0 out=   13
 116ns clock=0 in=    7 reset=0 inc=1 load=0 out=   13
 118ns clock=1 in=    7 reset=0 inc=1 load=0 out=   14
 120ns clock=0 in=    7 reset=0 inc=1 load=0 out=   14

結語

記憶單元的原理請參考下列文件:


本文部份內容與大部份圖片修改自 維基百科 , 使用時請遵守 姓名標示、相同方式分享 授權。
編輯: 陳鍾誠 email: ccckmit@gmail.com