Getting started¶
Installation¶
Install required system packages:
Debian:
apt install -y git g++ make python3 python3-pip antlr4 libantlr4-runtime-dev yosys npm
Arch:
pacman -Syu git gcc make python3 python-pip antlr4 antlr4-runtime yosys npm
Fedora:
dnf install git g++ make python3 python3-pip python3-devel antlr4 antlr4-cpp-runtime-devel yosys npm
Install the Topwrap package (It is highly recommended to run this step in a Python virtual environment, e.g. venv):
pip install .
Note
To use topwrap parse
command you also need to install optional dependencies:
pip install ".[topwrap-parse]"
On Arch-based distributions a symlink to antlr4 runtime library needs to created and an environment variable set:
ln -s /usr/share/java/antlr-complete.jar antlr4-complete.jar
ANTLR_COMPLETE_PATH=`pwd` pip install ".[topwrap-parse]"
On Fedora-based distributions symlinks need to be made inside /usr/share/java
directory itself:
sudo ln -s /usr/share/java/stringtemplate4/ST4.jar /usr/share/java/stringtemplate4.jar
sudo ln -s /usr/share/java/antlr4/antlr4.jar /usr/share/java/antlr4.jar
sudo ln -s /usr/share/java/antlr4/antlr4-runtime.jar /usr/share/java/antlr4-runtime.jar
sudo ln -s /usr/share/java/treelayout/org.abego.treelayout.core.jar /usr/share/java/treelayout.jar
pip install ".[topwrap-parse]"
Example usage¶
This section demonstrates the basic usage of Topwrap to generate IP wrappers and a top module.
Create IP core description file for every IP Core you want to use or let topwrap parse handle this for you. This file describes the ports, parameters and interfaces of an IP core.
As an example, Verilog module such as:
module ibuf (
input wire clk,
input wire rst,
input wire a,
output reg z
);
// ...
endmodule
Needs this corresponding description:
signals:
in:
- clk
- rst
- a
out:
- z
Create a design description file where you can specify all instances of IP cores and connections between them (
project.yaml
in this example)
Create instances of IP cores:
ips:
vexriscv_instance:
file: ipcores/gen_VexRiscv.yaml
wb_ram_data_instance:
file: ipcores/gen_mem.yaml
wb_ram_instr_instance:
file: ipcores/gen_mem.yaml
file
and module
are mandatory fields providing the IP description file and the name of the HDL module as it appears in the source.
(Optional) Set parameters for IP core instances:
parameters:
wb_ram_data_instance:
depth: 0x1000
memfile: '"top_sram.init"'
wb_ram_instr_instance:
depth: 0xA000
memfile: '"bios.init"'
Note that string parameters have to be wrapped in single quotation marks like this: '"string value"'
.
Connect desired ports of the IP cores:
ports:
wb_ram_data_instance:
clk: [some_other_ip, clk_out]
rst: [reset_core, reset0]
wb_ram_instr_instance:
clk: [some_other_ip, clk_out]
rst: [reset_core, reset0]
vexriscv_instance:
softwareInterrupt: [some_other_ip, sw_interrupt]
...
Connections only need to be written once, i.e. if A connects to B, then only a connection A: B has to be specified (B: A is redundant).
Connect desired interfaces of the IP cores:
interfaces:
vexriscv_instance:
iBusWishbone: [wb_ram_instr_instance, mem_bus]
dBusWishbone: [wb_ram_data_instance, mem_bus]
Specify external ports or interfaces of the top module and connect them with chosen IP cores’ ports or interfaces:
ports:
vexriscv_instance:
timerInterrupt: ext_timer_interrupt
...
external:
ports:
out:
- ext_timer_interrupt
interfaces:
...
Create a Core file template for FuseSoC, or use a default one bundled with Topwrap.
You may want to modify the file to add dependencies, source files, or change the target board.
The file should be named core.yaml.j2
. You can find an example template in examples/hdmi
directory of the project.
If you don’t create any template a default template bundled with Topwrap will be used (stored in templates
directory).
Place any additional source files in a directory (
sources
in this example).Run Topwrap:
python -m topwrap build --design project.yaml --sources sources
Example PWM design¶
There’s an example setup in examples/pwm
.
In order to generate the top module, run:
make generate
In order to generate bitstream, add Vivado to your path and run:
make
The FPGA design utilizes an AXI-mapped PWM IP Core.
You can access its registers starting from address 0x4000000
(that’s the base address of AXI_GP0
on ZYNQ).
Each IP Core used in the project is declared in ips
section in project.yml
file.
ports
section allows to connect individual ports of IP Cores, and interfaces
is used analogously for connecting whole interfaces.
Finally, you can specify which ports will be used as external I/O in external
section.
To connect the I/O signals to specific FPGA pins, you need proper mappings in a constraints file. See zynq.xdc
used in the setup and modify it accordingly.
Example HDMI design¶
There’s an example setup stored in examples/hdmi
.
You can generate bitstream for desired target:
Snickerdoodle Black:
make snickerdoodle
Zynq Video Board:
make zvb
If you wish to generate HDL sources without running Vivado, you can use:
make generate
You can find more information in README of the example setup.