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 belowCommon directory
common/
├── common.mk # Base Makefile that other projects inherit
├── tb_pkg.sv # re-usuable verification componentscommon.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.mkHere 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.mkDaily 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:~$ make test
Then inspect build errors, fix them and re-run if compiled you can then inspect with GTKWave
frap@dev:~$ make wave
Evolution
If something ever changes, this will act as the changelog/version section.