Background

Open-source Hardware Design tooling has gone a long way, not that I have used it before, but these tools that will come in next sections was surprisingly easy to get, configure and use. It was as easy to get a Hardware compilation and simulation up an running as any other software project.

Tools

  • Linter, Compiler and simulator used is Verilator a open-source
  • To inspect the simulation step by step GTKWave is used, also open-source.
  • Make (of course) to automate’ish the build system.

Current Setup

Chosen workflow already includes simple methods of re-usuability and dependencies from day one.

Currently chosen structure of all blocks, IPs and SoC designs are as monorepo here is an example of naming convention and structure used at the top level git root. You can check out an example project at this GitHub repo.

GitTopRoot/
├── common/       # the re-usuable common dir from above
├── xxx-copy/     # Empty dir structure consiting of all dirs and base files needed for a new project
├── 000-counter/  # simple counter project, this project used as an example below

Common directory

common/
├── common.mk       # Base Makefile that other projects inherit
├── tb_pkg.sv       # re-usuable verification components

common.mk

GIT_ROOT := $(shell git rev-parse --show-toplevel 2>/dev/null)

COMMON_DIR := ../common
RTL_DIR   := rtl
TB_DIR    := tb
SIM_DIR   := sim
WAVE_DIR  := waves

ifndef TOP
  $(error TOP is not defined. Define TOP in your projects Makefile)
endif
TB        := $(TOP)_tb

# Default to empty if project Makefile doesn't define extra sources
EXTRA_SRCS ?=

COMMON_SRCS := \
    $(COMMON_DIR)/tb_pkg.sv

# All commons to include
COMMON_SRCS := \
    $(COMMON_DIR)/tb_pkg.sv

.PHONY: help all build run wave test lint clean

help:
  @echo "SystemVerilog project"
  @echo ""
  @echo "Targets:"
  @echo "  build    Compile RTL and testbench"
  @echo "  run      Run simulation"
  @echo "  wave     Open GTKWave"
  @echo "  test     Build + run simulation"
  @echo "  lint     Run Verilator lint"
  @echo "  clean    Remove generated files"

all: test

build:
  @echo "Compiling..."
  @mkdir -p $(SIM_DIR)

  verilator \
    --binary \
    --trace \
    --sv \
    --top-module $(TB) \
    -j 0 \
    -Wall -Wno-UNUSEDSIGNAL \
    $(COMMON_SRCS) \
    $(EXTRA_SRCS) \
    $(RTL_DIR)/$(TOP).sv \
    $(TB_DIR)/$(TB).sv \
    -Mdir $(SIM_DIR)

run:
  @echo "Running simulation..."
  ./$(SIM_DIR)/V$(TB)

test: build run

wave:
  @echo "Opening GTKWave..."
  gtkwave $(WAVE_DIR)/$(TOP).vcd

lint:
  @echo "Running Verilator lint..."
  verilator \
    --lint-only \
    --sv \
    -Wall \
    $(RTL_DIR)/$(TOP).sv
clean:
  @echo "Cleaning build artifacts..."
  rm -rf $(SIM_DIR)
  rm -f $(WAVE_DIR)/*.vcd
  rm -f $(WAVE_DIR)/*.fst

Project / directory structure

Each project consists of these directories

000-counter/
├── rtl/       # Design: SystemVerilog design files
├── tb/        # Verification: SystemVerilog TestBench (tb) files which initiated the DUT
├── sim/       # simulation artifacts eg cpp builds
├── waves/     # simulation dumps as waves for GTKWave to open and inspect
├── docs/      # Documention and intial specs
├── Makefile   # Thing Makefile which inhertis from the common directory
└── README.md  # Short description with commands how to lint, build compile and simulate.

At each project level the Makefile becomes thin and focused, it is used to define the TOP level component for TB and compilation:

TOP       := counter

include ../common/common.mk

Here is an example of Ripple Carry Adder project which depends on halfadder and a fulladder blocks.

TOP       := ripple_carry_adder

EXTRA_SRCS := ../001_half_adder/rtl/half_adder.sv ../002_full_adder/rtl/full_adder.sv

include ../common/common.mk

Daily workflow

Start

New project?

Then copy the xxx-copy and change `counter` to your new project name in Makefile, rtl/ and tb/ files and inside files.

Then follow command section

Existing

Open up and edit :)

Command

To compile and then directly run a simulation do the short hand

frap@dev:~
frap@dev:~$ make test

Then inspect build errors, fix them and re-run if compiled you can then inspect with GTKWave

frap@dev:~
frap@dev:~$ make wave

Evolution

If something ever changes, this will act as the changelog/version section.

References