Skip to content

Instantly share code, notes, and snippets.

@dzaima
Forked from camel-cdr/README.md
Last active February 20, 2026 12:44
Show Gist options
  • Select an option

  • Save dzaima/645c92635c2717805b65b22d1d9222b0 to your computer and use it in GitHub Desktop.

Select an option

Save dzaima/645c92635c2717805b65b22d1d9222b0 to your computer and use it in GitHub Desktop.
Visualizing the RISC-V Instruction Set
#ifndef SEL
#define SEL 2 // select between first and second image
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <immintrin.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include "encoding.out.h"
/*
* This "encoding.out.h" was auto-generated by running
* 'PYTHONPATH=src python -m riscv_opcodes -c "rv*"' in
* https://github.com/riscv/riscv-opcodes (a2766fd)
* with the following patch applied:
* diff --git a/src/riscv_opcodes/c_utils.py b/src/riscv_opcodes/c_utils.py
* index 198a37f..236fe6f 100644
* --- a/src/riscv_opcodes/c_utils.py
* +++ b/src/riscv_opcodes/c_utils.py
* @@ -15,2 +15,3 @@ def make_c(instr_dict: InstrDict):
* declare_insn_str = ""
* + myexts = set()
* for i in instr_dict:
* @@ -22,3 +23,10 @@ def make_c(instr_dict: InstrDict):
* )
* - declare_insn_str += f'DECLARE_INSN({i.replace(".","_")}, MATCH_{i.upper().replace(".","_")}, MASK_{i.upper().replace(".","_")})\n'
* + myexts.add(instr_dict[i]["extension"][0])
* + declare_insn_str += f'DECLARE_INSN(EXT_{instr_dict[i]["extension"][0]}, {i.replace(".","_")}, MATCH_{i.upper().replace(".","_")}, MASK_{i.upper().replace(".","_")})\n'
* + ext_enum = ""
* + ext_str = ""
* + for e in myexts:
* + ext_enum += ( f'EXT_{e},\n' )
* + ext_str += ( f'"{e}",')
* +
*
* @@ -65,2 +73,7 @@ def make_c(instr_dict: InstrDict):
* {mask_match_str}
* +typedef enum Ext {{
* +EXT_NONE,
* +{ext_enum}EXT_COUNT
* +}} Ext;
* +const char *ext2str[EXT_COUNT] = {{ {ext_str} }};
* {csr_names_str}
*/
#define ARR_LEN(a) (sizeof (a) / sizeof *(a))
typedef struct ExtGroup {
const char *name;
Ext *exts;
} ExtGroup;
#define EXT_custom EXT_COUNT
#define EXT_gt32b (EXT_COUNT+1)
typedef struct Insn { uint32_t ext, match, mask; } Insn;
Insn insns[] = {
{EXT_custom, 0b1011011, 0b1011111 },
{EXT_custom, 0b0101011, 0b0111111 },
{EXT_gt32b, 0b0011111, 0b0011111 },
#define DECLARE_INSN(ext,name,match,mask) {ext, match, mask},
#include "encoding.out.h"
#undef DECLARE_INSN
};
#if SEL == 1
static const uint32_t BG = 0xFF100408;
static uint32_t colors[] = { 0x5F8E02, 0x002ECB, 0x1A95FF, };
static ExtGroup groups[] = {
{ "16-bit", (Ext[]){ EXT_rv_c, EXT_rv64_c, EXT_rv_c_d, EXT_rv_zcb, EXT_rv64_zcb, 0 } },
{ "custom", (Ext[]){ EXT_custom, 0 } },
{ "32-bit", (Ext[]){ EXT_rv_i, EXT_rv64_i, EXT_rv_m, EXT_rv64_m, EXT_rv_zicond, EXT_rv_zicsr, EXT_rv_zifencei, EXT_rv_zknh, EXT_rv64_zknd, EXT_rv_zksed, EXT_rv_zksh, EXT_rv64_zkne, EXT_rv64_zknh, EXT_rv_zba, EXT_rv64_zba, EXT_rv_zbb, EXT_rv64_zbb, EXT_rv_zbs, EXT_rv64_zbs, EXT_rv_zbc, EXT_rv_zbkb, EXT_rv_zbkx, EXT_rv64_zbkb, EXT_rv_a, EXT_rv64_a, EXT_rv_zabha, EXT_rv_zabha_zacas, EXT_rv_zacas, EXT_rv64_zacas, EXT_rv_zawrs, EXT_rv_s, EXT_rv_sdext, EXT_rv_smrnmi, EXT_rv_ssctr, EXT_rv_svinval, EXT_rv_system, EXT_rv_h, EXT_rv64_h, EXT_rv_svinval_h, EXT_rv_zfbfmin, EXT_rv_zicbo, EXT_rv_zimop, EXT_rv_zfh_zfa, EXT_rv_zfh, EXT_rv64_zfh, EXT_rv_zfhmin, EXT_rv_d_zfhmin, EXT_rv_f, EXT_rv64_f, EXT_rv_f_zfa, EXT_rv_d, EXT_rv64_d, EXT_rv_d_zfa, EXT_rv_q, EXT_rv64_q, EXT_rv_q_zfa, EXT_rv64_q_zfa, EXT_rv_q_zfhmin, EXT_rv_v, EXT_rv_zvbb, EXT_rv_zvbc, EXT_rv_zvkg, EXT_rv_zvkned, EXT_rv_zvknha, EXT_rv_zvksed, EXT_rv_zvksh, EXT_rv_zvfbfmin, EXT_rv_zvfbfwma, 0 } },
};
#elif SEL == 2
static const uint32_t BG = 0xFF332827;
static uint32_t colors[] = { 0xF8F8F2, 0xEFD966, 0x5ACFDC, 0x1F97FD, 0xFF81AE, 0x7226F9, };
static ExtGroup groups[] = {
{ "custom", (Ext[]){ EXT_custom, 0 } },
//{ "C", (Ext[]){ EXT_rv_c, EXT_rv64_c, EXT_rv_c_d, EXT_rv_zcb, EXT_rv64_zcb, 0 } },
{ "general", (Ext[]){ EXT_rv_i, EXT_rv64_i, EXT_rv_m, EXT_rv64_m, EXT_rv_zicond, EXT_rv_zicsr, EXT_rv_zifencei, EXT_rv_zknh, EXT_rv64_zknd, EXT_rv_zksed, EXT_rv_zksh, EXT_rv64_zkne, EXT_rv64_zknh, EXT_rv_zba, EXT_rv64_zba, EXT_rv_zbb, EXT_rv64_zbb, EXT_rv_zbs, EXT_rv64_zbs, EXT_rv_zbc, EXT_rv_zbkb, EXT_rv_zbkx, EXT_rv64_zbkb, EXT_rv_a, EXT_rv64_a, EXT_rv_zabha, EXT_rv_zabha_zacas, EXT_rv_zacas, EXT_rv64_zacas, EXT_rv_zawrs, 0 } },
{ "system", (Ext[]){ EXT_rv_zicsr, EXT_rv_zifencei, EXT_rv_s, EXT_rv_sdext, EXT_rv_smrnmi, EXT_rv_ssctr, EXT_rv_svinval, EXT_rv_system, EXT_rv_h, EXT_rv64_h, EXT_rv_svinval_h, EXT_rv_zicbo, EXT_rv_zimop, 0 } },
{ "FP", (Ext[]){ EXT_rv_zfh_zfa, EXT_rv_zfh, EXT_rv64_zfh, EXT_rv_zfhmin, EXT_rv_d_zfhmin, EXT_rv_f, EXT_rv64_f, EXT_rv_f_zfa, EXT_rv_d, EXT_rv64_d, EXT_rv_d_zfa, EXT_rv_q, EXT_rv64_q, EXT_rv_q_zfa, EXT_rv64_q_zfa, EXT_rv_q_zfhmin, EXT_rv_zfbfmin, 0 } },
{ "V", (Ext[]){ EXT_rv_v, EXT_rv_zvbb, EXT_rv_zvbc, EXT_rv_zvfbfmin, EXT_rv_zvfbfwma, 0 } },
{ "Zvk", (Ext[]){ EXT_rv_zvkg, EXT_rv_zvkned, EXT_rv_zvknha, EXT_rv_zvksed, EXT_rv_zvksh, 0 } },
};
#endif
_Static_assert(ARR_LEN(colors) >= ARR_LEN(groups), "insufficient colors defined");
#if SEL == 1
#define SHIFT 13
#define WIDTH (1ull<<13)
static const float amt_scale = 1.0 / (1<<6); // 2^(13*2) / 2^32
#elif SEL == 2
#define SHIFT 11
#define WIDTH (1ull<<11)
static const float amt_scale = 1.0 / (1<<8); // 4x larger to compensate only 25% of the encoding space being available
#endif
#include <immintrin.h>
uint32_t rol32(uint32_t v, int a) {
return v << a | v >> (32-a);
}
uint32_t sag(uint32_t v, uint32_t m) {
uint32_t lo = _pext_u32(v, m);
uint32_t hi = _pext_u32(v, ~m);
return hi<<__builtin_popcount(m) | lo;
}
static uint32_t
perm(uint32_t* scal, uint32_t u, bool comp) {
// pull interesting bits to LSB
if (comp) {
// compressed mostly puts the opcode in MSBs; funky sequence to pull bits to LSB in reverse-order-ish
u = sag(u, 0b1110000000000011);
u = sag(u, 0b1000000000011111);
u = sag(u, 0b1000000000111111);
u = sag(u, 0b1000000001111111);
// printf("%016b\n", u);
} else {
// funct7 srcs/imm fn3 dst opcode
u = sag(u, 0b1111111'0000000000'111'00000'1111111);
}
// reverse to put opcode first
u = ((u >> 1) & 0x55555555) | ((u & 0x55555555) << 1);
u = ((u >> 2) & 0x33333333) | ((u & 0x33333333) << 2);
u = ((u >> 4) & 0x0F0F0F0F) | ((u & 0x0F0F0F0F) << 4);
u = __builtin_bswap32(u);
if (SEL==2) u = rol32(u, 2); // for 32B-only, rotate away the boring 0b11 LSBs
int la = 32 - SHIFT*2;
uint32_t hi = u >> la;
uint32_t lo = u & ((1u<<la) - 1);
// if (SEL==1) printf("%032b %026b %06b\n", u, hi, lo);
// if (SEL==2) printf("%032b %022b %010b\n", u, hi, lo);
*scal = 1<<__builtin_popcount(lo);
// interleave for morton code
uint32_t x = _pext_u32(hi, 0b01010101010101010101010101010101) & (WIDTH-1);
uint32_t y = _pext_u32(hi, 0b10101010101010101010101010101010) & (WIDTH-1);
return x+y*WIDTH;
}
void unp(uint32_t dst[4], uint32_t src) {
for (int i = 0; i < 4; i++) dst[i] = (src>>(i*8)) & 0xff;
}
uint32_t pck(uint16_t src[4], bool opaque) {
uint32_t bad = false;
for (int i = 0; i < 4; i++) bad |= src[i]>255;
uint32_t r = 0;
if (bad) {
uint32_t max = 0;
for (int i = 0; i < 4; i++) if (src[i]>=max) max = src[i];
for (int i = 0; i < 4; i++) r |= (src[i] * 255 / max) << (i*8);
// printf("overflow in [%u,%u,%u,%u]; representing as %08x\n", src[0], src[1], src[2], src[3], r); // overflow does just happen on overlapping encodings (e.g. nop and add)
} else {
for (int i = 0; i < 4; i++) r |= src[i] << (i*8);
}
if (opaque) r |= 0xFF000000;
return r;
}
int
main(void)
{
size_t total = 0;
uint16_t (*raw)[4] = malloc(WIDTH*WIDTH * sizeof *raw);
uint32_t *amt = malloc(WIDTH*WIDTH * sizeof *amt);
for (size_t i = 0; i < WIDTH*WIDTH; ++i) {
for (int j = 0; j < 4; j++) raw[i][j] = 0;
}
for (size_t g = 0; g < ARR_LEN(groups); ++g) {
uint32_t color = colors[g] | 0xFF000000;
uint32_t b1[4];
unp(b1, color);
printf("%s:\t0x%08X\n", groups[g].name, color);
for (size_t i = 0; i < WIDTH*WIDTH; ++i) amt[i] = 0;
for (Ext *e = groups[g].exts; *e; ++e) {
for (size_t i = 0; i < ARR_LEN(insns); ++i) {
if (insns[i].ext != *e) continue;
uint32_t match0 = insns[i].match;
uint32_t mask0 = ~insns[i].mask;
bool comp = (match0&3) != 3;
uint32_t _, scal;
uint32_t match = perm(&_, match0, comp);
uint32_t mask = perm(&scal, mask0, comp);
uint32_t cnt = 1ull << __builtin_popcount(mask);
uint32_t opcode = 0;
for (uint32_t c = 0; c < cnt; ++c) {
++total;
size_t idx = opcode & mask | match;
amt[idx]+= scal;
opcode = (opcode | ~mask) + 1;
}
}
}
for (size_t i = 0; i < WIDTH*WIDTH; ++i) {
uint32_t n = amt[i];
if (n) {
float f = n * amt_scale;
for (int j = 0; j < 4; j++) raw[i][j] += b1[j]*f;
}
}
}
uint32_t *img = malloc(WIDTH*WIDTH * sizeof *img);
for (size_t i = 0; i < WIDTH*WIDTH; ++i) {
uint16_t* curr = raw[i];
// if (curr[3]) for (int i = 0; i < 3; i++) curr[i] = curr[i] * 255 / curr[3];
img[i] = pck(curr, true);
}
printf("%zu\n", total);
stbi_write_png("out.png", WIDTH, WIDTH, 4, img, WIDTH*4);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment