19 KiB
The memory_libmap
pass
The memory_libmap
pass is used to map memories to hardware primitives. To work,
it needs a description of available target memories in a custom format.
Basic structure
A basic library could look like this:
# A distributed-class RAM called $__RAM16X4SDP_
ram distributed $__RAM16X4SDP_ {
# Has 4 address bits (ie. 16 rows).
abits 4;
# Has 4 data bits.
width 4;
# Cost for the selection heuristic.
cost 4;
# Can be initialized to any value on startup.
init any;
# Has a synchronous write port called "W"...
port sw "W" {
# ... with a positive edge clock.
clock posedge;
}
# Has an asynchronous read port called "R".
port ar "R" {
}
}
# A block-class RAM called $__RAMB9K_
ram block $__RAMB9K_ {
# Has 13 address bits in the base (most narrow) data width.
abits 13;
# The available widths are:
# - 1 (13 address bits)
# - 2 (12 address bits)
# - 4 (11 address bits)
# - 9 (10 address bits)
# - 18 (9 address bits)
# The width selection is per-port.
widths 1 2 4 9 18 per_port;
# Has a write enable signal with 1 bit for every 9 data bits.
byte 9;
cost 64;
init any;
# Has two synchronous read+write ports, called "A" and "B".
port srsw "A" "B" {
clock posedge;
# Has a clock enable signal (gates both read and write).
clken;
# Has three per-port selectable options for handling read+write behavior:
portoption "RDWR" "NO_CHANGE" {
# When port is writing, reading is not done (output register keeps
# its value).
rdwr no_change;
}
portoption "RDWR" "OLD" {
# When port is writing, the data read is the old value (before the
# write).
rdwr old;
}
portoption "RDWR" "NEW" {
# When port is writing, the data read is the new value.
rdwr new;
}
}
}
The pass will automatically select between the two available cells and
the logic fallback (mapping the whole memory to LUTs+FFs) based on required
capabilities and cost function. The selected memories will be transformed
to intermediate $__RAM16X4SDP_
and $__RAMB9K_
cells that need to be mapped
to actual hardware cells by a techmap
pass, while memories selected for logic
fallback will be left unmapped and will be later mopped up by memory_map
pass.
RAM definition blocks
The syntax for a RAM definition is:
ram <kind: distributed|block|huge> <name> {
<ram properties>
<ports>
}
The <name>
is used as the type of the mapped cell that will be passed to techmap
.
The memory kind is one of distributed
, block
, or huge
. It describes the general
class of the memory and can be matched on by manual selection attributes.
The available ram properties are:
abits <address bits>;
width <width>;
widths <width 1> <width 2> ... <width n> <global|per_port>;
byte <width>;
cost <cost>;
widthscale [<factor>];
resource <name> <count>;
init <none|zero|any|no_undef>;
style "<name 1>" "<name 2>" "<name 3>" ...;
prune_rom;
RAM dimensions
The memory dimensions are described by abits
and width
or widths
properties.
For a simple memory cell with a fixed width, use abits
and width
like this:
abits 4;
width 4;
This will result in a 2**abits × width
memory cell.
Multiple-width memories are also possible, and use the widths
property instead.
The rules for multiple-width memories are:
- the widths are given in
widths
property in increasing order - the value in the
abits
property corresponds to the most narrow width - every width in the list needs to be greater than or equal to twice
the previous width (ie.
1 2 4 9 18
is valid,1 2 4 7 14
is not) - it is assumed that, for every width in progression, the word in memory is made of two smaller words, plus optionally some extra bits (eg. in the above list, the 9-bit word is made of two 4-bit words and 1 extra bit), and thus each sequential width in the list corresponds to one fewer usable address bit
- all addresses connected to memory ports are always
abits
bits wide, with const zero wired to the unused bits corresponding to wide ports
When multiple widths are specified, they can be per_port
or global
.
For the global
version, the pass has to pick one width for the whole cell,
and it is set on the resulting cell as the WIDTH
parameter. For the per_port
version, the selection is made on per-port basis, and passed using PORT_*_WIDTH
parameters. When the mode is per_port
, the width selection can be fine-tuned
with the port width
property.
Specifying dimensions is mandatory.
Byte width
If the memory cell has per-byte write enables, the byte
property can be used
to define the byte size (ie. how many data bits correspond to one write enable
bit).
The property is optional. If not used, it is assumed that there is a single write enable signal for each writable port.
The rules for this property are as follows:
- for every available width, the width needs to be a multiple of the byte size, or the byte size needs to be larger than the width
- if the byte size is larger than the width, the byte enable signel is assumed to be one bit wide and cover the whole port
- otherwise, the byte enable signal has one bit for every
byte
bits of the data port
The exact kind of byte enable signal is determined by the presence or absence
of the per-port wrbe_separate
property.
Cost properties
The cost
property is used to estimate the cost of using a given mapping.
This is the cost of using one cell, and will be scaled as appropriate if
the mapping requires multiple cells.
If the widthscale
property is specified, the mapping is assumed to be flexible,
with cost scaling with the percentage of data width actually used. The value
of the widthscale
property is how much of the cost is scalable as such.
If the value is omitted, all of the cost is assumed to scale.
Eg. for the following properties:
width 14;
cost 8;
widthscale 7;
The cost of a given cell will be assumed to be (8 - 7) + 7 * (used_bits / 14)
.
If widthscale
is used, The pass will attach a BITS_USED
parameter to mapped
calls, with a bitmask of which data bits of the memory are actually in use.
The parameter width will be the widest width in the widths
property, and
the bit correspondence is defined accordingly.
The cost
property is mandatory.
init
property
This property describes the state of the memory at initialization time. Can have one of the following values:
none
: the memory contents are unpredictable, memories requiring any sort of initialization will not be mapped to this cellzero
: the memory contents are zero, memories can be mapped to this cell iff their initialization value is entirely zero or undefany
: the memory contents can be arbitrarily selected, and the initialization will be passes as theINIT
parameter to the mapped cellno_undef
: likeany
, but only 0 and 1 bit values are supported (the pass will convert any x bits to 0)
The INIT
parameter is always constructed as a concatenation of words corresponding
to the widest available widths
setting, so that all available memory cell bits
are covered.
This property is optional and assumed to be none
when not present.
style
property
Provides a name (or names) for this definition that can be passed to the ram_style
or similar attribute to manually select it. Optional and can be used multiple times.
prune_rom
property
Specifying this property disqualifies the definition from consideration for source memories that have no write ports (ie. ROMs). Use this on definitions that have an obviously superior read-only alternative (eg. LUTRAMs) to make the pass skip them over quickly.
Port definition blocks
The syntax for a port group definition is:
port <ar|sr|sw|arsw|srsw> "NAME 1" "NAME 2" ... {
<port properties>
}
A port group definition defines a group of ports with identical properties. There are as many ports in a group as there are names given.
Ports come in 5 kinds:
ar
: asynchronous read portsr
: synchronous read portsw
: synchronous write portarsw
: simultanous synchronous write + asynchronous read with common address (commonly found in LUT RAMs)srsw
: synchronous write + synchronous read with common address
The port properties available are:
width <tied|mix>;
width <width 1> <width 2> ...;
width <tied|mix> <width 1> <width 2> ...;
width rd <width 1> <width 2> ... wr <width 1> <width 2> ...;
clock <posedge|negedge|anyedge> ["SHARED_NAME"];
clken;
rden;
wrbe_separate;
rdwr <undefined|no_change|new|old|new_only>;
rdinit <none|zero|any|no_undef>;
rdarst <none|zero|any|no_undef|init>;
rdsrst <none|zero|any|no_undef|init> <ungated|gatec_clken|gated_rden> [block_wr];
wrprio "NAME" "NAME" ...;
wrtrans <"NAME"|all> <old|new>;
optional;
optional_rw;
The base signals connected to the mapped cell for ports are:
PORT_<name>_ADDR
: the addressPORT_<name>_WR_DATA
: the write data (forsw
/arsw
/srsw
ports only)PORT_<name>_RD_DATA
: the read data (forar
/sr
/arsw
/srsw
ports only)PORT_<name>_WR_EN
: the write enable or enables (forsw
/arsw
/srsw
ports only)
The address is always abits
wide. If a non-narrowest width is used, the appropriate low
bits will be tied to 0.
Port width
property
If the RAM has per_port
widths, the available width selection can be further described
on per-port basis, by using one of the following properties:
width tied;
: any width from the masterwidths
list is acceptable, and (for read+write ports) the read and write width has to be the samewidth tied <width 1> <width 2> ...;
: like above, but limits the width selection to the given list; the list has to be a contiguous sublist of the masterwidths
listwidth <width 1> <width 2> ...;
: alias for the above, to be used for read-only or write-only portswidth mix;
: any width from the masterwidths
list is acceptable, and read width can be different than write width (only usable for read+write ports)width mix <width 1> <width 2> ...;
: like above, but limits the width selection to the given list; the list has to be a contiguous sublist of the masterwidths
listwidth rd <width 1> <width 2> ... wr <width 1> <width 2> ...;
: like above, but the limitted selection can be different for read and write widths
If per_port
widths are in use and this property is not specified, width tied;
is assumed.
The parameters attached to the cell in per_port
widths mode are:
PORT_<name>_WIDTH
: the selected width (fortied
ports)PORT_<name>_RD_WIDTH
: the selected read width (formix
ports)PORT_<name>_WR_WIDTH
: the selected write width (formix
ports)
clock
property
The clock
property is used with synchronous ports (and synchronous ports only).
It is mandatory for them and describes the clock polarity and clock sharing.
anyedge
means that both polarities are supported.
If a shared clock name is provided, the port is assumed to have a shared clock signal with all other ports using the same shared name. Otherwise, the port is assumed to have its own clock signal.
The port clock is always provided on the memory cell as PORT_<name>_CLK
signal
(even if it is also shared). Shared clocks are also provided as CLK_<shared_name>
signals.
For anyedge
clocks, the cell gets a PORT_<name>_CLKPOL
parameter that is set
to 1 for posedge
clocks and 0 for negedge
clocks. If the clock is shared,
the same information will also be provided as CLK_<shared_name>_POL
parameter.
clken
and rden
The clken
property, if present, means that the port has a clock enable signal
gating both reads and writes. Such signal will be provided to the mapped cell
as PORT_<name>_CLK_EN
. It is only applicable to synchronous ports.
The rden
property, if present, means that the port has a read clock enable signal.
Such signal will be provided to the mapped cell as PORT_<name>_RD_EN
. It is only
applicable to synchronous read ports (sr
and srsw
).
For sr
ports, both of these options are effectively equivalent.
wrbe_separate
and the write enables
The wrbe_separate
property specifies that the write byte enables are provided
as a separate signal from the main write enable. It can only be used when the
RAM-level byte
property is also specified.
The rules are as follows:
If no byte
is specified:
wrbe_separate
is not allowedPORT_<name>_WR_EN
signal is single bit
If byte
is specified, but wrbe_separate
is not:
PORT_<name>_WR_EN
signal has one bit for every data bytePORT_<name>_WR_EN_WIDTH
parameter is the width of the above (only present for multiple-width cells)
If byte
is specified and wrbe_separate
is present:
PORT_<name>_WR_EN
signal is single bitPORT_<name>_WR_BE
signal has one bit for every data bytePORT_<name>_WR_BE_WIDTH
parameter is the width of the above (only present for multiple-width cells)- a given byte is written iff all of
CLK_EN
(if present),WR_EN
, and the correspondingWR_BE
bit are one
This property can only be used on write ports.
rdwr
property
This property is allowed only on srsw
ports and describes read-write interactions.
The possible values are:
no_change
: if write is being performed (any bit ofWR_EN
is set), reading is not performed and theRD_DATA
keeps its old valueundefined
: allRD_DATA
bits corresponding to enabledWR_DATA
bits have undefined value, remaining bits read from memoryold
: allRD_DATA
bits get the previous value in memorynew
: allRD_DATA
bits get the new value in memory (transparent write)new_only
: allRD_DATA
bits corresponding to enabledWR_DATA
bits get the new value, all others are undefined
If this property is not found on an srsw
port, undefined
is assumed.
Read data initial value and resets
The rdinit
, rdarst
, and rdsrst
are applicable only to synchronous read
ports.
rdinit
describes the initial value of the read port data, and can be set to
one of the following:
none
: initial data is indeterminatezero
: initial data is all-0any
: initial data is arbitrarily configurable, and the selected value will be attached to the cell asPORT_<name>_RD_INIT_VALUE
parameterno_undef
: likeany
, but only 0 and 1 bits are allowed
rdarst
and rdsrst
describe the asynchronous and synchronous reset capabilities.
The values are similar to rdinit
:
none
: no resetzero
: reset to all-0 dataany
: reset to arbitrary value, the selected value will be attached to the cell asPORT_<name>_RD_ARST_VALUE
orPORT_<name>_RD_SRST_VALUE
parameterno_undef
: likeany
, but only 0 and 1 bits are allowedinit
: reset to the initial value, as specified byrdinit
(which must beany
orno_undef
itself)
If the capability is anything other than none
, the reset signal
will be provided as PORT_<name>_RD_ARST
or PORT_<name>_RD_SRST
.
For rdsrst
, the priority must be additionally specified, as one of:
ungated
:RD_SRST
has priority over bothCLK_EN
andRD_EN
(if present)gated_clken
:CLK_EN
has priority overRD_SRST
;RD_SRST
has priority overRD_EN
if presentgated_rden
:RD_EN
andCLK_EN
(if present) both have priority overRD_SRST
Also, rdsrst
can optionally have block_wr
specified, which means that sync reset
cannot be performed in the same cycle as a write.
If not provided, none
is assumed for all three properties.
Write priority
The wrprio
property is only allowed on write ports and defines a priority relationship
between port — when wrprio "B";
is used in definition of port "A"
, and both ports
simultanously write to the same memory cell, the value written by port "A"
will have
precedence.
This property is optional, and can be used multiple times as necessary. If no relationship is described for a pair of write ports, no priority will be assumed.
Write transparency
The wrtrans
property is only allowed on write ports and defines behavior when
another synchronous read port reads from the memory cell at the same time as the
given port writes it. The values are:
old
: the read port will get the old value of the cellnew
: the read port will get the new value of the cell
This property is optional, and can be used multiple times as necessary. If no relationship is described for a pair of ports, the value read is assumed to be indeterminate.
Note that this property is not used to describe the read value on the port itself for srsw
ports — for that purpose, the rdwr
property is used instead.
Optional ports
The optional;
property will make the pass attach a PORT_<name>_USED
parameter
with a boolean value specifying whether a given port was meaningfully used in
mapping a given cell. Likewise, optional_rw;
will attach PORT_<name>_RD_USED
and PORT_<name>_WR_USED
the specify whether the read / write part in particular
was used. These can be useful if the mapping has some meaningful optimization
to apply for unused ports, but doesn't otherwise influence the selection process.
Options
For highly configurable cells, multiple variants may be described in one cell description.
All properties and port definitions within a RAM or port definition can be put inside
an option
block as follows:
option "NAME" <value> {
<properties, ports, ...>
}
The value and name of an option are arbitrary, and the selected option value
will be provided to the cell as OPTION_<name>
parameter. Values can be
strings or integers.
Likewise, for per-port options, a portoption
block can be used:
portoption "NAME" <value> {
<properties, ...>
}
These options will be provided as PORT_<pname>_OPTION_<oname>
parameters.
The library parser will simply expand the RAM definition for every possible combination of option values mentioned in the RAM body, and likewise for port definitions. This can lead to a combinatorial explosion.
If some option values cannot be used together, a forbid
pseudo-property can be used
to discard a given combination, eg:
option "ABC" 1 {
portoption "DEF" "GHI" {
forbid;
}
}
will disallow combining the RAM option ABC = 2
with port option DEF = "GHI"
.
Ifdefs
To allow reusing a library for multiple FPGA families with slighly differing
capabilities, ifdef
(and ifndef
) blocks are provided:
ifdef IS_FANCY_FPGA_WITH_CONFIGURABLE_ASYNC_RESET {
rdarst any;
} else {
rdarst zero;
}
Such blocks can be enabled by passing the -D
option to the pass.