diff options
Diffstat (limited to 'passes/memory/memlib.md')
-rw-r--r-- | passes/memory/memlib.md | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/passes/memory/memlib.md b/passes/memory/memlib.md new file mode 100644 index 000000000..fdc2d4bed --- /dev/null +++ b/passes/memory/memlib.md @@ -0,0 +1,505 @@ +# 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 cell +- `zero`: the memory contents are zero, memories can be mapped to this cell iff + their initialization value is entirely zero or undef +- `any`: the memory contents can be arbitrarily selected, and the initialization + will be passes as the `INIT` parameter to the mapped cell +- `no_undef`: like `any`, 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 port +- `sr`: synchronous read port +- `sw`: synchronous write port +- `arsw`: 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 address +- `PORT_<name>_WR_DATA`: the write data (for `sw`/`arsw`/`srsw` ports only) +- `PORT_<name>_RD_DATA`: the read data (for `ar`/`sr`/`arsw`/`srsw` ports only) +- `PORT_<name>_WR_EN`: the write enable or enables (for `sw`/`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` prooperty + +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 master `widths` list is acceptable, and + (for read+write ports) the read and write width has to be the same +- `width 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 + master `widths` list +- `width <width 1> <width 2> ...;`: alias for the above, to be used for read-only + or write-only ports +- `width mix;`: any width from the master `widths` 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 + master `widths` list +- `width 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 (for `tied` ports) +- `PORT_<name>_RD_WIDTH`: the selected read width (for `mix` ports) +- `PORT_<name>_WR_WIDTH`: the selected write width (for `mix` 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 allowed +- `PORT_<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 byte +- `PORT_<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 bit +- `PORT_<name>_WR_BE` signal has one bit for every data byte +- `PORT_<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 corresponding `WR_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 of `WR_EN` is set), + reading is not performed and the `RD_DATA` keeps its old value +- `undefined`: all `RD_DATA` bits corresponding to enabled `WR_DATA` bits + have undefined value, remaining bits read from memory +- `old`: all `RD_DATA` bits get the previous value in memory +- `new`: all `RD_DATA` bits get the new value in memory (transparent write) +- `new_only`: all `RD_DATA` bits corresponding to enabled `WR_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 indeterminate +- `zero`: initial data is all-0 +- `any`: initial data is arbitrarily configurable, and the selected value + will be attached to the cell as `PORT_<name>_RD_INIT_VALUE` parameter +- `no_undef`: like `any`, 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 reset +- `zero`: reset to all-0 data +- `any`: reset to arbitrary value, the selected value + will be attached to the cell as `PORT_<name>_RD_ARST_VALUE` or + `PORT_<name>_RD_SRST_VALUE` parameter +- `no_undef`: like `any`, but only 0 and 1 bits are allowed +- `init`: reset to the initial value, as specified by `rdinit` (which must be `any` + or `no_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 both `CLK_EN` and `RD_EN` (if present) +- `gated_clken`: `CLK_EN` has priority over `RD_SRST`; `RD_SRST` has priority over `RD_EN` if present +- `gated_rden`: `RD_EN` and `CLK_EN` (if present) both have priority over `RD_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 cell +- `new`: 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. |