brup 0.1.1

Tool for updating the BRAM contents of NextPNR Lattice ECP5 FPGA configuration files
brup-0.1.1 is not a library.

brup - Block Ram UPdate tool

Berke Durak bd@exhrd.fr

Description

Tired of waiting after NextPNR just to get a bitstream with updated block RAM contents?

Suppose you have a design having a softcore CPU, say a RISCV core, with firmware in the initial contents of one or more memory blocks. These can be inferred by Yosys or explicitly instantiated in your RTL.

Once your RTL code is more or less stable you will be spending lots of time iterating on your firmware without any other changes to your RTL. During some or all the phases of your design you will want to test your changes on the hardware. You make a change to your firmware and recompile it. Compilation should be fast, as code that can fit in the block RAMs will be quite small. But you now need a new bitstream to actually run it on the FPGA.

Typically you type "make" and this re-runs Yosys, which is also pretty fast, but then NextPNR takes a while. It has to do placing and routing, which is a difficult combinatorial optimization problem. After that you can configure your FPGA over JTAG, which only takes one or two seconds.

Overall, if it were not for NextPNR, you would be able to test a change to your code in a couple of seconds. Instead, you have to wait tens of seconds if your design is small and up to many minutes if it is large. NextPNR wastes your time solving basically the same problem over and over again.

Using brup you can instantly update the already-routed ECP5 configuration file with your new BRAM contents without having to re-run NextPNR. Your edit-compile-run cycle only takes seconds.

Usage

brup works by locating the known contents of your firmware (or other memory) in the chip configuration produced by NextPNR, and replacing them with the new version.

To allow successful location, the memory contents need to be such that each column of each BRAM is unique, according to the way Yosys lays them out.

Yosys has a complex memory layout strategy, and the Lattice ECP5 memory blocks can be configured to have different widths. Yosys can slice the memory contents in multiple ways. It may reorder bits and may even delete bit positions that are constant.

Therefore, for successful updates, you should synthesize your RTL using (pseudo)-random contents for the memories that are to be updated using brup, so that the tool can locate them unambiguosly.

Step 1. Generate random hex files

For each memory that you want to update using brup, you need a unique pattern file pattern123.hex.

Suppose that you're using Verilog code and that your file foo.v contains a declaration for a 32-bit wide, 512 word memory rom123:

   reg [31:0] rom123[512-1:0]; 
   initial $readmemh("pattern123.hex",rom123);

Generate a random pattern using brup

brup genpat --output pattern.hex --width 32 --count 512 --seed 123

where 123 is distinct arbitrary integer for each memory. If you only have one memory you may omit --seed 123. You need to give the width of your memory with --width.

Alternatively, you can use the following incantation:

od -t x4 -A null /dev/urandom |
  tr ' ' '\n' | grep '^........$' | head -512 >pattern.hex

Step 2. Generate your initial configuration

Use Yosys then NextPNR for synthesis and placement as you usually do. Make sure you keep the text configuration produced by nextpnr-ecp5:

yosys -p "synth_ecp5 -top top -abc9 -json foo.json" foo.v
nextpnr-ecp5 --json foo.json --lpf foo.lpf ... --textcfg foo.cfg

Step 3. Produce your updated hex file

Compile your firmware and produce your hex file as usual, e.g.

riscv64-linux-gnu-gcc-12 -Wall -c foo_fw.c
riscv64-linux-gnu-ld <ETC.>
python3 makehex.py foo 4096 >foo.hex

You obtain foo_fw.hex

Step 4. Update your configuration

You may now call brup to produce an updated text configuration. Do not overwrite your initial foo.cfg!

brup update \
  --config foo.cfg \
  --mem pattern.hex \
  --new-mem foo_fw.hex \
  --width 32
  --output foo_with_fw.cfg

Step 5. Use ecppack to obtain an SVF file and configure your FPGA

Something like:

ecppack foo_with_fw.cfg --svf foo_with_cfg.svf
openocd -f ecp5.cfg -c "transport select jtag;init;svf foo_with_cfg.svf;exit"

Step 6. Fix your code, and repeat from step 3

Note that you don't need to re-route again!

Supported hardware and file formats

So far, brup has been designed for and only works with Lattice ECP5 configuration files produced by NextPNR and Yosys.

Configuration file formats

Arch Tool Format
ECP5 PrjTrellis Text configuration

Command-line interface

The general form is brup CMD args... where CMD is a subcommand. Available subcommands follow.

Subcommand Description
update Main subcommand, update config with new contents
genpat Generate pseudo-random pattern
help Display help
cpcfg Read and re-create a chip configuration
brdump Dump BRAM contents
brlayout Dump BRAM contents using a specific layout

The update subcommand

Takes an input chip configuration, a hex file giving the current contents, a hex file with the desired (new) contents and produces an updated NextPNR configuration.

Argument Description
--config PATH Chip configuration file produced by NextPNR
--mem PATH Hex file with the current memory contents of the config
--new-mem PATH Hex file with the desired contents
--width N Width, in bits, of the memory
--output PATH Output chip configuration file

The genpat subcommand

This generates a pseudo-random hex file that can be used to locate the memory contents in the chip configuration files.

Argument Description
--width N Width, in bits, of the memory
--count M Number of words (addresses) of the memory
--seed S Optional, different seeds produce different files
--output PATH Output hex configuration file

The cpcfg subcommand

Argument Description
--input PATH Chip configuration file produced by NextPNR
--output PATH Output chip configuration file

This subcommand is only useful for testing the configuration parser. It reads the configuration file and writes it back. The two files should be equivalent. In the test bench, this is verified by running ecppack on the original and copied configurations and comparing the resulting SVF files.

Other commands and options

Some other commands used for debugging are help, identify, modify, brdump and brlayout. The update and identify commands can take extra options --layout-clear, --layout-enable NAME and --layout-disable NAME to select the set of layouts brup will try. Available layouts are 512x36, 1024x18, 2048x9, 4096x4, 8192x2 and 16384x1. By default brup will try the layouts in that order until it finds one that allows identification.

Tips

  1. If you are using make, ensure that the configuration file generated by NextPNR is marked precious, otherwise it will be deleted.
  2. If your memory foo has size p and width q, that is, it consists of p words tha are q bits wide, as in reg [q-1:0] foo[0:p-1] then the hex file should have p lines, each line i with 1 <= i <= p should contain the hexadecimal value for the word foo[i-1]

Compilation and installation

  1. Install cargo from https://www.rust-lang.org/
  2. From the brup source directory type cargo install --path .
  3. The ECP5 sysMEM blocks have a two cycle latency between address presentation and data output, and Yosys will resort to LUTs if your design uses the memory output before it's ready.

Testbench

Under testbench/ you will find a Zsh script run.sh that will synthesize a series of ECP5 designs having one memory with various widths N and sizes M using Yosys and NextPNR. It will then test the genpat, cpcfg and update command of brup on those. Provided yosys, nextpnr-ecp5 and ecppack are in your PATH and you have zsh installed you can call it as testbench/run.sh all

Note that for some small values of M and N Yosys will not use block RAMs (i.e. DP16K sysMEM primitives) in spite of the nomem2reg attribute, and the testbench script will output "No BRAM synthesized".

Author and links

Author: Berke Durak bd@exhrd.fr

Keywords

  • FPGA, bitstream, configuration, Lattice, ECP5, block ram, Yosys, NextPNR, DP16KD

Revision history

Date Version Description
2025-09-23 0.1.0 Initial release
2025-09-24 0.1.1 Fix bug affecting configs with multiple memory layouts