SRAM, ROM, FIFO, and Register Files
| Type | Volatile? | Read/Write | Speed | Use Case |
|---|---|---|---|---|
| SRAM | Yes | R/W | Fast | Cache, registers |
| DRAM | Yes | R/W | Slower | Main memory |
| ROM | No | Read only | Fast | Boot code, constants |
| Flash | No | R/W (slow) | Medium | Storage |
| Register File | Yes | R/W | Fastest | CPU registers |
Each bit stored in a flip-flop/latch circuit. No refresh needed.
// Single-port SRAM
module sram #(
parameter DEPTH = 256,
parameter WIDTH = 8,
parameter ADDR_W = 8
)(
input clk,
input we, // Write enable
input [ADDR_W-1:0] addr,
input [WIDTH-1:0] din,
output reg [WIDTH-1:0] dout
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
always @(posedge clk) begin
if (we)
mem[addr] <= din;
dout <= mem[addr]; // Read (1 cycle latency)
end
endmodule
// True dual-port SRAM (both ports can read and write)
module dual_port_sram #(
parameter DEPTH = 256,
parameter WIDTH = 8
)(
input clk,
// Port A
input we_a,
input [7:0] addr_a,
input [WIDTH-1:0] din_a,
output reg [WIDTH-1:0] dout_a,
// Port B
input we_b,
input [7:0] addr_b,
input [WIDTH-1:0] din_b,
output reg [WIDTH-1:0] dout_b
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
// Port A
always @(posedge clk) begin
if (we_a)
mem[addr_a] <= din_a;
dout_a <= mem[addr_a];
end
// Port B
always @(posedge clk) begin
if (we_b)
mem[addr_b] <= din_b;
dout_b <= mem[addr_b];
end
endmodule
Contents fixed at design time. Used for lookup tables, boot code.
// ROM using case statement
module rom_16x8 (
input [3:0] addr,
output reg [7:0] data
);
always @(*) begin
case (addr)
4'h0: data = 8'h00;
4'h1: data = 8'h17;
4'h2: data = 8'h2E;
4'h3: data = 8'h44;
4'h4: data = 8'h58;
4'h5: data = 8'h6A;
4'h6: data = 8'h79;
4'h7: data = 8'h84;
4'h8: data = 8'h8C;
4'h9: data = 8'h90;
4'hA: data = 8'h91;
4'hB: data = 8'h8F;
4'hC: data = 8'h8A;
4'hD: data = 8'h81;
4'hE: data = 8'h76;
4'hF: data = 8'h69;
endcase
end
endmodule
// ROM using initialization file
module rom_file #(
parameter DEPTH = 256,
parameter WIDTH = 8
)(
input [7:0] addr,
output [WIDTH-1:0] data
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
initial begin
$readmemh("rom_data.hex", mem);
end
assign data = mem[addr];
endmodule
Small, fast memory with multiple read/write ports. Core of CPU.
// Register File: 32 registers, 2 read ports, 1 write port
module reg_file #(
parameter WIDTH = 32,
parameter DEPTH = 32,
parameter ADDR_W = 5
)(
input clk,
input we,
input [ADDR_W-1:0] waddr,
input [WIDTH-1:0] wdata,
input [ADDR_W-1:0] raddr1, raddr2,
output [WIDTH-1:0] rdata1, rdata2
);
reg [WIDTH-1:0] regs [0:DEPTH-1];
// Write (synchronous)
always @(posedge clk) begin
if (we && waddr != 0) // Register 0 often hardwired to 0
regs[waddr] <= wdata;
end
// Read (asynchronous - combinational)
assign rdata1 = (raddr1 == 0) ? 0 : regs[raddr1];
assign rdata2 = (raddr2 == 0) ? 0 : regs[raddr2];
endmodule
Queue memory - data comes out in the order it went in.
module sync_fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk, rst_n,
input wr_en, rd_en,
input [WIDTH-1:0] din,
output [WIDTH-1:0] dout,
output full, empty,
output [4:0] count
);
localparam PTR_W = $clog2(DEPTH);
reg [WIDTH-1:0] mem [0:DEPTH-1];
reg [PTR_W:0] wr_ptr, rd_ptr; // Extra bit for full/empty
wire [PTR_W-1:0] wr_addr = wr_ptr[PTR_W-1:0];
wire [PTR_W-1:0] rd_addr = rd_ptr[PTR_W-1:0];
// Write
always @(posedge clk) begin
if (wr_en && !full)
mem[wr_addr] <= din;
end
// Read
assign dout = mem[rd_addr];
// Pointers
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
end else begin
if (wr_en && !full)
wr_ptr <= wr_ptr + 1;
if (rd_en && !empty)
rd_ptr <= rd_ptr + 1;
end
end
// Status flags
assign empty = (wr_ptr == rd_ptr);
assign full = (wr_ptr[PTR_W] != rd_ptr[PTR_W]) &&
(wr_ptr[PTR_W-1:0] == rd_ptr[PTR_W-1:0]);
assign count = wr_ptr - rd_ptr;
endmodule
// Method 1: Initial block
reg [7:0] mem [0:255];
initial begin
mem[0] = 8'hAB;
mem[1] = 8'hCD;
// ...
end
// Method 2: Read from file
initial begin
$readmemh("data.hex", mem); // Hex format
// or
$readmemb("data.bin", mem); // Binary format
end
// data.hex file format:
// AB
// CD
// 12
// 34
// Read-First: Read returns OLD value
always @(posedge clk) begin
dout <= mem[addr]; // Read first
if (we)
mem[addr] <= din; // Then write
end
// Write-First: Read returns NEW value
always @(posedge clk) begin
if (we)
mem[addr] <= din; // Write first
dout <= mem[addr]; // Then read (new value)
end
// No-Change: Output unchanged during write
always @(posedge clk) begin
if (we)
mem[addr] <= din;
else
dout <= mem[addr];
end
| Memory Type | Ports | Typical Use |
|---|---|---|
| Single-port SRAM | 1 R/W | General storage |
| Dual-port SRAM | 2 R/W | Buffers, caches |
| ROM | 1 R | Constants, LUTs |
| Register File | Multi-port | CPU registers |
| FIFO | 1W, 1R | Rate matching, buffering |