Skip to content

stnolting/neorv32-verilog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NEORV32 in Verilog

neorv32-verilog License

  1. Prerequisites
  2. Configuration
  3. Conversion
  4. Simulation
  5. Evaluation

This repository shows how to convert a complex VHDL design into a synthesizable plain Verilog netlist module using GHDL's synthesis feature. The example in this repository is based on the NEORV32 RISC-V Processor, which is written in platform-independent VHDL. The resulting Verilog module can be instantiated within an all-Verilog design and can be successfully simulated and synthesized - tested with Xilinx Vivado and Intel Quartus (see section Evaluation).

Detailed information regarding GHDL's synthesis feature can be found in the GHDL synthesis documentation.

Note

The verification workflow converts a pre-configured setup of the latest NEORV32 version into a Verilog netlist and tests the result by running an Icarus Verilog simulation. The generated Verilog code for the default processor configuration can be downloaded as CI Workflow artifact.

Prerequisites

  1. Clone this repository recursively to include the NEORV32 submodule.

  2. Install GHDL. On a Linux machine GHDL can be installed easily via the package manager. :warning: Make sure to install a version with --synth option enabled (should be enabled by default). GHDL version 3.0.0 or higher is required.

$ sudo apt-get install ghdl
  1. Test the GHDL installation and check the version.
$ ghdl -v
GHDL 3.0.0-dev (v2.0.0-652-g6961b3f82) [Dunoon edition]
 Compiled with GNAT Version: 9.4.0
 mcode code generator
Written by Tristan Gingold.

Copyright (C) 2003 - 2022 Tristan Gingold.
GHDL is free software, covered by the GNU General Public License.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[back to top]

Configuration

GHDL's synth option is used to convert the whole NEORV32 processor - including all peripherals, memories and memory images - into a single Verilog netlist module file.

Warning

The output of the GHDL synthesis is a post-elaboration result. Therefore, all the processor's configuration options (i.e. VHDL generics) are resolved before the actual output is generated (see the GHDL internals documentation).

To ease configuration and customization a minimal VHDL wrapper src/neorv32_verilog_wrapper.vhd is provided. This wrapper can be used to configure the processor setup according to the requirements. The default wrapper from this repository only implements a minimal subset of the available configuration options and interfaces - just enough to run the processor's built-in bootloader.

Have a look at the original processor top entity (neorv32_top.vhd) and just copy the generics and ports that you would like to use for the Verilog setup. Note that all NEORV32 interface inputs and configuration generics do provide default values.

[back to top]

Conversion

The actual conversion is conducted by a conversion shell script, which analyzes all the processor's sources and finally calls GHDL synth to create the final Verilog netlist neorv32-verilog/src/neorv32_verilog_wrapper.v.

neorv32-verilog/src$ sh convert.sh

After generating the netlist the interface of the resulting neorv32_verilog_wrapper Verilog module is shown in the console. This can be used as instantiation template.

------ neorv32_verilog_wrapper interface ------
module neorv32_verilog_wrapper
  (input  clk_i,
   input  rstn_i,
   input  uart0_rxd_i,
   output uart0_txd_o);
-----------------------------------------------

Notes

  • GHDL synthesis generates an unoptimized plain Verilog netlist without any (technology-specific) primitives. However, optimizations will be performed by the synthesis tool (e.g. mapping to FPGA primitives like block RAMs).
  • The interface of the resulting NEORV32 Verilog module lists all inputs first followed by all outputs.
  • The original NEORV32 module hierarchy is preserved as well as most (all?) signal names, which allows easy inspection and debugging of simulation waveforms and synthesis results.
  • Custom VHDL interface types and records are collapsed into linear arrays.

[back to top]

Simulation

This repository provides a simple Verilog testbench that can be used to simulate the default NEORV32 configuration. The testbench includes a UART receiver, which is driven by the processor UART0. It outputs received characters to the simulator console.

A pre-configured simulation script based on Icarus Verilog can be used to simulate the Verilog setup (takes several minutes to complete):

neorv32-verilog/sim$ sh iverilog_sim.sh
neorv32-verilog verification testbench




<< NEORV32
Simulation successful!
./testbench.v:79: $finish called at 95372250 (100ps)

The simulation is terminated automatically as soon as the string "NEORV32" has been received from the processor's bootloader. In this case Simulation successful! is printed to the console. If Simulation terminated! appears in the simulator console the simulation has failed.

Check_iverilog
Prebuilt Icarus Verilog binaries for Linux can be downloaded from stnolting/icarus-verilog-prebuilt.

[back to top]

Evaluation

It's time for a "quality" evaluation of the auto-generated Verilog. Therefore, two projects were created: a pure Verilog one using the auto-generated src/neorv32_verilog_wrapper.v file and a pure VHDL one using the provided src/neorv32_verilog_wrapper.vhd file. For both projects a simple top entity was created (again, a Verilog and a VHDL version) that instantiate the according neorv32_verilog_wrapper module together with a PLL for providing clock (100MHz) and reset.

The default configuration of the wrapper was used:

  • Memories: 16kB IMEM (RAM), 8kB DMEM (RAM), 4kB internal bootloader ROM
  • CPU: rv32imc_Zicsr_Zicntr_Zifencei
  • Peripherals: UART0, GPIO, MTIME

Both setups were synthesized for an Intel Cyclone IV E FPGA (EP4CE22F17C6) using Intel Quartus Prime 21.1.0 with default settings ("balanced" implementation). The timing analyzer's "Slow 1200mV 0C Model" was used to evaluate the maximum operating frequency f_max. Additionally, both setups were (successfully! 🎉) tested on a Terasic DE0-nano FPGA board.

NEORV32 v1.7.6.0 All-Verilog All-VHDL
Total logic elements 3697 3287
Total registers 1436 1450
Total pins 4 4
Total memory bits 230400 230400
Embedded multiplier 0 0
f_max [MHz] 115.3 122.2
Operational yes yes

Not bad at all! Maybe the Verilog implementation result could be further improved by turning on more advanced synthesis/optimization options. Also, the coding style of the VHDL code base might not be optimal resulting in a less-good netlist.

[back to top]