Skip to content

Reference

Quick-lookup tables for encodings, constants, and signal names.


RV32I/M Opcode Map

Opcode [6:0] Hex Instruction class Engine
0110111 0x37 LUI CPU
0010111 0x17 AUIPC CPU
1101111 0x6F JAL CPU
1100111 0x67 JALR CPU
1100011 0x63 BRANCH (BEQ/BNE/BLT/BGE/BLTU/BGEU) CPU
0000011 0x03 LOAD (LB/LH/LW/LBU/LHU) CPU
0100011 0x23 STORE (SB/SH/SW) CPU
0010011 0x13 ALU-IMM (ADDI/SLTI/ANDI/ORI/XORI/SLLI/SRLI/SRAI) CPU
0110011 0x33 ALU-REG (ADD/SUB/AND/OR/XOR/SLT/SLL/SRL/SRA) + M-ext CPU
1010111 0x57 custom-0 (GPU vector ALU, see gpu.md) GPU
1101011 0x6B custom-1 (NPU v2) NPU

ALU Control Codes

Defined in both rtl/alu.sv and rtl/riscv_core.sv as localparam:

alu_control[3:0] Name Operation
4'd0 ALU_ADD a + b
4'd1 ALU_SUB a - b
4'd2 ALU_AND a & b
4'd3 ALU_OR a \| b
4'd4 ALU_XOR a ^ b
4'd5 ALU_SLT ($signed(a) < $signed(b)) ? 1 : 0
4'd6 ALU_SLTU (a < b) ? 1 : 0 (unsigned)
4'd7 ALU_SLL a << b[4:0]
4'd8 ALU_SRL a >> b[4:0] (logical)
4'd9 ALU_SRA $signed(a) >>> b[4:0] (arithmetic)
4'd10 ALU_MUL a × b lower 32 bits (3-cycle)
4'd11 ALU_MULH a × b upper 32 bits (3-cycle, signed)
4'd12 ALU_DIV $signed(a) / $signed(b) (34-cycle; div-by-0 → -1, INT_MIN/-1INT_MIN)
4'd13 ALU_DIVU a / b unsigned (34-cycle; div-by-0 → 0xFFFFFFFF)
4'd14 ALU_REM $signed(a) % $signed(b) (34-cycle; div-by-0 → a, INT_MIN/-10)
4'd15 ALU_REMU a % b unsigned (34-cycle; div-by-0 → a)

All 16 values of the 4-bit alu_control bus are in use — see "Known ISA Gaps" below for why MULHSU/MULHU aren't in this table.


CPU Writeback Selector (wb_sel[1:0])

Value Source Used by
2'b00 alu_result ALU-reg, ALU-imm, AUIPC, JALR address
2'b01 mem_rdata (load-extended) LW/LH/LB/LHU/LBU
2'b10 pc + 4 JAL, JALR (link address)
2'b11 imm_ext LUI (no ALU needed)

NPU Instruction Encoding

Opcode: 7'h6B (1101011)

 31    26 25    18 17   14 13   10  9   7  6       0
 ┌───────┬────────┬───────┬───────┬─────┬──────────┐
 │unused │  data  │  col  │  row  │ cmd │  0x6B    │
 │[31:26]│[25:18] │[17:14]│[13:10]│[9:7]│  [6:0]  │
 └───────┴────────┴───────┴───────┴─────┴──────────┘
Field Bits Width Type
opcode [6:0] 7 constant 0x6B
cmd [9:7] 3 NPU command
row [13:10] 4 weight row index (0–15)
col [17:14] 4 weight/activation col index (0–15)
data [25:18] 8 INT8 signed payload
unused [31:26] 6

NPU Command Codes

cmd [9:7] Mnemonic Payload used
3'b000 LOAD_W row, col, data
3'b001 LOAD_A col, data
3'b010 COMPUTE
3'b011 COMPUTE_ACC
3'b100 SWAP — (flips the ping-pong weight bank)
3'b101 SET_PREC row[0] (instr[10]): 0=INT8, 1=INT16
other NOP

INT16 mode repurposes the data field as 14 raw bits at instr[31:18] (sign-extended to 16 — see rtl/npu.sv's data_int16), not the 8-bit [25:18] field shown in the diagram above; the assembler (scripts/riscv_asm.py) always writes the value at instr[31:18] and lets the NPU's current precision mode decide how many of those bits matter.

Python Instruction Builder

def npu_instr(cmd, row=0, col=0, data=0):
    return (0x6B
            | (cmd   & 0x7) << 7
            | (row   & 0xF) << 10
            | (col   & 0xF) << 14
            | (data  & 0xFF) << 18)

NPU FSM States

State Encoding Description
IDLE 3'd0 Ready to accept instruction
LOAD_W_ST 3'd1 Writing one weight element
LOAD_A_ST 3'd2 Writing one activation element
COMPUTE_ST 3'd3 Computing acc = W·A (1 cycle)
COMP_ACC_ST 3'd4 Computing acc += W·A (1 cycle)
OUTPUT_ST 3'd5 Streaming 16 INT32 results

RV32I funct3 — Branch

funct3 Instruction ALU op selected
3'h0 BEQ ALU_SUB (checks zero)
3'h1 BNE ALU_SUB (checks zero)
3'h4 BLT ALU_SLT
3'h5 BGE ALU_SLT
3'h6 BLTU ALU_SLTU
3'h7 BGEU ALU_SLTU

See cpu.md (Stage 3 — Execute) for the full branch-resolution writeup — BLT/BGE/BLTU/BGEU need an actual comparison, not just alu_result[0] off a subtraction.


RV32I funct3 — Load

funct3 Instruction Sign
3'h0 LB signed byte
3'h1 LH signed halfword
3'h2 LW 32-bit word
3'h4 LBU unsigned byte
3'h5 LHU unsigned halfword

RV32I funct3 — Store / Byte Enable

funct3 Instruction mem_byte_en
3'h0 SB 0001/0010/0100/1000 by addr[1:0]
3'h1 SH 0011 (addr[1]=0) / 1100 (addr[1]=1)
3'h2 SW 1111

RV32M funct7 + funct3

All M-extension instructions: opcode=0x33, funct7=0x01

funct3 Instruction
3'h0 MUL (lower 32 bits)
3'h1 MULH (upper 32 bits, signed×signed)
3'h2 MULHSU (signed×unsigned)
3'h3 MULHU (unsigned×unsigned)
3'h4 DIV
3'h5 DIVU
3'h6 REM
3'h7 REMU

MUL/MULH/DIV/DIVU/REM/REMU are all implemented in alu.sv (3-cycle multiplier, 34-cycle divider). MULHSU/MULHU are the only gap — see "Known ISA Gaps" below.


Known ISA Gaps

Instruction Status Why
MULHSU (funct3=3'h2) Not implemented riscv_core.sv's OP_REG/M-ext decode has no case for it — falls through to default: ;, leaving alu_ctrl at its ALU_ADD default. The instruction silently computes rs1 + rs2 instead of a high-multiply.
MULHU (funct3=3'h3) Not implemented Same gap as MULHSU.

Fixing this needs a real ALU control code for each (there is no free encoding — all 16 values of the 4-bit alu_control bus are already assigned, see the ALU Control Codes table above), which means widening alu_control past 4 bits across the riscv_core.svalu.sv boundary. Tracked as a future item rather than done opportunistically, to keep that interface change deliberate rather than a side effect of an unrelated fix.

scripts/riscv_asm.py (the assembler — see the toolchain doc) refuses to assemble mulhsu/mulhu with an explicit error rather than silently emitting an instruction that computes the wrong thing.


Key Signal Names

Signal Location Description
net_valid/ready system_top boundary Network source handshake
cpu_accept server_dispatch_unitsystem_top 1-cycle SRAM write pulse
imem_wr_ptr system_top SRAM write pointer (word address)
core_stall riscv_coresystem_top MUL busy signal
mul_busy aluriscv_core ALU multiply counter running
mul_done riscv_core internal 1-cycle pulse: MUL result valid
branch_taken riscv_core EX stage Redirect fetch PC
flush_if_id riscv_core EX stage Clear IF/ID register (branch penalty)
load_use_stall riscv_core ID stage 1-cycle stall for load-use hazard
npu_ready npu Only high in IDLE state
result_valid npu High during OUTPUT_ST
halt system_top jal x0,0 sentinel detected

Simulation Constants

Constant Value Description
HALT_WORD 32'h0000_006F jal x0, 0 — simulation terminator
SRAM size 256 words (1 KiB) Instruction memory
MUL latency 3 cycles ALU counter: mul_cnt runs 2→1→0
NPU output lanes 16 One INT32 result per cycle in OUTPUT_ST
NPU max INT32 258,064 16 × 127 × 127, fits in INT32
cocotb version 2.0.1 From Docker container
iverilog version 11.0 (stable) From Docker container