├── .github ├── FUNDING.yml └── workflows │ ├── ox64-special.yml │ ├── ox64-test.yml │ └── ox64.yml ├── LICENSE ├── README.md ├── bl808-pine64-ox64.dtb ├── bl808-pine64-ox64.dts ├── mmu.html ├── mmu.js ├── nuttx.cfg └── nuttx.exp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lupyuen] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/lupyuen'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/ox64-special.yml: -------------------------------------------------------------------------------- 1 | ## Special Build and Test: NuttX for Ox64 2 | 3 | name: Special Test of NuttX for Ox64 4 | 5 | permissions: 6 | ## Allow publishing of GitHub Release 7 | contents: write 8 | 9 | on: 10 | 11 | ## Run every day at 0:00 UTC 12 | ## schedule: 13 | ## - cron: '0 0 * * *' 14 | 15 | ## Run on every commit to this branch 16 | push: 17 | branches: [ main ] 18 | 19 | jobs: 20 | build: 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | 26 | - name: Checkout Source Files 27 | run: | 28 | set -x # Echo commands 29 | 30 | ## TODO: Paste the GitHub Repo and Branch 31 | source=https://github.com/apache/nuttx/tree/master 32 | 33 | ## Match `https://github.com/user/repo/tree/branch` 34 | pattern='\(.*\)\/tree\/\(.*\)' 35 | 36 | ## `url` becomes `https://github.com/user/repo` 37 | ## `branch` becomes `branch` 38 | url=`echo $source | sed "s/$pattern/\1/"` 39 | branch=`echo $source | sed "s/$pattern/\2/"` 40 | 41 | ## Check out the `url` and `branch` 42 | mkdir nuttx 43 | cd nuttx 44 | git clone \ 45 | $url \ 46 | --branch $branch \ 47 | nuttx 48 | git clone https://github.com/apache/nuttx-apps apps 49 | 50 | - name: Install Build Tools 51 | run: | 52 | set -x # Echo commands 53 | sudo apt -y update 54 | sudo apt -y install \ 55 | bison flex gettext texinfo libncurses5-dev libncursesw5-dev \ 56 | gperf automake libtool pkg-config build-essential gperf genromfs \ 57 | libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \ 58 | libexpat-dev gcc-multilib g++-multilib u-boot-tools util-linux \ 59 | kconfig-frontends \ 60 | wget u-boot-tools 61 | 62 | - name: Install Toolchain 63 | run: | 64 | wget --no-check-certificate https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz 65 | tar -xf xpack-riscv-none-elf-gcc-*.tar.gz 66 | 67 | - name: Build 68 | run: | 69 | set -x # Echo commands 70 | 71 | ## Add toolchain to PATH 72 | export PATH=$PATH:$PWD/xpack-riscv-none-elf-gcc-13.2.0-2/bin 73 | cd nuttx/nuttx 74 | 75 | ## Dump the git hash 76 | hash1=`git rev-parse HEAD` 77 | pushd ../apps 78 | hash2=`git rev-parse HEAD` 79 | popd 80 | echo NuttX Source: https://github.com/apache/nuttx/tree/$hash1 >nuttx.hash 81 | echo NuttX Apps: https://github.com/apache/nuttx-apps/tree/$hash2 >>nuttx.hash 82 | cat nuttx.hash 83 | 84 | ## Show the GCC version 85 | riscv-none-elf-gcc -v 86 | 87 | ## Configure the build 88 | tools/configure.sh ox64:nsh 89 | 90 | ## Preserve the build config 91 | cp .config nuttx.config 92 | 93 | ## Run the build 94 | make 95 | 96 | ## Export the Binary Image to nuttx.bin 97 | riscv-none-elf-objcopy \ 98 | -O binary \ 99 | nuttx \ 100 | nuttx.bin 101 | 102 | ## Build Apps Filesystem 103 | make export 104 | pushd ../apps 105 | ./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz 106 | make import 107 | popd 108 | 109 | ## Generate Initial RAM Disk 110 | genromfs -f initrd -d ../apps/bin -V "NuttXBootVol" 111 | 112 | ## Prepare a Padding with 64 KB of zeroes 113 | head -c 65536 /dev/zero >/tmp/nuttx.pad 114 | 115 | ## Append Padding and Initial RAM Disk to NuttX Kernel 116 | cat nuttx.bin /tmp/nuttx.pad initrd \ 117 | >Image 118 | 119 | ## Show the size 120 | riscv-none-elf-size nuttx 121 | 122 | ## Dump the disassembly to nuttx.S 123 | riscv-none-elf-objdump \ 124 | --syms --source --reloc --demangle --line-numbers --wide \ 125 | --debugging \ 126 | nuttx \ 127 | >nuttx.S \ 128 | 2>&1 129 | 130 | ## Dump the init disassembly to init.S 131 | riscv-none-elf-objdump \ 132 | --syms --source --reloc --demangle --line-numbers --wide \ 133 | --debugging \ 134 | ../apps/bin/init \ 135 | >init.S \ 136 | 2>&1 137 | 138 | ## Dump the hello disassembly to hello.S 139 | riscv-none-elf-objdump \ 140 | --syms --source --reloc --demangle --line-numbers --wide \ 141 | --debugging \ 142 | ../apps/bin/hello \ 143 | >hello.S \ 144 | 2>&1 145 | 146 | - name: Upload Build Outputs as Artifacts 147 | uses: actions/upload-artifact@v4 148 | with: 149 | name: nuttx.zip 150 | path: | 151 | nuttx/nuttx/nuttx* 152 | nuttx/nuttx/initrd 153 | nuttx/nuttx/init.S 154 | nuttx/nuttx/hello.S 155 | nuttx/nuttx/Image 156 | nuttx/nuttx/System.map 157 | 158 | - name: Zip Build Outputs for GitHub Release 159 | run: | 160 | cd nuttx/nuttx 161 | zip nuttx.zip nuttx* initrd init.S hello.S Image System.map 162 | 163 | - name: Get Current Date 164 | id: date 165 | run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')" 166 | 167 | - name: Publish the GitHub Release 168 | uses: softprops/action-gh-release@v1 169 | with: 170 | tag_name: special-ox64-${{ steps.date.outputs.date }} 171 | draft: false 172 | prerelease: false 173 | generate_release_notes: false 174 | files: | 175 | nuttx/nuttx/nuttx.zip 176 | nuttx/nuttx/nuttx 177 | nuttx/nuttx/nuttx.S 178 | nuttx/nuttx/nuttx.bin 179 | nuttx/nuttx/nuttx.map 180 | nuttx/nuttx/nuttx.hex 181 | nuttx/nuttx/nuttx.config 182 | nuttx/nuttx/nuttx.manifest 183 | nuttx/nuttx/nuttx.hash 184 | nuttx/nuttx/nuttx-export* 185 | nuttx/nuttx/initrd 186 | nuttx/nuttx/init.S 187 | nuttx/nuttx/hello.S 188 | nuttx/nuttx/Image 189 | nuttx/nuttx/System.map 190 | 191 | - name: Install Emulator Build Tools 192 | run: | 193 | set -x # Echo commands 194 | sudo apt -y update 195 | sudo apt -y install \ 196 | expect libcurl4-openssl-dev libssl-dev zlib1g-dev libsdl2-dev wget 197 | 198 | - name: Checkout Emulator Source Files 199 | run: | 200 | set -x # Echo commands 201 | git clone https://github.com/lupyuen/ox64-tinyemu 202 | 203 | - name: Build Ox64 BL808 Emulator 204 | run: | 205 | set -x # Echo commands 206 | cd ox64-tinyemu 207 | make 208 | cp temu .. 209 | 210 | - name: Download Test Script 211 | run: | 212 | set -x # Echo commands 213 | cd nuttx/nuttx 214 | cp ../../temu . 215 | wget https://github.com/lupyuen/nuttx-ox64/raw/main/nuttx.cfg 216 | wget https://github.com/lupyuen/nuttx-ox64/raw/main/nuttx.exp 217 | chmod +x nuttx.exp 218 | ls -l 219 | cat nuttx.hash 220 | 221 | - name: Run Test 222 | run: | 223 | set -x # Echo commands 224 | cd nuttx/nuttx 225 | ./nuttx.exp 226 | -------------------------------------------------------------------------------- /.github/workflows/ox64-test.yml: -------------------------------------------------------------------------------- 1 | ## Test Mainline NuttX every day for Ox64 2 | 3 | name: Daily Test of NuttX for Ox64 4 | 5 | permissions: 6 | ## Allow publishing of GitHub Release 7 | contents: write 8 | 9 | on: 10 | 11 | ## Run every day at 0:55 UTC 12 | schedule: 13 | - cron: '55 0 * * *' 14 | 15 | ## Run on every commit to this branch 16 | ## push: 17 | ## branches: [ main ] 18 | 19 | jobs: 20 | test: 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | 26 | - name: Get Current Date 27 | id: date 28 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 29 | 30 | - name: Install Build Tools 31 | run: | 32 | sudo apt -y update 33 | sudo apt -y install \ 34 | expect libcurl4-openssl-dev libssl-dev zlib1g-dev libsdl2-dev wget 35 | 36 | - name: Checkout Source Files 37 | run: | 38 | git clone https://github.com/lupyuen/ox64-tinyemu 39 | 40 | - name: Build Ox64 BL808 Emulator 41 | run: | 42 | cd ox64-tinyemu 43 | make 44 | cp temu .. 45 | 46 | - name: Download NuttX Build and Test Script 47 | run: | 48 | wget https://github.com/lupyuen/nuttx-ox64/releases/download/nuttx-ox64-${{ steps.date.outputs.date }}/Image 49 | wget https://github.com/lupyuen/nuttx-ox64/releases/download/nuttx-ox64-${{ steps.date.outputs.date }}/nuttx.hash 50 | wget https://github.com/lupyuen/nuttx-ox64/raw/main/nuttx.cfg 51 | wget https://github.com/lupyuen/nuttx-ox64/raw/main/nuttx.exp 52 | chmod +x nuttx.exp 53 | ls -l 54 | cat nuttx.hash 55 | 56 | - name: Run Test 57 | run: | 58 | ./nuttx.exp 59 | -------------------------------------------------------------------------------- /.github/workflows/ox64.yml: -------------------------------------------------------------------------------- 1 | ## Build Mainline NuttX every day for Ox64 2 | 3 | name: Daily Build of NuttX for Ox64 4 | 5 | permissions: 6 | ## Allow publishing of GitHub Release 7 | contents: write 8 | 9 | on: 10 | 11 | ## Run every day at 0:00 UTC 12 | schedule: 13 | - cron: '0 0 * * *' 14 | 15 | ## Run on every commit to this branch 16 | ## push: 17 | ## branches: [ main ] 18 | 19 | jobs: 20 | build: 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | 26 | - name: Install Build Tools 27 | run: | 28 | sudo apt -y update 29 | sudo apt -y install \ 30 | bison flex gettext texinfo libncurses5-dev libncursesw5-dev \ 31 | gperf automake libtool pkg-config build-essential gperf genromfs \ 32 | libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \ 33 | libexpat-dev gcc-multilib g++-multilib u-boot-tools util-linux \ 34 | kconfig-frontends \ 35 | wget u-boot-tools 36 | 37 | - name: Install Toolchain 38 | run: | 39 | wget --no-check-certificate https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz 40 | tar -xf xpack-riscv-none-elf-gcc-*.tar.gz 41 | 42 | - name: Checkout Source Files 43 | run: | 44 | mkdir nuttx 45 | cd nuttx 46 | git clone https://github.com/apache/nuttx nuttx 47 | git clone https://github.com/apache/nuttx-apps apps 48 | 49 | - name: Build 50 | run: | 51 | ## Add toolchain to PATH 52 | export PATH=$PATH:$PWD/xpack-riscv-none-elf-gcc-13.2.0-2/bin 53 | cd nuttx/nuttx 54 | 55 | ## Dump the git hash 56 | hash1=`git rev-parse HEAD` 57 | pushd ../apps 58 | hash2=`git rev-parse HEAD` 59 | popd 60 | echo NuttX Source: https://github.com/apache/nuttx/tree/$hash1 >nuttx.hash 61 | echo NuttX Apps: https://github.com/apache/nuttx-apps/tree/$hash2 >>nuttx.hash 62 | cat nuttx.hash 63 | 64 | ## Show the GCC version 65 | riscv-none-elf-gcc -v 66 | 67 | ## Configure the build 68 | tools/configure.sh ox64:nsh 69 | 70 | ## Preserve the build config 71 | cp .config nuttx.config 72 | 73 | ## Run the build 74 | make 75 | 76 | ## Export the Binary Image to nuttx.bin 77 | riscv-none-elf-objcopy \ 78 | -O binary \ 79 | nuttx \ 80 | nuttx.bin 81 | 82 | ## Build Apps Filesystem 83 | make export 84 | pushd ../apps 85 | ./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz 86 | make import 87 | popd 88 | 89 | ## Generate Initial RAM Disk 90 | genromfs -f initrd -d ../apps/bin -V "NuttXBootVol" 91 | 92 | ## Prepare a Padding with 64 KB of zeroes 93 | head -c 65536 /dev/zero >/tmp/nuttx.pad 94 | 95 | ## Append Padding and Initial RAM Disk to NuttX Kernel 96 | cat nuttx.bin /tmp/nuttx.pad initrd \ 97 | >Image 98 | 99 | ## Show the size 100 | riscv-none-elf-size nuttx 101 | 102 | ## Dump the disassembly to nuttx.S 103 | riscv-none-elf-objdump \ 104 | --syms --source --reloc --demangle --line-numbers --wide \ 105 | --debugging \ 106 | nuttx \ 107 | >nuttx.S \ 108 | 2>&1 109 | 110 | ## Dump the init disassembly to init.S 111 | riscv-none-elf-objdump \ 112 | --syms --source --reloc --demangle --line-numbers --wide \ 113 | --debugging \ 114 | ../apps/bin/init \ 115 | >init.S \ 116 | 2>&1 117 | 118 | ## Dump the hello disassembly to hello.S 119 | riscv-none-elf-objdump \ 120 | --syms --source --reloc --demangle --line-numbers --wide \ 121 | --debugging \ 122 | ../apps/bin/hello \ 123 | >hello.S \ 124 | 2>&1 125 | 126 | - name: Upload Build Outputs as Artifacts 127 | uses: actions/upload-artifact@v4 128 | with: 129 | name: nuttx.zip 130 | path: | 131 | nuttx/nuttx/nuttx* 132 | nuttx/nuttx/initrd 133 | nuttx/nuttx/init.S 134 | nuttx/nuttx/hello.S 135 | nuttx/nuttx/Image 136 | nuttx/nuttx/System.map 137 | 138 | - name: Zip Build Outputs for GitHub Release 139 | run: | 140 | cd nuttx/nuttx 141 | zip nuttx.zip nuttx* initrd init.S hello.S Image System.map 142 | 143 | - name: Get Current Date 144 | id: date 145 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 146 | 147 | - name: Publish the GitHub Release 148 | uses: softprops/action-gh-release@v1 149 | with: 150 | tag_name: nuttx-ox64-${{ steps.date.outputs.date }} 151 | draft: false 152 | prerelease: false 153 | generate_release_notes: false 154 | files: | 155 | nuttx/nuttx/nuttx.zip 156 | nuttx/nuttx/nuttx 157 | nuttx/nuttx/nuttx.S 158 | nuttx/nuttx/nuttx.bin 159 | nuttx/nuttx/nuttx.map 160 | nuttx/nuttx/nuttx.hex 161 | nuttx/nuttx/nuttx.config 162 | nuttx/nuttx/nuttx.manifest 163 | nuttx/nuttx/nuttx.hash 164 | nuttx/nuttx/nuttx-export* 165 | nuttx/nuttx/initrd 166 | nuttx/nuttx/init.S 167 | nuttx/nuttx/hello.S 168 | nuttx/nuttx/Image 169 | nuttx/nuttx/System.map 170 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Pine64 Ox64 64-bit RISC-V SBC (Bouffalo Lab BL808)](https://lupyuen.github.io/images/ox64-sbc.jpg) 2 | 3 | # Apache NuttX RTOS for Pine64 Ox64 64-bit RISC-V SBC (Bouffalo Lab BL808) 4 | 5 | [![Daily Build of NuttX for Ox64](https://github.com/lupyuen/nuttx-ox64/actions/workflows/ox64.yml/badge.svg)](https://github.com/lupyuen/nuttx-ox64/actions/workflows/ox64.yml) 6 | [![Daily Test of NuttX for Ox64](https://github.com/lupyuen/nuttx-ox64/actions/workflows/ox64-test.yml/badge.svg)](https://github.com/lupyuen/nuttx-ox64/actions/workflows/ox64-test.yml) 7 | 8 | Read the articles... 9 | 10 | - ["Rust Apps on Ox64 BL808 RISC-V SBC and Apache NuttX RTOS"](https://lupyuen.github.io/articles/rust5) 11 | 12 | - ["Too many Embedded Logs? PureScript might help (Ox64 BL808 SBC / Apache NuttX RTOS)"](https://lupyuen.github.io/articles/purescript) 13 | 14 | - ["(Homage to MakeCode) Coding Ox64 BL808 SBC the Drag-n-Drop Way"](https://lupyuen.github.io/articles/quickjs2) 15 | 16 | - ["QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)"](https://lupyuen.github.io/articles/quickjs) 17 | 18 | - ["Zig runs ROM FS Filesystem in the Web Browser (thanks to Apache NuttX RTOS)"](https://lupyuen.github.io/articles/romfs) 19 | 20 | - ["TCC RISC-V Compiler runs in the Web Browser (thanks to Zig Compiler)"](https://lupyuen.github.io/articles/tcc) 21 | 22 | - ["Automated Testing with Ox64 BL808 Emulator (Apache NuttX RTOS)"](https://lupyuen.github.io/articles/tinyemu3) 23 | 24 | - ["Emulate Ox64 BL808 in the Web Browser: Experiments with TinyEMU RISC-V Emulator and Apache NuttX RTOS"](https://lupyuen.github.io/articles/tinyemu2) 25 | 26 | - ["Apache NuttX RTOS in a Web Browser? Adventures with TinyEMU and VirtIO"](https://lupyuen.github.io/articles/tinyemu) 27 | 28 | - ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim) 29 | 30 | - ["$8 RISC-V SBC on a Real-Time Operating System: Ox64 + NuttX"](https://www.hackster.io/lupyuen/8-risc-v-sbc-on-a-real-time-operating-system-ox64-nuttx-474358) 31 | 32 | - ["Fixed the UART Interrupt and Platform-Level Interrupt Controller (Ox64 BL808)"](https://lupyuen.github.io/articles/plic3) 33 | 34 | - ["RISC-V Ox64 BL808 SBC: UART Interrupt and Platform-Level Interrupt Controller (PLIC)"](https://lupyuen.github.io/articles/plic2) 35 | 36 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 37 | 38 | - ["RISC-V Ox64 BL808 SBC: Sv39 Memory Management Unit"](https://lupyuen.github.io/articles/mmu) 39 | 40 | - ["RISC-V Ox64 BL808 SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 41 | 42 | - ["RISC-V Ox64 BL808 SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 43 | 44 | _What's this BL808?_ [(Datasheet)](https://github.com/bouffalolab/bl_docs/blob/main/BL808_DS/en/BL808_DS_1.2_en.pdf) [(Reference Manual)](https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf) 45 | 46 | BL808 is a complex creature with 3 (Asymmetric) RISC-V Cores (linked via IPC)... 47 | 48 | 1. D0 Core: [T-Head C906 64-bit RV64IMAFCV](https://www.t-head.cn/product/c906?lang=en) (480 MHz) 49 | 50 | (Multimedia Core with MIPI CSI / DSI, Neural Proc Unit) 51 | 52 | (Memory Mgmt Unit is Sv39, 128/256/512 TLB table entry. Same as Star64) 53 | 54 | 1. M0 Core: [T-Head E907 32-bit RV32IMAFCP](https://www.t-head.cn/product/e907?lang=en) (320 MHz) 55 | 56 | (Wireless + Peripherals Core with WiFi, BLE, BT, Zigbee, Audio) 57 | 58 | 1. LP Core: [T-Head E902 32-bit RV32E[M]C](https://www.t-head.cn/product/e902?lang=en) (150 MHz) 59 | 60 | (Low Power Core) 61 | 62 | [(Upcoming BL606 looks similar, minus the Low Power Core)](https://en.bouffalolab.com/product/?type=detail&id=16) 63 | 64 | ![Bouffalo Lab BL808 is a complex creature with 3 (Asymmetric) RISC-V Cores](https://lupyuen.github.io/images/ox64-cores.jpg) 65 | 66 | [Pine64 Ox64](https://wiki.pine64.org/wiki/Ox64) is the dev board for BL808C. 67 | 68 | (BL808C supports MIPI CSI Cameras but not MIPI DSI Displays. Maybe someday we'll see BL808D for MIPI DSI Displays) 69 | 70 | _Is Ox64 BL808 an SBC? Or an MCU Board?_ 71 | 72 | Technically Ox64 BL808 boots 64-bit RISC-V Linux (via MicroSD), so it feels like an SBC... 73 | 74 | - ["Booting Linux on the Pine64 Ox64 SBC"](https://adventurist.me/posts/00317) 75 | 76 | - [OpenBouffalo Wiki](https://openbouffalo.org/index.php/Main_Page) 77 | 78 | - [Linux Image + OpenSBI + U-Boot Bootloader for BL808](https://github.com/openbouffalo/buildroot_bouffalo) 79 | 80 | [(Newer version?)](https://github.com/bouffalolab/buildroot_bouffalo) 81 | 82 | [(OpenSBI is the BIOS for RISC-V SBCs)](https://lupyuen.github.io/articles/sbi) 83 | 84 | - USB-C Port for Camera Module (Dual-Lane MIPI CSI) 85 | 86 | (USB-C is not for Flashing!) 87 | 88 | - USB 2.0 support for USB OTG 89 | 90 | (On-The-Go = USB Host + USB Device) 91 | 92 | But Ox64 BL808 also feels like an MCU Board... 93 | 94 | - Form Factor is similar to MCU Board 95 | 96 | - Limited Memory: 64 MB of RAM, [128 Megabits](https://pine64.com/product/128mb-ox64-sbc-available-on-december-2-2022/) (16 MB) of Flash Memory 97 | 98 | - M0 Wireless Core is 32-bit RISC-V MCU 99 | 100 | - UART Pins need a USB Serial Adapter for Flashing and Console I/O 101 | 102 | - Powered by Micro USB Port 103 | 104 | (Micro USB is not for Flashing either!) 105 | 106 | - Super Affordable: [$8 for a 64-bit RISC-V Board!](https://pine64.com/product/128mb-ox64-sbc-available-on-december-2-2022/) 107 | 108 | _Ox64 BL808 sounds a little tiny for 64-bit Linux?_ 109 | 110 | Yeah 64-bit Linux runs with Limited RAM on the D0 Multimedia Core. But most Peripherals are hosted on the M0 Wireless Core: WiFi, BLE, BT, Zigbee, Audio, ... 111 | 112 | So we flash M0 with a simple 32-bit RISC-V Firmware, to forward the Peripheral Interrupts from M0 to D0 Linux. 113 | 114 | Here are the binaries loaded into D0 Multimedia Core and M0 Wireless Core, from [buildroot_bouffalo](https://github.com/openbouffalo/buildroot_bouffalo)... 115 | 116 | * __d0_lowload_bl808_d0.bin__: This is a very basic bootloader that loads opensbi, the kernel and dts files into ram 117 | 118 | * __m0_lowload_bl808_m0.bin__: This firmware runs on M0 and forwards interupts to the D0 for several peripherals 119 | 120 | * __bl808-firmware.bin__: An image containing OpenSBI, Uboot and uboot dtb files. 121 | 122 | * __sdcard-*.tar.xz__: A tarball containing the rootfs for the image to be flashed to the SD card 123 | 124 | Perhaps Ox64 BL808 might run more efficiently with a tiny 64-bit RTOS. 125 | 126 | _Why Apache NuttX RTOS?_ 127 | 128 | It might be interesting to run Apache NuttX RTOS on both the D0 Multimedia Core and the M0 Wireless Core. Then D0 and M0 can talk over OpenAMP (Asymmetric Multi-Processing). 129 | 130 | Let's explore... 131 | 132 | # NuttX Automated Daily Build for Ox64 133 | 134 | NuttX for Ox64 is now built automatically every day via GitHub Actions. 135 | 136 | The Daily Releases are available here... 137 | 138 | - [nuttx-ox64/releases](https://github.com/lupyuen/nuttx-ox64/releases) 139 | 140 | [nuttx.hash](https://github.com/lupyuen/nuttx-ox64/releases/download/nuttx-ox64-2023-12-19/nuttx.hash) contains the Commit Hash of the NuttX Kernel and NuttX Apps repos... 141 | 142 | ```text 143 | NuttX Source: https://github.com/apache/nuttx/tree/31a6ffa15ca4e1604f80a02646a73c16570b8cbb 144 | NuttX Apps: https://github.com/apache/nuttx-apps/tree/dcfb4d066247463daed6c59ed09668004f72e1c8 145 | ``` 146 | 147 | The GitHub Actions Workflow is here... 148 | 149 | - [ox64.yml](https://github.com/lupyuen/nuttx-ox64/blob/main/.github/workflows/ox64.yml) 150 | 151 | To run the NuttX Daily Build on Ox64... 152 | 1. Download the `Image` file from the Daily Build 153 | 1. Overwrite the `Image` file in the [Ox64 Linux microSD Card](https://www.hackster.io/lupyuen/8-risc-v-sbc-on-a-real-time-operating-system-ox64-nuttx-474358) 154 | 1. Boot Ox64 with the microSD Card 155 | 156 | Maybe someday we'll do Daily Automated Testing with the Ox64 Emulator... 157 | 158 | - ["Emulate Ox64 BL808 SBC with TinyEMU"](https://github.com/lupyuen/nuttx-tinyemu#emulate-ox64-bl808-sbc-with-tinyemu) 159 | 160 | Or with a real Ox64 SBC... 161 | 162 | 1. Download the Daily Build to TFTP Server 163 | 1. Power on Ox64 with an [IKEA Smart Power Plug via Home Assistant](https://lupyuen.github.io/articles/tftp#whats-next) 164 | 1. Ox64 boots the Daily Build over TFTP 165 | 1. Capture the Automated Testing Log and write to the Release Notes 166 | 167 | [(Similar to BL602)](https://lupyuen.github.io/articles/auto) 168 | 169 | # Flashing UART vs Serial Console 170 | 171 | Read the article... 172 | 173 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 174 | 175 | ![Flashing UART vs Serial Console](https://lupyuen.github.io/images/ox64-pinout.jpg) 176 | 177 | _We need to handle TWO UART Ports on Ox64?_ 178 | 179 | Yeah don't confuse the 2 UART Ports on Ox64! Let's give the UART Ports distinctive names [(like Migi & Dali)](https://en.wikipedia.org/wiki/Migi_%26_Dali)... 180 | 181 | 1. __Ox64 Flashing UART__: Used for Flashing Ox64 182 | 183 | + Flashing UART TX is __GPIO 14__ (Physical Pin 1) 184 | + Flashing UART RX is __GPIO 15__ (Physical Pin 2) 185 | + Remember to connect GND 186 | + Baud Rate for Normal Mode: 2,000,000 (2 Mbps) 187 | + Baud Rate for Flashing Mode: 230,400 (230.4 kbps) 188 | + BL808 UART0 is controlled by the M0 Wireless Core (OpenBouffalo Firmware) 189 | 190 | 1. __Ox64 Serial Console__: Used for Linux Serial Console (plus OpenSBI and U-Boot Bootloader) 191 | 192 | + Serial Console TX is __GPIO 16__ (Physical Pin 32) 193 | + Serial Console RX is __GPIO 17__ (Physical Pin 31) 194 | + Remember to connect GND 195 | + Baud Rate: 2,000,000 (2 Mbps) 196 | + BL808 UART3 is controlled by the D0 Multimedia Core (Linux + OpenSBI + U-Boot) 197 | + Output is totally blank if OpenBouffalo Firmware [wasn't flashed correctly](https://github.com/openbouffalo/buildroot_bouffalo/issues/60), or if OpenSBI / U-Boot / Linux couldn't boot 198 | 199 | NEITHER UART Port is accessible over USB-C or Micro USB. So yeah it's totally counterintuitive. 200 | 201 | (Maybe someone can create a Stackable HAT or Breadboard, that will expose the 2 UART Ports as USB Dongles? Or a UART Switcher?) 202 | 203 | [(__For Pre-Production Ox64:__ Physical Pins are different, but GPIOs above are correct)](https://lupyuen.github.io/images/ox64-sd.jpg) 204 | 205 | _Why 2 Baud Rates for Flashing UART?_ 206 | 207 | When we power up Ox64 in __Normal Mode__: (Boot Button NOT pressed) 208 | 209 | - Flashing UART Port will show us the OpenBouffalo Firmware running on M0 Wireless Core 210 | 211 | - This M0 Firmware will forward Peripheral Interrupts to D0 Multimedia Core 212 | 213 | - M0 Firmware is hardcoded for 2 Mbps 214 | 215 | - Not really fun to watch. But we use this for testing our 2 Mbps USB Serial Adapter. 216 | 217 | When we power up Ox64 in __Flashing Mode__: (Boot Button pressed) 218 | 219 | - Ox64 is ready for Firmware Flashing by the BL DevCube GUI Tool 220 | 221 | - Firmware Flashing supports various Baud Rates: 230.4 kbps, 2 Mbps, ... 222 | 223 | - But 2 Mbps will fail on macOS. That's why we Flash Firmware at 230.4 kbps. 224 | 225 | [(Same problem when flashing BL602)](https://lupyuen.github.io/articles/flash#flash-the-firmware) 226 | 227 | _Serial Console is always 2 Mbps?_ 228 | 229 | Yeah 2 Mbps is hardcoded in Ox64 Linux. Switching to other Baud Rates will show garbled text. 230 | 231 | Thus our USB Serial Adapter must connect reliably to Ox64 at 2 Mbps. 232 | 233 | Now we flash Ox64 and boot Linux... 234 | 235 | # Flash OpenSBI and U-Boot Bootloader to Ox64 BL808 236 | 237 | Read the article... 238 | 239 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 240 | 241 | Before booting Linux on Ox64, we flash OpenSBI + U-Boot Bootloader to D0 Multimedia Core, and the Peripheral Interrupt Firmware to M0 Wireless Core. From [buildroot_bouffalo](https://github.com/openbouffalo/buildroot_bouffalo): 242 | 243 | * __d0_lowload_bl808_d0.bin__: This is a very basic bootloader that loads opensbi, the kernel and dts files into ram 244 | 245 | * __m0_lowload_bl808_m0.bin__: This firmware runs on M0 and forwards interupts to the D0 for several peripherals 246 | 247 | * __bl808-firmware.bin__: An image containing OpenSBI, Uboot and uboot dtb files. 248 | 249 | Here are the steps, based on the [Official Flashing Instructions](https://github.com/openbouffalo/buildroot_bouffalo#flashing-instructions)... 250 | 251 | 1. We tested with [Pine64 Woodpecker CH340G USB Serial Adapter](https://pine64.com/product/serial-console-woodpecker-edition/) on macOS x64. 252 | 253 | Warning: Some USB Serial Adapters [WON'T WORK!](https://wiki.pine64.org/wiki/Ox64#Compatible_UARTs_when_in_bootloader_mode) 254 | 255 | Probably because we are connecting at 2 Mbps, which might be too fast for some USB Serial Adapters. 256 | 257 | [(Like this CP2102, which shows garbled text at 2 Mbps)](https://www.lazada.sg/products/i2037772272-s11135131253.html) 258 | 259 | ![Flashing UART](https://lupyuen.github.io/images/ox64-pinout2.jpg) 260 | 261 | 1. To Test our USB Serial Adapter: Connect the USB Serial Adapter to __Ox64 Flashing UART__ (pic above)... 262 | + Flashing UART TX is __GPIO 14__ (Physical Pin 1) 263 | + Flashing UART RX is __GPIO 15__ (Physical Pin 2) 264 | + Remember to connect GND 265 | + Baud 2,000,000 (2 Mbps) 266 | 267 | Start the USB Serial Terminal (Flashing UART). 268 | 269 | Power up Ox64 via the Micro USB Port. Ox64 Green LED should light up. 270 | 271 | This Clickety Micro USB Cable is very handy for rebooting Ox64... 272 | 273 | ![Clickety Micro USB Cable](https://lupyuen.github.io/images/ox64-usb.jpg) 274 | 275 | 1. In the USB Serial Terminal (Flashing UART), we should see the Ox64 Factory Test Firmware... 276 | 277 | ```text 278 | Build:19:50:39,Nov 20 2022 279 | Copyright (c) 2022 Bouffalolab team 280 | dynamic memory init success,heap size = 93 Kbyte 281 | sig1:ffff32ff 282 | sig2:0000ffff 283 | Pong! 284 | Ping! 285 | ``` 286 | 287 | [(Source)](https://adventurist.me/posts/00317) 288 | 289 | If the text appears garbled: Try a different USB Serial Adapter. (See above) 290 | 291 | My prototype version shows this instead... 292 | 293 | ```text 294 | Init CLI with event Driven 295 | start aos loop... 296 | CLI RAW Data, c906 297 | /romfs/c906.bin not found! 298 | ``` 299 | 300 | [(Source)](https://gist.github.com/lupyuen/43676407bbced733e65566879e18732b) 301 | 302 | 1. Pre-Flash Check: Set BL808 board to programming mode 303 | + Remove the microSD Card 304 | + Press and Hold BOOT Button 305 | + Unplug and replug the Micro USB Port 306 | + Release BOOT button 307 | + Ox64 Green LED should turn on 308 | 309 | In the USB Serial Terminal (Flashing UART), we should see this... 310 | 311 | ```text 312 | . 313 | ``` 314 | 315 | Yep Ox64 is ready for flashing! 316 | 317 | 1. Now we prepare to flash: 318 | 319 | Disconnect the USB Serial Terminal (to release the Flashing UART) 320 | 321 | Set BL808 board to programming mode 322 | + Remove the microSD Card 323 | + Press and Hold BOOT Button 324 | + Unplug and replug the Micro USB Port 325 | + Release BOOT button 326 | + Ox64 Green LED should turn on 327 | 328 | 1. We download the Ox64 Binaries... 329 | 330 | - [bl808-linux-pine64_ox64_full_defconfig.tar.gz](https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz) 331 | 332 | From the latest Ox64 Linux Release... 333 | 334 | - [openbouffalo/buildroot_bouffalo (Release v1.0.1)](https://github.com/openbouffalo/buildroot_bouffalo/releases/tag/v1.0.1) 335 | 336 | Unzip the download and we should see this... 337 | 338 | ```bash 339 | → ls -l firmware 340 | 7340032 bl808-firmware.bin 341 | 31360 d0_lowload_bl808_d0.bin 342 | 65760 m0_lowload_bl808_m0.bin 343 | 43859444 sdcard-pine64_ox64_full_defconfig.img.xz 344 | ``` 345 | 346 | 1. We'll run BouffaloLab DevCube for Flashing BL808. 347 | 348 | Only Ubuntu x64, macOS and Windows are supported. 349 | 350 | TODO: How to flash BL808 on Arm64 SBCs and Pinebook Pro? Sigh. See [bflb-iot-tool / bflb-mcu-tool](https://wiki.pine64.org/wiki/Ox64#Alternative:_Open-Source_Flashing) 351 | 352 | 1. Download Bouffalo Lab DevCube 1.8.3 from... 353 | 354 | [openbouffalo.org/static-assets/bldevcube/BouffaloLabDevCube-v1.8.3.zip](https://openbouffalo.org/static-assets/bldevcube/BouffaloLabDevCube-v1.8.3.zip) 355 | 356 | [(1.8.4 and later won't work)](https://github.com/openbouffalo/buildroot_bouffalo/issues/60) 357 | 358 | May need to Grant Execute Permission... 359 | 360 | ```bash 361 | cd BouffaloLabDevCube-v1.8.3 362 | chmod +x BLDevCube-macos-x86_64 363 | ./BLDevCube-macos-x86_64 364 | ``` 365 | 366 | 1. Run DevCube, select "BL808", and switch to "MCU" page 367 | 368 | 1. M0 Group: Group0 369 | 370 | Image Addr: 0x58000000 371 | 372 | PATH: Select "m0_lowload_bl808_m0.bin" 373 | 374 | 1. D0 Group: Group0 375 | 376 | Image Addr: 0x58100000 377 | 378 | PATH: Select "d0_lowload_bl808_d0.bin" 379 | 380 | 1. Set UART Rate to 230400. 381 | 382 | Don't set to 2000000, it will fail on macOS! 383 | 384 | [(Same problem when flashing BL602)](https://lupyuen.github.io/articles/flash#flash-the-firmware) 385 | 386 | 1. Click "Create & Download" and wait until it's done 387 | 388 | [(See the log)](https://gist.github.com/lupyuen/125e15be5ed1e034bed33d16ed496d87) 389 | 390 | 1. Switch to "IOT" page 391 | 392 | 1. Enable 'Single Download' 393 | 394 | Set Address to 0x800000 395 | 396 | Select "bl808-firmware.bin" 397 | 398 | 1. Set UART Rate to 230400. 399 | 400 | Don't set to 2000000, it will fail on macOS! 401 | 402 | [(Same problem when flashing BL602)](https://lupyuen.github.io/articles/flash#flash-the-firmware) 403 | 404 | 1. Click "Create & Download" again and wait until it's done 405 | 406 | [(See the log)](https://gist.github.com/lupyuen/e8c0aca0ebd0f1eae034b0996a5b3ec3) 407 | 408 | 1. Start the USB Serial Terminal (Flashing UART at 2 Mbps). 409 | 410 | Unplug and replug the Micro USB Port. 411 | 412 | (Don't press the Boot Button!) 413 | 414 | 1. On the USB Serial Terminal (Flashing UART) we should see... 415 | 416 | ```text 417 | [I][] Powered by BouffaloLab 418 | [I][] Build:11:52:22,Mar 6 2023 419 | [I][] Copyright (c) 2023 OpenBouffalo team 420 | [I][] Copyright (c) 2022 Bouffalolab team 421 | [I][] =========== flash cfg ============== 422 | [I][] jedec id 0xEF6018 423 | [I][] mid 0xEF 424 | [I][] iomode 0x04 425 | [I][] clk delay 0x01 426 | [I][] clk invert 0x01 427 | [I][] read reg cmd0 0x05 428 | [I][] read reg cmd1 0x35 429 | [I][] write reg cmd0 0x01 430 | [I][] write reg cmd1 0x31 431 | [I][] qe write len 0x01 432 | [I][] cread support 0x00 433 | [I][] cread code 0xFF 434 | [I][] burst wrap cmd 0x77 435 | [I][] sector size: 0x04 436 | [I][] ===================================== 437 | [I][] dynamic memory init success,heap size = 156 Kbyte 438 | [I][MAIN] Starting Mailbox Handlers 439 | [I][MBOX] Forwarding Interupt SDH (33) to D0 (0x58008bbc) 440 | [I][MBOX] Forwarding Interupt GPIO (60) to D0 (0x58008d0e) 441 | [I][MAIN] Running... 442 | [I][MBOX] Mailbox IRQ Stats: 443 | [I][MBOX] .Peripheral SDH (33): 0 444 | [I][MBOX] .Peripheral GPIO (60): 0 445 | [I][MBOX] Unhandled Interupts: 0 Unhandled Signals 0 446 | ``` 447 | 448 | [(Source)](https://gist.github.com/lupyuen/52ccdf076ae294db26e837e6ffc4bafb) 449 | 450 | Yep we have flashed the OpenBouffalo Firmware successfully! 451 | 452 | ![Serial Console](https://lupyuen.github.io/images/ox64-pinout3.jpg) 453 | 454 | 1. Connect our USB Serial Adapter to __Ox64 Serial Console__: (pic above) 455 | + Serial Console TX is __GPIO 16__ (Physical Pin 32) 456 | + Serial Console RX is __GPIO 17__ (Physical Pin 31) 457 | + Remember to connect GND 458 | + Baud 2,000,000 (2 Mbps) 459 | 460 | Start the USB Serial Terminal (Serial Console). 461 | 462 | Unplug and replug the Micro USB Port. 463 | 464 | (Don't press the Boot Button!) 465 | 466 | 1. On the USB Serial Terminal (Serial Console) we should see... 467 | 468 | ```text 469 | U-Boot 2023.04-rc2 (Mar 06 2023 - 11:48:40 +0000) 470 | Card did not respond to voltage select! : -110 471 | BOOTP broadcast 472 | Retry time exceeded; starting again 473 | ``` 474 | 475 | [(Source)](https://gist.github.com/lupyuen/0b1a98781e86ba11c5538eb1e3058718) 476 | 477 | Which is OK because U-Boot Bootloader is waiting for a microSD Card. 478 | 479 | 1. If nothing appears... 480 | 481 | Check that we are using [Bouffalo Lab DevCube 1.8.3](https://openbouffalo.org/static-assets/bldevcube/BouffaloLabDevCube-v1.8.3.zip) 482 | 483 | [(1.8.4 and later won't work)](https://github.com/openbouffalo/buildroot_bouffalo/issues/60) 484 | 485 | In BL Dev Cube, UART Rate (for MCU and IoT) should be 230400. 486 | 487 | Don't set to 2000000, it will fail on macOS! 488 | 489 | [(Same problem when flashing BL602)](https://lupyuen.github.io/articles/flash#flash-the-firmware) 490 | 491 | Let's load Ox64 Linux into a microSD Card... 492 | 493 | # Boot Linux on Ox64 BL808 494 | 495 | ![Ox64 Linux in a microSD Card](https://lupyuen.github.io/images/ox64-sd.jpg) 496 | 497 | Read the article... 498 | 499 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 500 | 501 | Now that D0 Multimedia Core is flashed with OpenSBI and U-Boot Bootloader, we're ready to boot Linux on microSD! 502 | 503 | Based on the [Official Flashing Instructions](https://github.com/openbouffalo/buildroot_bouffalo#flashing-instructions)... 504 | 505 | 1. Look for the microSD Image that we downloaded earlier... 506 | 507 | ```text 508 | sdcard-pine64_ox64_full_defconfig.img.xz 509 | ``` 510 | 511 | Uncompress the file to get... 512 | 513 | ```text 514 | sdcard-pine64_ox64_full_defconfig.img 515 | ``` 516 | 517 | 1. Flash the uncompressed image to your microSD card. 518 | 519 | You can use [Balena Etcher](https://github.com/balena-io/etcher), GNOME Disks or `dd`. 520 | 521 | 1. Insert the microSD Card into Ox64. (Pic above) 522 | 523 | ![Flashing UART](https://lupyuen.github.io/images/ox64-pinout2.jpg) 524 | 525 | 1. Connect our USB Serial Adapter to __Ox64 Flashing UART__: (pic above) 526 | + Flashing UART TX is __GPIO 14__ (Physical Pin 1) 527 | + Flashing UART RX is __GPIO 15__ (Physical Pin 2) 528 | + Remember to connect GND 529 | + Baud 2,000,000 (2 Mbps) 530 | 531 | Start the USB Serial Terminal (Flashing UART). 532 | 533 | Unplug and replug the Micro USB Port. 534 | 535 | (Don't press the Boot Button!) 536 | 537 | 1. On the USB Serial Terminal (Flashing UART) we should see the same thing as earlier... 538 | 539 | ```text 540 | [I][MAIN] Starting Mailbox Handlers 541 | [I][MBOX] Forwarding Interupt SDH (33) to D0 (0x58008bbc) 542 | [I][MBOX] Forwarding Interupt GPIO (60) to D0 (0x58008d0e) 543 | [I][MAIN] Running... 544 | [I][MBOX] Mailbox IRQ Stats: 545 | [I][MBOX] .Peripheral SDH (33): 0 546 | [I][MBOX] .Peripheral GPIO (60): 0 547 | [I][MBOX] Unhandled Interupts: 0 Unhandled Signals 0 548 | ``` 549 | 550 | [(Source)](https://gist.github.com/lupyuen/52ccdf076ae294db26e837e6ffc4bafb) 551 | 552 | ![Serial Console](https://lupyuen.github.io/images/ox64-pinout3.jpg) 553 | 554 | 1. Connect our USB Serial Adapter to __Ox64 Serial Console__: (pic above) 555 | + Serial Console TX is __GPIO 16__ (Physical Pin 32) 556 | + Serial Console RX is __GPIO 17__ (Physical Pin 31) 557 | + Remember to connect GND 558 | + Baud 2,000,000 (2 Mbps) 559 | 560 | Start the USB Serial Terminal (Serial Console). 561 | 562 | Unplug and replug the Micro USB Port. 563 | 564 | (Don't press the Boot Button!) 565 | 566 | 1. On the USB Serial Terminal (Serial Console) we should see... 567 | 568 | ```text 569 | [I][] Powered by BouffaloLab 570 | [I][] Build:11:52:04,Mar 6 2023 571 | [I][] Copyright (c) 2023 OpenBouffalo team 572 | [I][] Copyright (c) 2022 Bouffalolab team 573 | [I][] dynamic memory init success,heap s[I][LowLoad] D0 start... 574 | [I][LowLoad] low_load start... 575 | [I][LowLoad] Header at 0x5d5ff000 576 | [I][LowLoad] Section dtb(1) - Start 0x5d5ff100, Size 14314 577 | [I][LowLoad] Copying DTB to 0x51ff8000...0x51ffb7ea 578 | [I][LowLoad] Done! 579 | [I][LowLoad] Section OpenSBI(2) - Start 0x5d60f100, Size 109864 580 | [I][LowLoad] Copying OpenSBI to 0x3ef80000...0x3ef9ad28 581 | [I][LowLoad] Done! 582 | [I][LowLoad] Section Kernel(3) - Start 0x5d62f100, Size 315597 583 | [I][LowLoad] Uncompressing Kernel to 0x50000000... 584 | [I][LowLoad] Done! 585 | [I][LowLoad] CRC: 00000000 586 | [I][LowLoad] load time: 61306 us 587 | [I][LowLoad] ing PMP 588 | [I][LowLoad] Booting OpenSBI at 0x000000003ef80000 with DTB at 0x51ff8000 589 | ... 590 | OpenSBI v1.2 591 | Platform Name : Pine64 Ox64 (D0) 592 | Platform Features medeleg 593 | Platform HART Count : 1 594 | Platform IPI Device : aclint-mswi 595 | Platform Timer Device : aclint-mtimer @ 1000000Hz 596 | Platform Console Device : bflb_uart 597 | Platform HSM Device : --- 598 | Platform PMU Device : --- 599 | Platform Reboot Device : --- 600 | Platform Shutdown Device : --- 601 | Firmware Base : 0x3ef80000 602 | Firmware Size : 200 KB 603 | Runtime SBI Version : 1.0 604 | ... 605 | 606 | U-Boot 2023.04-rc2 (Mar 06 2023 - 11:48:40 +0000) 607 | DRAM: 64 MiB 608 | Core: 36 devices, 17 uclasses, devicetree: board 609 | MMC: mmc@20060000: 0 610 | Loading Environment from FAT... Unable to read "uboot.env" from mmc0:2... 611 | ... 612 | Starting kernel ... 613 | Linux version 6.2.0 (runner@fv-az587-938) (riscv64-unknown-linux-gnu-gcc (Xuantie-900 linux-5.10.4 glibc gcc Toolchain V2.6.1 B-20220906) 10.2.0, GNU ld (GNU Binutils) 2.35) #1 Mon Mar 6 11:17:27 UTC 2023 614 | ... 615 | Welcome to Buildroot 616 | ox64 login: 617 | ``` 618 | 619 | [(See the Complete Log)](https://gist.github.com/lupyuen/3035a70d52d2d1d529e96f5292f54210) 620 | 621 | [(Watch the Video on YouTube)](https://youtu.be/UJ_7DyHnfDA) 622 | 623 | Yep Linux is running on Ox64 yay! (Pic below) 624 | 625 | 1. If nothing appears... 626 | 627 | Check that we are using [Bouffalo Lab DevCube 1.8.3](https://openbouffalo.org/static-assets/bldevcube/BouffaloLabDevCube-v1.8.3.zip) 628 | 629 | [(1.8.4 and later won't work)](https://github.com/openbouffalo/buildroot_bouffalo/issues/60) 630 | 631 | In BL Dev Cube, UART Rate (for MCU and IoT) should be 230400. 632 | 633 | Don't set to 2000000, it will fail on macOS! 634 | 635 | [(Same problem when flashing BL602)](https://lupyuen.github.io/articles/flash#flash-the-firmware) 636 | 637 | 1. If we see... 638 | 639 | ```text 640 | U-Boot 2023.04-rc2 (Mar 06 2023 - 11:48:40 +0000) 641 | Card did not respond to voltage select! : -110 642 | BOOTP broadcast 643 | Retry time exceeded; starting again 644 | ``` 645 | 646 | [(Source)](https://gist.github.com/lupyuen/0b1a98781e86ba11c5538eb1e3058718) 647 | 648 | Check that the microSD Card is inserted correctly. (Pic above) 649 | 650 | 1. TODO: TFTP Boot over Ethernet 651 | 652 | ![Boot Linux on Ox64 BL808](https://lupyuen.github.io/images/ox64-title.jpg) 653 | 654 | Comment by [@gamelaster](https://x.com/gamelaster/status/1719073156281798755?s=20)... 655 | 656 | > "This is not hardware specific, but flasher specific. With blisp, I was able to get faster flashing working, but this is Apple's quirk. Or maybe not? Because FreeBSD need same quirks and exact buffer sizes as Apple." 657 | 658 | Comment by [@madushan1000](https://x.com/madushan1000/status/1719069431580524720?s=20)... 659 | 660 | > "You can also use u-boot. https://github.com/openbouffalo/u-boot/releases/tag/bl808-2023-02-19 661 | You can also get rid of mailbox, but you will have to build the kernel yourself https://github.com/openbouffalo/linux/tree/bl808/all" 662 | 663 | # Forward Peripheral Interrupts 664 | 665 | Read the article... 666 | 667 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 668 | 669 | TODO 670 | 671 | ```text 672 | [I][MAIN] Starting Mailbox Handlers 673 | [I][MBOX] Forwarding Interupt SDH (33) to D0 (0x58008bbc) 674 | [I][MBOX] Forwarding Interupt GPIO (60) to D0 (0x58008d0e) 675 | [I][MAIN] Running... 676 | [I][MBOX] Mailbox IRQ Stats: 677 | [I][MBOX] .Peripheral SDH (33): 0 678 | [I][MBOX] .Peripheral GPIO (60): 0 679 | [I][MBOX] Unhandled Interupts: 0 Unhandled Signals 0 680 | ``` 681 | 682 | [(Source)](https://gist.github.com/lupyuen/52ccdf076ae294db26e837e6ffc4bafb) 683 | 684 | SDH: SD Card (SDIO) Host Controller (BL808 RM Page 561) 685 | 686 | IRQ 60: GPIO_INT0 (IRQ_NUM_BASE+44) GPIO Interrupt (BL808 RM Page 44) 687 | 688 | [GPIO_INT0_IRQn](https://github.com/bouffalolab/bl808_linux/blob/main/bl_mcu_sdk_bl808/drivers/bl808_driver/regs/bl808.h#L123) 689 | 690 | [SDH is IRQ 33: SDH_IRQn](https://github.com/bouffalolab/bl808_linux/blob/main/bl_mcu_sdk_bl808/drivers/bl808_driver/regs/bl808.h#L96) 691 | 692 | IRQ_NUM_BASE is 16 (BL808 RM Page 45) 693 | 694 | [m0_lowload](https://github.com/openbouffalo/OBLFR/tree/master/apps/m0_lowload) 695 | 696 | [d0_lowload](https://github.com/openbouffalo/OBLFR/tree/master/apps/d0_lowload) 697 | 698 | [Forward GPIO Interrupt](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L127-L135) 699 | 700 | [Forward SDH Interrupt](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L95-L103) 701 | 702 | [Setup SDH Interrupt](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L238C1-L257) 703 | 704 | Other Interrupts (unused) 705 | - [UART2](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L103-L111) 706 | - [USB](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L111-L119) 707 | - [EMAC](https://github.com/openbouffalo/OBLFR/blob/master/components/mailbox/src/oblfr_mailbox.c#L119-L127) 708 | 709 | # Inspect the Linux Image for Ox64 BL808 710 | 711 | Read the article... 712 | 713 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 714 | 715 | _Will Apache NuttX RTOS boot on Ox64 BL808?_ 716 | 717 | Let's examine the Linux Kernel Image for Ox64, and we replicate the same format for NuttX. (Which is how we ported NuttX to 64-bit RISC-V Star64 JH7110 SBC) 718 | 719 | We download the Ox64 Binaries... 720 | 721 | - [bl808-linux-pine64_ox64_full_defconfig.tar.gz](https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz) 722 | 723 | From the latest Ox64 Linux Release... 724 | 725 | - [openbouffalo/buildroot_bouffalo (Release v1.0.1)](https://github.com/openbouffalo/buildroot_bouffalo/releases/tag/v1.0.1) 726 | 727 | Unzip it and mount the SD Card Image... 728 | 729 | ```bash 730 | → ls -l sdcard-pine64_ox64_full_defconfig 731 | - 13,154,816 Image 732 | - 4,012 bl808-pine64-ox64.dtb 733 | - 4,106 bl808-sipeed-m1s.dtb 734 | - 350 boot-m1s.scr 735 | - 352 boot-pine64.scr 736 | - 352 boot.scr 737 | d 96 extlinux 738 | ``` 739 | 740 | Dump the `Image` as hex... 741 | 742 | ```bash 743 | → hexdump sdcard-pine64_ox64_full_defconfig/Image 744 | 0000000 4d 5a 6f 10 20 08 01 00 00 00 20 00 00 00 00 00 745 | 0000010 00 80 cd 00 00 00 00 00 00 00 00 00 00 00 00 00 746 | 0000020 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 747 | 0000030 52 49 53 43 56 00 00 00 52 53 43 05 40 00 00 00 748 | ``` 749 | 750 | The Linux Kernel Image begins with this __RISC-V Linux Image Header__... 751 | 752 | - [__"Boot Image Header in RISC-V Linux"__](https://www.kernel.org/doc/html/latest/riscv/boot-image-header.html) 753 | 754 | Here are the decoded bytes... 755 | 756 | 1. __code0__: Executable code 757 | 758 | (4 bytes, offset `0x00`) 759 | 760 | ```text 761 | 4d 5a 6f 10 762 | ``` 763 | 764 | 1. __code1__: Executable code 765 | 766 | (4 bytes, offset `0x04`) 767 | 768 | ```text 769 | 20 08 01 00 770 | ``` 771 | 772 | 1. __text_offset__: Image load offset, little endian 773 | 774 | (8 bytes, offset `0x08`) 775 | 776 | ```text 777 | 00 00 20 00 00 00 00 00 778 | ``` 779 | 780 | 1. __image_size__: Effective Image size, little endian 781 | 782 | (8 bytes, offset `0x10`) 783 | 784 | ```text 785 | 00 80 cd 00 00 00 00 00 786 | ``` 787 | 788 | 1. __flags__: Kernel flags, little endian 789 | 790 | (8 bytes, offset `0x18`) 791 | 792 | ```text 793 | 00 00 00 00 00 00 00 00 794 | ``` 795 | 796 | 1. __version__: Version of this header (_MinL_ _MinM_ `.` _MajL_ _MajM_) 797 | 798 | (4 bytes, offset `0x20`) 799 | 800 | ```text 801 | 02 00 00 00 802 | ``` 803 | 804 | 1. __res1__: Reserved 805 | 806 | (4 bytes, offset `0x24`) 807 | 808 | ```text 809 | 00 00 00 00 810 | ``` 811 | 812 | 1. __res2__: Reserved 813 | 814 | (8 bytes, offset `0x28`) 815 | 816 | ```text 817 | 00 00 00 00 00 00 00 00 818 | ``` 819 | 820 | 1. __magic__: Magic number, little endian, "RISCV\x00\x00\x00" 821 | 822 | (8 bytes, offset `0x30`) 823 | 824 | ```text 825 | 52 49 53 43 56 00 00 00 826 | ``` 827 | 828 | 1. __magic2__: Magic number 2, little endian, "RSC\x05" 829 | 830 | (4 bytes, offset `0x38`) 831 | 832 | ```text 833 | 52 53 43 05 834 | ``` 835 | 836 | 1. __res3__: Reserved for PE COFF offset 837 | 838 | (4 bytes, offset `0x3C`) 839 | 840 | ```text 841 | 40 00 00 00 842 | ``` 843 | 844 | Our NuttX Kernel shall __recreate this RISC-V Linux Image Header__. (Total `0x40` bytes) 845 | 846 | (Or U-Boot Bootloader might refuse to boot NuttX) 847 | 848 | Header Values are exactly the same as Star64. (Except the Image Size and Executable Code, since the Jump Address is different) 849 | 850 | Thus we simply reuse the code from NuttX Star64! 851 | 852 | # Linux Device Tree for Ox64 BL808 853 | 854 | Read the article... 855 | 856 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 857 | 858 | TODO: Dump the Device Tree 859 | 860 | ```text 861 | dtc \ 862 | -o bl808-pine64-ox64.dts \ 863 | -O dts \ 864 | -I dtb \ 865 | bl808-pine64-ox64.dtb 866 | ``` 867 | 868 | Here's the Decompiled Device Tree: [bl808-pine64-ox64.dts](bl808-pine64-ox64.dts) 869 | 870 | TODO: Transmit to UART3 at 0x30002000. Reuse the BL602 UART Driver for NuttX. 871 | 872 | ```text 873 | serial@30002000 { 874 | compatible = "bflb,bl808-uart"; 875 | reg = <0x30002000 0x1000>; 876 | interrupts = <0x14 0x04>; 877 | clocks = <0x04>; 878 | status = "okay"; 879 | phandle = <0x0a>; 880 | }; 881 | ``` 882 | 883 | [(Source)](https://github.com/lupyuen/nuttx-ox64/blob/main/bl808-pine64-ox64.dts#L89-L96) 884 | 885 | TODO: Forward the Interrupts from M0 Wireless Core to D0 Multimedia Core via Mailbox / IPC (Where are the addresses documented?) 886 | 887 | ```text 888 | mailbox@30005000 { 889 | compatible = "bflb,bl808-ipc"; 890 | reg = < 891 | 0x30005000 0x20 892 | 0x30005020 0x20 893 | 0x2000a800 0x20 894 | 0x2000a820 0x20 895 | >; 896 | interrupts = <0x36 0x04>; 897 | interrupt-controller; 898 | #interrupt-cells = <0x03>; 899 | #mbox-cells = <0x02>; 900 | status = "okay"; 901 | phandle = <0x03>; 902 | }; 903 | ``` 904 | 905 | [(Source)](https://github.com/lupyuen/nuttx-ox64/blob/main/bl808-pine64-ox64.dts#L118-L127) 906 | 907 | TODO: Print Debug Logs with OpenSBI 908 | 909 | # Boot Apache NuttX RTOS on Ox64 BL808 910 | 911 | Read the article... 912 | 913 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 914 | 915 | _What happens if we boot Star64 NuttX on Ox64 BL808?_ 916 | 917 | Let's find out! 918 | 919 | ```bash 920 | ## Download and build NuttX for Star64 921 | git clone --branch ox64 https://github.com/lupyuen2/wip-pinephone-nuttx nuttx 922 | git clone --branch ox64 https://github.com/lupyuen2/wip-pinephone-nuttx-apps apps 923 | cd nuttx 924 | tools/configure.sh star64:nsh 925 | make 926 | 927 | ## Export the Binary Image to nuttx.bin 928 | riscv64-unknown-elf-objcopy \ 929 | -O binary \ 930 | nuttx \ 931 | nuttx.bin 932 | 933 | ## TODO: Prepare the microSD for Ox64 Linux 934 | ## https://lupyuen.github.io/articles/ox64#boot-linux-on-ox64 935 | 936 | ## Copy and overwrite the `Image` file on the microSD for Ox64 Linux 937 | cp nuttx.bin Image 938 | cp Image "/Volumes/NO NAME" 939 | diskutil unmountDisk /dev/disk2 940 | ``` 941 | 942 | We boot NuttX on Ox64 via microSD... But Ox64 shows absolutely nothing! 943 | 944 | ```text 945 | Retrieving file: /extlinux/../Image 946 | append: root=PARTLABEL=rootfs rootwait rw rootfstype=ext4 console=ttyS0,2000000 loglevel=8 earlycon=sbi 947 | Retrieving file: /extlinux/../bl808-pine64-ox64.dtb 948 | ## Flattened Device Tree blob at 51ff8000 949 | Booting using the fdt blob at 0x51ff8000 950 | Working FDT set to 51ff8000 951 | Loading Device Tree to 0000000053f22000, end 0000000053f25fab ... OK 952 | Working FDT set to 53f22000 953 | Starting kernel ... 954 | ``` 955 | 956 | [(Source)](https://gist.github.com/lupyuen/8134f17502db733ce87d6fa8b00eab55) 957 | 958 | We're hoping that NuttX would crash and OpenSBI could print a meaningful Stack Trace. But nope! NuttX was probably stuck in a loop waiting for Star64 UART. 959 | 960 | Let's print to the Ox64 Serial Console in the NuttX Boot Code (in RISC-V Assembly)... 961 | 962 | # Print to Ox64 Serial Console in NuttX Boot Code 963 | 964 | Read the article... 965 | 966 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 967 | 968 | _How to print to the Ox64 Serial Console in the NuttX Boot Code? (RISC-V Assembly)_ 969 | 970 | When we compare the BL808 and BL602 Reference Manuals, we discover that BL808 UART works the same way as BL602. 971 | 972 | This is how the BL602 UART Driver prints to the Serial Console: [bl602_serial.c](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl602/bl602_serial.c#L704-L725) 973 | 974 | ```c 975 | #define BL602_UART_FIFO_WDATA_OFFSET 0x000088 /* uart_fifo_wdata */ 976 | #define BL602_UART_FIFO_WDATA(n) (BL602_UART_BASE(n) + BL602_UART_FIFO_WDATA_OFFSET) 977 | 978 | static void bl602_send(struct uart_dev_s *dev, int ch) { 979 | ... 980 | // Wait for FIFO to be empty 981 | while ((getreg32(BL602_UART_FIFO_CONFIG_1(uart_idx)) & \ 982 | UART_FIFO_CONFIG_1_TX_CNT_MASK) == 0); 983 | // Write output to FIFO 984 | putreg32(ch, BL602_UART_FIFO_WDATA(uart_idx)); 985 | } 986 | ``` 987 | 988 | So for BL808, we simply write the character to... 989 | 990 | - UART3 Base Address: 0x30002000 (from the Linux Device Tree earlier) 991 | 992 | - Offset: 0x88 993 | 994 | [Based on Star64 Debug Code](https://lupyuen.github.io/articles/nuttx2#print-to-qemu-console), we code this in RISC-V Assembly... 995 | 996 | ```text 997 | /* Load UART3 Base Address to Register t0 */ 998 | li t0, 0x30002000 999 | 1000 | /* Load `1` to Register t1 */ 1001 | li t1, 0x31 1002 | /* Store byte from Register t1 to UART3 Base Address, Offset 0x88 */ 1003 | sb t1, 0x88(t0) 1004 | 1005 | /* Load `2` to Register t1 */ 1006 | li t1, 0x32 1007 | /* Store byte from Register t1 to UART3 Base Address, Offset 0x88 */ 1008 | sb t1, 0x88(t0) 1009 | 1010 | /* Load `3` to Register t1 */ 1011 | li t1, 0x33 1012 | /* Store byte from Register t1 to UART3 Base Address, Offset 0x88 */ 1013 | sb t1, 0x88(t0) 1014 | ``` 1015 | 1016 | We insert the above code into the NuttX Boot Code: [jh7110_head.S](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/jh7110_head.S#L69-L87) 1017 | 1018 | Now NuttX prints to the Serial Console yay! (Pic below) 1019 | 1020 | ```text 1021 | Starting kernel ... 1022 | 123 1023 | ``` 1024 | 1025 | [(Source)](https://gist.github.com/lupyuen/1f895c9d57cb4e7294522ce27fea70fb) 1026 | 1027 | OpenSBI boots on Ox64 with Hart ID 0 (instead of 1), so we remove this code... 1028 | 1029 | ```text 1030 | /* We assume that OpenSBI has passed Hart ID (value 1) in Register a0. 1031 | * But NuttX expects Hart ID to start at 0, so we subtract 1. 1032 | */ 1033 | /* Previously: addi a0, a0, -1 */ 1034 | ``` 1035 | 1036 | [(Source)](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/jh7110_head.S#L89-L93) 1037 | 1038 | ![Booting Apache NuttX RTOS on Pine64 Ox64 64-bit RISC-V SBC (Bouffalo Lab BL808)](https://lupyuen.github.io/images/ox64-nuttx.png) 1039 | 1040 | # Update the NuttX Boot Address for Ox64 BL808 1041 | 1042 | Read the article... 1043 | 1044 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1045 | 1046 | _What is the Linux Boot Address for Ox64 BL808?_ 1047 | 1048 | From the [U-Boot Settings](https://gist.github.com/lupyuen/30df5a965fabf719cc52bf733e945db7)... 1049 | 1050 | ```bash 1051 | kernel_addr_r=0x50200000 1052 | ``` 1053 | 1054 | Let's update the Boot Address in NuttX: [ld.script](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/boards/risc-v/jh7110/star64/scripts/ld.script#L20-L27) 1055 | 1056 | ```text 1057 | MEMORY 1058 | { 1059 | kflash (rx) : ORIGIN = 0x50200000, LENGTH = 2048K /* w/ cache */ 1060 | ksram (rwx) : ORIGIN = 0x50400000, LENGTH = 2048K /* w/ cache */ 1061 | pgram (rwx) : ORIGIN = 0x50600000, LENGTH = 4096K /* w/ cache */ 1062 | ramdisk (rwx) : ORIGIN = 0x50A00000, LENGTH = 6M /* w/ cache */ 1063 | } 1064 | ``` 1065 | 1066 | TODO: Use up to 64 MB, the total RAM Size on Ox64 1067 | 1068 | We make the same changes to the NuttX Config: [nsh/defconfig](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/boards/risc-v/jh7110/star64/configs/nsh/defconfig) 1069 | 1070 | ```text 1071 | CONFIG_RAM_START=0x50200000 1072 | CONFIG_RAM_SIZE=1048576 1073 | CONFIG_ARCH_PGPOOL_PBASE=0x50600000 1074 | CONFIG_ARCH_PGPOOL_VBASE=0x50600000 1075 | CONFIG_ARCH_PGPOOL_SIZE=4194304 1076 | ``` 1077 | 1078 | And the Memory Mapping: [jh7110_mm_init.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ba093f2477f011ec7c5351eaba0a3002add02d6b/arch/risc-v/src/jh7110/jh7110_mm_init.c#L47-L50) 1079 | 1080 | ```c 1081 | /* Map the whole I/O memory with vaddr = paddr mappings */ 1082 | #define MMU_IO_BASE (0x00000000) 1083 | #define MMU_IO_SIZE (0x50000000) 1084 | ``` 1085 | 1086 | TODO: What's the RAM Disk Address? It's missing from [U-Boot Settings](https://gist.github.com/lupyuen/30df5a965fabf719cc52bf733e945db7) 1087 | 1088 | ```c 1089 | /* Ramdisk Load Address from U-Boot */ 1090 | #define RAMDISK_ADDR_R (0x46100000) 1091 | ``` 1092 | 1093 | [(Source)](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/jh7110_mm_init.c#L43-L45) 1094 | 1095 | NuttX shows the same output as earlier, no change... 1096 | 1097 | ```text 1098 | Starting kernel ... 1099 | 123 1100 | ``` 1101 | 1102 | [(Source)](https://gist.github.com/lupyuen/1f895c9d57cb4e7294522ce27fea70fb) 1103 | 1104 | Let's fix the NuttX UART Driver... 1105 | 1106 | # Fix the NuttX UART Driver for Ox64 BL808 1107 | 1108 | Read the article... 1109 | 1110 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1111 | 1112 | _NuttX on Ox64 has been awfully quiet. How to fix the UART Driver so that NuttX can print things?_ 1113 | 1114 | Ox64 is still running on the JH7110 UART Driver (16550). Let's make a quick patch so that we will see something on the Ox64 Serial Console... 1115 | 1116 | We hardcode the UART3 Base Address (from above) and FIFO Offset for now: [uart_16550.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/drivers/serial/uart_16550.c#L1698-L1716) 1117 | 1118 | ```c 1119 | // Write one character to the UART (polled) 1120 | static void u16550_putc(FAR struct u16550_s *priv, int ch) { 1121 | 1122 | // Hardcode the UART3 Base Address and FIFO Offset 1123 | *(volatile uint8_t *) 0x30002088 = ch; //// 1124 | 1125 | // Previously: 1126 | // while ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) == 0); 1127 | // u16550_serialout(priv, UART_THR_OFFSET, (uart_datawidth_t)ch); 1128 | } 1129 | ``` 1130 | 1131 | (Yeah the UART Buffer might overflow, we'll fix later) 1132 | 1133 | We skip the reading and writing of other UART Registers, because we'll patch them later: [uart_16550.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/drivers/serial/uart_16550.c#L604-L632) 1134 | 1135 | ```c 1136 | // Read UART Register 1137 | static inline uart_datawidth_t u16550_serialin(FAR struct u16550_s *priv, int offset) { 1138 | return 0; //// 1139 | // Commented out the rest 1140 | } 1141 | 1142 | // Write UART Register 1143 | static inline void u16550_serialout(FAR struct u16550_s *priv, int offset, uart_datawidth_t value) { 1144 | // Commented out the rest 1145 | } 1146 | ``` 1147 | 1148 | And we won't wait for UART Ready, since we're not accessing the Line Control Register: [uart_16550.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/drivers/serial/uart_16550.c#L633-L670) 1149 | 1150 | ```c 1151 | // Wait until UART is not busy. This is needed before writing to Line Control Register. 1152 | // Otherwise we will get spurious interrupts on Synopsys DesignWare 8250. 1153 | static int u16550_wait(FAR struct u16550_s *priv) { 1154 | // Nopez! No waiting for now 1155 | return OK; //// 1156 | } 1157 | ``` 1158 | 1159 | Now NuttX prints our very first Stack Dump on Ox64 yay! 1160 | 1161 | ```text 1162 | Starting kernel ... 1163 | 123 1164 | ABC 1165 | riscv_exception: EXCEPTION: Load access fault. MCAUSE: 0000000000000005, EPC: 0000000050208086, MTVAL: 000000000c002104 1166 | riscv_exception: PANIC!!! Exception = 0000000000000005 1167 | _assert: Current Version: NuttX 12.0.3 93a92a7-dirty Nov 5 2023 11:27:46 risc-v 1168 | _assert: Assertion failed panic: at file: common/riscv_exception.c:85 task: Idle_Task process: Kernel 0x50200e28 1169 | up_dump_register: EPC: 0000000050208086 1170 | up_dump_register: A0: 000000000c002104 A1: ffffffffffffffff A2: 0000000000000001 A3: 0000000000000003 1171 | up_dump_register: A4: ffffffffffffffff A5: 8000000200046000 A6: 0000000000000000 A7: fffffffffffffff8 1172 | up_dump_register: T0: 00000000502000a8 T1: 0000000000000007 T2: 656d616e2d64746d T3: 0000000050407b10 1173 | up_dump_register: T4: 0000000050407b08 T5: 0000000053f23fff T6: 0000000053f33870 1174 | up_dump_register: S0: 0000000000000000 S1: 0000000050400140 S2: 0000000000000001 S3: 8000000200046002 1175 | up_dump_register: S4: 0000000050400070 S5: 00000000000001b6 S6: 0000000000000000 S7: 0000000000000000 1176 | up_dump_register: S8: 0000000053f7a15c S9: 0000000053fcf2e0 S10: 0000000000000001 S11: 0000000000000003 1177 | up_dump_register: SP: 0000000050407a00 FP: 0000000000000000 TP: 0000000000000000 RA: 0000000050204064 1178 | ``` 1179 | 1180 | [(Source)](https://gist.github.com/lupyuen/36b8c47abc2632063ca5cdebb958e3e8) 1181 | 1182 | Let's look up the RISC-V Exception Code Address 0x50208086 in our RISC-V Disassembly... 1183 | 1184 | ```text 1185 | EXCEPTION: Load access fault 1186 | MCAUSE: 0000000000000005 1187 | EPC: 0000000050208086 1188 | MTVAL: 000000000c002104 1189 | ``` 1190 | 1191 | And the offending Data Address 0xc002104. (Which looks very familiar!) 1192 | 1193 | ![NuttX prints our very first Stack Dump on Ox64 yay!](https://lupyuen.github.io/images/ox64-stack.png) 1194 | 1195 | # Platform-Level Interrupt Controller for Ox64 BL808 1196 | 1197 | Read the article... 1198 | 1199 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1200 | 1201 | _Why did NuttX crash with this RISC-V Exception?_ 1202 | 1203 | ```text 1204 | EXCEPTION: Load access fault 1205 | MCAUSE: 0000000000000005 1206 | EPC: 0000000050208086 1207 | MTVAL: 000000000c002104 1208 | ``` 1209 | 1210 | NuttX crashed when it tried to access invalid Data Address 0xc002104 from Code Address 0x50208086. 1211 | 1212 | We look up Code Address 0x50208086 in our NuttX Disassembly... 1213 | 1214 | ```text 1215 | 000000005020807a : 1216 | up_irq_save(): 1217 | nuttx/include/arch/irq.h:689 1218 | 5020807a: 4789 li a5,2 1219 | 5020807c: 1007b7f3 csrrc a5,sstatus,a5 1220 | modifyreg32(): 1221 | nuttx/arch/risc-v/src/common/riscv_modifyreg32.c:52 1222 | { 1223 | irqstate_t flags; 1224 | uint32_t regval; 1225 | 1226 | flags = spin_lock_irqsave(NULL); 1227 | regval = getreg32(addr); 1228 | 50208080: 4118 lw a4,0(a0) 1229 | nuttx/arch/risc-v/src/common/riscv_modifyreg32.c:53 1230 | regval &= ~clearbits; 1231 | 50208082: fff5c593 not a1,a1 1232 | nuttx/arch/risc-v/src/common/riscv_modifyreg32.c:52 1233 | regval = getreg32(addr); 1234 | 50208086: 2701 sext.w a4,a4 1235 | ``` 1236 | 1237 | Which comes from here: [riscv_modifyreg32.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/common/riscv_modifyreg32.c#L38-L57) 1238 | 1239 | ```c 1240 | // Atomically modify the specified bits in a memory mapped register 1241 | void modifyreg32(uintptr_t addr, uint32_t clearbits, uint32_t setbits) { 1242 | irqstate_t flags; 1243 | uint32_t regval; 1244 | 1245 | flags = spin_lock_irqsave(NULL); 1246 | // Crashes here because `addr` is invalid... 1247 | regval = getreg32(addr); 1248 | regval &= ~clearbits; 1249 | regval |= setbits; 1250 | putreg32(regval, addr); 1251 | spin_unlock_irqrestore(NULL, flags); 1252 | } 1253 | ``` 1254 | 1255 | It's trying to modify a Memory-Mapped Register, and crashed. 1256 | 1257 | _But what Memory-Mapped Register?_ 1258 | 1259 | The offending Data Address 0xc002104 actually comes from Star64 PLIC! (Platform-Level Interrupt Controller) 1260 | 1261 | ```c 1262 | // From https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/hardware/jh7110_memorymap.h#L30 1263 | #define JH7110_PLIC_BASE 0x0c000000 1264 | 1265 | // From https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/hardware/jh7110_plic.h#L34-L49 1266 | /* Interrupt Priority */ 1267 | #define JH7110_PLIC_PRIORITY (JH7110_PLIC_BASE + 0x000000) 1268 | 1269 | /* Hart 1 S-Mode Interrupt Enable */ 1270 | #define JH7110_PLIC_ENABLE1 (JH7110_PLIC_BASE + 0x002100) 1271 | #define JH7110_PLIC_ENABLE2 (JH7110_PLIC_BASE + 0x002104) 1272 | 1273 | /* Hart 1 S-Mode Priority Threshold */ 1274 | #define JH7110_PLIC_THRESHOLD (JH7110_PLIC_BASE + 0x202000) 1275 | 1276 | /* Hart 1 S-Mode Claim / Complete */ 1277 | #define JH7110_PLIC_CLAIM (JH7110_PLIC_BASE + 0x202004) 1278 | ``` 1279 | 1280 | The PLIC Base Address is different for BL808, let's change it. 1281 | 1282 | _What's the PLIC Base Address in Ox64 BL808?_ 1283 | 1284 | PLIC Base Address is 0xe0000000, according to the Linux Device Tree: [bl808-pine64-ox64.dts](https://github.com/lupyuen/nuttx-ox64/blob/main/bl808-pine64-ox64.dts#L129-L138) 1285 | 1286 | ```text 1287 | interrupt-controller@e0000000 { 1288 | compatible = "thead,c900-plic"; 1289 | reg = <0xe0000000 0x4000000>; 1290 | interrupts-extended = <0x06 0xffffffff 0x06 0x09>; 1291 | interrupt-controller; 1292 | #address-cells = <0x00>; 1293 | #interrupt-cells = <0x02>; 1294 | riscv,ndev = <0x40>; 1295 | phandle = <0x01>; 1296 | }; 1297 | ``` 1298 | 1299 | TODO: Why isn't this documented in [XuanTie OpenC906 User Manual](https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf)? 1300 | 1301 | So we change the PLIC Base Address for Ox64: [jh7110_memorymap.h](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/hardware/jh7110_memorymap.h#L30) 1302 | 1303 | ```c 1304 | #define JH7110_PLIC_BASE 0xe0000000 1305 | ``` 1306 | 1307 | TODO: Enable Scheduler Debug 1308 | 1309 | # Handle RISC-V Exceptions in NuttX 1310 | 1311 | Read the article... 1312 | 1313 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1314 | 1315 | Now NuttX crashes at a different place, with IRQ 15... 1316 | 1317 | ```text 1318 | 123ABC 1319 | nx_start: Entry 1320 | up_irqinitialize: a 1321 | up_irqinitialize: b 1322 | up_irqinitialize: c 1323 | riscv_dispatch_irq: irq=15 1324 | irq_unexpected_isr: ERROR irq: 15 1325 | _assert: Current Version: NuttX 12.0.3 910bfca-dirty Nov 6 2023 15:23:11 risc-v 1326 | _assert: Assertion failed panic: at file: irq/irq_unexpectedisr.c:54 task: Idle_Task process: Kernel 0x50200e50 1327 | ``` 1328 | 1329 | [(Source)](https://gist.github.com/lupyuen/11b8d4221a150f10afa3aa5ab5e50a4c) 1330 | 1331 | _What's IRQ 15?_ 1332 | 1333 | From [XuanTie OpenC906 User Manual](https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf) (Page 21): 1334 | 1335 | > "Exception Vector ID 15: A store/atomic instruction page error exception." 1336 | 1337 | This RISC-V Exception says that we tried to write to an invalid Data Address. And failed. 1338 | 1339 | _Where did it crash?_ 1340 | 1341 | Based on our log, NuttX crashes before setting the PLIC! 1342 | 1343 | From [jh7110_irq.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/8f318c363c80e1d4f5788f3815009cb57b5ff298/arch/risc-v/src/jh7110/jh7110_irq.c#L42-L85) 1344 | 1345 | ```c 1346 | // Init the IRQs 1347 | void up_irqinitialize(void) { 1348 | _info("a\n");//// 1349 | 1350 | /* Disable S-Mode interrupts */ 1351 | _info("b\n");//// 1352 | up_irq_save(); 1353 | 1354 | /* Disable all global interrupts */ 1355 | _info("c\n");//// 1356 | // Crashes here! 1357 | putreg32(0x0, JH7110_PLIC_ENABLE1); 1358 | putreg32(0x0, JH7110_PLIC_ENABLE2); 1359 | 1360 | /* Colorize the interrupt stack for debug purposes */ 1361 | ... 1362 | 1363 | /* Set irq threshold to 0 (permits all global interrupts) */ 1364 | _info("e\n");//// 1365 | putreg32(0, JH7110_PLIC_THRESHOLD); 1366 | 1367 | /* Attach the common interrupt handler */ 1368 | _info("f\n");//// 1369 | riscv_exception_attach(); 1370 | ``` 1371 | 1372 | _But it's a RISC-V Exception! Shouldn't NuttX dump this as a proper exception?_ 1373 | 1374 | See the `riscv_exception_attach()` above? It happens AFTER the crash! This means NuttX hasn't properly initialised the Exception Handlers, when the crash happened. 1375 | 1376 | Let's init the Exception Handlers earlier: [jh7110_irq.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/jh7110/jh7110_irq.c#L42-L85) 1377 | 1378 | ```c 1379 | // Init the IRQs 1380 | void up_irqinitialize(void) { 1381 | _info("a\n");//// 1382 | 1383 | /* Disable S-Mode interrupts */ 1384 | _info("b\n");//// 1385 | up_irq_save(); 1386 | 1387 | /* Attach the common interrupt handler */ 1388 | _info("f\n");//// 1389 | // Init the Exception Handlers here 1390 | riscv_exception_attach(); 1391 | 1392 | /* Disable all global interrupts */ 1393 | _info("c\n");//// 1394 | // Crashes here! 1395 | putreg32(0x0, JH7110_PLIC_ENABLE1); 1396 | putreg32(0x0, JH7110_PLIC_ENABLE2); 1397 | ``` 1398 | 1399 | `riscv_exception_attach()` will handle all RISC-V Exceptions, including Store/AMO Page Fault (IRQ 15): [riscv_exception.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/arch/risc-v/src/common/riscv_exception.c#L89-L142) 1400 | 1401 | ```c 1402 | // Attach standard exception with suitable handler 1403 | void riscv_exception_attach(void) { 1404 | // Handle Store/AMO Page Fault (IRQ 15) 1405 | irq_attach(RISCV_IRQ_STOREPF, riscv_exception, NULL); 1406 | ``` 1407 | 1408 | Now we see the Store/AMO Page Fault Exception! 1409 | 1410 | ```text 1411 | up_irqinitialize: c 1412 | riscv_dispatch_irq: irq=15 1413 | riscv_exception: 1414 | EXCEPTION: Store/AMO page fault 1415 | MCAUSE: 000000000000000f 1416 | EPC: 0000000050207e6a 1417 | MTVAL: 00000000e0002100 1418 | ``` 1419 | 1420 | [(Source)](https://gist.github.com/lupyuen/85db0510712ba8c660e10f922d4564c9) 1421 | 1422 | Code Address is 0x50207e6a, from our PLIC Code... 1423 | 1424 | ```text 1425 | nuttx/arch/risc-v/src/chip/jh7110_irq.c:62 1426 | putreg32(0x0, JH7110_PLIC_ENABLE1); 1427 | 50207e64: 700017b7 lui a5,0x70001 1428 | 50207e68: 0786 slli a5,a5,0x1 1429 | 50207e6a: 1007a023 sw zero,256(a5) # 70001100 <__ramdisk_end+0x1e601100> 1430 | ``` 1431 | 1432 | The offending Data Address is 0xe0002100. Which is our BL808 PLIC! 1433 | 1434 | # Add PLIC to I/O Memory Map 1435 | 1436 | Read the article... 1437 | 1438 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1439 | 1440 | _But is 0xe0002100 accessible?_ 1441 | 1442 | Ah we forgot to add it to the I/O Memory Map! Let's fix it: [jh7110_mm_init.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/b244f85065ecc749599842088f35f1b190466429/arch/risc-v/src/jh7110/jh7110_mm_init.c#L47-L50) 1443 | 1444 | ```c 1445 | /* Map the whole I/O memory with vaddr = paddr mappings */ 1446 | #define MMU_IO_BASE (0x00000000) 1447 | #define MMU_IO_SIZE (0xf0000000) 1448 | ``` 1449 | 1450 | (Doesn't look right, but we'll fix later) 1451 | 1452 | Now NuttX boots further! And tries to register IRQ 57 for the Star64 UART Interrupt... 1453 | 1454 | ```text 1455 | up_irqinitialize: c 1456 | up_irqinitialize: d 1457 | up_irqinitialize: e 1458 | up_irqinitialize: g 1459 | irq_attach: irq=17, isr=0x50207eee 1460 | up_enable_irq: irq=17 1461 | uart_register: Registering /dev/console 1462 | uart_register: Registering /dev/ttyS0 1463 | irq_attach: irq=57, isr=0x502041fe 1464 | up_enable_irq: irq=57 1465 | riscv_dispatch_irq: irq=5 1466 | riscv_exception: 1467 | EXCEPTION: Load access fault 1468 | MCAUSE: 0000000000000005 1469 | EPC: 0000000050208342 1470 | MTVAL: 00000000e0002104 1471 | ``` 1472 | 1473 | [(Source)](https://gist.github.com/lupyuen/ade5ff1433812fb675ff06f805f7339f) 1474 | 1475 | But it crashes while accessing the PLIC at another address: 0xe0002104. 1476 | 1477 | _Are we tired of PLIC yet?_ 1478 | 1479 | Yeah let's fix PLIC later. The entire UART Driver will be revamped anyway, including the UART Interrupt. 1480 | 1481 | Let's disable the UART Interrupt for now: [uart_16550.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/drivers/serial/uart_16550.c#L902-L958) 1482 | 1483 | ```c 1484 | // Attach the UART Interrupt for Star64 1485 | static int u16550_attach(struct uart_dev_s *dev) { 1486 | // Don't attach the interrupt 1487 | // Previously: ret = irq_attach(priv->irq, u16550_interrupt, dev); 1488 | 1489 | // Don't enable the interrupt 1490 | // Previously: up_enable_irq(priv->irq); 1491 | ``` 1492 | 1493 | # Fail to Load Initial RAM Disk 1494 | 1495 | Read the article... 1496 | 1497 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1498 | 1499 | Now NuttX boots even further yay! But crashes in the NuttX Bringup... 1500 | 1501 | ```text 1502 | up_irqinitialize: c 1503 | up_irqinitialize: d 1504 | up_irqinitialize: e 1505 | up_irqinitialize: g 1506 | irq_attach: irq=17, isr=0x50207e64 1507 | up_enable_irq: irq=17 1508 | uart_register: Registering /dev/console 1509 | uart_register: Registering /dev/ttyS0 1510 | work_start_lowpri: Starting low-priority kernel worker thread(s) 1511 | _assert: Current Version: NuttX 12.0.3 b244f85-dirty Nov 6 2023 17:35:34 risc-v 1512 | _assert: Assertion failed ret >= 0: at file: init/nx_bringup.c:283 task: AppBringUp process: Kernel 0x5020107e 1513 | ``` 1514 | 1515 | [(Source)](https://gist.github.com/lupyuen/ab640bcb3ba3a19834bcaa29e43baddf) 1516 | 1517 | Because it couldn't map the Initial RAM Disk: [nx_bringup.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/sched/init/nx_bringup.c#L276-L284) 1518 | 1519 | ```c 1520 | /* Mount the file system containing the init program. */ 1521 | ret = nx_mount(CONFIG_INIT_MOUNT_SOURCE, CONFIG_INIT_MOUNT_TARGET, 1522 | CONFIG_INIT_MOUNT_FSTYPE, CONFIG_INIT_MOUNT_FLAGS, 1523 | CONFIG_INIT_MOUNT_DATA); 1524 | DEBUGASSERT(ret >= 0); 1525 | ``` 1526 | 1527 | That's because we haven't loaded the Initial RAM Disk! Let's fix this later. 1528 | 1529 | # NuttX Boot Flow for Ox64 BL808 1530 | 1531 | (To see the NuttX Source Code: Right-click the Node and select "Open Link") 1532 | 1533 | ```mermaid 1534 | flowchart TD 1535 | START --> bl808_head["NuttX Boot Code: \n bl808_head \n (Prints 123)"] 1536 | click bl808_head href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_head.S#L41-L156" "arch/risc-v/src/bl808/bl808_head.S" _blank 1537 | 1538 | bl808_head --> bl808_start["NuttX Start Code: \n bl808_start"] 1539 | click bl808_start href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L254-L297" "arch/risc-v/src/bl808/bl808_start.c" _blank 1540 | 1541 | bl808_start --> bl808_start_s["Start Supervisor Mode: \n bl808_start_s \n (Prints ABC)"] 1542 | click bl808_start_s href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L200-L254" "arch/risc-v/src/bl808/bl808_start.c" _blank 1543 | 1544 | bl808_start_s --> riscv_earlyserialinit["Early Serial Init: \n riscv_earlyserialinit"] 1545 | click riscv_earlyserialinit href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L297-L314" "arch/risc-v/src/bl808/bl808_start.c" _blank 1546 | 1547 | bl808_start_s --> bl808_mm_init["Memory Mgmt Init: \n bl808_mm_init \n (Map the Memory Mgmt Unit)"] 1548 | click bl808_mm_init href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_mm_init.c#L278-L298" "arch/risc-v/src/bl808/bl808_mm_init.c" _blank 1549 | 1550 | bl808_start_s --> nx_start["Start NuttX: \n nx_start \n (Starts many things)"] 1551 | click nx_start href "https://github.com/apache/nuttx/blob/master/sched/init/nx_start.c#L298-L713" "sched/init/nx_start.c" _blank 1552 | 1553 | riscv_earlyserialinit --> bl808_earlyserialinit["UART Early Init: \n bl808_earlyserialinit \n (Setup the UART)"] 1554 | click bl808_earlyserialinit href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_serial.c#L700-L720" "drivers/serial/uart_16550.c" _blank 1555 | 1556 | nx_start --> up_irqinitialize["IRQ Init: \n up_irqinitialize"] 1557 | click up_irqinitialize href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_irq.c#L41-L103" "arch/risc-v/src/bl808/bl808_irq.c#" _blank 1558 | 1559 | nx_start --> nx_bringup["Bringup NuttX: \n nx_bringup"] 1560 | click nx_bringup href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L379-L468" "sched/init/nx_bringup.c" _blank 1561 | 1562 | up_irqinitialize --> riscv_exception_attach["Attach RISC-V Exceptions: \n riscv_exception_attach \n (Attach the RISC-V Exception Handlers)"] 1563 | click riscv_exception_attach href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_exception.c#L89-L142" "arch/risc-v/src/common/riscv_exception.c" _blank 1564 | 1565 | up_irqinitialize --> up_initialize["Init NuttX: \n up_initialize"] 1566 | click up_initialize href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_initialize.c#L70-L132" "arch/risc-v/src/common/riscv_initialize.c" _blank 1567 | 1568 | up_initialize --> riscv_serialinit["Serial Init: \n riscv_serialinit"] 1569 | click riscv_serialinit href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L314-L327" "arch/risc-v/src/bl808/bl808_start.c" _blank 1570 | 1571 | riscv_serialinit --> bl808_serialinit["UART Init: \n bl808_serialinit \n (Register /dev/console)"] 1572 | click bl808_serialinit href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_serial.c#L720-L762" "drivers/serial/uart_16550.c" _blank 1573 | 1574 | nx_bringup --> nx_create_initthread["Create Init Thread: \n nx_create_initthread \n (Create AppBringUp thread)"] 1575 | click nx_create_initthread href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L336-L375" "sched/init/nx_bringup.c" _blank 1576 | 1577 | nx_create_initthread --> nx_start_task["Start NuttX Task: \n nx_start_task"] 1578 | click nx_start_task href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L310-L336" "sched/init/nx_bringup.c" _blank 1579 | 1580 | nx_start_task --> nx_start_application["Start Application: \n nx_start_application"] 1581 | click nx_start_application href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L213-L310" "sched/init/nx_bringup.c" _blank 1582 | 1583 | nx_start_application --> board_late_initialize["Board Late Init: board_late_initialize \n (Mount Initial RAM Disk)"] 1584 | click board_late_initialize href "https://github.com/apache/nuttx/blob/master/boards/risc-v/bl808/ox64/src/bl808_appinit.c#L134-L167" "boards/risc-v/bl808/ox64/src/bl808_appinit.c" _blank 1585 | 1586 | board_late_initialize --> nx_mount["Mount RAM Disk: nx_mount \n (Fails because Initial RAM Disk is missing)"] 1587 | click nx_mount href "https://github.com/apache/nuttx/blob/master/fs/mount/fs_mount.c#L260-L514" "fs/mount/fs_mount.c" _blank 1588 | ``` 1589 | 1590 | Read the article... 1591 | 1592 | - ["Ox64 BL808 RISC-V SBC: Starting Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox2) 1593 | 1594 | _What happens exactly when NuttX boots on Ox64?_ 1595 | 1596 | In this article, NuttX has booted plenty of code on Ox64. Here's the flow of the __NuttX Code that boots on Ox64__... 1597 | 1598 | [__NuttX Boot Code: bl808_head__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_head.S#L41-L156) calls... 1599 | 1600 | - [__NuttX Start Code: bl808_start__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L254-L297) which calls... 1601 | 1602 | - [__Start Supervisor Mode: bl808_start_s__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L200-L254) which prints "ABC" and calls... 1603 | 1604 | - [__Early Serial Init: riscv_earlyserialinit__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L297-L314) (see below) and... 1605 | 1606 | [__Memory Mgmt Init: bl808_mm_init__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_mm_init.c#L278-L298) (to init the Memory Mgmt Unit) and... 1607 | 1608 | [__Start NuttX: nx_start__](https://github.com/apache/nuttx/blob/master/sched/init/nx_start.c#L298-L713) (see below) 1609 | 1610 | [__Early Serial Init: riscv_earlyserialinit__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L297-L314) calls... 1611 | 1612 | - [__UART Early Init: bl808_earlyserialinit__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_serial.c#L700-L720) 1613 | 1614 | (To setup the UART) 1615 | 1616 | [__Memory Mgmt Init: bl808_mm_init__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_mm_init.c#L278-L298) inits the [__Memory Mgmt Unit__](https://lupyuen.github.io/articles/mmu) by calling... 1617 | 1618 | - [__MMU Map Region: mmu_ln_map_region__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L137-L153) (to map a Memory Region) and... 1619 | 1620 | [__MMU Set Entry: mmu_ln_setentry__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L61C1-L107) (to set a Page Table Entry) 1621 | 1622 | [(More about the __Memory Mgmt Unit__)](https://lupyuen.github.io/articles/mmu) 1623 | 1624 | [__Start NuttX: nx_start__](https://github.com/apache/nuttx/blob/master/sched/init/nx_start.c#L298-L713) does [__many things__](https://lupyuen.github.io/articles/unicorn2#after-primary-routine) and calls... 1625 | 1626 | - [__IRQ Init: up_irqinitialize__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_irq.c#L41C1-L103) (see below) and... 1627 | 1628 | [__Bringup NuttX: nx_bringup__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L379-L468) (see below) 1629 | 1630 | [__IRQ Init: up_irqinitialize__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_irq.c#L41-L103) calls... 1631 | 1632 | - [__Attach RISC-V Exceptions: riscv_exception_attach__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_exception.c#L89-L142) (to attach the RISC-V Exception Handlers) and... 1633 | 1634 | [__Init NuttX: up_initialize__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_initialize.c#L70-L132) (see below) 1635 | 1636 | [__Init NuttX: up_initialize__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_initialize.c#L70-L132) calls... 1637 | 1638 | - [__Serial Init: riscv_serialinit__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L314-L327) which calls... 1639 | 1640 | - [__UART Init: bl808_serialinit__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_serial.c#L720-L762) 1641 | 1642 | (To register "/dev/console") 1643 | 1644 | [__Bringup NuttX: nx_bringup__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L379-L468) calls... 1645 | 1646 | - [__Create Init Thread: nx_create_initthread__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L336-L375) (to create "AppBringUp" thread) which calls... 1647 | 1648 | - [__Start NuttX Task: nx_start_task__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L310-L336) (to start NuttX Task) which calls... 1649 | 1650 | - [__Start Application: nx_start_application__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L213-L310) which calls... 1651 | 1652 | - [__Board Late Init: board_late_initialize__](https://github.com/apache/nuttx/blob/master/boards/risc-v/bl808/ox64/src/bl808_appinit.c#L134-L167) (to mount Initial RAM Disk) and... 1653 | 1654 | [__Mount RAM Disk: nx_mount__](https://github.com/apache/nuttx/blob/master/fs/mount/fs_mount.c#L260-L514) (to mount ROM File System from Initial RAM Disk) 1655 | 1656 | (Which fails because our Initial RAM Disk is missing) 1657 | 1658 | (Which prevents NuttX Shell from starting) 1659 | 1660 | [(How __NuttX Shell__ should be started)](https://github.com/lupyuen/nuttx-ox64#start-nuttx-apps-on-ox64-bl808) 1661 | 1662 | [(How __NuttX Apps__ are started)](https://lupyuen.github.io/articles/app) 1663 | 1664 | Therefore we expect NuttX to __boot completely on Ox64__ when we've implemented... 1665 | 1666 | - [__Initial RAM Disk__](https://lupyuen.github.io/articles/ox2#appendix-initial-ram-disk) for Ox64 1667 | 1668 | - [__UART Driver and UART Interrupts__](https://lupyuen.github.io/articles/ox2#appendix-uart-driver-for-ox64) 1669 | 1670 | - [__Memory Map__](https://lupyuen.github.io/articles/ox2#appendix-memory-map-for-ox64) might need fixing too 1671 | 1672 | # NuttX UART Driver for Ox64 BL808 1673 | 1674 | Read the article... 1675 | 1676 | - ["RISC-V Ox64 BL808 SBC: UART Interrupt and Platform-Level Interrupt Controller (PLIC)"](https://lupyuen.github.io/articles/plic2) 1677 | 1678 | BL808 UART is mostly identical to BL602 UART, so we ported the NuttX BL602 UART Driver to BL808. 1679 | 1680 | Here's the UART Driver ported to BL808: [bl602_serial.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/bl602_serial.c) 1681 | 1682 | We hardcoded the UART3 Base Address: [bl602_uart.h](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/hardware/bl602_uart.h#L30-L41) 1683 | 1684 | ```c 1685 | #define BL602_UART0_BASE 0x30002000 1686 | #define BL602_UART_BASE(n) (BL602_UART0_BASE) 1687 | // Previously: #define BL602_UART_BASE(n) (BL602_UART0_BASE + (n * (BL602_UART1_BASE - BL602_UART0_BASE))) 1688 | ``` 1689 | 1690 | We fixed the NuttX Start Code to call our new UART Driver: [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L175-L184) 1691 | 1692 | ```c 1693 | void riscv_earlyserialinit(void) { 1694 | bl602_earlyserialinit(); 1695 | } 1696 | 1697 | void riscv_serialinit(void) { 1698 | bl602_serialinit(); 1699 | } 1700 | ``` 1701 | 1702 | We disabled UART Interrupts for now: [bl602_attach and bl602_detach](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/bl602_serial.c#L377-L431) 1703 | 1704 | And the UART Driver works! [(See the log)](https://gist.github.com/lupyuen/74a44a3e432e159c62cc2df6a726cb89) 1705 | 1706 | TODO: /dev/ttyS0 is missing 1707 | 1708 | TODO: Enable UART Interrupts 1709 | 1710 | # Initial RAM Disk for Ox64 BL808 1711 | 1712 | Read the article... 1713 | 1714 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 1715 | 1716 | Two ways we can load the Initial RAM Disk... 1717 | 1718 | 1. Load the Initial RAM Disk from a __Separate File: initrd__ (similar to Star64) 1719 | 1720 | This means we need to modify the [__U-Boot Script: boot-pine64.scr__](https://github.com/openbouffalo/buildroot_bouffalo/blob/main/board/pine64/ox64/boot-pine64.cmd) 1721 | 1722 | And make it [__load the initrd__](https://lupyuen.github.io/articles/semihost#appendix-boot-nuttx-over-tftp-with-initial-ram-disk) file into RAM. 1723 | 1724 | (Which is good for separating the NuttX Kernel and NuttX Apps) 1725 | 1726 | OR... 1727 | 1728 | 1. Append the Initial RAM Disk to the __NuttX Kernel Image__ 1729 | 1730 | So the U-Boot Bootloader will load (one-shot into RAM) the NuttX Kernel + Initial RAM Disk. 1731 | 1732 | And we reuse the existing __U-Boot Config__ on the microSD Card: [__extlinux/extlinux.conf__](https://github.com/openbouffalo/buildroot_bouffalo/blob/main/board/pine64/ox64/rootfs-overlay/boot/extlinux/extlinux.conf) 1733 | 1734 | (Which might be more efficient for our Limited RAM) 1735 | 1736 | [(See the __U-Boot Boot Flow__)](https://github.com/openbouffalo/buildroot_bouffalo/wiki/U-Boot-Bootflow) 1737 | 1738 | __TODO:__ Can we mount the File System directly from the __NuttX Kernel Image in RAM__? Without copying to the [__RAM Disk Memory Region__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64/boards/risc-v/jh7110/star64/scripts/ld.script#L26)? 1739 | 1740 | We'll do the Second Method, since we are low on RAM. Like this... 1741 | 1742 | ```bash 1743 | ## Export the Binary Image to `nuttx.bin` 1744 | riscv64-unknown-elf-objcopy \ 1745 | -O binary \ 1746 | nuttx \ 1747 | nuttx.bin 1748 | 1749 | ## Insert 64 KB of zeroes after Binary Image for Kernel Stack 1750 | head -c 65536 /dev/zero >/tmp/nuttx.zero 1751 | 1752 | ## Append Initial RAM Disk to Binary Image 1753 | cat nuttx.bin /tmp/nuttx.zero initrd \ 1754 | >Image 1755 | 1756 | ## Overwrite the Linux Image on Ox64 microSD 1757 | cp Image "/Volumes/NO NAME/" 1758 | ``` 1759 | 1760 | ![Initial RAM Disk for Ox64](https://lupyuen.github.io/images/app-initrd.jpg) 1761 | 1762 | This is how we copy the initrd in RAM to the Memory Region for the RAM Disk: [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L190-L245) 1763 | 1764 | ```c 1765 | static void jh7110_copy_ramdisk(void) { 1766 | // Based on ROM FS Format: https://docs.kernel.org/filesystems/romfs.html 1767 | // After _edata, search for "-rom1fs-". This is the RAM Disk Address. 1768 | // Stop searching after 64 KB. 1769 | extern uint8_t _edata[]; 1770 | extern uint8_t _sbss[]; 1771 | extern uint8_t _ebss[]; 1772 | const char *header = "-rom1fs-"; 1773 | uint8_t *ramdisk_addr = NULL; 1774 | for (uint8_t *addr = _edata; addr < (uint8_t *)JH7110_IDLESTACK_TOP + (65 * 1024); addr++) { 1775 | if (memcmp(addr, header, strlen(header)) == 0) { 1776 | ramdisk_addr = addr; 1777 | break; 1778 | } 1779 | } 1780 | // Check for Missing RAM Disk 1781 | if (ramdisk_addr == NULL) { _info("Missing RAM Disk"); } 1782 | DEBUGASSERT(ramdisk_addr != NULL); 1783 | 1784 | // RAM Disk must be after Idle Stack 1785 | if (ramdisk_addr <= (uint8_t *)JH7110_IDLESTACK_TOP) { _info("RAM Disk must be after Idle Stack"); } 1786 | DEBUGASSERT(ramdisk_addr > (uint8_t *)JH7110_IDLESTACK_TOP); 1787 | 1788 | // Read the Filesystem Size from the next 4 bytes, in Big Endian 1789 | // Add 0x1F0 to Filesystem Size 1790 | const uint32_t size = 1791 | (ramdisk_addr[8] << 24) + 1792 | (ramdisk_addr[9] << 16) + 1793 | (ramdisk_addr[10] << 8) + 1794 | ramdisk_addr[11] + 1795 | 0x1F0; 1796 | _info("size=%d\n", size); 1797 | 1798 | // Filesystem Size must be less than RAM Disk Memory Region 1799 | DEBUGASSERT(size <= (size_t)__ramdisk_size); 1800 | 1801 | // Before Copy: Verify the RAM Disk Image to be copied 1802 | verify_image(ramdisk_addr); 1803 | 1804 | // Copy the Filesystem Size to RAM Disk Start 1805 | // Warning: __ramdisk_start overlaps with ramdisk_addr + size 1806 | // memmove is aliased to memcpy, so we implement memmove ourselves 1807 | local_memmove((void *)__ramdisk_start, ramdisk_addr, size); 1808 | 1809 | // Before Copy: Verify the copied RAM Disk Image 1810 | verify_image(__ramdisk_start); 1811 | } 1812 | ``` 1813 | 1814 | We copy the initrd at the very top of our NuttX Start Code, before erasing the BSS (in case it corrupts our RAM Disk, but actually it shouldn't): [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L144-L156) 1815 | 1816 | ```c 1817 | // NuttX Start Code 1818 | void jh7110_start(int mhartid) { 1819 | DEBUGASSERT(mhartid == 0); /* Only Hart 0 supported for now */ 1820 | if (0 == mhartid) { 1821 | /* Copy the RAM Disk */ 1822 | jh7110_copy_ramdisk(); 1823 | 1824 | /* Clear the BSS */ 1825 | jh7110_clear_bss(); 1826 | ``` 1827 | 1828 | NuttX mounts the RAM Disk from the Memory Region later during startup: [jh7110_appinit.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/boards/risc-v/jh7110/star64/src/jh7110_appinit.c#L51-L87) 1829 | 1830 | ```c 1831 | // After NuttX has booted... 1832 | void board_late_initialize(void) { 1833 | // Mount the RAM Disk 1834 | mount_ramdisk(); 1835 | } 1836 | 1837 | // Mount the RAM Disk 1838 | int mount_ramdisk(void) { 1839 | desc.minor = RAMDISK_DEVICE_MINOR; 1840 | desc.nsectors = NSECTORS((ssize_t)__ramdisk_size); 1841 | desc.sectsize = SECTORSIZE; 1842 | desc.image = __ramdisk_start; 1843 | ret = boardctl(BOARDIOC_ROMDISK, (uintptr_t)&desc); 1844 | ``` 1845 | 1846 | And NuttX mounts our RAM Disk successfully! 1847 | 1848 | ```text 1849 | jh7110_copy_ramdisk: _edata=0x50400258, _sbss=0x50400290, _ebss=0x50407000, JH7110_IDLESTACK_TOP=0x50407c00 1850 | jh7110_copy_ramdisk: ramdisk_addr=0x50408288 1851 | jh7110_copy_ramdisk: size=8192016 1852 | jh7110_copy_ramdisk: Before Copy: ramdisk_addr=0x50408288 1853 | jh7110_copy_ramdisk: After Copy: __ramdisk_start=0x50a00000 1854 | ... 1855 | elf_initialize: Registering ELF 1856 | uart_register: Registering /dev/console 1857 | work_start_lowpri: Starting low-priority kernel worker thread(s) 1858 | nx_start_application: Starting init task: /system/bin/init 1859 | load_absmodule: Loading /system/bin/init 1860 | elf_loadbinary: Loading file: /system/bin/init 1861 | elf_init: filename: /system/bin/init loadinfo: 0x5040c618 1862 | elf_read: Read 64 bytes from offset 0 1863 | ``` 1864 | 1865 | [(Source)](https://gist.github.com/lupyuen/74a44a3e432e159c62cc2df6a726cb89) 1866 | 1867 | _Why did we insert 64 KB of zeroes after the NuttX Binary Image, before the initrd Initial RAM Disk?_ 1868 | 1869 | ```bash 1870 | ## Insert 64 KB of zeroes after Binary Image for Kernel Stack 1871 | head -c 65536 /dev/zero >/tmp/nuttx.zero 1872 | 1873 | ## Append Initial RAM Disk to Binary Image 1874 | cat nuttx.bin /tmp/nuttx.zero initrd \ 1875 | >Image 1876 | ``` 1877 | 1878 | When we refer to the [NuttX Log](https://gist.github.com/lupyuen/74a44a3e432e159c62cc2df6a726cb89) and the [NuttX Linker Script](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/boards/risc-v/jh7110/star64/scripts/ld.script)... 1879 | 1880 | ```text 1881 | // End of Data Section 1882 | _edata=0x50400258 1883 | 1884 | // Start of BSS Section 1885 | _sbss=0x50400290 1886 | 1887 | // End of BSS Section 1888 | _ebss=0x50407000 1889 | 1890 | // Top of Idle Stack 1891 | JH7110_IDLESTACK_TOP=0x50407c00 1892 | 1893 | // We located the initd after the Top of Idle Stack 1894 | ramdisk_addr=0x50408288, size=8192016 1895 | 1896 | // And we copied initrd to the Memory Region for the RAM Disk 1897 | __ramdisk_start=0x50a00000 1898 | ``` 1899 | 1900 | Which says... 1901 | 1902 | 1. The NuttX Binary Image `nuttx.bin` terminates at `_edata`. (End of Data Section) 1903 | 1904 | 1. If we append `initrd` directly to the end of `nuttx.bin`, it will collide with the [BSS Section](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L74-L92) and the [Idle Stack](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_head.S#L94-L101). And `initrd` will get overwritten by NuttX. 1905 | 1906 | 1. Best place to append `initrd` is after the Top of Idle Stack. Which is located 32 KB after `_edata`. (End of Data Section) 1907 | 1908 | 1. That's why we inserted a padding of 64 KB between `nuttx.bin` and `initrd`. So it won't collide with BSS and Idle Stack. 1909 | 1910 | 1. Our code locates `initrd` (searching by Magic Number "-rom1fs-"). And copies `initrd` to `__ramdisk_start`. (Memory Region for the RAM Disk) 1911 | 1912 | 1. NuttX mounts the RAM Disk from `__ramdisk_start`. (Memory Region for the RAM Disk) 1913 | 1914 | _But 64 KB sounds so arbitrary. What if the parameters change?_ 1915 | 1916 | That's why we have a Runtime Check: [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L190-L245) 1917 | 1918 | ```c 1919 | // RAM Disk must be after Idle Stack 1920 | if (ramdisk_addr <= (uint8_t *)JH7110_IDLESTACK_TOP) { _info("RAM Disk must be after Idle Stack"); } 1921 | DEBUGASSERT(ramdisk_addr > (uint8_t *)JH7110_IDLESTACK_TOP); 1922 | ``` 1923 | 1924 | _Why did we call local_memmove to copy `initrd` to `__ramdisk_start`? Why not memcpy?_ 1925 | 1926 | That's because `initrd` overlaps with `__ramdisk_start`! 1927 | 1928 | ``` 1929 | ramdisk_addr = 0x50408288, size = 8192016 1930 | ramdisk_addr + size = 0x50bd8298 1931 | Which is AFTER __ramdisk_start (0x50a00000) 1932 | ``` 1933 | 1934 | `memcpy` won't work with Overlapping Memory Regions. So we wrote our own: [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L246-L487) 1935 | 1936 | ```c 1937 | // From libs/libc/string/lib_memmove.c 1938 | static FAR void *local_memmove(FAR void *dest, FAR const void *src, size_t count) { 1939 | FAR char *d; 1940 | FAR char *s; 1941 | DEBUGASSERT(dest > src); 1942 | d = (FAR char *) dest + count; 1943 | s = (FAR char *) src + count; 1944 | 1945 | while (count--) { 1946 | d -= 1; 1947 | s -= 1; 1948 | // TODO: Very strange. This needs to be volatile or C Compiler will replace this by memcpy. 1949 | volatile char c = *s; 1950 | *d = c; 1951 | } 1952 | return dest; 1953 | } 1954 | ``` 1955 | 1956 | _We're sure that it works?_ 1957 | 1958 | That's why we called `verify_image` to do a simple integrity check on `initrd`, before and after copying. And that's how we discovered that `memcpy` doesn't work. From [jh7110_start.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_start.c#L246-L487) 1959 | 1960 | ```c 1961 | // Verify that image is correct 1962 | static void verify_image(uint8_t *addr) { 1963 | // Verify that the Byte Positions below (offset by 1) contain 0x0A 1964 | for (int i = 0; i < sizeof(search_addr) / sizeof(search_addr[0]); i++) { 1965 | const uint8_t *p = addr + search_addr[i] - 1; 1966 | if (*p != 0x0A) { _info("No Match: %p\n", p); } 1967 | } 1968 | } 1969 | 1970 | // Byte Positions (offset by 1) of 0x0A in initrd. Extracted from: 1971 | // grep --binary-files=text -b -o A initrd 1972 | const uint32_t search_addr[] = 1973 | { 1974 | 76654, 1975 | 78005, 1976 | 79250, 1977 | ... 1978 | 7988897, 1979 | 7992714, 1980 | }; 1981 | ``` 1982 | 1983 | But NuttX fails to start our NuttX Shell (NSH) ELF Executable from "/system/bin/init"... 1984 | 1985 | ```text 1986 | elf_read: Read 3392 bytes from offset 3385080 1987 | elf_addrenv_select: ERROR: up_addrenv_text_enable_write failed: -22 1988 | elf_load: ERROR: elf_addrenv_select() failed: -22 1989 | ... 1990 | elf_loadbinary: Failed to load ELF program binary: -22 1991 | exec_internal: ERROR: Failed to load program '/system/bin/init': -22 1992 | _assert: Current Version: NuttX 12.0.3 8017bd9-dirty Nov 10 2023 22:50:07 risc-v 1993 | _assert: Assertion failed ret > 0: at file: init/nx_bringup.c:302 task: AppBringUp process: Kernel 0x502014ea 1994 | ``` 1995 | 1996 | [(Source)](https://gist.github.com/lupyuen/74a44a3e432e159c62cc2df6a726cb89) 1997 | 1998 | Maybe because NuttX is trying to map the User Address Space 0xC000 0000: [nsh/defconfig](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/boards/risc-v/jh7110/star64/configs/nsh/defconfig#L17-L26) 1999 | 2000 | ```text 2001 | CONFIG_ARCH_TEXT_VBASE=0xC0000000 2002 | CONFIG_ARCH_TEXT_NPAGES=128 2003 | CONFIG_ARCH_DATA_VBASE=0xC0100000 2004 | CONFIG_ARCH_DATA_NPAGES=128 2005 | CONFIG_ARCH_HEAP_VBASE=0xC0200000 2006 | CONFIG_ARCH_HEAP_NPAGES=128 2007 | ``` 2008 | 2009 | But our Kernel Memory Space already extends to 0xF000 0000? (Because of the PLIC at 0xE000 0000) 2010 | 2011 | From [jh7110_mm_init.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_mm_init.c#L43-L46): 2012 | 2013 | ```c 2014 | /* Map the whole I/O memory with vaddr = paddr mappings */ 2015 | #define MMU_IO_BASE (0x00000000) 2016 | #define MMU_IO_SIZE (0xf0000000) 2017 | ``` 2018 | 2019 | _Let's disable PLIC, and exclude PLIC from Memory Map. Will the NuttX Shell start?_ 2020 | 2021 | Yep it does! [(See the log)](https://gist.github.com/lupyuen/9fc9b2de9938b48666cc5e5fa3f8278e) 2022 | 2023 | Now we fix the Memory Map... 2024 | 2025 | # NuttX Memory Map for Ox64 BL808 2026 | 2027 | Read the article... 2028 | 2029 | - ["RISC-V Ox64 BL808 SBC: Sv39 Memory Management Unit"](https://lupyuen.github.io/articles/mmu) 2030 | 2031 | To fix the NuttX Memory Map for Ox64, let's trace the MMU Page Table Entries. From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2032 | 2033 | ## Map the I/O Region (Level 1) 2034 | 2035 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2036 | 2037 | ```text 2038 | mmu_ln_map_region: 2039 | ptlevel=1, lnvaddr=0x50407000, paddr=0, vaddr=0, size=0x40000000, mmuflags=0x26 2040 | 2041 | mmu_ln_setentry: 2042 | ptlevel=1, lnvaddr=0x50407000, paddr=0, vaddr=0, mmuflags=0x26 2043 | mmu_ln_setentry: 2044 | index=0, paddr=0, mmuflags=0xe7, pte_addr=0x50407000, pte_val=0xe7 2045 | ``` 2046 | 2047 | `mmuflags=0x26` means Read + Write + Global 2048 | 2049 | ![Level 1 Page Table for Kernel](https://lupyuen.github.io/images/mmu-l1kernel2.jpg) 2050 | 2051 | ## Map the PLIC (Level 2) 2052 | 2053 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2054 | 2055 | ```text 2056 | mmu_ln_map_region: 2057 | ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, size=0x10000000, mmuflags=0x26 2058 | 2059 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, mmuflags=0x26 2060 | mmu_ln_setentry: index=0x100, paddr=0xe0000000, mmuflags=0xe7, pte_addr=0x50403800, pte_val=0x380000e7 2061 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0200000, vaddr=0xe0200000, mmuflags=0x26 2062 | mmu_ln_setentry: index=0x101, paddr=0xe0200000, mmuflags=0xe7, pte_addr=0x50403808, pte_val=0x380800e7 2063 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0400000, vaddr=0xe0400000, mmuflags=0x26 2064 | mmu_ln_setentry: index=0x102, paddr=0xe0400000, mmuflags=0xe7, pte_addr=0x50403810, pte_val=0x381000e7 2065 | ... 2066 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xefe00000, vaddr=0xefe00000, mmuflags=0x26 2067 | mmu_ln_setentry: index=0x17f, paddr=0xefe00000, mmuflags=0xe7, pte_addr=0x50403bf8, pte_val=0x3bf800e7 2068 | ``` 2069 | 2070 | `mmuflags=0x26` means Read + Write + Global 2071 | 2072 | ![Level 2 Page Table for Interrupt Controller](https://lupyuen.github.io/images/mmu-l2int.jpg) 2073 | 2074 | ## Connect the Level 1 and Level 2 Page Tables for PLIC 2075 | 2076 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2077 | 2078 | ```text 2079 | mmu_ln_setentry: 2080 | ptlevel=1, lnvaddr=0x50407000, paddr=0x50403000, vaddr=0xe0000000, mmuflags=0x20 2081 | 2082 | mmu_ln_setentry: 2083 | index=0x3, paddr=0x50403000, mmuflags=0x21, pte_addr=0x50407018, pte_val=0x14100c21 2084 | ``` 2085 | 2086 | `mmuflags=0x20` means PTE_G: Global Mapping. 2087 | 2088 | ![Level 1 Page Table for Kernel](https://lupyuen.github.io/images/mmu-l1kernel.jpg) 2089 | 2090 | ## Map the Kernel Text (Levels 2 & 3) 2091 | 2092 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2093 | 2094 | ```text 2095 | // Level 2 2096 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x50404000, vaddr=0x50200000, mmuflags=0x0 2097 | mmu_ln_setentry: index=0x81, paddr=0x50404000, mmuflags=0x1, pte_addr=0x50406408, pte_val=0x14101001 2098 | 2099 | // Level 3 2100 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50404000, paddr=0x50200000, vaddr=0x50200000, mmuflags=0x2a 2101 | mmu_ln_setentry: index=0, paddr=0x50200000, mmuflags=0xeb, pte_addr=0x50404000, pte_val=0x140800eb 2102 | 2103 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50404000, paddr=0x5020100, vaddr=0x50201000, mmuflags=0x2a 2104 | mmu_ln_setentry: index=0x1, paddr=0x50201000, mmuflags=0xeb, pte_addr=0x50404008, pte_val=0x140804eb 2105 | 2106 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50404000, paddr=0x50202000, vaddr=0x50202000, mmuflags=0x2a 2107 | mmu_ln_setentry: index=0x2, paddr=0x50202000, mmuflags=0xeb, pte_addr=0x50404010, pte_val=0x140808eb 2108 | ... 2109 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50404000, paddr=0x503ff000, vaddr=0x503ff000, mmuflags=0x2a 2110 | mmu_ln_setentry: index=0x1ff, paddr=0x503ff000, mmuflags=0xeb, pte_addr=0x50404ff8, pte_val=0x140ffceb 2111 | ``` 2112 | 2113 | `mmuflags=0x0` means PTE is a pointer to the next level of the page table 2114 | 2115 | `mmuflags=0x2a` means Read + Execute + Global 2116 | 2117 | ![Level 3 Page Table for Kernel](https://lupyuen.github.io/images/mmu-l3kernel.jpg) 2118 | 2119 | ## Map the Kernel Data (Levels 2 & 3) 2120 | 2121 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2122 | 2123 | ```text 2124 | // Level 2 2125 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x50405000, vaddr=0x50400000, mmuflags=0x0 2126 | mmu_ln_setentry: index=0x82, paddr=0x50405000, mmuflags=0x1, pte_addr=0x50406410, pte_val=0x14101401 2127 | 2128 | // Level 3 2129 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50405000, paddr=0x50400000, vaddr=0x5040000, mmuflags=0x26 2130 | mmu_ln_setentry: index=0, paddr=0x50400000, mmuflags=0xe7, pte_addr=0x50405000, pte_val=0x141000e7 2131 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50405000, paddr=0x50401000, vaddr=0x50401000, mmuflags=0x26 2132 | mmu_ln_setentry: index=0x1, paddr=0x50401000, mmuflags=0xe7, pte_addr=0x50405008, pte_val=0x141004e7 2133 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50405000, paddr=0x50402000, vaddr=0x50402000, mmuflags=0x26 2134 | mmu_ln_setentry: index=0x2, paddr=0x50402000, mmuflags=0xe7, pte_addr=0x50405010, pte_val=0x141008e7 2135 | ... 2136 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50405000, paddr=0x505ff000, vaddr=0x505ff000, mmuflags=0x26 2137 | mmu_ln_setentry: index=0x1ff, paddr=0x505ff000, mmuflags=0xe7, pte_addr=0x50405ff8, pte_val=0x1417fce7 2138 | ``` 2139 | 2140 | `mmuflags=0x0` means PTE is a pointer to the next level of the page table 2141 | 2142 | `mmuflags=0x26` means Read + Write + Global 2143 | 2144 | ## Connect the Level 1 and Level 2 Page Tables 2145 | 2146 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2147 | 2148 | ```text 2149 | mmu_ln_setentry: 2150 | ptlevel=1, lnvaddr=0x50407000, paddr=0x50406000, vaddr=0x50200000, mmuflags=0x20 2151 | 2152 | mmu_ln_setentry: 2153 | index=0x1, paddr=0x50406000, mmuflags=0x21, pte_addr=0x50407008, pte_val=0x14101821 2154 | ``` 2155 | 2156 | `mmuflags=0x20` means PTE_G: Global Mapping. 2157 | 2158 | And PTE is a pointer to the next level of the page table. 2159 | 2160 | Which means that Virtual Address 0x5020 0000 points to the L2 Page Table 0x5040 6000 2161 | 2162 | ## Map the Page Pool (Level 2) 2163 | 2164 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2165 | 2166 | ```text 2167 | mmu_ln_map_region: ptlevel=2, lnvaddr=0x50406000, paddr=0x50600000, vaddr=0x50600000, size=0x1400000, mmuflags=0x26 2168 | 2169 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x50600000, vaddr=0x50600000, mmuflags=0x26 2170 | mmu_ln_setentry: index=0x83, paddr=0x50600000, mmuflags=0xe7, pte_addr=0x50406418, pte_val=0x141800e7 2171 | 2172 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x50800000, vaddr=0x50800000, mmuflags=0x26 2173 | mmu_ln_setentry: index=0x84, paddr=0x50800000, mmuflags=0xe7, pte_addr=0x50406420, pte_val=0x142000e7 2174 | 2175 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x50a00000, vaddr=0x50a00000, mmuflags=0x26 2176 | mmu_ln_setentry: index=0x85, paddr=0x50a00000, mmuflags=0xe7, pte_addr=0x50406428, pte_val=0x142800e7 2177 | ... 2178 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50406000, paddr=0x51800000, vaddr=0x51800000, mmuflags=0x26 2179 | mmu_ln_setentry: index=0x8c, paddr=0x51800000, mmuflags=0xe7, pte_addr=0x50406460, pte_val=0x146000e7 2180 | ``` 2181 | 2182 | `mmuflags=0x26` means Read + Write + Global 2183 | 2184 | ![Level 2 Page Table for Kernel](https://lupyuen.github.io/images/mmu-l2kernel.jpg) 2185 | 2186 | ## Map the User Code, Data and Heap (Levels 1, 2, 3) 2187 | 2188 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2189 | 2190 | ```text 2191 | nx_start_application: Starting init task: /system/bin/init 2192 | // Level 1 (Code, Data, Heap) 2193 | // `mmuflags=0x0` means it's a link to a PTE 2194 | // `vaddr=0x80100000` will be aligned to 0x80000000 2195 | mmu_ln_setentry: ptlevel=1, lnvaddr=0x50600000, paddr=0x50601000, vaddr=0x80100000, mmuflags=0x0 2196 | mmu_ln_setentry: index=0x2, paddr=0x50601000, mmuflags=0x1, pte_addr=0x50600010, pte_val=0x14180401 2197 | 2198 | // Level 2 (Code, Data) 2199 | // `mmuflags=0x0` means it's a link to a PTE 2200 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x50602000, vaddr=0x80100000, mmuflags=0x0 2201 | mmu_ln_setentry: index=0, paddr=0x50602000, mmuflags=0x1, pte_addr=0x50601000, pte_val=0x14180801 2202 | 2203 | // Level 3 (Data) 2204 | // `mmuflags=0x16` means Read + Write + User 2205 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50602000, paddr=0x50603000, vaddr=0x80100000, mmuflags=0x16 2206 | mmu_ln_setentry: index=0x100, paddr=0x50603000, mmuflags=0xd7, pte_addr=0x50602800, pte_val=0x14180cd7 2207 | 2208 | // Level 3 (Code) 2209 | // `mmuflags=0x1a` means Read + Execute + User 2210 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50602000, paddr=0x50604000, vaddr=0x80000000, mmuflags=0x1a 2211 | mmu_ln_setentry: index=0, paddr=0x50604000, mmuflags=0xdb, pte_addr=0x50602000, pte_val=0x141810db 2212 | 2213 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50602000, paddr=0x50605000, vaddr=0x80001000, mmuflags=0x1a 2214 | mmu_ln_setentry: index=0x1, paddr=0x50605000, mmuflags=0xdb, pte_addr=0x50602008, pte_val=0x141814db 2215 | ... 2216 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50602000, paddr=0x50619000, vaddr=0x80015000, mmuflags=0x1a 2217 | mmu_ln_setentry: index=0x15, paddr=0x50619000, mmuflags=0xdb, pte_addr=0x506020a8, pte_val=0x141864db 2218 | 2219 | // Level 3 (Data) 2220 | // `mmuflags=0x16` means Read + Write + User 2221 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50602000, paddr=0x5061a000, vaddr=0x80101000, mmuflags=0x16 2222 | mmu_ln_setentry: index=0x101, paddr=0x5061a000, mmuflags=0xd7, pte_addr=0x50602808, pte_val=0x141868d7 2223 | 2224 | // Level 2 (Heap) 2225 | // `mmuflags=0x0` means it's a link to a PTE 2226 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x5061b000, vaddr=0x80200000, mmuflags=0x0 2227 | mmu_ln_setentry: index=0x1, paddr=0x5061b000, mmuflags=0x1, pte_addr=0x50601008, pte_val=0x14186c01 2228 | 2229 | // Level 3 (Heap) 2230 | // `mmuflags=0x16` means Read + Write + User 2231 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x5061b000, paddr=0x5061c000, vaddr=0x80200000, mmuflags=0x16 2232 | mmu_ln_setentry: index=0, paddr=0x5061c000, mmuflags=0xd7, pte_addr=0x5061b000, pte_val=0x141870d7 2233 | 2234 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x5061b000, paddr=0x5061d000, vaddr=0x80201000, mmuflags=0x16 2235 | mmu_ln_setentry: index=0x1, paddr=0x5061d000, mmuflags=0xd7, pte_addr=0x5061b008, pte_val=0x141874d7 2236 | ... 2237 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x5061b000, paddr=0x5069c000, vaddr=0x80280000, mmuflags=0x16 2238 | mmu_ln_setentry: index=0x80, paddr=0x5069c000, mmuflags=0xd7, pte_addr=0x5061b400, pte_val=0x141a70d7 2239 | ``` 2240 | 2241 | ![Level 1 Page Table for User](https://lupyuen.github.io/images/mmu-l1user.jpg) 2242 | 2243 | ![Level 2 Page Table for User](https://lupyuen.github.io/images/mmu-l2user.jpg) 2244 | 2245 | ![Level 3 Page Table for User](https://lupyuen.github.io/images/mmu-l3user.jpg) 2246 | 2247 | ## Page Tables 2248 | 2249 | _What are the 3 Levels of Page Tables?_ 2250 | 2251 | From the [MMU Log](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2252 | 2253 | lnvaddr=0x50407000 is m_l1_pgtable (Level 1 Page Table for Kernel) 2254 | 2255 | lnvaddr=0x50406000 is m_l2_pgtable (Level 2 Page Table for Kernel) 2256 | 2257 | lnvaddr=0x50404000 is m_l3_pgtable (Level 3 Page Table for Kernel) 2258 | 2259 | lnvaddr=0x50403000 is m_int_l3_pgtable (Level 3 Page Table for Kernel PLIC) 2260 | 2261 | lnvaddr=0x5061b000 is User L1 Page Table for Code, Data, Heap 2262 | 2263 | lnvaddr=0x50601000 is User L2 Page Table for Code, Data 2264 | 2265 | lnvaddr=0x50602000 is User L3 Page Table for Code, Data 2266 | 2267 | lnvaddr=0x50601000 is User L2 Page Table for Heap 2268 | 2269 | lnvaddr=0x5061b000 is User L3 Page Table for Heap 2270 | 2271 | # Fix the NuttX Memory Map for PLIC 2272 | 2273 | Read the article... 2274 | 2275 | - ["RISC-V Ox64 BL808 SBC: Sv39 Memory Management Unit"](https://lupyuen.github.io/articles/mmu) 2276 | 2277 | _Can we add L1 for 0xE000 0000 to access PLIC?_ 2278 | 2279 | [Nope it fails](https://gist.github.com/lupyuen/73906723edf5f1611c7829779b18668a). Because 0xE000 0000 is not aligned to 0x4000 0000 for L1. 2280 | 2281 | But when we add L1 for 0xC000 0000, it works! 2282 | 2283 | ```c 2284 | // Map PLIC 2285 | mmu_ln_map_region(1, PGT_L1_VBASE, 0xC0000000, 0xC0000000, 2286 | 0x40000000, MMU_IO_FLAGS); 2287 | ``` 2288 | 2289 | But NuttX Apps need to access 0xC000 0000. So NuttX Apps will fail... 2290 | 2291 | ```text 2292 | nx_start_application: Starting init task: /system/bin/init 2293 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x50602000, vaddr=0xc0100000, mmuflags=0x0 2294 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x5061b000, vaddr=0xc0200000, mmuflags=0x0 2295 | elf_addrenv_select: ERROR: up_addrenv_text_enable_write failed: -22 2296 | elf_load: ERROR: elf_addrenv_select() failed: -22 2297 | elf_loadbinary: Failed to load ELF program binary: -22 2298 | exec_internal: ERROR: Failed to load program '/system/bin/init': -22 2299 | _assert: Current Version: NuttX 12.0.3 47ec850-dirty Nov 11 2023 20:24:22 risc-v 2300 | _assert: Assertion failed ret > 0: at file: init/nx_bringup.c:302 task: AppBringUp process: Kernel 0x502014b0 2301 | ``` 2302 | 2303 | [(Source)](https://gist.github.com/lupyuen/124aa56c263e51d9306e0d70321f2864) 2304 | 2305 | _What if we map PLIC as L2 at 0xE000 0000? By reusing the existing L2 Page Tables?_ 2306 | 2307 | ```c 2308 | // Map PLIC as L2, by reusing the existing L2 Page Tables 2309 | map_region(0xE0000000, 0xE0000000, 0x10000000, MMU_IO_FLAGS); 2310 | ``` 2311 | 2312 | This fails with IRQ 5. The existing L2 Page Tables can't accommodate PLIC at 0xE000 0000. 2313 | 2314 | _What if we add L1 for 0xC000 0000? And move apps to 0x8000 0000?_ 2315 | 2316 | From [jh7110_mm_init.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_mm_init.c#L238-L246): 2317 | 2318 | ```c 2319 | // Map PLIC as L1 2320 | // This will waste a whole chunk of L1 Addresses (Size 0x4000 0000) just for PLIC: 2321 | mmu_ln_map_region(1, PGT_L1_VBASE, 0xC0000000, 0xC0000000, 2322 | 0x40000000, MMU_IO_FLAGS); 2323 | ``` 2324 | 2325 | NSH starts OK! Though we wasted a whole chunk of L1 Addresses (Size 0x4000 0000) just for PLIC... 2326 | 2327 | ```text 2328 | jh7110_kernel_mappings: map PLIC as L1 2329 | mmu_ln_map_region: ptlevel=1, lnvaddr=0x50406000, paddr=0xc0000000, vaddr=0xc0000000, size=0x40000000, mmuflags=0x26 2330 | mmu_ln_setentry: ptlevel=1, lnvaddr=0x50406000, paddr=0xc0000000, vaddr=0xc0000000, mmuflags=0x26 2331 | ... 2332 | uart_register: Registering /dev/console 2333 | work_start_lowpri: Starting low-priority kernel worker thread(s) 2334 | nx_start_application: Starting init task: /system/bin/init 2335 | mmu_ln_setentry: ptlevel=1, lnvaddr=0x50600000, paddr=0x50601000, vaddr=0x80100000, mmuflags=0x0 2336 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x50602000, vaddr=0x80100000, mmuflags=0x0 2337 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x5061b000, vaddr=0x80200000, mmuflags=0x0 2338 | elf_symname: Symbol has no name 2339 | elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3 2340 | elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3 2341 | up_exit: TCB=0x50409900 exiting 2342 | riscv_dispatch_irq: irq=8 2343 | 2344 | NuttShell (NSH) NuttX-12.0.3 2345 | riscv_dispatch_irq: irq=8 2346 | nsh> riscv_dispatch_irq: irq=8 2347 | riscv_dispatch_irq: irq=8 2348 | nx_start: CPU0: Beginning Idle Loop 2349 | ``` 2350 | 2351 | [(Source)](https://gist.github.com/lupyuen/0f4bd7efc4d2d2839eba5ad62349af35) 2352 | 2353 | _What if we map PLIC as a new L2 Page Table: Interrupt L2?_ 2354 | 2355 | From [jh7110_mm_init.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64a/arch/risc-v/src/jh7110/jh7110_mm_init.c#L250-L257): 2356 | 2357 | ```c 2358 | // Interrupt L2 Table Table 2359 | #define PGT_INT_L2_PBASE (uintptr_t)&m_int_l2_pgtable 2360 | #define PGT_INT_L2_VBASE PGT_INT_L2_PBASE 2361 | #define PGT_INT_L2_SIZE (512) /* Enough to map 1 GiB */ 2362 | static size_t m_int_l2_pgtable[PGT_INT_L2_SIZE] locate_data(".pgtables"); 2363 | 2364 | // Map PLIC as Interrupt L2 Table Table 2365 | mmu_ln_map_region(2, PGT_INT_L2_PBASE, 0xE0000000, 0xE0000000, 0x10000000, 2366 | MMU_IO_FLAGS); 2367 | 2368 | // Connect the L1 and Interrupt L2 Page Tables for PLIC 2369 | mmu_ln_setentry(1, PGT_L1_VBASE, PGT_INT_L2_PBASE, 0xE0000000, PTE_G); 2370 | ``` 2371 | 2372 | NSH starts OK yay! 2373 | 2374 | ```text 2375 | jh7110_kernel_mappings: map PLIC as Interrupt L2 2376 | mmu_ln_map_region: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, size=0x10000000, mmuflags=0x26 2377 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, mmuflags=0x26 2378 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0200000, vaddr=0xe0200000, mmuflags=0x26 2379 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xe0400000, vaddr=0xe0400000, mmuflags=0x26 2380 | ... 2381 | 2382 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xefa00000, vaddr=0xefa00000, mmuflags=0x26 2383 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xefc00000, vaddr=0xefc00000, mmuflags=0x26 2384 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50403000, paddr=0xefe00000, vaddr=0xefe00000, mmuflags=0x26 2385 | jh7110_kernel_mappings: connect the L1 and Interrupt L2 page tables for PLIC 2386 | mmu_ln_setentry: ptlevel=1, lnvaddr=0x50407000, paddr=0x50403000, vaddr=0xe0000000, mmuflags=0x20 2387 | ... 2388 | uart_register: Registering /dev/console 2389 | work_start_lowpri: Starting low-priority kernel worker thread(s) 2390 | nx_start_application: Starting init task: /system/bin/init 2391 | mmu_ln_setentry: ptlevel=1, lnvaddr=0x50600000, paddr=0x50601000, vaddr=0x80100000, mmuflags=0x0 2392 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x50602000, vaddr=0x80100000, mmuflags=0x0 2393 | mmu_ln_setentry: ptlevel=2, lnvaddr=0x50601000, paddr=0x5061b000, vaddr=0x80200000, mmuflags=0x0 2394 | elf_symname: Symbol has no name 2395 | elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3 2396 | elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3 2397 | up_exit: TCB=0x5040a900 exiting 2398 | riscv_dispatch_irq: irq=8 2399 | 2400 | NuttShell (NSH) NuttX-12.0.3 2401 | riscv_dispatch_irq: irq=8 2402 | nsh> riscv_dispatch_irq: irq=8 2403 | riscv_dispatch_irq: irq=8 2404 | nx_start: CPU0: Beginning Idle Loop 2405 | ``` 2406 | 2407 | [(Source)](https://gist.github.com/lupyuen/74c8cb0e519984f6392384f6cca3daff) 2408 | 2409 | TODO: Who maps the User Memory for `lnvaddr=0x50600000`? 2410 | 2411 | ![Ox64 boots to NuttX Shell](https://lupyuen.github.io/images/mmu-boot1.png) 2412 | 2413 | [_Ox64 boots to NuttX Shell_](https://gist.github.com/lupyuen/aa9b3e575ba4e0c233ab02c328221525) 2414 | 2415 | __Compute Level 1 PTE:__ 2416 | 2417 | Based the [Updated MMU Log with PTE](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2418 | 2419 | ```text 2420 | connect the L1 and Interrupt L2 page tables for PLIC 2421 | mmu_ln_setentry: 2422 | ptlevel=1, lnvaddr=0x50407000, paddr=0x50403000, vaddr=0xe0000000, mmuflags=0x20 2423 | mmu_ln_setentry: 2424 | index=0x3, 2425 | paddr=0x50403000, 2426 | mmuflags=0x21, 2427 | pte_addr=0x50407018, 2428 | pte_val=0x14100c21 2429 | ``` 2430 | 2431 | To compute the Address of Level 1 PTE: 2432 | 2433 | - pte_addr = lnvaddr + (index * 8) = 0x50407018 2434 | 2435 | (8 bytes per PTE) 2436 | 2437 | - index = vpn >> 18 = 3 2438 | 2439 | (Extract Bits 18 to 26 to get Level 1 Index) 2440 | 2441 | - vpn = vaddr >> 12 = 0xe0000 2442 | 2443 | (4096 bytes per Memory Page) 2444 | 2445 | To compute the Value of Level 1 PTE: 2446 | 2447 | - pte_val = (ppn << 10) | mmuflags = 0x14100c21 2448 | 2449 | (Shift 10 bits to accommodate MMU Flags) 2450 | 2451 | - ppn = paddr >> 12 = 0x50403 2452 | 2453 | (1<<12 bits per Memory Page) 2454 | 2455 | mmuflags=0x21 means 0b100001: 2456 | 2457 | - NOT Dirty 2458 | 2459 | - NOT Accessed 2460 | 2461 | - Global Mapping 2462 | 2463 | - NOT User-Accessible 2464 | 2465 | - NOT eXecutable 2466 | 2467 | - NOT Writeable 2468 | 2469 | - NOT Readable 2470 | 2471 | - Valid 2472 | 2473 | __Compute Level 2 PTE:__ 2474 | 2475 | Based the [Updated MMU Log with PTE](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2476 | 2477 | ```text 2478 | map PLIC as Interrupt L2 2479 | mmu_ln_map_region: 2480 | ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, size=0x10000000, mmuflags=0x26 2481 | mmu_ln_setentry: 2482 | ptlevel=2, lnvaddr=0x50403000, paddr=0xe0000000, vaddr=0xe0000000, mmuflags=0x26 2483 | mmu_ln_setentry: 2484 | index=0x100, 2485 | paddr=0xe0000000, 2486 | mmuflags=0xe7, 2487 | pte_addr=0x50403800, 2488 | pte_val=0x380000e7 2489 | ``` 2490 | 2491 | To compute the Address of Level 2 PTE: 2492 | 2493 | - pte_addr = lnvaddr + (index * 8) = 0x50403800 2494 | 2495 | (8 bytes per PTE) 2496 | 2497 | - index = (vpn >> 9) & 0b111111111 = 0x100 2498 | 2499 | (Extract Bits 9 to 17 to get Level 2 Index) 2500 | 2501 | - vpn = vaddr >> 12 = 0xe0000 2502 | 2503 | (4096 bytes per Memory Page) 2504 | 2505 | To compute the Value of Level 2 PTE: 2506 | 2507 | - pte_val = (ppn << 10) | mmuflags = 0x380000e7 2508 | 2509 | (Shift 10 bits to accommodate MMU Flags) 2510 | 2511 | - ppn = paddr >> 12 = 0xe0000 2512 | 2513 | (4096 bytes per Memory Page) 2514 | 2515 | mmuflags=0xe7 means 0b11100111: 2516 | 2517 | - Dirty 2518 | 2519 | - Accessed 2520 | 2521 | - Global Mapping 2522 | 2523 | - NOT User-Accessible 2524 | 2525 | - NOT eXecutable 2526 | 2527 | - Writeable 2528 | 2529 | - Readable 2530 | 2531 | - Valid 2532 | 2533 | __Compute Level 3 PTE:__ 2534 | 2535 | Based the [Updated MMU Log with PTE](https://gist.github.com/lupyuen/22712d6a2c3a7eb2da1f3cd5c2f4f6cf)... 2536 | 2537 | ```text 2538 | map kernel text 2539 | mmu_ln_setentry: 2540 | ptlevel=2, lnvaddr=0x50406000, paddr=0x50404000, vaddr=0x50200000, mmuflags=0x0 2541 | mmu_ln_setentry: 2542 | index=0x81, paddr=0x50404000, mmuflags=0x1, pte_addr=0x50406408, pte_val=0x14101001 2543 | ... 2544 | mmu_ln_setentry: ptlevel=3, lnvaddr=0x50404000, paddr=0x5020100, vaddr=0x50201000, mmuflags=0x2a 2545 | mmu_ln_setentry: 2546 | index=0x1, 2547 | paddr=0x50201000, 2548 | mmuflags=0xeb, 2549 | pte_addr=0x50404008, 2550 | pte_val=0x140804eb 2551 | ``` 2552 | 2553 | To compute the Address of Level 3 PTE: 2554 | 2555 | - pte_addr = lnvaddr + (index * 8) = 0x50404008 2556 | 2557 | (8 bytes per PTE) 2558 | 2559 | - index = vpn & 0b111111111 = 0x1 2560 | 2561 | (Extract Bits 0 to 8 to get Level 3 Index) 2562 | 2563 | - vpn = vaddr >> 12 = 0x50201 2564 | 2565 | (4096 bytes per Memory Page) 2566 | 2567 | To compute the Value of Level 3 PTE: 2568 | 2569 | - pte_val = (ppn << 10) | mmuflags = 0x140804eb 2570 | 2571 | (Shift 10 bits to accommodate MMU Flags) 2572 | 2573 | - ppn = paddr >> 12 = 0x50201 2574 | 2575 | (4096 bytes per Memory Page) 2576 | 2577 | mmuflags=0xeb means 0b11101011: 2578 | 2579 | - Dirty 2580 | 2581 | - Accessed 2582 | 2583 | - Global Mapping 2584 | 2585 | - NOT User-Accessible 2586 | 2587 | - eXecutable 2588 | 2589 | - NOT Writeable 2590 | 2591 | - Readable 2592 | 2593 | - Valid 2594 | 2595 | TODO: [G Bit](https://five-embeddev.com/riscv-isa-manual/latest/supervisor.html#sec:translation) 2596 | 2597 | > "The G bit designates a global mapping. Global mappings are those that exist in all address spaces. For non-leaf PTEs, the global setting implies that all mappings in the subsequent levels of the page table are global. Note that failing to mark a global mapping as global merely reduces performance, whereas marking a non-global mapping as global is a software bug that, after switching to an address space with a different non-global mapping for that address range, can unpredictably result in either mapping being used." 2598 | 2599 | TODO: [SATP Log](https://gist.github.com/lupyuen/aa9b3e575ba4e0c233ab02c328221525) 2600 | 2601 | ![NuttX swaps the SATP Register](https://lupyuen.github.io/images/mmu-boot2.jpg) 2602 | 2603 | [_NuttX swaps the SATP Register_](https://gist.github.com/lupyuen/aa9b3e575ba4e0c233ab02c328221525) 2604 | 2605 | # Start NuttX Apps on Ox64 BL808 2606 | 2607 | Read the article... 2608 | 2609 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 2610 | 2611 | NuttX Kernel starts a NuttX App (in ELF Format) by calling... 2612 | 2613 | - [__ELF Loader: g_elfbinfmt__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/elf.c#L84-L94), which calls... 2614 | 2615 | - [__elf_loadbinary__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/elf.c#L225-L355), which calls... 2616 | 2617 | - [__elf_load__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/libelf/libelf_load.c#L297-L445), which calls... 2618 | 2619 | - [__elf_addrenv_alloc__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/libelf/libelf_addrenv.c#L56-L178), which calls... 2620 | 2621 | - [__up_addrenv_create__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_addrenv.c#L339-L490), which calls... 2622 | 2623 | (Also calls [__mmu_satp_reg__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_mmu.h#L152-L176) to set SATP Register) 2624 | 2625 | - [__create_region__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_addrenv.c#L213-L310), which calls... 2626 | 2627 | - [__mmu_ln_setentry__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_mmu.c#L62-L109) to populate the Page Table Entries 2628 | 2629 | _Who calls [ELF Loader g_elfbinfmt](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/elf.c#L84-L94) to start the NuttX App?_ 2630 | 2631 | Earlier we stepped through the __Boot Sequence__ for NuttX... 2632 | 2633 | - [__"NuttX Boot Flow"__](https://lupyuen.github.io/articles/ox2#appendix-nuttx-boot-flow) 2634 | 2635 | Right after that, [__nx_bringup__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L373-L458) calls... 2636 | 2637 | - [__nx_create_initthread__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L330-L367), which calls... 2638 | 2639 | - [__nx_start_application__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L212C1-L302), which calls... 2640 | 2641 | - [__exec_spawn__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L183-L223), which calls... 2642 | 2643 | - [__exec_internal__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L42-L179), which calls... 2644 | 2645 | - [__load_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L136-L225) and... 2646 | 2647 | [__exec_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_execmodule.c#L190-L450) 2648 | 2649 | [__load_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L136-L225) calls... 2650 | 2651 | - [__load_absmodule__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L83-L132), which calls... 2652 | 2653 | - [__binfmt_s.load__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/master/include/nuttx/binfmt/binfmt.h#L122-L148), which calls... 2654 | 2655 | - [__ELF Loader: g_elfbinfmt__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/binfmt/elf.c#L84-L94) to load the ELF File (explained above) 2656 | 2657 | # Simple NuttX App for Ox64 BL808 2658 | 2659 | Read the article... 2660 | 2661 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 2662 | 2663 | _What's inside the simplest app for NuttX?_ 2664 | 2665 | From [hello_main.c](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/ox64b/examples/hello/hello_main.c#L36-L40) 2666 | 2667 | ```c 2668 | int main(int argc, FAR char *argv[]) { 2669 | printf("Hello, World!!\n"); 2670 | return 0; 2671 | } 2672 | ``` 2673 | 2674 | Here's the RISC-V Disassembly: [hello.S](https://github.com/lupyuen2/wip-pinephone-nuttx/releases/download/ox64a-1/hello.S) 2675 | 2676 | ```text 2677 | 000000000000003e
: 2678 | main(): 2679 | apps/examples/hello/hello_main.c:37 2680 | /**************************************************************************** 2681 | * hello_main 2682 | ****************************************************************************/ 2683 | 2684 | int main(int argc, FAR char *argv[]) 2685 | { 2686 | 3e: 1141 addi sp,sp,-16 2687 | apps/examples/hello/hello_main.c:38 2688 | printf("Hello, World!!\n"); 2689 | 40: 00000517 auipc a0,0x0 40: R_RISCV_PCREL_HI20 .LC0 2690 | 40: R_RISCV_RELAX *ABS* 2691 | 44: 00050513 mv a0,a0 44: R_RISCV_PCREL_LO12_I .L0 2692 | 44: R_RISCV_RELAX *ABS* 2693 | 2694 | 0000000000000048 <.LVL1>: 2695 | apps/examples/hello/hello_main.c:37 2696 | { 2697 | 48: e406 sd ra,8(sp) 2698 | apps/examples/hello/hello_main.c:38 2699 | printf("Hello, World!!\n"); 2700 | 4a: 00000097 auipc ra,0x0 4a: R_RISCV_CALL puts 2701 | 4a: R_RISCV_RELAX *ABS* 2702 | 4e: 000080e7 jalr ra # 4a <.LVL1+0x2> 2703 | 2704 | 0000000000000052 <.LVL2>: 2705 | apps/examples/hello/hello_main.c:40 2706 | return 0; 2707 | } 2708 | 52: 60a2 ld ra,8(sp) 2709 | 54: 4501 li a0,0 2710 | 56: 0141 addi sp,sp,16 2711 | 58: 8082 ret 2712 | ``` 2713 | 2714 | We see that [main](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/ox64b/examples/hello/hello_main.c#L36-L40) calls... 2715 | 2716 | - [puts](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/libs/libc/stdio/lib_puts.c#L34-L96), which calls... 2717 | 2718 | - [lib_fwrite_unlocked](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/libs/libc/stdio/lib_libfwrite.c#L45-L200), which calls... 2719 | 2720 | - [stream->fs_iofunc.write](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/libs/libc/stdio/lib_libfwrite.c#L145) OR... 2721 | 2722 | [write](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/libs/libc/stdio/lib_libfwrite.c#L149) (See below) 2723 | 2724 | TODO: Which one? 2725 | 2726 | TODO: _start prepares sig_trampoline and calls main 2727 | 2728 | _This code doesn't look right..._ 2729 | 2730 | ```text 2731 | apps/examples/hello/hello_main.c:38 2732 | printf("Hello, World!!\n"); 2733 | 4a: 00000097 auipc ra,0x0 2734 | 4e: 000080e7 jalr ra 2735 | ``` 2736 | 2737 | That's because this is __Relocatable Code__. The auipc offset will be fixed up by the NuttX ELF Loader when it loads this code into User Memory. 2738 | 2739 | The Relocation Info shows that 0x0 will be replaced by the address of `puts`... 2740 | 2741 | ```text 2742 | 4a: 00000097 auipc ra,0x0 2743 | 4a: R_RISCV_CALL puts 2744 | 4e: 000080e7 jalr ra 2745 | ``` 2746 | 2747 | _Why `puts` instead of `printf`?_ 2748 | 2749 | The GCC Compiler has cleverly optimised away `printf` to become `puts`. 2750 | 2751 | If we do this... 2752 | 2753 | ```c 2754 | printf("Hello, World %s!!\n", "Luppy"); 2755 | ``` 2756 | 2757 | Then `printf` will appear in our disassembly. 2758 | 2759 | # NuttX App calls NuttX Kernel 2760 | 2761 | ![NuttX App calls NuttX Kernel](https://lupyuen.github.io/images/app-syscall.jpg) 2762 | 2763 | Read the article... 2764 | 2765 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 2766 | 2767 | _How does a NuttX App make a System Call to NuttX Kernel?_ 2768 | 2769 | Our NuttX App calls `write`, which is a Proxy Version... 2770 | 2771 | ```c 2772 | // From nuttx/syscall/proxies/PROXY_write.c 2773 | /* Auto-generated write proxy file -- do not edit */ 2774 | #include 2775 | #include 2776 | #include 2777 | 2778 | ssize_t write(int parm1, FAR const void * parm2, size_t parm3) { 2779 | return (ssize_t)sys_call3((unsigned int)SYS_write, (uintptr_t)parm1, (uintptr_t)parm2, (uintptr_t)parm3); 2780 | } 2781 | ``` 2782 | Proxy for `write` calls [sys_call3](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/include/syscall.h), which makes an `ecall` to NuttX Kernel... 2783 | 2784 | ```c 2785 | // Make an `ecall` to NuttX Kernel 2786 | static inline uintptr_t sys_call3(unsigned int nbr, uintptr_t parm1, 2787 | uintptr_t parm2, uintptr_t parm3) { 2788 | register long r0 asm("a0") = (long)(nbr); 2789 | register long r1 asm("a1") = (long)(parm1); 2790 | register long r2 asm("a2") = (long)(parm2); 2791 | register long r3 asm("a3") = (long)(parm3); 2792 | asm volatile 2793 | ( 2794 | "ecall" 2795 | :: "r"(r0), "r"(r1), "r"(r2), "r"(r3) 2796 | : "memory" 2797 | ); 2798 | asm volatile("nop" : "=r"(r0)); 2799 | return r0; 2800 | } 2801 | ``` 2802 | 2803 | Here's how it works... 2804 | 2805 | ![NuttX App calls NuttX Kernel](https://lupyuen.github.io/images/app-run.png) 2806 | 2807 | We can see the list of proxies in the RISC-V Disassembly of the NuttX Apps... 2808 | 2809 | ```bash 2810 | ## Hello App 2811 | → grep PROXY hello.S 2812 | PROXY__assert.c 2813 | PROXY__exit.c 2814 | PROXY_clock_gettime.c 2815 | PROXY_gettid.c 2816 | PROXY_lseek.c 2817 | PROXY_nxsem_wait.c 2818 | PROXY_sem_clockwait.c 2819 | PROXY_sem_destroy.c 2820 | PROXY_sem_post.c 2821 | PROXY_sem_trywait.c 2822 | PROXY_task_setcancelstate.c 2823 | PROXY_write.c 2824 | 2825 | ## NuttX Shell NSH 2826 | → grep PROXY init.S 2827 | PROXY__assert.c 2828 | PROXY__exit.c 2829 | PROXY_clock_gettime.c 2830 | PROXY_gettid.c 2831 | PROXY_nxsem_wait.c 2832 | PROXY_sched_getparam.c 2833 | PROXY_sched_setparam.c 2834 | PROXY_sem_clockwait.c 2835 | PROXY_sem_destroy.c 2836 | PROXY_sem_post.c 2837 | PROXY_sem_trywait.c 2838 | PROXY_task_setcancelstate.c 2839 | PROXY_write.c 2840 | PROXY_boardctl.c 2841 | PROXY_clock_nanosleep.c 2842 | PROXY_close.c 2843 | PROXY_ftruncate.c 2844 | PROXY_get_environ_ptr.c 2845 | PROXY_getenv.c 2846 | PROXY_gethostname.c 2847 | PROXY_ioctl.c 2848 | PROXY_kill.c 2849 | PROXY_lseek.c 2850 | PROXY_lstat.c 2851 | PROXY_mkdir.c 2852 | PROXY_mount.c 2853 | PROXY_nx_pthread_create.c 2854 | PROXY_nx_pthread_exit.c 2855 | PROXY_nx_vsyslog.c 2856 | PROXY_open.c 2857 | PROXY_pgalloc.c 2858 | PROXY_posix_spawn.c 2859 | PROXY_pthread_detach.c 2860 | PROXY_read.c 2861 | PROXY_rename.c 2862 | PROXY_rmdir.c 2863 | PROXY_sched_getscheduler.c 2864 | PROXY_sched_lock.c 2865 | PROXY_sched_unlock.c 2866 | PROXY_setenv.c 2867 | PROXY_stat.c 2868 | PROXY_sysinfo.c 2869 | PROXY_umount2.c 2870 | PROXY_unlink.c 2871 | PROXY_unsetenv.c 2872 | PROXY_waitpid.c 2873 | ``` 2874 | 2875 | # Kernel Handles App Call 2876 | 2877 | Read the article... 2878 | 2879 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 2880 | 2881 | _Our App makes an ecall to jump to NuttX Kernel..._ 2882 | 2883 | _What happens on the other side?_ 2884 | 2885 | Remember the Proxy Function from earlier? Now we do the exact opposite in our __Stub Function__ (that runs in the Kernel)... 2886 | 2887 | ```c 2888 | // From nuttx/syscall/stubs/STUB_write.c 2889 | /* Auto-generated write stub file -- do not edit */ 2890 | #include 2891 | #include 2892 | #include 2893 | 2894 | uintptr_t STUB_write(int nbr, uintptr_t parm1, uintptr_t parm2, uintptr_t parm3) { 2895 | return (uintptr_t)write((int)parm1, (FAR const void *)parm2, (size_t)parm3); 2896 | } 2897 | ``` 2898 | 2899 | To handle IRQ 8 (RISCV_IRQ_ECALLU), NuttX does... 2900 | 2901 | - [Attach RISCV_IRQ_ECALLU](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_exception.c#L114-L119), which calls... 2902 | 2903 | - [riscv_swint](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_swint.c#L105-L537), which calls... 2904 | 2905 | - [dispatch_syscall](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_swint.c#L54-L100), which calls Kernel Function Stub and... 2906 | 2907 | - [sys_call2](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/supervisor/riscv_syscall.S#L49-L177) with A0=SYS_syscall_return (3), which calls... 2908 | 2909 | - [riscv_perform_syscall](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/supervisor/riscv_perform_syscall.c#L36-L78), which calls... 2910 | 2911 | - [riscv_swint](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/common/riscv_swint.c#L105-L537) with IRQ 0, to return from Syscall 2912 | 2913 | _How did we figure out that 63 is the System Call Number for "write"?_ 2914 | 2915 | OK this part gets tricky. Below is the Enum that defines all __System Call Numbers__: [syscall.h](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/include/sys/syscall.h#L55-L66) and [syscall_lookup.h](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/include/sys/syscall_lookup.h#L202) 2916 | 2917 | ```c 2918 | // System Call Enum sequentially assigns 2919 | // all System Call Numbers (8 to 147-ish) 2920 | enum { 2921 | ... 2922 | SYSCALL_LOOKUP(close, 1) 2923 | SYSCALL_LOOKUP(ioctl, 3) 2924 | SYSCALL_LOOKUP(read, 3) 2925 | SYSCALL_LOOKUP(write, 3) 2926 | ... 2927 | }; 2928 | ``` 2929 | 2930 | But it's an Enum, __numbered sequentially__ from 8 to 147-ish. We won't actually see 63 in the NuttX Source Code. 2931 | 2932 | When we look at the RISC-V Disassembly hello.S: 2933 | 2934 | ```text 2935 | ssize_t write(int parm1, FAR const void * parm2, size_t parm3) 2936 | { 2937 | dcc: 872a mv a4,a0 2938 | 2939 | 0000000000000dce <.LVL1>: 2940 | dce: 87ae mv a5,a1 2941 | 2942 | 0000000000000dd0 <.LVL2>: 2943 | dd0: 86b2 mv a3,a2 2944 | 2945 | 0000000000000dd2 <.LBB4>: 2946 | sys_call3(): 2947 | /Users/Luppy/ox64/nuttx/include/arch/syscall.h:252 2948 | register long r0 asm("a0") = (long)(nbr); 2949 | dd2: 03f00513 li a0,63 2950 | ``` 2951 | 2952 | We see that SYS_write = 63. Also from hello.S: 2953 | 2954 | ```text 2955 | <2><66e7>: Abbrev Number: 6 (DW_TAG_enumerator) 2956 | <66e8> DW_AT_name : (indirect string, offset: 0x4b98): SYS_write 2957 | <66ec> DW_AT_const_value : 63 2958 | ``` 2959 | 2960 | _What happens when we run this?_ 2961 | 2962 | In `make menuconfig`, enable CONFIG_DEBUG_SYSCALL_INFO: Build Setup > Debug Options > Syscall Debug Features > Syscall Warning / Error / Info 2963 | 2964 | The [NuttX Log](https://gist.github.com/lupyuen/ce82b29c664b1d5898b6a59743310c17) shows the System Calls from NuttX App to NuttX Kernel... 2965 | 2966 | ```text 2967 | riscv_dispatch_irq: irq=8 2968 | riscv_swint: Entry: regs: 0x5040bcb0 cmd: 63 2969 | up_dump_register: EPC: 00000000800019b2 2970 | up_dump_register: A0: 000000000000003f A1: 0000000000000001 A2: 000000008000ad00 A3: 000000000000001e 2971 | up_dump_register: A4: 0000000000000001 A5: 000000008000ad00 A6: 0000000000000000 A7: fffffffffffffff8 2972 | up_dump_register: T0: 0000000050212a20 T1: 0000000000000007 T2: 0000000000000000 T3: 0000000080200908 2973 | up_dump_register: T4: 0000000080200900 T5: 0000000000000000 T6: 0000000000000000 2974 | up_dump_register: S0: 00000000802005c0 S1: 0000000080202010 S2: 0000000080202010 S3: 0000000000000000 2975 | up_dump_register: S4: 0000000000000001 S5: 0000000000000000 S6: 0000000000000000 S7: 0000000000000000 2976 | up_dump_register: S8: 0000000000000000 S9: 0000000000000000 S10: 0000000000000000 S11: 0000000000000000 2977 | up_dump_register: SP: 0000000080202b70 FP: 00000000802005c0 TP: 0000000000000000 RA: 0000000080001a6a 2978 | riscv_swint: SWInt Return: 37 2979 | STUB_write: nbr=440, parm1=1, parm2=8000ad00, parm3=1e 2980 | 2981 | NuttShell (NSH) NuttX-12.0.3 2982 | riscv_swint: Entry: regs: 0x5040baa0 cmd: 3 2983 | up_dump_register: EPC: 0000000080001a6a 2984 | up_dump_register: A0: 0000000000000003 A1: 000000005040bbec A2: 000000000000001e A3: 0000000000000000 2985 | up_dump_register: A4: 0000000000007fff A5: 0000000000000001 A6: 0000000000000009 A7: fffffffffffffff8 2986 | up_dump_register: T0: 000000000000002e T1: 000000000000006a T2: 00000000000001ff T3: 000000000000006c 2987 | up_dump_register: T4: 0000000000000068 T5: 0000000000000009 T6: 000000000000002a 2988 | up_dump_register: S0: 00000000802005c0 S1: 0000000080202010 S2: 0000000080202010 S3: 0000000000000000 2989 | up_dump_register: S4: 0000000000000001 S5: 0000000000000000 S6: 0000000000000000 S7: 0000000000000000 2990 | up_dump_register: S8: 0000000000000000 S9: 0000000000000000 S10: 0000000000000000 S11: 0000000000000000 2991 | up_dump_register: SP: 000000005040bcb0 FP: 00000000802005c0 TP: 0000000000000000 RA: 0000000080001a6a 2992 | riscv_swint: SWInt Return: 1e 2993 | ``` 2994 | 2995 | _What are the Register Values?_ 2996 | 2997 | Before System Call: 2998 | 2999 | - A0 = 0x3f (SYS_write) 3000 | 3001 | - A1 = 1 (stdout) 3002 | 3003 | - A2 = 0x8000ad00 (g_nshgreeting) 3004 | 3005 | - A3 = 0x1e (length) 3006 | 3007 | - nbr = 440 (Offset for the stub lookup table, [g_stublookup](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/syscall/syscall_stublookup.c#L80-L93)) 3008 | 3009 | After System Call: 3010 | 3011 | - A0 = 3 [(SYS_syscall_return)](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/include/syscall.h#L80-L87) 3012 | 3013 | - Returns 0x1E = 30 chars, including [linefeeds before and after](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/ox64b/nshlib/nsh_parse.c#L292-L302) 3014 | 3015 | # Kernel Starts a NuttX App 3016 | 3017 | (To see the NuttX Source Code: Right-click the Node and select "Open Link") 3018 | 3019 | ```mermaid 3020 | flowchart TD 3021 | START --> nx_bringup["NuttX Bringup: \n nx_bringup"] 3022 | click nx_bringup href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L373-L458" "sched/init/nx_bringup.c" _blank 3023 | 3024 | nx_bringup --> nx_create_initthread["Create Init Thread: \n nx_create_initthread \n (Create the Init Thread)"] 3025 | click nx_create_initthread href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L330-L367" "sched/init/nx_bringup.c" _blank 3026 | 3027 | nx_create_initthread --> nx_start_application["Start App: \n nx_start_application \n (Start NuttX Shell)"] 3028 | click nx_start_application href "https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L212C1-L302" "sched/init/nx_bringup.c" _blank 3029 | 3030 | nx_start_application --> exec_spawn["Exec Spawn: \n exec_spawn \n (Start the app)"] 3031 | click exec_spawn href "https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L183-L223" "binfmt/binfmt_exec.c" _blank 3032 | 3033 | exec_spawn --> exec_internal["Exec Internal: \n exec_internal \n (Start the app)"] 3034 | click exec_internal href "https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L42-L179" "binfmt/binfmt_exec.c" _blank 3035 | 3036 | exec_internal --> load_module["Load Module: \n load_module \n (Load the app)"] 3037 | click load_module href "https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L136-L225" "binfmt/binfmt_loadmodule.c" _blank 3038 | 3039 | exec_internal --> exec_module["Execute Module: \n exec_module \n (Execute the app)"] 3040 | click exec_module href "https://github.com/apache/nuttx/blob/master/binfmt/binfmt_execmodule.c#L190-L450" "binfmt/binfmt_execmodule.c" _blank 3041 | 3042 | load_module --> load_absmodule["Load Absolute Module: \n load_absmodule \n (Load an absolute path)"] 3043 | click load_absmodule href "https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L83-L132" "binfmt/binfmt_loadmodule.c" _blank 3044 | 3045 | load_absmodule --> binfmt_s.load["Load Binary Format: \n binfmt_s.load \n (Load a binary module)"] 3046 | click binfmt_s.load href "https://github.com/apache/nuttx/blob/master/include/nuttx/binfmt/binfmt.h#L122-L148" "include/nuttx/binfmt/binfmt.h" _blank 3047 | 3048 | binfmt_s.load --> g_elfbinfmt["ELF Loader: \n g_elfbinfmt \n (Load the ELF File)"] 3049 | click g_elfbinfmt href "https://github.com/apache/nuttx/blob/master/binfmt/elf.c#L84-L94" "binfmt/elf.c" _blank 3050 | 3051 | g_elfbinfmt --> elf_loadbinary["Load ELF Binary: \n elf_loadbinary \n (Load the ELF Binary)"] 3052 | click elf_loadbinary href "https://github.com/apache/nuttx/blob/master/binfmt/elf.c#L225-L355" "binfmt/elf.c" _blank 3053 | 3054 | elf_loadbinary --> elf_load["Load ELF: \n elf_load \n (Load the ELF Binary)"] 3055 | click elf_load href "https://github.com/apache/nuttx/blob/master/binfmt/libelf/libelf_load.c#L297-L445" "binfmt/libelf/libelf_load.c" _blank 3056 | 3057 | elf_load --> elf_addrenv_alloc["Allocate Address Env: \n elf_addrenv_alloc \n (Allocate the Address Env)"] 3058 | click elf_addrenv_alloc href "https://github.com/apache/nuttx/blob/master/binfmt/libelf/libelf_addrenv.c#L56-L178" "binfmt/libelf/libelf_addrenv.c" _blank 3059 | 3060 | elf_addrenv_alloc --> up_addrenv_create["Create Address Env: \n up_addrenv_create \n (Create the Address Env)"] 3061 | click up_addrenv_create href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_addrenv.c#L339-L490" "arch/risc-v/src/common/riscv_addrenv.c" _blank 3062 | 3063 | elf_addrenv_alloc --> mmu_satp_reg["Set SATP Register: \n mmu_satp_reg \n (Set SATP Register)"] 3064 | click mmu_satp_reg href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.h#L152-L176" "arch/risc-v/src/common/riscv_mmu.h" _blank 3065 | 3066 | up_addrenv_create --> create_region["Create MMU Region: \n create_region \n (Create the MMU Region)"] 3067 | click create_region href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_addrenv.c#L213-L310" "arch/risc-v/src/common/riscv_addrenv.c" _blank 3068 | 3069 | create_region --> mmu_ln_setentry["Set MMU Page Table Entry: \n mmu_ln_setentry \n (Populate the Page Table Entries)"] 3070 | click mmu_ln_setentry href "https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L62-L109" "arch/risc-v/src/common/riscv_mmu.c" _blank 3071 | ``` 3072 | 3073 | Read the article... 3074 | 3075 | - ["RISC-V Ox64 BL808 SBC: NuttX Apps and Initial RAM Disk"](https://lupyuen.github.io/articles/app) 3076 | 3077 | _Phew so NuttX Apps can call NuttX Kernel..._ 3078 | 3079 | _But how does NuttX Kernel start a NuttX App?_ 3080 | 3081 | Earlier we stepped through the __Boot Sequence__ for NuttX... 3082 | 3083 | - [__"NuttX Boot Flow"__](https://lupyuen.github.io/articles/ox2#appendix-nuttx-boot-flow) 3084 | 3085 | Right after that, [__NuttX Bringup (nx_bringup)__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L373-L458) calls... 3086 | 3087 | - [__Create Init Thread: nx_create_initthread__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L330-L367) (to create the Init Thread), which calls... 3088 | 3089 | - [__Start App: nx_start_application__](https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L212C1-L302) (to start NuttX Shell), which calls... 3090 | 3091 | - [__Exec Spawn: exec_spawn__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L183-L223) (to start the app), which calls... 3092 | 3093 | - [__Exec Internal: exec_internal__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_exec.c#L42-L179) (to start the app), which calls... 3094 | 3095 | - [__Load Module: load_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L136-L225) (to load the app, see below) and... 3096 | 3097 | [__Execute Module: exec_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_execmodule.c#L190-L450) (to execute the app) 3098 | 3099 | To load a NuttX App module: [__load_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L136-L225) calls... 3100 | 3101 | - [__Load Absolute Module: load_absmodule__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_loadmodule.c#L83-L132) (to load an absolute path), which calls... 3102 | 3103 | - [__Load Binary Format: binfmt_s.load__](https://github.com/apache/nuttx/blob/master/include/nuttx/binfmt/binfmt.h#L122-L148) (to load a binary module), which calls... 3104 | 3105 | - [__ELF Loader: g_elfbinfmt__](https://github.com/apache/nuttx/blob/master/binfmt/elf.c#L84-L94) (to load the ELF File, see below) 3106 | 3107 | To load the ELF File: [__ELF Loader g_elfbinfmt__](https://github.com/apache/nuttx/blob/master/binfmt/elf.c#L84-L94) calls... 3108 | 3109 | - [__Load ELF Binary: elf_loadbinary__](https://github.com/apache/nuttx/blob/master/binfmt/elf.c#L225-L355) (to load the ELF Binary), which calls... 3110 | 3111 | - [__Load ELF: elf_load__](https://github.com/apache/nuttx/blob/master/binfmt/libelf/libelf_load.c#L297-L445) (to load the ELF Binary), which calls... 3112 | 3113 | - [__Allocate Address Env: elf_addrenv_alloc__](https://github.com/apache/nuttx/blob/master/binfmt/libelf/libelf_addrenv.c#L56-L178) (to allocate the Address Env), which calls... 3114 | 3115 | - [__Create Address Env: up_addrenv_create__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_addrenv.c#L339-L490) (to create the Address Env), which calls... 3116 | 3117 | (Also calls [__mmu_satp_reg__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.h#L152-L176) to set SATP Register) 3118 | 3119 | - [__Create MMU Region: create_region__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_addrenv.c#L213-L310) (to create the MMU Region), which calls... 3120 | 3121 | - [__Set MMU Page Table Entry: mmu_ln_setentry__](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L62-L109) (to populate the Page Table Entries) 3122 | 3123 | There's plenty happening inside [__Execute Module: exec_module__](https://github.com/apache/nuttx/blob/master/binfmt/binfmt_execmodule.c#L190-L450). But we won't explore today. 3124 | 3125 | # UART Interrupt for Ox64 BL808 3126 | 3127 | ![Platform-Level Interrupt Controller for Pine64 Ox64 64-bit RISC-V SBC (Bouffalo Lab BL808)](https://lupyuen.github.io/images/plic2-registers.jpg) 3128 | 3129 | Read the article... 3130 | 3131 | - ["RISC-V Ox64 BL808 SBC: UART Interrupt and Platform-Level Interrupt Controller (PLIC)"](https://lupyuen.github.io/articles/plic2) 3132 | 3133 | Let's fix the UART Interrupts for NuttX on Ox64 BL808! 3134 | 3135 | We fix the PLIC Offsets according to [C906 User Manual (Page 77)](https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf) 3136 | 3137 | _What's the UART3 IRQ?_ 3138 | 3139 | From the Linux Device Tree... 3140 | 3141 | ```text 3142 | serial@30002000 { 3143 | compatible = "bflb,bl808-uart"; 3144 | reg = <0x30002000 0x1000>; 3145 | interrupts = <0x14 0x04>; 3146 | clocks = <0x04>; 3147 | status = "okay"; 3148 | phandle = <0x0a>; 3149 | }; 3150 | ``` 3151 | 3152 | Thus... 3153 | 3154 | - RISC-V IRQ = 0x14 = 20 3155 | 3156 | - UART3 Int = (IRQ_NUM_BASE + 4) 3157 | 3158 | - IRQ_NUM_BASE = 16 3159 | 3160 | - NuttX IRQ = 45 (Offset by RISCV_IRQ_EXT) 3161 | 3162 | - RISCV_IRQ_EXT = 25 3163 | 3164 | We show the UART Interrupt Status... 3165 | 3166 | ```text 3167 | bl602_attach: BL602_UART_INT_STS=0x84 3168 | bl602_attach: BL602_UART_INT_MASK=0xfff 3169 | bl602_attach: BL602_UART_INT_CLEAR=0x0 3170 | bl602_attach: BL602_UART_INT_EN=0xfff 3171 | ``` 3172 | 3173 | [(Source)](https://gist.github.com/lupyuen/c3f187af9f5c81594ddf8f854de2ed0a) 3174 | 3175 | "urx_fer_int = 1" means "UART RX FIFO error interrupt, auto-cleared when FIFO overflow/underflow error flag is cleared" 3176 | 3177 | We clear the RX FIFO Underflow, but still no UART Interrupts... 3178 | 3179 | ```text 3180 | bl602_attach: BL602_UART_FIFO_CONFIG_0=0x80 3181 | bl602_attach: BL602_UART_FIFO_CONFIG_0=0x8 3182 | ``` 3183 | 3184 | We dump the PLIC and UART Registers in U-Boot... 3185 | 3186 | ```bash 3187 | ## UART Registers 3188 | => md 0x30002000 0x36 3189 | 30002000: 00001705 00000701 00130013 00000000 ................ 3190 | 30002010: 009f0070 0000006f 0000000f 00000000 p...o.......... 3191 | 30002020: 00000012 00000fff 00000000 00000fff ................ 3192 | 30002030: 00000001 00000000 00000000 00000000 ................ 3193 | 30002040: 00000000 00000000 00000003 00000000 ................ 3194 | 30002050: 0026ffff 00000002 00000000 00000000 ..&............. 3195 | 30002060: 00000000 00000000 00000000 00000000 ................ 3196 | 30002070: 00000000 00000000 00000000 00000000 ................ 3197 | 30002080: 00000000 07070000 0000000a 00000078 ............x... 3198 | 30002090: 00000000 00000000 00000000 00000000 ................ 3199 | 300020a0: 00000000 00000000 00000000 00000000 ................ 3200 | 300020b0: 00000000 00000000 00000000 00000000 ................ 3201 | 300020c0: 00000000 00000000 00000000 00000000 ................ 3202 | 300020d0: 00000000 00000000 ........ 3203 | 3204 | ## PLIC Interrupt Priority 3205 | => md 0xe0000004 0x50 3206 | e0000004: 00000000 00000000 00000000 00000000 ................ 3207 | e0000014: 00000000 00000000 00000000 00000000 ................ 3208 | e0000024: 00000000 00000000 00000000 00000000 ................ 3209 | e0000034: 00000000 00000000 00000000 00000000 ................ 3210 | e0000044: 00000000 00000000 00000000 00000000 ................ 3211 | e0000054: 00000000 00000000 00000000 00000000 ................ 3212 | e0000064: 00000000 00000000 00000000 00000000 ................ 3213 | e0000074: 00000000 00000000 00000000 00000000 ................ 3214 | e0000084: 00000000 00000000 00000000 00000000 ................ 3215 | e0000094: 00000000 00000000 00000000 00000000 ................ 3216 | e00000a4: 00000000 00000000 00000000 00000000 ................ 3217 | e00000b4: 00000000 00000000 00000000 00000000 ................ 3218 | e00000c4: 00000000 00000000 00000000 00000000 ................ 3219 | e00000d4: 00000000 00000000 00000000 00000000 ................ 3220 | e00000e4: 00000000 00000000 00000000 00000000 ................ 3221 | e00000f4: 00000000 00000000 00000000 00000000 ................ 3222 | e0000104: 00000000 00000000 00000000 00000000 ................ 3223 | e0000114: 00000000 00000000 00000000 00000000 ................ 3224 | e0000124: 00000000 00000000 00000000 00000000 ................ 3225 | e0000134: 00000000 00000000 00000000 00000000 ................ 3226 | 3227 | ## PLIC Hart 0 S-Mode Interrupt Enable 3228 | => md 0xe0002080 2 3229 | e0002080: 00000000 00000000 ........ 3230 | 3231 | ## PLIC Hart 0 S-Mode Priority Threshold 3232 | => md 0xe0201000 2 3233 | e0201000: 00000007 00000000 ........ 3234 | 3235 | ## PLIC Hart 0 S-Mode Claim / Complete 3236 | => md 0xe0201004 1 3237 | e0201004: 00000000 .... 3238 | 3239 | ## Interrupt Pending 3240 | => md 0xe0001000 2 3241 | e0001000: 00000000 00000000 ........ 3242 | 3243 | ## PLIC Hart 0 M-Mode Interrupt Enable 3244 | => md 0xe0002000 2 3245 | e0002000: 00000000 00000000 ........ 3246 | 3247 | ## PLIC Hart 0 M-Mode Priority Threshold 3248 | => md 0xe0200000 2 3249 | e0200000: 00000007 00000000 ........ 3250 | 3251 | ## PLIC Hart 0 M-Mode Claim / Complete 3252 | => md 0xe0200004 1 3253 | e0200004: 00000000 .... 3254 | 3255 | ## Doesn't work: PLIC permission control register 3256 | ## md 0xe01ffffc 1 3257 | ``` 3258 | 3259 | _Claim / Complete M-Mode seems OK in U-Boot. Can we Claim / Complete M-Mode in NuttX S-Mode?_ 3260 | 3261 | ```text 3262 | => md 0xe0200004 1 3263 | e0200004: 00000000 .... 3264 | => mw 0xe0200004 0x00 1 3265 | ``` 3266 | 3267 | Yes we can Claim / Complete M-Mode in NuttX S-Mode. But S-Mode Claim is still 0... 3268 | 3269 | ```text 3270 | PLIC Hart 0 M-Mode Claim / Complete (0xe0200004): 3271 | 0000 07 00 00 00 .... 3272 | bl602_attach: Claim / Complete M-Mode: claim=7 3273 | PLIC Hart 0 M-Mode Claim / Complete (0xe0200004): 3274 | 0000 00 00 00 00 .... 3275 | ... 3276 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3277 | ``` 3278 | 3279 | TODO: Why UART Interrupt not enabled? U-Boot and OpenSBI don't use UART Interrupts? 3280 | 3281 | TODO: What is Priority Threshold 7? 3282 | 3283 | But after enabling UART IRQ, PLIC Interrupt Priority is 0 and Invalid! 3284 | 3285 | ```text 3286 | PLIC Interrupt Priority (0xe0000004): 3287 | 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3288 | 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3289 | 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3290 | 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3291 | 0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3292 | ``` 3293 | 3294 | We set PLIC Interrupt Priority to 1 ourselves... 3295 | 3296 | ```text 3297 | bl602_attach: Set PLIC Interrupt Priority to 1 3298 | PLIC Interrupt Priority (0xe0000004): 3299 | 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3300 | 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3301 | 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3302 | 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3303 | 0040 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................ 3304 | 0050 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 ................ 3305 | 0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3306 | 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3307 | 0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3308 | 0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3309 | 00a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3310 | 00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3311 | 00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3312 | 00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ............... 3313 | 00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3314 | 00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3315 | 0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3316 | 0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3317 | 0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 3318 | 0130 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................ 3319 | ``` 3320 | 3321 | Then [IRQ 25 is OK yay!](https://gist.github.com/lupyuen/af6112c80db6907e5e5dec3519af53ff) 3322 | 3323 | ```text 3324 | riscv_dispatch_irq: irq=8 3325 | riscv_dispatch_irq: irq=8 3326 | 3327 | NuttShell (NSH) NuttX-12.0.3 3328 | riscv_dispatch_irq: irq=25 3329 | riscv_dispatch_irq: irq=25 3330 | riscv_dispatch_irq: irq=25 3331 | ``` 3332 | 3333 | But UART Interrupt can't be handled because [PLIC Claim is 0](https://gist.github.com/lupyuen/e1e6bf670ee4eefa0f968f1901407419)... 3334 | 3335 | ```text 3336 | riscv_dispatch_irq: Do irq=8 3337 | NuttShell (NSH) NuttX-12.0.3 3338 | riscv_dispatch_irq: irq=25, claim=0 3339 | riscv_dispatch_irq: *0xe0201004=0 3340 | PLIC Interrupt Pending (0xe0001000): 3341 | 0000 00 00 10 00 00 00 10 00 ........ 3342 | ``` 3343 | 3344 | _Why is PLIC Claim = 0?_ 3345 | 3346 | This means that there are no External Interrupts triggered. So NuttX does nothing, again and again, till it becomes too busy to respond. 3347 | 3348 | But Interrupt Pending is actually set for 2 External Interrupts! 3349 | 3350 | _What are the 2 Interrupts Pending?_ 3351 | 3352 | ```text 3353 | PLIC Interrupt Pending (0xe0001000): 3354 | 0000 00 00 10 00 00 00 10 00 ........ 3355 | ``` 3356 | 3357 | - (0xe0001000) PLIC_IP0: Interrupt Pending for interrupts 1 to 31 3358 | 3359 | 0x100000 = (1 << 20) = IRQ 20 (UART3) 3360 | 3361 | - (0xe0001004) PLIC_IP1: Interrupt Pending for interrupts 32 to 63 3362 | 3363 | 0x100000 = (1 << 20) = IRQ 52 (EMAC2) 3364 | 3365 | TODO: Why EMAC2? We didn't enable the interrupt 3366 | 3367 | We [handle Interrupt Pending](https://gist.github.com/lupyuen/84959d9ba79498a13a759b5b86c6fa29) ourselves... 3368 | 3369 | ```text 3370 | riscv_dispatch_irq: irq=25, claim=0 3371 | riscv_dispatch_irq: *0xe0201004=0 3372 | PLIC Interrupt Pending (0xe0001000): 3373 | 0000 00 00 10 00 00 00 10 00 ........ 3374 | riscv_dispatch_irq: Do irq=45 3375 | After Claim (0xe0001000): 3376 | 0000 00 00 10 00 00 00 10 00 ........ 3377 | 3378 | riscv_dispatch_irq: irq=25, claim=0 3379 | riscv_dispatch_irq: *0xe0201004=0 3380 | PLIC Interrupt Pending (0xe0001000): 3381 | 0000 00 00 10 00 00 00 10 00 ........ 3382 | riscv_dispatch_irq: Do irq=45 3383 | After Claim (0xe0001000): 3384 | 0000 00 00 10 00 00 00 10 00 ........ 3385 | ``` 3386 | 3387 | "Do IRQ" now works! But Interrupt Pending is not cleared, after we Claimed the interrupt. 3388 | 3389 | _Doesn't NuttX already implement C906 PLIC?_ 3390 | 3391 | Yep but for Machine Mode only... 3392 | 3393 | - [c906_irq.c](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/c906/c906_irq.c) 3394 | 3395 | - [c906_irq_dispatch.c](https://github.com/apache/nuttx/blob/master/arch/risc-v/src/c906/c906_irq_dispatch.c) 3396 | 3397 | _What if we copy this code into Ox64 PLIC?_ 3398 | 3399 | ```c 3400 | // From arch/risc-v/src/c906/c906_irq.c 3401 | /* Clear pendings in PLIC */ 3402 | uintptr_t val = getreg32(JH7110_PLIC_CLAIM); 3403 | putreg32(val, JH7110_PLIC_CLAIM); 3404 | ``` 3405 | 3406 | Still the same, Claim = 0... 3407 | 3408 | ```text 3409 | riscv_dispatch_irq: irq=25, claim=0 3410 | riscv_dispatch_irq: *0xe0201004=0 3411 | PLIC Interrupt Pending (0xe0001000): 3412 | 0000 00 00 10 00 00 00 10 00 ........ 3413 | ``` 3414 | 3415 | _Something special about T-Head C906 PLIC?_ 3416 | 3417 | According to the Linux Device Tree, Ox64 uses this PLIC Driver: "thead,c900-plic" 3418 | 3419 | ```text 3420 | interrupt-controller@e0000000 { 3421 | compatible = "thead,c900-plic"; 3422 | ``` 3423 | 3424 | Then from this [Linux Patch](https://lore.kernel.org/lkml/CAJF2gTS8Z+6Ewy0D5+0X_h2Jz4BqsJp7wEC5F0iNaDsSpiE2aw@mail.gmail.com/) 3425 | 3426 | > "The T-HEAD C9xx SoC implements a modified/custom T-HEAD PLIC 3427 | specification which will mask current IRQ upon read to CLAIM register 3428 | and will unmask the IRQ upon write to CLAIM register. The 3429 | thead,c900-plic compatible string represents the custom T-HEAD PLIC 3430 | specification." 3431 | 3432 | "thead,c900-plic" is implemented in Linux here: [irq-sifive-plic.c](https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-sifive-plic.c#L574-L582) 3433 | 3434 | Which sets [PLIC_QUIRK_EDGE_INTERRUPT](https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-sifive-plic.c#L64), which is used by... 3435 | 3436 | - [plic_irq_set_type](https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-sifive-plic.c#L212-L235) 3437 | 3438 | - [plic_irq_domain_translate](https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-sifive-plic.c#L312-L325) 3439 | 3440 | Which calls [irq_domain_translate_twocell](https://github.com/torvalds/linux/blob/master/kernel/irq/irqdomain.c#L1060-L1081) 3441 | 3442 | (Instead of the normal [irq_domain_translate_onecell](https://github.com/torvalds/linux/blob/master/kernel/irq/irqdomain.c#L1043-L1060)) 3443 | 3444 | _What does it do?_ 3445 | 3446 | From another [Linux Patch](https://lore.kernel.org/all/20220630100241.35233-3-samuel@sholland.org/) 3447 | 3448 | > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The 3449 | NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In 3450 | case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt 3451 | edge until the previous completion message has been received and 3452 | NCEPLIC100 doesn't support pending interrupt counter, hence losing the 3453 | interrupts if not acknowledged in time. 3454 | 3455 | > So the workaround for edge-triggered interrupts to be handled correctly 3456 | and without losing is that it needs to be acknowledged first and then 3457 | handler must be run so that we don't miss on the next edge-triggered 3458 | interrupt. 3459 | 3460 | TODO: Fix this for Ox64 NuttX 3461 | 3462 | After Clearing the Pending Interrupts, [NuttX responds to Key Presses yay!](https://gist.github.com/lupyuen/4e8ca1f0c0c2bd3b22a8b63f098abdd5) 3463 | 3464 | ```text 3465 | NuttShell (NSH) NuttX-12.0.3 3466 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3467 | PLIC Interrupt Pending (0xe0001000): 3468 | 0000 00 00 00 00 00 00 00 00 ........ 3469 | nsh> riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3470 | PLIC Interrupt Pending (0xe0001000): 3471 | 0000 00 00 00 00 00 00 00 00 ........ 3472 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3473 | PLIC Interrupt Pending (0xe0001000): 3474 | 0000 00 00 00 00 00 00 00 00 ........ 3475 | nx_start: CPU0: Beginning Idle Loop 3476 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3477 | PLIC Interrupt Pending (0xe0001000): 3478 | 0000 00 00 00 00 00 00 00 00 ........ 3479 | ˇriscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3480 | PLIC Interrupt Pending (0xe0001000): 3481 | 0000 00 00 00 00 00 00 00 00 ........ 3482 | ``` 3483 | 3484 | But Claim is still 0 though. 3485 | 3486 | _Does it work for UART Input?_ 3487 | 3488 | Since we've correctly identified the IRQ Number, [__riscv_dispatch_irq__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/jh7110/jh7110_irq_dispatch.c#L48-L105) will (eventually) call [__bl602_receive__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/jh7110/bl602_serial.c#L859-L904) to read the UART Input (pic below)... 3489 | 3490 | ```text 3491 | bl602_receive: rxdata=-1 3492 | bl602_receive: rxdata=0x0 3493 | ``` 3494 | 3495 | But the [__UART Input is empty__](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/jh7110/bl602_serial.c#L892-L901)! We need to troubleshoot our UART Driver some more. 3496 | 3497 | [(See the __Complete Log__)](https://gist.github.com/lupyuen/cf32c834f4f5b8f66715ee4c606b7580#file-ox64-nuttx-int-clear-pending2-log-L294-L325) 3498 | 3499 | [(Watch the __Demo on YouTube__)](https://youtu.be/VSTpsSJ_7L0) 3500 | 3501 | ![NuttX boots OK on Ox64 BL808! But UART Input is null](https://lupyuen.github.io/images/plic2-run.png) 3502 | 3503 | # Strangeness in Ox64 BL808 PLIC 3504 | 3505 | Read the article... 3506 | 3507 | - ["RISC-V Ox64 BL808 SBC: UART Interrupt and Platform-Level Interrupt Controller (PLIC)"](https://lupyuen.github.io/articles/plic2) 3508 | 3509 | _PLIC in Ox64 BL808 is acting really strange..._ 3510 | 3511 | _Why is Interrupt Priority set for 4 Interrupts, when we only set 1 (for UART)?_ 3512 | 3513 | ```text 3514 | bl602_attach: Set PLIC Interrupt Priority to 1 3515 | PLIC Interrupt Priority (0xe0000004): 3516 | ... 3517 | 0040 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................ 3518 | 0050 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 ................ 3519 | ``` 3520 | 3521 | _Maybe it's a problem with the RISC-V Code generated by GCC?_ 3522 | 3523 | Let's do a simple test: [bl602_serial.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64b/arch/risc-v/src/jh7110/bl602_serial.c#L447-L473) 3524 | 3525 | ```c 3526 | // Test the setting of PLIC Interrupt Priority 3527 | void test_interrupt_priority(void) { 3528 | static uint32_t before1 = 0xFF; 3529 | static uint32_t before2 = 0xFF; 3530 | static uint32_t after1 = 0xFF; 3531 | static uint32_t after2 = 0xFF; 3532 | 3533 | // Read the values before setting Interrupt Priority 3534 | before1 = *(volatile uint32_t *) 0xe0000050UL; 3535 | before2 = *(volatile uint32_t *) 0xe0000054UL; 3536 | 3537 | // Set the Interrupt Priority 3538 | *(volatile uint32_t *) 0xe0000050UL = 1; 3539 | 3540 | // Read the values after setting Interrupt Priority 3541 | after1 = *(volatile uint32_t *) 0xe0000050UL; 3542 | after2 = *(volatile uint32_t *) 0xe0000054UL; 3543 | _info("before1=%u, before2=%u, after1=%u, after2=%u\n", before1, before2, after1, after2); 3544 | } 3545 | ``` 3546 | 3547 | The Interrupt Priority [wasn't be set correctly](https://gist.github.com/lupyuen/4e8ca1f0c0c2bd3b22a8b63f098abdd5). Why did 0xe0000054 change from 0 to 1? 3548 | 3549 | ```text 3550 | bl602_attach: Test Interrupt Priority 3551 | test_interrupt_priority: before1=0, before2=0, after1=1, after2=1 3552 | ``` 3553 | 3554 | Here's the Disassembly, which looks OK... 3555 | 3556 | ```text 3557 | 0000000050200daa : 3558 | test_interrupt_priority(): 3559 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:451 3560 | uint32_t before1 = *(volatile uint32_t *) 0xe0000050; 3561 | 50200daa: 461d li a2,7 3562 | 50200dac: 0676 slli a2,a2,0x1d 3563 | 3564 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:454 3565 | *(volatile uint32_t *) 0xe0000050 = 1; 3566 | 50200dae: 4785 li a5,1 3567 | 3568 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:451 3569 | uint32_t before1 = *(volatile uint32_t *) 0xe0000050; 3570 | 50200db0: 4a34 lw a3,80(a2) 3571 | 3572 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:452 3573 | uint32_t before2 = *(volatile uint32_t *) 0xe0000054; 3574 | 50200db2: 4a78 lw a4,84(a2) 3575 | 3576 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:454 3577 | *(volatile uint32_t *) 0xe0000050 = 1; 3578 | 50200db4: ca3c sw a5,80(a2) 3579 | 3580 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:457 3581 | uint32_t after1 = *(volatile uint32_t *) 0xe0000050; 3582 | 50200db6: 4a3c lw a5,80(a2) 3583 | 3584 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:458 3585 | uint32_t after2 = *(volatile uint32_t *) 0xe0000054; 3586 | 50200db8: 05462803 lw a6,84(a2) 3587 | ``` 3588 | 3589 | _Maybe we need to flush the CPU Cache?_ 3590 | 3591 | Nope `sfence` doesn't help... 3592 | 3593 | ```text 3594 | 0000000050200daa : 3595 | test_interrupt_priority(): 3596 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:451 3597 | uint32_t before1 = *(volatile uint32_t *) 0xe0000050; 3598 | 50200daa: 461d li a2,7 3599 | 50200dac: 0676 slli a2,a2,0x1d 3600 | 50200dae: 4a34 lw a3,80(a2) 3601 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:452 3602 | uint32_t before2 = *(volatile uint32_t *) 0xe0000054; 3603 | 50200db0: 4a78 lw a4,84(a2) 3604 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:455 3605 | *(volatile uint32_t *) 0xe0000050 = 1; 3606 | 50200db2: 4785 li a5,1 3607 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:451 3608 | uint32_t before1 = *(volatile uint32_t *) 0xe0000050; 3609 | 50200db4: 2681 sext.w a3,a3 3610 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:452 3611 | uint32_t before2 = *(volatile uint32_t *) 0xe0000054; 3612 | 50200db6: 2701 sext.w a4,a4 3613 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:455 3614 | *(volatile uint32_t *) 0xe0000050 = 1; 3615 | 50200db8: ca3c sw a5,80(a2) 3616 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:458 3617 | __asm__ __volatile__ 3618 | 50200dba: 12000073 sfence.vma 3619 | 50200dbe: 0330000f fence rw,rw 3620 | 50200dc2: 0000100f fence.i 3621 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:467 3622 | uint32_t after1 = *(volatile uint32_t *) 0xe0000050; 3623 | 50200dc6: 4a3c lw a5,80(a2) 3624 | 50200dc8: 2781 sext.w a5,a5 3625 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:469 3626 | __asm__ __volatile__ 3627 | 50200dca: 12000073 sfence.vma 3628 | 50200dce: 0330000f fence rw,rw 3629 | 50200dd2: 0000100f fence.i 3630 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:477 3631 | uint32_t after2 = *(volatile uint32_t *) 0xe0000054; 3632 | 50200dd6: 05462803 lw a6,84(a2) 3633 | 50200dda: 2801 sext.w a6,a6 3634 | /Users/Luppy/ox64/nuttx/arch/risc-v/src/chip/bl602_serial.c:479 3635 | __asm__ __volatile__ 3636 | 50200ddc: 12000073 sfence.vma 3637 | 50200de0: 0330000f fence rw,rw 3638 | 50200de4: 0000100f fence.i 3639 | ``` 3640 | 3641 | Let's do the same with U-Boot Bootloader. It looks OK, doesn't have the same problem... 3642 | 3643 | ```bash 3644 | ## Read the values before setting Interrupt Priority 3645 | => md 0xe0000050 1 3646 | e0000050: 00000000 .... 3647 | => md 0xe0000054 1 3648 | e0000054: 00000000 .... 3649 | 3650 | ## Set the Interrupt Priority 3651 | => mw 0xe0000050 0x01 1 3652 | 3653 | ## Read the values after setting Interrupt Priority 3654 | => md 0xe0000050 1 3655 | e0000050: 00000001 .... 3656 | => md 0xe0000054 1 3657 | e0000054: 00000000 .... 3658 | ``` 3659 | 3660 | And U-Boot doesn't use MMU. 3661 | 3662 | _Could it be caused by MMU?_ 3663 | 3664 | Let's try setting Interrupt Priority before MMU Init. It works OK! 3665 | 3666 | ```text 3667 | 123jh7110_copy_ramdisk: _edata=0x50400258, _sbss=0x504002a0, _ebss=0x50408000, JH7110_IDLESTACK_TOP=0x50408c00 3668 | jh7110_copy_ramdisk: ramdisk_addr=0x50410291 3669 | jh7110_copy_ramdisk: size=8192000 3670 | ABCjh7110_mm_init: Test Interrupt Priority 3671 | test_interrupt_priority: before1=0, before2=0, after1=1, after2=0 3672 | jh7110_kernel_mappings: map I/O regions 3673 | ``` 3674 | 3675 | When we set Interrupt Priority after MMU Init, it looks incorrect... 3676 | 3677 | ```text 3678 | 123jh7110_copy_ramdisk: _edata=0x50400258, _sbss=0x504002a0, _ebss=0x50408000, JH7110_IDLESTACK_TOP=0x50408c00 3679 | jh7110_copy_ramdisk: ramdisk_addr=0x50410291 3680 | jh7110_copy_ramdisk: size=8192000 3681 | ABCjh7110_kernel_mappings: map I/O regions 3682 | jh7110_kernel_mappings: map PLIC as Interrupt L2 3683 | jh7110_kernel_mappings: connect the L1 and Interrupt L2 page tables for PLIC 3684 | jh7110_kernel_mappings: map kernel text 3685 | jh7110_kernel_mappings: map kernel data 3686 | jh7110_kernel_mappings: connect the L1 and L2 page tables 3687 | jh7110_kernel_mappings: map the page pool 3688 | mmu_satp_reg: pgbase=0x50407000, asid=0x0, reg=0x8000000000050407 3689 | mmu_write_satp: reg=0x8000000000050407 3690 | jh7110_mm_init: Test Interrupt Priority 3691 | test_interrupt_priority: before1=0, before2=0, after1=0, after2=0 3692 | nx_start: Entry 3693 | ``` 3694 | 3695 | So it's an MMU problem! 3696 | 3697 | _Why is MMU messing up our updates to Ox64 BL808 PLIC?_ 3698 | 3699 | We might have missed something specific to C906 MMU. Here are the Extended Page Attributes, from [C906 User Manual (Page 53)](https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf) 3700 | 3701 | - __SO – Strong order__ (Bit 63) 3702 | 3703 | Indicates the access order required by memory. 3704 | 3705 | 0: no strong order (Normal-memory) 3706 | 3707 | 1: strong order (Device) 3708 | 3709 | The default value is no strong order. 3710 | 3711 | - __C – Cacheable__ (Bit 62) 3712 | 3713 | 0: Non-cacheable 3714 | 3715 | 1: Cacheable 3716 | 3717 | The default value is Non-cacheable. 3718 | 3719 | - __B – Buffer__ (Bit 61) 3720 | 3721 | 0: Non-bufferable 3722 | 3723 | 1: Bufferable 3724 | 3725 | The default value is Non-bufferable 3726 | 3727 | Also... 3728 | 3729 | > "C906 extended page attributes exist only when the MAEE bit in the MXSTATUS register is 1." 3730 | 3731 | __So Beware:__ T-Head MMU Flags (Strong Order / Shareable) are available only if OpenSBI has set the __MAEE Bit in the MXSTATUS Register to 1__. Otherwise the MMU will crash when we set the flags! 3732 | 3733 | __For Ox64 (T-Head C906):__ MAEE Bit in MXSTATUS Register is set to 1. So T-Head MMU Flags are allowed. (But [d0_lowload Boot Code](https://github.com/openbouffalo/OBLFR/blob/master/apps/d0_lowload/src/rv32i_xtheade_lz4.S) doesn't set MXSTATUS?) 3734 | 3735 | [__For CanMV-k230 (T-Head C908):__](https://github.com/apache/nuttx/pull/11379) MAEE Bit in MXSTATUS Register is (probably) set to 0. So T-Head MMU Flags are NOT ALLOWED. MMU will crash if we set the T-Head MMU Flags! [(Reported by yf13)](https://github.com/yf13) 3736 | 3737 | TODO: Does T-Head C908 support T-Head MMU Flags or RISC-V Svpbmt Extension? We can't find the C908 Manual, and this [Linux Patch](https://lore.kernel.org/lkml/20230622231305.631331-4-heiko@sntech.de/T/) says... 3738 | 3739 | > In an ideal world those would be handled as extensions as well - T-Head fixed 3740 | their vectors with the C908 so they might do standards-compliant Svpbmt and 3741 | Zicbom in the future. 3742 | 3743 | This is how we set the T-Head MMU Flags (Strong Order / Shareable)... 3744 | 3745 | - ["Fixed the UART Interrupt and Platform-Level Interrupt Controller (Ox64 BL808)"](https://lupyuen.github.io/articles/plic3) 3746 | 3747 | _What is `ERRATA_THEAD_*` in Linux Kernel?_ 3748 | 3749 | - [riscv/errata/thead/errata.c](https://github.com/torvalds/linux/blob/master/arch/riscv/errata/thead/errata.c#L88C1-L122) 3750 | 3751 | - [riscv/include/asm/errata_list.h](https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/errata_list.h#L69-L164) 3752 | 3753 | - [riscv/include/asm/pgtable-64.h](https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/pgtable-64.h#L126-L142) 3754 | 3755 | - [drivers/perf/riscv_pmu_sbi.c](https://github.com/torvalds/linux/blob/master/drivers/perf/riscv_pmu_sbi.c#L803-L845) 3756 | 3757 | Check out the answer here... 3758 | 3759 | - ["Fixed the UART Interrupt and Platform-Level Interrupt Controller (Ox64 BL808)"](https://lupyuen.github.io/articles/plic3) 3760 | 3761 | _What if we disable and re-enable MMU, while setting PLIC Interrupt Priority?_ 3762 | 3763 | Yep seems to work... 3764 | 3765 | ```text 3766 | jh7110_mm_init: Disable MMU 3767 | mmu_write_satp: reg=0 3768 | jh7110_mm_init: Test Interrupt Priority 3769 | test_interrupt_priority: before1=0, before2=0, after1=1, after2=0 3770 | jh7110_mm_init: Enable MMU 3771 | ``` 3772 | 3773 | # Compare Ox64 BL808 UART Registers 3774 | 3775 | Read the article... 3776 | 3777 | - ["Fixed the UART Interrupt and Platform-Level Interrupt Controller (Ox64 BL808)"](https://lupyuen.github.io/articles/plic3) 3778 | 3779 | To fix the null UART Input, let's compare the [UART Registers from NuttX](https://gist.github.com/lupyuen/5d16f536133c0c3b5a30a50950a1ee75) vs [U-Boot Bootloader](https://gist.github.com/lupyuen/e0d13fb888a490fbf3dfcb01bbdd86fc) 3780 | 3781 | UART Registers from [NuttX UART Driver](https://gist.github.com/lupyuen/5d16f536133c0c3b5a30a50950a1ee75)... 3782 | 3783 | ```bash 3784 | // UART Registers from NuttX 3785 | bl602_receive: rxdata=-1 3786 | bl602_receive: rxdata=0x0 3787 | UART Registers (0x30002000): 3788 | 0000 05 17 00 00 | 01 07 00 00 | 13 00 13 00 | 00 00 00 00 ................ 3789 | 0010 70 00 9f 00 | 6f 00 00 00 | 0f 00 00 00 | 00 00 00 00 p...o........... 3790 | 0020 [94 00 00 00]|[f5 0f 00 00]| 00 00 00 00 | ff 0f 00 00 ................ 3791 | 0030 01 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3792 | 0040 00 00 00 00 | 00 00 00 00 | 03 00 00 00 | 00 00 00 00 ................ 3793 | 0050 [ff ff 1c 00]| 02 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3794 | 0060 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ............... 3795 | 0070 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3796 | 0080 [80 00 00 00]|[18 00 07 07]| 0a 00 00 00 |[00 00 00 00] ................ 3797 | 0090 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3798 | 00a0 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3799 | 00b0 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3800 | 00c0 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 ................ 3801 | 00d0 00 00 00 00 | 00 00 00 00 | | ........ 3802 | ``` 3803 | 3804 | UART Registers from [U-Boot Bootloader](https://gist.github.com/lupyuen/e0d13fb888a490fbf3dfcb01bbdd86fc)... 3805 | 3806 | ```bash 3807 | ## UART Registers from U-Boot 3808 | => md 0x30002000 0x36 3809 | 30002000: 00001705 00000701 00130013 00000000 ................ 3810 | 30002010: 009f0070 0000006f 0000000f 00000000 p...o.......... 3811 | 30002020:[00000012][00000fff]00000000 00000fff ................ 3812 | 30002030: 00000001 00000000 00000000 00000000 ................ 3813 | 30002040: 00000000 00000000 00000003 00000000 ................ 3814 | 30002050:[0026ffff] 00000002 00000000 00000000 ..&............. 3815 | 30002060: 00000000 00000000 00000000 00000000 ................ 3816 | 30002070: 00000000 00000000 00000000 00000000 ................ 3817 | 30002080:[00000000][07070000]0000000a[00000078] ............x... 3818 | 30002090: 00000000 00000000 00000000 00000000 ................ 3819 | 300020a0: 00000000 00000000 00000000 00000000 ................ 3820 | 300020b0: 00000000 00000000 00000000 00000000 ................ 3821 | 300020c0: 00000000 00000000 00000000 00000000 ................ 3822 | 300020d0: 00000000 00000000 ........ 3823 | ``` 3824 | 3825 | Here are the differences (marked above)... 3826 | 3827 | ```text 3828 | Offset 20: uart_int_sts (Interrupt Status) 3829 | 3830 | 00000094 = 0b10010100 3831 | Bit 7 urx_fer_int: UART RX FIFO error interrupt, auto-cleared when FIFO overflow/underflow error flag is cleared 3832 | Bit 4 urx_rto_int: UART RX Time-out interrupt 3833 | Bit 2 utx_frdy_int: UART TX FIFO ready (tx_fifo_cnt > tx_fifo_th) interrupt, auto-cleared when data is pushed 3834 | 3835 | 00000012 = 0b00010010 3836 | Bit 4 urx_rto_int: UART RX Time-out interrupt 3837 | Bit 1 urx_end_int: UART RX transfer end interrupt (set according to cr_urx_-len) 3838 | 3839 | Offset 24: uart_int_mask (Interrupt Mask) 3840 | 00000ff5 3841 | 00000fff 3842 | TODO: Set to 0xfff 3843 | 3844 | Offset 50: urx_bcr_int_cfg (Receive Byte Count) 3845 | 001cffff 3846 | 0026ffff 3847 | Number of bytes received. OK to ignore this. 3848 | 3849 | Offset 80: uart_fifo_config_0 (FIFO Config 0) 3850 | 00000080 3851 | 00000000 3852 | Bit 7 rx_fifo_underflow: Underflow flag of RX FIFO 3853 | Can be cleared by rx_fifo_clr. 3854 | TODO: Set Bit 3 rx_fifo_clr: Clear signal of RX FIFO 3855 | 3856 | Offset 84: uart_fifo_config_1 (FIFO Config 1) 3857 | 07070018 3858 | 07070000 3859 | rx_fifo_cnt = 1 (RX FIFO available count) 3860 | tx_fifo_cnt = 8 (TX FIFO available count) 3861 | Let's ignore this. 3862 | 3863 | Offset 8c: uart_fifo_rdata (Receive Data) 3864 | 00000000 3865 | 00000078 3866 | RX FIFO. OK to ignore this. 3867 | ``` 3868 | 3869 | Nope still the same. 3870 | 3871 | # Fix the UART Interrupt for Ox64 BL808 3872 | 3873 | Read the article... 3874 | 3875 | - ["Fixed the UART Interrupt and Platform-Level Interrupt Controller (Ox64 BL808)"](https://lupyuen.github.io/articles/plic3) 3876 | 3877 | Let's fix the UART Interrupt for Ox64 BL808! 3878 | 3879 | UART Input is strangely null, so we tried printing the UART Input just before reading it: [bl602_serial.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64c/arch/risc-v/src/jh7110/bl602_serial.c#L1026-L1044) 3880 | 3881 | ```c 3882 | /**************************************************************************** 3883 | * Name: bl602_rxavailable 3884 | * 3885 | * Description: 3886 | * Return true if the receive register is not empty 3887 | * 3888 | ****************************************************************************/ 3889 | 3890 | static bool bl602_rxavailable(struct uart_dev_s *dev) 3891 | { 3892 | struct bl602_uart_s *priv = (struct bl602_uart_s *)dev->priv; 3893 | uint8_t uart_idx = priv->config.idx; 3894 | 3895 | /* Return true is data is available in the receive data buffer */ 3896 | 3897 | uintptr_t rx = getreg32(0x3000208c); _info("rx=%p\n", rx); //// 3898 | return (getreg32(BL602_UART_FIFO_CONFIG_1(uart_idx)) & \ 3899 | UART_FIFO_CONFIG_1_RX_CNT_MASK) != 0; 3900 | } 3901 | ``` 3902 | 3903 | Yes UART Input is correct! 3904 | 3905 | ```text 3906 | nx_start: CPU0: Beginning Idle Loop 3907 | bl602_rxavailable: rx=0x31 3908 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3909 | PLIC Interrut Pending (0xe0001000): 3910 | 0000 00 00 00 00 00 00 00 00 ........ 3911 | bl602_rxavailable: rx=0x32 3912 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3913 | PLIC Interrupt Pending (0xe0001000): 3914 | 0000 00 00 00 00 00 00 00 00 ........ 3915 | ``` 3916 | 3917 | But somehow UART Input is erased when we read BL602_UART_FIFO_CONFIG_1 (Offset 0x84)... 3918 | 3919 | ```c 3920 | uintptr_t fifo = getreg32(0x30002084); 3921 | uintptr_t rx = getreg32(0x3000208c); 3922 | _info("fifo=%p, rx=%p\n", fifo, rx); 3923 | ``` 3924 | 3925 | Which shows... 3926 | 3927 | ```text 3928 | nx_start: CPU0: Beginning Idle Loop 3929 | bl602_rxavailable: fifo=0x7070120, rx=0 3930 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3931 | PLIC Interrupt Pending (0xe0001000): 3932 | 0000 00 00 00 00 00 00 00 00 ........ 3933 | ``` 3934 | 3935 | _Why is C906 messing up the memory access?_ 3936 | 3937 | From Linux Kernel we see the MMU Flags (Strong Order + Shareable) for I/O Memory: [pgtable-64.h](https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/pgtable-64.h#L126-L142) 3938 | 3939 | Which is used by this T-Head Errata: [errata_list.h](https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/errata_list.h#L70-L92) 3940 | 3941 | We do the same to Enable Strong Order + Shareable in NuttX Page Table Entries: [riscv_mmu.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/ox64c/arch/risc-v/src/common/riscv_mmu.c#L100-L127) 3942 | 3943 | ```c 3944 | // Save the Page Table Entry 3945 | lntable[index] = (paddr | mmuflags); 3946 | 3947 | //// Begin Test 3948 | // From https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/pgtable-64.h#L126-L142 3949 | /* 3950 | * [63:59] T-Head Memory Type definitions: 3951 | * bit[63] SO - Strong Order 3952 | * bit[60] SH - Shareable 3953 | * 10010 - IO Strongly-ordered, Non-cacheable, Non-bufferable, Shareable, Non-trustable 3954 | */ 3955 | #define _PAGE_IO_THEAD ((1UL << 63) | (1UL << 60)) 3956 | if ((mmuflags & PTE_R) && // Leaf Page Table Entry 3957 | (vaddr < 0x40000000UL || vaddr >= 0xe0000000UL)) // I/O or PLIC Memory 3958 | { 3959 | // Enable Strong Order and Shareable 3960 | lntable[index] = lntable[index] | _PAGE_IO_THEAD; 3961 | _info("vaddr=%p, lntable[index]=%p\n", vaddr, lntable[index]); 3962 | } 3963 | //// End Test 3964 | ``` 3965 | 3966 | Yep [UART Input works OK](https://gist.github.com/lupyuen/6f3e24278c4700f73da72b9efd703167) yay! 3967 | 3968 | ```text 3969 | nx_start: CPU0: Beginning Idle Loop 3970 | bl602_receive: rxdata=0x31 3971 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3972 | PLIC Interrupt Pending (0xe0001000): 3973 | 0000 00 00 00 00 00 00 00 00 ........ 3974 | 1riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3975 | PLIC Interrupt Pending (0xe0001000): 3976 | 0000 00 00 00 00 00 00 00 00 ........ 3977 | bl602_receive: rxdata=0x32 3978 | riscv_dispatch_irq: Clear Pending Interrupts, irq=45, claim=0 3979 | PLIC Interrupt Pending (0xe0001000): 3980 | 0000 00 00 00 00 00 00 00 00 ........ 3981 | 2 3982 | ``` 3983 | 3984 | Finally [UART Input and PLIC are both OK](https://gist.github.com/lupyuen/3761d9e73ca2c5b97b2f33dc1fc63946) yay! 3985 | 3986 | ```text 3987 | Starting kernel ... 3988 | 123jh7110_copy_ramdisk: _edata=0x50400258, _sbss=0x50400290, _ebss=0x50110_IDLESTACK_TOP=0x50407c00 3989 | jh7110_copy_ramdisk: ramdisk_addr=0x50410281 3990 | jh7110_copy_ramdisk: size=8192000 3991 | ABCjh7110_kernel_mappings: map I/O regions 3992 | mmu_ln_setentry: vaddr=0, lntable[index]=0x90000000000000e7 3993 | jh7110_kernel_mappings: map PLIC as Interrupt L2 3994 | mmu_ln_setentry: vaddr=0xe0000000, lntable[index]=0x90000000380000e7 3995 | mmu_ln_setentry: vaddr=0xe0200000, lntable[index]=0x90000000380800e7 3996 | mmu_ln_setentry: vaddr=0xe0400000, lntable[index]=0x90000000381000e7 3997 | mmu_ln_setentry: vaddr=0xe0600000, lntable[index]=0x90000000381800e7 3998 | ... 3999 | mmu_ln_setentry: vaddr=0xefc00000, lntable[index]=0x900000003bf000e7 4000 | mmu_ln_setentry: vaddr=0xefe00000, lntable[index]=0x900000003bf800e7 4001 | jh7110_kernel_mappings: connect the L1 and Interrupt L2 page tables for PLIC 4002 | jh7110_kernel_mappings: map kernel text 4003 | jh7110_kernel_mappings: map kernel data 4004 | jh7110_kernel_mappings: connect the L1 and L2 page tables 4005 | jh7110_kernel_mapmap the page pool 4006 | up_irqinitialize: 4007 | 4008 | NuttShell (NSH) NuttX-12.0.3 4009 | nsh> uname -a 4010 | NuttX 12.0.3 fd05b07 Nov 24 2023 07:42:54 risc-v star64 4011 | nsh> 4012 | nsh> ls /dev 4013 | /dev: 4014 | console 4015 | null 4016 | ram0 4017 | zero 4018 | nsh> 4019 | nsh> hello 4020 | Hello, World!! 4021 | ``` 4022 | 4023 | [(Watch the __Demo on YouTube__)](https://youtu.be/l7Y36nTkr8c) 4024 | 4025 | C906 MMU is actually explained in [__C906 Integration Manual (Chinese)__](https://github.com/T-head-Semi/openc906/blob/main/doc/%E7%8E%84%E9%93%81C906%E9%9B%86%E6%88%90%E6%89%8B%E5%86%8C.pdf), Page 9. 4026 | 4027 | And here's the [__RTL Code for C906 MMU__](https://github.com/T-head-Semi/openc906/tree/main/C906_RTL_FACTORY/gen_rtl/mmu/rtl). 4028 | 4029 | ![UART Input and PLIC are both OK!](https://lupyuen.github.io/images/plic3-title.png) 4030 | 4031 | # Fix the RISC-V Timer with OpenSBI 4032 | 4033 | Read the article... 4034 | 4035 | - ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim) 4036 | 4037 | _The `sleep` command hangs in NuttX Shell. How to fix it?_ 4038 | 4039 | That's because we haven't implemented the RISC-V Timer for Ox64! We should call OpenSBI to handle the Timer, [here's the fix](https://github.com/lupyuen2/wip-pinephone-nuttx/commit/57ea5f000636f739ac3cb8ea1e60936798f6c3a9#diff-535879ffd6d9fc8e7d84b37a88bdeb1609c4a90e3777150939a96bed18696aee). 4040 | 4041 | (Ignore riscv_mtimer.c, we were verifying that mtime and mtimecmp are unused in Kernel Mode) 4042 | 4043 | We only need to change [arch/risc-v/src/bl808/bl808_timerisr.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/bl808/bl808_timerisr.c#L44-L116) 4044 | 4045 | ```c 4046 | // Timer Frequency 4047 | #define MTIMER_FREQ 1000000 4048 | 4049 | // This function is called during start-up to initialize the timer interrupt. 4050 | void up_timer_initialize(void) { 4051 | struct oneshot_lowerhalf_s *lower = riscv_mtimer_initialize( 4052 | 0, 0, RISCV_IRQ_STIMER, MTIMER_FREQ); 4053 | DEBUGASSERT(lower); 4054 | up_alarm_set_lowerhalf(lower); 4055 | } 4056 | ``` 4057 | 4058 | How it works: At startup, `up_timer_initialize` (above) calls... 4059 | 4060 | - [riscv_mtimer_initialize](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/common/riscv_mtimer.c#L318-L332) which calls... 4061 | 4062 | - [riscv_mtimer_set_mtimecmp](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/common/riscv_mtimer.c#L136-L141) which calls... 4063 | 4064 | - [riscv_sbi_set_timer](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/common/supervisor/riscv_sbi.c#L94-L107) which calls... 4065 | 4066 | - [sbi_ecall](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/common/supervisor/riscv_sbi.c#L53-L76) which makes an ecall to OpenSBI 4067 | 4068 | - Which accesses the System Timer 4069 | 4070 | Originally we set MTIMER_FREQ to 10000000: [bl808_timerisr.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/bl808/bl808_timerisr.c#L44-L48) 4071 | 4072 | ```c 4073 | #define MTIMER_FREQ 10000000 4074 | ``` 4075 | 4076 | But this causes the command `sleep 1` to pause for 10 seconds. So we divide the frequency by 10: [bl808_timerisr.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/arch/risc-v/src/bl808/bl808_timerisr.c#L44-L48) 4077 | 4078 | ```c 4079 | #define MTIMER_FREQ 1000000 4080 | ``` 4081 | 4082 | Now the `sleep` command works correctly in NuttX Shell! 4083 | 4084 | [Here's the log (ignore the errors)](https://gist.github.com/lupyuen/8aa66e7f88d1e31a5f198958c15e4393) 4085 | 4086 | # LED Driver for Ox64 BL808 4087 | 4088 | Read the article... 4089 | 4090 | - ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim) 4091 | 4092 | We wish to blink an LED with Nim on Ox64... 4093 | 4094 | - ["Blink an LED with Nim"](https://github.com/lupyuen/nuttx-nim#blink-an-led-with-nim) 4095 | 4096 | But first we need a barebones NuttX LED Driver for Ox64. 4097 | 4098 | _How to create the NuttX LED Driver?_ 4099 | 4100 | We assume LED is connected to GPIO 29, Pin 21. [(See the Pinout)](https://wiki.pine64.org/wiki/File:Ox64_pinout.png) 4101 | 4102 | (With a 47 Ohm Resistor, yellow-purple-black-gold) 4103 | 4104 | _How do we flip a BL808 GPIO High and Low?_ 4105 | 4106 | From BL808 Reference Manual Page 56, "Normal GPIO Output Mode"... 4107 | 4108 | - Set reg_gpio_xx_oe (Bit 6) to 1 to enable the GPIO output mode
4109 | = (1 << 6) 4110 | 4111 | - Set reg_gpio_xx_func_sel (Bits 8 to 12) to 11 to enter the SWGPIO mode
4112 | = (11 << 8) 4113 | 4114 | - Set reg_gpio_xx_mode (Bits 30 to 31) to 0 to enable the normal output function of I/O
4115 | = (0 << 30) 4116 | 4117 | - Set reg_gpio_xx_pu (Bit 4) and reg_gpio_xx_pd (Bit 5) to 0 to disable the internal pull-up and pull-down functions
4118 | = (0 << 4) 4119 | 4120 | - Set the level of I/O pin through reg_gpio_xx_o (Bit 24)
4121 | = Either (0 << 24) Or (1 << 24) 4122 | 4123 | (GPIO Bit Definitions are below) 4124 | 4125 | Which means... 4126 | 4127 | - Set GPIO Output to 0
4128 | = (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24)
4129 | = 0xb40 4130 | 4131 | - Set GPIO Output to 1
4132 | = (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24)
4133 | = 0x1000b40 4134 | 4135 | _How to test this?_ 4136 | 4137 | GPIO 29 Base Address `gpio_cfg29` is 0x20000938. 4138 | 4139 | For testing, we run U-Boot Bootloader Commands to set GPIO 29 to High and Low... 4140 | 4141 | ```bash 4142 | ## Dump gpio_cfg29 at 0x20000938 4143 | $ md 0x20000938 1 4144 | 20000938: 00400803 ..@. 4145 | 4146 | ## Set GPIO Output to 0: (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24) 4147 | ## = 0xb40 4148 | $ mw 0x20000938 0xb40 1 4149 | $ md 0x20000938 1 4150 | 20000938: 00000b40 @... 4151 | 4152 | ## Set GPIO Output to 1: (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24) 4153 | ## = 0x1000b40 4154 | $ mw 0x20000938 0x1000b40 1 4155 | $ md 020000938 1 4156 | 20000938: 01000b40 @... 4157 | ``` 4158 | 4159 | And U-Boot switches the LED On and Off correctly yay! 4160 | 4161 | _How to flip the GPIO in our NuttX LED Driver?_ 4162 | 4163 | This is how we flip the GPIO in our NuttX LED Driver: [bl808_userleds.c](https://github.com/lupyuen2/wip-pinephone-nuttx/blob/nim/boards/risc-v/bl808/ox64/src/bl808_userleds.c#L176-L209) 4164 | 4165 | ```c 4166 | // Switch the LEDs On and Off according to the LED Set 4167 | // (Bit 0 = LED 0) 4168 | void board_userled_all(uint32_t ledset) 4169 | { 4170 | _info("ledset=0x%x\n", ledset);//// 4171 | int i; 4172 | 4173 | // For LED 0 to 2... 4174 | for (i = 0; i < BOARD_LEDS; i++) 4175 | { 4176 | // Get the desired state of the LED 4177 | bool val = ((ledset & g_led_setmap[i]) != 0); 4178 | _info("led=%d, val=%d\n", i, val);//// 4179 | 4180 | // If this is LED 0... 4181 | if (i == 0) 4182 | { 4183 | // Switch it On or Off? 4184 | if (val) 4185 | { 4186 | // Switch LED 0 (GPIO 29) to On: 4187 | // Set gpio_cfg29 to (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24) 4188 | // mw 0x20000938 0x1000b40 1 4189 | *(volatile uint32_t *) 0x20000938 = 0x1000b40; 4190 | } 4191 | else 4192 | { 4193 | // Switch LED 0 (GPIO 29) to Off: 4194 | // Set gpio_cfg29 to (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24) 4195 | // mw 0x20000938 0xb40 1 4196 | *(volatile uint32_t *) 0x20000938 = 0xb40; 4197 | } 4198 | } 4199 | ////TODO: a64_pio_write(g_led_map[i], (ledset & g_led_setmap[i]) != 0); 4200 | } 4201 | } 4202 | ``` 4203 | 4204 | And our LED Driver works OK with Nim: It blinks our LED on Ox64 BL808 SBC! 4205 | 4206 | - [Watch the Demo on YouTube](https://youtube.com/shorts/KCkiXFxBgxQ) 4207 | 4208 | - [See the Log](https://gist.github.com/lupyuen/553c2da4ad5d119468d223e162573e96) 4209 | 4210 | - ["Blink an LED with Nim"](https://github.com/lupyuen/nuttx-nim#blink-an-led-with-nim) 4211 | 4212 | Later we'll replace the (awful) code above by the BL808 GPIO Driver. Which we'll copy from NuttX for BL602. 4213 | 4214 | _How did we get the GPIO Bit Definitions?_ 4215 | 4216 | From BL808 Reference Manual Page 119... 4217 | 4218 | ```text 4219 | 4.8.30 gpio_cfg29 4220 | Base Address:0x20000938 4221 | 4222 | Bits Name Type Reset Description 4223 | 4224 | 31:30 reg_gpio_29_mode r/w 0 When GPIO Function Selected to SWGPIO 4225 | 00 (Output Value Mode): GPIO Output by reg_gpio_x_o 4226 | Value 4227 | 01 (Set/Celar Mode ) :GPIO Output set by reg_gpio_x_set 4228 | and clear by reg_gpio_x_clr 4229 | 10 : SWGPIO Source comes from GPIO DMA (GPIO DMA 4230 | Mode), GPIO Output value by gpio_dma_o 4231 | 11: SWGPIO Source comes from GPIO DMA (GPIO DMA 4232 | Mode), GPIO Outout value by gpio_dma_set/gpio_dma_clr 4233 | 4234 | 29 RSVD 4235 | 4236 | 28 reg_gpio_29_i r 0 4237 | 4238 | 27 RSVD 4239 | 4240 | 26 reg_gpio_29_clr w1p 0 When SWGPIO @ Set/Clear Mode 4241 | Set this bit will clear GPIO output value to 0,when set/clr at 4242 | the same time, only set take effect 4243 | 4244 | 25 reg_gpio_29_set w1p 0 When SWGPIO @ Set/Clear Mode 4245 | Set this bit will set GPIO output value to 1,when set/clr at 4246 | the same time, only set take effect 4247 | 4248 | 24 reg_gpio_29_o r/w 0 When SWGPIO @ Output Value Mode 4249 | 00 : GPIO Value changes according to this value 4250 | 01 : GPIO Value Set by this register and clr by clr_reg 4251 | 4252 | 23 RSVD 4253 | 4254 | 22 reg_gpio_29_int_mask r/w 1 mask interrupt (1) 4255 | 4256 | 21 gpio_29_int_stat r 0 interrupt status 4257 | 4258 | 20 reg_gpio_29_int_clr r/w 0 clear interrupt 4259 | 4260 | 19:16 reg_gpio_29_int_mode_set r/w 0 0000 : sync falling edge trigger 4261 | 0001 : sync rising edge trigger 4262 | 0010 : sync low level trigger 4263 | 0011 : sync high level trigger 4264 | 01xx : sync rising & falling edge trigger 4265 | 1000 : async falling edge trigger 4266 | 1001 : async rising edge trigger 4267 | 1010 : async low level trigger 4268 | 1011 : async high level trigger 4269 | 4270 | 15:13 RSVD 4271 | 4272 | 12:8 reg_gpio_29_func_sel r/w 5’hB GPIO Function Select (Default : SW-GPIO) 4273 | 4274 | 7 RSVD 4275 | 4276 | 6 reg_gpio_29_oe r/w 0 Register Controlled GPIO Output Enable (Used when GPIO 4277 | Function select to Register Control GPIO) 4278 | 4279 | 5 reg_gpio_29_pd r/w 0 GPIO Pull Down Control 4280 | 4281 | 4 reg_gpio_29_pu r/w 0 GPIO Pull Up Control 4282 | 4283 | 3:2 reg_gpio_29_drv r/w 0 GPIO Driving Control 4284 | 4285 | 1 reg_gpio_29_smt r/w 1 GPIO SMT Control 4286 | 4287 | 0 reg_gpio_29_ie r/w 0 GPIO Input Enable 4288 | ``` 4289 | 4290 | # Emulate Ox64 BL808 SBC with TinyEMU 4291 | 4292 | Read the articles... 4293 | 4294 | - ["Automated Testing with Ox64 BL808 Emulator (Apache NuttX RTOS)"](https://lupyuen.github.io/articles/tinyemu3) 4295 | 4296 | - ["Emulate Ox64 BL808 in the Web Browser: Experiments with TinyEMU RISC-V Emulator and Apache NuttX RTOS"](https://lupyuen.github.io/articles/tinyemu2) 4297 | 4298 | Objective: Take the NuttX Kernel built for Ox64 BL808 SBC. And boot it on TinyEMU RISC-V Emulator in the Web Browser! 4299 | 4300 | Check out the details here... 4301 | 4302 | - ["Emulate Ox64 BL808 SBC with TinyEMU"](https://github.com/lupyuen/nuttx-tinyemu#emulate-ox64-bl808-sbc-with-tinyemu) 4303 | 4304 | ![Ox64 BL808 Emulator with TinyEMU RISC-V Emulator and Apache NuttX RTOS](https://lupyuen.github.io/images/tinyemu2-title.png) 4305 | 4306 | [_(Live Demo of Ox64 BL808 Emulator)_](https://lupyuen.github.io/nuttx-tinyemu/ox64) 4307 | 4308 | # Build and Test NuttX Apps in the Web Browser 4309 | 4310 | Ox64 Emulator is now integrated with TCC RISC-V Compiler in WebAssembly. So we can Build and Test NuttX Apps in the Web Browser! 4311 | 4312 | Read the article... 4313 | 4314 | - ["Zig runs ROM FS Filesystem in the Web Browser (thanks to Apache NuttX RTOS)"](https://lupyuen.github.io/articles/romfs) 4315 | 4316 | # QuickJS JavaScript Engine on Ox64 BL808 SBC 4317 | 4318 | QuickJS JavaScript Engine now runs on Ox64 BL808 SBC, and in the Ox64 Web Emulator! 4319 | 4320 | Read the article... 4321 | 4322 | - ["QuickJS JavaScript Engine on a Real-Time Operating System (Apache NuttX RTOS)"](https://lupyuen.github.io/articles/quickjs) 4323 | 4324 | # Documentation for Ox64 BL808 4325 | 4326 | ![Pine64 Ox64 64-bit RISC-V SBC (Sorry for my substandard soldering)](https://lupyuen.github.io/images/ox64-solder.jpg) 4327 | 4328 | [_Pine64 Ox64 64-bit RISC-V SBC (Sorry for my substandard soldering)_](https://wiki.pine64.org/wiki/Ox64) 4329 | 4330 | - ["Ox64 BL808 RISC-V SBC: Booting Linux and (maybe) Apache NuttX RTOS"](https://lupyuen.github.io/articles/ox64) 4331 | 4332 | - ["Booting Linux on the Pine64 Ox64 SBC"](https://adventurist.me/posts/00317) 4333 | 4334 | - [Pine64 Ox64 Wiki](https://wiki.pine64.org/wiki/Ox64) 4335 | 4336 | - [Pine64 Ox64 Schematic](https://files.pine64.org/doc/ox64/PINE64_Ox64-Schematic-202221018.pdf) 4337 | 4338 | - [OpenBouffalo Wiki](https://openbouffalo.org/index.php/Main_Page) 4339 | 4340 | - [Linux Image + OpenSBI + U-Boot for BL808](https://github.com/openbouffalo/buildroot_bouffalo) 4341 | 4342 | [(Newer version?)](https://github.com/bouffalolab/buildroot_bouffalo) 4343 | 4344 | - [BL808 Datasheet](https://github.com/bouffalolab/bl_docs/blob/main/BL808_DS/en/BL808_DS_1.2_en.pdf) 4345 | 4346 | - [BL808 Reference Manual](https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf) 4347 | 4348 | - [XuanTie OpenC906 User Manual](https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf) 4349 | 4350 | - [BL808 D0 Core: T-Head C906 480MHz 64-bit RISC-V CPU](https://www.t-head.cn/product/c906?lang=en) 4351 | 4352 | (Multimedia Core: MIPI CSI / DSI, Neural Proc Unit) 4353 | 4354 | Memory Mgmt Unit is Sv39, 128/256/512 TLB table entry. (Same as Star64?) 4355 | 4356 | - [BL808 M0 Core: T-Head E907 320MHz 32-bit RISC-V CPU](https://www.t-head.cn/product/e907?lang=en) 4357 | 4358 | (Wireless + Peripherals Core: WiFi, BLE, BT, Zigbee, Audio) 4359 | 4360 | - [BL808 LP Core: T-Head E902 150MHz 32-bit RISC-V CPU](https://www.t-head.cn/product/e902?lang=en) 4361 | 4362 | (Low Power Core) 4363 | -------------------------------------------------------------------------------- /bl808-pine64-ox64.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/nuttx-ox64/a2957e08fd62130d30a0edf9af6b4cb97ff64709/bl808-pine64-ox64.dtb -------------------------------------------------------------------------------- /bl808-pine64-ox64.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | compatible = "pine64,ox64\0bflb,bl808"; 5 | #address-cells = <0x01>; 6 | #size-cells = <0x01>; 7 | model = "Pine64 Ox64"; 8 | 9 | cpus { 10 | timebase-frequency = <0xf4240>; 11 | #address-cells = <0x01>; 12 | #size-cells = <0x00>; 13 | 14 | cpu@0 { 15 | compatible = "thead,c906\0riscv"; 16 | device_type = "cpu"; 17 | reg = <0x00>; 18 | d-cache-block-size = <0x40>; 19 | d-cache-sets = <0x100>; 20 | d-cache-size = <0x8000>; 21 | i-cache-block-size = <0x40>; 22 | i-cache-sets = <0x80>; 23 | i-cache-size = <0x8000>; 24 | mmu-type = "riscv,sv39"; 25 | riscv,isa = "rv64imafdc"; 26 | phandle = <0x07>; 27 | 28 | interrupt-controller { 29 | compatible = "riscv,cpu-intc"; 30 | interrupt-controller; 31 | #address-cells = <0x00>; 32 | #interrupt-cells = <0x01>; 33 | phandle = <0x06>; 34 | }; 35 | }; 36 | }; 37 | 38 | xtal-clk { 39 | compatible = "fixed-clock"; 40 | clock-frequency = <0x2625a00>; 41 | clock-output-names = "xtal"; 42 | #clock-cells = <0x00>; 43 | phandle = <0x04>; 44 | }; 45 | 46 | sdh-clk { 47 | compatible = "fixed-clock"; 48 | clock-frequency = <0x5b8d800>; 49 | clock-output-names = "sdh"; 50 | #clock-cells = <0x00>; 51 | phandle = <0x05>; 52 | }; 53 | 54 | soc { 55 | compatible = "simple-bus"; 56 | ranges; 57 | interrupt-parent = <0x01>; 58 | dma-noncoherent; 59 | #address-cells = <0x01>; 60 | #size-cells = <0x01>; 61 | 62 | pinctrl@0x200008C4 { 63 | compatible = "bflb,pinctrl"; 64 | reg = <0x200008c4 0x1000>; 65 | gpio-controller; 66 | #gpio-cells = <0x02>; 67 | gpio-ranges = <0x02 0x00 0x00 0x2e>; 68 | bflb,npins = <0x2e>; 69 | status = "okay"; 70 | interrupt-controller; 71 | #interrupt-cells = <0x02>; 72 | interrupts-extended = <0x03 0x00 0x04 0x01>; 73 | phandle = <0x02>; 74 | 75 | sdh-pins { 76 | pins = "GPIO0\0GPIO1\0GPIO2\0GPIO3\0GPIO4\0GPIO5"; 77 | function = "sdh"; 78 | phandle = <0x08>; 79 | }; 80 | }; 81 | 82 | seceng@0x20004000 { 83 | compatible = "bflb,seceng"; 84 | reg = <0x20004000 0x1000>; 85 | status = "okay"; 86 | phandle = <0x09>; 87 | }; 88 | 89 | serial@30002000 { 90 | compatible = "bflb,bl808-uart"; 91 | reg = <0x30002000 0x1000>; 92 | interrupts = <0x14 0x04>; 93 | clocks = <0x04>; 94 | status = "okay"; 95 | phandle = <0x0a>; 96 | }; 97 | 98 | serial@0x2000AA00 { 99 | compatible = "bflb,bl808-uart"; 100 | reg = <0x2000aa00 0x100>; 101 | interrupts-extended = <0x03 0x00 0x01 0x01>; 102 | mboxes = <0x03 0x00 0x01>; 103 | clocks = <0x04>; 104 | status = "okay"; 105 | phandle = <0x0b>; 106 | }; 107 | 108 | sdhci@20060000 { 109 | compatible = "bflb,bl808-sdhci"; 110 | reg = <0x20060000 0x100>; 111 | interrupts-extended = <0x03 0x00 0x00 0x01>; 112 | mboxes = <0x03 0x00 0x00>; 113 | clocks = <0x05>; 114 | status = "okay"; 115 | phandle = <0x0c>; 116 | }; 117 | 118 | mailbox@30005000 { 119 | compatible = "bflb,bl808-ipc"; 120 | reg = <0x30005000 0x20 0x30005020 0x20 0x2000a800 0x20 0x2000a820 0x20>; 121 | interrupts = <0x36 0x04>; 122 | interrupt-controller; 123 | #interrupt-cells = <0x03>; 124 | #mbox-cells = <0x02>; 125 | status = "okay"; 126 | phandle = <0x03>; 127 | }; 128 | 129 | interrupt-controller@e0000000 { 130 | compatible = "thead,c900-plic"; 131 | reg = <0xe0000000 0x4000000>; 132 | interrupts-extended = <0x06 0xffffffff 0x06 0x09>; 133 | interrupt-controller; 134 | #address-cells = <0x00>; 135 | #interrupt-cells = <0x02>; 136 | riscv,ndev = <0x40>; 137 | phandle = <0x01>; 138 | }; 139 | 140 | timer@e4000000 { 141 | compatible = "thead,c900-clint"; 142 | reg = <0xe4000000 0xc000>; 143 | interrupts-extended = <0x06 0x03 0x06 0x07>; 144 | phandle = <0x0d>; 145 | }; 146 | }; 147 | 148 | aliases { 149 | serial0 = "/soc/serial@30002000"; 150 | serial1 = "/soc/serial@0x2000AA00"; 151 | }; 152 | 153 | chosen { 154 | stdout-path = "serial0:2000000n8"; 155 | bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=PARTLABEL=rootfs rootwait rootfstype=ext4"; 156 | linux,initrd-start = <0x00 0x52000000>; 157 | linux,initrd-end = <0x00 0x52941784>; 158 | }; 159 | 160 | memory@50000000 { 161 | device_type = "memory"; 162 | reg = <0x50000000 0x4000000>; 163 | }; 164 | 165 | xip_flash@58500000 { 166 | compatible = "mtd-rom"; 167 | reg = <0x58500000 0x400000>; 168 | linux,mtd-name = "xip-flash.0"; 169 | erase-size = <0x10000>; 170 | bank-width = <0x04>; 171 | #address-cells = <0x01>; 172 | #size-cells = <0x01>; 173 | }; 174 | 175 | __symbols__ { 176 | cpu0 = "/cpus/cpu@0"; 177 | cpu0_intc = "/cpus/cpu@0/interrupt-controller"; 178 | xtal = "/xtal-clk"; 179 | sdh = "/sdh-clk"; 180 | pinctrl = "/soc/pinctrl@0x200008C4"; 181 | sdh_pins = "/soc/pinctrl@0x200008C4/sdh-pins"; 182 | seceng = "/soc/seceng@0x20004000"; 183 | uart0 = "/soc/serial@30002000"; 184 | uart1 = "/soc/serial@0x2000AA00"; 185 | sdhci0 = "/soc/sdhci@20060000"; 186 | ipclic = "/soc/mailbox@30005000"; 187 | plic = "/soc/interrupt-controller@e0000000"; 188 | clint = "/soc/timer@e4000000"; 189 | }; 190 | }; 191 | -------------------------------------------------------------------------------- /mmu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apache NuttX RTOS on Ox64 BL808 SBC: Memory Management Unit 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /nuttx.cfg: -------------------------------------------------------------------------------- 1 | /* VM configuration file */ 2 | { 3 | version: 1, 4 | machine: "riscv64", 5 | memory_size: 256, 6 | bios: "Image", 7 | } 8 | -------------------------------------------------------------------------------- /nuttx.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect 2 | ## Expect Script for Testing NuttX with Ox64 BL808 Emulator 3 | 4 | ## Wait at most 30 seconds 5 | set timeout 30 6 | 7 | ## For every 1 character sent, wait 0.001 milliseconds 8 | set send_slow {1 0.001} 9 | 10 | ## Start the Ox64 BL808 Emulator 11 | spawn ./temu nuttx.cfg 12 | 13 | ## Wait for the prompt and enter `uname -a` 14 | expect "nsh> " 15 | send -s "uname -a\r" 16 | 17 | ## Wait for the prompt and enter `free` 18 | expect "nsh> " 19 | send -s "free\r" 20 | 21 | ## Wait for the prompt and enter `hello` 22 | expect "nsh> " 23 | send -s "hello\r" 24 | 25 | ## Wait for the prompt and enter `getprime` 26 | expect "nsh> " 27 | send -s "getprime\r" 28 | 29 | ## Wait for the prompt and enter `hello` 30 | expect "nsh> " 31 | send -s "hello\r" 32 | 33 | ## Wait for the prompt and enter `getprime` 34 | expect "nsh> " 35 | send -s "getprime\r" 36 | 37 | ## Wait for the prompt and enter `ostest` 38 | expect "nsh> " 39 | send -s "ostest\r" 40 | 41 | ## Check the response... 42 | expect { 43 | ## If we see this message, exit normally 44 | "ostest_main: Exiting with status 0" { exit 0 } 45 | 46 | ## If timeout, exit with an error 47 | timeout { exit 1 } 48 | } 49 | --------------------------------------------------------------------------------