---
# System prepended metadata

title: "\U0001F527 Lessons Learned: Using a Req/Ack Handshake Protocol for CPU-Accessible Control & Status Registers (CSR)"

---

# 🔧 Lessons Learned: Using a Req/Ack Handshake Protocol for CPU-Accessible Control & Status Registers (CSR) - PART I

Recently, we designed a hardware module that exposes its internal control/status registers (CSR) to a CPU through a simple `req/ack` handshake interface. While the protocol itself is minimal and elegant, implementing it robustly—especially from the CPU's perspective—came with several key challenges.

## Background

### ✅ Req/Ack Handshake Protocol – Hardware Module Interface Specification

The `req/ack` interface defines a lightweight handshake protocol commonly used for synchronous communication between hardware modules. It is particularly useful for coordinating control transactions or command executions where explicit synchronization is required between an initiator and a target module.

---

### 🔧 Signal Definitions

| Signal     | Direction           | Description |
|------------|---------------------|-------------|
| `req`      | Initiator → Target  | Asserted by the initiator to indicate a valid request. It remains high until the target acknowledges it. |
| `ack`      | Target → Initiator  | A one-cycle pulse asserted by the target when the request has been processed and completed. |

> Optional extensions (application dependent):
> - `cmd` / `addr` / `data_in` → from Initiator to Target  
> - `data_out` → from Target to Initiator  
> - `write_en` or `op` → to distinguish read/write/custom operations  

---

### 🔁 Handshake Behavior

1. **Request Phase**:
   - The initiator raises `req` to signal a new request.
   - `req` must be held high until `ack` is received.

2. **Acknowledge Phase**:
   - The target begins processing once it detects `req` high.
   - Upon completing the task, the target issues a **1-cycle pulse** on `ack`.
   - The initiator deasserts `req` after detecting the `ack` pulse.

---

### 📈 Timing Diagram Summary

- `req` is **level-sensitive** and remains high during the request.
- `ack` is a **1-cycle pulse** to indicate completion.
- Handshake completes when `ack` is received and `req` is deasserted.
```plantuml
@startuml
' Define a clock
clock clk with period 1

' Define req and ack as concise timelines
binary "req" as R
binary "ack" as A

' Initial state
@0
R is low
A is low

@1
R is high

@4
A is high

@5
A is low
R is low

@enduml
```
---

### 📌 Design Guidelines

- The interface assumes both modules operate in the **same clock domain**.
- `req` must not be deasserted until `ack` is observed.
- `ack` must only be asserted while `req` is high.
- One request must be fully completed before the next is issued.

---


## Practical Pitfalls and How to Address Them

Here are 3 practical pitfalls we encountered, and how we addressed them:

---

### (1) Clock Domain Crossing (CDC) Risks

> When the CPU and CSR module are in different clock domains, `req` and `ack` signals must be synchronized to avoid metastability.


---

### (2) ACK as a 1-Cycle Pulse – Easy to Miss

> If the CPU polls for `ack`, it might miss the 1-cycle pulse and hang indefinitely, waiting for a signal that already passed.


---

### (3) REQ Timing Is Hard to Control in Software

> It's non-trivial for software to assert `req`, wait for `ack`, and deassert `req` in tight coordination, especially without hardware assistance.
