From c7d255de6fd78d631ed46189dda48fdec8a9e261 Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Tue, 15 Oct 2024 09:27:22 +0200 Subject: [PATCH] xbflash.qspi: Add tool from Xilinx to flash cards (it is a nightmare to... --- DEPLOYMENT.md | 72 +- fpga/host_library/CMakeLists.txt | 5 +- gitlab_upload_release.sh | 18 +- tools/CMakeLists.txt | 2 + tools/xbflash.qspi/CMakeLists.txt | 16 + tools/xbflash.qspi/LICENSE | 517 ++++++ tools/xbflash.qspi/README.md | 16 + tools/xbflash.qspi/firmware_image.cpp | 43 + tools/xbflash.qspi/firmware_image.h | 37 + tools/xbflash.qspi/main.cpp | 565 +++++++ tools/xbflash.qspi/pcidev.cpp | 228 +++ tools/xbflash.qspi/pcidev.h | 58 + tools/xbflash.qspi/xqspips.cpp | 1610 ++++++++++++++++++ tools/xbflash.qspi/xqspips.h | 110 ++ tools/xbflash.qspi/xspi.cpp | 2241 +++++++++++++++++++++++++ tools/xbflash.qspi/xspi.h | 81 + 16 files changed, 5570 insertions(+), 49 deletions(-) create mode 100644 tools/xbflash.qspi/CMakeLists.txt create mode 100644 tools/xbflash.qspi/LICENSE create mode 100644 tools/xbflash.qspi/README.md create mode 100644 tools/xbflash.qspi/firmware_image.cpp create mode 100644 tools/xbflash.qspi/firmware_image.h create mode 100644 tools/xbflash.qspi/main.cpp create mode 100644 tools/xbflash.qspi/pcidev.cpp create mode 100644 tools/xbflash.qspi/pcidev.h create mode 100644 tools/xbflash.qspi/xqspips.cpp create mode 100644 tools/xbflash.qspi/xqspips.h create mode 100644 tools/xbflash.qspi/xspi.cpp create mode 100644 tools/xbflash.qspi/xspi.h diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index e2720ed5..3c2c9506 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -2,8 +2,8 @@ To deploy Jungfraujoch, one needs to follow four steps: -1. Flash the U55C FPGA card with a proper image and install Linux kernel driver -2. Install main Jungfraujoch code and frontend web interface +1. Install main Jungfraujoch code and frontend web interface +2. Flash the U55C FPGA card with a proper image and install Linux kernel driver 3. Install Jungfraujoch writer 4. Install Python OpenAPI client @@ -11,21 +11,38 @@ Installation procedure depend a lot on the operating system. For RedHat Enterpri installation can be done with prebuilt RPMs and is relatively straightforward. For other systems one needs to build software from source. Both ways will be presented. +## Install main Jungfraujoch code and frontend web interface + +On RHEL 8 systems there is a `jfjoch--1.el8.x86_64.rpm` that needs to be installed and contains all the necessary software and web interface. + +On other OSes one needs to compile Jungfraujoch from source (from the repo directory): +``` +$ mkdir build +$ cd build +$ cmake .. -DCMAKE_INSTALL_PREFIX= +$ make +$ sudo make install +``` +For manual installation, we recommend to use non-standard directory (like `/opt/jfjoch`), to facilitate upgrades and removal. + +Frontend web user interface has to be built separately with: +``` +$ cd build +$ make frontend +``` +Frontend files (.html and .js) will be placed in `frontend/dist` (outside of `build/` directory!) and has to be copied to a general location, e.g. `/usr/local/jfjoch/frontend` or `/opt/jfjoch/frotend`. + ## Flash the U55C FPGA card with a proper image and install Linux kernel driver. - ### Firmware flashing -1. Install the `xbflash2` tool from Xilinx, which allows to flash the firmware through PCIe connector. - -It can be downloaded as RPM/DEB file from [Alveo product page](https://www.xilinx.com/products/boards-and-kits/alveo/u55c.html#xbflash2). For RHEL9 this needs to built from source - [Xilinx/XRT github repository](https://github.com/Xilinx/XRT). - -2. Check that the card is detected by OS with "lspci |grep Xilinx" and check the PCIe slot number (`11:00.0` in this case): +1. Check that the card is detected by OS with "lspci |grep Xilinx" and check the PCIe bus/device/function (BDF) number, `11:00.0` in this case: ``` $ lspci |grep Xilinx -23:00.0 Processing accelerators: Xilinx Corporation Device 3450 (rev 58) +23:00.0 Processing accelerators: Xilinx Corporation Device 3450 (rev 2) ``` -Note the device number `3450` that identifies Jungfraujoch device (Jungfraujoch pass is 3450 m above sea level) and `rev 58` identifying release of the firmware. -3. Check the speed of the card, that it is detected as PCIe Gen4x8 device (needs to be done as root, otherwise configuration details are not given): +Note the device number `3450` that identifies Jungfraujoch device (Jungfraujoch pass is 3450 m above sea level) and `rev 2` identifying release of the firmware. + +2. Check the speed of the card, that it is detected as PCIe Gen4x8 device (needs to be done as root, otherwise configuration details are not given): ``` $ sudo lspci -vv -s 23:00.0 Processing accelerators: Xilinx Corporation Device 3450 @@ -34,20 +51,20 @@ LnkSta: Speed 16GT/s (ok), Width x8 (ok) (...) ``` -4. Download the MCS image from release files or build it using Vivado (WARNING! building time can be about 8 hours and doesn't allways reach correct timing). -5. Flash the card with xbflash2. For fresh card use: +3. Download the MCS image from release files or build it using Vivado (WARNING! building time can be about 8 hours and doesn't allways reach correct timing). +4. Flash the card with `xbflash.qspi` tool (part of Jungfraujoch). For fresh card use: ``` -sudo xbflash2 program --spi --image --bar-offset 0x1f06000 -d +sudo xbflash.qspi --primary --card --bar-offset 0x1f06000 ``` For card that was already flashed with Jungfraujoch images: ``` -sudo xbflash2 program --spi --image -d +sudo xbflash.qspi --primary --card ``` -It is necessary to confirm the operation by pressing `Y` key. +It is necessary to confirm the operation by pressing `Y` key or one can add `--force` option to avoid confirmation. It is safe to run multiple flashing processes in parallel for different cards, for example in separate screen sessions. -6. Cold reboot: +5. Cold reboot: ``` sudo ipmitool chassis power cycle ``` @@ -82,27 +99,6 @@ $ sudo dracut -f Configure switch according to [FPGA network guide](fpga/NETWORK.md) - specifically set manual speed and turn off auto-negotiation for the port used to connect U55C card and connect card to switch. -## Install main Jungfraujoch code and frontend web interface - -On RHEL 8 systems there is a `jfjoch--1.el8.x86_64.rpm` that needs to be installed and contains all the necessary software and web interface. - -On other OSes one needs to compile Jungfraujoch from source (from the repo directory): -``` -$ mkdir build -$ cd build -$ cmake .. -DCMAKE_INSTALL_PREFIX= -$ make -$ sudo make install -``` -For manual installation, we recommend to use non-standard directory (like `/opt/jfjoch`), to facilitate upgrades and removal. - -Frontend web user interface has to be built separately with: -``` -$ cd build -$ make frontend -``` -Frontend files (.html and .js) will be placed in `frontend/dist` (outside of `build/` directory!) and has to be copied to a general location, e.g. `/usr/local/jfjoch/frontend` or `/opt/jfjoch/frotend`. - ### Running Jungfraujoch software Main Jungfraujoch service is called `jfjoch_broker`. It is responsible for handling data from FPGAs, doing processing, analysis, compression and sending images on ZeroMQ output. It is recommended to run the service as `systemd` service. diff --git a/fpga/host_library/CMakeLists.txt b/fpga/host_library/CMakeLists.txt index 4ed71b41..3b8f9397 100644 --- a/fpga/host_library/CMakeLists.txt +++ b/fpga/host_library/CMakeLists.txt @@ -8,11 +8,12 @@ INSTALL(TARGETS jfjoch_pcie_status RUNTIME COMPONENT jfjoch) ADD_EXECUTABLE(jfjoch_pcie_net_cfg jfjoch_pcie_net_cfg.cpp) TARGET_LINK_LIBRARIES(jfjoch_pcie_net_cfg JFJochDevice ) -INSTALL(TARGETS jfjoch_pcie_net_cfg RUNTIME) +INSTALL(TARGETS jfjoch_pcie_net_cfg RUNTIME COMPONENT jfjoch ) ADD_EXECUTABLE(jfjoch_pcie_clear_net_counters jfjoch_pcie_clear_net_counters.cpp) TARGET_LINK_LIBRARIES(jfjoch_pcie_clear_net_counters JFJochDevice ) -INSTALL(TARGETS jfjoch_pcie_clear_net_counters RUNTIME) +INSTALL(TARGETS jfjoch_pcie_clear_net_counters RUNTIME COMPONENT jfjoch ) +# Clock cfg test is not too important to be installed as part of Jungfraujoch ADD_EXECUTABLE(jfjoch_pcie_clock_cfg_test jfjoch_pcie_clock_cfg_test.cpp) TARGET_LINK_LIBRARIES(jfjoch_pcie_clock_cfg_test JFJochDevice ) diff --git a/gitlab_upload_release.sh b/gitlab_upload_release.sh index a204fb5a..2c8c709f 100644 --- a/gitlab_upload_release.sh +++ b/gitlab_upload_release.sh @@ -5,7 +5,7 @@ # export PACKAGE_VERSION_SEM=`head -n1 VERSION` -export FPGA_VERSION="1.0.0-rc.14" +export FPGA_VERSION="1.0.0-rc.16" export PACKAGE_VERSION=${PACKAGE_VERSION_SEM//-/_} export PACKAGE_VERSION_PYTHON=${PACKAGE_VERSION_SEM//-rc./rc} export PACKAGE_VERSION_PYTHON=${PACKAGE_VERSION_PYTHON//-alpha./a} @@ -23,12 +23,12 @@ curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_frontend.tar.gz "$ curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-client-${PACKAGE_VERSION_PYTHON}.tar.gz "${PACKAGE_REGISTRY_URL}/jfjoch-client-${PACKAGE_VERSION_PYTHON}.tar.gz" curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl "${PACKAGE_REGISTRY_URL}/jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl" -release-cli create --name "Release $PACKAGE_VERSION_SEM" --tag-name $PACKAGE_VERSION_SEM - --assets-link "{\"name\":\"jfjoch_frontend.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz\"}" - --assets-link "{\"name\":\"jfjoch_fpga_pcie_8x10g.mcs\",\"url\":\"${FPGA_REGISTRY_URL}/jfjoch_fpga_pcie_8x10g.mcs\"}" - --assets-link "{\"name\":\"jfjoch_fpga_pcie_100g.mcs\",\"url\":\"${FPGA_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs\"}" - --assets-link "{\"name\":\"jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}" - --assets-link "{\"name\":\"jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}" - --assets-link "{\"name\":\"jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"link_type\":\"package\"}" - --assets-link "{\"name\":\"jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl\",\"link_type\":\"package\"}" +release-cli create --name "Release $PACKAGE_VERSION_SEM" --tag-name $PACKAGE_VERSION_SEM \ + --assets-link "{\"name\":\"jfjoch_frontend.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz\"}" \ + --assets-link "{\"name\":\"jfjoch_fpga_pcie_8x10g.mcs\",\"url\":\"${FPGA_REGISTRY_URL}/jfjoch_fpga_pcie_8x10g.mcs\"}" \ + --assets-link "{\"name\":\"jfjoch_fpga_pcie_100g.mcs\",\"url\":\"${FPGA_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs\"}" \ + --assets-link "{\"name\":\"jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}" \ + --assets-link "{\"name\":\"jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}" \ + --assets-link "{\"name\":\"jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"link_type\":\"package\"}" \ + --assets-link "{\"name\":\"jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_client-${PACKAGE_VERSION_PYTHON}-py3-none-any.whl\",\"link_type\":\"package\"}" \ --assets-link "{\"name\":\"jfjoch-client-${PACKAGE_VERSION_PYTHON}.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-client-${PACKAGE_VERSION_PYTHON}.tar.gz\",\"link_type\":\"package\"}" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index f0f3ceec..10f51e41 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -24,3 +24,5 @@ ADD_EXECUTABLE(AzimIntDataset AzimIntDataset.cpp) TARGET_LINK_LIBRARIES(AzimIntDataset JFJochImageAnalysis JFJochWriter JFJochCommon) INSTALL(TARGETS jfjoch_udp_simulator HDF5DatasetWriteTest jfjoch_writer_test AzimIntDataset RUNTIME) + +ADD_SUBDIRECTORY(xbflash.qspi) diff --git a/tools/xbflash.qspi/CMakeLists.txt b/tools/xbflash.qspi/CMakeLists.txt new file mode 100644 index 00000000..58984d77 --- /dev/null +++ b/tools/xbflash.qspi/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (C) 2019-2021 Xilinx, Inc. All rights reserved. +# + +add_executable(xbflash.qspi + firmware_image.cpp + firmware_image.h + main.cpp + pcidev.cpp + pcidev.h + xqspips.cpp + xqspips.h + xspi.cpp + xspi.h) + +INSTALL(TARGETS xbflash.qspi RUNTIME COMPONENT jfjoch ) \ No newline at end of file diff --git a/tools/xbflash.qspi/LICENSE b/tools/xbflash.qspi/LICENSE new file mode 100644 index 00000000..2d57c902 --- /dev/null +++ b/tools/xbflash.qspi/LICENSE @@ -0,0 +1,517 @@ +All userspace code authored by Xilinx or Advanced Micro Devices is +released under the following license: + +Copyright (C) 2016-2022 Xilinx, Inc +Copyright (C) 2022-2023 Advanced Micro Devices, Inc + +Licensed under the Apache License, Version 2.0 (the "License"). You may +not use this file except in compliance with the License. A copy of the +License is located at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. + +All Linux kernel driver code included in "xocl" or "xclmgmt" authored by +Xilinx is released under the terms of GNU General Public License version 2: + +Copyright (C) 2016-2022 Xilinx, Inc. All rights reserved. +Copyright (C) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + +All Linux kernel driver code included in "zocl" authored by Xilinx is +released under the terms of either Apache License, Version 2.0, +or alternatively under the terms of the GNU General Public License +version 2: + +Copyright (C) 2016-2020 Xilinx, Inc. All rights reserved. +Copyright (C) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You may use the Linux kernel driver code according to either of these +licenses as is most appropriate for your project on a case-by-case basis. + +The full text for both licenses is included for reference below. + + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/tools/xbflash.qspi/README.md b/tools/xbflash.qspi/README.md new file mode 100644 index 00000000..30c7b138 --- /dev/null +++ b/tools/xbflash.qspi/README.md @@ -0,0 +1,16 @@ +# xbflash.qspi + +This program allows to flash firmware of Xilinx/AMD FPGA cards via PCIe edge and QSPI core. +This tool is taken from the Xilinx/AMD [XRT repository](https://githb.com/Xilinx/XRT) and extracted for convenience, since the original repository has +an extreme number of dependencies. + +Note: Xilinx has two tools `xbflash.qspi` and `xbflash2`. +The latter is only repackaging of the original code with extra Boost libraries for nice options parsing. +Thus, we decided not to only take the original program, to not increase number of dependencies of the code. + +## Usage +`xbflash.qspi` has to be run as root. + +``` +xbflash.qspi --primary --card +``` diff --git a/tools/xbflash.qspi/firmware_image.cpp b/tools/xbflash.qspi/firmware_image.cpp new file mode 100644 index 00000000..69e50ee0 --- /dev/null +++ b/tools/xbflash.qspi/firmware_image.cpp @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2020-2021 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include "firmware_image.h" + +firmwareImage::firmwareImage(const char *file) : + mBuf(nullptr) +{ + std::ifstream in(file, std::ios::binary | std::ios::ate); + if (!in.is_open()) + { + this->setstate(failbit); + std::cout << "Can't open " << file << std::endl; + return; + } + auto bufsize = in.tellg(); + in.seekg(0); + + // For non-dsabin file, the entire file is the image. + mBuf = new char[bufsize]; + in.seekg(0); + in.read(mBuf, bufsize); + this->rdbuf()->pubsetbuf(mBuf, bufsize); +} + +firmwareImage::~firmwareImage() +{ + delete[] mBuf; +} diff --git a/tools/xbflash.qspi/firmware_image.h b/tools/xbflash.qspi/firmware_image.h new file mode 100644 index 00000000..3058493b --- /dev/null +++ b/tools/xbflash.qspi/firmware_image.h @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2018 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/* + * Contains definitions for all firmware (DSA/BMC) related classes + */ + +#ifndef _FIRMWARE_IMAGE_H_ +#define _FIRMWARE_IMAGE_H_ + +#include +#include + +class firmwareImage : public std::istringstream +{ +public: + firmwareImage(const char *file); + ~firmwareImage(); + +private: + char *mBuf; +}; + +#endif diff --git a/tools/xbflash.qspi/main.cpp b/tools/xbflash.qspi/main.cpp new file mode 100644 index 00000000..fe6b16d6 --- /dev/null +++ b/tools/xbflash.qspi/main.cpp @@ -0,0 +1,565 @@ +/** + * Copyright (C) 2020 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +// +// This utility is implemented by porting flash code from xbmgmt. Since that +// it's used only by non-XRT users, we do not expect to maintain and enhance +// this code a lot in the future. Hence, no cleanup effort is ever attempted +// while porting the code from xbmgmt at this point. +// If it works, don't change it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcidev.h" +#include "xspi.h" +#include "xqspips.h" +#include "firmware_image.h" + +const option flash_opts[] = { + // Key option to identify flash operation, must be 0 + { "primary", required_argument, nullptr, '0' }, + + { "force", no_argument, nullptr, '1' }, + { "card", required_argument, nullptr, '2' }, + { "secondary", required_argument, nullptr, '3' }, + { "bar", required_argument, nullptr, '4' }, + { "bar-offset", required_argument, nullptr, '5' }, + { nullptr, 0, nullptr, 0 }, +}; + +const option reset_opts[] = { + // Key option to identify reset operation, must be 0 + { "factory-reset", no_argument, nullptr, '0' }, + + { "force", no_argument, nullptr, '1' }, + { "card", required_argument, nullptr, '2' }, + { "dual-flash", no_argument, nullptr, '3' }, + { "bar", required_argument, nullptr, '4' }, + { "bar-offset", required_argument, nullptr, '5' }, + { nullptr, 0, nullptr, 0 }, +}; + +const option qspips_erase_opts[] = { + // Key option to identify flash operation, must be 0 + { "qspips-erase", no_argument, nullptr, '0' }, + + { "card", required_argument, nullptr, '1' }, + { "offset", required_argument, nullptr, '2' }, + { "length", required_argument, nullptr, '3' }, + { "flash-type", required_argument, nullptr, '4' }, + { "bar", required_argument, nullptr, '5' }, + { "bar-offset", required_argument, nullptr, '6' }, + { "force", no_argument, nullptr, '7' }, + { nullptr, 0, nullptr, 0 }, +}; + +const option qspips_flash_opts[] = { + // Key option to identify flash operation, must be 0 + { "qspips-flash", no_argument, nullptr, '0' }, + + { "card", required_argument, nullptr, '1' }, + { "input", required_argument, nullptr, '2' }, + { "offset", required_argument, nullptr, '3' }, + { "flash-type", required_argument, nullptr, '4' }, + { "bar", required_argument, nullptr, '5' }, + { "bar-offset", required_argument, nullptr, '6' }, + { "force", no_argument, nullptr, '7' }, + { nullptr, 0, nullptr, 0 }, +}; + +const option qspips_readback_opts[] = { + // Key option to identify flash operation, must be 0 + { "qspips-read", no_argument, nullptr, '0' }, + + { "card", required_argument, nullptr, '1' }, + { "output", required_argument, nullptr, '2' }, + { "offset", required_argument, nullptr, '3' }, + { "length", required_argument, nullptr, '4' }, + { "flash-type", required_argument, nullptr, '5' }, + { "bar", required_argument, nullptr, '6' }, + { "bar-offset", required_argument, nullptr, '7' }, + { nullptr, 0, nullptr, 0 }, +}; + +static const char *option_key(const option *opts) +{ + while (opts->name != nullptr) { + if (opts->val == '0') + return opts->name; + opts++; + } + return nullptr; +} + +static bool is_op(const option *opts, int argc, char *argv[]) +{ + const char *key = option_key(opts); + std::string optkey = "--"; + + if (key == nullptr) + return false; + + optkey += key; + for (int i = 0; i < argc; i++) { + if (optkey.compare(argv[i]) == 0) + return true; + } + return false; +} + +static void sudoOrDie() +{ + const char* SudoMessage = "ERROR: root privileges required."; + if ((getuid() == 0) || (geteuid() == 0)) + return; + std::cout << SudoMessage << std::endl; + exit(-EPERM); +} + +static bool canProceed() +{ + std::string input; + bool answered = false; + bool proceed = false; + + while (!answered) { + std::cout << "Are you sure you wish to proceed? [y/n]: "; + std::cin >> input; + answered = (input.compare("y") == 0 || input.compare("n") == 0); + } + + proceed = (input.compare("y") == 0); + if (!proceed) + std::cout << "Action canceled." << std::endl; + return proceed; +} + +static void printHelp(const char *fname) +{ + auto tmp = std::unique_ptr{strdup(fname), std::free}; + + std::cout << "Usage: " << std::endl; + + std::cout << basename(tmp.get()) << " [options]" << std::endl; + std::cout << "\nOptions:" << std::endl; + + std::cout << "\n\"SPI flash\"\n"; + std::cout << " --primary, , must be 1st option\n"; + std::cout << " [--secondary ], default is empty\n"; + std::cout << " --card \n"; + std::cout << " [--force, yes for prompt]\n"; + std::cout << " [--bar ], default is 0\n"; + std::cout << " [--bar-offset ], default is 0x40000\n"; + + std::cout << "\n\"SPI factory-reset\"\n"; + std::cout << " --factory-reset, must be 1st option\n"; + std::cout << " [--dual-flash]\n"; + std::cout << " --card \n"; + std::cout << " [--force, yes for prompt]\n"; + std::cout << " [--bar ], default is 0\n"; + std::cout << " [--bar-offset ], default is 0x40000\n"; + + std::cout << "\n\"QSPIPS erase\"\n"; + std::cout << " --qspips-erase, must be 1st option\n"; + std::cout << " --card \n"; + std::cout << " [--offset ], default is 0\n"; + std::cout << " [--length ], default is 96MB\n"; + std::cout << " [--flash-type ], default is qspi_ps_x2_single\n"; + std::cout << " [--bar ], default is 0\n"; + std::cout << " [--bar-offset ], default is 0x40000\n"; + std::cout << " [--force, yes for prompt]\n"; + + std::cout << "\n\"QSPIPS flash\"\n"; + std::cout << " --qspips-flash, must be 1st option\n"; + std::cout << " --input \n"; + std::cout << " --card \n"; + std::cout << " [--offset ], default is 0\n"; + std::cout << " [--flash-type ], default is qspi_ps_x2_single\n"; + std::cout << " [--bar ], default is 0\n"; + std::cout << " [--bar-offset ], default is 0x40000\n"; + std::cout << " [--force, yes for prompt]\n"; + + std::cout << "\n\"QSPIPS read back\"\n"; + std::cout << " --qspips-read, must be 1st option\n"; + std::cout << " --output \n"; + std::cout << " --card \n"; + std::cout << " [--offset ], default is 0\n"; + std::cout << " [--length ], default is 128MB\n"; + std::cout << " [--flash-type ], default is qspi_ps_x2_single\n"; + std::cout << " [--bar ], default is 0\n"; + std::cout << " [--bar-offset ], default is 0x40000\n"; +} + +int reset(int argc, char *argv[]) +{ + bool force = false; + std::string bdf; + const char *fname = argv[0]; + int bar = 0; + size_t baroff = INVALID_OFFSET; + bool dualflash = false; + + sudoOrDie(); + + while (true) { + const auto opt = getopt_long(argc, argv, "", reset_opts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case '2': + bdf = std::string(optarg); + break; + case '1': + force = true; + break; + case '0': + break; + case '3': + dualflash = true; + break; + case '4': + bar = std::stoi(optarg, nullptr, 0); + break; + case '5': + baroff = std::stoi(optarg, nullptr, 0); + break; + default: + printHelp(fname); + return -EINVAL; + } + } + if (bdf.empty()) { + printHelp(fname); + return -EINVAL; + } + + std::cout + << "About to revert to golden image for card " << bdf << std::endl; + + if (!force && !canProceed()) + return -ECANCELED; + + pcidev::pci_device dev(bdf, bar, baroff); + XSPI_Flasher xspi(&dev, dualflash); + return xspi.revertToMFG(); +} + +int flash(int argc, char *argv[]) +{ + int ret = 0; + bool force = false; + std::string primary_file; + std::string secondary_file; + std::string bdf; + const char *fname = argv[0]; + int bar = 0; + size_t baroff = INVALID_OFFSET; + + sudoOrDie(); + + while (true) { + const auto opt = getopt_long(argc, argv, "", flash_opts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case '2': + bdf = std::string(optarg); + break; + case '1': + force = true; + break; + case '0': + primary_file = std::string(optarg); + break; + case '3': + secondary_file = std::string(optarg); + break; + case '4': + bar = std::stoi(optarg); + break; + case '5': + baroff = std::stoi(optarg, nullptr, 0); + break; + default: + printHelp(fname); + return -EINVAL; + } + } + if (bdf.empty() || primary_file.empty()) { + printHelp(fname); + return -EINVAL; + } + + std::cout + << "About to flash below MCS bitstream onto card " + << bdf << ":" << std::endl; + std::cout << primary_file << std::endl; + if (!secondary_file.empty()) + std::cout << secondary_file << std::endl; + + if (!force && !canProceed()) + return -ECANCELED; + + pcidev::pci_device dev(bdf, bar, baroff); + XSPI_Flasher xspi(&dev, !secondary_file.empty()); + + if (secondary_file.empty()) { + firmwareImage pri(primary_file.c_str()); + if (pri.fail()) + return -EINVAL; + ret = xspi.xclUpgradeFirmware1(pri); + } else { + firmwareImage pri(primary_file.c_str()); + firmwareImage sec(secondary_file.c_str()); + if (pri.fail() || sec.fail()) + return -EINVAL; + ret = xspi.xclUpgradeFirmware2(pri, sec); + } + + return ret; +} + +int qspips_erase(int argc, char *argv[]) +{ + std::string bdf; + const char *fname = argv[0]; + int bar = 0; + size_t baroff = INVALID_OFFSET; + std::string flash_type; + size_t offset = 0, len = GOLDEN_BASE; + bool force = false; + + sudoOrDie(); + + while (true) { + const auto opt = getopt_long(argc, argv, "", qspips_erase_opts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case '0': + break; + case '1': + bdf = std::string(optarg); + break; + case '2': + offset = std::stoi(optarg, nullptr, 0); + break; + case '3': + len = std::stoi(optarg, nullptr, 0); + break; + case '4': + flash_type = std::string(optarg); + break; + case '5': + bar = std::stoi(optarg); + break; + case '6': + baroff = std::stoi(optarg, nullptr, 0); + break; + case '7': + force = true; + break; + default: + printHelp(fname); + return -EINVAL; + } + } + if (bdf.empty()) { + printHelp(fname); + return -EINVAL; + } + + std::cout << "About to erase flash" + << " [0x" << std::hex << offset << ",0x" << offset+len + << "] on card " << bdf << std::endl; + + if (offset + len > GOLDEN_BASE) + std::cout << "\nThis might erase golden image if there is !!\n" << std::endl; + + if (!force && !canProceed()) + return -ECANCELED; + + pcidev::pci_device dev(bdf, bar, baroff, flash_type); + XQSPIPS_Flasher qspips(&dev); + + return qspips.xclErase(offset, len); +} + +int qspips_flash(int argc, char *argv[]) +{ + std::string bdf; + const char *fname = argv[0]; + int bar = 0; + size_t baroff = INVALID_OFFSET; + size_t offset = 0; + std::string flash_type; + std::string bin_file; + bool force = false; + + sudoOrDie(); + + while (true) { + const auto opt = getopt_long(argc, argv, "", qspips_flash_opts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case '0': + break; + case '1': + bdf = std::string(optarg); + break; + case '2': + bin_file = std::string(optarg); + break; + case '3': + offset = std::stoi(optarg, nullptr, 0); + break; + case '4': + flash_type = std::string(optarg); + break; + case '5': + bar = std::stoi(optarg); + break; + case '6': + baroff = std::stoi(optarg, nullptr, 0); + break; + case '7': + force = true; + break; + default: + printHelp(fname); + return -EINVAL; + } + } + if (bdf.empty() || bin_file.empty()) { + printHelp(fname); + return -EINVAL; + } + + firmwareImage bin(bin_file.c_str()); + if (bin.fail()) + return -EINVAL; + std::cout + << "About to program flash on card " << bdf << " at offset 0x" << std::hex << offset << std::dec << std::endl; + + if (!force && !canProceed()) + return -ECANCELED; + + pcidev::pci_device dev(bdf, bar, baroff, flash_type); + XQSPIPS_Flasher qspips(&dev); + + return qspips.xclUpgradeFirmware(bin, offset); +} + +int qspips_readback(int argc, char *argv[]) +{ + std::string bdf; + const char *fname = argv[0]; + int bar = 0; + size_t baroff = INVALID_OFFSET; + size_t offset = 0, len = FLASH_SIZE; + std::string flash_type; + std::string output; + + sudoOrDie(); + + while (true) { + const auto opt = getopt_long(argc, argv, "", qspips_readback_opts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case '0': + break; + case '1': + bdf = std::string(optarg); + break; + case '2': + output = std::string(optarg); + break; + case '3': + offset = std::stoi(optarg, nullptr, 0); + break; + case '4': + len = std::stoi(optarg, nullptr, 0); + break; + case '5': + flash_type = std::string(optarg); + break; + case '6': + bar = std::stoi(optarg); + break; + case '7': + baroff = std::stoi(optarg, nullptr, 0); + break; + default: + printHelp(fname); + return -EINVAL; + } + } + if (bdf.empty() || output.empty()) { + printHelp(fname); + return -EINVAL; + } + + std::cout << "Read out flash" + << " [0x" << std::hex << offset << ",0x" << offset+len + << "] on card " << bdf << " to " << output << std::dec << std::endl; + + pcidev::pci_device dev(bdf, bar, baroff, flash_type); + XQSPIPS_Flasher qspips(&dev); + + return qspips.xclReadBack(output, offset, len); +} + +int main(int argc, char *argv[]) +{ + try { + if (is_op(reset_opts, argc, argv)) + return reset(argc, argv); + + if (is_op(flash_opts, argc, argv)) + return flash(argc, argv); + + if (is_op(qspips_erase_opts, argc, argv)) + return qspips_erase(argc, argv); + + if (is_op(qspips_flash_opts, argc, argv)) + return qspips_flash(argc, argv); + + if (is_op(qspips_readback_opts, argc, argv)) + return qspips_readback(argc, argv); + + } catch (const std::exception& ex) { + std::cout << "Failed to flash: " << ex.what() << std::endl; + return -EINVAL; + } + + printHelp(argv[0]); + return -EINVAL; +} diff --git a/tools/xbflash.qspi/pcidev.cpp b/tools/xbflash.qspi/pcidev.cpp new file mode 100644 index 00000000..b8746d92 --- /dev/null +++ b/tools/xbflash.qspi/pcidev.cpp @@ -0,0 +1,228 @@ +/** + * Copyright (C) 2020 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcidev.h" + +namespace pcidev { + +/* + * wordcopy() + * + * Copy bytes word (32bit) by word. + * Neither memcpy, nor std::copy work as they become byte copying + * on some platforms. + */ +inline void* +wordcopy(void *dst, const void* src, size_t bytes) +{ + // assert dest is 4 byte aligned + assert((reinterpret_cast(dst) % 4) == 0); + + using word = uint32_t; + volatile auto d = reinterpret_cast(dst); + auto s = reinterpret_cast(src); + auto w = bytes/sizeof(word); + + for (size_t i=0; i= 0) { + std::cout << "Successfully opened " << file << std::endl; + return fd; + } + + // Open xoclv2 subdevice node + char bdf[20]; + std::snprintf(bdf, sizeof(bdf), "%04x:%02x:%02x.%x", domain, bus, dev, func); + file = "/dev/xfpga/"; + file += subdev; + file += "."; + file += bdf; + fd = ::open(file.c_str(), flag); + if (fd >= 0) { + std::cout << "Successfully opened " << file << std::endl; + return fd; + } + + return fd; +} + +pci_device:: +pci_device(const std::string& sysfs, int ubar, size_t flash_off, std::string flash_type) + : user_bar_index(ubar), flash_offset(flash_off), flash_type_str(flash_type) +{ + + uint32_t pcmd = 0; + char sysfsname[20] = {0}; + uint16_t dom = 0, b, d, f; + if (sscanf(sysfs.c_str(), "%hx:%hx.%hx", &b, &d, &f) < 3 && + sscanf(sysfs.c_str(), "%hx:%hx:%hx.%hx", &dom, &b, &d, &f) < 4) + throw std::runtime_error("Couldn't parse entry name " + sysfs); + + domain = dom; + bus = b; + dev = d; + func = f; + user_bar_size = 0; + + std::snprintf(sysfsname, sizeof(sysfsname), "%04x:%02x:%02x.%x", + domain, bus, dev, func); + std::string conffile("/sys/bus/pci/devices/"); + conffile += sysfsname; + conffile += "/config"; + + int conf_handle = ::open(conffile.c_str(), O_RDWR | O_SYNC); + if (conf_handle < 0) { + throw std::runtime_error("Failed to open " + conffile); + } + + if(lseek(conf_handle, 4, SEEK_SET) != 4) + throw std::runtime_error("Failed to set file pointer for " + conffile); + + if(::read(conf_handle, &pcmd, 4) < 0) + throw std::runtime_error("Failed to read " + conffile); + + pcmd = pcmd | PCI_COMMAND_MEMORY; + + if(lseek(conf_handle, 4, SEEK_SET) != 4) + throw std::runtime_error("Failed to set file pointer for " + conffile); + + if(::write(conf_handle, &pcmd, 4) < 0) + throw std::runtime_error("Failed to write " + conffile); + + close(conf_handle); +} + +pci_device:: +~pci_device() +{ + if (user_bar_map != MAP_FAILED) + ::munmap(user_bar_map, user_bar_size); +} + +int +pci_device:: +map_usr_bar() +{ + std::lock_guard l(lock); + + if (user_bar_map != MAP_FAILED) + return 0; + + char sysfsname[20]; + std::snprintf(sysfsname, sizeof(sysfsname), "%04x:%02x:%02x.%x", + domain, bus, dev, func); + std::string resfile("/sys/bus/pci/devices/"); + resfile += sysfsname; + resfile += "/resource"; + resfile += std::to_string(user_bar_index); + int dev_handle = ::open(resfile.c_str(), O_RDWR | O_SYNC); + if (dev_handle < 0) { + int err = errno; + std::cout << "Failed to open " << resfile << " : " + << strerror(err) << std::endl; + return -err; + } + + struct stat sb; + if (fstat(dev_handle, &sb) == -1) { // To obtain file size + int err = errno; + std::cout << "Failed to stat " << resfile << ": " + << strerror(err) << std::endl; + (void) close(dev_handle); + return -err; + } + user_bar_size = sb.st_size; + + user_bar_map = (char *)::mmap(0, user_bar_size, PROT_READ | PROT_WRITE, + MAP_SHARED, dev_handle, 0); + + // Mapping should stay valid after handle is closed + // (according to man page) + (void)close(dev_handle); + + if (user_bar_map == MAP_FAILED) { + int err = errno; + std::cout << "Failed to map " << resfile << ": " + << strerror(err) << std::endl; + return -err; + } + + return 0; +} + +void +pci_device:: +close(int dev_handle) +{ + if (dev_handle != -1) + (void)::close(dev_handle); +} + + +int +pci_device:: +pcieBarRead(uint64_t offset, void* buf, uint64_t len) +{ + if (user_bar_map == MAP_FAILED) { + int ret = map_usr_bar(); + if (ret) { + std::cout << "Failed to map in PCIE BAR. Does the card specified exist?" << std::endl; + throw; + } + } + (void) wordcopy(buf, user_bar_map + offset, len); + return 0; +} + +int +pci_device:: +pcieBarWrite(uint64_t offset, const void* buf, uint64_t len) +{ + if (user_bar_map == MAP_FAILED) { + int ret = map_usr_bar(); + if (ret) + return ret; + } + (void) wordcopy(user_bar_map + offset, buf, len); + return 0; +} + +} // namespace pcidev diff --git a/tools/xbflash.qspi/pcidev.h b/tools/xbflash.qspi/pcidev.h new file mode 100644 index 00000000..eac9c877 --- /dev/null +++ b/tools/xbflash.qspi/pcidev.h @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2020 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +#ifndef _PCIDEV_H_ +#define _PCIDEV_H_ + +#include +#include +#include + +#define INVALID_ID ((uint16_t)-1) + +namespace pcidev { + +// One PCIE function on FPGA board +class pci_device +{ +public: + pci_device(const std::string& sysfs, int ubar, size_t flash_off, std::string flash_type = ""); + ~pci_device(); + int pcieBarRead(uint64_t offset, void *buf, uint64_t len); + int pcieBarWrite(uint64_t offset, const void *buf, uint64_t len); + int open(const std::string& subdev, int flag); + void close(int devhdl); + size_t get_flash_offset() { return flash_offset; } + int get_flash_bar_index() { return user_bar_index; } + std::string get_flash_type() { return flash_type_str; }; + +private: + uint16_t domain = INVALID_ID; + uint16_t bus = INVALID_ID; + uint16_t dev = INVALID_ID; + uint16_t func = INVALID_ID; + + int map_usr_bar(void); + std::mutex lock; + char *user_bar_map = reinterpret_cast(MAP_FAILED); + int user_bar_index; + size_t user_bar_size; + size_t flash_offset; + std::string flash_type_str; +}; + +} // namespace pcidev + +#endif diff --git a/tools/xbflash.qspi/xqspips.cpp b/tools/xbflash.qspi/xqspips.cpp new file mode 100644 index 00000000..d710dab1 --- /dev/null +++ b/tools/xbflash.qspi/xqspips.cpp @@ -0,0 +1,1610 @@ +/** + * Copyright (C) 2016-2022 Xilinx, Inc + * Author(s) : Min Ma + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include "xqspips.h" + +#include "unistd.h" + +#ifdef WINDOWS +#define __func__ __FUNCTION__ +#endif + +#ifdef __GNUC__ +# define XQSPIPS_UNUSED __attribute__((unused)) +#endif + +#define SAVE_FILE 0 +#define FLASH_BASE 0x040000 +#define FLASH_TYPE "qspi_ps_x2_single" + +/* + * The following constants define the commands which may be sent to the Flash device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define FAST_READ_CMD_4B 0x0C +#define WRITE_4B_CMD 0x12 +#define READ_CMD_4B 0x13 +#define BANK_REG_RD 0x16 +#define BANK_REG_WR 0x17 +#define EXIT_4B_ADDR_MODE_ISSI 0x29 +#define QUAD_WRITE_CMD 0x32 +#define READ_CONFIG_CMD 0x35 +#define DUAL_READ_CMD 0x3B +#define DUAL_READ_CMD_4B 0x3C +#define VOLATILE_WRITE_ENABLE_CMD 0x50 +#define QUAD_READ_CMD 0x6B +#define QUAD_READ_CMD_4B 0x6C +#define READ_FLAG_STATUS_CMD 0x70 +#define READ_ID 0x9F +#define DUAL_WRITE_CMD 0xA2 +#define ENTER_4B_ADDR_MODE 0xB7 +#define DIE_ERASE_CMD 0xC4 +/* Bank register is called Extended Address Register in Micron */ +#define EXTADD_REG_WR 0xC5 +#define BULK_ERASE_CMD 0xC7 +#define EXTADD_REG_RD 0xC8 +#define FOURKB_SUBSECTOR_ERASE_CMD 0x20 +#define SEC_ERASE_CMD 0xD8 +#define SEC_4B_ERASE_CMD 0xDC +#define EXIT_4B_ADDR_MODE 0xE9 + +#define IDCODE_READ_BYTES 6 +#define WRITE_ENABLE_BYTES 1 /* Write Enable bytes */ +#define BULK_ERASE_BYTES 1 /* Bulk erase extra bytes */ +#define STATUS_READ_BYTES 2 /* Status read bytes count */ +#define STATUS_WRITE_BYTES 2 /* Status write bytes count */ + +#define FLASH_SR_BUSY_MASK 0x01 +#define FOURKB_SUBSECTOR_SIZE 0x1000 +#define SECTOR_SIZE 0x10000 + +#define ENTER_4B 1 +#define EXIT_4B 0 + +/* Registers offset */ +#define GQSPI_CFG_OFFSET 0x100 /* GQSPI Configuration Register*/ +#define GQSPI_ISR_OFFSET 0x104 /* GQSPI Status Register */ +#define GQSPI_IER_OFFSET 0x108 /* GQSPI Interrupt Enable Register */ +#define GQSPI_IDR_OFFSET 0x10C /* GQSPI Interrupt Disable Register */ +#define GQSPI_IMR_OFFSET 0x110 /* GQSPI Interrupt Mask Register */ +#define GQSPI_EN_OFFSET 0x114 /* GQSPI Enable Register */ +#define GQSPI_TXD_OFFSET 0x11C /* GQSPI Transmit Data Register */ +#define GQSPI_RXD_OFFSET 0x120 /* GQSPI Receive Data Register */ +#define GQSPI_TX_THRESH_OFFSET 0x128 /* GQSPI TX FIFO Threshold Level Register */ +#define GQSPI_RX_THRESH_OFFSET 0x12C /* GQSPI RX FIFO Threshold Level Register */ +#define GQSPI_GPIO_OFFSET 0x130 /* GQSPI GPIO for Write Protect Register */ +#define GQSPI_LPBK_DLY_ADJ_OFFSET 0x138 /* GQSPI Lookback clock delay adjustment Register */ +#define GQSPI_GEN_FIFO_OFFSET 0x140 /* GQSPI Generic FIFO Configuration Register */ +#define GQSPI_SEL_OFFSET 0x144 /* GQSPI Select Register */ +#define GQSPI_FIFO_CTRL_OFFSET 0x14C /* GQSPI FIFO Control Register */ +#define GQSPI_GF_THRESH_OFFSET 0x150 /* GQSPI Generic FIFO Threshold Level Register */ +#define GQSPI_POLL_CFG_OFFSET 0x154 /* GQSPI Poll Configuration Register */ +#define GQSPI_P_TIMEOUT_OFFSET 0x158 /* GQSPI Poll Time Out Register */ +#define GQSPI_DATA_DLY_ADJ_OFFSET 0x1F8 /* GQSPI Rx Data Delay Register */ +#define GQSPI_MOD_ID_OFFSET 0x1FC /* GQSPI Module Identification Register */ + +/* Register constants/masks */ +#define XQSPIPSU_CFG_MODE_EN_MASK 0XC0000000U +#define XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK 0X20000000U +#define XQSPIPSU_CFG_START_GEN_FIFO_MASK 0X10000000U +#define XQSPIPSU_CFG_ENDIAN_MASK 0X04000000U +#define XQSPIPSU_CFG_EN_POLL_TO_MASK 0X00100000U +#define XQSPIPSU_CFG_WP_HOLD_MASK 0X00080000U +#define XQSPIPSU_CFG_BAUD_RATE_DIV_MASK 0X00000038U +#define XQSPIPSU_CFG_CLK_PHA_MASK 0X00000004U +#define XQSPIPSU_CFG_CLK_POL_MASK 0X00000002U + +#define XQSPIPSU_GENFIFO_IMM_DATA_MASK 0x000FFU +#define XQSPIPSU_GENFIFO_DATA_XFER 0x00100U +#define XQSPIPSU_GENFIFO_EXP 0x00200U +#define XQSPIPSU_GENFIFO_EXP_START 0x100U +#define XQSPIPSU_GENFIFO_MODE_MASK 0x00C00U /* And with ~MASK first */ +#define XQSPIPSU_GENFIFO_BUS_MASK 0x0C000U /* And with ~MASK first */ +#define XQSPIPSU_GENFIFO_TX 0x10000U /* inverse is zero pump */ +#define XQSPIPSU_GENFIFO_RX 0x20000U /* inverse is RX discard */ +#define XQSPIPSU_GENFIFO_STRIPE 0x40000U +#define XQSPIPSU_GENFIFO_POLL 0x80000U + +#define XQSPIPSU_ISR_WR_TO_CLR_MASK 0X00000002U +#define XQSPIPSU_ISR_POLL_TIME_EXPIRE_MASK 0X00000002U +#define XQSPIPSU_ISR_TXNOT_FULL_MASK 0X00000004U +#define XQSPIPSU_ISR_TXFULL_MASK 0X00000008U +#define XQSPIPSU_ISR_RXNEMPTY_MASK 0X00000010U +#define XQSPIPSU_ISR_RXFULL_MASK 0X00000020U +#define XQSPIPSU_ISR_GENFIFOEMPTY_MASK 0X00000080U +#define XQSPIPSU_ISR_TXEMPTY_MASK 0X00000100U +#define XQSPIPSU_ISR_GENFIFOFULL_MASK 0X00000400U +#define XQSPIPSU_ISR_RXEMPTY_MASK 0X00000800U +#define XQSPIPSU_IDR_ALL_MASK 0X00000FBEU +#define XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK 0X00000001U +#define XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK 0X00000002U +#define XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK 0X00000004U +#define XQSPIPSU_LPBK_DLY_ADJ_USE_LPBK_MASK 0X00000020U + +#define CFG_BAUD_RATE_DIV_2 0X00000000U +#define CFG_BAUD_RATE_DIV_4 0X00000008U +#define CFG_BAUD_RATE_DIV_8 0X00000010U +#define CFG_BAUD_RATE_DIV_16 0X00000018U +#define CFG_BAUD_RATE_DIV_32 0X00000020U +#define CFG_BAUD_RATE_DIV_64 0X00000028U +#define CFG_BAUD_RATE_DIV_128 0X00000030U +#define CFG_BAUD_RATE_DIV_256 0X00000038U + +#define XQSPIPSU_GENFIFO_CS_LOWER 0x01000U +#define XQSPIPSU_GENFIFO_CS_UPPER 0x02000U +#define XQSPIPSU_GENFIFO_CS_BOTH 0x03000U /* inverse is reserved */ +#define XQSPIPSU_GENFIFO_BUS_LOWER 0x04000U +#define XQSPIPSU_GENFIFO_BUS_UPPER 0x08000U +#define XQSPIPSU_GENFIFO_BUS_BOTH 0x0C000U /* inverse is no bus */ +#define XQSPIPSU_GENFIFO_MODE_SPI 0x00400U +#define XQSPIPSU_GENFIFO_MODE_DUALSPI 0x00800U +#define XQSPIPSU_GENFIFO_MODE_QUADSPI 0x00C00U +#define XQSPIPSU_GENFIFO_CS_SETUP 0x05U +#define XQSPIPSU_GENFIFO_CS_HOLD 0x04U +#define XQSPIPSU_TX_FIFO_THRESHOLD_RESET_VAL 0X00000001U +#define XQSPIPSU_RX_FIFO_THRESHOLD_RESET_VAL 0X00000001U +#define XQSPIPSU_GEN_FIFO_THRESHOLD_RESET_VAL 0X00000010U +#define XQSPIPSU_TXD_DEPTH 64 + +// JEDEC vendor IDs +#define MICRON_VENDOR_ID 0x20 +#define MACRONIX_VENDOR_ID 0xC2 + +#define XQSpiPS_ReadReg(RegOffset) readReg(RegOffset) +#define XQSpiPS_WriteReg(RegOffset, Value) writeReg(RegOffset, Value) + +#define XQSpiPS_GetConfigReg() XQSpiPS_ReadReg(GQSPI_CFG_OFFSET) +#define XQSpiPS_SetConfigReg(mask) XQSpiPS_WriteReg(GQSPI_CFG_OFFSET, mask) +#define XQSpiPS_GetStatusReg() XQSpiPS_ReadReg(GQSPI_ISR_OFFSET) +#define XQSpiPS_SetStatusReg(mask) XQSpiPS_WriteReg(GQSPI_ISR_OFFSET, mask) +#define XQSpiPS_Enable_GQSPI() XQSpiPS_WriteReg(GQSPI_EN_OFFSET, 0x1) +#define XQSpiPS_Disable_GQSPI() XQSpiPS_WriteReg(GQSPI_EN_OFFSET, 0x0) +#define XQSpiPS_Sel_GQSPI() XQSpiPS_WriteReg(GQSPI_SEL_OFFSET, 0x1) +#define is_GQSPI_Enable() XQSpiPS_ReadReg(GQSPI_EN_OFFSET) +#define is_GQSPI_Mode() XQSpiPS_ReadReg(GQSPI_SEL_OFFSET) + +#define XQSPIPSU_MSG_FLAG_STRIPE 0x1U +#define XQSPIPSU_MSG_FLAG_RX 0x2U +#define XQSPIPSU_MSG_FLAG_TX 0x4U + +#define XQSPIPSU_SELECT_MODE_SPI 0x1U +#define XQSPIPSU_SELECT_MODE_DUALSPI 0x2U +#define XQSPIPSU_SELECT_MODE_QUADSPI 0x4U + +#define printHEX(RegName, RegValue) \ +do { \ + std::cout << RegName " 0x" << std::hex << RegValue << std::dec << std::endl; \ +} while(0); + +static bool TEST_MODE = false; + +static std::array flashVendors = { + MICRON_VENDOR_ID, + MACRONIX_VENDOR_ID +}; +static int flashVendor = -1; + +/** + * @brief XQSPIPS_Flasher::XQSPIPS_Flasher + * + * - Bring mgmt mapping from Flasher object + */ +XQSPIPS_Flasher::XQSPIPS_Flasher(pcidev::pci_device *dev) +{ + std::string err; + std::string typeStr; + std::string baroffStr = ""; + + mDev = dev; + mTxBytes = 0; + mRxBytes = 0; + + flash_base = dev->get_flash_offset(); + if (flash_base == INVALID_OFFSET) + flash_base = FLASH_BASE; + + typeStr = dev->get_flash_type(); + if (typeStr.empty()) + typeStr = FLASH_TYPE; + + // By default, it is 'perallel' + mConnectMode = 0; + if (typeStr.find("single") != std::string::npos) { + mConnectMode = 1; + } + + mBusWidth = 2; +} + +/** + * @brief XQSPIPS_Flasher::~XQSPIPS_Flasher + * + * - munmap + * - delete file descriptor + */ + +XQSPIPS_Flasher::~XQSPIPS_Flasher() +{ +} + +void XQSPIPS_Flasher::clearReadBuffer(unsigned size) +{ + for (unsigned i = 0; i < size; i++) { + mReadBuffer[i] = 0; + } +} + +void XQSPIPS_Flasher::clearWriteBuffer(unsigned size) +{ + for (unsigned i = 0; i < size; i++) { + mWriteBuffer[i] = 0; + } +} + +void XQSPIPS_Flasher::clearBuffers(unsigned size) +{ + clearReadBuffer(PAGE_SIZE); + clearWriteBuffer(PAGE_SIZE); +} + +uint32_t XQSPIPS_Flasher::readReg(unsigned RegOffset) +{ + unsigned value; + int status = mDev->pcieBarRead(flash_base + RegOffset, &value, 4); + if(status != 0) { + assert(0); + std::cout << "read reg ERROR" << std::endl; + } + //std::cout << "Read 0x" << std::hex << RegOffset + // << ": 0x" << value << std::dec << std::endl; + return value; +} + +int XQSPIPS_Flasher::writeReg(unsigned RegOffset, unsigned value) +{ + int status = mDev->pcieBarWrite(flash_base + RegOffset, &value, 4); + if(status != 0) { + assert(0); + std::cout << "write reg ERROR " << std::endl; + } + //std::cout << "Write 0x" << std::hex << RegOffset + // << ": 0x" << value << std::dec << std::endl; + return 0; + +} + +uint32_t XQSPIPS_Flasher::selectSpiMode(uint8_t SpiMode) +{ + uint32_t mask; + + switch (SpiMode) { + case XQSPIPSU_SELECT_MODE_SPI: + mask = XQSPIPSU_GENFIFO_MODE_SPI; + break; + case XQSPIPSU_SELECT_MODE_DUALSPI: + mask = XQSPIPSU_GENFIFO_MODE_DUALSPI; + break; + case XQSPIPSU_SELECT_MODE_QUADSPI: + mask = XQSPIPSU_GENFIFO_MODE_QUADSPI; + break; + default: + mask = XQSPIPSU_GENFIFO_MODE_SPI; + } + +#if defined(_DEBUG) + printHEX("SPI Mode is:", (unsigned)SpiMode); +#endif + + return mask; +} + +bool XQSPIPS_Flasher::waitGenFifoEmpty() +{ + long long delay = 0; + const timespec req = {0, 5000}; + while (delay < 30000000000) { + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + if (StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) { + return true; + } +#if defined(_DEBUG) + printHEX("Gen FIFO Not Empty", StatusReg); +#endif + nanosleep(&req, 0); + delay += 5000; + } + std::cout << "Unable to get Gen FIFO Empty" << std::endl; + return false; +} + +bool XQSPIPS_Flasher::waitTxEmpty() +{ + long long delay = 0; + const timespec req = {0, 5000}; + while (delay < 30000000000) { + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + if (StatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) { + return true; + } +#if defined(_DEBUG) + printHEX("TXD Not Empty", StatusReg); +#endif + nanosleep(&req, 0); + delay += 5000; + } + std::cout << "Unable to get Tx Empty" << std::endl; + return false; +} + +void XQSPIPS_Flasher::program(std::istream& binStream, unsigned base) +{ + unsigned total_size = 0; + unsigned remain = 0; + unsigned pages = 0; + unsigned addr = 0; + unsigned size = 0; + int beatCount = 0; + + binStream.seekg(0, binStream.end); + total_size = binStream.tellg(); + binStream.seekg(0, binStream.beg); + + pages = total_size / PAGE_SIZE; + remain = total_size % PAGE_SIZE; + +#if defined(_DEBUG) + std::cout << "Verify earse flash" << std::endl; + int mismatched = 0; + for (unsigned page = 0; page <= pages; page++) { + addr = page * PAGE_SIZE; + if (page != pages) + size = PAGE_SIZE; + else + size = remain; + + readFlash(base + addr, size); + for (unsigned i = 0; i < size; i++) { + if (0xFF != mReadBuffer[i]) { + mismatched = 1; + } + } + + if (mismatched) { + std::cout << "Erase failed at page " << page << std::endl; + mismatched = 0; + } + + } +#endif + + std::cout << "Programming flash" << std::flush; + beatCount = 0; + for (unsigned page = 0; page <= pages; page++) { + beatCount++; + if (beatCount % 4000 == 0) { + std::cout << "." << std::flush; + } + + addr = page * PAGE_SIZE; + if (page != pages) + size = PAGE_SIZE; + else + size = remain; + + binStream.read((char *)mWriteBuffer, size); + writeFlash(base + addr, size); + } + std::cout << std::endl; +} + +int XQSPIPS_Flasher::verify(std::istream& binStream, unsigned base, bool quiet) +{ + unsigned total_size = 0; + unsigned remain = 0; + unsigned pages = 0; + unsigned addr = 0; + unsigned size = 0; + int beatCount = 0; + int mismatched = 0; + + binStream.seekg(0, binStream.end); + total_size = binStream.tellg(); + binStream.seekg(0, binStream.beg); + +#if SAVE_FILE + std::ofstream of_flash; + of_flash.open("/tmp/BOOT.BIN", std::ofstream::out); + if (!of_flash.is_open()) { + std::cout << "Could not open /tmp/BOOT.BIN" << std::endl; + return false; + } +#endif + + remain = total_size % PAGE_SIZE; + pages = total_size / PAGE_SIZE; + + if (!quiet) + std::cout << "Verifying" << std::flush; + + beatCount = 0; + for (unsigned page = 0; page <= pages; page++) { + beatCount++; + if (beatCount % 4000 == 0 && !quiet) { + std::cout << "." << std::flush; + } + + addr = page * PAGE_SIZE; + if (page != pages) + size = PAGE_SIZE; + else + size = remain; + + binStream.read((char *)mWriteBuffer, size); + + readFlash(base + addr, size); + + mismatched = 0; + for (unsigned i = 0; i < size; i++) { +#if SAVE_FILE + of_flash << mReadBuffer[i]; +#endif + if (mWriteBuffer[i] != mReadBuffer[i]) { + mismatched = 1; + break; + } + } + if (mismatched) { + std::cout << "Find mismatch at page " << page << std::endl; + break; + } + } + std::cout << std::endl; + +#if SAVE_FILE + of_flash.close(); +#endif + + return mismatched; +} + +int XQSPIPS_Flasher::xclReadBack(std::string output, unsigned base, unsigned total_size) +{ + unsigned remain = 0; + unsigned pages = 0; + unsigned addr = 0; + unsigned size = 0; + int beatCount = 0; + + initQSpiPS(); + + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + + if (StatusReg == 0xFFFFFFFF) { + std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl; + exit(-EOPNOTSUPP); + } + + /* Make sure it is ready to receive commands. */ + resetQSpiPS(); + XQSpiPS_Enable_GQSPI(); + + if (!getFlashID()) { + std::cout << "[ERROR]: Could not get Flash ID" << std::endl; + exit(-EOPNOTSUPP); + } + + std::ofstream of_flash; + of_flash.open(output, std::ofstream::out); + if (!of_flash.is_open()) { + std::cout << "Could not open " << output << std::endl; + return false; + } + + if (!total_size) + total_size = FLASH_SIZE; + + if (base + total_size > FLASH_SIZE) { + std::cout << "[ERROR]: Invalid argument" << std::endl; + exit(-EINVAL); + } + + remain = total_size % PAGE_SIZE; + pages = total_size / PAGE_SIZE; + + beatCount = 0; + for (unsigned page = 0; page <= pages; page++) { + beatCount++; + if (beatCount % 4000 == 0) { + std::cout << "." << std::flush; + } + + addr = page * PAGE_SIZE; + if (page != pages) + size = PAGE_SIZE; + else + size = remain; + + readFlash(base + addr, size); + + for (unsigned i = 0; i < size; i++) { + of_flash << mReadBuffer[i]; + } + } + std::cout << std::endl; + + of_flash.close(); + + return 0; +} + +int XQSPIPS_Flasher::revertToMFG(std::istream& binStream) +{ + initQSpiPS(); + + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + + if (StatusReg == 0xFFFFFFFF) { + std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl; + exit(-EOPNOTSUPP); + } + + /* Make sure it is ready to receive commands. */ + resetQSpiPS(); + XQSpiPS_Enable_GQSPI(); + + if (!getFlashID()) { + std::cout << "[ERROR]: Could not get Flash ID" << std::endl; + exit(-EOPNOTSUPP); + } + + /* Use 4 bytes address mode */ + enterOrExitFourBytesMode(ENTER_4B); + + if (verify(binStream, GOLDEN_BASE, true)) { + std::cout << "[ERROR]: Doesn't find valid golden on flash !!" << std::endl; + return -ECANCELED; + } + + std::cout << "Golden detected at 96MB " << std::endl; + // Sectoer size is defined by SECTOR_SIZE + std::cout << "Factory resetting " << std::flush; + eraseSector(0, GOLDEN_BASE); + std::cout << std::endl; + + return 0; +} + +int XQSPIPS_Flasher::xclUpgradeFirmware(std::istream& binStream, unsigned offset) +{ + unsigned total_size = 0; + + binStream.seekg(0, binStream.end); + total_size = binStream.tellg(); + binStream.seekg(0, binStream.beg); + + std::cout << "INFO: ***BOOT.BIN has " << total_size << " bytes" << std::endl; + if (total_size + offset > FLASH_SIZE) { + std::cout << "[ERROR]: Invalid argument" << std::endl; + exit(-EINVAL); + } + /* Test only */ + //if (xclTestXQSpiPS(0)) + // return -1; + //else + // return 0; + + initQSpiPS(); + + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + + if (StatusReg == 0xFFFFFFFF) { + std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl; + exit(-EOPNOTSUPP); + } + + /* Make sure it is ready to receive commands. */ + resetQSpiPS(); + XQSpiPS_Enable_GQSPI(); + + if (!getFlashID()) { + std::cout << "[ERROR]: Could not get Flash ID" << std::endl; + exit(-EOPNOTSUPP); + } + + /* Use 4 bytes address mode */ + enterOrExitFourBytesMode(ENTER_4B); + + // Sectoer size is defined by SECTOR_SIZE + std::cout << "Erasing flash" << std::flush; + eraseSector(offset, offset >= GOLDEN_BASE ? FLASH_SIZE - offset : GOLDEN_BASE - offset); + //eraseBulk(); + std::cout << std::endl; + + program(binStream, offset); + int ret = verify(binStream, offset); + + enterOrExitFourBytesMode(EXIT_4B); + + return ret; +} + +int XQSPIPS_Flasher::xclErase(unsigned offset, unsigned total_size) +{ + + initQSpiPS(); + + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + + if (StatusReg == 0xFFFFFFFF) { + std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl; + exit(-EOPNOTSUPP); + } + + /* Make sure it is ready to receive commands. */ + resetQSpiPS(); + XQSpiPS_Enable_GQSPI(); + + if (!getFlashID()) { + std::cout << "[ERROR]: Could not get Flash ID" << std::endl; + exit(-EOPNOTSUPP); + } + + /* Use 4 bytes address mode */ + enterOrExitFourBytesMode(ENTER_4B); + + // Sectoer size is defined by SECTOR_SIZE + std::cout << "Erasing flash..." << std::flush; + eraseSector(offset, total_size); + //eraseBulk(); + std::cout << std::endl; + + enterOrExitFourBytesMode(EXIT_4B); + + return 0; +} + +void XQSPIPS_Flasher::initQSpiPS() +{ + /* Should Select GQSPI mode */ + if (!is_GQSPI_Mode()) { + std::cout << "Not support LQSPI mode, switch to GQSPI mode" << std::endl; + XQSpiPS_Sel_GQSPI(); + } + + /* Disable GQSPI */ + XQSpiPS_Disable_GQSPI(); + + if (TEST_MODE) + std::cout << "Initialize GQSPI done" << std::endl; +} + +void XQSPIPS_Flasher::resetQSpiPS() +{ + uint32_t ConfigReg; + + abortQSpiPS(); + + /* Initial Configure register target value is 0x00080010 */ + ConfigReg = XQSpiPS_GetConfigReg(); + + ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK; /* IO mode */ + ConfigReg &= ~XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK; /* Auto start */ + //ConfigReg |= XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK; /* Manual start */ + ConfigReg &= ~XQSPIPSU_CFG_ENDIAN_MASK; /* Little endain by default */ + ConfigReg &= ~XQSPIPSU_CFG_EN_POLL_TO_MASK; /* Disable poll timeout */ + ConfigReg |= XQSPIPSU_CFG_WP_HOLD_MASK; /* Set hold bit */ + ConfigReg &= ~XQSPIPSU_CFG_BAUD_RATE_DIV_MASK; /* Clear prescalar by default */ + ConfigReg |= CFG_BAUD_RATE_DIV_8; /* Divide by 8 */ + ConfigReg &= ~XQSPIPSU_CFG_CLK_PHA_MASK; /* CPHA 0 */ + ConfigReg &= ~XQSPIPSU_CFG_CLK_POL_MASK; /* CPOL 0 */ + + XQSpiPS_SetConfigReg(ConfigReg); + + //XQSpiPS_WriteReg(GQSPI_LPBK_DLY_ADJ_OFFSET, XQSPIPSU_LPBK_DLY_ADJ_USE_LPBK_MASK); + XQSpiPS_WriteReg(GQSPI_TX_THRESH_OFFSET, XQSPIPSU_TX_FIFO_THRESHOLD_RESET_VAL); + XQSpiPS_WriteReg(GQSPI_RX_THRESH_OFFSET, XQSPIPSU_RX_FIFO_THRESHOLD_RESET_VAL); + XQSpiPS_WriteReg(GQSPI_GF_THRESH_OFFSET, XQSPIPSU_GEN_FIFO_THRESHOLD_RESET_VAL); + + if (TEST_MODE) { + printHEX("CFG Reg:", ConfigReg); + printHEX("TX Thresh Reg:", XQSpiPS_ReadReg(GQSPI_TX_THRESH_OFFSET)); + printHEX("RX Thresh Reg:", XQSpiPS_ReadReg(GQSPI_RX_THRESH_OFFSET)); + printHEX("GF Thresh Reg:", XQSpiPS_ReadReg(GQSPI_GF_THRESH_OFFSET)); + std::cout << "Reset GQSPI done" << std::endl; + } +} + +void XQSPIPS_Flasher::abortQSpiPS() +{ + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + uint32_t ConfigReg = XQSpiPS_GetConfigReg(); + + /* Clear and diable interrupts (Ignore DMA register) */ + XQSpiPS_WriteReg(GQSPI_ISR_OFFSET, StatusReg | XQSPIPSU_ISR_WR_TO_CLR_MASK); + XQSpiPS_WriteReg(GQSPI_IDR_OFFSET, XQSPIPSU_IDR_ALL_MASK); + + /* Clear FIFO */ + if (XQSpiPS_GetStatusReg() & XQSPIPSU_ISR_RXEMPTY_MASK) { + XQSpiPS_WriteReg(GQSPI_FIFO_CTRL_OFFSET, XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK | XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK); + + } + + if (StatusReg & XQSPIPSU_ISR_RXEMPTY_MASK) { + /* Switch to IO mode to clear RX FIFO */ + ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK; /* IO mode */ + XQSpiPS_SetConfigReg(ConfigReg); + XQSpiPS_WriteReg(GQSPI_FIFO_CTRL_OFFSET, XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK); + } + + /* Disable GQSPI */ + XQSpiPS_Disable_GQSPI(); + + if (TEST_MODE) + std::cout << "Abort QSPI done" << std::endl; +} + +void XQSPIPS_Flasher::readRxFifo(xqspips_msg_t *msg, int32_t Size) +{ + int32_t Count = 0; + uint32_t Data = 0; + + assert(msg != NULL); + + while (mRxBytes != 0 && (Count < Size)) { + Data = XQSpiPS_ReadReg(GQSPI_RXD_OFFSET); +#if defined(_DEBUG) + printHEX("RX Data:", Data); +#endif + if (mRxBytes >= 4) { + memcpy(msg->bufPtr, &Data, 4); + msg->bufPtr += 4; + mRxBytes -= 4; + Count += 4; + } else { + /* less than 4 bytes */ + memcpy(msg->bufPtr, &Data, mRxBytes); + msg->bufPtr += mRxBytes; + Count += mRxBytes; + mRxBytes = 0; + } + } + +} + +void XQSPIPS_Flasher::fillTxFifo(xqspips_msg_t *msg, int32_t Size) +{ + int32_t Count = 0; + uint32_t Data = 0; + + assert(msg != NULL); + + while ((mTxBytes > 0) && (Count < Size)) { + if (mTxBytes >= 4) { + memcpy(&Data, msg->bufPtr, 4); + msg->bufPtr += 4; + Count += 4; + mTxBytes -= 4; + } else { + /* less than 4 bytes */ + memcpy(&Data, msg->bufPtr, mTxBytes); + msg->bufPtr += mTxBytes; + Count += mTxBytes; + mTxBytes = 0; + } + + XQSpiPS_WriteReg(GQSPI_TXD_OFFSET, Data); +#if defined(_DEBUG) + printHEX("TX Data:", Data); +#endif + } + +#if defined(_DEBUG) + std::cout << "Fill Tx FIFO " << Count << " Bytes." << std::endl; +#endif +} + +void XQSPIPS_Flasher::setupTXRX(xqspips_msg_t *msg, uint32_t *GenFifoEntry) +{ + /* Transmit */ + if (msg->flags & XQSPIPSU_MSG_FLAG_TX) { + *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER; + *GenFifoEntry |= XQSPIPSU_GENFIFO_TX; + *GenFifoEntry &= ~XQSPIPSU_GENFIFO_RX; + mTxBytes = msg->byteCount; + mRxBytes = 0; + fillTxFifo(msg, XQSPIPSU_TXD_DEPTH); + return; + } + + /* Receive */ + if (msg->flags & XQSPIPSU_MSG_FLAG_RX) { + *GenFifoEntry &= ~XQSPIPSU_GENFIFO_TX; + *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER; + *GenFifoEntry |= XQSPIPSU_GENFIFO_RX; + mRxBytes = msg->byteCount; + /* Support Rx DMA? */ + return; + } + + /* dummy */ + if (!(msg->flags & XQSPIPSU_GENFIFO_RX) && !(msg->flags & XQSPIPSU_GENFIFO_TX)) { + *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER; + *GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX); + return; + } +} + +void XQSPIPS_Flasher::sendGenFifoEntryCSAssert() +{ + uint32_t GenFifoEntry = 0x0U; + + GenFifoEntry &= ~(XQSPIPSU_GENFIFO_DATA_XFER | XQSPIPSU_GENFIFO_EXP); + GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK; + GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI; + /* By default, use upper and lower CS and Bus */ + GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK; + if (mConnectMode == 0) { + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH; + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_BOTH; + } else { + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER; + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_LOWER; + } + GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX | + XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL); + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_SETUP; + /* Write GEN FIFO */ + XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry); + +#if defined(_DEBUG) + printHEX("Assert CS: expectd 0xf405, got", GenFifoEntry); +#endif +} + +void XQSPIPS_Flasher::sendGenFifoEntryData(xqspips_msg_t *msg) +{ + uint32_t GenFifoEntry = 0x0U; + uint32_t tmpCount = 0; + + /* Mode SPI/Dual/Quad */ + GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK; + GenFifoEntry |= selectSpiMode(msg->busWidth); + + /* By default, use upper and lower CS and Bus */ + GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK; + if (mConnectMode == 0) { + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH; + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_BOTH; + } else { + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER; + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_LOWER; + } + + /* Stripe */ + if (msg->flags & XQSPIPSU_MSG_FLAG_STRIPE) { + GenFifoEntry |= XQSPIPSU_GENFIFO_STRIPE; + } else { + GenFifoEntry &= ~XQSPIPSU_GENFIFO_STRIPE; + } + + /* Do transfer in IO mode */ + XQSpiPS_SetConfigReg(XQSpiPS_GetConfigReg() & ~XQSPIPSU_CFG_MODE_EN_MASK); + + setupTXRX(msg, &GenFifoEntry); + + if (msg->byteCount < XQSPIPSU_GENFIFO_IMM_DATA_MASK) { + GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK; + GenFifoEntry |= msg->byteCount; +#if defined(_DEBUG) + printHEX("GenFifo data:", GenFifoEntry); +#endif + XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry); + } else { + /* Exponent entries */ + tmpCount = msg->byteCount; + uint8_t exponent = 8; + uint8_t immData = tmpCount & 0xFFU; + + GenFifoEntry |= XQSPIPSU_GENFIFO_EXP; + while (tmpCount != 0x0U) { + /* only support 1, 2, 4, 8... pages*/ + if (tmpCount & XQSPIPSU_GENFIFO_EXP_START) { + GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK; + GenFifoEntry |= exponent; +#if defined(_DEBUG) + printHEX("GenFifo data:", GenFifoEntry); +#endif + XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry); + } + tmpCount = tmpCount >> 1; + exponent++; + } + + /* Immediate entry */ + GenFifoEntry &= ~XQSPIPSU_GENFIFO_EXP; + if (immData > 0) { + GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK; + GenFifoEntry |= immData; +#if defined(_DEBUG) + printHEX("GenFifo data:", GenFifoEntry); +#endif + XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry); + } + } + +#if defined(_DEBUG) + std::cout << "Sent GenFifo Entry Data" << std::endl; +#endif +} + +void XQSPIPS_Flasher::sendGenFifoEntryCSDeAssert() +{ + uint32_t GenFifoEntry = 0x0U; + + GenFifoEntry &= ~(XQSPIPSU_GENFIFO_DATA_XFER | XQSPIPSU_GENFIFO_EXP); + GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK; + //GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI; + /* By default, use upper and lower CS and Bus */ + GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK; + if (mConnectMode == 0) + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH; + else + GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER; + + GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX | + XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL); + + GenFifoEntry |= XQSPIPSU_GENFIFO_CS_HOLD; + + /* Write GEN FIFO */ + XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry); + +#if defined(_DEBUG) + printHEX("De-Assert CS: expectd 0xc004, got", GenFifoEntry); +#endif +} + +/** + * @brief XQSPIPS_Flasher::finalTransfer + * + * This function performs a transfer on the bus in polled mode. The messages passed are all transferred between one CS asser and de-assert. + * + * @param msg is a pointer to the struct containing transfer data + * @param numMsg is the number of message to be transferrd. + * + * @return + * - True Success + * - Error code + * + */ +bool XQSPIPS_Flasher::finalTransfer(xqspips_msg_t *msg, uint32_t numMsg) +{ + uint32_t StatusReg; + + /* Make sure GQSPI is enable */ + XQSpiPS_Enable_GQSPI(); + sendGenFifoEntryCSAssert(); + + for (uint32_t Index = 0; Index < numMsg; Index++) { + /* Only handle one message at a time */ + sendGenFifoEntryData(&msg[Index]); + + do { + StatusReg = XQSpiPS_GetStatusReg(); + + /* Transmit more data if left */ + if (StatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK && + msg[Index].flags & XQSPIPSU_MSG_FLAG_TX && + mTxBytes > 0) { + fillTxFifo(&msg[Index], XQSPIPSU_TXD_DEPTH); + } + + if (msg[Index].flags & XQSPIPSU_MSG_FLAG_RX) { + uint32_t RxThr = XQSpiPS_ReadReg(GQSPI_RX_THRESH_OFFSET); + + if (StatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK) { + readRxFifo(&msg[Index], RxThr * 4); + } else { + if (StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) { + readRxFifo(&msg[Index], msg[Index].byteCount); + } + } + + } + + if (!waitGenFifoEmpty() || !waitTxEmpty()) + return false; + + } while (mTxBytes != 0 || mRxBytes != 0); + + } + + sendGenFifoEntryCSDeAssert(); + + StatusReg = XQSpiPS_GetStatusReg(); + while (!(StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK)) { + StatusReg = XQSpiPS_GetStatusReg(); + } + + XQSpiPS_Disable_GQSPI(); + +#if defined(_DEBUG) + std::cout << "Final transfer finished" << std::endl; +#endif + return true; +} + +bool XQSPIPS_Flasher::isFlashReady() +{ + xqspips_msg_t msgFlashStatus[2]; + uint8_t writeCmd = READ_STATUS_CMD; + uint32_t StatusReg = 0; + const timespec req = {0, 20000}; + long long delay = 0; + + msgFlashStatus[0].byteCount = 1; + msgFlashStatus[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgFlashStatus[0].flags = XQSPIPSU_MSG_FLAG_TX; + + msgFlashStatus[1].byteCount = STATUS_READ_BYTES; + msgFlashStatus[1].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgFlashStatus[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE; + + while (delay < 30000000000) { + msgFlashStatus[0].bufPtr = &writeCmd; + msgFlashStatus[1].bufPtr = mReadBuffer; + bool Status = finalTransfer(msgFlashStatus, 2); + if (!Status) { + return false; + } + if (mConnectMode == 0) + StatusReg = mReadBuffer[1] |= mReadBuffer[0]; + else + StatusReg = mReadBuffer[0]; + +#if defined(_DEBUG) + printHEX("Flash ready:", StatusReg); +#endif + if (!(StatusReg & FLASH_SR_BUSY_MASK)) { + return true; + } + nanosleep(&req, 0); + delay += 5000; + } + std::cout << "Unable to get Flash Ready" << std::endl; + return false; +} + +bool XQSPIPS_Flasher::setWriteEnable() +{ + xqspips_msg_t msgWriteEnable[2]; + uint8_t writeCmd = WRITE_ENABLE_CMD; + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + + if (StatusReg & XQSPIPSU_ISR_TXFULL_MASK) { + std::cout << "TXD FIFO full during WriteEnable" << std::endl; + return false; + } + + msgWriteEnable[0].bufPtr = &writeCmd; + msgWriteEnable[0].byteCount = 1; + msgWriteEnable[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgWriteEnable[0].flags = XQSPIPSU_MSG_FLAG_TX; + + if (!finalTransfer(msgWriteEnable, 1)) + return false; + + if (TEST_MODE) + std::cout << "Set write enable" << std::endl; + + return waitTxEmpty(); +} + +bool XQSPIPS_Flasher::getFlashID() +{ + xqspips_msg_t msgFlashID[2]; + uint32_t Status; + + if (!isFlashReady()) + return false; + + mWriteBuffer[0] = READ_ID; + + msgFlashID[0].bufPtr = mWriteBuffer; + msgFlashID[0].byteCount = 1; + msgFlashID[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgFlashID[0].flags = XQSPIPSU_MSG_FLAG_TX; + + msgFlashID[1].bufPtr = mReadBuffer; + msgFlashID[1].byteCount = IDCODE_READ_BYTES; + msgFlashID[1].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgFlashID[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE; + + Status = finalTransfer(msgFlashID, 2); + if ( !Status ) { + return false; + } + + if (mConnectMode == 0) { + // Stripe data: Lower data bus uses even bytes, i.e byte 0, 2, 4, ... + // Upper data bus uses odd bytes, i.e byte 1, 3, 5, ... + if (mReadBuffer[0] != mReadBuffer[1]) { + std::cout << "Upper Flash chip and lower Flash chip have differe vender id" << std::endl; + return false; + } + + if (mReadBuffer[2] != mReadBuffer[3]) { + std::cout << "Upper Flash chip and lower Flash chip have differe type" << std::endl; + return false; + } + + if (mReadBuffer[4] != mReadBuffer[5]) { + std::cout << "Upper Flash chip and lower Flash chip have differe capacity" << std::endl; + return false; + } + } + + //Update flash vendor + for (size_t i = 0; i < flashVendors.size(); i++) + if (mReadBuffer[0] == flashVendors[i]) + flashVendor = flashVendors[i]; + + //Update max number of sector. Value of 0x18 is 1 128Mbit sector + if(mReadBuffer[4] == 0xFF) + return false; + + for (int i = 0; i < IDCODE_READ_BYTES; i++) { + std::cout << "Idcode byte[" << i << "]=" << std::hex << (int)mReadBuffer[i] << std::dec << std::endl; + mReadBuffer[i] = 0; + } + + return true; +} + +bool XQSPIPS_Flasher::eraseSector(unsigned addr, uint32_t byteCount, uint8_t eraseCmd) +{ + xqspips_msg_t msgEraseFlash[1]; + uint8_t writeCmds[5]; + uint32_t realAddr; + uint32_t Sector; + + if (eraseCmd == 0xff) + eraseCmd = SEC_4B_ERASE_CMD; + + int beatCount = 0; + //roundup byteCount to next SECTOR boundary + byteCount = (byteCount + SECTOR_SIZE - 1) & (~SECTOR_SIZE); + for (Sector = 0; Sector < (byteCount / SECTOR_SIZE); Sector++) { + + if(!isFlashReady()) + return false; + + beatCount++; + if (beatCount % 64 == 0) { + std::cout << "." << std::flush; + } + + if (mConnectMode == 0) + realAddr = addr / 2; + else + realAddr = addr; + + if(!setWriteEnable()) + return false; + + writeCmds[0] = eraseCmd; + writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24); + writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16); + writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8); + writeCmds[4] = (uint8_t)(realAddr & 0xFF); + + msgEraseFlash[0].bufPtr = writeCmds; + msgEraseFlash[0].byteCount = 5; + msgEraseFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgEraseFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + if (!finalTransfer(msgEraseFlash, 1)) + return false; + + addr += SECTOR_SIZE; + } + + if (TEST_MODE) + std::cout << "Erase Flash done " << byteCount << " bytes" << std::endl; + + return true; +} + +bool XQSPIPS_Flasher::eraseBulk() +{ + xqspips_msg_t msgEraseFlash[1]; + uint8_t writeCmds[5]; + uint8_t eraseCmd; + + if(!isFlashReady()) + return false; + + eraseCmd = BULK_ERASE_CMD; + + if(!setWriteEnable()) + return false; + + writeCmds[0] = eraseCmd; + + msgEraseFlash[0].bufPtr = writeCmds; + msgEraseFlash[0].byteCount = 1; + msgEraseFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgEraseFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + if (!finalTransfer(msgEraseFlash, 1)) + return false; + + if(!isFlashReady()) + return false; + + return true; +} + +bool XQSPIPS_Flasher::readFlash(unsigned addr, uint32_t byteCount, uint8_t readCmd) +{ + xqspips_msg_t msgReadFlash[3]; + uint8_t writeCmds[5]; + uint32_t realAddr; + uint32_t commandBytes; + uint32_t msgCnt; + + if (!isFlashReady()) + return false; + + if (mConnectMode == 0) + realAddr = addr / 2; + else + realAddr = addr; + + if (readCmd == 0xff) { + switch(mBusWidth) { + case 2: + readCmd = DUAL_READ_CMD; + break; + case 4: + readCmd = QUAD_READ_CMD; + break; + default: + readCmd = READ_CMD; + } + } + + writeCmds[0] = readCmd; + writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24); + writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16); + writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8); + writeCmds[4] = (uint8_t)(realAddr & 0xFF); + commandBytes = 5; + + msgReadFlash[0].bufPtr = writeCmds; + msgReadFlash[0].byteCount = commandBytes; + msgReadFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgReadFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + msgCnt = 1; + /* Dummy clock */ + if (readCmd == QUAD_READ_CMD) { + msgReadFlash[msgCnt].bufPtr = NULL; + msgReadFlash[msgCnt].byteCount = 8; + msgReadFlash[msgCnt].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgReadFlash[msgCnt].flags = 0; + msgCnt++; + } else if (readCmd == DUAL_READ_CMD) { + msgReadFlash[msgCnt].bufPtr = NULL; + msgReadFlash[msgCnt].byteCount = 8; + msgReadFlash[msgCnt].busWidth = mBusWidth; + msgReadFlash[msgCnt].flags = 0; + msgCnt++; + } + + msgReadFlash[msgCnt].bufPtr = mReadBuffer; + msgReadFlash[msgCnt].byteCount = byteCount; + msgReadFlash[msgCnt].busWidth = mBusWidth; + msgReadFlash[msgCnt].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE; + msgCnt++; + + if (!finalTransfer(msgReadFlash, msgCnt)) + return false; + + if (TEST_MODE) + std::cout << "Read Flash done " << byteCount << " bytes" << std::endl; + + return true; +} + +bool XQSPIPS_Flasher::writeFlash(unsigned addr, uint32_t byteCount, uint8_t writeCmd) +{ + xqspips_msg_t msgWriteFlash[3]; + uint8_t writeCmds[5]; + uint32_t realAddr; + uint32_t commandBytes; + + if(!isFlashReady()) + return false; + + if (mConnectMode == 0) + realAddr = addr / 2; + else + realAddr = addr; + + if (!setWriteEnable()) + return false; + + if(!isFlashReady()) + return false; + + if (writeCmd == 0xff) { + switch(mBusWidth) { + case 2: + writeCmd = DUAL_WRITE_CMD; + break; + case 4: + writeCmd = QUAD_WRITE_CMD; + break; + default: + writeCmd = WRITE_CMD; + } + } + + writeCmds[0] = writeCmd; + writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24); + writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16); + writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8); + writeCmds[4] = (uint8_t)(realAddr & 0xFF); + commandBytes = 5; + + msgWriteFlash[0].bufPtr = writeCmds; + msgWriteFlash[0].byteCount = commandBytes; + msgWriteFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgWriteFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + /* The data to write is already filled up */ + msgWriteFlash[1].bufPtr = mWriteBuffer; + msgWriteFlash[1].byteCount = byteCount; + msgWriteFlash[1].busWidth = mBusWidth; + msgWriteFlash[1].flags = XQSPIPSU_MSG_FLAG_TX | XQSPIPSU_MSG_FLAG_STRIPE; + + if (!finalTransfer(msgWriteFlash, 2)) + return false; + + if (TEST_MODE) + std::cout << "Write Flash done " << byteCount << " bytes" << std::endl; + + return true; +} + +bool XQSPIPS_Flasher::enterOrExitFourBytesMode(uint32_t enable) +{ + uint8_t cmd; + + if (enable) { + cmd = ENTER_4B_ADDR_MODE; + } else { + cmd = EXIT_4B_ADDR_MODE; + } + + writeFlashReg(cmd, 0, 0); + + if (!isFlashReady()) + return false; + + if (TEST_MODE) + std::cout << "Four Bytes Mode " << enable << std::endl; + + return true; +} + +bool XQSPIPS_Flasher::readFlashReg(unsigned commandCode, unsigned bytes) +{ + xqspips_msg_t msgToFlash[2]; + bool Status = false; + + if (!isFlashReady()) + return false; + + mWriteBuffer[0] = commandCode; + + msgToFlash[0].bufPtr = mWriteBuffer; + msgToFlash[0].byteCount = 1; + msgToFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgToFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + msgToFlash[1].bufPtr = mReadBuffer; + msgToFlash[1].byteCount = bytes; + msgToFlash[1].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgToFlash[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE; + + Status = finalTransfer(msgToFlash, 2); + if ( !Status ) { + return false; + } + +#if defined(_DEBUG) + std::cout << "Printing output (with some extra bytes of readFlashReg cmd)" << std::endl; +#endif + + for(unsigned i = 0; i < bytes; ++ i) //Some extra bytes, no harm + { +#if defined(_DEBUG) + std::cout << i << " " << std::hex << (int)mReadBuffer[i] << std::dec << std::endl; +#endif + mReadBuffer[i] = 0; //clear + } + + return Status; +} + +bool XQSPIPS_Flasher::writeFlashReg(unsigned commandCode, unsigned value, unsigned bytes) +{ + bool Status = false; + xqspips_msg_t msgWriteFlash[1]; + + if (!setWriteEnable()) + return false; + + mWriteBuffer[0] = commandCode; + + switch (bytes) { + case 0: + break; + case 1: + mWriteBuffer[1] = (uint8_t) (value); + break; + case 2: + mWriteBuffer[1] = (uint8_t) (value >> 8); + mWriteBuffer[2] = (uint8_t) (value); + break; + default: + std::cout << "ERROR: Setting more than 2 bytes" << std::endl; + assert(0); + } + + msgWriteFlash[0].bufPtr = mWriteBuffer; + msgWriteFlash[0].byteCount = 1 + bytes; + msgWriteFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI; + msgWriteFlash[0].flags = XQSPIPSU_MSG_FLAG_TX; + + Status = finalTransfer(msgWriteFlash, 1); + if (!Status) + return false; + + if (!waitTxEmpty()) + return false; + + return Status; +} + +int XQSPIPS_Flasher::xclTestXQSpiPS(int index) +{ + TEST_MODE = false; + + std::cout << ">>> Test XQSpiPS engine <<<" << std::endl; + initQSpiPS(); + + /* print the IP (not of flash) control/status register. */ + uint32_t ConfigReg = XQSpiPS_GetConfigReg(); + uint32_t StatusReg = XQSpiPS_GetStatusReg(); + std::cout << "PS GQSPI Config/Status " << std::hex << ConfigReg << "/" << StatusReg << std::dec << std::endl; + + /* Make sure it is ready to receive commands. */ + resetQSpiPS(); + + XQSpiPS_Enable_GQSPI(); + printHEX("GQSPI enable:", XQSpiPS_ReadReg(GQSPI_EN_OFFSET)); + + /* 1. idcode read */ + std::cout << ">>> Testing read Flash ID" << std::endl; + if (!getFlashID()) { + std::cout << "[ERROR]: Could not get Flash ID" << std::endl; + exit(-EOPNOTSUPP); + } + + std::cout << "id code successful (please verify the idcode output too)" << std::endl; + std::cout << ">>> Now reading various flash registers <<<" << std::endl; + + /* 2. register read */ + std::cout << "Testing READ_STATUS_CMD" << std::endl; + uint8_t Cmd = READ_STATUS_CMD; + readFlashReg(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing READ_FLAG_STATUS_CMD" << std::endl; + Cmd = READ_FLAG_STATUS_CMD; + readFlashReg(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing EXTADD_REG_RD" << std::endl; + Cmd = EXTADD_REG_RD; + readFlashReg(Cmd, STATUS_READ_BYTES); + + /* 3. Testing simple read and write */ + enterOrExitFourBytesMode(ENTER_4B); + + std::cout << ">>> Testing simple read and write <<<" << std::endl; + unsigned addr = 0; + unsigned size = 0; + // Write/Read 16K + 100 bytes + //int total_size = 16 * 1024 + 100; + int total_size = 300; + + int remain = total_size % PAGE_SIZE; + int pages = total_size / PAGE_SIZE; + + std::cout << "Write " << total_size << " bytes" << std::endl; + + std::cout << "earse flash" << std::endl; + eraseSector(0, total_size); + //eraseBulk(); + + std::cout << ">>>>>> Write " << std::endl; + for (int page = 0; page <= pages; page++) { + addr = page * PAGE_SIZE; + if (page != pages) + size = PAGE_SIZE; + else + size = remain; + + for (unsigned index = 0; index < size; index++) { + mWriteBuffer[index] = (uint8_t)index; + } + + writeFlash(addr, size); + } + + remain = total_size % 256; + pages = total_size / 256; + + std::cout << ">>>>>> Verify data" << std::endl; + for (int page = 0; page <= pages; page++) { + addr = page * 256; + if (page != pages) + size = 256; + else + size = remain; + + readFlash(addr, size); + + // Verify + for (unsigned i = 0; i < size; i++) { + std::cout << i << " 0x" << std::hex << (int)mReadBuffer[i] << std::dec << std::endl; + if (mReadBuffer[i] != (i % PAGE_SIZE)) { + std::cout << "Found mismatch" << std::endl; + return -1; + } + } + } + std::cout << ">>>>>> " << total_size << " bytes data correct!" << std::endl; + + enterOrExitFourBytesMode(EXIT_4B); + + std::cout << ">>> Test Passed <<<" << std::endl; + return 0; +} diff --git a/tools/xbflash.qspi/xqspips.h b/tools/xbflash.qspi/xqspips.h new file mode 100644 index 00000000..6465f731 --- /dev/null +++ b/tools/xbflash.qspi/xqspips.h @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2016-2018 Xilinx, Inc + * Author(s) : Min Ma + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#ifndef _XQSPIPS_H_ +#define _XQSPIPS_H_ + +#include +#include +#include +#include "pcidev.h" + +#define PAGE_SIZE 256 +#define PAGE_8K 8192 +#define GOLDEN_BASE 0x06000000 +#define FLASH_SIZE 0x08000000 +#define INVALID_OFFSET ((size_t)-1) + +class XQSPIPS_Flasher +{ + struct ELARecord + { + unsigned mStartAddress; + unsigned mEndAddress; + unsigned mDataCount; + std::streampos mDataPos; + ELARecord() : mStartAddress(0), mEndAddress(0), mDataCount(0), mDataPos(0) {} + }; + + typedef std::list ELARecordList; + ELARecordList mRecordList; + +public: + XQSPIPS_Flasher(pcidev::pci_device *dev); + ~XQSPIPS_Flasher(); + void program(std::istream& binStream, unsigned base = 0); + int verify(std::istream& binStream, unsigned base = 0, bool quiet = false); + int revertToMFG(std::istream& binStream); + int xclUpgradeFirmware(std::istream& binStream, unsigned offset = 0); + int xclErase(unsigned offset = 0, unsigned total_size = GOLDEN_BASE); + int xclReadBack(std::string output, unsigned base = 0, unsigned total_size = FLASH_SIZE); + +private: + typedef struct { + uint8_t *bufPtr; + uint32_t byteCount; + uint32_t busWidth; + uint32_t flags; + } xqspips_msg_t; + + pcidev::pci_device *mDev; + uint8_t mWriteBuffer[PAGE_8K]; + uint8_t mReadBuffer[PAGE_8K]; + uint32_t mTxBytes; + uint32_t mRxBytes; + uint64_t flash_base; + + /* QSPI configure */ + uint32_t mConnectMode; //Single, Stacked and Parallel mode + uint32_t mBusWidth; // x1, x2, x4 + + void clearReadBuffer(unsigned size); + void clearWriteBuffer(unsigned size); + void clearBuffers(unsigned size); + + /* Flash functions */ + int xclTestXQSpiPS(int device_index); + bool getFlashID(); + bool isFlashReady(); + bool readFlashReg(unsigned commandCode, unsigned bytes); + bool writeFlashReg(unsigned commandCode, unsigned value, unsigned bytes); + bool eraseSector(unsigned addr, uint32_t byteCount, uint8_t eraseCmd = 0xff); + bool eraseBulk(); + bool readFlash(unsigned addr, uint32_t byteCount, uint8_t readCmd = 0xff); + bool writeFlash(unsigned addr, uint32_t byteCount, uint8_t writeCmd = 0xff); + + /* PS QSPI functions */ + void initQSpiPS(); + void abortQSpiPS(); + void resetQSpiPS(); + bool waitGenFifoEmpty(); + bool waitTxEmpty(); + int writeReg(unsigned regOffset, unsigned value); + bool enterOrExitFourBytesMode(uint32_t enable); + uint32_t readReg(unsigned RegOffset); + uint32_t selectSpiMode(uint8_t SpiMode); + bool setWriteEnable(); + void readRxFifo(xqspips_msg_t *msg, int32_t Size); + void fillTxFifo(xqspips_msg_t *msg, int32_t Size); + void setupTXRX(xqspips_msg_t *msg, uint32_t *GenFifoEntry); + void sendGenFifoEntryCSAssert(); + void sendGenFifoEntryData(xqspips_msg_t *msg); + void sendGenFifoEntryCSDeAssert(); + bool finalTransfer(xqspips_msg_t *msg, uint32_t numMsg); +}; + +#endif diff --git a/tools/xbflash.qspi/xspi.cpp b/tools/xbflash.qspi/xspi.cpp new file mode 100644 index 00000000..9da478a1 --- /dev/null +++ b/tools/xbflash.qspi/xspi.cpp @@ -0,0 +1,2241 @@ +/** + * Copyright (C) 2020 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xspi.h" +#include "pcidev.h" + +#ifdef WINDOWS +#define __func__ __FUNCTION__ +#endif + +#ifdef __GNUC__ +# define XSPI_UNUSED __attribute__((unused)) +#endif + +//#define FLASH_BASE_ADDRESS BPI_FLASH_OFFSET +#define PAGE_SIZE 256 +static const bool FOUR_BYTE_ADDRESSING = false; + +uint32_t MAX_NUM_SECTORS = 0; +uint32_t selected_sector = -1; + +//testing sizes. +#define WRITE_DATA_SIZE 128 +#define READ_DATA_SIZE 128 + + +#define COMMAND_PAGE_PROGRAM 0x02 /* Page Program command */ +#define COMMAND_QUAD_WRITE 0x32 /* Quad Input Fast Program */ +#define COMMAND_EXT_QUAD_WRITE 0x38 /* Extended quad input fast program */ +#define COMMAND_4KB_SUBSECTOR_ERASE 0x20 /* 4KB Subsector Erase command */ +#define COMMAND_32KB_SUBSECTOR_ERASE 0x52 /* 32KB Subsector Erase command */ +#define COMMAND_SECTOR_ERASE 0xD8 /* Sector Erase command */ +#define COMMAND_BULK_ERASE 0xC7 /* Bulk Erase command */ +#define COMMAND_RANDOM_READ 0x03 /* Random read command */ +#define COMMAND_DUAL_READ 0x3B /* Dual Output Fast Read */ +#define COMMAND_DUAL_IO_READ 0xBB /* Dual IO Fast Read */ +#define COMMAND_QUAD_READ 0x6B /* Quad Output Fast Read */ +#define COMMAND_QUAD_IO_READ 0xEB /* Quad IO Fast Read */ +#define COMMAND_IDCODE_READ 0x9F /* Read ID Code */ + +//Macronix Only +#define COMMAND_GBULK 0x98 /* Gang Block UnLock */ + +//read commands +#define COMMAND_STATUSREG_READ 0x05 /* Status read command */ +#define COMMAND_FLAG_STATUSREG_READ 0x70 /* Status flag read command */ +#define COMMAND_NON_VOLATILE_CFGREG_READ 0xB5 /* Non volatile configuration register read command */ +#define COMMAND_VOLATILE_CFGREG_READ 0x85 /* Volatile configuration register read command */ +#define COMMAND_ENH_VOLATILE_CFGREG_READ 0x65 /* Enhanced volatile configuration register read command */ +#define COMMAND_EXTENDED_ADDRESS_REG_READ 0xC8 /* Enhanced volatile configuration register read command */ +//write commands +#define COMMAND_STATUSREG_WRITE 0x01 /* Status read command */ +#define COMMAND_NON_VOLATILE_CFGREG_WRITE 0xB1 /* Non volatile configuration register read command */ +#define COMMAND_VOLATILE_CFGREG_WRITE 0x81 /* Volatile configuration register read command */ +#define COMMAND_ENH_VOLATILE_CFGREG_WRITE 0x61 /* Enhanced volatile configuration register read command */ +#define COMMAND_EXTENDED_ADDRESS_REG_WRITE 0xC5 /* Enhanced volatile configuration register read command */ + +#define COMMAND_CLEAR_FLAG_REGISTER 0x50 /* Clear flag register */ + +//4-byte addressing +#define ENTER_FOUR_BYTE_ADDR_MODE 0xB7 /* enter 4-byte address mode */ +#define EXIT_FOUR_BYTE_ADDR_MODE 0xE9 /* exit 4-byte address mode */ +#define FOUR_BYTE_READ 0x13 /* 4-byte read */ +#define FOUR_BYTE_FAST_READ 0x0C /* 4-byte fast read */ +#define FOUR_BYTE_DUAL_OUTPUT_FAST_READ 0x3C /* 4-byte dual output fast read */ +#define FOUR_BYTE_DUAL_IO_FAST_READ 0xBC /* 4-byte dual Input/output fast read */ +#define FOUR_BYTE_QUAD_OUTPUT_FAST_READ 0x6C /* 4-byte quad output fast read */ +#define FOUR_BYTE_QUAD_IO_FAST_READ 0xEC /* 4-byte quad output fast read */ +#define FOUR_BYTE_PAGE_PROGRAM 0x12 /* 4-byte page program */ +#define FOUR_BYTE_QUAD_INPUT_FAST_PROGRAM 0x34 /* 4-byte quad input fast program */ +#define FOUR_BYTE_QUAD_INPUT_EXT_FAST_PROGRAM 0x3E /* 4-byte quad input extended fast program */ +#define FOUR_BYTE_SECTOR_ERASE 0xDC /* 4-byte sector erase */ + +static const unsigned READ_WRITE_EXTRA_BYTES = FOUR_BYTE_ADDRESSING ? 5 :4; +static const unsigned SECTOR_ERASE_BYTES = FOUR_BYTE_ADDRESSING ? 5 :4; + + +#define IDCODE_READ_BYTES 5 + +#define DUAL_READ_DUMMY_BYTES 2 +#define QUAD_READ_DUMMY_BYTES 4 +#define DUAL_IO_READ_DUMMY_BYTES 2 +#define QUAD_IO_READ_DUMMY_BYTES 5 + +//#define READ_WRITE_EXTRA_BYTES 4 /* Read/Write extra bytes */ +//#define SECTOR_ERASE_BYTES 4 /* Sector erase extra bytes */ +#define WRITE_ENABLE_BYTES 1 /* Write Enable bytes */ +#define BULK_ERASE_BYTES 1 /* Bulk erase extra bytes */ +#define STATUS_READ_BYTES 2 /* Status read bytes count */ +#define STATUS_WRITE_BYTES 2 /* Status write bytes count */ + + + +#define NUM_SLAVES 2 +#define SLAVE_SELECT_MASK ((1 << NUM_SLAVES) -1) +/* + * Flash not busy mask in the status register of the flash device. + */ +#define FLASH_SR_IS_READY_MASK 0x01 /* Ready mask */ +#define COMMAND_WRITE_ENABLE 0x06 /* Write Enable command */ + +//SPI control reg masks. +#define XSP_CR_LOOPBACK_MASK 0x00000001 /**< Local loopback mode */ +#define XSP_CR_ENABLE_MASK 0x00000002 /**< System enable */ +#define XSP_CR_MASTER_MODE_MASK 0x00000004 /**< Enable master mode */ +#define XSP_CR_CLK_POLARITY_MASK 0x00000008 /**< Clock polarity high or low */ +#define XSP_CR_CLK_PHASE_MASK 0x00000010 /**< Clock phase 0 or 1 */ +#define XSP_CR_TXFIFO_RESET_MASK 0x00000020 /**< Reset transmit FIFO */ +#define XSP_CR_RXFIFO_RESET_MASK 0x00000040 /**< Reset receive FIFO */ +#define XSP_CR_MANUAL_SS_MASK 0x00000080 /**< Manual slave select assert */ +#define XSP_CR_TRANS_INHIBIT_MASK 0x00000100 /**< Master transaction inhibit */ + +/** + * LSB/MSB first data format select. The default data format is MSB first. + * The LSB first data format is not available in all versions of the Xilinx Spi + * Device whereas the MSB first data format is supported by all the versions of + * the Xilinx Spi Devices. Please check the HW specification to see if this + * feature is supported or not. + */ +#define XSP_CR_LSB_MSB_FIRST_MASK 0x00000200 + +//End SPI CR masks + +//SPI status reg masks +#define XSP_SR_RX_EMPTY_MASK 0x00000001 /**< Receive Reg/FIFO is empty */ +#define XSP_SR_RX_FULL_MASK 0x00000002 /**< Receive Reg/FIFO is full */ +#define XSP_SR_TX_EMPTY_MASK 0x00000004 /**< Transmit Reg/FIFO is empty */ +#define XSP_SR_TX_FULL_MASK 0x00000008 /**< Transmit Reg/FIFO is full */ +#define XSP_SR_MODE_FAULT_MASK 0x00000010 /**< Mode fault error */ +#define XSP_SR_SLAVE_MODE_MASK 0x00000020 /**< Slave mode select */ + +/* + * The following bits are available only in axi_qspi Status register. + */ +#define XSP_SR_CPOL_CPHA_ERR_MASK 0x00000040 /**< CPOL/CPHA error */ +#define XSP_SR_SLAVE_MODE_ERR_MASK 0x00000080 /**< Slave mode error */ +#define XSP_SR_MSB_ERR_MASK 0x00000100 /**< MSB Error */ +#define XSP_SR_LOOP_BACK_ERR_MASK 0x00000200 /**< Loop back error */ +#define XSP_SR_CMD_ERR_MASK 0x00000400 /**< 'Invalid cmd' error */ + + +//End SPI SR masks + +#define XSP_SRR_OFFSET 0x40 /**< Software Reset register */ +#define XSP_CR_OFFSET 0x60 /**< Control register */ +#define XSP_SR_OFFSET 0x64 /**< Status Register */ +#define XSP_DTR_OFFSET 0x68 /**< Data transmit */ +#define XSP_DRR_OFFSET 0x6C /**< Data receive */ +#define XSP_SSR_OFFSET 0x70 /**< 32-bit slave select */ +#define XSP_TFO_OFFSET 0x74 /**< Tx FIFO occupancy */ +#define XSP_RFO_OFFSET 0x78 /**< Rx FIFO occupancy */ + +#define BYTE1 0 /* Byte 1 position */ +#define BYTE2 1 /* Byte 2 position */ +#define BYTE3 2 /* Byte 3 position */ +#define BYTE4 3 /* Byte 4 position */ +#define BYTE5 4 /* Byte 5 position */ +#define BYTE6 5 /* Byte 6 position */ +#define BYTE7 6 /* Byte 7 position */ +#define BYTE8 7 /* Byte 8 position */ + +//JEDEC vendor IDs +#define MICRON_VENDOR_ID 0x20 +#define MACRONIX_VENDOR_ID 0xC2 + +/** + * SPI Software Reset Register (SRR) mask. + */ +#define XSP_SRR_RESET_MASK 0x0000000A + +// Bitstream guard information +#define NOOP 0x00000020//0x20000000 +#define DUMMY 0xFFFFFFFF +#define BUSWIDTH1 0xBB000000//0x000000BB +#define BUSWIDTH2 0x44002211//0x11220044 +#define SYNC 0x665599AA//0xAA995566 +#define TIMER 0x01200230//0x30022001 +#define WDT_ENABLE 0x02000040//0x40000002 +#define CFG_CMD 0x01800030//0x30008001 +#define LTIMER 0x11000000//0x00000011 +#define FLASH_BASE 0x040000 +#define BITSTREAM_GUARD_SIZE 0x1000 +uint32_t BITSTREAM_GUARD[] = { + DUMMY, + BUSWIDTH1, + BUSWIDTH2, + DUMMY, + DUMMY, + SYNC, + NOOP, + TIMER, + WDT_ENABLE, + NOOP, + NOOP +}; + +//---- +#define XSpi_ReadReg(RegOffset) readReg(RegOffset) +#define XSpi_WriteReg(RegOffset, RegisterValue) writeReg(RegOffset, RegisterValue) + +#define XSpi_SetControlReg(Mask) XSpi_WriteReg(XSP_CR_OFFSET, (Mask)) +#define XSpi_GetControlReg() XSpi_ReadReg(XSP_CR_OFFSET) + +#define XSpi_GetStatusReg() XSpi_ReadReg(XSP_SR_OFFSET) + +#define XSpi_SetSlaveSelectReg(Mask) XSpi_WriteReg(XSP_SSR_OFFSET, (Mask)) +#define XSpi_GetSlaveSelectReg() XSpi_ReadReg(XSP_SSR_OFFSET) + +//--- + +static uint8_t WriteBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES]; +static uint8_t ReadBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES + 4]; + +static int slave_index = 0; + +static std::array flashVendors = { + MICRON_VENDOR_ID, + MACRONIX_VENDOR_ID +}; +static int flashVendor = -1; + +static bool TEST_MODE = false; +static bool TEST_MODE_MCS_ONLY = false; + +static const uint32_t CONTROL_REG_START_STATE = XSP_CR_TRANS_INHIBIT_MASK | XSP_CR_MANUAL_SS_MASK |XSP_CR_RXFIFO_RESET_MASK + | XSP_CR_TXFIFO_RESET_MASK | XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ; + +static void clearReadBuffer(unsigned size) { + for(unsigned i =0; i < size; ++i) { + ReadBuffer[i] = 0; + } +} + +static void clearWriteBuffer(unsigned size) { + for(unsigned i =0; i < size; ++i) { + WriteBuffer[i] = 0; + } +} + +static void clearBuffers() { + clearReadBuffer(PAGE_SIZE + READ_WRITE_EXTRA_BYTES+4); + clearWriteBuffer(PAGE_SIZE + READ_WRITE_EXTRA_BYTES); +} + +//Helper functions to interleave/deinterleave nibbles +static void stripe_data(uint8_t *intrlv_buf, uint8_t *buf0, uint8_t *buf1, uint32_t num_bytes) { + for(uint32_t i=0; i> 4); + } +} + +XSPI_Flasher::~XSPI_Flasher() +{ + if (mFlashDev) + std::fclose(mFlashDev); +} + +XSPI_Flasher::XSPI_Flasher(pcidev::pci_device *dev, bool dualQSPI) +{ + mDev = dev; + + std::string err; + + mDualQSPI = dualQSPI; + flash_base = dev->get_flash_offset(); + if (flash_base == INVALID_OFFSET) + flash_base = FLASH_BASE; + mFlashDev = nullptr; + + if (std::getenv("FLASH_VIA_USER") == NULL) { + int fd = mDev->open("flash", O_RDWR); + if (fd >= 0) + mFlashDev = fdopen(fd, "r+"); + // If we can't open driver, we flash via the BAR mapping in user space + } + + if (mFlashDev != nullptr) { + std::cout << "flashing via QSPI driver" << std::endl; + } else { + std::cout << "flashing via QSPI controller located at 0x" + << std::hex <get_flash_bar_index() + << std::endl; + } +} + +unsigned XSPI_Flasher::getSector(unsigned address) { + return (address >> 24) & 0xF; +} + +bool XSPI_Flasher::setSector(unsigned address) { + uint32_t sector = getSector(address); + //Select sector before + if(sector >= MAX_NUM_SECTORS) { + std::cout << "ERROR: Invalid sector encountered" << std::endl; + std::cout << "ERROR: Bad address 0x" << std::hex << address << std::dec << std::endl; + return false; + } else if(sector == selected_sector) //Don't do anything if its already selected + return true; + + if(!writeRegister(COMMAND_EXTENDED_ADDRESS_REG_WRITE, sector, 1)) + return false; + else { + selected_sector = sector; + return true; + } +} + +int XSPI_Flasher::xclTestXSpi(int index) +{ + TEST_MODE = true; + + if(TEST_MODE_MCS_ONLY) { + //just test the mcs. + return 0; + } + + //2 slaves present, set the slave index. + slave_index = index; + + //print the IP (not of flash) control/status register. + uint32_t ControlReg = XSpi_GetControlReg(); + uint32_t StatusReg = XSpi_GetStatusReg(); + std::cout << "Boot IP Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + //Make sure it is ready to receive commands. + ControlReg = XSpi_GetControlReg(); + ControlReg = CONTROL_REG_START_STATE; + + XSpi_SetControlReg(ControlReg); + ControlReg = XSpi_GetControlReg(); + StatusReg = XSpi_GetStatusReg(); + std::cout << "Reset IP Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + //1. Testing idCode reads. + //-- + std::cout << "Testing id code " << std::endl; + if(!getFlashId()) { + std::cout << "Exiting now, as could not get correct idcode" << std::endl; + exit(-EOPNOTSUPP); + } + + std::cout << "id code successful (please verify the idcode output too" << std::endl; + std::cout << "Now reading various flash registers" << std::endl; + + //2. Testing register reads. + //Using STATUS_READ_BYTES 2 for all, TODO ? + uint8_t Cmd = COMMAND_STATUSREG_READ; + std::cout << "Testing COMMAND_STATUSREG_READ" << std::endl; + readRegister(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing COMMAND_FLAG_STATUSREG_READ" << std::endl; + Cmd = COMMAND_FLAG_STATUSREG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing COMMAND_NON_VOLATILE_CFGREG_READ" << std::endl; + Cmd = COMMAND_NON_VOLATILE_CFGREG_READ; + readRegister(Cmd, 4); + + std::cout << "Testing COMMAND_VOLATILE_CFGREG_READ" << std::endl; + Cmd = COMMAND_VOLATILE_CFGREG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing COMMAND_ENH_VOLATILE_CFGREG_READ" << std::endl; + Cmd = COMMAND_ENH_VOLATILE_CFGREG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + std::cout << "Testing COMMAND_EXTENDED_ADDRESS_REG_READ" << std::endl; + Cmd = COMMAND_EXTENDED_ADDRESS_REG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + //3. Testing simple read and write + std::cout << "Testing read and write of 16 bytes" << std::endl; + + //unsigned baseAddr = 0x007A0000; + unsigned baseAddr = 0; + unsigned Addr = 0; + unsigned AddressBytes = 3; + if(FOUR_BYTE_ADDRESSING) { + AddressBytes = 4; + writeRegister(ENTER_FOUR_BYTE_ADDR_MODE, 0, 0); + }else + writeRegister(EXIT_FOUR_BYTE_ADDR_MODE, 0, 0); + + //Verify 3 or 4 byte addressing, 0th bit == 1 => 4 byte. + std::cout << "Testing COMMAND_FLAG_STATUSREG_READ" << std::endl; + Cmd = COMMAND_FLAG_STATUSREG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + XSPI_UNUSED uint8_t WriteCmd = 0xff; + XSPI_UNUSED uint8_t ReadCmd = 0xff; + + //Test the higher two sectors - first test erase. + + //First try erasing a sector and reading a + //page (we should get FFFF ...) + for(unsigned sector = 2 ; sector <= 3; sector++) + { + clearBuffers(); + + if(!writeRegister(COMMAND_EXTENDED_ADDRESS_REG_WRITE, sector, 1)) + return false; + + std::cout << "Testing COMMAND_EXTENDED_ADDRESS_REG_READ" << std::endl; + Cmd = COMMAND_EXTENDED_ADDRESS_REG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + //Sector Erase will reset TX and RX FIFO + if(!sectorErase(Addr + baseAddr, COMMAND_4KB_SUBSECTOR_ERASE)) + return false; + + bool ready = isFlashReady(); + if(!ready){ + std::cout << "Unable to get flash ready" << std::endl; + return false; + } + + //try faster read. + if(FOUR_BYTE_ADDRESSING) { + ReadCmd = FOUR_BYTE_QUAD_OUTPUT_FAST_READ; + }else + ReadCmd = COMMAND_QUAD_READ; + + //if(!readPage(Addr, ReadCmd)) + if(!readPage(Addr + baseAddr)) + return false; + } + + clearBuffers(); + //---Erase test done + + + //---Now try writing and reading a page. + //first write 2 pages (using 4 128Mb writes) each to 2 sectors, and then read them + + //Write data + for(unsigned sector = 2 ; sector <= 3; sector++) + { + if(!writeRegister(COMMAND_EXTENDED_ADDRESS_REG_WRITE, sector, 1)) + return false; + + std::cout << "Testing COMMAND_EXTENDED_ADDRESS_REG_READ" << std::endl; + Cmd = COMMAND_EXTENDED_ADDRESS_REG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + for(int j = 0; j < 4; ++j) + { + clearBuffers(); + for(unsigned i = 0; i < WRITE_DATA_SIZE; ++ i) { + WriteBuffer[i+ AddressBytes + 1] = j + sector + i; //some random data. + } + + Addr = baseAddr + WRITE_DATA_SIZE*j; + + if(!writePage(Addr)) { + std::cout << "Write page unsuccessful, returning" << std::endl; + return -ENXIO; + } + } + + } + + clearBuffers(); + + //Read the data back, use 2 reads each of 128 bytes, twice to test 2 pages. + for(unsigned sector = 2 ; sector <= 3; sector++) + { + //Select a sector (sector 2) + if(!writeRegister(COMMAND_EXTENDED_ADDRESS_REG_WRITE, sector, 1)) + return false; + + std::cout << "Testing COMMAND_EXTENDED_ADDRESS_REG_READ" << std::endl; + Cmd = COMMAND_EXTENDED_ADDRESS_REG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + + //This read should be mix of a b c .. and Z Y X ... + for(int j = 0 ; j < 4; ++j) + { + clearBuffers(); + Addr = baseAddr + WRITE_DATA_SIZE*j; + if(!readPage(Addr)) { + std::cout << "Read page unsuccessful, returning" << std::endl; + return -ENXIO; + } + } + std::cout << "Done reading sector: " << sector << std::endl; + } + + return 0; +} + +//Method for updating QSPI (expects 1 MCS files) +int XSPI_Flasher::xclUpgradeFirmware1(std::istream& mcsStream1) { + int status = 0; + uint32_t bitstream_start_loc = 0, bitstream_shift_addr = 0; + + if (mFlashDev) + return upgradeFirmware1Drv(mcsStream1); + + //Parse MCS file for first flash device + status = parseMCS(mcsStream1); + if(status) + return status; + + //Get bitstream start location + bitstream_start_loc = recordList.front().mStartAddress; + + //Write bitstream guard if MCS file is not at address 0 + if(bitstream_start_loc != 0) { + if(!writeBitstreamGuard(bitstream_start_loc)) { + std::cout << "ERROR: Unable to set bitstream guard!" << std::endl; + return -EINVAL; + } + bitstream_shift_addr += BITSTREAM_GUARD_SIZE; + std::cout << "Enabled bitstream guard. Bitstream will not be loaded until flashing is finished." << std::endl; + } + + //Set slave index to 0 + std::cout << "Preparing flash chip 0" << std::endl; + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return -EINVAL; + } + //Program MCS file + status = programXSpi(mcsStream1, bitstream_shift_addr); + if(status) + return status; + + //Finally we clear bitstream guard if not writing to address 0 + //This will allow the bitstream to be loaded + if(bitstream_start_loc != 0) { + if(!clearBitstreamGuard(bitstream_start_loc)) { + std::cout << "ERROR: Unable to clear bitstream guard!" << std::endl; + return -EINVAL; + } + std::cout << "Cleared bitstream guard. Bitstream now active." << std::endl; + } + + return 0; + + //} +} + + +//Method for updating dual QSPI (expects 2 MCS files) +int XSPI_Flasher::xclUpgradeFirmware2(std::istream& mcsStream1, + std::istream& mcsStream2) { + int status = 0; + uint32_t bitstream_start_loc = 0, bitstream_shift_addr = 0; + + if (!mDualQSPI) { + std::cout << "ERROR: Device does not support dual QSPI!" << std::endl; + exit(-EINVAL); + } + + if (mFlashDev) + return upgradeFirmware2Drv(mcsStream1, mcsStream2); + + //Parse MCS file for first flash device + status = parseMCS(mcsStream1); + if(status) + return status; + + //Get bitstream start location + bitstream_start_loc = recordList.front().mStartAddress; + + //Write bitstream guard if MCS file is not at address 0 + if(bitstream_start_loc != 0) { + if(!writeBitstreamGuard(bitstream_start_loc)) { + std::cout << "ERROR: Unable to set bitstream guard!" << std::endl; + return -EINVAL; + } + bitstream_shift_addr += BITSTREAM_GUARD_SIZE; + std::cout << "Enabled bitstream guard. Bitstream will not be loaded until flashing is finished." << std::endl; + } + + //Set slave index to 0 + std::cout << "Preparing flash chip 0" << std::endl; + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return -EINVAL; + } + //Program first MCS file + status = programXSpi(mcsStream1, bitstream_shift_addr); + if(status) + return status; + + //Parse MCS file for second flash device + status = parseMCS(mcsStream2); + if(status) + return status; + + //Set slave index to 1 + std::cout << "Preparing flash chip 1" << std::endl; + if (!prepareXSpi(1)) { + std::cout << "ERROR: Unable to prepare the flash chip 1\n"; + return -EINVAL; + } + //Program second MCS file + status = programXSpi(mcsStream2, bitstream_shift_addr); + if(status) + return status; + + //Finally we clear bitstream guard if not writing to address 0 + //This will allow the bitstream to be loaded + if(bitstream_start_loc != 0) { + if(!clearBitstreamGuard(bitstream_start_loc)) { + std::cout << "ERROR: Unable to clear bitstream guard!" << std::endl; + return -EINVAL; + } + std::cout << "Cleared bitstream guard. Bitstream now active." << std::endl; + } + + return 0; +} + +int XSPI_Flasher::parseMCS(std::istream& mcsStream) { + clearBuffers(); + recordList.clear(); + + std::string line; + std::string startAddress; + ELARecord record; + bool endRecordFound = false; + + int lineno = 0; + while (!mcsStream.eof() && !endRecordFound) { + lineno++; + std::string line; + std::getline(mcsStream, line); + if (line.size() == 0) { + continue; + } + if (line[0] != ':') { + return -EINVAL; + } + const unsigned dataLen = std::stoi(line.substr(1, 2), 0 , 16); + const unsigned address = std::stoi(line.substr(3, 4), 0, 16); + const unsigned recordType = std::stoi(line.substr(7, 2), 0 , 16); + switch (recordType) { + case 0x00: + { + if (dataLen > 16) { + // For xilinx mcs files data length should be 16 for all records + // except for the last one which can be smaller + return -EINVAL; + } + if (address != (record.mDataCount+(record.mStartAddress & 0xFFFF))) { + if(record.mDataCount == 0) { + //First entry only. + assert(record.mStartAddress != 0); + assert(record.mEndAddress != 0); + record.mStartAddress += address; + record.mEndAddress += address; + }else { + std::cout << "Address is not contiguous ! " << std::endl; + return -EINVAL; + } + } + //if ( ((record.mEndAddress-record.mStartAddress)& 0xFFFF) != address) { + // return -EINVAL; + //} + record.mDataCount += dataLen; + record.mEndAddress += dataLen; + break; + } + case 0x01: + { + if (startAddress.size() == 0) { + break; + } + recordList.push_back(record); + endRecordFound = true; + break; + } + case 0x02: + { + assert(0); + break; + } + case 0x04: + { + if (address != 0x0) { + return -EINVAL; + } + if (dataLen != 2) { + return -EINVAL; + } + std::string newAddress = line.substr(9, dataLen * 2); + if (startAddress.size()) { + // Finish the old record + recordList.push_back(record); + } + // Start a new record + record.mStartAddress = std::stoi(newAddress, 0 , 16); + record.mStartAddress <<= 16; + record.mDataPos = mcsStream.tellg(); + record.mEndAddress = record.mStartAddress; + record.mDataCount = 0; + startAddress = newAddress; + } + } + } + + mcsStream.seekg(0); + std::cout << "INFO: ***Found " << recordList.size() << " ELA Records" << std::endl; + + return 0; +} + +unsigned XSPI_Flasher::readReg(unsigned RegOffset) { + unsigned value; + if( mDev->pcieBarRead( flash_base + RegOffset, &value, 4 ) != 0 ) { + assert(0); + throw std::runtime_error("read reg ERROR"); + } + return value; +} + +int XSPI_Flasher::writeReg(unsigned RegOffset, unsigned value) { + int status = mDev->pcieBarWrite(flash_base + RegOffset, &value, 4); + if(status != 0) { + assert(0); + throw std::runtime_error("write reg ERROR"); + } + return 0; +} + + +bool XSPI_Flasher::waitTxEmpty() { + long long delay = 0; + const timespec req = {0, 5000}; + while (delay < 30000000000) { + uint32_t StatusReg = XSpi_GetStatusReg(); + if(StatusReg & XSP_SR_TX_EMPTY_MASK ) + return true; + //If not empty, check how many bytes remain. + uint32_t Data = XSpi_ReadReg(XSP_TFO_OFFSET); + std::cout << std::hex << Data << std::dec << std::endl; + nanosleep(&req, 0); + delay += 5000; + } + std::cout << "Unable to get Tx Empty\n"; + return false; +} + +bool XSPI_Flasher::isFlashReady() { + uint32_t StatusReg; + const timespec req = {0, 5000}; + long long delay = 0; + while (delay < 30000000000) { + //StatusReg = XSpi_GetStatusReg(); + WriteBuffer[BYTE1] = COMMAND_STATUSREG_READ; + bool status = finalTransfer(WriteBuffer, ReadBuffer, STATUS_READ_BYTES); + if( !status ) { + return false; + } + //TODO: wait ? + StatusReg = ReadBuffer[1]; + if( (StatusReg & FLASH_SR_IS_READY_MASK) == 0) { + return true; + } + //TODO: Try resetting. Uncomment next line? + //XSpi_WriteReg(XSP_SRR_OFFSET, XSP_SRR_RESET_MASK); + nanosleep(&req, 0); + delay += 5000; + } + std::cout << "Unable to get Flash Ready\n"; + return false; +} + +bool XSPI_Flasher::sectorErase(unsigned Addr, unsigned erase_cmd) { + if(!isFlashReady()) + return false; + + if(!FOUR_BYTE_ADDRESSING) { + //Select sector when only using 24bit address + if(!setSector(Addr)) { + std::cout << "ERROR: Unable to set sector for sectorErase cmd" << std::endl; + return false; + } + } + + if(!writeEnable()) + return false; + + if(TEST_MODE) { + std::cout << "Testing COMMAND_FLAG_STATUSREG_READ" << std::endl; + unsigned Cmd = COMMAND_FLAG_STATUSREG_READ; + readRegister(Cmd, STATUS_READ_BYTES); + } + + uint32_t ControlReg = XSpi_GetControlReg(); + ControlReg |= XSP_CR_RXFIFO_RESET_MASK ; + ControlReg |= XSP_CR_TXFIFO_RESET_MASK; + XSpi_SetControlReg(ControlReg); + + /* + * Prepare the WriteBuffer. + */ + if(!FOUR_BYTE_ADDRESSING) { + WriteBuffer[BYTE1] = erase_cmd; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE4] = (uint8_t) (Addr); + }else { + WriteBuffer[BYTE1] = erase_cmd; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 24); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE4] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE5] = (uint8_t) Addr; + } + + if(!finalTransfer(WriteBuffer, NULL, SECTOR_ERASE_BYTES)) + return false; + + /* + * Wait till the Transfer is complete and check if there are any errors + * in the transaction.. + */ + if(!waitTxEmpty()) + return false; + + return true; +} + +bool XSPI_Flasher::bulkErase() +{ + if(!isFlashReady()) + return false; + + if(!writeEnable()) + return false; + + uint32_t ControlReg = CONTROL_REG_START_STATE; + XSpi_SetControlReg(ControlReg); + + XSPI_UNUSED uint32_t testControlReg = XSpi_GetControlReg(); + XSPI_UNUSED uint32_t testStatusReg = XSpi_GetStatusReg(); + + //2 + WriteBuffer[BYTE1] = COMMAND_BULK_ERASE; + + if(!finalTransfer(WriteBuffer, NULL, BULK_ERASE_BYTES)) + return false; + + return waitTxEmpty(); +} + +//Bitstream guard protects from partially programmed bitstreams +bool XSPI_Flasher::writeBitstreamGuard(unsigned Addr) { + unsigned char buf[WRITE_DATA_SIZE], buf1[WRITE_DATA_SIZE/2], buf2[WRITE_DATA_SIZE/2]; + unsigned char* write_buffer = &WriteBuffer[READ_WRITE_EXTRA_BYTES]; + + if(!mDualQSPI) { + memset(buf, 0xFF, sizeof(buf)); + memcpy(buf, BITSTREAM_GUARD, sizeof(BITSTREAM_GUARD)); + + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + //Clear whatever was at bitstream guard location + if(!sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE)) + return false; + + //We skip the first page of 4KB subsector so that there's a page of dummy words before bitstream guard + memcpy(write_buffer, buf, sizeof(buf)); + return writePage(Addr+WRITE_DATA_SIZE); + } else { + //Stripe data to separate nibbles for each flash chip + memset(buf, 0xFF, sizeof(buf)); + memcpy(buf, BITSTREAM_GUARD, sizeof(BITSTREAM_GUARD)); + stripe_data(buf, buf1, buf2, sizeof(buf)); + + //Select flash chip 0 + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + //Clear whatever was at bitstream guard location + if(!sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE)) + return false; + + //We skip the first page of 4KB subsector so that there's a page of dummy words before bitstream guard + memcpy(write_buffer, buf1, sizeof(buf)/2); + if(!writePage(Addr+WRITE_DATA_SIZE)) { + std::cout << "ERROR: Unable to write bitstream guard to flash chip 1!\n"; + return false; + } + + //Select flash chip 1 + if (!prepareXSpi(1)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + + //Clear whatever was at bitstream guard location + if(!sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE)) + return false; + + //We skip the first page of 4KB subsector so that there's a page of dummy words before bitstream guard + memcpy(write_buffer, buf2, sizeof(buf)/2); + if(!writePage(Addr+WRITE_DATA_SIZE)) { + std::cout << "ERROR: Unable to write bitstream guard to flash chip 0!\n"; + return false; + } + + return true; + } +} + +bool XSPI_Flasher::clearBitstreamGuard(unsigned Addr) { + //Clear whatever was at bitstream guard location + if(!mDualQSPI) { + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + //Clear whatever was at bitstream guard location + return sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE); + } else { + //Select flash chip 0 + if (!prepareXSpi(0)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + //Clear whatever was at bitstream guard location + if(!sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE)) + return false; + //Select flash chip 1 + if (!prepareXSpi(1)) { + std::cout << "ERROR: Unable to prepare the flash chip 0\n"; + return false; + } + //Clear whatever was at bitstream guard location + return sectorErase(Addr, COMMAND_4KB_SUBSECTOR_ERASE); + } +} + +bool XSPI_Flasher::writeEnable() { + uint32_t StatusReg = XSpi_GetStatusReg(); + if(StatusReg & XSP_SR_TX_FULL_MASK) { + std::cout << "Tx fifo fill during WriteEnable" << std::endl; + return false; + } + + //1 + uint32_t ControlReg = XSpi_GetControlReg(); + ControlReg |= CONTROL_REG_START_STATE; + XSpi_SetControlReg(ControlReg); + + //2 + WriteBuffer[BYTE1] = COMMAND_WRITE_ENABLE; //0x06 + + if(!finalTransfer(WriteBuffer, NULL, WRITE_ENABLE_BYTES)) + return false; + + return waitTxEmpty(); +} + +int XSPI_Flasher::macronixConfigure() +{ + bool ready = isFlashReady(); + if(!ready){ + std::cout << "ERROR: Unable to get flash ready" << std::endl; + return false; + } + + if(!writeEnable()) + return false; + + WriteBuffer[BYTE1] = COMMAND_STATUSREG_WRITE; + WriteBuffer[BYTE2] = 0x40; + WriteBuffer[BYTE3] = 0x07; + if(!finalTransfer(WriteBuffer, NULL, STATUS_WRITE_BYTES + 1)) + return false; + + ready = isFlashReady(); + if(!ready){ + std::cout << "ERROR: Unable to get flash ready" << std::endl; + return false; + } + + if(!writeEnable()) + return false; + + WriteBuffer[BYTE1] = COMMAND_GBULK; + if(!finalTransfer(WriteBuffer, NULL, BULK_ERASE_BYTES)) + return false; + + return true; +} + +bool XSPI_Flasher::getFlashId() +{ + if(!isFlashReady()) { + std::cout << "Unable to get flash ready " << std::endl; + return false; + } + + bool Status = false; + /* * Prepare the Write Buffer. */ + WriteBuffer[BYTE1] = COMMAND_IDCODE_READ; + + //First read is throwaway + Status = finalTransfer(WriteBuffer, ReadBuffer, IDCODE_READ_BYTES); + if( !Status ) { + return false; + } + + Status = finalTransfer(WriteBuffer, ReadBuffer, IDCODE_READ_BYTES); + if( !Status ) { + return false; + } + +#if defined(_debug) + for (int i = 0; i < IDCODE_READ_BYTES; i++) + std::cout << "Idcode byte[" << i << "] " << std::hex << (int)ReadBuffer[i] << std::endl; +#endif + + //Update flash vendor + for (size_t i = 0; i < flashVendors.size(); i++) + if(ReadBuffer[1] == flashVendors[i]) + flashVendor = flashVendors[i]; + + //Update max number of sector. Value of 0x18 is 1 128Mbit sector + //Note that macronix/micron use different #s + if(ReadBuffer[3] == 0xFF) + return false; + else { + switch(ReadBuffer[3]) { + case 0x38: + case 0x17: + case 0x18: + MAX_NUM_SECTORS = 1; + break; + case 0x39: + case 0x19: + MAX_NUM_SECTORS = 2; + break; + case 0x3A: + case 0x20: + MAX_NUM_SECTORS = 4; + break; + case 0x3B: + case 0x21: + MAX_NUM_SECTORS = 8; + break; + case 0x3C: + case 0x22: + MAX_NUM_SECTORS = 16; + break; + default: + std::cout << "ERROR: Unrecognized sector field! Exiting..." << std::endl; + return false; + } + } + + for (int i = 0; i < IDCODE_READ_BYTES; i++) + ReadBuffer[i] = 0; + + unsigned ffCount = 0; + for (int i = 1; i < IDCODE_READ_BYTES; i++) { + if ((unsigned int)ReadBuffer[i] == 0xff) + ffCount++; + } + + if(ffCount == IDCODE_READ_BYTES -1) + return false; + + return true; +} + + +bool XSPI_Flasher::finalTransfer(uint8_t *SendBufPtr, uint8_t *RecvBufPtr, int ByteCount) +{ + uint32_t ControlReg; + uint32_t StatusReg; + uint32_t Data = 0; + uint8_t DataWidth = 8; + uint32_t SlaveSelectMask = SLAVE_SELECT_MASK; + + uint32_t SlaveSelectReg = 0; + if(slave_index == 0) + SlaveSelectReg = ~0x01; + else if(slave_index == 1) + SlaveSelectReg = ~0x02; + + /* + * Enter a critical section from here to the end of the function since + * state is modified, an interrupt is enabled, and the control register + * is modified (r/m/w). + */ + ControlReg = XSpi_GetControlReg(); + StatusReg = XSpi_GetStatusReg(); + + if(TEST_MODE) + std::cout << "Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + + /* + * If configured as a master, be sure there is a slave select bit set + * in the slave select register. If no slaves have been selected, the + * value of the register will equal the mask. When the device is in + * loopback mode, however, no slave selects need be set. + */ + if (ControlReg & XSP_CR_MASTER_MODE_MASK) { + if ((ControlReg & XSP_CR_LOOPBACK_MASK) == 0) { + if (SlaveSelectReg == SlaveSelectMask) { + std::cout << "No slave selected" << std::endl; + return false; + } + } + } + + /* + * Set up buffer pointers. + */ + uint8_t* SendBufferPtr = SendBufPtr; + uint8_t* RecvBufferPtr = RecvBufPtr; + + int RemainingBytes = ByteCount; + unsigned int BytesTransferred = 0; + + /* + * Fill the DTR/FIFO with as many bytes as it will take (or as many as + * we have to send). We use the tx full status bit to know if the device + * can take more data. By doing this, the driver does not need to know + * the size of the FIFO or that there even is a FIFO. The downside is + * that the status register must be read each loop iteration. + */ + StatusReg = XSpi_GetStatusReg(); + if((StatusReg & (1<<10)) != 0) { + std::cout << "status reg in error situation " << std::endl; + return false; + } + + while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) && (RemainingBytes > 0)) { + if (DataWidth == 8) { + Data = *SendBufferPtr; + } else if (DataWidth == 16) { + Data = *(uint16_t *)SendBufferPtr; + } else if (DataWidth == 32){ + Data = *(uint32_t *)SendBufferPtr; + } + + if (writeReg(XSP_DTR_OFFSET, Data) != 0) { + return false; + } + SendBufferPtr += (DataWidth >> 3); + RemainingBytes -= (DataWidth >> 3); + StatusReg = XSpi_GetStatusReg(); + if((StatusReg & (1<<10)) != 0) { + std::cout << "Write command caused created error" << std::endl; + return false; + } + } + + + /* + * Set the slave select register to select the device on the SPI before + * starting the transfer of data. + */ + XSpi_SetSlaveSelectReg(SlaveSelectReg); + + ControlReg = XSpi_GetControlReg(); + StatusReg = XSpi_GetStatusReg(); + + if(TEST_MODE) + std::cout << "Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + if((StatusReg & (1<<10)) != 0) { + std::cout << "status reg in error situation: 2 " << std::endl; + return false; + } + + /* + * Start the transfer by no longer inhibiting the transmitter and + * enabling the device. For a master, this will in fact start the + * transfer, but for a slave it only prepares the device for a transfer + * that must be initiated by a master. + */ + ControlReg = XSpi_GetControlReg(); + ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK; + XSpi_SetControlReg(ControlReg); + + if(TEST_MODE) + std::cout << "Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + + //Data transfer to actual flash has already started happening here. + + { /* Polled mode of operation */ + + // poll the status register to * Transmit/Receive SPI data. + while(ByteCount > 0) + { + + /* + * Wait for the transfer to be done by polling the + * Transmit empty status bit + */ + do { + StatusReg = XSpi_GetStatusReg(); + } while ((StatusReg & XSP_SR_TX_EMPTY_MASK) == 0); + + /* + * A transmit has just completed. Process received data + * and check for more data to transmit. Always inhibit + * the transmitter while the transmit register/FIFO is + * being filled, or make sure it is stopped if we're + * done. + */ + ControlReg = XSpi_GetControlReg(); + XSpi_SetControlReg(ControlReg | XSP_CR_TRANS_INHIBIT_MASK); + + ControlReg = XSpi_GetControlReg(); + + if(TEST_MODE) + std::cout << "Control/Status " << std::hex << ControlReg << "/" << StatusReg << std::dec << std::endl; + + /* + * First get the data received as a result of the + * transmit that just completed. We get all the data + * available by reading the status register to determine + * when the Receive register/FIFO is empty. Always get + * the received data, but only fill the receive + * buffer if it points to something (the upper layer + * software may not care to receive data). + */ + StatusReg = XSpi_GetStatusReg(); + + while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) + { + //read the data. + try { + Data = readReg(XSP_DRR_OFFSET); + } catch (const std::exception& ex) { + return false; + } + + if (DataWidth == 8) { + if(RecvBufferPtr != NULL) { + *RecvBufferPtr++ = (uint8_t)Data; + } + } else if (DataWidth == 16) { + if (RecvBufferPtr != NULL){ + *(uint16_t *)RecvBufferPtr = (uint16_t)Data; + RecvBufferPtr += 2; + } + } else if (DataWidth == 32) { + if (RecvBufferPtr != NULL){ + *(uint32_t *)RecvBufferPtr = Data; + RecvBufferPtr += 4; + } + } + + BytesTransferred += (DataWidth >> 3); + ByteCount -= (DataWidth >> 3); + StatusReg = XSpi_GetStatusReg(); + if((StatusReg & (1<<10)) != 0) { + std::cout << "status reg in error situation " << std::endl; + return false; + } + } + + //If there are still unwritten bytes, then finishing writing (below code) + //and reading (above code) them. + if (RemainingBytes > 0) { + /* + * Fill the DTR/FIFO with as many bytes as it + * will take (or as many as we have to send). + * We use the Tx full status bit to know if the + * device can take more data. + * By doing this, the driver does not need to + * know the size of the FIFO or that there even + * is a FIFO. + * The downside is that the status must be read + * each loop iteration. + */ + StatusReg = XSpi_GetStatusReg(); + + while(((StatusReg & XSP_SR_TX_FULL_MASK)== 0) && (RemainingBytes > 0)) + { + if (DataWidth == 8) { + Data = *SendBufferPtr; + } else if (DataWidth == 16) { + Data = *(uint16_t *)SendBufferPtr; + } else if (DataWidth == 32) { + Data = *(uint32_t *)SendBufferPtr; + } + + if(writeReg(XSP_DTR_OFFSET, Data) != 0) { + return false; + } + + SendBufferPtr += (DataWidth >> 3); + RemainingBytes -= (DataWidth >> 3); + StatusReg = XSpi_GetStatusReg(); + if((StatusReg & (1<<10)) != 0) { + std::cout << "status reg in error situation " << std::endl; + return false; + } + } + + //Start the transfer by not inhibiting the transmitter any longer. + ControlReg = XSpi_GetControlReg(); + ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK; + XSpi_SetControlReg(ControlReg); + } + } + + //Stop the transfer by inhibiting * the transmitter. + ControlReg = XSpi_GetControlReg(); + XSpi_SetControlReg(ControlReg | XSP_CR_TRANS_INHIBIT_MASK); + + /* + * Deassert the slaves on the SPI bus when the transfer is complete, + */ + XSpi_SetSlaveSelectReg(SlaveSelectMask); + } + + return true; +} + + +bool XSPI_Flasher::writePage(unsigned Addr, uint8_t writeCmd) +{ + if(!isFlashReady()) + return false; + + if(!FOUR_BYTE_ADDRESSING) { + //Select sector when only using 24bit address + if(!setSector(Addr)) { + std::cout << "ERROR: Unable to set sector for writePage cmd" << std::endl; + return false; + } + } + + if(!writeEnable()) + return false; + + //1 : reset Tx and Rx FIFO's + uint32_t ControlReg = CONTROL_REG_START_STATE; + // uint32_t ControlReg = XSpi_GetControlReg(); + // ControlReg |= XSP_CR_RXFIFO_RESET_MASK ; + // ControlReg |= XSP_CR_TXFIFO_RESET_MASK; + XSpi_SetControlReg(ControlReg); + + uint8_t WriteCmd = writeCmd; + //2 + if(!FOUR_BYTE_ADDRESSING) { + //3 byte address mode + //COMMAND_PAGE_PROGRAM gives out all FF's + //COMMAND_EXT_QUAD_WRITE: hangs the system + if(writeCmd == 0xff) { + if(flashVendor == MACRONIX_VENDOR_ID) + WriteCmd = COMMAND_PAGE_PROGRAM; + else + WriteCmd = COMMAND_QUAD_WRITE; + } + + WriteBuffer[BYTE1] = WriteCmd; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE4] = (uint8_t) Addr; + }else { + if(writeCmd == 0xff) + WriteBuffer[BYTE1] = FOUR_BYTE_QUAD_INPUT_FAST_PROGRAM; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 24); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE4] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE5] = (uint8_t) Addr; + } + + //The data to write is already filled up, so now just write the buffer. + if(!finalTransfer(WriteBuffer, ReadBuffer, WRITE_DATA_SIZE + READ_WRITE_EXTRA_BYTES)) + return false; + + if(!waitTxEmpty()) + return false; + + + return true; + +} + +bool XSPI_Flasher::readPage(unsigned Addr, uint8_t readCmd) +{ + if(!isFlashReady()) + return false; + + if(!FOUR_BYTE_ADDRESSING) { + //Select sector when only using 24bit address + if(!setSector(Addr)) { + std::cout << "ERROR: Unable to set sector for writePage cmd" << std::endl; + return false; + } + } + + //-- + uint32_t ControlReg = CONTROL_REG_START_STATE; + // uint32_t ControlReg = XSpi_GetControlReg(); + // ControlReg |= XSP_CR_RXFIFO_RESET_MASK ; + // ControlReg |= XSP_CR_TXFIFO_RESET_MASK; + XSpi_SetControlReg(ControlReg); + + //1 : reset TX/RX FIFO's + uint8_t ReadCmd = readCmd; + + //uint8_t ReadCmd = COMMAND_RANDOM_READ; + if(!FOUR_BYTE_ADDRESSING) { + //3 byte addressing mode + if(readCmd == 0xff) + ReadCmd = COMMAND_QUAD_READ; + + //3 byte address mode + WriteBuffer[BYTE1] = ReadCmd; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE4] = (uint8_t) Addr; + }else { + if(readCmd == 0xff) + ReadCmd = FOUR_BYTE_READ; + WriteBuffer[BYTE1] = ReadCmd; + WriteBuffer[BYTE2] = (uint8_t) (Addr >> 24); + WriteBuffer[BYTE3] = (uint8_t) (Addr >> 16); + WriteBuffer[BYTE4] = (uint8_t) (Addr >> 8); + WriteBuffer[BYTE5] = (uint8_t) Addr; + } + + unsigned ByteCount = READ_DATA_SIZE; + + if (ReadCmd == COMMAND_DUAL_READ) { + ByteCount += DUAL_READ_DUMMY_BYTES; + } else if (ReadCmd == COMMAND_DUAL_IO_READ) { + ByteCount += DUAL_READ_DUMMY_BYTES; + } else if (ReadCmd == COMMAND_QUAD_IO_READ) { + ByteCount += QUAD_IO_READ_DUMMY_BYTES; + } else if ( (ReadCmd==COMMAND_QUAD_READ) || (ReadCmd==FOUR_BYTE_QUAD_OUTPUT_FAST_READ)) { + ByteCount += QUAD_READ_DUMMY_BYTES; + } + + if(!finalTransfer(WriteBuffer, ReadBuffer, ByteCount + READ_WRITE_EXTRA_BYTES)) + return false; + + if(!waitTxEmpty()) + return false; + + //reset the RXFIFO bit so. + ControlReg = XSpi_GetControlReg(); + ControlReg |= XSP_CR_RXFIFO_RESET_MASK ; + XSpi_SetControlReg(ControlReg); + + return true; + +} + +bool XSPI_Flasher::prepareXSpi(uint8_t slave_sel) +{ + if(TEST_MODE) + return true; + + //Set slave index + slave_index = slave_sel; +#if defined(_debug) + std::cout << "Slave select " << slave_sel << std::endl; +#endif + + //Resetting selected_sector + selected_sector = -1; + + XSPI_UNUSED uint32_t tControlReg = XSpi_GetControlReg(); + XSPI_UNUSED uint32_t tStatusReg = XSpi_GetStatusReg(); + +#if defined(_debug) + std::cout << "Boot Control/Status " << std::hex << tControlReg << "/" << tStatusReg << std::dec << std::endl; +#endif + + //Reset IP + XSpi_WriteReg(XSP_SRR_OFFSET, XSP_SRR_RESET_MASK); + + //Init IP settings + uint32_t ControlReg = CONTROL_REG_START_STATE; + XSpi_SetControlReg(ControlReg); + + tControlReg = XSpi_GetControlReg(); + tStatusReg = XSpi_GetStatusReg(); + +#if defined(_debug) + std::cout << "After setting start state, Control/Status " << std::hex << tControlReg << "/" << tStatusReg << std::dec << std::endl; +#endif + //-- + + if(!getFlashId()) { + std::cout << "Exiting now, as could not get correct idcode" << std::endl; + exit(-EOPNOTSUPP); + } + + if(flashVendor == MACRONIX_VENDOR_ID) { + if(!macronixConfigure()) { + std::cout << "Exiting now, Macronix configuration failed" << std::endl; + exit(-EOPNOTSUPP); + } + } + +#if defined(_debug) + std::cout << "Slave " << slave_sel << " ready" << std::endl; +#endif + + //TODO: Do we need this short delay still? + const timespec req = {0, 20000}; + nanosleep(&req, 0); + + return true; +} + +int XSPI_Flasher::programRecord(std::istream& mcsStream, const ELARecord& record) { + //TODO: decrease the sleep time. + const timespec req = {0, 20000}; + +#if defined(_debug) + std::cout << "Programming block (" << std::hex << record.mStartAddress << ", " << record.mEndAddress << std::dec << ")" << std::endl; +#endif + + assert(mcsStream.tellg() < record.mDataPos); + mcsStream.seekg(record.mDataPos, std::ios_base::beg); + unsigned char* buffer = &WriteBuffer[READ_WRITE_EXTRA_BYTES]; + int bufferIndex = 0; + int pageIndex = 0; + std::string prevLine(""); + for (unsigned index = record.mDataCount; index > 0;) { + std::string line; + std::getline(mcsStream, line); + if(TEST_MODE) + std::cout << line << std::endl; + const unsigned dataLen = std::stoi(line.substr(1, 2), 0 , 16); + index -= dataLen; + const unsigned recordType = std::stoi(line.substr(7, 2), 0 , 16); + if (recordType != 0x00) { + continue; + } + const std::string data = line.substr(9, dataLen * 2); + // Write in byte swapped order + for (unsigned i = 0; i < data.length(); i += 2) { + unsigned value = std::stoi(data.substr(i, 2), 0, 16); + buffer[bufferIndex++] = (unsigned char)value; + assert(bufferIndex <= WRITE_DATA_SIZE); + +#if 0 + //To enable byte swapping uncomment this. + // if ((bufferIndex % 4) == 0) { + // bufferIndex += 4; + // } + // assert(bufferIndex <= WRITE_DATA_SIZE); + // unsigned value = std::stoi(data.substr(i, 2), 0, 16); + // if(TEST_MODE) + // std::cout << data.substr(i, 2); + // buffer[--bufferIndex] = (unsigned char)value; + // if ((bufferIndex % 4) == 0) { + // bufferIndex += 4; + // } +#endif + if (bufferIndex == WRITE_DATA_SIZE) { + break; + } + } + + if(TEST_MODE) + std::cout << std::endl; + +#if 0 + //Uncomment if byte swapping enabled. + + //account for the last line + //which can have say 14 bytes instead of 16 + if((bufferIndex %4)!= 0) { + while ((bufferIndex %4)!= 0) { + unsigned char fillValue = 0xFF; + buffer[--bufferIndex] = fillValue; + } + bufferIndex += 4; + } + + assert((bufferIndex % 4) == 0); +#endif + + assert(bufferIndex <= WRITE_DATA_SIZE); + if (bufferIndex == WRITE_DATA_SIZE) { +#if defined(_debug) + std::cout << "writing page " << pageIndex << std::endl; +#endif + const unsigned address = std::stoi(line.substr(3, 4), 0, 16); + if(TEST_MODE) { + std::cout << (address + dataLen) << " " << (pageIndex +1)*WRITE_DATA_SIZE << std::endl; + std::cout << record.mStartAddress << " " << record.mStartAddress + pageIndex*PAGE_SIZE; + std::cout << " " << address << std::endl; + } else { + if(!writePage(record.mStartAddress + pageIndex*WRITE_DATA_SIZE)) + return -ENXIO; + clearBuffers(); + { + //debug stuff +#if defined(_debug) + if(pageIndex == 0) { + if(!readPage(record.mStartAddress + pageIndex*WRITE_DATA_SIZE)) + return -ENXIO; + clearBuffers(); + } +#endif + } + } + pageIndex++; + nanosleep(&req, 0); + bufferIndex = 0; + } + prevLine = line; + + } + if (bufferIndex) { + //Write the last page + if(TEST_MODE) { + std::cout << "writing final page " << pageIndex << std::endl; + std::cout << bufferIndex << std::endl; + std::cout << prevLine << std::endl; + } + + const unsigned address = std::stoi(prevLine.substr(3, 4), 0, 16); + const unsigned dataLen = std::stoi(prevLine.substr(1, 2), 0 , 16); + + if(TEST_MODE) + std::cout << address % WRITE_DATA_SIZE << " " << dataLen << std::endl; + + //assert( (address % WRITE_DATA_SIZE + dataLen) == bufferIndex); + + if(!TEST_MODE) { + + //Fill unused half page to FF + for(unsigned i = bufferIndex; i < WRITE_DATA_SIZE; ++i) { + buffer[i] = 0xff; + } + + if(!writePage(record.mStartAddress + pageIndex*WRITE_DATA_SIZE)) + return -ENXIO; + nanosleep(&req, 0); + clearBuffers(); + { + //debug stuff +#if defined(_debug) + if(!readPage(record.mStartAddress + pageIndex*WRITE_DATA_SIZE)) + return -ENXIO; + clearBuffers(); +#endif + } + } + } + return 0; +} + +int XSPI_Flasher::programXSpi(std::istream& mcsStream, uint32_t bitstream_shift_addr) +{ + const timespec req = {0, 20000}; + + //Now we can safely erase all subsectors + int beatCount = 0; + std::cout << "Erasing flash" << std::flush; + for (ELARecordList::iterator i = recordList.begin(), e = recordList.end(); i != e; ++i) { + beatCount++; + if(beatCount%20==0) { + std::cout << "." << std::flush; + } + + //Shift all write addresses below bitstream guard + i->mStartAddress += bitstream_shift_addr; + i->mEndAddress += bitstream_shift_addr; + + //Erase any subsectors in address range. + for(uint32_t j = i->mStartAddress; j < i->mEndAddress; j+=0x1000) { + //std::cout << "DEBUG: Erasing subsector @ 0x" << std::hex << j << std::dec << std::endl; + if(!sectorErase(j, COMMAND_4KB_SUBSECTOR_ERASE)) { + std::cout << "\nERROR: Failed to erase subsector!" << std::endl; + return -EINVAL; + } + nanosleep(&req, 0); //Pause before next sector erase + } + } + //New line after ... + std::cout << std::endl; + + //Next we program flash. Note that bitstream guard is still active + beatCount = 0; + std::cout << "Programming flash" << std::flush; + for (ELARecordList::iterator i = recordList.begin(), e = recordList.end(); i != e; ++i) + { + beatCount++; + if(beatCount%20==0) { + std::cout << "." << std::flush; + } + + if(TEST_MODE) { + std::cout << "INFO: Start address 0x" << std::hex << recordList.front().mStartAddress << std::dec << "\n"; + std::cout << "INFO: End address 0x" << std::hex << recordList.back().mEndAddress << std::dec << "\n"; + } + + bool ready = isFlashReady(); + if(!ready){ + std::cout << "\nERROR: Unable to get flash ready" << std::endl; + return -EINVAL; + } + + clearBuffers(); + + if (programRecord(mcsStream, *i)) { + std::cout << "\nERROR: Could not program the block" << std::endl; + return -EINVAL; + } + nanosleep(&req, 0); + } + std::cout << std::endl; + return 0; +} + +bool XSPI_Flasher::readRegister(unsigned commandCode, unsigned bytes) { + + if(!isFlashReady()) + return false; + + bool Status = false; + + WriteBuffer[BYTE1] = commandCode; + + Status = finalTransfer(WriteBuffer, ReadBuffer, bytes); + + if( !Status ) { + return false; + } + +#if defined(_debug) + std::cout << "Printing output (with some extra bytes of readRegister cmd)" << std::endl; +#endif + + for(unsigned i = 0; i < 5; ++ i) //Some extra bytes, no harm + { +#if defined(_debug) + std::cout << i << " " << std::hex << (int)ReadBuffer[i] << std::dec << std::endl; +#endif + ReadBuffer[i] = 0; //clear + } + //Reset the FIFO bit. + uint32_t ControlReg = XSpi_GetControlReg(); + ControlReg |= XSP_CR_RXFIFO_RESET_MASK ; + ControlReg |= XSP_CR_TXFIFO_RESET_MASK ; + XSpi_SetControlReg(ControlReg); + + return Status; +} + +//max 16 bits for nonvolative cfg register. +//If extra_bytes == 0, then only the command is sent. +bool XSPI_Flasher::writeRegister(unsigned commandCode, unsigned value, unsigned extra_bytes) { + if(!isFlashReady()) + return false; + + if(!writeEnable()) + return false; + + uint32_t ControlReg = XSpi_GetControlReg(); + ControlReg |= XSP_CR_TXFIFO_RESET_MASK; + ControlReg |= XSP_CR_RXFIFO_RESET_MASK; + XSpi_SetControlReg(ControlReg); + + bool Status = false; + + WriteBuffer[BYTE1] = commandCode; + + if(extra_bytes == 0) { + //do nothing + } else if(extra_bytes == 1) + WriteBuffer[BYTE2] = (uint8_t) (value); + else if(extra_bytes == 2) { + WriteBuffer[BYTE2] = (uint8_t) (value >> 8); + WriteBuffer[BYTE3] = (uint8_t) value; + }else { + std::cout << "ERROR: Setting more than 2 bytes" << std::endl; + assert(0); + } + + //+1 for cmd byte. + Status = finalTransfer(WriteBuffer,NULL, extra_bytes+1); + if(!Status) + return false; + + if(!waitTxEmpty()) + return false; + + return Status; +} + +// The bitstream guard location is fixed for now for all platforms. +const unsigned int dftBitstreamGuardAddress = 0x01002000; +const unsigned int bitstreamGuardSize = 4096; +// Print out "." for each pagesz bytes of data processed. +const size_t pagesz = 1024 * 1024ul; + +static inline long toAddr(const int slave, const unsigned int offset) +{ + long addr = slave; + + // Slave index is the MSB of the address. + addr <<= 56; + addr |= offset; + return addr; +} + +static int writeToFlash(std::FILE *flashDev, int slave, + const unsigned int address, const unsigned char *buf, size_t len) +{ + int ret = 0; + long addr = toAddr(slave, address); + + ret = std::fseek(flashDev, addr, SEEK_SET); + if (ret) + return ret; + + std::fwrite(buf, 1, len, flashDev); + std::fflush(flashDev); + if (ferror(flashDev)) + ret = -errno; + + return ret; +} +#if 0 +static int readFromFlash(std::FILE *flashDev, int slave, + const unsigned int address, unsigned char *buf, size_t len) +{ + int ret = 0; + long addr = toAddr(slave, address); + + ret = std::fseek(flashDev, addr, SEEK_SET); + if (ret) + return ret; + + size_t read = std::fread(buf, 1, len, flashDev); + if (ferror(flashDev)) + ret = -errno; + if (read != len) + ret = -EIO; + + return ret; +} +#endif +static int installBitstreamGuard(std::FILE *flashDev, uint32_t address, bool dual) +{ + const size_t bitstream_guard_offset = 128; + unsigned char buf[4096], buf1[4096], buf2[4096]; + int ret; + + memset(buf, 0xff, sizeof(buf)); + memset(buf, 0xff, sizeof(buf1)); + memset(buf, 0xff, sizeof(buf2)); + memcpy(buf + bitstream_guard_offset, BITSTREAM_GUARD, + sizeof(BITSTREAM_GUARD)); + stripe_data(buf, buf1, buf2, sizeof(buf)); + + if(!dual) { + ret = writeToFlash(flashDev, 0, address, buf, sizeof(buf)); + if (ret) { + std::cout << "Failed to install bitstream guard: " + << ret << std::endl; + } else { + std::cout << "Bitstream guard installed on flash @0x" + << std::hex << address << std::dec << std::endl; + } + } else { + ret = writeToFlash(flashDev, 0, address, buf1, sizeof(buf1)); + if (ret) { + std::cout << "Failed to install bitstream guard on flash 0: " + << ret << std::endl; + return ret; + } + ret = writeToFlash(flashDev, 1, address, buf2, sizeof(buf2)); + if (ret) { + std::cout << "Failed to install bitstream guard on flash 1: " + << ret << std::endl; + } else { + std::cout << "Bitstream guard installed on both flashes @0x" + << std::hex << address << std::dec << std::endl; + } + } + + return ret; +} + +static int removeBitstreamGuard(std::FILE *flashDev, uint32_t address, bool dual) +{ + unsigned char buf[4096]; + int ret; + + memset(buf, 0xff, sizeof(buf)); + + if(!dual) { + ret = writeToFlash(flashDev, 0, address, buf, sizeof(buf)); + if (ret) { + std::cout << "Failed to remove bitstream guard from flash: " + << ret << std::endl; + } else { + std::cout << "Bitstream guard removed from flash" << std::endl; + } + } else { + ret = writeToFlash(flashDev, 0, address, buf, sizeof(buf)/2); + if (ret) { + std::cout << "Failed to remove bitstream guard from flash 0: " + << ret << std::endl; + } else { + ret = writeToFlash(flashDev, 1, address, buf, sizeof(buf)/2); + if (ret) { + std::cout << "Failed to remove bitstream guard from flash 1: " + << ret << std::endl; + } else { + std::cout << "Bitstream guard removed from both flashes" + << std::endl; + } + } + } + return ret; +} + +static int bitstreamGuardAddress(uint32_t& addr, bool dual) +{ + // Shift bitstream guard address if dual QSPI + if (dual) + addr = dftBitstreamGuardAddress >> 1; + else + addr = dftBitstreamGuardAddress; + return 0; +} + +int XSPI_Flasher::revertToMFG(void) +{ + uint32_t bitstream_start_loc; + + int ret = bitstreamGuardAddress(bitstream_start_loc, mDualQSPI); + if (ret) + return ret; + + if (mFlashDev) + return installBitstreamGuard(mFlashDev, bitstream_start_loc, mDualQSPI); + + if(!writeBitstreamGuard(bitstream_start_loc)) { + std::cout << "ERROR: Unable to set bitstream guard!" << std::endl; + return -EINVAL; + } + std::cout << "Successfully set bitstream guard!" << std::endl; + return 0; +} + +static int splitMcsLine(std::string& line, + unsigned int& type, unsigned int& address, std::vector& data) +{ + int ret = 0; + + // Every line should start with ":" + if (line[0] != ':') + return -EINVAL; + + unsigned int len = std::stoi(line.substr(1, 2), NULL , 16); + type = std::stoi(line.substr(7, 2), NULL , 16); + address = std::stoi(line.substr(3, 4), NULL, 16); + data.clear(); + + switch (type) { + case 0: + if (len > 16) { + // For xilinx mcs files data length should be 16 for all records + // except for the last one which can be smaller + ret = -EINVAL; + } else { + unsigned int l = len * 2; + std::string d = line.substr(9, len * 2); + for (unsigned int i = 0; i < l; i += 2) + data.push_back(std::stoi(d.substr(i, 2), NULL, 16)); + } + break; + case 1: + break; + case 4: + if (len != 2) { + // For xilinx mcs files extended address can only be 2 bytes + ret = -EINVAL; + } + address = std::stoi(line.substr(9, len * 2), NULL, 16); + break; + default: + // Xilinx mcs files should not contain other types + ret = -EINVAL; + break; + } + + return ret; +} + +static inline unsigned int pageOffset(unsigned int addr) +{ + return (addr & 0xffff); +} + +static bool mcsStreamIsGolden(std::istream& mcsStream) +{ + std::string line; + unsigned int type = 0; + unsigned int addr = 0; + std::vector data; + + mcsStream.seekg(0, std::ios_base::beg); + + // Try to find the first address in mcs stream + while (!mcsStream.eof() && type != 4) { + std::getline(mcsStream, line); + if (!line.empty()) + splitMcsLine(line, type, addr, data); + } + + mcsStream.seekg(0, std::ios_base::beg); + return addr == 0; +} + +static int mcsStreamToBin(std::istream& mcsStream, unsigned int& currentAddr, + std::vector& buf, unsigned int& nextAddr) +{ + bool done = false; + size_t cnt = 0; + + buf.clear(); + + while (!mcsStream.eof() && !done) { + std::string line; + unsigned int type; + unsigned int addr; + std::vector data; + + std::getline(mcsStream, line); + if (line.size() == 0) + continue; + + if (splitMcsLine(line, type, addr, data) != 0) { + std::cout << "Found invalid MCS line: " << line << std::endl; + return -EINVAL; + } + + switch (type) { + case 0: + if (currentAddr == UINT_MAX) { + std::cout << "MCS missing page starting address" << std::endl; + return -EINVAL; + } else if (buf.size() == 0) { + // we're the 1st data in this page, updating the current addr + assert(pageOffset(currentAddr) == 0); + currentAddr |= addr; + } else if (pageOffset(currentAddr + buf.size()) != addr) { + std::cout << "MCS page offset is not contiguous, expecting 0x" + << std::hex << pageOffset(currentAddr) + buf.size() + << ", but, got 0x" << addr << std::dec << std::endl; + return -EINVAL; + } + // keep adding data to existing buffer + buf.insert(buf.end(), data.begin(), data.end()); + cnt += data.size(); + break; + case 1: + // end current buffer and no more + nextAddr = UINT_MAX; + done = true; + break; + case 4: + addr <<= 16; + if (currentAddr == UINT_MAX) { + currentAddr = addr; // addr of the very first page + } else if (currentAddr + buf.size() != addr) { + nextAddr = addr; // start new page + done = true; + } + // otherwise, continue to next page since page is still contiguous + break; + default: + assert(1); // shouldn't be here + break; + } + + if (cnt >= pagesz) { + std::cout << "." << std::flush; + cnt = 0; + } + } + if (cnt) // print the last "." + std::cout << "." << std::flush; + std::cout << std::endl; + + if (buf.size() > UINT_MAX) { + std::cout << "MCS bitstream is too large: 0x" << std::hex << buf.size() + << std::dec << " bytes" << std::endl; + return -EINVAL; + } + + return 0; +} + +static int writeBitstream(std::FILE *flashDev, int index, unsigned int addr, + std::vector& buf) +{ + int ret = 0; + size_t len = 0; + + // Write to flash page by page and print '.' for each write + // as progress indicator + for (size_t i = 0; ret == 0 && i < buf.size(); i += len) { + len = pagesz - ((addr + i) % pagesz); + len = std::min(len, buf.size() - i); + + std::cout << "." << std::flush; + ret = writeToFlash(flashDev, index, addr + i, buf.data() + i, len); + } + std::cout << std::endl; + return ret; +} + +static int programXSpiDrv(std::FILE *mFlashDev, std::istream& mcsStream, + int index, uint32_t addressShift, pcidev::pci_device *dev) +{ + // Parse MCS data and write each contiguous chunk to flash. + std::vector buf; + unsigned int curAddr = UINT_MAX; + unsigned int nextAddr = 0; + bool store = true; + int ret; + + while (nextAddr != UINT_MAX) { + std::cout << "Extracting bitstream from MCS data:" << std::endl; + ret = mcsStreamToBin(mcsStream, curAddr, buf, nextAddr); + if (ret) + return ret; + assert(nextAddr == UINT_MAX || pageOffset(nextAddr) == 0); + if (store) { + store = false; + } + std::cout << "Extracted " << buf.size() << " bytes from bitstream @0x" + << std::hex << curAddr << std::dec << std::endl; + + std::cout << "Writing bitstream to flash " << index << ":" << std::endl; + ret = writeBitstream(mFlashDev, index, curAddr + addressShift, buf); + if (ret) + return ret; + curAddr = nextAddr; + } + + return 0; +} + +int XSPI_Flasher::upgradeFirmware1Drv(std::istream& mcsStream) +{ + int ret = 0; + uint32_t bsGuardAddr; + + if (mcsStreamIsGolden(mcsStream)) + return programXSpiDrv(mFlashDev, mcsStream, 0, 0, mDev); + + ret = bitstreamGuardAddress(bsGuardAddr, mDualQSPI); + if (ret) + return ret; + + // Enable bitstream guard. + ret = installBitstreamGuard(mFlashDev, bsGuardAddr, mDualQSPI); + if (ret) + return ret; + + // Write MCS + ret = programXSpiDrv(mFlashDev, mcsStream, 0, bitstreamGuardSize, mDev); + if (ret) + return ret; + + // Disable bitstream guard. + return removeBitstreamGuard(mFlashDev, bsGuardAddr, mDualQSPI); +} + +int XSPI_Flasher::upgradeFirmware2Drv(std::istream& mcsStream0, + std::istream& mcsStream1) +{ + int ret = 0; + uint32_t bsGuardAddr; + + if (mcsStreamIsGolden(mcsStream0)) { + ret = programXSpiDrv(mFlashDev, mcsStream0, 0, 0, mDev); + if (ret) + return ret; + return programXSpiDrv(mFlashDev, mcsStream1, 1, 0, mDev); + } + + ret = bitstreamGuardAddress(bsGuardAddr, mDualQSPI); + if (ret) + return ret; + + // Enable bitstream guard. + ret = installBitstreamGuard(mFlashDev, bsGuardAddr, mDualQSPI); + if (ret) + return ret; + + // Write MCS + ret = programXSpiDrv(mFlashDev, mcsStream0, 0, bitstreamGuardSize, mDev); + if (ret) + return ret; + ret = programXSpiDrv(mFlashDev, mcsStream1, 1, bitstreamGuardSize, mDev); + if (ret) + return ret; + + // Disable bitstream guard. + return removeBitstreamGuard(mFlashDev, bsGuardAddr, mDualQSPI); +} diff --git a/tools/xbflash.qspi/xspi.h b/tools/xbflash.qspi/xspi.h new file mode 100644 index 00000000..9cab16ae --- /dev/null +++ b/tools/xbflash.qspi/xspi.h @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2020 Xilinx, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may + * not use this file except in compliance with the License. A copy of the + * License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +#ifndef _XSPI_H_ +#define _XSPI_H_ + +#include +#include "pcidev.h" + +#define INVALID_OFFSET ((size_t)-1) + +class XSPI_Flasher +{ + struct ELARecord + { + unsigned mStartAddress; + unsigned mEndAddress; + unsigned mDataCount; + std::streampos mDataPos; + ELARecord() : mStartAddress(0), mEndAddress(0), mDataCount(0), mDataPos(0) {} + }; + + typedef std::list ELARecordList; + ELARecordList recordList; + +public: + XSPI_Flasher(pcidev::pci_device *dev, bool dualQSPI); + ~XSPI_Flasher(); + int xclUpgradeFirmware1(std::istream& mcsStream1); + int xclUpgradeFirmware2(std::istream& mcsStream1, std::istream& mcsStream2); + int revertToMFG(void); + +private: + pcidev::pci_device *mDev; + std::FILE *mFlashDev = nullptr; + bool mDualQSPI; + + int parseMCS(std::istream& mcsStream); + + size_t flash_base; + int xclTestXSpi(int device_index); + unsigned readReg(unsigned offset); + int writeReg(unsigned regOffset, unsigned value); + bool waitTxEmpty(); + int macronixConfigure(); + bool isFlashReady(); + bool sectorErase(unsigned Addr, unsigned erase_cmd); + bool writeBitstreamGuard(unsigned Addr); + bool clearBitstreamGuard(unsigned Addr); + bool bulkErase(); + bool writeEnable(); + bool getFlashId(); + bool finalTransfer(uint8_t *sendBufPtr, uint8_t *recvBufPtr, int byteCount); + bool writePage(unsigned addr, uint8_t writeCmd = 0xff); + bool readPage(unsigned addr, uint8_t readCmd = 0xff); + bool prepareXSpi(uint8_t slave_sel); + int programRecord(std::istream& mcsStream, const ELARecord& record); + int programXSpi(std::istream& mcsStream, uint32_t bitstream_shift_addr); + bool readRegister(unsigned commandCode, unsigned bytes); + bool writeRegister(unsigned commandCode, unsigned value, unsigned bytes); + bool setSector(unsigned address); + unsigned getSector(unsigned address); + + // Upgrade firmware via driver. + int upgradeFirmware1Drv(std::istream& mcsStream1); + int upgradeFirmware2Drv(std::istream& mcsStream1, std::istream& mcsStream2); +}; + +#endif