12.8
Memory Synthesis
There are several approaches to
memory synthesis:
-
Random logic using flip-flops or latches
-
Register files in datapaths
-
RAM standard components
-
RAM compilers
The first approach uses large vectors or arrays in the HDL code. The synthesizer will map these elements to arrays of flip-flops or latches depending on how the timing of the assignments is handled. This approach is independent of any software or type of ASIC and is the easiest to use but inefficient in terms of area. A flip-flop may take up 10 to 20 times the area of a six-transistor static RAM cell.
The second approach uses a synthesis directive or hand instantiation to synthesize a memory to a datapath component. Usually the datapath components are constructed from latches in a regular array. These are slightly more efficient than a random arrangement of logic cells, but the way we create the memory then depends on the software and the ASIC technology we are using.
The third approach uses standard components supplied by an ASIC vendor. For example, we can instantiate a small RAM using CLBs in a Xilinx FPGA. This approach is very dependent on the technology. For example, we could not easily transfer a design that uses Xilinx CLBs as SRAM to an Actel FPGA.
The last approach, using a custom RAM compiler, is the most area-efficient approach. It depends on having the capability to call a compiler from within the synthesis tool or to instantiate a component that has already been compiled.
12.8.1 Memory Synthesis in Verilog
Most synthesizers implement a Verilog memory array, such as the one shown in the following code, as an array of latches or flip-flops.
reg
[31:0] MyMemory [3:0]; // a 4 x 32-bit register
For example, the following code models a small RAM, and the synthesizer maps the memory array to sequential logic:
module
RAM_1(A, CEB, WEB, OEB, INN, OUTT);
input
[6:0] A;
input
CEB,WEB,OEB;
input
[4:0]INN;
output
[4:0] OUTT;
reg
[4:0] OUTT;
reg
[4:0] int_bus;
reg
[4:0] memory [127:0];
always
@(
negedge
CEB)
begin
if
(CEB == 0)
begin
if
(WEB == 1) int_bus = memory[A];
else
if
(WEB == 0)
begin
memory[A] = INN; int_bus = INN;
end
else
int_bus = 5'bxxxxx;
end
end
always
@(OEB
or
int_bus)
begin
case
(OEB) 0 : OUTT = int_bus;
default
: OUTT = 5'bzzzzz;
endcase
end
endmodule
Memory synthesis using random control logic and transparent latches for each bit is reasonable only for small, fast register files, or for local RAM on an MGA or CBIC. For large RAMs synthesized memory becomes very expensive and instead you should normally use a dedicated RAM compiler.
Typically there will be restrictions on synthesizing RAM with multiple read/writes:
-
If you write to the same memory in two different processes, be careful to avoid address contention.
-
You need a multiport RAM if you read or write to multiple locations simultaneously.
-
If you write and read the same memory location, you have to be very careful. To mimic hardware you need to read before you write so that you read the old memory value. If you attempt to write before reading, the difference between blocking and nonblocking assignments can lead to trouble.
You cannot make a memory access that depends on another memory access in the same clock cycle. For example, you cannot do this:
memory[i + 1] = memory[i]; // needs two clock cycles
or this:
pointer = memory[memory[i]]; // needs two clock cycles
For the same reason (but less obviously) we cannot do this:
pc = memory[addr1]; memory[addr2] = pc + 1; // not on the same cycle
12.8.2 Memory Synthesis in VHDL
VHDL allows multidimensional arrays so that we can synthesize a memory as an array of latches by declaring a two-dimensional array as follows:
type
memStor
is
array(3
downto
0)
of
integer
; -- This is OK.
subtype
MemReg
is
STD_LOGIC_VECTOR(15
downto
0); -- So is this.
type
memStor
is
array(3
downto
0)
of
MemReg;
-- other code...
signal
Mem1 : memStor;
As an example, the following code models a standard-cell RAM:
library
IEEE;
use
IEEE.STD_LOGIC_1164.
all
;
package
RAM_package
is
constant numOut : INTEGER := 8;
constant wordDepth: INTEGER := 8;
constant numAddr : INTEGER := 3;
subtype
MEMV
is
STD_LOGIC_VECTOR(numOut-1
downto
0);
type
MEM
is
array
(wordDepth-1
downto
0)
of
MEMV;
end
RAM_package;
library
IEEE;
use
IEEE.STD_LOGIC_1164.
all
;
use
IEEE.NUMERIC_STD.
all
;
use
work.RAM_package.
all
;
entity
RAM_1
is
port
(signal A :
in
STD_LOGIC_VECTOR(numAddr-1
downto
0);
signal
CEB, WEB, OEB :
in
STD_LOGIC;
signal
INN :
in
MEMV;
signal
OUTT :
out
MEMV);
end
RAM_1;
architecture
Synthesis_1
of
RAM_1
is
signal
i_bus : MEMV; -- RAM internal data latch
signal
mem : MEM; -- RAM data
begin
process
begin
wait
until
CEB = '0';
if
WEB = '1'
then
i_bus <= mem(TO_INTEGER(UNSIGNED(A)));
elsif
WEB = '0'
then
mem(TO_INTEGER(UNSIGNED(A))) <= INN;
i_bus <= INN;
else
i_bus <= (
others
=> 'X');
end
if
;
end
process
;
process
(OEB, int_bus)
begin
-- control output drivers:
case
(OEB)
is
when
'0' => OUTT <= i_bus;
when
'1' => OUTT <= (
others
=> 'Z');
when
others
=> OUTT <= (
others
=> 'X');
end
case
;
end
process
;
end
Synthesis_1;
[ Chapter start ] [ Previous page ] [ Next page ] |