move pages to top directory authored by Toru Koizumi's avatar Toru Koizumi
RAM の書き方
============
## RAM の種類
FPGA 上で RAM を構成する方法には、フリップフロップ、分散RAM 、ブロックRAM の三種類があります。
作り方によって回路面積や読み出しのタイミングなどが異なりますが、フリップフロップは回路面積を大きく消費するため、基本的には 分散RAM か ブロックRAM を使うようにしましょう。
分散RAM と ブロックRAM の違いは、簡単に言ってしまえば、あらかじめ用意されている RAM を使うのが ブロックRAM 、LUT を用いて構成されるのが 分散RAM です。
回路面積を消費しないのは ブロックRAM ですが、その分すこし制約がかかります。
## 分散RAM
1 read / 1 write の 分散RAM の記述例です。
重要なのは、**書き込みのタイミングをクロックと同期する**ということです。
```verilog
module ram(clk, we, r_addr, r_data, w_addr, w_data);
input clk, we;
input [4:0] r_addr, w_addr;
input [31:0] w_data;
output [31:0] r_data;
reg [31:0] mem [0:31]; //32bitのレジスタが32個(アドレスは5bit)
always @(posedge clk) begin
if(we) mem[w_addr] <= w_data; //クロックと同期して書き込まれる
end
assign r_data = mem[r_addr];
endmodule
```
同時に読める数(read port 数)を増やすのも難しくはありません。単に入出力の信号線を増やし、`assign`文を複数にするだけです。
同時に書ける数(write port 数)を増やすのも同様であり、記述上は[^1]難しくありません。当たり前ですが、同じ場所に複数同時に書き込んだりするとおかしなことになります。
[^1]:実際にどういった回路が組みあがるか、という話になると難しいですが、その辺は論理合成ツールが頑張ってくれるので気にする必要はありません。
## ブロックRAM
1 read / 1 write の ブロックRAM の記述例です。
重要なのは、**書き込みのタイミングと読み出しアドレスをそれぞれクロックと同期する**ということです。
```verilog
module ram(clk, we, r_addr, r_data, w_addr, w_data);
input clk, we;
input [4:0] r_addr, w_addr;
input [31:0] w_data;
output [31:0] r_data;
reg [4:0] addr_reg;
reg [31:0] mem [0:31];
always @(posedge clk) begin
if(we) mem[w_addr] <= w_data; //書き込みのタイミングを同期
addr_reg <= r_addr; //読み出しアドレスを同期
end
assign r_data = mem[addr_reg];
endmodule
```
port 数の増やし方は、分散RAM の時と同様です。
## RAM の初期化
RAM の初期化は、モジュール内に次の記述をすることで行うことができます(もちろん、パスは適宜読み替えてください)。
```verilog
initial $readmemh("/home/username/workspace/test.hex", mem)
```
`$readmemh` の引数には初期化したいデータが書かれた**テキストファイル**へのパスを記述します(バイナリデータではありません。十六進数で記述したテキストデータです)。
```verilog
0C00000000000000
08_01_04_00 //アンダーバーを入れてもOK
```
`readmemh` の二進数版である `readmemb` もあります。この場合、参照するファイルには
```verilog
00001100000000000000000000000000
00000100000000010000010000000000
00000100000000100000000001001000
000001_00000_00011_0000000001100101
```
などと記述します。
次のような記述も可能です。
```verilog
integer i;
initial begin
for(i=0;i<32;i=i+1)
mem[i]=0;
end
```
```verilog
initial begin
mem[0]=32'h12345678;
mem[1]=32'h87654321;
// ...
end
```
今回の実験ではプログラムをビルドすると、ICache と DCache のそれぞれの初期化データとして`code.hex``data.hex`が生成されるようになっています。
従って、ICache と DCache それぞれで`readmemh`を用いて`.hex`を読み込んでください。
## ROM の記述
ROM の場合も、write 線がないだけで RAM と同様です。すなわち、読み出しを同期すれば ブロックRAM で構成されます。
```verilog
module rom(clk, r_addr, r_data);
input clk;
input [4:0] r_addr;
output [31:0] r_data;
reg [4:0] addr_reg;
reg [31:0] mem [0:31]
always @(posedge clk) begin
addr_reg <= r_addr; //読み出しアドレスを同期
end
assign r_data = mem[addr_reg];
endmodule
```
---