Memory
Lookup Tables (LUTs)
The fundamental combinational primitive in an FPGA is a lookup-table, or LUT. LUTs are indexed by an n-bit value.
The argument to the LUT function initializes the LUT.
init
can be an int, a sequence, or an expression.
If init
is a Sequence of 0s and 1s,
each output bit in the table is initialized with those values.
An n-bit LUT is initialized with a sequence of length 2^n.
For example, to create a 2-input and gate,
LUT2
can be initialized to with the sequence[0,0,0,1]
.
If init
is an int, then the int is converted to a list of bits.
If the LUT contains n-bits, the sequence used to initialize is is 2^n bits.
The bit in the sequence, is set to the bit in init
.
For example, initializing LUT2
with init=0x8
is equivalent to initializing it to [0,0,0,1]
.
The 4th entry in the sequence is 1 because
the both bit in 0x8
is set.
All the other entries in the sequence are 0,
since all the other bits in init
are cleared.
Since the number of bits depends on the size of the LUT,
using this method of initialization requires you to set
the size of the LUT by calling a LUT function with
a known numbers of bits
(in this case, LUT2
or LUTN(init,2)
;
you cannot call the general LUT
function.
Integer expressions can also be created from python expressions.
The constants I0
, I1
, I2
, and I3
have been predefined values.
For example, LUT2(I0 & I1)
will create an LUT
which corresponds to a 2-input and gate.
The final way to initialize an LUT
is to use a python function.
This function will be called for each input value,
and should return True or False.
The various LUT functions are shown below.
Note that we are using type signatures to indicate
the inputs and the outputs of the circuit.
For example, I0, I1 -> O
indicates that there are two inputs named I0
and I1
,
and a single outout O
.
lut = LUT1(init) :: I0:Bit -> O:Bit
lut = LUT2(init) :: I0:Bit, I1:Bit -> O:Bit
lut = LUT3(init) :: I0:Bit, I1:Bit, I2:Bit -> O:Bit
lut = LUT4(init) :: I0:Bit, I1:Bit, I2:Bit, I3:Bit -> O:Bit
lut = LUT5(init) :: I0:Bit, I1:Bit, I2:Bit, ..., I4:Bit -> O:Bit
lut = LUT6(init) :: I0:Bit, I1:Bit, I2:Bit, ..., I5:Bit -> O:Bit
lut = LUT7(init) :: I0:Bit, I1:Bit, I2:Bit, ..., I6:Bit -> O:Bit
lut = LUT8(init) :: I0:Bit, I1:Bit, I2:Bit, ..., I7:Bit -> O:Bit
lut = LUTN(init, n=None) :: I0:Bit, ..., In:Bit -> O:Bit
ROM
# DefineROM ::
# RADDR:In(Bits(clog2(height))),
# RDATA:Out(Bits(width))
ROM = DefineROM(height, width)
# ROM ::
# RADDR:In(Bits(clog2(height))),
# RDATA:Out(Bits(width)),
rom = ROM(height, width)
RAM
1 read port, 1 write port RAM
# DefineRAM ::
# RADDR:In(Bits(clog2(height))),
# RDATA:Out(Bits(width)),
# WADDR:In(Bits(clog2(height))),
# WDATA:Out(Bits(width)),
# WE,In(Bit),
# CLK:In(Clock)
RAM = DefineRAM(height, width)
# RAM ::
# RADDR:In(Bits(clog2(height))),
# RDATA:Out(Bits(width)),
# WADDR:In(Bits(clog2(height))),
# WDATA:Out(Bits(width)),
# WE,In(Bit),
# CLK:In(Clock)
ram = RAM(height, width)
DualRAM
Two read port, one write port RAM
# DefineDualRAM ::
# RADDR0:In(Bits(height)),
# RDATA0:Out(Bits(width)),
# RADDR1:In(Bits(height)),
# RDATA1:Out(Bits(width)),
# WADDR:In(Bits(height)),
# WDATA:Out(Bits(width)),
# WE,In(Bit),
# CLK:In(Clock)
DualRAM = DefineDualRAM(height, width)
# DualRAM ::
# RADDR0:In(Bits(height)),
# RDATA0:Out(Bits(width)),
# RADDR1:In(Bits(height)),
# RDATA1:Out(Bits(width)),
# WADDR:In(Bits(height)),
# WDATA:Out(Bits(width)),
# WE,In(Bit),
# CLK:In(Clock)
ram = DualRAM(height, width)
Memory
Implement a blocked or SRAM. Currently all SRAMs are 4k bits. Only certain heights and widths are currently supported: (height, width) = (256, 16), (512,8), (1024, 4), (2048, 2)
# if readonly:
# ROM ::
# "RDATA", Out(Bits(width)),
# "RADDR", In(Bits(clog2(height))),
# "RCLK", In(Clock),
# "RE", In(Bit)
# else:
# RAM ::
# "RDATA", Out(Bits(width)),
# "RADDR", In(Bits(clog2(height))),
# "RCLK", In(Clock),
# "RE", In(Bit),
# "WADDR", In(Bits(height)),
# "WDATA", In(Bits(width))
# "WCLK", In(Clock),
# "WE", In(Bit),
mem = Memory( height, width, data=None, readonly=False )