Skip to content

Project 2 - The HW/SW Interface

This project builds the bridge between the processor you built in Project 1 and the rest of the system. You will extend its instruction set with standard extensions (Zicsr, Zicntr, Zmmul) and a custom instruction (CMAC), design a small memory-mapped bus, and attach peripherals to it - including a UART that will later be used to load data for the Keyword Spotting example.

Objectives

  1. Add the Zicsr extension (CSR instructions), Zicntr (hardware counters), and Zmmul (integer multiply) to your Project 1 core.
  2. Implement a CMAC custom instruction (multiply-accumulate with implicit CSR accumulator) using the custom-0 opcode.
  3. Design a simple memory-mapped on-chip bus (a minimal Wishbone-like interface is recommended).
  4. Implement and integrate at least two memory-mapped peripherals:
    • A UART (used from Project 2 onward to load input data and report results).
    • A timer or GPIO peripheral (the timer complements the Zicntr CSR counters with programmable compare/interrupt capability).
  5. Write C drivers for these peripherals and demonstrate their use from software running on your core.

Specification

Standard extensions

Zicsr — adds 6 instructions for reading/writing Control and Status Registers: CSRRW, CSRRS, CSRRC, and their immediate variants (CSRRWI, CSRRSI, CSRRCI). These are I-type instructions with opcode = 1110011. This extension is a prerequisite for Zicntr and for the CMAC accumulator.

Zicntr — defines three 64-bit read-only CSR counters: cycle/cycleh (clock cycles), instret/instreth (instructions retired), and time/timeh (wall-clock time). These are used for profiling in Project 3 via rdcycle and rdinstret pseudoinstructions. Comparing cycle vs instret gives the CPI of your multicycle core.

Zmmul — the multiply-only subset of the M extension (ratified 2022, designed for microcontrollers and FPGA soft cores). Adds 4 instructions sharing the same R-type encoding (opcode = 0110011, funct7 = 0000001):

Instruction funct3 Result
MUL 000 Low 32 bits of rs1 × rs2
MULH 001 High 32 bits (signed × signed)
MULHSU 010 High 32 bits (signed × unsigned)
MULHU 011 High 32 bits (unsigned × unsigned)

Compile with -march=rv32i_zicsr_zmmul.

Custom instruction: CMAC

Implement a CMAC (custom multiply-accumulate) instruction using the custom-0 opcode (0001011). CMAC accumulates rs1 * rs2 into a dedicated 32-bit CSR (macc, address 0x800):

CMAC rs1, rs2:   macc_csr += rs1 * rs2

The accumulator is read with csrr rd, macc and cleared with csrw macc, zero — reusing the Zicsr instructions already implemented.

This is a true custom instruction: it uses a RISC-V reserved opcode, its encoding is your own design, and GCC does not know about it natively. Access it from C via inline assembly (.insn directive) or a macro wrapper.

Bus and memory map

Define a memory map that separates instruction/data memory from peripheral address space. Peripherals are accessed via ordinary load/store instructions to fixed addresses (memory-mapped I/O). This memory-mapped-registers style is one of several ways an accelerator can talk to a CPU - see Design Space & Alternatives for the others, and why we chose this one.

The table below is a suggested starting point - adjust it to your own design (e.g., your RAM size from Project 1), but document whatever map you settle on:

Address range Region Notes
0x0000_0000 - 0x0000_FFFF Instruction memory From Project 1
0x0001_0000 - 0x0001_FFFF Data memory From Project 1
0x8000_0000 - 0x8000_000F UART TXDATA (W), RXDATA (R), STATUS (R: bit0=TX ready, bit1=RX data available)
0x8000_0010 - 0x8000_001F Timer CYCLES (R, free-running 32-bit counter), CTRL (W: reset/enable)
0x8000_0020 - 0x8000_002F GPIO OUT (W), IN (R)

Zmmul and CMAC are ISA instructions (result written to register file or CSR) — they do not appear in the memory map.

Peripherals

  • UART: a simple transmit/receive UART (no flow control required) is sufficient. This is the channel you will use to load MFCC feature vectors onto the board and to report classification results back to a host PC (see Running Example).
  • Timer/GPIO: a free-running cycle counter (complements the Zicntr CSR counters with programmable compare or interrupt capability) and/or GPIO for driving LEDs.

Drivers

Provide C functions (e.g., uart_putc/uart_getc, timer_read) that wrap the memory-mapped registers, and a small demo program exercising each peripheral.

Deliverables

  • Updated SystemVerilog source: core with Zicsr, Zicntr, Zmmul, CMAC, bus, and peripherals.
  • Verilator testbenches covering all new instructions and each peripheral.
  • Memory map documentation.
  • C drivers and a demo program, executed on the Tang Nano 9K.

Submission

Same pair as Project 1 (see Home). Submit via GitHub Classroom. Due: see Calendar.

Evaluation criteria

  1. Correctness of the ISA extensions (Zicsr, Zicntr, Zmmul) and the CMAC custom instruction.
  2. Design and documentation of the memory-mapped bus / memory map.
  3. Correctness of the UART and timer/GPIO peripherals, including drivers.
  4. End-to-end demonstration on real hardware (e.g., echo a string over UART, read cycle counter via CSR).
  5. Code quality, git usage, and documentation.