SoftSSD-Platform

Development Notes of SoftSSD

Setup

Firmware Preparation

  1. Install AMD Xilinx Vitis and Vivado Tools.
  2. Download the source code.
  3. Create a Vivado project and generate the bitstream, or use a pre-generated bitstream file.
  4. Initialize the Vitis project (according to the content of the README).
  5. Compile the Vitis project to generate the firmware.

    Hardware Bootup

  6. Prepare two host machines:

   one serves as the host PC for downloading firmware to and monitoring the output from the target board, and the other as the test machine.

  1. Install flash chips.
  2. (Optional) Configure the flash parameters.
  3. Adjust the power switch.
  4. Connect the board to the host PC
  5. Insert the board into the test machine’s PCIe slot.
  6. Boot the board by downloading and launching the firmware.
  7. Boot the test machine’s
  8. The storage device can be observed using lspci or lsblk.

Software modules

Host Interface Layer [code@nvme_ftl/src/hostif]

Flash Translation Layer [code@nvme_ftl/src/ftl]

Consequently, when the data cache flushes a dirty cache block, it must consult the Logical-to-Physical (L2P) mapping table to identify which 4 KB sectors within the 16 KB page are valid. If only a subset of sectors is being updated, a Read-Modify-Write operation is performed to preserve the integrity of the unchanged sectors.

Initially, the in-memory mapping table cache is empty. Translation pages are fetched into memory only when needed and are updated out-of-place: when modified, dirty pages are written back to new flash locations using a write-back policy, and the updated physical addresses are recorded in the GTD.

The current garbage collection (GC) policy is intra-plane, online, and blocking. Specifically, when the block manager attempts to open a new block and finds insufficient free blocks available on the plane, it triggers an intra-plane GC operation. During this GC process, all other read and write requests directed to that plane are blocked until GC completes.

Flash Interface Layer [code@nvme_fil/src/fil]

The Flash Interface Layer is responsible for handling data read, write, and erase operations on the flash chips. It first enqueues flash transactions into a per-chip pending queue and processes requests from each chip in a round-robin polling manner. Flash commands are issued through the ONFI controller.

ECC Engine[code@nvme_ecc/src]

Both ECC encoding and decoding are now fully integrated into the flash controller hardware. Once the controller is properly configured, it automatically performs ECC operations on all data during read and write transactions—without requiring software intervention. The ECC parity bits are stored alongside the user data in the out-of-band (OOB) area of each flash page.

However, when ECC decoding detects an uncorrectable error (or in some implementations, even a correctable multi-bit error), explicit error handling is required, and the correction procedure is handled by the code in ECC engine.

Metadata Persistence and Recovery

After data is written to flash memory, certain metadata structures in the SSD’s internal memory are generated or updated. These metadata must be persisted to non-volatile storage to ensure consistency and durability. When the host issues an fsync request, the SSD performs the following operations in sequence:

  1. Flushes dirty data pages from the data cache to flash;
  2. Writes back dirty translation pages from the L2P mapping cache;
  3. Persists the Global Translation Directory (GTD);
  4. Saves the per-plane block allocation state (e.g., open block information and free block bitmap).

Upon system shutdown, the host sends a Shutdown Notification (as defined in the NVMe specification) to the SSD. In response, the SSD executes the same fsync-like persistence sequence to safely commit all in-memory metadata to flash before power is removed.

Important: The SSD board does not include power-loss protection capacitors. An unexpected power loss will result in data corruption or metadata inconsistency, as in-flight writes and unflushed metadata cannot be recovered.

Request Processing Flow

NVMe requests residing in the Submission Queue (SQ) are fetched by the SSD into its internal memory via PCIe DMA. Each request is then split into 16 KB-aligned sub-requests, which are processed sequentially. Upon completion of all sub-requests, a completion entry is posted to the Completion Queue (CQ).

For each sub-request, the SSD first checks the data cache:

During both data write-back and data loading, the SSD consults the L2P mapping cache to translate logical addresses to physical flash locations:

Notably, data write-back requires allocation of a new flash page (due to the out-of-place update nature of NAND flash).

All flash read and write operations—for both data pages and translation pages—are performed by submitting requests to a dedicated request queue of an ARM core that runs the Flash Interface Layer. Completed flash operations are returned via a completion queue associated with that core.

Coroutine Scheduling

Our SSD platform employs a multi-tasking concurrency model to process multiple NVMe requests simultaneously. Each task handles a single NVMe request, and tasks share critical resources using locks, semaphores, and other synchronization primitives.

Underlying this model is a lightweight coroutine framework. The main program initializes 16 task coroutines at startup. During the execution gaps between coroutine yields, the scheduler polls various queues—such as the NVMe Submission Queues (SQs) and flash request completion queues—and resumes the appropriate coroutines when their awaited events or data become available.