-
Notifications
You must be signed in to change notification settings - Fork 855
/
example_synth.rst
843 lines (648 loc) · 31.4 KB
/
example_synth.rst
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
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
Synthesis starter
-----------------
This page will be a guided walkthrough of the prepackaged iCE40 FPGA synthesis
script - `synth_ice40`. We will take a simple design through each step, looking
at the commands being called and what they do to the design. While `synth_ice40`
is specific to the iCE40 platform, most of the operations we will be discussing
are common across the majority of FPGA synthesis scripts. Thus, this document
will provide a good foundational understanding of how synthesis in Yosys is
performed, regardless of the actual architecture being used.
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/synthesis/synth`
Demo design
~~~~~~~~~~~
.. role:: yoscrypt(code)
:language: yoscrypt
First, let's quickly look at the design we'll be synthesizing:
.. todo:: reconsider including the whole (~77 line) design like this
.. literalinclude:: /code_examples/fifo/fifo.v
:language: Verilog
:linenos:
:caption: :file:`fifo.v`
:name: fifo-v
.. todo:: fifo.v description
Loading the design
~~~~~~~~~~~~~~~~~~
Let's load the design into Yosys. From the command line, we can call ``yosys
fifo.v``. This will open an interactive Yosys shell session and immediately
parse the code from :ref:`fifo-v` and convert it into an Abstract Syntax Tree
(AST). If you are interested in how this happens, there is more information in
the document, :doc:`/yosys_internals/flow/verilog_frontend`. For now, suffice
it to say that we do this to simplify further processing of the design. You
should see something like the following:
.. literalinclude:: /code_examples/fifo/fifo.out
:language: console
:start-at: $ yosys fifo.v
:end-before: echo on
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/more_scripting/load_design`
Elaboration
~~~~~~~~~~~
Now that we are in the interactive shell, we can call Yosys commands directly.
Our overall goal is to call :yoscrypt:`synth_ice40 -top fifo`, but for now we
can run each of the commands individually for a better sense of how each part
contributes to the flow. We will also start with just a single module;
``addr_gen``.
At the bottom of the `help` output for
`synth_ice40` is the complete list of commands called by this script.
Let's start with the section labeled ``begin``:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: begin:
:end-before: flatten:
:dedent:
:caption: ``begin`` section
:name: synth_begin
:yoscrypt:`read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v` loads the
iCE40 cell models which allows us to include platform specific IP blocks in our
design. PLLs are a common example of this, where we might need to reference
``SB_PLL40_CORE`` directly rather than being able to rely on mapping passes
later. Since our simple design doesn't use any of these IP blocks, we can skip
this command for now. Because these cell models will also be needed once we
start mapping to hardware we will still need to load them later.
.. note::
``+/`` is a dynamic reference to the Yosys ``share`` directory. By default,
this is ``/usr/local/share/yosys``. If using a locally built version of
Yosys from the source directory, this will be the ``share`` folder in the
same directory.
.. _addr_gen_example:
The addr_gen module
^^^^^^^^^^^^^^^^^^^
Since we're just getting started, let's instead begin with :yoscrypt:`hierarchy
-top addr_gen`. This command declares that the top level module is
``addr_gen``, and everything else can be discarded.
.. literalinclude:: /code_examples/fifo/fifo.v
:language: Verilog
:start-at: module addr_gen
:end-at: endmodule //addr_gen
:lineno-match:
:caption: ``addr_gen`` module source
:name: addr_gen-v
.. note::
`hierarchy` should always be the first command after the design has been
read. By specifying the top module, `hierarchy` will also set the ``(* top
*)`` attribute on it. This is used by other commands that need to know which
module is the top.
.. use doscon for a console-like display that supports the `yosys> [command]` format.
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> hierarchy -top addr_gen
:end-before: yosys> select
:caption: :yoscrypt:`hierarchy -top addr_gen` output
:name: hierarchy_output
Our ``addr_gen`` circuit now looks like this:
.. figure:: /_images/code_examples/fifo/addr_gen_hier.*
:class: width-helper
:name: addr_gen_hier
``addr_gen`` module after `hierarchy`
Simple operations like ``addr + 1`` and ``addr == MAX_DATA-1`` can be extracted
from our ``always @`` block in :ref:`addr_gen-v`. This gives us the highlighted
`$add` and `$eq` cells we see. But control logic (like the ``if .. else``) and
memory elements (like the ``addr <= 0``) are not so straightforward. These get
put into "processes", shown in the schematic as ``PROC``. Note how the second
line refers to the line numbers of the start/end of the corresponding ``always
@`` block. In the case of an ``initial`` block, we instead see the ``PROC``
referring to line 0.
To handle these, let us now introduce the next command: :doc:`/cmd/proc`. `proc`
is a macro command like `synth_ice40`. Rather than modifying the design
directly, it instead calls a series of other commands. In the case of `proc`,
these sub-commands work to convert the behavioral logic of processes into
multiplexers and registers. Let's see what happens when we run it. For now, we
will call :yoscrypt:`proc -noopt` to prevent some automatic optimizations which
would normally happen.
.. figure:: /_images/code_examples/fifo/addr_gen_proc.*
:class: width-helper
:name: addr_gen_proc
``addr_gen`` module after :yoscrypt:`proc -noopt`
There are now a few new cells from our ``always @``, which have been
highlighted. The ``if`` statements are now modeled with `$mux` cells, while the
register uses an `$adff` cell. If we look at the terminal output we can also
see all of the different ``proc_*`` commands being called. We will look at each
of these in more detail in :doc:`/using_yosys/synthesis/proc`.
Notice how in the top left of :ref:`addr_gen_proc` we have a floating wire,
generated from the initial assignment of 0 to the ``addr`` wire. However, this
initial assignment is not synthesizable, so this will need to be cleaned up
before we can generate the physical hardware. We can do this now by calling
`clean`. We're also going to call `opt_expr` now, which would normally be
called at the end of `proc`. We can call both commands at the same time by
separating them with a colon and space: :yoscrypt:`opt_expr; clean`.
.. figure:: /_images/code_examples/fifo/addr_gen_clean.*
:class: width-helper
:name: addr_gen_clean
``addr_gen`` module after :yoscrypt:`opt_expr; clean`
You may also notice that the highlighted `$eq` cell input of ``255`` has changed
to ``8'11111111``. Constant values are presented in the format
``<bit_width>'<bits>``, with 32-bit values instead using the decimal number.
This indicates that the constant input has been reduced from 32-bit wide to
8-bit wide. This is a side-effect of running `opt_expr`, which performs
constant folding and simple expression rewriting. For more on why this
happens, refer to :doc:`/using_yosys/synthesis/opt` and the :ref:`section on
opt_expr <adv_opt_expr>`.
.. note::
:doc:`/cmd/clean` can also be called with two semicolons after any command,
for example we could have called :yoscrypt:`opt_expr;;` instead of
:yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line
with ``;;``. It is beneficial to run `clean` before inspecting intermediate
products to remove disconnected parts of the circuit which have been left
over, and in some cases can reduce the processing required in subsequent
commands.
.. todo:: consider a brief glossary for terms like adff
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/proc`
- :doc:`/using_yosys/synthesis/opt`
The full example
^^^^^^^^^^^^^^^^
Let's now go back and check on our full design by using :yoscrypt:`hierarchy
-check -top fifo`. By passing the ``-check`` option there we are also telling
the `hierarchy` command that if the design includes any non-blackbox modules
without an implementation it should return an error.
Note that if we tried to run this command now then we would get an error. This
is because we already removed all of the modules other than ``addr_gen``. We
could restart our shell session, but instead let's use two new commands:
- :doc:`/cmd/design`, and
- :doc:`/cmd/read_verilog`.
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: design -reset
:end-before: yosys> proc
:caption: reloading :file:`fifo.v` and running :yoscrypt:`hierarchy -check -top fifo`
Notice how this time we didn't see any of those ``$abstract`` modules? That's
because when we ran ``yosys fifo.v``, the first command Yosys called was
:yoscrypt:`read_verilog -defer fifo.v`. The ``-defer`` option there tells
`read_verilog` only read the abstract syntax tree and defer actual compilation
to a later `hierarchy` command. This is useful in cases where the default
parameters of modules yield invalid code which is not synthesizable. This is why
Yosys defers compilation automatically and is one of the reasons why hierarchy
should always be the first command after loading the design. If we know that
our design won't run into this issue, we can skip the ``-defer``.
.. todo:: `hierarchy` failure modes
.. note::
The number before a command's output increments with each command run. Don't
worry if your numbers don't match ours! The output you are seeing comes from
the same script that was used to generate the images in this document,
included in the source as :file:`fifo.ys`. There are extra commands being run
which you don't see, but feel free to try them yourself, or play around with
different commands. You can always start over with a clean slate by calling
``exit`` or hitting :kbd:`ctrl+d` (i.e. EOF) and re-launching the Yosys
interactive terminal. :kbd:`ctrl+c` (i.e. SIGINT) will also end the terminal
session but will return an error code rather than exiting gracefully.
We can also run `proc` now to finish off the full :ref:`synth_begin`. Because
the design schematic is quite large, we will be showing just the data path for
the ``rdata`` output. If you would like to see the entire design for yourself,
you can do so with :doc:`/cmd/show`. Note that the `show` command only works
with a single module, so you may need to call it with :yoscrypt:`show fifo`.
:ref:`show_intro` section in :doc:`/getting_started/scripting_intro` has more on
how to use `show`.
.. figure:: /_images/code_examples/fifo/rdata_proc.*
:class: width-helper
:name: rdata_proc
``rdata`` output after `proc`
The highlighted ``fifo_reader`` block contains an instance of the
:ref:`addr_gen_proc` that we looked at earlier. Notice how the type is shown as
``$paramod\\addr_gen\\MAX_DATA=s32'...``. This is a "parametric module": an
instance of the ``addr_gen`` module with the ``MAX_DATA`` parameter set to the
given value.
The other highlighted block is a `$memrd` cell. At this stage of synthesis we
don't yet know what type of memory is going to be implemented, but we *do* know
that ``rdata <= data[raddr];`` could be implemented as a read from memory. Note
that the `$memrd` cell here is asynchronous, with both the clock and enable
signal undefined; shown with the ``1'x`` inputs.
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/synthesis/proc`
Flattening
~~~~~~~~~~
At this stage of a synthesis flow there are a few other commands we could run.
In `synth_ice40` we get these:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: flatten:
:end-before: coarse:
:dedent:
:name: synth_flatten
:caption: ``flatten`` section
First off is `flatten`. Flattening the design like this can allow for
optimizations between modules which would otherwise be missed. Let's run
:yoscrypt:`flatten;;` on our design.
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> flatten
:end-before: yosys> select
:name: flat_clean
:caption: output of :yoscrypt:`flatten;;`
.. figure:: /_images/code_examples/fifo/rdata_flat.*
:class: width-helper
:name: rdata_flat
``rdata`` output after :yoscrypt:`flatten;;`
.. role:: yoterm(code)
:language: doscon
The pieces have moved around a bit, but we can see :ref:`addr_gen_proc` from
earlier has replaced the ``fifo_reader`` block in :ref:`rdata_proc`. We can
also see that the ``addr`` output has been renamed to :file:`fifo_reader.addr`
and merged with the ``raddr`` wire feeding into the `$memrd` cell. This wire
merging happened during the call to `clean` which we can see in the
:ref:`flat_clean`.
.. note::
`flatten` and `clean` would normally be combined into a
single :yoterm:`yosys> flatten;;` output, but they appear separately here as
a side effect of using `echo` for generating the terminal style
output.
Depending on the target architecture, this stage of synthesis might also see
commands such as `tribuf` with the ``-logic`` option and `deminout`. These
remove tristate and inout constructs respectively, replacing them with logic
suitable for mapping to an FPGA. Since we do not have any such constructs in
our example running these commands does not change our design.
The coarse-grain representation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At this stage, the design is in coarse-grain representation. It still looks
recognizable, and cells are word-level operators with parametrizable width. This
is the stage of synthesis where we do things like const propagation, expression
rewriting, and trimming unused parts of wires.
This is also where we convert our FSMs and hard blocks like DSPs or memories.
Such elements have to be inferred from patterns in the design and there are
special passes for each. Detection of these patterns can also be affected by
optimizations and other transformations done previously.
.. note::
While the iCE40 flow had a :ref:`synth_flatten` and put `proc` in the
:ref:`synth_begin`, some synthesis scripts will instead include these in this
section.
Part 1
^^^^^^
In the iCE40 flow, we start with the following commands:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: coarse:
:end-before: wreduce
:dedent:
:caption: ``coarse`` section (part 1)
:name: synth_coarse1
We've already come across `opt_expr`, and `opt_clean` is the same as `clean` but
with more verbose output. The `check` pass identifies a few obvious problems
which will cause errors later. Calling it here lets us fail faster rather than
wasting time on something we know is impossible.
Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple
optimizations on the design. This command also ensures that only a specific
subset of FF types are included, in preparation for the next command:
:doc:`/cmd/fsm`. Both `opt` and `fsm` are macro commands which are explored in
more detail in :doc:`/using_yosys/synthesis/opt` and
:doc:`/using_yosys/synthesis/fsm` respectively.
Up until now, the data path for ``rdata`` has remained the same since
:ref:`rdata_flat`. However the next call to `opt` does cause a change.
Specifically, the call to `opt_dff` without the ``-nodffe -nosdff`` options is
able to fold one of the `$mux` cells into the `$adff` to form an `$adffe` cell;
highlighted below:
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> opt_dff
:end-before: yosys> select
:caption: output of `opt_dff`
.. figure:: /_images/code_examples/fifo/rdata_adffe.*
:class: width-helper
:name: rdata_adffe
``rdata`` output after `opt_dff`
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/fsm`
- :doc:`/using_yosys/synthesis/opt`
Part 2
^^^^^^
The next group of commands performs a series of optimizations:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-at: wreduce
:end-before: t:$mul
:dedent:
:caption: ``coarse`` section (part 2)
:name: synth_coarse2
First up is :doc:`/cmd/wreduce`. If we run this we get the following:
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> wreduce
:end-before: yosys> select
:caption: output of `wreduce`
Looking at the data path for ``rdata``, the most relevant of these width
reductions are the ones affecting ``fifo.$flatten\fifo_reader.$add$fifo.v``.
That is the `$add` cell incrementing the fifo_reader address. We can look at
the schematic and see the output of that cell has now changed.
.. todo:: pending bugfix in `wreduce` and/or `opt_clean`
.. figure:: /_images/code_examples/fifo/rdata_wreduce.*
:class: width-helper
:name: rdata_wreduce
``rdata`` output after `wreduce`
The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`.
Neither of these affect our design, and they're explored in more detail in
:doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap
-map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by
converting them to LUTs instead. The usage of `techmap` is explored more in
:doc:`/using_yosys/synthesis/techmap_synth`.
Our next command to run is
:doc:`/cmd/memory_dff`.
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> memory_dff
:end-before: yosys> select
:caption: output of `memory_dff`
.. figure:: /_images/code_examples/fifo/rdata_memrdv2.*
:class: width-helper
:name: rdata_memrdv2
``rdata`` output after `memory_dff`
As the title suggests, `memory_dff` has merged the output `$dff` into the
`$memrd` cell and converted it to a `$memrd_v2` (highlighted). This has also
connected the ``CLK`` port to the ``clk`` input as it is now a synchronous
memory read with appropriate enable (``EN=1'1``) and reset (``ARST=1'0`` and
``SRST=1'0``) inputs.
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/opt`
- :doc:`/using_yosys/synthesis/techmap_synth`
- :doc:`/using_yosys/synthesis/memory`
Part 3
^^^^^^
The third part of the `synth_ice40` flow is a series of commands for mapping to
DSPs. By default, the iCE40 flow will not map to the hardware DSP blocks and
will only be performed if called with the ``-dsp`` flag: :yoscrypt:`synth_ice40
-dsp`. While our example has nothing that could be mapped to DSPs we can still
take a quick look at the commands here and describe what they do.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-at: t:$mul
:end-before: alumacc
:dedent:
:caption: ``coarse`` section (part 3)
:name: synth_coarse3
:yoscrypt:`wreduce t:$mul` performs width reduction again, this time targetting
only cells of type `$mul`. :yoscrypt:`techmap -map +/mul2dsp.v -map
+/ice40/dsp_map.v ... -D DSP_NAME=$__MUL16X16` uses `techmap` to map `$mul`
cells to ``$__MUL16X16`` which are, in turn, mapped to the iCE40 ``SB_MAC16``.
Any multipliers which aren't compatible with conversion to ``$__MUL16X16`` are
relabelled to ``$__soft_mul`` before `chtype` changes them back to `$mul`.
During the mul2dsp conversion, some of the intermediate signals are marked with
the attribute ``mul2dsp``. By calling :yoscrypt:`select a:mul2dsp` we restrict
the following commands to only operate on the cells and wires used for these
signals. `setattr` removes the now unnecessary ``mul2dsp`` attribute.
`opt_expr` we've already come across for const folding and simple expression
rewriting, the ``-fine`` option just enables more fine-grain optimizations.
Then we perform width reduction a final time and clear the selection.
.. todo:: ``ice40_dsp`` is pmgen
Finally we have `ice40_dsp`: similar to the `memory_dff` command we saw in the
previous section, this merges any surrounding registers into the ``SB_MAC16``
cell. This includes not just the input/output registers, but also pipeline
registers and even a post-adder where applicable: turning a multiply + add into
a single multiply-accumulate.
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/synthesis/techmap_synth`
Part 4
^^^^^^
That brings us to the fourth and final part for the iCE40 synthesis flow:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-at: alumacc
:end-before: map_ram:
:dedent:
:caption: ``coarse`` section (part 4)
:name: synth_coarse4
Where before each type of arithmetic operation had its own cell, e.g. `$add`, we
now want to extract these into `$alu` and `$macc` cells which can help identify
opportunities for reusing logic. We do this by running `alumacc`, which we can
see produce the following changes in our example design:
.. literalinclude:: /code_examples/fifo/fifo.out
:language: doscon
:start-at: yosys> alumacc
:end-before: yosys> select
:caption: output of `alumacc`
.. figure:: /_images/code_examples/fifo/rdata_alumacc.*
:class: width-helper
:name: rdata_alumacc
``rdata`` output after `alumacc`
Once these cells have been inserted, the call to `opt` can combine cells which
are now identical but may have been missed due to e.g. the difference between
`$add` and `$sub`.
The other new command in this part is :doc:`/cmd/memory`. `memory` is another
macro command which we examine in more detail in
:doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on
the step most relevant to our example: `memory_collect`. Up until this point,
our memory reads and our memory writes have been totally disjoint cells;
operating on the same memory only in the abstract. `memory_collect` combines all
of the reads and writes for a memory block into a single cell.
.. figure:: /_images/code_examples/fifo/rdata_coarse.*
:class: width-helper
:name: rdata_coarse
``rdata`` output after `memory_collect`
Looking at the schematic after running `memory_collect` we see that our
`$memrd_v2` cell has been replaced with a `$mem_v2` cell named ``data``, the
same name that we used in :ref:`fifo-v`. Where before we had a single set of
signals for address and enable, we now have one set for reading (``RD_*``) and
one for writing (``WR_*``), as well as both ``WR_DATA`` input and ``RD_DATA``
output.
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/opt`
- :doc:`/using_yosys/synthesis/memory`
Final note
^^^^^^^^^^
Having now reached the end of the the coarse-grain representation, we could also
have gotten here by running :yoscrypt:`synth_ice40 -top fifo -run :map_ram`
after loading the design. The :yoscrypt:`-run <from_label>:<to_label>` option
with an empty ``<from_label>`` starts from the :ref:`synth_begin`, while the
``<to_label>`` runs up to but including the :ref:`map_ram`.
Hardware mapping
~~~~~~~~~~~~~~~~
The remaining sections each map a different type of hardware and are much more
architecture dependent than the previous sections. As such we will only be
looking at each section very briefly.
If you skipped calling :yoscrypt:`read_verilog -D ICE40_HX -lib -specify
+/ice40/cells_sim.v` earlier, do it now.
Memory blocks
^^^^^^^^^^^^^
Mapping to hard memory blocks uses a combination of `memory_libmap` and
`techmap`.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_ram:
:end-before: map_ffram:
:dedent:
:name: map_ram
:caption: ``map_ram`` section
.. figure:: /_images/code_examples/fifo/rdata_map_ram.*
:class: width-helper
:name: rdata_map_ram
``rdata`` output after :ref:`map_ram`
The :ref:`map_ram` converts the generic `$mem_v2` into the iCE40 ``SB_RAM40_4K``
(highlighted). We can also see the memory address has been remapped, and the
data bits have been reordered (or swizzled). There is also now a `$mux` cell
controlling the value of ``rdata``. In :ref:`fifo-v` we wrote our memory as
read-before-write, however the ``SB_RAM40_4K`` has undefined behaviour when
reading from and writing to the same address in the same cycle. As a result,
extra logic is added so that the generated circuit matches the behaviour of the
verilog. :ref:`no_rw_check` describes how we could change our verilog to match
our hardware instead.
If we run `memory_libmap` under the `debug` command we can see candidates which
were identified for mapping, along with the costs of each and what logic
requires emulation.
.. literalinclude:: /code_examples/fifo/fifo.libmap
:language: doscon
:lines: 2, 6-
The ``$__ICE40_RAM4K_`` cell is defined in the file |techlibs/ice40/brams.txt|_,
with the mapping to ``SB_RAM40_4K`` done by `techmap` using
|techlibs/ice40/brams_map.v|_. Any leftover memory cells are then converted
into flip flops (the ``logic fallback``) with `memory_map`.
.. |techlibs/ice40/brams.txt| replace:: :file:`techlibs/ice40/brams.txt`
.. _techlibs/ice40/brams.txt: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams.txt
.. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v`
.. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams_map.v
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_ffram:
:end-before: map_gates:
:dedent:
:name: map_ffram
:caption: ``map_ffram`` section
.. figure:: /_images/code_examples/fifo/rdata_map_ffram.*
:class: width-helper
:name: rdata_map_ffram
``rdata`` output after :ref:`map_ffram`
.. note::
The visual clutter on the ``RDATA`` output port (highlighted) is an
unfortunate side effect of `opt_clean` on the swizzled data bits. In
connecting the `$mux` input port directly to ``RDATA`` to reduce the number
of wires, the ``$techmap579\data.0.0.RDATA`` wire becomes more visually
complex.
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/techmap_synth`
- :doc:`/using_yosys/synthesis/memory`
Arithmetic
^^^^^^^^^^
Uses `techmap` to map basic arithmetic logic to hardware. This sees somewhat of
an explosion in cells as multi-bit `$mux` and `$adffe` are replaced with
single-bit `$_MUX_` and `$_DFFE_PP0P_` cells, while the `$alu` is replaced with
primitive `$_OR_` and `$_NOT_` gates and a `$lut` cell.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_gates:
:end-before: map_ffs:
:dedent:
:name: map_gates
:caption: ``map_gates`` section
.. figure:: /_images/code_examples/fifo/rdata_map_gates.*
:class: width-helper
:name: rdata_map_gates
``rdata`` output after :ref:`map_gates`
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/synthesis/techmap_synth`
Flip-flops
^^^^^^^^^^
Convert FFs to the types supported in hardware with `dfflegalize`, and then use
`techmap` to map them. In our example, this converts the `$_DFFE_PP0P_` cells
to ``SB_DFFER``.
We also run `simplemap` here to convert any remaining cells which could not be
mapped to hardware into gate-level primitives. This includes optimizing
`$_MUX_` cells where one of the inputs is a constant ``1'0``, replacing it
instead with an `$_AND_` cell.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_ffs:
:end-before: map_luts:
:dedent:
:name: map_ffs
:caption: ``map_ffs`` section
.. figure:: /_images/code_examples/fifo/rdata_map_ffs.*
:class: width-helper
:name: rdata_map_ffs
``rdata`` output after :ref:`map_ffs`
.. seealso:: Advanced usage docs for
:doc:`/using_yosys/synthesis/techmap_synth`
LUTs
^^^^
`abc` and `techmap` are used to map LUTs; converting primitive cell types to use
`$lut` and ``SB_CARRY`` cells. Note that the iCE40 flow uses `abc9` rather than
`abc`. For more on what these do, and what the difference between these two
commands are, refer to :doc:`/using_yosys/synthesis/abc`.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_luts:
:end-before: map_cells:
:dedent:
:name: map_luts
:caption: ``map_luts`` section
.. figure:: /_images/code_examples/fifo/rdata_map_luts.*
:class: width-helper
:name: rdata_map_luts
``rdata`` output after :ref:`map_luts`
Finally we use `techmap` to map the generic `$lut` cells to iCE40 ``SB_LUT4``
cells.
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: map_cells:
:end-before: check:
:dedent:
:name: map_cells
:caption: ``map_cells`` section
.. figure:: /_images/code_examples/fifo/rdata_map_cells.*
:class: width-helper
:name: rdata_map_cells
``rdata`` output after :ref:`map_cells`
.. seealso:: Advanced usage docs for
- :doc:`/using_yosys/synthesis/techmap_synth`
- :doc:`/using_yosys/synthesis/abc`
Other cells
^^^^^^^^^^^
The following commands may also be used for mapping other cells:
`hilomap`
Some architectures require special driver cells for driving a constant hi or
lo value. This command replaces simple constants with instances of such
driver cells.
`iopadmap`
Top-level input/outputs must usually be implemented using special I/O-pad
cells. This command inserts such cells to the design.
These commands tend to either be in the :ref:`map_cells` or after the
:ref:`check` depending on the flow.
Final steps
~~~~~~~~~~~~
The next section of the iCE40 synth flow performs some sanity checking and final
tidy up:
.. literalinclude:: /cmd/synth_ice40.rst
:language: yoscrypt
:start-after: check:
:end-before: blif:
:dedent:
:name: check
:caption: ``check`` section
The new commands here are:
- :doc:`/cmd/autoname`,
- :doc:`/cmd/stat`, and
- :doc:`/cmd/blackbox`.
The output from `stat` is useful for checking resource utilization; providing a
list of cells used in the design and the number of each, as well as the number
of other resources used such as wires and processes. For this design, the final
call to `stat` should look something like the following:
.. literalinclude:: /code_examples/fifo/fifo.stat
:language: doscon
:start-at: yosys> stat -top fifo
Note that the :yoscrypt:`-top fifo` here is optional. `stat` will automatically
use the module with the ``top`` attribute set, which ``fifo`` was when we called
`hierarchy`. If no module is marked ``top``, then stats will be shown for each
module selected.
The `stat` output is also useful as a kind of sanity-check: Since we have
already run `proc`, we wouldn't expect there to be any processes. We also expect
``data`` to use hard memory; if instead of an ``SB_RAM40_4K`` saw a high number
of flip-flops being used we might suspect something was wrong.
If we instead called `stat` immediately after :yoscrypt:`read_verilog fifo.v` we
would see something very different:
.. literalinclude:: /code_examples/fifo/fifo.stat
:language: doscon
:start-at: yosys> stat
:end-before: yosys> stat -top fifo
Notice how ``fifo`` and ``addr_gen`` are listed separately, and the statistics
for ``fifo`` show 2 ``addr_gen`` modules. Because this is before the memory has
been mapped, we also see that there is 1 memory with 2048 memory bits; matching
our 8-bit wide ``data`` memory with 256 values (:math:`8*256=2048`).
Synthesis output
^^^^^^^^^^^^^^^^
The iCE40 synthesis flow has the following output modes available:
- :doc:`/cmd/write_blif`,
- :doc:`/cmd/write_edif`, and
- :doc:`/cmd/write_json`.
As an example, if we called :yoscrypt:`synth_ice40 -top fifo -json fifo.json`,
our synthesized ``fifo`` design will be output as :file:`fifo.json`. We can
then read the design back into Yosys with `read_json`, but make sure you use
:yoscrypt:`design -reset` or open a new interactive terminal first. The JSON
output we get can also be loaded into `nextpnr`_ to do place and route; but that
is beyond the scope of this documentation.
.. _nextpnr: https://github.com/YosysHQ/nextpnr
.. seealso:: :doc:`/cmd/synth_ice40`