aboutsummaryrefslogtreecommitdiffstats
path: root/passes/memory/memlib.md
blob: fdc2d4bed27f19e6c7ba24e27d72845e09b133c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
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.