From 119e2d1731923bbbaff7cb1db61ffefde4dd7284 Mon Sep 17 00:00:00 2001 From: Oliver Gurka Date: Mon, 14 Oct 2024 17:21:37 +0200 Subject: [PATCH 1/3] CI: [STYLE] Add correct indentations --- .gitlab-ci.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6c9c152a7..7f7628b7c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,23 +7,23 @@ variables: GIT_SUBMODULE_STRATEGY: recursive include: - - local: 'tests/ci/check.gitlab-ci.yaml' + - local: "tests/ci/check.gitlab-ci.yaml" pages: stage: deploy image: python:3.9-alpine script: - - apk add git - - pip install -U GitPython - - pip install -U sphinx - - pip install -U sphinx-rtd-theme - - pip install -U sphinx-vhdl - - echo "Hotfix detached head, causing trouble in GitPython:" - - git checkout $CI_COMMIT_BRANCH - - git status - - sphinx-build -b html doc/source/ public + - apk add git + - pip install -U GitPython + - pip install -U sphinx + - pip install -U sphinx-rtd-theme + - pip install -U sphinx-vhdl + - echo "Hotfix detached head, causing trouble in GitPython:" + - git checkout $CI_COMMIT_BRANCH + - git status + - sphinx-build -b html doc/source/ public artifacts: paths: - - public + - public only: - - devel + - devel From ddccb0e45b6f1b9b154fe5ecd56db9d666d9d40c Mon Sep 17 00:00:00 2001 From: Oliver Gurka Date: Fri, 11 Oct 2024 18:42:53 +0200 Subject: [PATCH 2/3] DOC: [FEATURE] Improve documentation generally - use piccolo theme - add ndk fpga logo - improve deprecation warnings - improve welcome page to make user more interested --- .github/workflows/doc.yml | 2 +- .gitlab-ci.yml | 2 +- README.md | 2 +- doc/source/_templates/layout.html | 115 +++++++++++++++++++++++++ doc/source/base.rst | 3 +- doc/source/conf.py | 23 +++-- doc/source/fifo.rst | 36 ++++++-- doc/source/img/ndk_fpga_logo.png | Bin 0 -> 36230 bytes doc/source/index.rst | 136 +++++++++++++++++++++--------- doc/source/memory.rst | 37 ++++++-- doc/source/mfb.rst | 1 - doc/source/mi.rst | 1 - doc/source/mvb.rst | 5 +- 13 files changed, 288 insertions(+), 75 deletions(-) create mode 100644 doc/source/_templates/layout.html create mode 100644 doc/source/img/ndk_fpga_logo.png diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index c847603b1..4ec53bfb1 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -16,7 +16,7 @@ jobs: - name: Setup and build run: | sudo apt-get install python3-pip - pip3 install sphinx sphinx-vhdl sphinx_rtd_theme GitPython + pip3 install sphinx sphinx-vhdl sphinx_rtd_theme piccolo_theme GitPython mkdir public mkdir public/html cp doc/source/index.html public/html/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f7628b7c..3e1bed62b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ pages: - apk add git - pip install -U GitPython - pip install -U sphinx - - pip install -U sphinx-rtd-theme + - pip install -U piccolo_theme - pip install -U sphinx-vhdl - echo "Hotfix detached head, causing trouble in GitPython:" - git checkout $CI_COMMIT_BRANCH diff --git a/README.md b/README.md index 8079c34cf..55327c80c 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ First, you need to install a few Python packages: $ pip3 install --user GitPython $ pip3 install --user sphinx $ pip3 install --user sphinx-vhdl -$ pip3 install --user sphinx_rtd_theme +$ pip3 install --user piccolo_theme ``` Then the documentation is generated simply by issuing these two commands: diff --git a/doc/source/_templates/layout.html b/doc/source/_templates/layout.html new file mode 100644 index 000000000..f318c80fc --- /dev/null +++ b/doc/source/_templates/layout.html @@ -0,0 +1,115 @@ +{% extends "basic/layout.html" %} {%- block body_tag %} + + {% endblock %} {%- block header %} + +
+ {% if theme_banner_text and theme_banner_text != 'false' %} {% include + "./components/notification_banner.html" %} {% endif %} + + +
+ {% endblock %} {%- block sidebar1 %} {{ sidebar() }} {%- block sidebarlogo + %}{% endblock %} {% endblock %} {%- block sidebar2 %} {% if display_toc %} +
+

+ <Page contents +

+
+ + + {% endif %} {% endblock %} {%- block relbar1 %}{% endblock %} {%- block + scripts %} {{- script() }} + + + {%- endblock %} {%- block relbar2 %} +
+
+
+ {% if prev %} + + <{{ prev.title }} + {% endif %} +
+ +
+ {% if next %} + {{ next.title }}> + {% endif %} +
+
+
+ {% endblock %} {%- block footer %} {{ super() }} +
+ {{ version }} +
+ +

+ {% if theme_show_theme_credit %}Styled using the + Piccolo Theme{% endif %} +

+ {%- endblock %} + diff --git a/doc/source/base.rst b/doc/source/base.rst index 17a3b4dcb..15f6b19af 100644 --- a/doc/source/base.rst +++ b/doc/source/base.rst @@ -2,7 +2,7 @@ Basic Tools =========== This chapter describes the basic components such as FIFOs, RAMs, multiplexers, encoders, decoders, etc. -The basic components are typically located in the ``comp/base/`` directory in the OFM repository. +The basic components are typically located in the ``comp/base/`` directory in the NDK-FPGA repository. .. toctree:: :maxdepth: 1 @@ -12,6 +12,5 @@ The basic components are typically located in the ``comp/base/`` directory in th memory fifo dsp - shift logic misc diff --git a/doc/source/conf.py b/doc/source/conf.py index 3f39d97a9..c36b59868 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -18,10 +18,10 @@ # -- Project information ----------------------------------------------------- -project = 'NDK-FPGA Docs' +project = 'NDK-FPGA' copyright = str(current_year) + ', CESNET z.s.p.o.' author = 'CESNET TMC' -version = 'Git branch: ' + str(git_branch) + ',
Git hash: ' + str(git_sha_short) +version = 'Git branch: ' + str(git_branch) + '
Git hash: ' + str(git_sha_short) # -- General configuration --------------------------------------------------- @@ -30,8 +30,9 @@ # ones. extensions = [ "ndk-fpga", - "sphinx_rtd_theme", - "sphinxvhdl.vhdl" + "piccolo_theme", + "sphinxvhdl.vhdl", + "sphinx.ext.autosectionlabel" ] vhdl_autodoc_source_path = (Path(__file__).parent.parent.parent).resolve() @@ -50,18 +51,16 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = 'piccolo_theme' html_theme_options = { - 'collapse_navigation': True, - 'sticky_navigation': True, - 'navigation_depth': 4, - 'includehidden': True, - 'display_version': True, + "globaltoc_maxdepth": 4, + "banner_hiding": "permanent", + "show_theme_credit": False, } +html_logo = "img/ndk_fpga_logo_simple.png" + # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] - -html_style = 'css/theme_overrides.css' diff --git a/doc/source/fifo.rst b/doc/source/fifo.rst index 549fc5b86..8fab2e755 100644 --- a/doc/source/fifo.rst +++ b/doc/source/fifo.rst @@ -6,9 +6,17 @@ FIFO components Dual clock (asynchronous) FIFOs ------------------------------- -**ASFIFO** - Behavioral dual clock FIFO implementation based on LUTMEMs and optimized for Xilinx only. Include status signal. ``OBSOLETE, use ASFIFOX!`` +**ASFIFO** - Behavioral dual clock FIFO implementation, based on LUTMEMs and optimized for Xilinx only. Includes status signal. -**ASFIFO_BRAM** - Behavioral dual clock FIFO implementation based on BRAMs and optimized for Xilinx only. Include status signal. ``OBSOLETE, use ASFIFOX!`` +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **ASFIFOX** instead. + +**ASFIFO_BRAM** - Behavioral dual clock FIFO implementation, based on BRAMs and optimized for Xilinx only. Includes status signal. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **ASFIFOX** instead. **ASFIFO_BRAM_BLOCK** - Similar to ASFIFO_BRAM but with extra signal to mark end of input data block, output remains in empty state until such mark is received. Located in the same folder as ASFIFO_BRAM. @@ -34,13 +42,25 @@ Detailed :ref:`documentation can be found here`. Single clock FIFOs ------------------ -**FIFO** - Behavioral FIFO implementation based on LUTMEMs and optimized for Xilinx only. Include status signal. ``OBSOLETE, use FIFOX!`` +**FIFO** - Behavioral FIFO implementation, based on LUTMEMs and optimized for Xilinx only. Includes status signal. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **FIFOX** instead. -**FIFO_BRAM** - Behavioral FIFO implementation based on BRAMs and optimized for Xilinx only. Include status signal. ``OBSOLETE, use FIFOX!`` +**FIFO_BRAM** - Behavioral FIFO implementation, based on BRAMs and optimized for Xilinx only. Includes status signal. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **FIFOX** instead. **FIFO_BRAM_XILINX** - Structural implementation of FIFO based on Xilinx specific BRAM FIFO primitives (no extra logic). Include almost full and almost empty signal. -**FIFO_N1** - Behavioral implementation of FIFO with multiple write ports, it based on LUTMEMs and optimized for Xilinx only. ``OBSOLETE, use FIFOX_MULTI!`` +**FIFO_N1** - Behavioral implementation of FIFO with multiple write ports, it is based on LUTMEMs and optimized for Xilinx only. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **FIFOX** instead. **FIFOX** - Universal FIFO for Xilinx and Intel FPGAs. It support various memory implementation: LUTMEMs, BRAMs, URAMs (Xilinx only) and shift-registers in LUT slices (effective on Xilinx only). Include almost full, almost empty and status signal. Possible automatic selection of a suitable memory implementation. Detailed :ref:`documentation can be found here`. @@ -50,7 +70,11 @@ Include almost full, almost empty and status signal. Possible automatic selectio **MULTI_FIFO** - Behavioral implementation of FIFO for Xilinx and Intel FPGAs with multiple independent channels. It support various memory implementation: LUTMEMs, BRAMs, URAMs (Xilinx only). The memory type is selected automatically. -**SH_FIFO** - Behavioral FIFO implementation based on shift-registers in LUT slices and optimized for Xilinx only. ``OBSOLETE, use FIFOX!`` +**SH_FIFO** - Behavioral FIFO implementation, based on shift-registers in LUT slices and optimized for Xilinx only. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **FIFOX** instead. .. toctree:: :maxdepth: 1 diff --git a/doc/source/img/ndk_fpga_logo.png b/doc/source/img/ndk_fpga_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a2838018cd23a6d00748506bf0218b16360b4f2c GIT binary patch literal 36230 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, then it forwards the network packets to and from the computer memory. - -.. warning:: - - The DMA Medusa IP is not part of the open-source NDK. `You can get the NDK, including the DMA Medusa IP and professional support, through our partner BrnoLogic. `_ - -.. toctree:: - :maxdepth: 2 - :caption: Application: - :hidden: - - app-minimal +Overview +======== .. toctree:: :maxdepth: 2 @@ -36,10 +20,80 @@ The NDK-based Minimal application is a simple example of how to build an FPGA ap ndk_core/doc/devtree ndk_core/doc/faq +The **Network Development Kit (NDK) for FPGAs** is a comprehensive framework designed +for the rapid and efficient development of FPGA-accelerated network applications. Optimized +for scalability and high throughput, the NDK supports speeds up to **400 Gigabit Ethernet**. + +-------- + +The NDK provides a minimal example application. The **NDK Minimal Application** +demonstrates how to build an FPGA application using the NDK and serves as a starting point +for your own development. The minimal application doesn't process network packets; it simply +sends and receives them. If a DMA IP is enabled (see the :ref:`DMA Module `), +the packets are forwarded to and from the host's memory. Otherwise, DMA IP is replaced with +a loopback and packets may be forwarded from RX to TX ethernet interface. + +Other example applications will be added in the future, stay tuned! + .. toctree:: - :maxdepth: 2 - :caption: Supported cards: - :hidden: + :maxdepth: 1 + :caption: Applications + + app-minimal + +-------- + +In addition, the NDK provides a collection of reusable components, some of which are vendor and +vendor- and tool-independent, while others are optimized for specific FPGA vendors and architectures. +For transferring packets (frames) and auxiliary data at such high rates, the NDK uses its own set of what are called +"multibuses" that can transfer multiple frames and values, respectively, within a single clock cycle. +For details on these protocols, see the specifications for the :ref:`Multi Value Bus` and +:ref:`Multi Frame Bus`. + +To simplify module development, the NDK includes components for common operations on these buses, +multiplexers, FIFOs, BRAMs and lookup tables. To improve compatibility with other popular buses, +it also provides converters: + +* MFB to AXI stream, +* MFB to Avalon stream, +* MI to Avalon MM, +* MI to AXI4, +* MVB to MFB. + +.. toctree:: + :maxdepth: 1 + :caption: Reusable Modules Library + + base + ctrls + mi + mfb + mvb + nic + pcie + debug + ver + +-------- + +.. toctree :: + :maxdepth: 1 + :caption: Bus Specifications + + comp/mi_tools/readme + comp/mvb_tools/readme + comp/mfb_tools/readme + +-------- + +The NDK supports a wide range of FPGA cards, providing access to features such as DDR and HBM +memories, PCIe, and Ethernet in your applications. However, different applications may only +support a subset of these cards. A complete list of supported FPGA cards can be found below +(minimal app supports all of them). + +.. toctree:: + :caption: Supported Cards + :maxdepth: 1 ndk_cards/reflexces/agi-fh400g/readme ndk_cards/intel/dk-dev-1sdx-p/readme @@ -51,20 +105,26 @@ The NDK-based Minimal application is a simple example of how to build an FPGA ap ndk_cards/amd/alveo-u200/readme ndk_cards/amd/alveo-u55c/readme ndk_cards/amd/vcu118/readme - ndk_extra/nfb-200g2ql/readme + extra/nfb-200g2ql/readme ndk_cards/prodesign/pd-falcon/readme -.. toctree:: - :maxdepth: 2 - :caption: VHDL components: - :hidden: +-------- - base - ctrls - mi - mfb - mvb - nic - pcie - debug - ver +NDK provides two implementations of DMA IPs: + +* DMA Medusa +* DMA Calypte + +DMA Medusa is a state-of-the-art DMA module that supports up to 400Gbps of throughput to +host memory. DMA Calypte is an open-source low-latency DMA supporting throughput up +to tens of Gigabits per second. However, the DMA Calypte is still under development +and is not yet officially released (stay tuned). + +.. warning:: + + The DMA Medusa IP is not included in the open-source version of the NDK. `You can obtain the full NDK package, including DMA Medusa IP and professional support, from our partner BrnoLogic. `_ + + +.. image:: img/liberouter_logo.svg + :align: center + :width: 50 % diff --git a/doc/source/memory.rst b/doc/source/memory.rst index 4788b1d4d..14e253585 100644 --- a/doc/source/memory.rst +++ b/doc/source/memory.rst @@ -3,9 +3,17 @@ Memory modules **CAM** - Ternary content addressable memory implemented in memory LUTs, optimized for Xilinx only. Also there is **light variant** implemented using register array, simpler but less effective. -**DP_BMEM** - Behavioral implementation of dual clock BRAM memory with two read/write port. ``OBSOLETE, use DP_BRAM or DP_BRAM_XILINX!`` +**DP_BMEM** - Behavioral implementation of dual clock BRAM memory with two read/write ports. -**DP_BMEM_V7** - Structural implementation of dual clock BRAM memory based on Virtex 7 specific primitives with two read/write ports. ``OBSOLETE, use DP_BRAM or DP_BRAM_XILINX!`` +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **DP_BRAM** or **DP_BRAM_XILINX** instead. + +**DP_BMEM_V7** - Structural implementation of dual clock BRAM memory based on Virtex 7 specific primitives with two read/write ports. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **DP_BRAM** or **DP_BRAM_XILINX** instead. **DP_BRAM** - Behavioral implementation of single clock BRAM memory with two read/write port. Optimized for Xilinx and Intel FPGAs. @@ -25,21 +33,28 @@ The read latency is 0 clock cycles. Optimized for same FPGAs as GEN_LUTRAM. **NP_LUTRAM_PRO** - An alternative version of NP_LUTRAM, which uses additional multiple frequency clock signal. Ports are registered and the read latency is 2 clock cycles. Expert knowledge is required to use this component! -**SDP_BMEM** - Behavioral implementation of dual clock BRAM memory with one read port and one write port. Located in the same folder as DP_BMEM. ``OBSOLETE, use DP_BRAM or DP_BRAM_XILINX!`` +**SDP_BMEM** - Behavioral implementation of dual clock BRAM memory with one read port and one write port. Located in the same folder as DP_BMEM. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **DP_BRAM** or **DP_BRAM_XILINX** instead. **SDP_BMEM_V7** - Structural implementation of dual clock BRAM memory based on Virtex 7 specific primitives with one read port and one write port. -Located in the same folder as DP_BMEM_V7. ``OBSOLETE, use SDP_BRAM or SDP_BRAM_XILINX!`` +Located in the same folder as DP_BMEM_V7. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **DP_BRAM** or **DP_BRAM_XILINX** instead. **SDP_BRAM** - Structural implementation of dual clock BRAM memory based on Xilinx and Intel specific primitives (xpm_memory_sdpram, altera_syncram) with one read port and one write port. It supports the byte enable feature! -**MP_BRAM** - Generic multiported single clock BRAM memory based on **SDP_BRAM**. Currently supports only 1 write port. Amount of read ports is not restricted. Also supports byte enable -feature. +**MP_BRAM** - Generic multi-port single-clock BRAM memory based on **SDP_BRAM**. -**LVT_MEM** - Multiported memory implemented suitable for shallow memories, supports generic amount of write/read ports and has customizable read during write behaviour. +**LVT_MEM** - Multi-port memory is suitable for shallow memories, supports a generic amount of write/read ports, and has customizable read during write behavior. **SDP_BRAM_BEHAV** - Another behavioral implementation of dual clock BRAM memory with one read port and one write port. -Located in the same folder as SDP_BRAM. ``OBSOLETE, use DP_BRAM or DP_BRAM_XILINX!`` +Located in the same folder as SDP_BRAM. **SDP_BRAM_XILINX** - Structural implementation of dual clock BRAM memory based on Xilinx specific primitives with one read port and one write port. Only for Xilinx FPGAs. @@ -48,7 +63,11 @@ Allows setting type of memory (LUT, BRAM, URAM) or automatic mode. Optimized for **SDP_URAM_XILINX** - Structural implementation of single clock URAM memory based on Xilinx specific primitives with one read port and one write port. Only for Xilinx UltraScale+ FPGAs. -**SP_BMEM** - Old behavioral implementation of single clock BRAM memory with one read/write port. ``OBSOLETE, use SP_BRAM or SP_BRAM_XILINX!`` +**SP_BMEM** - Old behavioral implementation of a single-clock BRAM memory with one read/write port. + +.. warning:: + .. deprecated:: 0.7.0 + This component is obsolete and is a candidate for removal, use **SP_BRAM** or **SP_BRAM_XILINX** instead. **SP_BRAM** - Behavioral implementation of single clock BRAM memory with one read/write port. Optimized for Xilinx and Intel FPGAs. diff --git a/doc/source/mfb.rst b/doc/source/mfb.rst index e432cfc3a..b9cee458f 100644 --- a/doc/source/mfb.rst +++ b/doc/source/mfb.rst @@ -9,7 +9,6 @@ Components using the MFB bus are typically located in the ``comp/mfb_tools/`` di :maxdepth: 1 :caption: Content: - comp/mfb_tools/readme comp/mfb_tools/flow/reconfigurator/readme comp/mfb_tools/flow/frame_packer/readme comp/mfb_tools/flow/frame_unpacker/readme diff --git a/doc/source/mi.rst b/doc/source/mi.rst index 26eb15ee5..01d1d10c8 100644 --- a/doc/source/mi.rst +++ b/doc/source/mi.rst @@ -8,7 +8,6 @@ Components using the MI bus are typically located in the ``comp/mi_tools/`` dire :maxdepth: 1 :caption: Content: - comp/mi_tools/readme comp/mi_tools/async/readme comp/mi_tools/pipe/readme comp/mi_tools/indirect_access/readme diff --git a/doc/source/mvb.rst b/doc/source/mvb.rst index a623f3c58..facb915ab 100644 --- a/doc/source/mvb.rst +++ b/doc/source/mvb.rst @@ -1,5 +1,5 @@ -MVB Tools -========= +MVB Components +============== This chapter contains the specifications of the MVB bus and a description of the components that use MVB bus. The MVB bus was developed to support multiple items/values in one clock cycle. @@ -9,7 +9,6 @@ Components using the MFB bus are typically located in the ``comp/mvb_tools/`` di :maxdepth: 1 :caption: Content: - comp/mvb_tools/readme comp/mvb_tools/flow/channel_router/readme comp/mvb_tools/flow/discard/readme comp/mvb_tools/flow/item_collision_resolver/readme From 83b34be265436fd3380d545ed248af94a6e73d30 Mon Sep 17 00:00:00 2001 From: Oliver Gurka Date: Fri, 11 Oct 2024 18:44:16 +0200 Subject: [PATCH 3/3] CHANGELOG: Add docs improvements --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abc735b88..e7d1b5a1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Improve documentation +- Improve documentation looks ## [0.7.2] - 2024-10-17