Skip to content

Instantly share code, notes, and snippets.

@xypron
Last active September 2, 2026 20:06
Show Gist options
  • Select an option

  • Save xypron/83f68494e144069674e337fbd243a2ee to your computer and use it in GitHub Desktop.

Select an option

Save xypron/83f68494e144069674e337fbd243a2ee to your computer and use it in GitHub Desktop.
vloxei64.v.test.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define PAGE_SIZE 4096
// Robust 16-bit bit reversal using mask-and-swap layers (bytes -> nibbles -> pairs -> bits)
uint16_t reverse_16_bits(uint16_t x, uint32_t bit_width) {
// 1. Swap bytes: 0xAABB -> 0xBBAA
x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8);
// 2. Swap nibbles: 0x1234 -> 0x2143
x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4);
// 3. Swap 2-bit pairs
x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2);
// 4. Swap individual bits
x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1);
// Shift down to the actual active bit width of our system
// Example: If bit_width is 4 (16 elements), we shift down by (16 - 4) = 12
return x >> (16 - bit_width);
}
int main() {
uint32_t num_elements = 0;
// 1. Query the hardware to check how many elements fit in LMUL=1
asm volatile (
"vsetvli %0, zero, e8, m1, ta \n\t"
: "=r" (num_elements)
);
printf("Detected Hardware Profiling:\n");
printf(" Elements handled per register (VLEN/8): %u\n", num_elements);
printf(" Implied VLEN size: %u bits\n\n", num_elements * 8);
// Calculate the binary bit-width needed for this element count
uint32_t bit_width = 0;
while ((1 << bit_width) < num_elements) {
bit_width++;
}
// 2. Allocate page-aligned memory chunks dynamically
uint8_t **pages = malloc(num_elements * sizeof(uint8_t*));
for (uint32_t i = 0; i < num_elements; i++) {
if (posix_memalign((void**)&pages[i], PAGE_SIZE, PAGE_SIZE) != 0) {
perror("Allocation failed");
return 1;
}
*pages[i] = (uint8_t)(0x55 ^ i);
}
// 3. Setup base address and compute true bit-reversed offsets
uintptr_t base_addr = (uintptr_t)pages;
uint64_t *offsets = malloc(num_elements * sizeof(uint64_t));
for (uint32_t i = 0; i < num_elements; i++) {
uint32_t rev_i = reverse_16_bits((uint16_t)i, bit_width);
offsets[i] = (uint64_t)((uintptr_t)pages[rev_i] - base_addr);
}
// Allocate output buffer
uint8_t *destination_output = calloc(num_elements, sizeof(uint8_t));
// 4. Inline Dynamic RISC-V Assembly Block
asm volatile (
"vsetvli zero, %3, e8, m1, ta \n\t"
// Step A: Switch configuration to load the 64-bit indices.
// Index EMUL = (64 / 8) * m1 = m8.
"vsetvli zero, %3, e64, m8, ta \n\t"
"vle64.v v8, (%1) \n\t" // Fills v8-v15 completely
// Step B: Revert back to the destination data layout configuration
"vsetvli zero, %3, e8, m1, ta \n\t"
// Step C: Execute Gather Load
"vloxei64.v v0, (%0), v8 \n\t"
// Step D: Store out the dynamically filled vector register
"vse8.v v0, (%2) \n\t"
:
: "r" (base_addr), "r" (offsets), "r" (destination_output), "r" (num_elements)
: "t0", "memory", "v4", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15"
);
// 5. Dynamic Verification Check
int success = 1;
for (uint32_t i = 0; i < num_elements; i++) {
uint32_t rev_i = reverse_16_bits((uint16_t)i, bit_width);
uint8_t expected = (uint8_t)(0x55 ^ rev_i);
uint8_t actual = destination_output[i];
printf("Slot %5d: Expected Page %5d (Value 0x%02x), Loaded 0x%02x -> %s\n",
i, rev_i, expected, actual, (expected == actual) ? "PASS" : "FAIL");
if (expected != actual) {
success = 0;
}
}
// Cleanup
for (uint32_t i = 0; i < num_elements; i++) free(pages[i]);
free(pages);
free(offsets);
free(destination_output);
if (success) {
printf("\nResult: SUCCESS! 16-bit bit-reversed data verified successfully across all lanes.\n");
return 0;
} else {
printf("\nResult: FAILURE! Data verification failed.\n");
return 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment