0% found this document useful (0 votes)
99 views17 pages

Block RAM

Block RAM (BRAM) is a type of embedded memory in FPGAs that can be used to efficiently store data. BRAM has a finite number on each FPGA in fixed sizes. It can operate synchronously or asynchronously and supports true dual-port or simple dual-port operation. Single port BRAM allows either reading or writing at a time controlled by a clock. BRAM can be implemented using the Block Memory Generator IP in Vivado with options to configure ports, width, depth and initialize values.

Uploaded by

Tini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
99 views17 pages

Block RAM

Block RAM (BRAM) is a type of embedded memory in FPGAs that can be used to efficiently store data. BRAM has a finite number on each FPGA in fixed sizes. It can operate synchronously or asynchronously and supports true dual-port or simple dual-port operation. Single port BRAM allows either reading or writing at a time controlled by a clock. BRAM can be implemented using the Block Memory Generator IP in Vivado with options to configure ports, width, depth and initialize values.

Uploaded by

Tini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

IMPLEMENTING BLOCK RAM(BRAM) MEMORY IN VIVADO

Block RAM (BRAM) is a type of random access memory embedded throughout an FPGA for data
storage. You can use BRAM to transfer data between multiple clock domains by using local FIFO,
transfer data between an FPGA target and a host processor by using a DMA FIFO, transfer data
between FPGA targets by using a peer-to-peer FIFO and store large data sets on an FPGA target more
efficiently than RAM built from look-up tables.

Block RAM is a discrete part of FPGA, meaning there are only so many of them available of them on
the chip. Usually the bigger and more expensive the FPGA, the more Block RAM it will have on it.
Block RAMs come in a finite size, 4/8/16/32 kb (kilobits) are common. They have a customizable
width and depth.

Features

Synchronous Operation

Each memory access, read, and write is controlled by the clock. All inputs, data, address, clock
enable, and write enable are registered. The data output is always latched, retaining data until the
next operation. An optional output data pipeline register allows higher clock rates at the cost of an
extra cycle of latency. During a write operation, the data output can be made to reflect the
previously stored data, the newly written data, or remain unchanged. There is independent reset
control of output latches and registers.

Asynchronous Operation

The data outputs can also be set or reset asynchronously. Sleep input (places array in low power
state) can be optionally asynchronous.

True Dual-port Operation

The block RAM has two completely independent ports that share nothing but the stored

data. Simple Dual-port Operation

One port is dedicated as a write port and the other as a read port. Consequently, the data width can
be extended to 72 bits for the 36 Kb full block RAM or 36 bits for the "split" 18 Kb block RAM.

Single Port Block RAM:


The Single Port Block RAM configuration is useful when there is just one interface that needs to
retrieve data. This is also the simplest configuration and is useful for some applications. One
example would be storing Read-Only Data that is written to a fixed value when the FPGA is
programmed.
The way they work is all based on a clock. Data will be read out on the positive edge of the clock
cycle at the address specified by address port as long as Write Enable(wea) signal is not active. Read
values
come out on douta, this is the data stored in the BRAM. Note that you can only read one Rd Data
value per clock cycle. So if your Block RAM is 1024 values deep, it will take at least 1024 clock cycles
to read the entire thing out.

Suppose you want to write some data into the Block RAM buffer, then read it out at a later time. This
would involve driving wen high for one clock cycle and dout would have your write data. For the
single port configuration, you can either read or write data on Port A, you can’t do both at the same
time.

Steps to create a Single Port Block RAM:


Method 1 –
- Open Vivado > Create New Project > Specify parts/board > Finish.
- IP Integrator > Create Block Design
- Click ‘+’ and select Block Memory Generator from the menu. We obtain a block diagram. -
Double click on the block diagram to set the write width, read width, etc of the block RAM. From
Memory Type drop down list, select True Single Port RAM
- On the block diagram, right-click on the ports to draw external connections - From Design
Source create an HDL Wrapper. You get the top module for the BRAM is obtained. You can
choose if the code should be in Verilog or VHDL from Project Settings. - Write a testbench to load
some values and observe the results.

Verilog Code:
`timescale 1 ps / 1 ps

module blockram_wrapper

(BRAM_PORTA_0_addr,
BRAM_PORTA_0_clk,

BRAM_PORTA_0_din,

BRAM_PORTA_0_dout,
BRAM_PORTA_0_en,

BRAM_PORTA_0_rst,

BRAM_PORTA_0_we);

input [31:0]BRAM_PORTA_0_addr;

input BRAM_PORTA_0_clk;

input [31:0]BRAM_PORTA_0_din;

output [31:0]BRAM_PORTA_0_dout;

input BRAM_PORTA_0_en;

input BRAM_PORTA_0_rst;

input [3:0]BRAM_PORTA_0_we;

wire [31:0]BRAM_PORTA_0_addr;

wire BRAM_PORTA_0_clk;

wire [31:0]BRAM_PORTA_0_din;

wire [31:0]BRAM_PORTA_0_dout;

wire BRAM_PORTA_0_en;

wire BRAM_PORTA_0_rst;

wire [3:0]BRAM_PORTA_0_we;

blockram_vaes2 blockram_vaes2_i

(.BRAM_PORTA_0_addr(BRAM_PORTA_0_addr),

.BRAM_PORTA_0_clk(BRAM_PORTA_0_clk),

.BRAM_PORTA_0_din(BRAM_PORTA_0_din),

.BRAM_PORTA_0_dout(BRAM_PORTA_0_dout),

.BRAM_PORTA_0_en(BRAM_PORTA_0_en),

.BRAM_PORTA_0_rst(BRAM_PORTA_0_rst),

.BRAM_PORTA_0_we(BRAM_PORTA_0_we));
endmodule

TESTBENCH:

module tb_blockram;
reg [31:0] addr;

reg clk;

reg [31:0] din;

wire [31:0] dout;

reg en;

reg rst;

reg [3:0] we;

blockram_wrapper uut (

.BRAM_PORTA_0_addr(addr),

.BRAM_PORTA_0_clk(clk),

.BRAM_PORTA_0_din(din),

.BRAM_PORTA_0_dout(dout),

.BRAM_PORTA_0_en(en),

.BRAM_PORTA_0_rst(rst),

.BRAM_PORTA_0_we(we) );

initial begin

addr = 0;

clk = 0;

din = 0;

en = 0;

rst = 0;

we = 0;

rst = 1;
#10;

rst = 0;

#100;

addr = 0; // Set the address


din = 32'hA5A5A5A5; // Set the data input

en = 1; // Enable the read/write operation

we = 4'b1111; // Write-enable all bytes

#100;

addr = 4; // Set a different address

din = 32'hB5B5B5B5; // Set a different data input

en = 1;

we = 4'b0001; // Write-enable only the least significant byte

// Disable read/write

#100;

en = 0;

// Wait for a few clock cycles

#20;

// Perform read operation

addr = 0; // Set the address for read

en = 1; // Enable read

// Display the read data

#10;

$display("Read Data: %h", dout);

// Finish the simulation

#10;

$finish;

end

always #5 clk = ~clk; // Toggle the clock every 5 time

units endmodule
VHDL Code:
library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

library UNISIM;

use UNISIM.VCOMPONENTS.ALL;
entity bram_1_wrapper is

port (

BRAM_PORTA_0_addr : in STD_LOGIC_VECTOR ( 31 downto 0 );

BRAM_PORTA_0_clk : in STD_LOGIC;

BRAM_PORTA_0_din : in STD_LOGIC_VECTOR ( 31 downto 0 );


BRAM_PORTA_0_dout : out STD_LOGIC_VECTOR ( 31 downto 0 );

BRAM_PORTA_0_en : in STD_LOGIC;

BRAM_PORTA_0_rst : in STD_LOGIC;

BRAM_PORTA_0_we : in STD_LOGIC_VECTOR ( 3 downto 0 )

);

end bram_1_wrapper;

architecture STRUCTURE of bram_1_wrapper is

component bram_1 is

port (

BRAM_PORTA_0_addr : in STD_LOGIC_VECTOR ( 31 downto 0 );

BRAM_PORTA_0_clk : in STD_LOGIC;

BRAM_PORTA_0_din : in STD_LOGIC_VECTOR ( 31 downto 0 );

BRAM_PORTA_0_dout : out STD_LOGIC_VECTOR ( 31 downto 0 );

BRAM_PORTA_0_en : in STD_LOGIC;

BRAM_PORTA_0_rst : in STD_LOGIC;

BRAM_PORTA_0_we : in STD_LOGIC_VECTOR ( 3 downto 0 )

);

end
component bram_1;

begin

bram_1_i: component bram_1

port map (

BRAM_PORTA_0_addr(31 downto 0) => BRAM_PORTA_0_addr(31 downto 0),

BRAM_PORTA_0_clk => BRAM_PORTA_0_clk,

BRAM_PORTA_0_din(31 downto 0) => BRAM_PORTA_0_din(31 downto 0),


BRAM_PORTA_0_dout(31 downto 0) => BRAM_PORTA_0_dout(31 downto 0),

BRAM_PORTA_0_en => BRAM_PORTA_0_en,

BRAM_PORTA_0_rst => BRAM_PORTA_0_rst,

BRAM_PORTA_0_we(3 downto 0) => BRAM_PORTA_0_we(3 downto 0)

);

end STRUCTURE;

Results:

Schematic:
Method 2: From IP Catalog -
- Project Manager > IP Catalog OR Window > IP Catalog
- In IP Catalog window, search for Block Memory Generator and double click on Block Memory
Generator.
- A window will open with lots of options here. Using this window, we can create RAM and ROM.
- Select Interface Type to Native and Memory Type Single Port RAM.
- Click on Port A Options and type 8 in Write Width and Read Width.
- Type 16 in Write Depth and Read Depth
- Select Enable Port Type to Always Enable. Click OK.
- This will create a Block RAM of width 8-bit and depth 16 (Total 16 numbers of locations). For
locating 16 locations, 4-bit address lines is required.

Verilog Code:
module bram_singleport(clk,address,data_in,data_out,write_en);

input clk;

input write_en;

input [3:0]address;

input [7:0]data_in;

output [7:0]data_out;

blk_mem_gen_0

bram(.clka(clk),.addra(address),.dina(data_in),.douta(data_out),.wea(write_en)); endmodule

Testbench:
module tb_bram_singleport;

reg clk;
reg [3:0]address;

reg [7:0]data_in;

wire [7:0]data_out;

reg write_en;

bram_singleport uut(clk,address,data_in,data_out,write_en);
initial begin

clk = 0;

data_in = 8'h42;

address = 4'h5;

write_en = 0; //Reads from address 0

#20

write_en = 1; //Write HEX 67 to address HEX 5

#20

write_en = 0; //Reads from address HEX 5

#20

$finish();

end

always #10 clk = ~clk; //Creates clock of period 20 ns

endmodule

Schematic –
Results:

Method 3: Initializing Using COE File-


- Project Manager > IP Catalog OR Window > IP Catalog
- In the IP Catalog window, search for Block Memory Generator and double click on Block
Memory Generator.
- A window will open with lots of options here. Using this window, we can create RAM and ROM.
- Select Interface Type to Native and Memory Type Single Port RAM.
- Click on Port A Options and type 8 in Write Width and Read Width.
- Type 16 in Write Depth and Read Depth
- Select Enable Port Type to Always Enable.
- Select other options, under memory initialization >click on load Init File > upload Coe file. Then
click OK.
- This will create a Block RAM of width 8-bit and depth 16 (Total 16 numbers of locations). For
locating 16 locations, 4-bit address lines are required.
Note:
Steps for creating Coe file
- Open a Notepad
- And write the required code,
Eg:
memory_initialization_radix=16;
memory_initialization_vector=00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 20;
- Save the file with extension ‘.coe’

Verilog Code:
module bram_singleport(clk,address,data_in,data_out,write_en);

input clk;

input write_en;

input [3:0]address;

input [7:0]data_in;

output [7:0]data_out;

blk_mem_gen_0

bram(.clka(clk),.addra(address),.dina(data_in),.douta(data_out),.wea(write_en)); endmodule

Testbench:

module tb_design_1_wrapper;
reg [12:0] BRAM_PORTA_0_addr;
reg BRAM_PORTA_0_clk;
reg [5:0] BRAM_PORTA_0_din;
reg BRAM_PORTA_0_en;
reg BRAM_PORTA_0_rst;
reg [0:0] BRAM_PORTA_0_we;
wire [5:0] BRAM_PORTA_0_dout;
design_1_wrapper dut
(.BRAM_PORTA_0_addr(BRAM_PORTA_0_addr),
.BRAM_PORTA_0_clk(BRAM_PORTA_0_clk),
.BRAM_PORTA_0_din(BRAM_PORTA_0_din),
.BRAM_PORTA_0_dout(BRAM_PORTA_0_dout),
.BRAM_PORTA_0_en(BRAM_PORTA_0_en),
.BRAM_PORTA_0_rst(BRAM_PORTA_0_rst),
.BRAM_PORTA_0_we(BRAM_PORTA_0_we));

always #5 BRAM_PORTA_0_clk = ~BRAM_PORTA_0_clk;

initial begin
BRAM_PORTA_0_clk = 0;
BRAM_PORTA_0_addr = 0;
BRAM_PORTA_0_en = 0;
BRAM_PORTA_0_rst = 0;
BRAM_PORTA_0_we = 0;

#200;

for (BRAM_PORTA_0_addr = 0; BRAM_PORTA_0_addr <= 15; BRAM_PORTA_0_addr =


BRAM_PORTA_0_addr + 1)
begin
BRAM_PORTA_0_en = 1;
BRAM_PORTA_0_clk = 1;
#100;
BRAM_PORTA_0_clk = 0;
#100;
end

end

endmodule

Schematic
Results:

Simple Two Port Block RAM


The Dual Port Block RAM (or DPRAM) configuration behaves exactly the same way as the single port
configuration, except there is another port available for reading and writing data. Both Port A and
Port B behave exactly the same. Port A can perform a read on Address 0 on the same clock cycle that
Port B is writing to address 200. Therefore a DPRAM is able to perform a write on one address while
reading from a completely different address.
Verilog Code:
module dualport_bram(input CLK,

input wr_en,

input port_en_a,

input port_en_b,

input [3:0] addr_in_a,

input [3:0] addr_in_b,

input [7:0] data_in,

output [7:0] data_out_a,

output [7:0] data_out_b );

reg[7:0] ram [15:0];

always@ (posedge CLK)

begin

if (port_en_a == 1 && wr_en == 1)

ram [addr_in_a] <= data_in;

end
assign data_out_a = port_en_a ? ram[addr_in_a] :

'dZ; assign data_out_b = port_en_b ? ram[addr_in_b]

: 'dZ;

endmodule

Testbench:
module tb_dualport_bram;

reg CLK;
reg wr_en;
reg [7:0] data_in;
reg [3:0] addr_in_a;
reg [3:0] addr_in_b;
reg port_en_a;
reg port_en_b;

wire [7:0] data_out_a;


wire [7:0] data_out_b;

integer i;

dualport_bram uut (.CLK(CLK), .wr_en(wr_en), .data_in(data_in), .addr_in_a(addr_in_a),


.addr_in_b(addr_in_b), .port_en_a(port_en_a), .port_en_b(port_en_b),
.data_out_a(data_out_a), .data_out_b(data_out_b));

always #5 CLK = ~CLK;

initial begin
CLK = 1;
addr_in_a = 0;
addr_in_b = 0;
port_en_a = 0;
port_en_b = 0;
wr_en = 0;
data_in = 0;
#20;

port_en_a = 1;
wr_en = 1;
for(i=1; i <= 16; i = i + 1)
begin
data_in = i;
addr_in_a = i-1;
#10;
end

wr_en = 0;
port_en_a = 0;
port_en_b = 1;
for(i=1; i <= 16; i = i + 1)
begin
addr_in_b = i-1;
#10;
end

port_en_b = 0;

end
endmodule
Results:

You might also like