Skip to content

Getting Started

Overview

Regardless of if you use it in a traditional Verilog-based workflow or natively in an Amaranth design, using Manta consists of the following steps:

  • Specify and configure the cores you wish to include in your FPGA design.
  • Generate RTL for these cores, and include this RTL in your design.
  • Build the design, and upload it to your device.
  • Operate the cores from the host machine.

Manta’s cores are small, configurable blocks that each provide some functionality. More information on each core can be found on their respective documentation pages. Manta supports an arbitrary amount of unique cores, limited only by the available resources of your device.

Usage in Traditional Verilog-Based Workflows

Although modern HDLs are rising in popularity, most existing FPGA designs use a Verilog-based workflow. This consists of synthesizing a number of Verilog files into a single design. Manta can easily be used in such a workflow, as described below:

  • The cores and interface used to communicate with them is specified in a configuration file, written in YAML. This file is typically named manta.yaml .
  • A Verilog file containing the cores specified in the configuration file is generated. This is most often done at the command line with the manta gen command (for example, manta gen manta.yaml manta.v) but can also be done with the generate_verilogmethod of the Manta Python class. This file contains the definition of a Verilog module named manta, which includes all the cores specified in the configuration file.
  • This manta module is instantiated in your design, and the signals to each core are connected to your logic. Connections are also made to the signals of the interface specified in the configuration file. The manta inst command can also be used to generate a Verilog instantiation that can be copy-pasted into your source.
  • After the design is built and uploaded to the FPGA, the cores are operated from the host machine. Each core exposes a Python API containing methods that can be invoked from a Python script. These are described in great detail in the documentation for each core.

VHDL works too!

If your FPGA design is VHDL-based, fret not! Most synthesis tools support mixed-language projects, and will happily ingest both a Verilog-based Manta module inside of a VHDL-based design. Just take care to ensure that interfaces match between the VHDL and Verilog modules.

Example

A minimal example of a manta.yaml file may be observed below:

cores:
  my_io_core:
    type: io

    inputs:
      my_input_signal: 6

    outputs:
      my_output_signal: 12

uart:
  port: "auto"
  baudrate: 3e6
  clock_freq: 100e6

This includes a single IO core in the manta module, which communicates with the host machine over a 3Mbaud UART link. Instantiating this core in your design might look like the following, as generated by manta inst:

manta manta_inst (
    .clk(clk),
    .rst(rst),
    .rx(rx),
    .tx(tx),
    .my_input_signal(my_input_signal),
    .my_output_signal(my_output_signal));

Reset is active high!

The Manta instance will reset while rst is held high. If you want to share reset logic with an active low reset signal (for example, rst_n), be sure to invert it first.

More examples of Verilog-based designs can be found in the examples/verilog folder of the repo.

Usage in Amaranth Designs

Since Manta itself is written in Amaranth, it’s very easy to use Manta in an Amaranth design. In this flow, the RTL build and generation are offloaded to Amaranth’s build system, such that you need only to configure and operate the core, which is done from Python.

Configuration is done by creating a Manta object in Python, adding cores to it, and adding it to your design as an Amaranth submodule:

from amaranth import *
from manta import *

class ExampleDesign(Elaborateable):
    def __init__(self):
        self.manta = Manta()
        self.manta.interface = UARTInterface("auto", 2e6, 12e6)
        self.manta.cores.my_io_core = IOCore(inputs=[], outputs=[])


    def elaborate(self, platform):
        m = Module()
        m.submodules.manta = self.manta

        return m

    def operate(self):
        self.manta.cores.my_io_core.set_probe("foo", 2)
        self.manta.cores.my_io_core.get_probe(bar)

Using this ExampleDesign in the configure-build-operate flow might look like the following:

>>> from amaranth_boards.icestick import ICEStickPlatform
>>> design = ExampleDesign()
>>> ICEStickPlatform.build(design, do_program=True)
>>> design.operate()
>>> design.manta.my_io_core.get_probe(bar)

Here, Amaranth’s board definitions and build system are being used to build and program an iCEstick development board. More examples of Amaranth-based designs can be found in the examples/amaranth folder of the repo.

Usage with older versions of Amaranth

Unfortunately, Manta has a hard dependency on Amaranth 0.5 (due to this bugfix), and it may not work correctly in projects built upon older versions of Amaranth. If this is the case for your project, you may need to generate a standalone Verilog module from the Verilog-based flow, and then include in your project as an Instance. Alternatively, you may upgrade your project’s version of Amaranth by following the migration guides.

Adding Manta as an Instance Variable

It’s worth noting that this usage represents a slight departure from typical Amaranth style. Typically, submodules would be defined and added to a Module in the elaborate method. Here, the Manta module is instead defined as an instance variable in the __init__ function, and then later added as a submodule in the elaborate method.

This is necessary as the Manta object contains both HDL needed for build and methods for operating the cores. Saving the Manta instance in the class and re-using it later removes the need to define and configure separate instances when elaborating and operating the cores.

Lastly, including manta as an instance variable also allows it to be directly accessed from an interpreter, as shown above. This allows for a more interactive debugging session, as the definition of the operate method doesn’t have to change when you wish to use Manta’s cores differently.