| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2022 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 10 | * included LICENSE.LGPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include <sys/mman.h> | ||
| 16 | #include <sys/stat.h> | ||
| 17 | #include <sys/wait.h> | ||
| 18 | #include <algorithm> | ||
| 19 | #include <cerrno> | ||
| 20 | #include <cstring> | ||
| 21 | #include <elf.h> | ||
| 22 | #include <fcntl.h> | ||
| 23 | #include <filesystem> | ||
| 24 | #include <inttypes.h> | ||
| 25 | #include <map> | ||
| 26 | #include <mutex> | ||
| 27 | #include <stdexcept> | ||
| 28 | #include <stdint.h> | ||
| 29 | #include <thread> | ||
| 30 | #include <unistd.h> | ||
| 31 | #include <vector> | ||
| 32 | |||
| 33 | #include "../common.h" | ||
| 34 | #include "../log.h" | ||
| 35 | |||
| 36 | #include "BBBPruUtils.h" | ||
| 37 | |||
| 38 | class PRUCoreInfo { | ||
| 39 | public: | ||
| 40 | int pru_num = 0; | ||
| 41 | uint8_t* dataRam = nullptr; | ||
| 42 | uintptr_t dataRamPhyLoc = 0; | ||
| 43 | int dataRamSize = 0; | ||
| 44 | uint8_t* sharedRam = nullptr; | ||
| 45 | uintptr_t sharedRamPhyLoc = 0; | ||
| 46 | int sharedRamSize = 0; | ||
| 47 | |||
| 48 | uint8_t* sram = nullptr; | ||
| 49 | uint32_t sram_addr = 0; | ||
| 50 | size_t sram_size = 0; | ||
| 51 | |||
| 52 | uint8_t* m4ram = nullptr; | ||
| 53 | uint32_t m4ram_addr = 0; | ||
| 54 | size_t m4ram_size = 0; | ||
| 55 | |||
| 56 | 206 | std::string getRemoteProcStateFile() { | |
| 57 | #ifdef PLATFORM_BBB | ||
| 58 |
2/4✓ Branch 0 taken 159 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 159 times.
✗ Branch 3 not taken.
|
159 | return "/sys/class/remoteproc/remoteproc" + std::to_string(pru_num) + "/state"; |
| 59 | #elif defined(PLATFORM_BB64) | ||
| 60 | 47 | std::string base; | |
| 61 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 41 times.
|
47 | if (pru_num == 0) { |
| 62 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | base = "/sys/devices/platform/bus@f0000/30040000.pruss/30074000.pru/remoteproc/remoteproc"; |
| 63 |
1/2✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
|
41 | } else if (pru_num == 1) { |
| 64 |
1/2✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
|
41 | base = "/sys/devices/platform/bus@f0000/30040000.pruss/30078000.pru/remoteproc/remoteproc"; |
| 65 | } else { | ||
| 66 | // M4 core, not PRU, but startable via remoteproc | ||
| 67 | ✗ | base = "/sys/devices/platform/bus@f0000/bus@f0000:bus@4000000/5000000.m4fss/remoteproc/remoteproc"; | |
| 68 | } | ||
| 69 |
1/2✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
|
88 | for (int x = 0; x < 10; x++) { |
| 70 |
5/8✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 47 times.
✓ Branch 7 taken 41 times.
|
88 | if (FileExists(base + std::to_string(x) + "/state")) { |
| 71 |
2/4✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
|
47 | return base + std::to_string(x) + "/state"; |
| 72 | } | ||
| 73 | } | ||
| 74 | ✗ | return "/sys/class/remoteproc/remoteproc" + std::to_string(pru_num) + "/state"; | |
| 75 | #endif | ||
| 76 | 47 | } | |
| 77 | 137 | void disable() { | |
| 78 |
1/2✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
|
137 | std::string filename = getRemoteProcStateFile(); |
| 79 |
2/4✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 137 times.
✗ Branch 3 not taken.
|
137 | if (FileExists(filename)) { |
| 80 |
1/2✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
|
137 | FILE* rp = fopen(filename.c_str(), "w"); |
| 81 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 136 times.
|
137 | if (rp == nullptr) { |
| 82 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | LogWarn(VB_CHANNELOUT, "BBBPru::Pru::disable() - could not open %s: %s\n", filename.c_str(), strerror(errno)); |
| 83 | 1 | return; | |
| 84 | } | ||
| 85 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | fprintf(rp, "stop"); |
| 86 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | fclose(rp); |
| 87 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 88 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | std::string f = GetFileContents(filename); |
| 89 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | TrimWhiteSpace(f); |
| 90 | 136 | int cnt = 0; | |
| 91 |
3/8✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 136 times.
|
136 | while (f != "offline" && cnt < 500) { |
| 92 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | |
| 93 | ✗ | f = GetFileContents(filename); | |
| 94 | ✗ | TrimWhiteSpace(f); | |
| 95 | ✗ | cnt++; | |
| 96 | } | ||
| 97 |
2/4✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
|
136 | if (f != "offline") { |
| 98 | ✗ | LogWarn(VB_CHANNELOUT, "BBBPru::Pru::disable() - could not stop PRU core %d State: %s\n", pru_num, f.c_str()); | |
| 99 | } | ||
| 100 | 136 | } | |
| 101 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1 times.
|
137 | } |
| 102 | 69 | bool enable() { | |
| 103 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | std::string filename = getRemoteProcStateFile(); |
| 104 | 69 | int cnt = 0; | |
| 105 |
3/8✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 69 times.
|
69 | while (!FileExists(filename) && cnt < 10000) { |
| 106 | ✗ | cnt++; | |
| 107 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | |
| 108 | } | ||
| 109 |
4/8✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 69 times.
✗ Branch 7 not taken.
|
69 | if (FileExists(filename) && cnt < 10000) { |
| 110 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | if (pru_num <= 1) { |
| 111 | // explicitly select the firmware image; the sysfs setting is | ||
| 112 | // sticky so anything else that loaded firmware on this core | ||
| 113 | // (tests, other tools) would otherwise be re-booted instead | ||
| 114 |
2/4✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
|
69 | std::string fwFile = filename.substr(0, filename.size() - 5) + "firmware"; |
| 115 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | FILE* fw = fopen(fwFile.c_str(), "w"); |
| 116 |
2/2✓ Branch 0 taken 68 times.
✓ Branch 1 taken 1 times.
|
69 | if (fw) { |
| 117 | #if defined(PLATFORM_BBB) | ||
| 118 |
1/2✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
|
53 | fprintf(fw, "am335x-pru%d-fw", pru_num); |
| 119 | #else | ||
| 120 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | fprintf(fw, "am62x-pru%d-fw", pru_num); |
| 121 | #endif | ||
| 122 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | fclose(fw); |
| 123 | } | ||
| 124 | 69 | } | |
| 125 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | FILE* rp = fopen(filename.c_str(), "w"); |
| 126 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68 times.
|
69 | if (rp == nullptr) { |
| 127 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not open %s: %s\n", filename.c_str(), strerror(errno)); |
| 128 | 1 | return false; | |
| 129 | } | ||
| 130 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | fprintf(rp, "start"); |
| 131 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | fclose(rp); |
| 132 | 68 | cnt = 0; | |
| 133 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 134 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | std::string f = GetFileContents(filename); |
| 135 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | TrimWhiteSpace(f); |
| 136 |
3/8✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 68 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 68 times.
|
68 | while (f != "running" && cnt < 500) { |
| 137 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | |
| 138 | ✗ | f = GetFileContents(filename); | |
| 139 | ✗ | TrimWhiteSpace(f); | |
| 140 | ✗ | cnt++; | |
| 141 | } | ||
| 142 |
2/4✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 68 times.
|
68 | if (f != "running") { |
| 143 | ✗ | LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not start PRU core %d State: %s\n", pru_num, f.c_str()); | |
| 144 | ✗ | return false; | |
| 145 | } | ||
| 146 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | } else { |
| 147 | ✗ | LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not start PRU core %d\n", pru_num); | |
| 148 | ✗ | return false; | |
| 149 | } | ||
| 150 | 68 | return true; | |
| 151 | 69 | } | |
| 152 | }; | ||
| 153 | |||
| 154 | static int prussUseCount = 0; | ||
| 155 | |||
| 156 | static uint8_t* ddr_mem_loc = nullptr; | ||
| 157 | static uint32_t ddr_phy_mem_loc = 0; | ||
| 158 | static size_t ddr_filelen = 0; | ||
| 159 | static uint8_t* base_memory_location = nullptr; | ||
| 160 | static PRUCoreInfo prus[2]; | ||
| 161 | |||
| 162 | #if defined(PLATFORM_BBB) | ||
| 163 | |||
| 164 | constexpr uintptr_t PRUSS_MMAP_BASE = 0x4a300000; | ||
| 165 | constexpr size_t PRUSS_MMAP_SIZE = 0x40000; | ||
| 166 | |||
| 167 | constexpr uintptr_t PRUSS_DATARAM0_PHYS_BASE = 0x4a300000; | ||
| 168 | constexpr uintptr_t PRUSS_DATARAM1_PHYS_BASE = 0x4a302000; | ||
| 169 | constexpr size_t PRUSS_DATARAM_SIZE = 8 * 1024; // 8K | ||
| 170 | |||
| 171 | constexpr uintptr_t PRUSS_SHAREDRAM_BASE = 0x4a310000; | ||
| 172 | constexpr size_t PRUSS_SHAREDRAM_SIZE = 12 * 1024; // 12K | ||
| 173 | |||
| 174 | constexpr uint32_t DDR_ADDR = 0x8f000000; | ||
| 175 | constexpr size_t DDR_SIZE = 0x00400000; | ||
| 176 | |||
| 177 | constexpr std::string FIRMWARE_PREFIX = "am335x"; | ||
| 178 | constexpr bool FAKE_PRU = false; | ||
| 179 | |||
| 180 | static uint8_t* PRUSS_SRAM_BASE = 0x00000000; | ||
| 181 | constexpr uint32_t PRUSS_SRAM_GLOBAL = 0x00000000; | ||
| 182 | constexpr size_t PRUSS_SRAM_SIZE = 0; | ||
| 183 | |||
| 184 | static uint8_t* PRUSS_M4RAM_BASE = 0x00000000; | ||
| 185 | constexpr uint32_t PRUSS_M4RAM_GLOBAL = 0x00000000; | ||
| 186 | constexpr size_t PRUSS_M4RAM_SIZE = 0; | ||
| 187 | |||
| 188 | #elif defined(PLATFORM_BB64) | ||
| 189 | constexpr uintptr_t PRUSS_MMAP_BASE = 0x30040000; | ||
| 190 | constexpr size_t PRUSS_MMAP_SIZE = 0x80000; | ||
| 191 | |||
| 192 | constexpr size_t PRUSS_DATARAM_SIZE = 8 * 1024; // 8K | ||
| 193 | constexpr uintptr_t PRUSS_DATARAM0_PHYS_BASE = 0x30040000; | ||
| 194 | constexpr uintptr_t PRUSS_DATARAM1_PHYS_BASE = 0x30042000; | ||
| 195 | |||
| 196 | constexpr uintptr_t PRUSS_SHAREDRAM_BASE = 0x30050000; | ||
| 197 | constexpr size_t PRUSS_SHAREDRAM_SIZE = 32 * 1024; // 32K | ||
| 198 | |||
| 199 | constexpr uint32_t DDR_ADDR = 0x8f000000; | ||
| 200 | constexpr size_t DDR_SIZE = 0x00400000; | ||
| 201 | |||
| 202 | // LDI32 r8, 0x70000000 // OCSRAM | ||
| 203 | // LDI32 r8, 0x0005040000 // M4 DRAM | ||
| 204 | |||
| 205 | static uint8_t* PRUSS_SRAM_BASE = nullptr; | ||
| 206 | constexpr uint32_t PRUSS_SRAM_GLOBAL = 0x70000000; | ||
| 207 | constexpr size_t PRUSS_SRAM_SIZE = 64 * 1024; // 64K | ||
| 208 | |||
| 209 | static uint8_t* PRUSS_M4RAM_BASE = nullptr; | ||
| 210 | constexpr uint32_t PRUSS_M4RAM_GLOBAL = 0x05040000; | ||
| 211 | constexpr size_t PRUSS_M4RAM_SIZE = 256 * 1024; // 256K; | ||
| 212 | |||
| 213 | constexpr std::string FIRMWARE_PREFIX = "am62x"; | ||
| 214 | |||
| 215 | static bool FAKE_PRU = !FileExists("/sys/class/remoteproc/remoteproc0/state"); | ||
| 216 | #endif | ||
| 217 | |||
| 218 | 89 | static void releasePrus() { | |
| 219 |
1/2✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
|
89 | if (ddr_mem_loc) { |
| 220 | 89 | munmap(ddr_mem_loc, ddr_filelen); | |
| 221 | 89 | ddr_mem_loc = nullptr; | |
| 222 | } | ||
| 223 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 1 times.
|
89 | if (base_memory_location) { |
| 224 | 88 | munmap(base_memory_location, PRUSS_MMAP_SIZE); | |
| 225 | 88 | base_memory_location = nullptr; | |
| 226 | } | ||
| 227 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 74 times.
|
89 | if (PRUSS_SRAM_BASE) { |
| 228 | 15 | munmap(PRUSS_SRAM_BASE, PRUSS_SRAM_SIZE); | |
| 229 | 15 | PRUSS_SRAM_BASE = nullptr; | |
| 230 | } | ||
| 231 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 74 times.
|
89 | if (PRUSS_M4RAM_BASE) { |
| 232 | 15 | munmap(PRUSS_M4RAM_BASE, PRUSS_M4RAM_SIZE); | |
| 233 | 15 | PRUSS_M4RAM_BASE = nullptr; | |
| 234 | } | ||
| 235 | 89 | } | |
| 236 | |||
| 237 | // mmap() reports failure as MAP_FAILED, not nullptr; normalise so the rest of | ||
| 238 | // this file can keep testing the pointers for null, and keep the first errno. | ||
| 239 | 208 | static uint8_t* mapOrNull(size_t len, int flags, int fd, off_t off, int& err) { | |
| 240 | 208 | void* p = mmap(0, len, PROT_WRITE | PROT_READ, flags, fd, off); | |
| 241 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 207 times.
|
208 | if (p == MAP_FAILED) { |
| 242 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!err) { |
| 243 | 1 | err = errno; | |
| 244 | } | ||
| 245 | 1 | return nullptr; | |
| 246 | } | ||
| 247 | 207 | return (uint8_t*)p; | |
| 248 | } | ||
| 249 | |||
| 250 | // Returns 0, or the errno of the first thing that failed. On failure nothing | ||
| 251 | // is left mapped. | ||
| 252 | 89 | static int initPrus() { | |
| 253 | 89 | int err = 0; | |
| 254 | 89 | int mem_fd = -1; | |
| 255 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 38 times.
|
53 | if (!FAKE_PRU) { |
| 256 |
1/2✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
|
51 | mem_fd = open("/dev/mem", O_RDWR | O_CLOEXEC); |
| 257 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
|
51 | if (mem_fd < 0) { |
| 258 | ✗ | return errno; | |
| 259 | } | ||
| 260 | 51 | base_memory_location = mapOrNull(PRUSS_MMAP_SIZE, MAP_SHARED, mem_fd, PRUSS_MMAP_BASE, err); | |
| 261 | if (PRUSS_SRAM_SIZE > 0) { | ||
| 262 | 15 | PRUSS_SRAM_BASE = mapOrNull(PRUSS_SRAM_SIZE, MAP_SHARED, mem_fd, PRUSS_SRAM_GLOBAL, err); | |
| 263 | } | ||
| 264 | if (PRUSS_M4RAM_SIZE > 0) { | ||
| 265 | 15 | PRUSS_M4RAM_BASE = mapOrNull(PRUSS_M4RAM_SIZE, MAP_SHARED, mem_fd, PRUSS_M4RAM_GLOBAL, err); | |
| 266 | } | ||
| 267 | } else { | ||
| 268 | 38 | base_memory_location = mapOrNull(PRUSS_MMAP_SIZE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, err); | |
| 269 | } | ||
| 270 | |||
| 271 | 89 | uint32_t ddr_addr = DDR_ADDR; | |
| 272 | 89 | size_t ddr_sizeb = DDR_SIZE; | |
| 273 | |||
| 274 |
3/4✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 38 times.
|
89 | if (ddr_mem_loc == nullptr && !FAKE_PRU) { |
| 275 | 51 | ddr_phy_mem_loc = ddr_addr; | |
| 276 | 51 | ddr_filelen = ddr_sizeb; | |
| 277 | 51 | ddr_mem_loc = mapOrNull(ddr_filelen, MAP_SHARED, mem_fd, ddr_addr, err); | |
| 278 |
1/2✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
|
51 | if (ddr_mem_loc) { |
| 279 | 51 | madvise(ddr_mem_loc, ddr_filelen, MADV_HUGEPAGE); | |
| 280 | } | ||
| 281 |
2/4✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
|
38 | } else if (ddr_addr == 0 || FAKE_PRU) { |
| 282 | // just malloc some memory so we don't crash | ||
| 283 | 38 | ddr_phy_mem_loc = ddr_addr; | |
| 284 | 38 | ddr_filelen = ddr_sizeb; | |
| 285 | 38 | ddr_mem_loc = mapOrNull(ddr_filelen, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, err); | |
| 286 | } | ||
| 287 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 38 times.
|
89 | if (mem_fd >= 0) { |
| 288 |
1/2✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
|
51 | close(mem_fd); |
| 289 | } | ||
| 290 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 88 times.
|
89 | if (err) { |
| 291 | 1 | releasePrus(); | |
| 292 | 1 | return err; | |
| 293 | } | ||
| 294 | |||
| 295 | 88 | prus[0].pru_num = 0; | |
| 296 | 88 | prus[1].pru_num = 1; | |
| 297 | 88 | prus[0].dataRam = base_memory_location + (PRUSS_DATARAM0_PHYS_BASE - PRUSS_MMAP_BASE); | |
| 298 | 88 | prus[0].dataRamSize = PRUSS_DATARAM_SIZE; | |
| 299 | 88 | prus[0].dataRamPhyLoc = PRUSS_DATARAM0_PHYS_BASE; | |
| 300 | 88 | prus[1].dataRam = base_memory_location + (PRUSS_DATARAM1_PHYS_BASE - PRUSS_MMAP_BASE); | |
| 301 | 88 | prus[1].dataRamSize = PRUSS_DATARAM_SIZE; | |
| 302 | 88 | prus[1].dataRamPhyLoc = PRUSS_DATARAM1_PHYS_BASE; | |
| 303 | 88 | prus[0].sharedRam = base_memory_location + (PRUSS_SHAREDRAM_BASE - PRUSS_MMAP_BASE); | |
| 304 | 88 | prus[0].sharedRamSize = PRUSS_SHAREDRAM_SIZE; | |
| 305 | 88 | prus[0].sharedRamPhyLoc = PRUSS_SHAREDRAM_BASE; | |
| 306 | 88 | prus[1].sharedRam = base_memory_location + (PRUSS_SHAREDRAM_BASE - PRUSS_MMAP_BASE); | |
| 307 | 88 | prus[1].sharedRamSize = PRUSS_SHAREDRAM_SIZE; | |
| 308 | 88 | prus[1].sharedRamPhyLoc = PRUSS_SHAREDRAM_BASE; | |
| 309 | |||
| 310 |
2/2✓ Branch 0 taken 176 times.
✓ Branch 1 taken 88 times.
|
264 | for (int x = 0; x < 2; x++) { |
| 311 | 176 | prus[x].sram = PRUSS_SRAM_BASE; | |
| 312 | 176 | prus[x].sram_addr = PRUSS_SRAM_GLOBAL; | |
| 313 | 176 | prus[x].sram_size = PRUSS_SRAM_SIZE; | |
| 314 | |||
| 315 | 176 | prus[x].m4ram = PRUSS_M4RAM_BASE; | |
| 316 | 176 | prus[x].m4ram_addr = PRUSS_M4RAM_GLOBAL; | |
| 317 | 176 | prus[x].m4ram_size = PRUSS_M4RAM_SIZE; | |
| 318 | } | ||
| 319 | |||
| 320 |
1/2✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
|
88 | if (ddr_sizeb) { |
| 321 | 88 | memset(ddr_mem_loc, 0, ddr_sizeb); | |
| 322 | } | ||
| 323 | 88 | __asm__ __volatile__("" :: | |
| 324 | : "memory"); | ||
| 325 | 88 | return 0; | |
| 326 | } | ||
| 327 | |||
| 328 | 122 | BBBPru::BBBPru(int pru, bool mapShared, bool mapOther) : | |
| 329 | 122 | pru_num(pru) { | |
| 330 |
2/2✓ Branch 0 taken 89 times.
✓ Branch 1 taken 33 times.
|
122 | if (prussUseCount == 0) { |
| 331 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 88 times.
|
89 | if (int err = initPrus()) { |
| 332 | // Nothing is mapped and prussUseCount is untouched, so no | ||
| 333 | // destructor work is owed. Output Init() runs inside the channel | ||
| 334 | // output loader's try/catch, which logs this, raises its own | ||
| 335 | // (reload-cleared) warning and drops the output -- instead of | ||
| 336 | // memset()ing through MAP_FAILED as this used to. | ||
| 337 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | std::string msg = std::string("could not map PRU memory: ") + FPPstrerror(err); |
| 338 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | LogErr(VB_CHANNELOUT, "BBBPru: %s\n", msg.c_str()); |
| 339 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | throw std::runtime_error(msg); |
| 340 | 1 | } | |
| 341 | } | ||
| 342 | 121 | prussUseCount++; | |
| 343 | |||
| 344 | 121 | this->data_ram = prus[pru].dataRam; | |
| 345 | 121 | this->data_ram_size = prus[pru].dataRamSize; | |
| 346 | |||
| 347 | 121 | this->ddr = ddr_mem_loc; | |
| 348 | 121 | this->ddr_size = ddr_filelen; | |
| 349 | 121 | this->ddr_addr = ddr_phy_mem_loc; | |
| 350 | |||
| 351 |
2/2✓ Branch 0 taken 93 times.
✓ Branch 1 taken 28 times.
|
121 | if (mapShared) { |
| 352 | 93 | this->shared_ram_size = prus[pru].sharedRamSize; | |
| 353 | 93 | this->shared_ram = prus[pru].sharedRam; | |
| 354 | } else { | ||
| 355 | 28 | this->shared_ram_size = 0; | |
| 356 | 28 | this->shared_ram = 0; | |
| 357 | } | ||
| 358 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 92 times.
|
121 | if (mapOther) { |
| 359 | 29 | int p = pru ? 0 : 1; | |
| 360 | 29 | this->other_data_ram = prus[p].dataRam; | |
| 361 | 29 | this->other_data_ram_size = prus[p].dataRamSize; | |
| 362 | } else { | ||
| 363 | 92 | this->other_data_ram_size = 0; | |
| 364 | 92 | this->other_data_ram = 0; | |
| 365 | } | ||
| 366 | |||
| 367 | 121 | this->sram = prus[pru].sram; | |
| 368 | 121 | this->sram_addr = prus[pru].sram_addr; | |
| 369 | 121 | this->sram_size = prus[pru].sram_size; | |
| 370 | |||
| 371 | 121 | this->m4ram = prus[pru].m4ram; | |
| 372 | 121 | this->m4ram_addr = prus[pru].m4ram_addr; | |
| 373 | 121 | this->m4ram_size = prus[pru].m4ram_size; | |
| 374 | 121 | } | |
| 375 | |||
| 376 | 121 | BBBPru::~BBBPru() { | |
| 377 | 121 | prussUseCount--; | |
| 378 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 33 times.
|
121 | if (prussUseCount == 0) { |
| 379 | 88 | releasePrus(); | |
| 380 | } | ||
| 381 | 121 | } | |
| 382 | |||
| 383 | // The core control register sits 0x22000 into the PRUSS mapping, one 0x2000 | ||
| 384 | // block per core, on both the AM335x and the AM62x. Bit 1 enables the core. | ||
| 385 | static constexpr uintptr_t PRU_CTRL_OFFSET = 0x22000; | ||
| 386 | static constexpr uintptr_t PRU_CTRL_STRIDE = 0x2000; | ||
| 387 | static constexpr uint32_t PRU_CTRL_ENABLE = 0x2; | ||
| 388 | |||
| 389 | 137 | static volatile uint32_t* pruControlReg(unsigned pru_num) { | |
| 390 | // only the two PRU cores: one block further on is the PRUSS CFG space | ||
| 391 |
2/4✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
|
137 | if (!base_memory_location || pru_num > 1) { |
| 392 | ✗ | return nullptr; | |
| 393 | } | ||
| 394 | 137 | return (volatile uint32_t*)(base_memory_location + PRU_CTRL_OFFSET + (pru_num * PRU_CTRL_STRIDE)); | |
| 395 | } | ||
| 396 | |||
| 397 | // Clear the core's enable bit and wait until it reads back clear with the | ||
| 398 | // core no longer running. A remoteproc stop alone does not guarantee this: | ||
| 399 | // the firmware's RESET_PRU_CLOCK read-modify-writes this same register to | ||
| 400 | // restart the cycle counter, and every FPP program runs it constantly while | ||
| 401 | // idle. A stop that clears EN between the firmware's read and its write is | ||
| 402 | // undone, and the core keeps running its old program while remoteproc | ||
| 403 | // reports it offline - acting on any command word it sees, and executing | ||
| 404 | // whatever lands in its instruction RAM when the next program is loaded. | ||
| 405 | // Our own write can be undone the same way, so it is repeated until it | ||
| 406 | // sticks; once the core is halted nothing can set the bit again. | ||
| 407 | 137 | static bool haltCore(unsigned pru_num) { | |
| 408 | 137 | volatile uint32_t* ctrl = pruControlReg(pru_num); | |
| 409 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
|
137 | if (!ctrl) { |
| 410 | ✗ | return true; | |
| 411 | } | ||
| 412 | 137 | constexpr uint32_t PRU_CTRL_RUNSTATE = 0x8000; | |
| 413 |
1/2✓ Branch 0 taken 243 times.
✗ Branch 1 not taken.
|
243 | for (int x = 0; x < 100; x++) { |
| 414 | 243 | uint32_t v = *ctrl; | |
| 415 |
4/4✓ Branch 0 taken 143 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 137 times.
✓ Branch 3 taken 6 times.
|
243 | if (!(v & PRU_CTRL_ENABLE) && !(v & PRU_CTRL_RUNSTATE)) { |
| 416 | 137 | return true; | |
| 417 | } | ||
| 418 | 106 | *ctrl = v & ~PRU_CTRL_ENABLE; | |
| 419 | 106 | __sync_synchronize(); | |
| 420 |
1/2✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
|
106 | std::this_thread::sleep_for(std::chrono::microseconds(10)); |
| 421 | } | ||
| 422 | ✗ | LogWarn(VB_CHANNELOUT, "BBBPru: PRU core %d did not halt (control 0x%X)\n", pru_num, *ctrl); | |
| 423 | ✗ | return false; | |
| 424 | } | ||
| 425 | |||
| 426 | 363 | static std::string pruFirmwareName(int pru_num) { | |
| 427 |
3/6✓ Branch 0 taken 363 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 363 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 363 times.
✗ Branch 5 not taken.
|
363 | return FIRMWARE_PREFIX + "-pru" + std::to_string(pru_num) + "-fw"; |
| 428 | } | ||
| 429 | |||
| 430 | // /lib/firmware/<prefix>-pruN-fw ships as a symlink to the "-fw.sleep" | ||
| 431 | // parking firmware that the image loads at boot. Copying the program | ||
| 432 | // through that name would overwrite the sleep firmware itself, so install | ||
| 433 | // the program under its own name and atomically repoint the link at it. | ||
| 434 | // Returns false if the program could not be copied in, leaving the | ||
| 435 | // previously installed program in place. | ||
| 436 | 121 | static bool installPRUFirmware(int pru_num, const std::string& program) { | |
| 437 |
2/4✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
|
121 | std::string fw = "/lib/firmware/" + pruFirmwareName(pru_num); |
| 438 |
3/6✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 121 times.
|
121 | if (!CopyFileContents(program, fw + ".fpp")) { |
| 439 | ✗ | return false; | |
| 440 | } | ||
| 441 |
1/2✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
|
121 | std::string tmpLink = fw + ".new"; |
| 442 | 121 | unlink(tmpLink.c_str()); | |
| 443 |
3/6✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
|
121 | if (symlink((pruFirmwareName(pru_num) + ".fpp").c_str(), tmpLink.c_str()) == 0) { |
| 444 | 121 | rename(tmpLink.c_str(), fw.c_str()); | |
| 445 | } | ||
| 446 | 121 | return true; | |
| 447 | 121 | } | |
| 448 | |||
| 449 | // Point the firmware name back at the sleep firmware so the next boot parks | ||
| 450 | // the PRU instead of re-running the last output program. FalconSerial in | ||
| 451 | // particular free-runs frames without waiting for a first command, so leaving | ||
| 452 | // it installed as the boot firmware blasts garbage on any muxed pins. | ||
| 453 | 68 | static void restorePRUSleepFirmware(int pru_num) { | |
| 454 |
2/4✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
|
68 | std::string sleepName = pruFirmwareName(pru_num) + ".sleep"; |
| 455 |
4/6✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 53 times.
|
68 | if (!FileExists("/lib/firmware/" + sleepName)) { |
| 456 | 15 | return; | |
| 457 | } | ||
| 458 |
2/4✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
✗ Branch 3 not taken.
|
53 | std::string fw = "/lib/firmware/" + pruFirmwareName(pru_num); |
| 459 |
1/2✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
|
53 | std::string tmpLink = fw + ".new"; |
| 460 | 53 | unlink(tmpLink.c_str()); | |
| 461 |
1/2✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
|
53 | if (symlink(sleepName.c_str(), tmpLink.c_str()) == 0) { |
| 462 | 53 | rename(tmpLink.c_str(), fw.c_str()); | |
| 463 | } | ||
| 464 |
2/2✓ Branch 0 taken 53 times.
✓ Branch 1 taken 15 times.
|
68 | } |
| 465 | |||
| 466 | 121 | bool BBBPru::run(const std::string& program, bool clearSharedMems, bool clearMems) { | |
| 467 |
1/2✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
|
121 | LogDebug(VB_CHANNELOUT, "BBBPru[%d]::run(%s)\n", pru_num, program.c_str()); |
| 468 | |||
| 469 | 189 | auto clearPRUMems = [&]() { | |
| 470 |
2/2✓ Branch 0 taken 55 times.
✓ Branch 1 taken 134 times.
|
189 | if (!clearMems) { |
| 471 | 55 | return; | |
| 472 | } | ||
| 473 | 134 | clearPRUMem(data_ram, data_ram_size); | |
| 474 |
2/2✓ Branch 0 taken 117 times.
✓ Branch 1 taken 17 times.
|
134 | if (clearSharedMems) { |
| 475 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 56 times.
|
117 | if (shared_ram) { |
| 476 | 61 | clearPRUMem(shared_ram, shared_ram_size); | |
| 477 | } | ||
| 478 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | if (other_data_ram) { |
| 479 | ✗ | clearPRUMem(other_data_ram, other_data_ram_size); | |
| 480 | } | ||
| 481 | } | ||
| 482 | 121 | }; | |
| 483 | // A program that is missing - a runtime firmware build that failed - must | ||
| 484 | // not start the core: the copy below would fail and the core would boot | ||
| 485 | // whatever an earlier run left installed, possibly another output's | ||
| 486 | // firmware, while this reported success. | ||
| 487 |
2/4✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 121 times.
|
121 | if (!FileExists(program)) { |
| 488 | ✗ | LogErr(VB_CHANNELOUT, "BBBPru[%d]: PRU program %s does not exist\n", pru_num, program.c_str()); | |
| 489 | ✗ | return false; | |
| 490 | } | ||
| 491 | 121 | bool enabled = true; | |
| 492 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 52 times.
|
68 | if (!FAKE_PRU) { |
| 493 | // halted before the program is installed: remoteproc loads it into | ||
| 494 | // the instruction RAM of a core it believes is stopped | ||
| 495 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | haltCore(pru_num); |
| 496 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | prus[pru_num].disable(); |
| 497 |
2/4✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
|
69 | if (!installPRUFirmware(pru_num, program)) { |
| 498 | ✗ | LogErr(VB_CHANNELOUT, "BBBPru[%d]: could not install PRU program %s\n", pru_num, program.c_str()); | |
| 499 | ✗ | return false; | |
| 500 | } | ||
| 501 | // Clear before the core starts as well as after. Firmware loading | ||
| 502 | // does not clear data RAM, so on a restart the previous run's | ||
| 503 | // handshake words (the SMEM ring config in particular) are still | ||
| 504 | // there: the new firmware passes its wait on them at once and reads | ||
| 505 | // config the ARM has not written yet. The clear after enable() stays | ||
| 506 | // for a PRUSS that was powered down, where these writes are dropped. | ||
| 507 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | clearPRUMems(); |
| 508 |
1/2✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
|
69 | enabled = prus[pru_num].enable(); |
| 509 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68 times.
|
69 | if (!enabled) { |
| 510 | 1 | return false; | |
| 511 | } | ||
| 512 |
2/4✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
|
52 | } else if (!installPRUFirmware(pru_num, program)) { |
| 513 | ✗ | LogErr(VB_CHANNELOUT, "BBBPru[%d]: could not install PRU program %s\n", pru_num, program.c_str()); | |
| 514 | ✗ | return false; | |
| 515 | } | ||
| 516 |
1/2✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
|
120 | clearPRUMems(); |
| 517 | /* | ||
| 518 | printf("DL: %p\n", data_ram); | ||
| 519 | printf("OL: %p\n", other_data_ram); | ||
| 520 | printf("SH: %p\n", shared_ram); | ||
| 521 | printf("DDR: %p\n", ddr); | ||
| 522 | */ | ||
| 523 | 120 | return true; | |
| 524 | } | ||
| 525 | |||
| 526 | #ifdef PLATFORM_BBB | ||
| 527 | 54 | void BBBPru::clearPRUMem(uint8_t* ptr, size_t sz) { | |
| 528 | 54 | memset(ptr, 0, sz); | |
| 529 | 54 | } | |
| 530 | 99 | void BBBPru::memcpyToPRU(uint8_t* dst, uint8_t* src, size_t sz) { | |
| 531 | 99 | memcpy(dst, src, sz); | |
| 532 | 99 | } | |
| 533 | #elif defined(PLATFORM_BB64) | ||
| 534 | #pragma GCC push_options | ||
| 535 | #pragma GCC optimize("O0") | ||
| 536 | // The optimized memset and memcpy on Arm64 will segfault | ||
| 537 | // when doing certain sized operations to un-cacheable | ||
| 538 | // ram segments. Need to use ldnp/stnp instructions | ||
| 539 | // | ||
| 540 | // The block loop moves whole 64 byte units and needs 16 byte aligned operands | ||
| 541 | // (the PRU memories map uncached, where an unaligned vector access faults), so | ||
| 542 | // the head/tail and anything that does not qualify go through the byte loop | ||
| 543 | // instead - slower, but correct for every size and alignment. Exactly sztotal | ||
| 544 | // bytes are written and no more. | ||
| 545 | 132 | void memcpy_ldnp(volatile unsigned char* dst, volatile unsigned char* src, int sztotal) { | |
| 546 | // NB: "sztotal - sztotal & 64" parses as "(sztotal - sztotal) & 64", i.e. | ||
| 547 | // always zero, which left the block loop below writing one fixed 64 byte | ||
| 548 | // store - past the end for anything shorter - and everything else crawling | ||
| 549 | // through the byte loop. Mask off the low bits instead. | ||
| 550 | 132 | int sz = 0; | |
| 551 |
4/4✓ Branch 0 taken 68 times.
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 2 times.
|
132 | if (sztotal >= 64 && ((((uintptr_t)dst | (uintptr_t)src) & 15) == 0)) { |
| 552 | 66 | sz = sztotal & ~63; | |
| 553 | } | ||
| 554 |
2/2✓ Branch 0 taken 4992 times.
✓ Branch 1 taken 132 times.
|
5124 | for (int x = sz; x < sztotal; x++) { |
| 555 | 4992 | dst[x] = src[x]; | |
| 556 | } | ||
| 557 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 66 times.
|
132 | if (sz == 0) { |
| 558 | 66 | return; | |
| 559 | } | ||
| 560 | 66 | asm volatile( | |
| 561 | "NEONCopyPLD: \n" | ||
| 562 | "sub %[dst], %[dst], #64 \n" | ||
| 563 | "1: \n" | ||
| 564 | "ldnp q0, q1, [%[src]] \n" | ||
| 565 | "ldnp q2, q3, [%[src], #32] \n" | ||
| 566 | "add %[dst], %[dst], #64 \n" | ||
| 567 | "subs %[sz], %[sz], #64 \n" | ||
| 568 | "add %[src], %[src], #64 \n" | ||
| 569 | "stnp q0, q1, [%[dst]] \n" | ||
| 570 | "stnp q2, q3, [%[dst], #32] \n" | ||
| 571 | "b.gt 1b \n" | ||
| 572 | : [dst] "+r"(dst), [src] "+r"(src), [sz] "+r"(sz) : : "q0", "q1", "q2", "q3", "cc", "memory"); | ||
| 573 | } | ||
| 574 | |||
| 575 | 141 | void BBBPru::clearPRUMem(uint8_t* ptr, size_t sz) { | |
| 576 | 141 | __uint128_t z = 0; | |
| 577 | 141 | int c = sz / sizeof(z); | |
| 578 | 141 | __uint128_t* p = (__uint128_t*)ptr; | |
| 579 |
2/2✓ Branch 0 taken 165888 times.
✓ Branch 1 taken 141 times.
|
166029 | for (int x = 0; x < c; ++x) { |
| 580 | 165888 | *p = z; | |
| 581 | 165888 | p++; | |
| 582 | } | ||
| 583 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
|
141 | for (int x = c * sizeof(z); x < sz; ++x) { |
| 584 | ✗ | ptr[x] = 0; | |
| 585 | } | ||
| 586 | 141 | } | |
| 587 | 132 | void BBBPru::memcpyToPRU(uint8_t* dst, uint8_t* src, size_t sz) { | |
| 588 | 132 | memcpy_ldnp(dst, src, sz); | |
| 589 | 132 | } | |
| 590 | #pragma GCC pop_options | ||
| 591 | #endif | ||
| 592 | |||
| 593 | static std::mutex ddrAllocLock; | ||
| 594 | // owner -> (offset, size) within the reserved region | ||
| 595 | static std::map<std::string, std::pair<size_t, size_t>> ddrAllocs; | ||
| 596 | |||
| 597 | 45 | uint8_t* BBBPru::ddrAlloc(const std::string& owner, size_t size, uint32_t& physAddr, size_t minOffset) { | |
| 598 |
1/2✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
|
45 | std::unique_lock<std::mutex> lock(ddrAllocLock); |
| 599 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
|
45 | if (ddr_mem_loc == nullptr) { |
| 600 | ✗ | LogErr(VB_CHANNELOUT, "BBBPru::ddrAlloc(%s): DDR region not mapped yet\n", owner.c_str()); | |
| 601 | ✗ | return nullptr; | |
| 602 | } | ||
| 603 | 45 | size = (size + 4095) & ~(size_t)4095; | |
| 604 | 45 | size_t pos = (minOffset + 4095) & ~(size_t)4095; | |
| 605 |
1/2✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
|
45 | ddrAllocs.erase(owner); |
| 606 | 45 | std::vector<std::pair<size_t, size_t>> used; | |
| 607 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 45 times.
|
49 | for (auto& a : ddrAllocs) { |
| 608 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | used.push_back(a.second); |
| 609 | } | ||
| 610 |
1/2✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
|
45 | std::sort(used.begin(), used.end()); |
| 611 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 44 times.
|
48 | for (auto& u : used) { |
| 612 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (pos + size <= u.first) { |
| 613 | 1 | break; | |
| 614 | } | ||
| 615 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (u.first + u.second > pos) { |
| 616 | 3 | pos = u.first + u.second; | |
| 617 | } | ||
| 618 | } | ||
| 619 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43 times.
|
45 | if (pos + size > ddr_filelen) { |
| 620 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | LogErr(VB_CHANNELOUT, "BBBPru::ddrAlloc(%s): %zuKB does not fit in the PRU DDR region (%zuKB of %zuKB already in use)\n", |
| 621 | owner.c_str(), size / 1024, pos / 1024, ddr_filelen / 1024); | ||
| 622 | 2 | return nullptr; | |
| 623 | } | ||
| 624 |
1/2✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
|
43 | ddrAllocs[owner] = { pos, size }; |
| 625 | 43 | physAddr = ddr_phy_mem_loc + pos; | |
| 626 |
1/2✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
|
43 | LogInfo(VB_CHANNELOUT, "BBBPru: DDR region: '%s' at 0x%zX-0x%zX (%zuKB)\n", |
| 627 | owner.c_str(), pos, pos + size, size / 1024); | ||
| 628 | 43 | return ddr_mem_loc + pos; | |
| 629 | 45 | } | |
| 630 | |||
| 631 | 93 | void BBBPru::ddrRelease(const std::string& owner) { | |
| 632 |
1/2✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
|
93 | std::unique_lock<std::mutex> lock(ddrAllocLock); |
| 633 |
1/2✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
|
93 | ddrAllocs.erase(owner); |
| 634 | 93 | } | |
| 635 | |||
| 636 | 119 | void BBBPru::stop() { | |
| 637 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
|
66 | if (!FAKE_PRU) { |
| 638 | 68 | haltCore(pru_num); | |
| 639 | 68 | prus[pru_num].disable(); | |
| 640 | 68 | restorePRUSleepFirmware(pru_num); | |
| 641 | } | ||
| 642 | 119 | } | |
| 643 | |||
| 644 | ✗ | void BBBPru::pause() { | |
| 645 | ✗ | if (FAKE_PRU) { | |
| 646 | ✗ | return; | |
| 647 | } | ||
| 648 | ✗ | volatile uint32_t* ctrl = pruControlReg(pru_num); | |
| 649 | ✗ | if (ctrl) { | |
| 650 | ✗ | *ctrl = *ctrl & ~PRU_CTRL_ENABLE; | |
| 651 | ✗ | __sync_synchronize(); | |
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | ✗ | void BBBPru::resume() { | |
| 656 | ✗ | if (FAKE_PRU) { | |
| 657 | ✗ | return; | |
| 658 | } | ||
| 659 | ✗ | volatile uint32_t* ctrl = pruControlReg(pru_num); | |
| 660 | ✗ | if (ctrl) { | |
| 661 | ✗ | *ctrl = *ctrl | PRU_CTRL_ENABLE; | |
| 662 | ✗ | __sync_synchronize(); | |
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | #include "../pru/SMEMRing.hp" | ||
| 667 | |||
| 668 | 47 | void BBBPruSMEMRing::attach(BBBPru* pru, uint32_t pruBaseAddr, uint32_t ringSize, bool usePointerMode) { | |
| 669 | 47 | basePru = pruBaseAddr; | |
| 670 | 47 | size = ringSize; | |
| 671 | 47 | pointerMode = usePointerMode; | |
| 672 | 47 | writeOff = 0; | |
| 673 | 47 | produced = 0; | |
| 674 | 47 | uint8_t* arm = pru->shared_ram + (pruBaseAddr - SMEM_RING_SMEM_PRU_ADDR); | |
| 675 | 47 | ring = (volatile uint64_t*)arm; | |
| 676 | 47 | ctrl = (volatile uint32_t*)(arm + ringSize); | |
| 677 |
1/2✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
|
47 | ctrl[0] = pointerMode ? basePru : 0; |
| 678 |
1/2✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
|
47 | ctrl[1] = pointerMode ? basePru : 0; |
| 679 | // the firmware polls for the config; base first, then the nonzero size | ||
| 680 | 47 | volatile uint32_t* rc = (volatile uint32_t*)(pru->data_ram + SMEM_RING_CONFIG_OFFSET); | |
| 681 | 47 | rc[0] = basePru; | |
| 682 | 47 | rc[1] = ringSize; | |
| 683 | 47 | } | |
| 684 | |||
| 685 | 2935 | uint32_t BBBPruSMEMRing::write(const uint8_t* src, uint32_t len) { | |
| 686 | // Counters are free running, so their difference is the exact number of | ||
| 687 | // bytes in flight across the 2^32 wrap. Reducing a raw count modulo the | ||
| 688 | // ring size is not: 2^32 is not a multiple of the size, so each wrap moved | ||
| 689 | // the computed read position (by 4096 bytes on a 30720 byte ring) and, | ||
| 690 | // once that error was large enough, let the producer overwrite data the | ||
| 691 | // consumer had not read yet. | ||
| 692 |
1/2✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
|
2935 | uint32_t used = pointerMode ? (writeOff - (ctrl[1] - basePru) + size) % size |
| 693 | ✗ | : (produced - ctrl[1]); | |
| 694 | 2935 | uint32_t space = size - 64 - used; | |
| 695 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2935 times.
|
2935 | if (len > space) { |
| 696 | ✗ | len = space; | |
| 697 | } | ||
| 698 | 2935 | uint32_t total = 0; | |
| 699 |
2/2✓ Branch 0 taken 3856 times.
✓ Branch 1 taken 2935 times.
|
6791 | while (total < len) { |
| 700 | 3856 | uint32_t n = len - total; | |
| 701 |
2/2✓ Branch 0 taken 921 times.
✓ Branch 1 taken 2935 times.
|
3856 | if (n > (size - writeOff)) { |
| 702 | 921 | n = size - writeOff; | |
| 703 | } | ||
| 704 | 3856 | volatile uint64_t* d = ring + writeOff / 8; | |
| 705 | 3856 | const uint64_t* s = (const uint64_t*)(src + total); | |
| 706 |
2/2✓ Branch 0 taken 2473672 times.
✓ Branch 1 taken 3856 times.
|
2477528 | for (uint32_t i = 0; i < n / 8; i++) { |
| 707 | 2473672 | d[i] = s[i]; | |
| 708 | } | ||
| 709 | 3856 | writeOff += n; | |
| 710 |
2/2✓ Branch 0 taken 1042 times.
✓ Branch 1 taken 2814 times.
|
3856 | if (writeOff == size) { |
| 711 | 1042 | writeOff = 0; | |
| 712 | } | ||
| 713 | 3856 | produced += n; | |
| 714 | 3856 | total += n; | |
| 715 | } | ||
| 716 |
1/2✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
|
2935 | if (total) { |
| 717 | // the data must be observable before the position update | ||
| 718 | 2935 | __sync_synchronize(); | |
| 719 |
1/2✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
|
2935 | ctrl[0] = pointerMode ? (basePru + writeOff) : produced; |
| 720 | } | ||
| 721 | 2935 | return total; | |
| 722 | } | ||
| 723 | |||
| 724 | 48 | void CompilePRUProgram(const std::string& script, const std::vector<std::string>& args) { | |
| 725 | // argv is built before the fork: the child of a threaded process should | ||
| 726 | // do nothing but exec | ||
| 727 | 48 | std::vector<char*> argv; | |
| 728 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | argv.push_back((char*)"/bin/bash"); |
| 729 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | argv.push_back((char*)script.c_str()); |
| 730 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | std::string log = script; |
| 731 |
2/2✓ Branch 0 taken 506 times.
✓ Branch 1 taken 48 times.
|
554 | for (auto& a : args) { |
| 732 |
1/2✓ Branch 0 taken 506 times.
✗ Branch 1 not taken.
|
506 | argv.push_back((char*)a.c_str()); |
| 733 |
2/4✓ Branch 0 taken 506 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 506 times.
✗ Branch 3 not taken.
|
506 | log += " " + a; |
| 734 | } | ||
| 735 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | argv.push_back(nullptr); |
| 736 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | LogDebug(VB_CHANNELOUT, "Compiling PRU program: %s\n", log.c_str()); |
| 737 | |||
| 738 | 48 | pid_t pid = fork(); | |
| 739 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
|
96 | if (pid < 0) { |
| 740 | ✗ | LogErr(VB_CHANNELOUT, "Could not fork to compile PRU program %s: %s\n", script.c_str(), strerror(errno)); | |
| 741 | ✗ | return; | |
| 742 | } | ||
| 743 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
|
96 | if (pid == 0) { |
| 744 | 48 | execv("/bin/bash", argv.data()); | |
| 745 | // never return into the caller: that would be a second fppd | ||
| 746 | 48 | _exit(127); | |
| 747 | } | ||
| 748 | // returns when this child exits (with ECHILD, as it is reaped for us) | ||
| 749 |
6/8✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 48 times.
|
51 | while (waitpid(pid, nullptr, 0) < 0 && errno == EINTR) { |
| 750 | } | ||
| 751 |
2/4✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
|
48 | } |
| 752 |