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
- Add the Zicsr extension (CSR instructions), Zicntr (hardware counters), and Zmmul (integer multiply) to your Project 1 core.
- Implement a CMAC custom instruction (multiply-accumulate with implicit CSR accumulator) using the
custom-0opcode. - Design a simple memory-mapped on-chip bus (a minimal Wishbone-like interface is recommended).
- 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).
- 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):
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
- Correctness of the ISA extensions (Zicsr, Zicntr, Zmmul) and the CMAC custom instruction.
- Design and documentation of the memory-mapped bus / memory map.
- Correctness of the UART and timer/GPIO peripherals, including drivers.
- End-to-end demonstration on real hardware (e.g., echo a string over UART, read cycle counter via CSR).
- Code quality, git usage, and documentation.