30 July 2021

SPRAM on iCE40 FPGA

The iCE40 UltraPlus distinguishes itself from the rest of the iCE40 FPGA family by including a relatively generous 1 Mb (128 KiB) of single port synchronous SRAM known as SPRAM. SPRAM blocks are much larger than BRAM but are limited to a single port and 16-bit data bus.

In this quick FPGA recipe, we learn how to use SPRAM with Yosys and contrast it with Block RAM. If you want to learn more about other FPGA memory, see FPGA Memory Types.

Updated 2021-09-20. Share your thoughts with @WillFlux or find me on 1BitSquared Discord.

Specification

iCE40 UltraPlus FPGAs include four 256 Kb SPRAM blocks organized as 16K x 16-bit (16,384 locations of 16 bits each). You can combine these blocks to create wider or deeper memories. For example, you can create a 32-bit memory by combining two SPRAM blocks to form a 16K x 32-bit memory (64 KiB).

Using SPRAM

SPRAM is easy to use, but you need to explicitly request SPRAM: Yosys won’t infer it for you.
The SPRAM primitive is named SB_SPRAM256KA.

See the comparison with Block RAM for more details on which memory is best for your design.

Example Verilog Module

Here’s a simple Verilog wrapper around a single 256 Kb SPRAM block that works with Yosys:

module spram #(
    localparam WIDTH=16,     // fixed data width: 16-bits
    localparam DEPTH=16384,  // fixed depth: 16K 
    localparam ADDRW=$clog2(DEPTH)
    ) (
    input wire logic clk,
    input wire logic [3:0] we,
    input wire logic [ADDRW-1:0] addr,
    input wire logic [WIDTH-1:0] data_in,
    output     logic [WIDTH-1:0] data_out
    );

    SB_SPRAM256KA spram_inst (
        .ADDRESS(addr),
        .DATAIN(data_in),
        .MASKWREN(we),
        .WREN(|we),
        .CHIPSELECT(1'b1),
        .CLOCK(clk),
        .STANDBY(1'b0),
        .SLEEP(1'b0),
        .POWEROFF(1'b1),
        .DATAOUT(data_out)
    );
endmodule

This example uses a 4-bit write enable signal, we, to select individual nibbles for write. If any of the bits of we are set, then WREN is enabled for writing. All the ports are described in the next section.

Port Summary

The SB_SPRAM256KA primitive has the following ports:

Port Default Description
ADDRESS [13:0] 14'h0 Memory address
DATAIN [15:0] 16'h0 Value to write
MASKWREN [3:0] 4'b1111 Select nibbles for writing
WREN 1'b0 Write enable (active high)
CHIPSELECT 1'b0 Enable block (active high)
CLOCK N/A Clock for memory
STANDBY 1'b0 Light sleep (active high)
SLEEP 1'b0 Deep sleep (active high)
POWEROFF 1'b1 Turn off memory (active low)
DATAOUT [15:0] N/A Output value from memory read

All ports are inputs apart from DATAOUT.
For details on the sleep and power pins see the iCE40 SPRAM Usage Guide.

Write Mask

SPRAM has a fixed 16-bit data bus, but you can select individual nibbles (4 bits) for writing with MASKWREN. CPUs usually expect memory to be byte writeable; the write mask allows you to trivially support byte writes. 4-bit writes are more unusual but come in handy when working with SPI devices or 16 colour (4 bit) graphics.

For example, with MASKWREN(4'b1100), the upper byte of DATAIN is written to memory.

Initializing SPRAM

Unlike Block RAM, SPRAM cannot be initialized by device configuration, so you can’t use $readmemh. SPRAM starts out with undefined values, which you need to explicitly initialize in logic if required.

Compared with Block RAM

The following comparison can help you decide between SPRAM and block RAM:

Feature SPRAM Block RAM (EBR)
Ports One (Shared by Read & Write) Separate Read & Write or one R/W
Data Width Fixed 16 bit (4-bit write mask) 2, 4, 8, or 16 bit
Block Size 256 Kb (32 KiB) 4 Kb (512 bytes)
Block Count 4 30 (UP5K) or 20 (UP3K)
Total Size 1024 Kb 120 Kb (UP5K) or 80 Kb (UP3K)
Init Values No Yes

NB. There are two models of UltraPlus, UP3K and UP5K, but both include the full 1 Mb of SPRAM.

Project Ideas

For examples of SPRAM as a framebuffer, see Lines and Triangles and 2D Shapes.

If you’re doing CPU development, try combining two SPRAM blocks to make a 32-bit wide, 64 KiB memory.

iCE40UP Boards

The following boards include the iCE40UP5K FPGA:

Reference

Constructive feedback is always welcome. Get in touch with @WillFlux or open an issue on GitHub.

Sponsor Project F
If you like what I do, consider sponsoring me on GitHub.
I’ll use contributions to spend more time creating open-source FPGA designs and tutorials.

©2021 Will Green, Project F