What is a single port RAM ?
A single-port RAM (Random Access Memory) is a type of digital memory component that allows data to be read from and written to a single memory location (address) at a time. It is a simple form of memory that provides a basic storage mechanism for digital systems. Each memory location in a single-port RAM can store a fixed number of bits (usually a power of 2, such as 8, 16, 32, etc.).
During a read operation, the data stored at a specific address is retrieved. During a write operation, new data is stored at a specific address, replacing the previous data.
Why is it called single port ?
A single-port RAM has only one data port, which means that read and write operations cannot occur simultaneously at different addresses. If a write operation is in progress, a read operation must wait, and vice versa.
Signals
Single-port RAMs have address lines that are used to select the memory location to be accessed. The number of address lines determines the maximum number of memory locations that the RAM can hold.
Data lines are used to carry the actual data to be read from or written to the memory location.
Control signals, such as read enable (read request) and write enable (write request), are used to initiate specific memory operations.

Verilog Design
module single_port_sync_ram
# (parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 32,
parameter DEPTH = 16
)
( input clk,
input [ADDR_WIDTH-1:0] addr,
inout [DATA_WIDTH-1:0] data,
input cs,
input we,
input oe
);
reg [DATA_WIDTH-1:0] tmp_data;
reg [DATA_WIDTH-1:0] mem [DEPTH];
always @ (posedge clk) begin
if (cs & we)
mem[addr] <= data;
end
always @ (posedge clk) begin
if (cs & !we)
tmp_data <= mem[addr];
end
assign data = cs & oe & !wr ? tmp_data : 'hz;
endmodule

Verilog Testbench
module tb;
parameter ADDR_WIDTH = 4;
parameter DATA_WIDTH = 16;
parameter DEPTH = 16;
reg clk;
reg cs;
reg we;
reg oe;
reg [ADDR_WIDTH-1:0] addr;
wire [DATA_WIDTH-1:0] data;
reg [DATA_WIDTH-1:0] tb_data;
single_port_sync_ram #(.DATA_WIDTH(DATA_WIDTH)) u0
( .clk(clk),
.addr(addr),
.data(data),
.cs(cs),
.we(we),
.oe(oe)
);
always #10 clk = ~clk;
assign data = !oe ? tb_data : 'hz;
initial begin
{clk, cs, we, addr, tb_data, oe} <= 0;
repeat (2) @ (posedge clk);
for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
repeat (1) @(posedge clk) addr <= i; we <= 1; cs <=1; oe <= 0; tb_data <= $random;
end
for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
repeat (1) @(posedge clk) addr <= i; we <= 0; cs <= 1; oe <= 1;
end
#20 $finish;
end
endmodule
Single-port RAMs are commonly used in various digital systems for tasks such as data storage, temporary buffering, and data manipulation.