diff --git a/dev/404.html b/dev/404.html index fee6395..24e3495 100644 --- a/dev/404.html +++ b/dev/404.html @@ -46,6 +46,8 @@ + + @@ -136,7 +138,7 @@ - + @@ -146,7 +148,7 @@ - + @@ -156,7 +158,7 @@ - + @@ -168,43 +170,6 @@ - - - - -
For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side.
sockets
For applications where UART is too slow or isn't available, Manta provides the option to run over Ethernet. This is done via UDP, so the FPGA can be anywhere on the same network as the host machine - as opposed to MAC-based Ethernet interfaces, which usually require a point-to-point network connection between the FPGA and the host. Although UDP does not guaruntee reliable, in-order packet delivery, this generally tends to be the case on uncongested networks. In the future, Manta will enforce this at the application layer.
Not every device is supported!
Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help!
Internally, the Ethernet Interface uses LiteEth to generate cross-platform RTL for the FPGA. As a result, the supported devices are loosely restricted to those supported by LiteEth. If a device you'd like to use isn't on the list, the community would love your help.
Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue.
Both the Use Cases page and the repository's examples folder contain examples of the Ethernet Interface for your reference.
The configuration of the Ethernet core is best shown by example: -
ethernet: +Verilog-Based Workflows +The UART interface is used by adding a ethernet entry at the bottom of the configuration file. This is best shown by example: +ethernet: phy: LiteEthPHYRMII vendor: xilinx toolchain: vivado @@ -757,35 +835,172 @@ fpga_ip_addr: "192.168.0.110" host_ip_addr: "192.168.0.100" -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. +phy (required): The name of the LiteEth PHY class to use. Select the appropriate one from this list for your FPGA vendor and family. -vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. +vendor (required): The vendor of your FPGA. Currently only values of xilinx and lattice are supported. This is used to generate (currently unused) timing constraints files. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. -clk_freq (required): The frequency of the clock provided to the Manta instance. +clk_freq (required): The frequency of the clock provided to the Manta module on the FPGA, in Hertz (Hz). -refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. +refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY, in Hertz (Hz). This frequency must match the MII variant used by the PHY, and speed it is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. -fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. +fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. Ping this address after power-on to check if this request was successful, or check your router for a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. + +udp_port (optional): The UDP port to communicate over. Defaults to 2001. + Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the EthernetInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.EthernetInterface + + + +EthernetInterface(phy, clk_freq, fpga_ip_addr, host_ip_addr, udp_port=2001, **kwargs) + + + + + + A synthesizable module for Ethernet (UDP) communication between a host +machine and the FPGA. + + This function is the main mechanism for configuring an Ethernet +Interface in an Amaranth-native design. + + +Parameters: + + + phy + (str) + – + + The name of the LiteEth PHY class to use. Select the +appropriate one from this list +for your FPGA vendor and family. + + + + clk_freq + (int | float) + – + + The frequency of the clock provided to the +Manta module on the FPGA, in Hertz (Hz). + + + + fpga_ip_addr + (str) + – + + The IP address the FPGA will attempt to claim. +Upon power-on, the FPGA will issue a DHCP request for this IP +address. Ping this address after power-on to check if this +request was successful, or check your router for a list of +connected devices. + + + + host_ip_addr + (str) + – + + The IP address of the host machine, which the +FPGA will send packets back to. + + + + udp_port + (Optional[int], default: + 2001 +) + – + + The UDP port to communicate over. + + + + **kwargs + – + + Any additional keyword arguments to this function will +be passed to the LiteEth RTL generator. Some examples are +provided below: + + +mac_address (int): A 48-bit integer representing the MAC + address the FPGA will assume. If not provided, an address + within the Locally Administered, Administratively Assigned group + will be randomly generated. + + +vendor (str): The vendor of your FPGA. Currently only values + of xilinx and lattice are supported. This is used to + generate (currently unused) timing constraints files. + + +toolchain (str): The toolchain being used. Currently only + values of vivado and diamond are supported. + + +refclk_freq (int | float): The frequency of the reference + clock to be provided to the Ethernet PHY, in Hertz (Hz). + This frequency must match the MII variant used by the PHY, + and speed it is being operated at. For instance, a RGMII + PHY may be operated at either 125MHz in Gigabit mode, or + 25MHz in 100Mbps mode. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/getting_started/index.html b/dev/getting_started/index.html index 93dbd13..4eda5b0 100644 --- a/dev/getting_started/index.html +++ b/dev/getting_started/index.html @@ -18,7 +18,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -374,21 +339,51 @@ - + - Example Configuration + Usage in Traditional Verilog-Based Workflows + + + + + + + + + + Example + + + + + - + - Example Instantiation + Usage in Amaranth Designs + + + + + + + Adding Manta as an Instance Variable + + + + + + + + @@ -406,11 +401,11 @@ - + - Alternatives + Use Cases @@ -445,6 +440,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -460,24 +475,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -544,6 +559,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -614,10 +678,10 @@ - + - + @@ -628,8 +692,8 @@ - - + + For Developers @@ -641,27 +705,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -718,21 +761,51 @@ - + - Example Configuration + Usage in Traditional Verilog-Based Workflows + + + + + + + + + + Example + + + + + - + - Example Instantiation + Usage in Amaranth Designs + + + + + + + Adding Manta as an Instance Variable + + + + + + + + @@ -756,78 +829,96 @@ Getting Started Overview -To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: +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 a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. -Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. -Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. -Build and upload the design to your FPGA using your preferred toolchain. -Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. -Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. +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. -Example Configuration -An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---- -cores: - my_io_core: - type: io - - inputs: - probe_0_in: 6 - probe_1_in: 12 - - outputs: - probe_2_out: 20 - probe_3_out: 1 - - my_logic_analyzer: - type: logic_analyzer - sample_depth: 4096 - trigger_location: 1000 - - probes: - larry: 1 - curly: 3 - moe: 9 - - triggers: - - moe RISING - - curly FALLING - -uart: - port: "auto" - baudrate: 3000000 - clock_freq: 100000000 - -Although it's just an example, this config file shows the two things every Manta configuration needs, namely: +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: - -Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. - - -Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. - +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. -This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core. -Example Instantiation -Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: - -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. + +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), - .probe_0_in(probe_0_in), - .probe_1_in(probe_1_in), - .probe_2_out(probe_2_out), - .probe_3_out(probe_3_out), - .larry(larry), - .curly(curly), - .moe(moe)); + .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. diff --git a/dev/index.html b/dev/index.html index 1e891d1..f745ea1 100644 --- a/dev/index.html +++ b/dev/index.html @@ -50,6 +50,8 @@ + + @@ -145,7 +147,7 @@ - + @@ -155,7 +157,7 @@ - + @@ -165,7 +167,7 @@ - + @@ -177,43 +179,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -329,39 +294,6 @@ - - - - - - - Logic Analyzer Core - - - - - - - - - I/O Core - - - - - - - - - Memory Cores - - - - - - - - @@ -428,11 +360,11 @@ - + - Alternatives + Use Cases @@ -467,6 +399,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -482,24 +434,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -566,6 +518,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -636,10 +637,10 @@ - + - + @@ -650,8 +651,8 @@ - - + + For Developers @@ -663,27 +664,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -737,39 +717,6 @@ - - - - - - - Logic Analyzer Core - - - - - - - - - I/O Core - - - - - - - - - Memory Cores - - - - - - - - @@ -803,48 +750,12 @@ Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping -Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. -Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. -Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. -Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: -Logic Analyzer Core -More details available on the full documentation page. -This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. -You may find this core useful for: - - -Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. - - -Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. - - -I/O Core -More details available on the full documentation page. -This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. -You may find this core useful for: - - -Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. - - -Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. - - -Memory Cores -More details available on the full documentation page. -This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. -You may find this core useful for: - - -Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. - - -Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. - - +Manta is a tool for rapidly prototyping and debugging FPGA designs. It works by providing a Manta module which is included in a FPGA design, which itself contains a number of cores - small, configurable debugging blocks that each provide some functionality. These cores are then connected to your design, and allow you to interface with it from a connected host machine. +These cores include functionality such as register reads/writes, memory accesses, and an embedded logic analyzer. Manta includes both a UART and Ethernet (via UDP) interface for communication between the host and FPGA. +For more information on how Manta can be used, please refer to the Use Cases page and the repository's examples folder. +Manta specifies its RTL logic with Amaranth which allows it to target nearly any FPGA device, regardless of vendor. Manta itself is written in pure Python, which allows it to run on Windows, macOS, Linux, and BSD across a variety of CPU architectures. Manta can be included natively in Amaranth-based designs, or export Verilog-2001 for use in traditional Verilog-based workflows. About -Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: +Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The following Bibtex is available if you wish to cite it: @misc{manta2023, author={Fischer Moseley}, title={Manta: An In-Situ Debugging Tool for Programmable Hardware}, diff --git a/dev/installation/index.html b/dev/installation/index.html index 754b251..75280a5 100644 --- a/dev/installation/index.html +++ b/dev/installation/index.html @@ -52,6 +52,8 @@ + + @@ -80,7 +82,7 @@ - + Skip to content @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -345,9 +310,18 @@ - + - Latest Version + Latest Release (Recommended) + + + + + + + + + Development Snapshot @@ -363,18 +337,9 @@ - + - Adding Manta to Path (Recommended) - - - - - - - - - Dependencies + Adding Manta to PATH (Optional) @@ -415,11 +380,11 @@ - + - Alternatives + Use Cases @@ -454,6 +419,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -469,24 +454,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -553,6 +538,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -623,10 +657,10 @@ - + - + @@ -637,8 +671,8 @@ - - + + For Developers @@ -650,27 +684,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -718,9 +731,18 @@ - + - Latest Version + Latest Release (Recommended) + + + + + + + + + Development Snapshot @@ -736,18 +758,9 @@ - + - Adding Manta to Path (Recommended) - - - - - - - - - Dependencies + Adding Manta to PATH (Optional) @@ -776,39 +789,38 @@ Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip -Latest Version -You can install the latest version of Manta directly from source with: -pip install --upgrade git+https://github.com/fischermoseley/manta.git +Latest Release (Recommended) +The latest release of Manta can be installed from PyPI with: +pip install --upgrade manta-python + +Development Snapshot +The latest development snapshot of Manta can be installed with: +pip install --upgrade git+https://github.com/fischermoseley/manta.git Editable Development Install If you're working on the source, you might want an editable installation with some extra dependencies used for development: -git clone https://github.com/fischermoseley/manta.git -cd manta -pip install -e ".[dev]" +git clone https://github.com/fischermoseley/manta.git +cd manta +pip install -e ".[dev]" -Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: -ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" +Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. +If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: +ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" -Be sure to reload your udev rules after saving the file. -Adding Manta to Path (Recommended) -It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. -Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet. -Dependencies -Manta requires the following dependencies: +Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with: +udevadm control --reload-rules && udevadm trigger + +Adding Manta to PATH (Optional) +Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example: -Amaranth HDL, which comes with it's own built-in copy of Yosys. -LiteEth, for sending and receiving UDP packets on the FPGA. -pySerial, for communicating with the FPGA over UART. -pyYAML, for parsing configuration files written in YAML. -pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. - -As well as these dependencies for development, which are installed with the [dev] argument: - -Pytest, for unit testing. -Black, for formatting the Python source. -mkdocs-material, for generating the documentation site. -amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. + +Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment. + + +macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment. + +This also adds any other Python scripts exposed by your installed packages to your PATH. diff --git a/dev/io_core/index.html b/dev/io_core/index.html index be1af65..95c95b4 100644 --- a/dev/io_core/index.html +++ b/dev/io_core/index.html @@ -15,7 +15,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -482,22 +467,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -509,12 +494,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -568,6 +601,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +720,10 @@ - + - + @@ -652,8 +734,8 @@ - - + + For Developers @@ -665,27 +747,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -748,22 +809,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -775,12 +836,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -804,82 +913,267 @@ IO Core Overview -Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference. + +It's not instantaneous! +If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful. + Configuration -Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3 -Inside this configuration, the following parameters may be configured: +Inside this configuration, the following parameters may be set: -name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name (required): The name of the IO core, which is used when working with the API. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. -inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. -outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. -initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. +inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an input. +outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an output. +initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and defaults to zero. -user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. + + Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. -Python API -The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: - -Manta.IOCore.set_probe(name, data) - -[string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. -[int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. -returns: None - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. - -Manta.IOCore.get_probe(name) - -[string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. -returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received. - -Example -A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. ->>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
The UART interface is used by adding a ethernet entry at the bottom of the configuration file. This is best shown by example:
ethernet
ethernet: phy: LiteEthPHYRMII vendor: xilinx toolchain: vivado @@ -757,35 +835,172 @@ fpga_ip_addr: "192.168.0.110" host_ip_addr: "192.168.0.100"
phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family.
phy
phy (required): The name of the LiteEth PHY class to use. Select the appropriate one from this list for your FPGA vendor and family.
vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused.
vendor
xilinx
lattice
vendor (required): The vendor of your FPGA. Currently only values of xilinx and lattice are supported. This is used to generate (currently unused) timing constraints files.
toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported.
toolchain
vivado
diamond
clk_freq (required): The frequency of the clock provided to the Manta instance.
clk_freq
clk_freq (required): The frequency of the clock provided to the Manta module on the FPGA, in Hertz (Hz).
refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode.
refclk_freq
refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY, in Hertz (Hz). This frequency must match the MII variant used by the PHY, and speed it is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode.
fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices.
fpga_ip_addr
fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. Ping this address after power-on to check if this request was successful, or check your router for a list of connected devices.
host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to.
host_ip_addr
udp_port (optional): The UDP port to communicate over. Defaults to 2001.
udp_port
Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA!
LiteEth doesn't always generate its own refclk!
refclk
Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance.
Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the EthernetInterface class constructor below, as well as the Amaranth examples in the repo.
EthernetInterface
EthernetInterface(phy, clk_freq, fpga_ip_addr, host_ip_addr, udp_port=2001, **kwargs) +
A synthesizable module for Ethernet (UDP) communication between a host +machine and the FPGA.
This function is the main mechanism for configuring an Ethernet +Interface in an Amaranth-native design.
Parameters:
str
The name of the LiteEth PHY class to use. Select the +appropriate one from this list +for your FPGA vendor and family.
int | float
The frequency of the clock provided to the +Manta module on the FPGA, in Hertz (Hz).
The IP address the FPGA will attempt to claim. +Upon power-on, the FPGA will issue a DHCP request for this IP +address. Ping this address after power-on to check if this +request was successful, or check your router for a list of +connected devices.
The IP address of the host machine, which the +FPGA will send packets back to.
Optional[int]
2001
The UDP port to communicate over.
**kwargs
Any additional keyword arguments to this function will +be passed to the LiteEth RTL generator. Some examples are +provided below:
mac_address (int): A 48-bit integer representing the MAC + address the FPGA will assume. If not provided, an address + within the Locally Administered, Administratively Assigned group + will be randomly generated.
vendor (str): The vendor of your FPGA. Currently only values + of xilinx and lattice are supported. This is used to + generate (currently unused) timing constraints files.
toolchain (str): The toolchain being used. Currently only + values of vivado and diamond are supported.
refclk_freq (int | float): The frequency of the reference + clock to be provided to the Ethernet PHY, in Hertz (Hz). + This frequency must match the MII variant used by the PHY, + and speed it is being operated at. For instance, a RGMII + PHY may be operated at either 125MHz in Gigabit mode, or + 25MHz in 100Mbps mode.
To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then:
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:
manta.yaml
.yaml
.yml
.json
manta gen [config_file] [verilog_file]
manta.v
manta
manta inst [config_file]
An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file.
manta gen manta.yaml manta.v
--- -cores: - my_io_core: - type: io - - inputs: - probe_0_in: 6 - probe_1_in: 12 - - outputs: - probe_2_out: 20 - probe_3_out: 1 - - my_logic_analyzer: - type: logic_analyzer - sample_depth: 4096 - trigger_location: 1000 - - probes: - larry: 1 - curly: 3 - moe: 9 - - triggers: - - moe RISING - - curly FALLING - -uart: - port: "auto" - baudrate: 3000000 - clock_freq: 100000000 -
Although it's just an example, this config file shows the two things every Manta configuration needs, namely:
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.
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:
Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog.
Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file.
uart
manta gen
generate_verilog
Manta
manta inst
This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core.
Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above:
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.
rst
rst_n
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.
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), - .probe_0_in(probe_0_in), - .probe_1_in(probe_1_in), - .probe_2_out(probe_2_out), - .probe_3_out(probe_3_out), - .larry(larry), - .curly(curly), - .moe(moe)); + .my_input_signal(my_input_signal), + .my_output_signal(my_output_signal));
More examples of Verilog-based designs can be found in the examples/verilog folder of the repo.
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:
ExampleDesign
>>> 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.
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.
Module
elaborate
__init__
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.
operate
Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer.
Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API.
Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed.
Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:
More details available on the full documentation page.
This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities.
You may find this core useful for:
Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic.
Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you.
This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools.
Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog.
Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library.
This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA.
Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core.
Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream.
Manta is a tool for rapidly prototyping and debugging FPGA designs. It works by providing a Manta module which is included in a FPGA design, which itself contains a number of cores - small, configurable debugging blocks that each provide some functionality. These cores are then connected to your design, and allow you to interface with it from a connected host machine.
These cores include functionality such as register reads/writes, memory accesses, and an embedded logic analyzer. Manta includes both a UART and Ethernet (via UDP) interface for communication between the host and FPGA.
For more information on how Manta can be used, please refer to the Use Cases page and the repository's examples folder.
Manta specifies its RTL logic with Amaranth which allows it to target nearly any FPGA device, regardless of vendor. Manta itself is written in pure Python, which allows it to run on Windows, macOS, Linux, and BSD across a variety of CPU architectures. Manta can be included natively in Amaranth-based designs, or export Verilog-2001 for use in traditional Verilog-based workflows.
Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it:
Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The following Bibtex is available if you wish to cite it:
@misc{manta2023, author={Fischer Moseley}, title={Manta: An In-Situ Debugging Tool for Programmable Hardware}, diff --git a/dev/installation/index.html b/dev/installation/index.html index 754b251..75280a5 100644 --- a/dev/installation/index.html +++ b/dev/installation/index.html @@ -52,6 +52,8 @@ + + @@ -80,7 +82,7 @@ - + Skip to content @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -345,9 +310,18 @@ - + - Latest Version + Latest Release (Recommended) + + + + + + + + + Development Snapshot @@ -363,18 +337,9 @@ - + - Adding Manta to Path (Recommended) - - - - - - - - - Dependencies + Adding Manta to PATH (Optional) @@ -415,11 +380,11 @@ - + - Alternatives + Use Cases @@ -454,6 +419,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -469,24 +454,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -553,6 +538,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -623,10 +657,10 @@ - + - + @@ -637,8 +671,8 @@ - - + + For Developers @@ -650,27 +684,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -718,9 +731,18 @@ - + - Latest Version + Latest Release (Recommended) + + + + + + + + + Development Snapshot @@ -736,18 +758,9 @@ - + - Adding Manta to Path (Recommended) - - - - - - - - - Dependencies + Adding Manta to PATH (Optional) @@ -776,39 +789,38 @@ Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip -Latest Version -You can install the latest version of Manta directly from source with: -pip install --upgrade git+https://github.com/fischermoseley/manta.git +Latest Release (Recommended) +The latest release of Manta can be installed from PyPI with: +pip install --upgrade manta-python + +Development Snapshot +The latest development snapshot of Manta can be installed with: +pip install --upgrade git+https://github.com/fischermoseley/manta.git Editable Development Install If you're working on the source, you might want an editable installation with some extra dependencies used for development: -git clone https://github.com/fischermoseley/manta.git -cd manta -pip install -e ".[dev]" +git clone https://github.com/fischermoseley/manta.git +cd manta +pip install -e ".[dev]" -Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: -ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" +Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. +If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: +ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" -Be sure to reload your udev rules after saving the file. -Adding Manta to Path (Recommended) -It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. -Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet. -Dependencies -Manta requires the following dependencies: +Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with: +udevadm control --reload-rules && udevadm trigger + +Adding Manta to PATH (Optional) +Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example: -Amaranth HDL, which comes with it's own built-in copy of Yosys. -LiteEth, for sending and receiving UDP packets on the FPGA. -pySerial, for communicating with the FPGA over UART. -pyYAML, for parsing configuration files written in YAML. -pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. - -As well as these dependencies for development, which are installed with the [dev] argument: - -Pytest, for unit testing. -Black, for formatting the Python source. -mkdocs-material, for generating the documentation site. -amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. + +Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment. + + +macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment. + +This also adds any other Python scripts exposed by your installed packages to your PATH. diff --git a/dev/io_core/index.html b/dev/io_core/index.html index be1af65..95c95b4 100644 --- a/dev/io_core/index.html +++ b/dev/io_core/index.html @@ -15,7 +15,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -482,22 +467,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -509,12 +494,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -568,6 +601,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +720,10 @@ - + - + @@ -652,8 +734,8 @@ - - + + For Developers @@ -665,27 +747,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -748,22 +809,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -775,12 +836,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -804,82 +913,267 @@ IO Core Overview -Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference. + +It's not instantaneous! +If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful. + Configuration -Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3 -Inside this configuration, the following parameters may be configured: +Inside this configuration, the following parameters may be set: -name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name (required): The name of the IO core, which is used when working with the API. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. -inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. -outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. -initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. +inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an input. +outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an output. +initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and defaults to zero. -user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. + + Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. -Python API -The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: - -Manta.IOCore.set_probe(name, data) - -[string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. -[int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. -returns: None - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. - -Manta.IOCore.get_probe(name) - -[string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. -returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received. - -Example -A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. ->>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
Before installing, make sure to upgrade your pip to the latest version:
pip
pip install --upgrade pip
You can install the latest version of Manta directly from source with:
pip install --upgrade git+https://github.com/fischermoseley/manta.git +Latest Release (Recommended) +The latest release of Manta can be installed from PyPI with: +pip install --upgrade manta-python + +Development Snapshot +The latest development snapshot of Manta can be installed with: +pip install --upgrade git+https://github.com/fischermoseley/manta.git Editable Development Install If you're working on the source, you might want an editable installation with some extra dependencies used for development: -git clone https://github.com/fischermoseley/manta.git -cd manta -pip install -e ".[dev]" +git clone https://github.com/fischermoseley/manta.git +cd manta +pip install -e ".[dev]" -Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: -ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" +Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. +If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: +ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" -Be sure to reload your udev rules after saving the file. -Adding Manta to Path (Recommended) -It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. -Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet. -Dependencies -Manta requires the following dependencies: +Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with: +udevadm control --reload-rules && udevadm trigger + +Adding Manta to PATH (Optional) +Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example: -Amaranth HDL, which comes with it's own built-in copy of Yosys. -LiteEth, for sending and receiving UDP packets on the FPGA. -pySerial, for communicating with the FPGA over UART. -pyYAML, for parsing configuration files written in YAML. -pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. - -As well as these dependencies for development, which are installed with the [dev] argument: - -Pytest, for unit testing. -Black, for formatting the Python source. -mkdocs-material, for generating the documentation site. -amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. + +Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment. + + +macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment. + +This also adds any other Python scripts exposed by your installed packages to your PATH. diff --git a/dev/io_core/index.html b/dev/io_core/index.html index be1af65..95c95b4 100644 --- a/dev/io_core/index.html +++ b/dev/io_core/index.html @@ -15,7 +15,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -482,22 +467,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -509,12 +494,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -568,6 +601,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +720,10 @@ - + - + @@ -652,8 +734,8 @@ - - + + For Developers @@ -665,27 +747,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -748,22 +809,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -775,12 +836,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -804,82 +913,267 @@ IO Core Overview -Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference. + +It's not instantaneous! +If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful. + Configuration -Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3 -Inside this configuration, the following parameters may be configured: +Inside this configuration, the following parameters may be set: -name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name (required): The name of the IO core, which is used when working with the API. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. -inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. -outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. -initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. +inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an input. +outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an output. +initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and defaults to zero. -user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. + + Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. -Python API -The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: - -Manta.IOCore.set_probe(name, data) - -[string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. -[int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. -returns: None - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. - -Manta.IOCore.get_probe(name) - -[string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. -returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received. - -Example -A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. ->>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
The latest release of Manta can be installed from PyPI with:
pip install --upgrade manta-python +
The latest development snapshot of Manta can be installed with:
pip install --upgrade git+https://github.com/fischermoseley/manta.git
If you're working on the source, you might want an editable installation with some extra dependencies used for development:
git clone https://github.com/fischermoseley/manta.git -cd manta -pip install -e ".[dev]" +git clone https://github.com/fischermoseley/manta.git +cd manta +pip install -e ".[dev]" -Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: -ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" +Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. +If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: +ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" -Be sure to reload your udev rules after saving the file. -Adding Manta to Path (Recommended) -It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. -Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet. -Dependencies -Manta requires the following dependencies: +Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with: +udevadm control --reload-rules && udevadm trigger + +Adding Manta to PATH (Optional) +Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example: -Amaranth HDL, which comes with it's own built-in copy of Yosys. -LiteEth, for sending and receiving UDP packets on the FPGA. -pySerial, for communicating with the FPGA over UART. -pyYAML, for parsing configuration files written in YAML. -pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. - -As well as these dependencies for development, which are installed with the [dev] argument: - -Pytest, for unit testing. -Black, for formatting the Python source. -mkdocs-material, for generating the documentation site. -amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. + +Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment. + + +macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment. + +This also adds any other Python scripts exposed by your installed packages to your PATH. diff --git a/dev/io_core/index.html b/dev/io_core/index.html index be1af65..95c95b4 100644 --- a/dev/io_core/index.html +++ b/dev/io_core/index.html @@ -15,7 +15,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -482,22 +467,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -509,12 +494,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -568,6 +601,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +720,10 @@ - + - + @@ -652,8 +734,8 @@ - - + + For Developers @@ -665,27 +747,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -748,22 +809,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -775,12 +836,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -804,82 +913,267 @@ IO Core Overview -Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference. + +It's not instantaneous! +If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful. + Configuration -Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3 -Inside this configuration, the following parameters may be configured: +Inside this configuration, the following parameters may be set: -name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name (required): The name of the IO core, which is used when working with the API. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. -inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. -outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. -initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. +inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an input. +outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an output. +initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and defaults to zero. -user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. + + Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. -Python API -The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: - -Manta.IOCore.set_probe(name, data) - -[string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. -[int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. -returns: None - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. - -Manta.IOCore.get_probe(name) - -[string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. -returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received. - -Example -A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. ->>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
git clone https://github.com/fischermoseley/manta.git +cd manta +pip install -e ".[dev]"
Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains:
xc3sprog
iceprog
PATH
/etc/udev/rules.d/99-ftdi-devices.rules
ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" +Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. +If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: +ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" -Be sure to reload your udev rules after saving the file. -Adding Manta to Path (Recommended) -It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. -Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet. -Dependencies -Manta requires the following dependencies: +Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with: +udevadm control --reload-rules && udevadm trigger + +Adding Manta to PATH (Optional) +Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example: -Amaranth HDL, which comes with it's own built-in copy of Yosys. -LiteEth, for sending and receiving UDP packets on the FPGA. -pySerial, for communicating with the FPGA over UART. -pyYAML, for parsing configuration files written in YAML. -pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. - -As well as these dependencies for development, which are installed with the [dev] argument: - -Pytest, for unit testing. -Black, for formatting the Python source. -mkdocs-material, for generating the documentation site. -amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. + +Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment. + + +macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment. + +This also adds any other Python scripts exposed by your installed packages to your PATH. diff --git a/dev/io_core/index.html b/dev/io_core/index.html index be1af65..95c95b4 100644 --- a/dev/io_core/index.html +++ b/dev/io_core/index.html @@ -15,7 +15,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -482,22 +467,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -509,12 +494,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -568,6 +601,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +720,10 @@ - + - + @@ -652,8 +734,8 @@ - - + + For Developers @@ -665,27 +747,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -748,22 +809,22 @@ - - - - - - Python API - - - - + - + - Example + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs @@ -775,12 +836,60 @@ - + - Limitations + Operation + + + + + + Python API Documentation + + + + + + + + + + IOCore + + + + + + + + + + get_probe + + + + + + + + + set_probe + + + + + + + + + + + + + @@ -804,82 +913,267 @@ IO Core Overview -Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference. + +It's not instantaneous! +If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful. + Configuration -Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3 -Inside this configuration, the following parameters may be configured: +Inside this configuration, the following parameters may be set: -name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name (required): The name of the IO core, which is used when working with the API. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. -inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. -outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. -initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. +inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an input. +outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. This parameter is somewhat optional as an IO Core must have at least one probe, but it need not be an output. +initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and defaults to zero. -user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. + + Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. -Python API -The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: - -Manta.IOCore.set_probe(name, data) - -[string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. -[int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. -returns: None - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. - -Manta.IOCore.get_probe(name) - -[string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. -returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. - -This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received. - -Example -A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. ->>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH.
If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains:
ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666"
Be sure to reload your udev rules after saving the file.
It's recommended to place Manta on your system path by adding export PATH="~/.local/bin:$PATH" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different.
export PATH="~/.local/bin:$PATH"
.bashrc
.zshrc
python3 -m manta
Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet.
pip install mantaray
Manta requires the following dependencies:
Be sure to reload your udev rules after saving the file. On most distributions, this is accomplished with:
udevadm control --reload-rules && udevadm trigger +
Although optional, it is convenient to add the manta executable to your system's path. This allows you to invoke Manta's CLI with manta, rather than the more verbose python3 -m manta. The location of this executable depends on both your platform and if you're using a virtual environment. For example:
As well as these dependencies for development, which are installed with the [dev] argument:
[dev]
Windows: %APPDATA%\Python\Scripts, or path\to\venv\Scripts if using a virtual environment.
%APPDATA%\Python\Scripts
path\to\venv\Scripts
macOS/Linux/BSD: $HOME/.local/bin, or path\to\venv\bin if using a virtual environment.
$HOME/.local/bin
path\to\venv\bin
This also adds any other Python scripts exposed by your installed packages to your PATH.
Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs.
This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:
Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define inputs and outputs of arbitrary width, and then write values to the outputs and read values from the inputs.
This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the IO Core for your reference.
It's not instantaneous!
If you're trying to read or write values in your design with cycle-accurate timing, this will not do that for you. The time taken by Python to evaluate an expression is not constant, nor is the amount of time it takes for your OS to put bytes on the wire. If you need cycle-accurate timing, the Logic Analyzer Core and it's Playback feature may be helpful.
Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example:
As explained in the getting started page, the IO Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design.
The IO Core is used by adding an entry in a cores section of a configuration file. This is best shown by example:
cores
--- -my_io_core: - type: io - - inputs: - kermit: 3 - piggy: 1 - animal: 38 - scooter: - width: 4 - initial_value: 13 - - outputs: - fozzy: 1 - gonzo: 3 - - user_clock: True +cores: + my_io_core: + type: io + + inputs: + kermit: 3 + piggy: 1 + animal: 38 + scooter: + width: 4 + initial_value: 13 + + outputs: + fozzy: 1 + gonzo: 3
name
type
io
inputs
outputs
initial_value
initial
user_clock
clk
Name things carefully!
The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate.
The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions:
Manta.IOCore
Manta.IOCore.set_probe(name, data)
string
int
bool
This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent.
Manta.IOCore.get_probe(name)
This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received.
A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder.
>>> import Manta ->>> m = Manta ->>> m.my_io_core.set_probe("fozzy", True) ->>> m.my_io_core.get_probe("fozzy") -True ->>> m.my_io_core.set_probe("gonzo", 4) ->>> m.my_io_core.get_probe("scooter") -5 +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below. +These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.IOCore + + + +IOCore(inputs=[], outputs=[]) -Limitations -While the IO core performs a very, very simple task, it carries a few caveats. - - -First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. - - -Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. - - + + + + + A synthesizable module for setting and getting the values of registers of +arbitrary size. + + Create an IO Core, with the given input and output probes. +This function is the main mechanism for configuring an IO Core in an +Amaranth-native design. + + +Parameters: + + + inputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input. + + + + outputs + (Optional[List[Signal]], default: + [] +) + – + + A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output. + + + + + + + + + + + + + + + + + + + + + + get_probe + + + +get_probe(probe) + + + + + Get the value of an input or output probe on the FPGA. +If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs. + + + + + +Returns: + + +value ( int +) – + + The value of the probe, interpreted as an unsigned +integer. + + + + + +Raises: + + + ValueError + – + + The probe was not found in the IO Core, or many probes +were found with the same name. + + + + + + + + + + + + + set_probe + + + +set_probe(probe, value) + + + + + Set the value of an output probe on the FPGA. +This method is blocking. + + +Parameters: + + + probe + (str | Signal) + – + + The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs. + + + + value + (int) + – + + The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + ValueError + – + + The probe was not found to be an output of the IO Core, +or many probes were found with the same name. + + + + + + + + + + + + + + + diff --git a/dev/logic_analyzer_core/index.html b/dev/logic_analyzer_core/index.html index 4f69bc8..f4bc611 100644 --- a/dev/logic_analyzer_core/index.html +++ b/dev/logic_analyzer_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -507,18 +492,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -534,18 +510,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -590,12 +566,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -628,6 +736,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -698,10 +855,10 @@ - + - + @@ -712,8 +869,8 @@ - - + + For Developers @@ -725,27 +882,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -812,18 +948,9 @@ - + - Sample Depth - - - - - - - - - Probes + Verilog-Based Workflows @@ -839,18 +966,18 @@ - + - Trigger Position (optional) + Trigger Location (optional) - + - Capture Modes (optional) + Trigger Modes (optional) @@ -895,12 +1022,144 @@ - + - Python API + Python API Documentation + + + + + + + LogicAnalyzerCore + + + + + + + + + + capture + + + + + + + + + set_triggers + + + + + + + + + + + + + + LogicAnalyzerCapture + + + + + + + + + + export_csv + + + + + + + + + export_playback_verilog + + + + + + + + + export_vcd + + + + + + + + + get_playback_module + + + + + + + + + get_trace + + + + + + + + + get_trigger_location + + + + + + + + + + + + + + LogicAnalyzerPlayback + + + + + + + + + + get_top_level_ports + + + + + + + + + + + + + @@ -924,11 +1183,13 @@ Logic Analyzer Core Overview -The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. -While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below: +The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module. +This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference. Configuration -Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the IOCore class constructor below, as well as the Amaranth examples in the repo.
IOCore
Regardless of the technique you used to configure your IO Core, it is operated using the set_probe() and get_probe() methods. Documentation for these methods is available below.
set_probe()
get_probe()
These methods are members of the IOCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an IOCore member. This is done with Manta.from_config(), as shown in the Verilog examples.
Manta.from_config()
IOCore(inputs=[], outputs=[])
While the IO core performs a very, very simple task, it carries a few caveats.
First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful.
Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet.
A synthesizable module for setting and getting the values of registers of +arbitrary size.
Create an IO Core, with the given input and output probes.
This function is the main mechanism for configuring an IO Core in an +Amaranth-native design.
Optional[List[Signal]]
[]
A list of +Amaranth Signals to use as inputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an input.
A list of +Amaranth Signals to use as outputs. Defaults to an empty list. +This parameter is somewhat optional as an IO Core must have +at least one probe, but it need not be an output.
get_probe(probe) +
Get the value of an input or output probe on the FPGA.
If called on an output probe, this function will return the last value +written to the output probe. If no value has been written to the output +probe, then it will return the probe's initial value. This method is +blocking.
probe
str | Signal
The probe to get the value of. This +may be either a string containing the name of the probe, or the +Amaranth Signal representing the probe itself. Strings are +typically used in Verilog-based workflows, and Amaranth Signals +are typically used in Amaranth-native designs.
Returns:
value
The value of the probe, interpreted as an unsigned +integer.
Raises:
ValueError
The probe was not found in the IO Core, or many probes +were found with the same name.
set_probe(probe, value) +
Set the value of an output probe on the FPGA.
This method is blocking.
The output probe to set the value +of. This may be either a string containing the name of the +probe, or the Amaranth Signal representing the probe itself. +Strings are typically used in Verilog-based workflows, and +Amaranth Signals are typically used in Amaranth-native designs.
The value to set the probe to. This may be either +positive or negative, but must fit within the width of the +probe.
None
The probe was not found to be an output of the IO Core, +or many probes were found with the same name.
The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user.
While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:
The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts a capture, which continues until the onboard memory is full. The resulting data is then read out to the host machine, and can be displayed as a waveform, exported as a CSV file, or turned into a synthesizable playback module.
This is very similar to the behavior of a benchtop logic analyzer, but Manta's Logic Analyzer Core includes some extra features you may find useful. Both the Use Cases page and the repository's examples folder contain examples of the Logic Analyzer Core for your reference.
Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example:
--- +As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: +--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. -type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +name(required): The name of the Logic Analyzer core, which is used when working with the API. +type(required): This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. +sample_depth(required): The number of samples saved in the capture. A larger sample depth will use more FPGA resources, but will show what the probes are doing over a longer time interval. +probes (required): The signals in your logic that the Logic Analyzer connects to. Each probe is specified with a name and a width. -Sample Depth -This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time. -Probes -Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate. + +Name things carefully! +The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate. + Triggers Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: @@ -983,22 +1246,21 @@ argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. -Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger. -Trigger Position (optional) -Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. +Trigger Location (optional) +Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after. -Capture Modes (optional) -The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: +Trigger Modes (optional) +The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. -Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file. +Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file. Usage Capturing Data Once you have your Logic Analyzer core on the FPGA, you can capture data with: -manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
As explained in the getting started page, the Logic Analyzer Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design.
The Logic Analyzer Core is used by adding an entry in a cores section of a configuration file. This is best shown by example:
--- cores: my_logic_analyzer: type: logic_analyzer @@ -944,15 +1205,17 @@ - moe RISING - curly FALLING
There's a few parameters that get configured here, including:
logic_analyzer
sample_depth
probes
This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time.
Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate.
Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core.
triggers
Each individual trigger is specified with the following structure:
argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width.
Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger.
external_trigger: true
Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer.
trigger_position
Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Location parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a "holdoff" option on a traditional oscilloscope or logic analyzer.
If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after.
The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below:
The logic analyzer has a few different ways of capturing data, which are represented by the trigger modes below:
Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file.
capture_mode
Manta will use an Immediate trigger mode if no trigger_mode is provided in the configuration file.
Immediate
trigger_mode
Once you have your Logic Analyzer core on the FPGA, you can capture data with:
manta capture [config file] [LA core] [output filename] [[additional output filenames]...] +manta capture [config_file] [la_core_name] [output path] [[additional output paths]...] The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
manta capture [config_file] [la_core_name] [output path] [[additional output paths]...]
manta capture manta.yaml my_logic_analyzer capture.vcd @@ -1019,8 +1281,458 @@ The capture may be exported as either a VCD or CSV file. If manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
manta.yamlSparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. -Python API -The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list. +Python API Documentation + + + + + + + + manta.LogicAnalyzerCore + + + +LogicAnalyzerCore(sample_depth, probes) + + + + + + A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes. +Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register. + + Create a Logic Analyzer Core with the given probes and sample depth. +This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design. + + +Parameters: + + + sample_depth + (int) + – + + The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval. + + + + probes + (List[Signal]) + – + + The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width. + + + + + + + + + + + + + + + + + + + + + + capture + + + +capture() + + + + + Performs a capture, recording the state of all probes to memory. + + +Returns: + + +capture ( LogicAnalyzerCapture +) – + + A LogicAnalyzerCapture object +containing the capture and its metadata. + + + + + + + + + + + + + set_triggers + + + +set_triggers(trigger_mode=None, triggers=None, trigger_location=None) + + + + + + +Parameters: + + + trigger_mode + (TriggerMode | str, default: + None +) + – + + + + + + triggers + (Optional[Sequence[Sequence[str | int]]], default: + None +) + – + + + + + + trigger_location + (Optional[int], default: + None +) + – + + + + + + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerCapture + + + +LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) + + + + + + A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module. + + + + + + + + + + + + + + + + + + + export_csv + + + +export_csv(path) + + + + + Export the capture to a CSV file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + export_playback_verilog + + + +export_playback_verilog(path) + + + + + Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + export_vcd + + + +export_vcd(path) + + + + + Export the capture to a VCD file. + + +Parameters: + + + path + (str) + – + + Path to the destination file. + + + + + +Returns: + + + – + + None + + + + + + + + + + + + + get_playback_module + + + +get_playback_module() + + + + + Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process. + + + + + + + + + + get_trace + + + +get_trace(name) + + + + + Gets the value of a single probe over the capture. + + +Parameters: + + + name + (str) + – + + The name of the probe. + + + + + +Returns: + + +data ( List[int] +) – + + The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture. + + + + + + + + + + + + + get_trigger_location + + + +get_trigger_location() + + + + + Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture. + + + + + + + + + + + + + + + + + + + manta.LogicAnalyzerPlayback + + + +LogicAnalyzerPlayback(probes, data) + + + + + + A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it. + + + + + + + + + + + + + + + + + + + get_top_level_ports + + + +get_top_level_ports() + + + + + Returns the Amaranth signals that should be included as ports in the +exported Verilog module. + + + + + + + + + + + + diff --git a/dev/memory_core/index.html b/dev/memory_core/index.html index f80fda9..dec8ad8 100644 --- a/dev/memory_core/index.html +++ b/dev/memory_core/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,6 +345,26 @@ + + + + + + + + Similar Tools + + + + + + + + + + + + @@ -397,24 +382,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -515,6 +500,15 @@ + + + + + + On-Chip Implementation + + + @@ -528,27 +522,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -557,6 +594,11 @@ + + + + + @@ -568,6 +610,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -638,10 +729,10 @@ - + - + @@ -652,8 +743,8 @@ - - + + For Developers @@ -665,27 +756,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -739,6 +809,15 @@ + + + + + + On-Chip Implementation + + + @@ -752,27 +831,70 @@ - + - On-Chip Implementation + Verilog-Based Workflows - + - Python API + Amaranth-Native Designs + + + + + + + + + + + + + + Operation + + + + + + + + + Python API Documentation + + + + + + + + + + MemoryCore + + + + + + + + + + read - + - Synchronicity + write @@ -781,6 +903,11 @@ + + + + + @@ -804,10 +931,19 @@ Memory Core Overview -Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! -This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below: +Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA. +This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference. +Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA. + +Words update 16 bits at a time! +Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores. + +On-Chip Implementation +Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well. Configuration -Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: +As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design. +Verilog-Based Workflows +The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example: --- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384 -There's a few parameters that get configured here, including: +Inside this configuration, the following parameters may be set: -name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. +name: The name of the Memory core, which is used when working with the API. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. -Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here. -On-Chip Implementation -For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation. -Python API -The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: -Just like with the other cores, interfacing with the Memory with the Python API is simple: -from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list.
Manta.LogicAnalyzerCore
.vcd
.v
.csv
LogicAnalyzerCore(sample_depth, probes) +
A module for generating a logic analyzer on the FPGA, with configurable +triggers, trigger position, and trigger modes.
Provides methods for generating synthesizable logic for the FPGA, as well +as methods for reading and writing the value of a register.
Create a Logic Analyzer Core with the given probes and sample depth.
This function is the main mechanism for configuring a Logic Analyzer in +an Amaranth-native design.
The number of samples saved in the capture. A +larger sample depth will use more FPGA resources, but will show +what the probes are doing over a longer time interval.
List[Signal]
The signals in your logic that the Logic +Analyzer connects to. Each probe is specified with a name and +a width.
capture() +
Performs a capture, recording the state of all probes to memory.
capture
LogicAnalyzerCapture
A LogicAnalyzerCapture object +containing the capture and its metadata.
set_triggers(trigger_mode=None, triggers=None, trigger_location=None) +
TriggerMode | str
Optional[Sequence[Sequence[str | int]]]
trigger_location
LogicAnalyzerCapture(probes, trigger_location, trigger_mode, data, interface) +
A container class for the data collected by a LogicAnalyzerCore. Contains +methods for exporting the data as a VCD waveform file, a Python list, a +CSV file, or a Verilog module.
export_csv(path) +
Export the capture to a CSV file.
path
Path to the destination file.
export_playback_verilog(path) +
Exports a Verilog module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process.
export_vcd(path) +
Export the capture to a VCD file.
get_playback_module() +
Returns an Amaranth module that will playback the captured data. This +module is synthesizable, so it may be used in either simulation or +on the FPGA directly by including it your build process.
get_trace(name) +
Gets the value of a single probe over the capture.
The name of the probe.
data
List[int]
The value of the probe at every timestep, +interpreted as an unsigned integer. Has length equal to +the sample_depth of the core that produced the capture.
get_trigger_location() +
Returns the location of the trigger in the capture. This will match the +value of "trigger_location" provided in the configuration file at the +time of capture.
LogicAnalyzerPlayback(probes, data) +
A synthesizable module that plays back data captured by a +LogicAnalyzerCore. Takes a list of all the samples captured by a core, +along with the config of the core used to take it.
get_top_level_ports() +
Returns the Amaranth signals that should be included as ports in the +exported Verilog module.
Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both!
addr
data_in
data_out
write_enable
Memory is used to store data when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with a simple addr/data_in/data_out/write_enable interface. This allows the host machine to exchange larger amounts of data with your logic on the FPGA.
This is a very, very simple task - however it's surprisingly useful in practice. Both the Use Cases page and the repository's examples folder contain examples of the Memory Core for your reference.
Manta won't impose any limit on the width or depth of the memory you instantiate, but you will be limited by the available resources and timing properties of your FPGA.
Words update 16 bits at a time!
Due to the structure of Manta's internal bus, the Memory core only updates 16 bits of a word at a time. For instance, writing a new value to a 33-bit wide memory would update bits 0-15 on one clock cycle, bits 16-31 on another, and bit 32 on another still. Manta makes no guaruntees about the time taken between each of these updates. If this is a problem for your application, consider using an IO Core as a doorbell to signal when the memory is valid, or ping-pong between two Memory Cores.
Manta will make a best-effort attempt to implement the memory in Block RAM, if it is available on the device. This is done by exporting Verilog that synthesis tools should infer as Block RAMs, however this inference is not guarunteed. Depending on your toolchain and the FPGA's architecture, the Verilog produced by Manta may be implemented as FF RAM, LUT (Distributed) RAM, or something else. These memory types are well explained in the Yosys documentation, but be sure to check your toolchain's documentation as well.
Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example:
As explained in the getting started page, the Memory Core must be configured and included in the FPGA design before it can be operated. Configuration is performed differently depending on if you're using a traditional Verilog-based workflow, or if you're building an Amaranth-native design.
The Memory Core is used by adding an entry in a cores section of a configuration file. This is best shown by example:
--- cores: my_memory: @@ -816,42 +952,232 @@ width: 12 depth: 16384
Inside this configuration, the following parameters may be set:
memory
mode
bidirectional
host_to_fpga
fpga_to_host
width
depth
Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here.
For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation.
The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions:
Manta.MemoryCore
Just like with the other cores, interfacing with the Memory with the Python API is simple:
from manta import Manta -m = manta('manta.yaml') - -m.my_memory.write(addr=38, data=600) -m.my_memory.write(addr=0x1234, data = 0b100011101011) -m.my_memory.write(0x0612, 0x2001) - -foo = m.my_memory.write(addr=38) -foo = m.my_memory.write(addr=1234) -foo = m.my_memory.write(0x0612) +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo. +Operation +Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below. +These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples. +Python API Documentation + + + + + + + + manta.MemoryCore + + + +MemoryCore(mode, width, depth) -Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: -addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + + diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..1eca2fb Binary files /dev/null and b/dev/objects.inv differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json deleted file mode 100644 index 4d166ca..0000000 --- a/dev/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#manta-a-configurable-and-approachable-tool-for-fpga-debugging-and-rapid-prototyping","title":"Manta: A Configurable and Approachable Tool for FPGA Debugging and Rapid Prototyping","text":"Manta is a tool for moving data between a host machine and a FPGA over UART or Ethernet. It's primarily intended for debugging and rapid prototyping of FPGA designs, but it's robust enough to be used as a simple, reliable transport layer. Manta works by generating a number of cores that are instantiated in the FPGA design. These allow for a variety of functions, such as reading and writing to registers and memory, or capturing data with a logic analyzer. These cores are operated by the connected host machine through either the Manta CLI, or a simple Python API. Manta is written in Amaranth HDL, and the generated designs may be used natively in other Amaranth designs, or exported to vendor-agnostic Verilog-2001. All dependencies are cross-platform, so Manta can be used on any machine that has at least Python 3.8 or newer installed. Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below:"},{"location":"#logic-analyzer-core","title":"Logic Analyzer Core","text":"More details available on the full documentation page. This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. You may find this core useful for: Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. "},{"location":"#io-core","title":"I/O Core","text":"More details available on the full documentation page. This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. You may find this core useful for: Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. "},{"location":"#memory-cores","title":"Memory Cores","text":"More details available on the full documentation page. This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. You may find this core useful for: Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. "},{"location":"#about","title":"About","text":"Manta and its source code are released under a GPLv3 license, and it was originally developed as part of my Master's Thesis at MIT in 2023, done under the supervision of Dr. Joe Steinmeyer. The thesis itself is copyrighted by Fischer Moseley (me!), but feel free to use the following Bibtex if you'd like to cite it: @misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n"},{"location":"alternatives/","title":"Alternatives","text":"There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow."},{"location":"alternatives/#open-source-tools","title":"Open Source Tools","text":""},{"location":"alternatives/#litescope","title":"LiteScope","text":"An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. Source Code Documentation "},{"location":"alternatives/#gatemate-ila","title":"GateMate ILA","text":"An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files. Source Code "},{"location":"alternatives/#zipcpu-debugger","title":"ZipCPU Debugger","text":"A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file. Source Code Documentation, under the How to Debug an FPGA section. "},{"location":"alternatives/#commercial-tools","title":"Commercial Tools","text":""},{"location":"alternatives/#xilinx-integrated-logic-analzyer","title":"Xilinx Integrated Logic Analzyer","text":"An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado. ILA Documentation ILA User's Guide ChipScoPy API "},{"location":"alternatives/#xilinx-virtual-io","title":"Xilinx Virtual IO","text":"A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers. Virtual IO Documentation ChipScoPy API "},{"location":"alternatives/#intel-signal-tap","title":"Intel Signal Tap","text":"An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4. Documentation "},{"location":"alternatives/#intel-in-system-sources-and-probes","title":"Intel In-System Sources and Probes","text":"A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG. Documentation "},{"location":"alternatives/#lattice-reveal","title":"Lattice Reveal","text":"An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices. Documentation "},{"location":"alternatives/#opal-kelly-frontpanel-sdk","title":"Opal Kelly FrontPanel SDK","text":"Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic. Documentation User's Guide "},{"location":"alternatives/#matlab-fpga-data-capture","title":"MATLAB FPGA Data Capture","text":"An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet. Documentation "},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#system-architecture","title":"System Architecture","text":"The whole system looks like the following: Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA."},{"location":"architecture/#manta-architecture","title":"Manta Architecture","text":"The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it. These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus. At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA."},{"location":"architecture/#data-bus","title":"Data Bus","text":"The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory: addr [15:0], indicating the memory address targeted by the current transaction. data [15:0], which data is read from during a read, or written to during a write. rw, indicating a read or write transaction if the signal is low or high respectively. valid, which is driven high only when the operation specified by the other signals is to be executed. Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed. "},{"location":"architecture/#message-format","title":"Message Format","text":"Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure: Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF). These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line. If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value. An example of some bus traffic is provided below: Sequence Number Interface Activity Operation 1 Host \u2192 FPGA: R1234(CR)(LF) - 2 FPGA \u2192 Host: R5678(CR)(LF) Read 0x5678 from 0x1234 3 Host \u2192 FPGA: WF00DBEEF(CR)(LF) Write 0xBEEF to 0xF00D 4 Host \u2192 FPGA: RF00D(CR)(LF) - 5 FPGA \u2192 Host: RBEEF(CR)(LF) Read 0xBEEF from 0xF00D 6 Host \u2192 FPGA: W12340000(CR)(LF) Write 0x0000 to 0x1234 When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field."},{"location":"architecture/#cores","title":"Cores","text":""},{"location":"architecture/#io-core","title":"IO Core","text":"This is done with the architecture shown below: Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple. Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis."},{"location":"architecture/#logic-analyzer","title":"Logic Analyzer","text":"The Logic Analyzer Core's implementation on the FPGA consists of three primary components: The Finite State Machine (FSM), which controls the operation of the core. The FSM's operation is driven by its associated registers, which are placed in a separate module. This permits simple CDC between the bus and user clock domains. The Trigger Block, which generates the core's trigger condition. The trigger block contains a trigger for each input probe, and the registers necessary to configure them. It also contains the \\(N\\)-logic gate (either AND or OR) that generates the core's trigger from the individual probe triggers. CDC is performed in exactly the same manner as the FSM. If an external trigger is specified, the trigger block is omitted from the Logic Analyzer Core, and the external trigger is routed to the FSM's trig input. The Sample Memory, which stores the states of the probes during a capture. This is implemented as a dual-port, dual-clock block memory, with the bus on one port and the probes on the other. The probe-connected port only writes to the memory, with the address and enable pins managed by the FSM. CDC is performed in the block RAM primitive itself. "},{"location":"architecture/#memory-core","title":"Memory Core","text":"Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram: Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as: Bus Address Space BRAM Address Space BASE_ADDR + 0 address 0, bits 0-15 BASE_ADDR + 1 address 1, bits 0-15 BASE_ADDR + n address n, bits 0-15 ... ... BASE_ADDR + 0 + DEPTH address 0, bits 16-31 BASE_ADDR + 1 + DEPTH address 1, bits 16-31 BASE_ADDR + n + DEPTH address n, bits 16-31 ... ... BASE_ADDR + 0 + (2 * DEPTH) address 0, bits 32-33 BASE_ADDR + 1 + (2 * DEPTH) address 1, bits 32-33 BASE_ADDR + n + (2 * DEPTH) address n, bits 32-33 ... ... ...and so on."},{"location":"ethernet_interface/","title":"Ethernet Interface","text":""},{"location":"ethernet_interface/#overview","title":"Overview","text":"For scenarios where UART is not available or higher bandwidth is desired, Manta provides an Ethernet interface for communicating between the host and FPGA. This interface uses UDP for communication, and leverages the builtin Python sockets module on the host side, and the open-source LiteEth Ethernet core on the FPGA side. Not every device is supported! Although Manta aims to be as platform-agnostic as possible, Ethernet PHYs and FPGA clock primitives are very particular devices. As a result, the supported devices are loosely restricted to those on this list. If a device you'd like to use isn't on the list, the community would love your help! Although UDP does not guarantee reliable packet delivery, this usually doesn't pose an issue in practice. Manta will throw a runtime error if packets are dropped, and the UDP checksum and Ethernet FCS guarantee that any data delivered is not corrupted. Together, these two behaviors prevent corrupted data from being provided to the user, as Manta will error before returning invalid data. As long as your network is not terribly congested, Manta will operate without issue."},{"location":"ethernet_interface/#configuration","title":"Configuration","text":"The configuration of the Ethernet core is best shown by example: ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: phy (required): The name of the LiteEth PHY class to use. Valid values consist of any of the names in this list. Select the appropriate one for your FPGA vendor and family. vendor (required): The vendor of the FPGA being designed for. Currently only values of xilinx and lattice are supported. Used to generate timing constraints files, which are currently unused. toolchain (required): The toolchain being used. Currently only values of vivado and diamond are supported. clk_freq (required): The frequency of the clock provided to the Manta instance. refclk_freq (required): The frequency of the reference clock to be provided to the Ethernet PHY. This frequency must match the MII variant supported by the PHY, as well as speed that the PHY is being operated at. For instance, a RGMII PHY may be operated at either 125MHz in Gigabit mode, or 25MHz in 100Mbps mode. fpga_ip_addr (required): The IP address the FPGA will attempt to claim. Upon power-on, the FPGA will issue a DHCP request for this IP address. The easiest way to check if this was successful is by pinging the FPGA's IP, but if you have access to your network's router it may report a list of connected devices. host_ip_addr (required): The IP address of the host machine, which the FPGA will send packets back to. Lastly, any additonal arguments provided in the ethernet section of the config file will be passed to the LiteEth standalone core generator. As a result, the examples provided by LiteEth may be of some service to you if you're bringing up a different FPGA! LiteEth doesn't always generate its own refclk! Although LitEth is built on Migen and LiteX which support PLLs and other clock generation primitives, I haven't seen it instantiate one to synthesize a suitable refclk at the appropriate frequency from the input clock. As a result, for now it's recommended to generate your refclk outside Manta, and then use it to clock your Manta instance."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#overview","title":"Overview","text":"To use Manta, you'll need a host machine with a FPGA board connected over UART, or a FPGA board connected to the same network via Ethernet. You'll then: Specify a set of debug cores you wish to include in your design. This is done by writing a configuration file, typically called manta.yaml. Specifying files in JSON is also supported, as long as the hierarchy in the file is equivalent. Just make sure that your YAML files end in .yaml or .yml, and that JSON files end in .json. Invoke Manta to generate Verilog from the configuration provided. This is done by running manta gen [config_file] [verilog_file] at the command line, which generates a Verilog file (typically named manta.v) from the provided configuration file. This Verilog file contains a definition for a Verilog module named manta, and all its constituent modules. Instantiate manta in your design, and connecting it to the logic you'd like to debug. Manta will provide an example instantiation if you run manta inst [config_file], which you can copy-paste into your source code. You'll connect its ports to the logic you're trying to debug, as well as to whatever interface you're using to communicate with the host. This will be a serial transciever on your development board if you're using UART, or it's RMII PHY if you're using Ethernet. Build and upload the design to your FPGA using your preferred toolchain. Use the debug core(s) through the Python API or the command line. The functions availble to each core are described in their documentation. Repeat! As you debug, you'll probably want to change exactly how Manta is configured. This means tweaking the configuration file, regenerating the Verilog module, and so on. "},{"location":"getting_started/#example-configuration","title":"Example Configuration","text":"An example config file is provided below. If this file was named manta.yaml then running manta gen manta.yaml manta.v would generate Verilog for a manta module that matched the config file. ---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n Although it's just an example, this config file shows the two things every Manta configuration needs, namely: Cores: A list of the debug cores Manta should place on your FPGA. The behavior and configuration of the cores is described in more detail on their documentation pages, but this list contains each core you'd like included in your manta module. This list can have as many entires as your FPGA can support, so long as Manta can address them all. If it can't, it'll throw an error when it tries to generate Verilog. Interface: The way data gets on and off the FPGA. At present, Manta only supports UART and Ethernet interfaces. These are described in more detail on their documentation pages, but the interface of choice is specified with either a uart or ethernet at the end of the configuration file. This Manta instance has an IO Core and a Logic Analyzer, each containing a number of probes at variable widths. The Manta module itself is provided a 100MHz clock, and communicates with the host over UART running at 3Mbaud. This is just an example, and more details are available in the documentation page for each core."},{"location":"getting_started/#example-instantiation","title":"Example Instantiation","text":"Lastly, we Manta can automatically generate a copy-pasteable Verilog snippet to instantiate Manta in your design by running manta inst [config_file]. For example, the following snippet is generated for the configuration above: 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. manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n"},{"location":"installation/","title":"Installation","text":"Before installing, make sure to upgrade your pip to the latest version: pip install --upgrade pip\n"},{"location":"installation/#latest-version","title":"Latest Version","text":"You can install the latest version of Manta directly from source with: pip install --upgrade git+https://github.com/fischermoseley/manta.git\n"},{"location":"installation/#editable-development-install","title":"Editable Development Install","text":"If you're working on the source, you might want an editable installation with some extra dependencies used for development: git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n Manta's hardware-in-the-loop tests rely on Amaranth's build system for programming FPGAs, which in turn rely on the open-source xc3sprog and iceprog tools for programming Xilinx and ice40 devices, respecitvely. If you'd like to run these tests locally, you may need to install these tools and have them available on your PATH. If you're on Linux, you may also need to add a new udev rule to give non-superuser accounts access to any connected FTDI devices. This can be done by making a new file at /etc/udev/rules.d/99-ftdi-devices.rules, which contains: ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n Be sure to reload your udev rules after saving the file."},{"location":"installation/#adding-manta-to-path-recommended","title":"Adding Manta to Path (Recommended)","text":"It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different. Later Manta will be availabe on the PyPI lists, and you'll be able to just pip install mantaray, but that's not configured quite yet."},{"location":"installation/#dependencies","title":"Dependencies","text":"Manta requires the following dependencies: Amaranth HDL, which comes with it's own built-in copy of Yosys. LiteEth, for sending and receiving UDP packets on the FPGA. pySerial, for communicating with the FPGA over UART. pyYAML, for parsing configuration files written in YAML. pyVCD, for writing waveforms captured by the Logic Analyzer Core to standard Value Change Dump (VCD) files. As well as these dependencies for development, which are installed with the [dev] argument: Pytest, for unit testing. Black, for formatting the Python source. mkdocs-material, for generating the documentation site. amaranth_boards, for building designs for hardware-in-the-loop testing done by the CI. "},{"location":"io_core/","title":"IO Core","text":""},{"location":"io_core/#overview","title":"Overview","text":"Registers are a fundamental building block of digital hardware, and the IO core provides a simple way of interacting with them from the host machine. It allows you to define a set of inputs and outputs of arbitrary width, and then set values to the outputs and read values from the inputs. This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"io_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the IO core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n Inside this configuration, the following parameters may be configured: name (required): The name of the IO core. This name is used to reference the core when working with the API, and can be whatever you'd like. type (required): This denotes that this is an IO core. All cores contain a type field, which must be set to io to be recognized as an IO core. inputs (optional): This lists all inputs from from the FPGA fabric to the host machine. Signals in this list may be read by the host, but cannot be written to. Technically specifying input probes is totally optional - it's perfectly fine to have an IO core with only output probes. outputs (optional): This lists all outputs from the host machine to the FPGA fabric. Signals in this list are usually written to by the host, but they can also be read from. Doing so returns the value last written to the register. Just like the inputs parameter, this list is technically optional, and it's perfectly valid to have an IO core with input probes only. initial_value (optional): This sets an initial value for an output probe to take after the FPGA powers on. This is done with an initial statement in Manta's Verilog, and is independent of the input clock or resets elsewhere in the FPGA. This parameter is optional, and if it isn't provided the probe will initialize to zero. user_clock (optional): If set to True, an extra input port will be added to the manta module for an clock input to run the IO core on. This lets the IO Core handle clock domain crossing through its internal buffers. If set to False, Manta will run the IO core from its internal clock (the one provided through manta's clk port). More information on this is available in the diagram below. This parameter is optional, and defaults to False. Name things carefully! The names of the core and its probes are referenced in the autogenerated Verilog. This means that while the names can be arbitrary, they must be unique within your project and not contain any characters that your synthesis engine won't appreciate."},{"location":"io_core/#python-api","title":"Python API","text":"The IO core functionality is stored in the Manta.IOCore class in src/manta/io_core/__init__.py, and it may be controlled with the two functions: Manta.IOCore.set_probe(name, data) [string] name: The probe to write to. Must not be an output port, and must match the name provided in the config file. [int, bool] data: The value to write to an output probe. May be signed or unsigned, but will raise an exception if the value is too large for the width of the port. returns: None This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent. Manta.IOCore.get_probe(name) [string] name: The probe to read from. May be either an input or an output port, and must match the name provided in the config file. returns: The value of an input or output probe. In the case of an output probe, the value returned will be the last value written to the probe. This method is blocking. When called it will dispatch a request to the FPGA, and halt execution until the request has been sent and a response has been received."},{"location":"io_core/#example","title":"Example","text":"A small example is shown below, using the example configuration above. More extensive examples can also be found in the repository's examples/ folder. >>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n"},{"location":"io_core/#limitations","title":"Limitations","text":"While the IO core performs a very, very simple task, it carries a few caveats. First, it's not instantaneous. Manta has designed to be as fast as possible, but setting and querying registers relies on passing messages between the host and FPGA, which is slow relative to FPGA clock speeds! If you're trying to set values in your design with cycle-accurate timing, this will not do that for you. However, the Logic Analyzer's playback feature might be helpful. Second, the API methods are blocking, and will wait for a response from the FPGA before resuming program execution. Depending on your application, you might want to run your IO Core operations in a separate thread, but you can also decrease the execution time by using a faster interface between the host and FPGA. This means using a higher UART baudrate, or using Ethernet. "},{"location":"logic_analyzer_core/","title":"Logic Analyzer Core","text":""},{"location":"logic_analyzer_core/#overview","title":"Overview","text":"The Logic Analyzer core allows for debugging logic by capturing a set of digital signals to memory. This is done in response to a trigger condition, which starts the capture, which continues until the onboard memory is full, and the resulting capture is then read out to the user. While this is pretty much identical to the behavior of a benchtop logic analyzer, Manta has a few tricks up its sleeve that you may find useful! These are described below:"},{"location":"logic_analyzer_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Logic Analyzer core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n There's a few parameters that get configured here, including: name: The name of the Logic Analyzer core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Logic Analyzer core. All cores contain a type field, which must be set to logic_analyzer to be recognized as an Logic Analyzer core. "},{"location":"logic_analyzer_core/#sample-depth","title":"Sample Depth","text":"This refers to the number of samples saved in the capture, and is set with the sample_depth entry in the config file. A larger sample depth will use more resources, but show what your probes are doing over a longer time."},{"location":"logic_analyzer_core/#probes","title":"Probes","text":"Probes are the signals in your logic that the Logic Analyzer connects to, and are specified in the probes entry of the config file. Each probe requires both a name and a width to be specified. These names can be whatever you'd like, however they are referenced in the autogenerated Verilog - so don't use something your synthesis engine won't appreciate."},{"location":"logic_analyzer_core/#triggers","title":"Triggers","text":"Triggers are the conditions that your logic must meet in order to start a capture, and they're specified under the triggers entry in the config file. Manta's triggers are reprogrammable, meaning you don't need to rebuild your source code to change the trigger condition - just updating the configuration file is enough. If multiple triggers are provided, any one trigger being met will trigger the entire core. Each individual trigger is specified with the following structure: [probe] [operation] [argument] probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly. operation: The logical operation to perform. Manta supports the following operations: RISING, which checks if the probe has increased in value since the last clock cycle. FALLING, which checks if the probe has decreased in value since the last clock cycle. CHANGING, which checks if the probe is changed in value since the last clock cycle. These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including: GT, for greater than. LT, for less than. GEQ, for greater than or equal to. LEQ, for less than or equal to. EQ, for equal to. NEQ, for not equal to. These operations require a constant to compare against, referred to as an argument, which is described below: argument: A constant to compare against, if the operation specified requires one. On the FPGA, the argument will have just as many bits as the probe width. Lastly, if you're not able to express your desired trigger condition in terms of the operators above, fear not! You can also specify an external_trigger: true entry in the config file, which exposes an input on Manta's top level for your own trigger."},{"location":"logic_analyzer_core/#trigger-position-optional","title":"Trigger Position (optional)","text":"Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer. If trigger_position is not specified, Manta will default to centering the capture window around the trigger condition. This results in just as many samples before the trigger as after."},{"location":"logic_analyzer_core/#capture-modes-optional","title":"Capture Modes (optional)","text":"The logic analyzer has a few different ways of capturing data, which are represented by the capture modes below: Single-Shot: Once the trigger condition is met, record the value of the probes on every clock cycle in a continuous single shot. Incremental: Record samples when the trigger condition is met, but don't record the samples when the trigger condition is not met. This is super useful for applications like audio processing or memory controllers, where there are many system clock cycles between signals of interest. Immediate: Record the value of the probes on every clock cycle, beginning immediately, and regardless of if the trigger condition is met. This is useful for investigating cases where a trigger condition is never being met (such as latchup or deadlock conditions) or obtaining a random snapshot of the FPGA's state. Most logic analyzers use a single-shot capture by default, so Manta will do the same if no capture_mode entry is provided in the project's configuration file."},{"location":"logic_analyzer_core/#usage","title":"Usage","text":""},{"location":"logic_analyzer_core/#capturing-data","title":"Capturing Data","text":"Once you have your Logic Analyzer core on the FPGA, you can capture data with: manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n The capture may be exported as either a VCD or CSV file. If manta.yaml contained the configuration at the top of this page, then the following would export a .vcd file containing the captured waveform: manta capture manta.yaml my_logic_analyzer capture.vcd\n This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below."},{"location":"logic_analyzer_core/#playback","title":"Playback","text":"Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design. If the file manta.yaml contained the configuration above, then running: manta capture manta.yaml my_logic_analyzer capture.v\n Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed. This is useful for two situations in particular: Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly. Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table. "},{"location":"logic_analyzer_core/#python-api","title":"Python API","text":"The Logic Analyzer core functionality is stored in the Manta.LogicAnalyzerCore class in src/manta/logic_analyzer/__init__.py. This class contains methods for capturing data, exporting it as .vcd, .v or .csv files, or as a Python list."},{"location":"memory_core/","title":"Memory Core","text":""},{"location":"memory_core/#overview","title":"Overview","text":"Memory is the de facto means of storing data on FPGAs when the space needed exceeds a few registers. As a result, Manta provides a Memory core, which instantiates a dual-port RAM on the FPGA. One port is provided to the host, and the other is connected to your logic with the standard RAM interface (addr, data_in, data_out, write_enable). This allows the host to provide reasonably large amounts of data to user logic - or the other way around, or a mix of both! This is a very, very simple task - and while configuration is straightforward, there are a few caveats. More on both topics below:"},{"location":"memory_core/#configuration","title":"Configuration","text":"Just like the rest of the cores, the Memory core is configured via an entry in a project's configuration file. This is easiest to show by example: ---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n There's a few parameters that get configured here, including: name: The name of the Memory core. This name is used to reference the core when working with the API, and can be whatever you'd like. type: This denotes that this is a Memory core. All cores contain a type field, which must be set to memory to be recognized as an Memory core. mode: The mode for the Memory core to operate in. This must be one of bidirectional, host_to_fpga, or fpga_to_host. Bidirectional memories can be both read or written to by the host and FPGA, but they require the use of a True Dual Port RAM, which is not available on all platforms (most notably, the ice40). Host-to-fpga and fpga-to-host RAMs only require a Simple Dual Port RAM, which is available on nearly all platforms. width: The width of the Memory core, in bits. depth: The depth of the Memory core, in entries. Manta won't impose any limit on the width or depth of the memory you instantiate, but since Manta instantiates BRAM primitives on the FPGA, you will be limited by what your FPGA can support. It helps to know your particular FPGA's architecture here."},{"location":"memory_core/#on-chip-implementation","title":"On-Chip Implementation","text":"For most use cases, Manta will choose to implement the memory in Block RAM, if it is available on the device. However, the Verilog produced by Manta may be inferred to a number of memory types, including FF RAM or LUT (Distributed) RAM. For more information on how this is chosen, please refer to the Yosys documentation."},{"location":"memory_core/#python-api","title":"Python API","text":"The Memory core functionality is stored in the Manta.MemoryCore classes in src/manta/memory_core.py, and it may be controlled with the two functions: Just like with the other cores, interfacing with the Memory with the Python API is simple: from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk: addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n"},{"location":"memory_core/#synchronicity","title":"Synchronicity","text":"Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller."},{"location":"repository_structure/","title":"Repository Structure","text":""},{"location":"repository_structure/#repository-structure","title":"Repository Structure","text":" src/manta/ contains the Python source needed to generate and run the cores. test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. doc/ contains the documentation you're reading right now! examples/ contains examples of Manta being used in designs for a handful of FPGA boards. .github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. "},{"location":"repository_structure/#tools-used","title":"Tools Used","text":" The YosysHQ tools and Vivado are used for building bitstreams. draw.io is used for block diagrams. GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. GitHub Actions is used for continuous integration. "},{"location":"repository_structure/#github-actions-setup","title":"GitHub Actions Setup","text":"Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware."},{"location":"uart_interface/","title":"UART Interface","text":""},{"location":"uart_interface/#overview","title":"Overview","text":"Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here."},{"location":"uart_interface/#configuration","title":"Configuration","text":"The configuration of the UART interface is best shown by example: uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n This snippet at the end of the configuration file defines the interface. The following parameters must be set: port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. "}]} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/similar_tools/index.html similarity index 89% rename from dev/alternatives/index.html rename to dev/similar_tools/index.html index f613778..b452035 100644 --- a/dev/alternatives/index.html +++ b/dev/similar_tools/index.html @@ -12,13 +12,13 @@ - + - + - + @@ -26,7 +26,7 @@ - Alternatives - Manta Documentation + Similar Tools - Manta Documentation @@ -52,6 +52,8 @@ + + @@ -131,7 +133,7 @@ - Alternatives + Similar Tools @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -340,6 +305,46 @@ + + + + + + + + Use Cases + + + + + + + + + + + + + + + + + + + + Architecture + + + + + + + + + + + + @@ -354,7 +359,7 @@ - Alternatives + Similar Tools @@ -365,7 +370,7 @@ - Alternatives + Similar Tools @@ -518,26 +523,6 @@ - - - - - - Architecture - - - - - - - - - - - - - - @@ -553,24 +538,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -637,6 +622,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -707,10 +741,10 @@ - + - + @@ -721,8 +755,8 @@ - - + + For Developers @@ -734,27 +768,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -939,9 +952,11 @@ - Alternatives + Similar Tools -There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow. +This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project. +That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end. +If you're aware of a tool missing from this list, please let us know! Open Source Tools LiteScope An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export. diff --git a/dev/sitemap.xml b/dev/sitemap.xml index f9e06bb..ea497d6 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,46 +2,46 @@ https://fischermoseley.github.io/manta/dev/ - 2024-10-07 - - - https://fischermoseley.github.io/manta/dev/alternatives/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/architecture/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/ethernet_interface/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/getting_started/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/installation/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/io_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/logic_analyzer_core/ - 2024-10-07 + 2024-10-08 https://fischermoseley.github.io/manta/dev/memory_core/ - 2024-10-07 + 2024-10-08 - https://fischermoseley.github.io/manta/dev/repository_structure/ - 2024-10-07 + https://fischermoseley.github.io/manta/dev/similar_tools/ + 2024-10-08 https://fischermoseley.github.io/manta/dev/uart_interface/ - 2024-10-07 + 2024-10-08 + + + https://fischermoseley.github.io/manta/dev/use_cases/ + 2024-10-08 \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index e2fa63d..72a95e2 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/dev/uart_interface/index.html b/dev/uart_interface/index.html index effee52..3cad0c7 100644 --- a/dev/uart_interface/index.html +++ b/dev/uart_interface/index.html @@ -52,6 +52,8 @@ + + @@ -147,7 +149,7 @@ - + @@ -157,7 +159,7 @@ - + @@ -167,7 +169,7 @@ - + @@ -179,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -343,11 +308,11 @@ - + - Alternatives + Use Cases @@ -380,7 +345,25 @@ + + + + + + + + Similar Tools + + + + + + + + + + @@ -393,28 +376,30 @@ - + - - - + + + + + - Usage + Cores - - + + - Usage + Cores @@ -481,6 +466,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -545,6 +579,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -605,10 +672,10 @@ - + - + @@ -619,8 +686,8 @@ - - + + For Developers @@ -632,27 +699,6 @@ - - - - - - Repository Structure - - - - - - - - - - - - - - - @@ -715,6 +761,39 @@ + + + + + + + Verilog-Based Workflows + + + + + + + + + Amaranth-Native Designs + + + + + + + + + UARTInterface + + + + + + + + @@ -738,26 +817,165 @@ UART Interface Overview -Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here. +The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA. +Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference. Configuration -The configuration of the UART interface is best shown by example: +Verilog-Based Workflows +The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example: uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256 -This snippet at the end of the configuration file defines the interface. The following parameters must be set: +Inside this configuration, the following parameters may be set: -port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually. +port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually. -baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud. +baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency. -clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case. +clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate. + + +stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped. + + +chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work. +Amaranth-Native Designs +Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo. + + + + + + + + manta.UARTInterface + + + +UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) + + + + + + A synthesizable module for UART communication between a host machine and +the FPGA. + + This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design. + + +Parameters: + + + port + (str) + – + + The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually. + + + + baudrate + (float | int) + – + + The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation. + + + + clock_freq + (float | int) + – + + The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate. + + + + stall_interval + (Optional[int], default: + 16 +) + – + + The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped. + + + + chunk_size + (Optional[int], default: + 256 +) + – + + The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work. + + + + + +Raises: + + + ValueError + – + + The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/repository_structure/index.html b/dev/use_cases/index.html similarity index 71% rename from dev/repository_structure/index.html rename to dev/use_cases/index.html index 762edd6..0da0e39 100644 --- a/dev/repository_structure/index.html +++ b/dev/use_cases/index.html @@ -12,19 +12,21 @@ - + - + + + - Repository Structure - Manta Documentation + Use Cases - Manta Documentation @@ -50,6 +52,8 @@ + + @@ -78,7 +82,7 @@ - + Skip to content @@ -129,7 +133,7 @@ - Repository Structure + Use Cases @@ -145,7 +149,7 @@ - + @@ -155,7 +159,7 @@ - + @@ -165,7 +169,7 @@ - + @@ -177,43 +181,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializing search - - - - - - - - @@ -338,18 +305,81 @@ + - - + + + + + + + + + + + + Use Cases + + + + + + + - Alternatives + Use Cases + + + + + + + + + + + Table of contents + + + + + + + Logic Analyzer Core + + + + + + + + + I/O Core + + + + + + + + + Memory Cores + + + + + + + + + @@ -380,6 +410,26 @@ + + + + + + Similar Tools + + + + + + + + + + + + + + @@ -395,24 +445,24 @@ - + - + - Usage + Cores - - + + - Usage + Cores @@ -479,6 +529,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interfaces + + + + + + + + + + Interfaces + + + @@ -532,8 +631,6 @@ - - @@ -545,14 +642,16 @@ - + - - - + + + + + @@ -563,8 +662,8 @@ - - + + For Developers @@ -574,90 +673,6 @@ - - - - - - - - - - - - - - - Repository Structure - - - - - - - - - - - Repository Structure - - - - - - - - - - - - - - - Table of contents - - - - - - - Repository Structure - - - - - - - - - Tools Used - - - - - - - - - GitHub Actions Setup - - - - - - - - - - - - - - - - - - @@ -707,27 +722,27 @@ - + - Repository Structure + Logic Analyzer Core - + - Tools Used + I/O Core - + - GitHub Actions Setup + Memory Cores @@ -751,25 +766,45 @@ - Repository Structure + Use Cases -Repository Structure +Manta's capabilities are best reflected in its cores, for which a brief description of each is provided below: +Logic Analyzer Core +More details available on the full documentation page. +This core captures a timeseries of digital signals from within the FPGA, much like a benchtop logic analyzer would. This captures data on the FPGA's native clock and presents it as a waveform, making it very useful for debugging logic cycle-by-cycle. This concept is very similar to the Xilinx Integrated Logic Analyzer (ILA) and Intel SignalTap utilities. +You may find this core useful for: -src/manta/ contains the Python source needed to generate and run the cores. -test/ contains Manta's tests, which are a mix of functional simulations and hardware-in-the-loop testing. These tests leverage the pytest testing framework. -doc/ contains the documentation you're reading right now! -examples/ contains examples of Manta being used in designs for a handful of FPGA boards. -.github/ contains GitHub Actions workflows for automatically running the tests and building the documentation site on every commit. + +Verifying specification adherence for connected hardware - for instance, you're writing a S/PDIF decoder that works in simulation, but fails in hardware. The logic analyzer core can record a cycle-by-cycle capture of what's coming off the cable, letting you verify that your input signals are what you expect. Even better, Manta will let you play that capture back in your preferred simulator, letting you feed the exact same inputs to your module in simulation and check your logic. + + +Capturing arbitrary data - you're working on a DSP project, and you'd like to grab some test data from your onboard ADCs to start prototyping your signal processing with. Manta will grab that data, and export it for you. + -Tools Used +I/O Core +More details available on the full documentation page. +This core presents a series of user-accessbile registers to the FPGA fabric, which may be configured as either inputs or outputs. The value of an input register can be read off the FPGA by the host machine, and the value of an output register on the FPGA may be set by the host machine. This is handy for getting small amounts of information into and out of the FPGA, debugging, configuration, or experimentation. This concept is very similar to the Xilinx Virtual IO and Intel In-System Sources and Probes tools. +You may find this core useful for: -The YosysHQ tools and Vivado are used for building bitstreams. -draw.io is used for block diagrams. -GitHub Pages is used to serve the documentation site, which is built with Material for MkDocs. -GitHub Actions is used for continuous integration. + +Prototyping designs in Python, and incrementally migrating them to hardware - you're working on some real-time signal processing, but you want to prototype it with some sample data in Numpy before meticulously implementing everything in Verilog. + + +Making dashboards - you'd like to get some telemetry out of your existing FPGA design and display it nicely, but you don't want to implement an interface, design a packetization scheme, and write a library. + + +Memory Cores +More details available on the full documentation page. +This core creates a two-port block memory on the FPGA, and gives one port to the host machine, and the other to your logic on the FPGA. The width and depth of this block memory is configurable, allowing large chunks of arbitrarily-sized data to be shuffled onto and off of the FPGA by the host machine, via the Python API. This lets you establish a transport layer between the host and FPGA, that treats the data as exactly how it exists on the FPGA. +You may find this core useful for: + + +Moving data between a host and connected FPGA - you're working on a cool new machine learning accelerator, but you don't want to think about how to get training data and weights out of TensorFlow, and into your core. + + +Hand-tuning ROMs - you're designing a digital filter for a DSP project and would like to tune it in real-time, or you're developing a soft processor and want to upload program code without rebuilding a bitstream. + -GitHub Actions Setup -Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
Since Amaranth modules are Python objects, the configuration of the Memory Core is given by the arguments given during initialization. See the documentation for the MemoryCore class constructor below, as well as the Amaranth examples in the repo.
MemoryCore
Regardless of the technique you used to configure your Memory Core, it is operated using the read() and write() methods. Documentation for these methods is available below.
read()
write()
These methods are members of the MemoryCore class, so if you're using Manta in a Verilog-based workflow, you'll first need to obtain a Manta object that contains an MemoryCore member. This is done with Manta.from_config(), as shown in the Verilog examples.
MemoryCore(mode, width, depth)
Reading/writing in batches is also supported. This is recommended where possible, as reads are massively sped up by performing them in bulk:
addrs = list(range(0, 1234)) -datas = list(range(1234, 2468)) -m.my_memory.write(addrs, datas) - -foo = m.my_memory.read(addrs) + + + + + A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic. + + Create a Memory Core with the given width and depth. +This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design. + + +Parameters: + + + mode + (str) + – + + Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms. + + + + width + (int) + – + + The width of the memory, in bits. + + + + depth + (int) + – + + The depth of the memory, in entries. + + + + + + + + + + + + + + + + + + + + + + read + + + +read(addrs) -Synchronicity -Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic. -There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach. -Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller. + + + + Read the data stored in the Memory Core at one or many address. +This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to read +from. + + + + + +Returns: + + +datas ( int | List[int] +) – + + The data stored at the address (or +addresses), represented as an unsigned integer. + + + + + +Raises: + + + TypeError + – + + addrs is not an integer or list of integers. + + + + + + + + + + + + + write + + + +write(addrs, datas) + + + + + Write data to the Memory core at one or many addresses. +This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking. + + +Parameters: + + + addrs + (int | List[int]) + – + + The memory address (or addresses) to write +to. + + + + datas + (int | List[int]) + – + + The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory. + + + + + +Returns: + + + – + + None + + + + + +Raises: + + + TypeError + – + + addrs or datas is not an integer or list of integers. + + + + + + + + + + + + + + +
A synthesizable module for accessing a memory. This is accomplished by +instantiating a dual-port memory with one end tied to Manta's internal bus, +and the other provided to user logic.
Create a Memory Core with the given width and depth.
This function is the main mechanism for configuring a Memory Core in an +Amaranth-native design.
Must be one of bidirectional, host_to_fpga, or +'fpga_to_host'. Bidirectional memories can be both read or +written to by the host and FPGA, but they require the use +of a True Dual Port RAM, which is not available on all +platforms (most notably, the ice40). Host-to-fpga and +fpga-to-host RAMs only require a Simple Dual Port RAM, which +is available on nearly all platforms.
The width of the memory, in bits.
The depth of the memory, in entries.
read(addrs)
Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's ≤16 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's ≥16 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic.
There's a few different ways to solve this - you could use an IO core to signal when a RAM's contents or valid - or you could ping-pong between two RAMs while one is being modified. The choice is yours, and Manta makes no attempt to prescribe any particular approach.
Lastly, the interface you use (and to a lesser extent, your operating system) will determine the space between bus transactions. For instance, 100Mbit Ethernet is a thousand times faster than 115200bps UART, so the time where the RAM is invalid is a thousand times smaller.
Read the data stored in the Memory Core at one or many address.
This function can read from either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address read is +significantly faster than multiple single-address reads. Prefer their +usage where possible. This method is blocking.
addrs
int | List[int]
The memory address (or addresses) to read +from.
datas
The data stored at the address (or +addresses), represented as an unsigned integer.
TypeError
addrs is not an integer or list of integers.
write(addrs, datas) +
Write data to the Memory core at one or many addresses.
This function can write to either one or multiple addresses at a time. +Due to the the IO latency in most OSes, a single multi-address write is +significantly faster than multiple single-address write. Prefer their +usage where possible. This method is blocking.
The memory address (or addresses) to write +to.
The data to store at the address (or +addresses). This may be either positive or negative, but must +fit within the width of the memory.
addrs or datas is not an integer or list of integers.
@misc{manta2023,\n author={Fischer Moseley},\n title={Manta: An In-Situ Debugging Tool for Programmable Hardware},\n year={2023},\n month={may}\n howpublished={\\url{https://hdl.handle.net/1721.1/151223}}\n}\n
There's quite a few FPGA debugging tools out there, and it may happen that your needs are better met by another tool! This section aims to provide a list of alternatives, in hopes that you're able to be confident in your debugging flow.
An embedded logic analyzer written in Migen, primarily for use in LiteX SoC designs. Also includes IO peek and poke, as well as UART, Ethernet, and PCIe interfaces with a host machine. Includes VCD, Sigrok, CSV, and Python data export.
An embedded logic analyzer written in Verilog, targeting the GateMate FPGA family from Colonge Chip and the Yosys/NextPNR toolchain. Communication between the host and FPGA is accomplished with SPI, via a FT232 or FT2232 used as a USB-SPI adapter. GateMate is inteneded to be used with GTKWave, so the tool generates .gtkw files in addition to VCD files.
.gtkw
A set of embedded debugging modules written by Dan Gisselquist of ZipCPU fame. Communication between the host and FPGA is accomplished with UART, and control of the debugger is performed with a C++ API on the host. A wishbone interface is provided on the FPGA side to connect to other Wishbone-based debugging tools that can provide control of user registers, block RAM, and an embedded logic analyzer. Supports dumping of signals to a VCD file.
An embedded logic analyzer for Xilinx FPGAs, provided as part of the Xilinx Vivado development suite. Communication between the host and FPGA is accomplished with JTAG, typically running over a USB cable to the device. Includes an integrated waveform viewer, and VCD and CSV export. Also supports a JTAG-to-AXI mode, which integrates well with Xilinx IP, which uses primarily AXI. Also integrates with the ChipScoPy API, which allows for Python control of the ILA on Versal devices. The ILA was previously known as ChipScope in earlier versions of Vivado.
A tool for reading and writing to individual registers on the FPGA, provided as part of the Xilinx Vivado development suite. Just like the ILA, communication between the host and FPGA is done over JTAG. Control over the registers is done through the Vivado GUI or through the Tcl interpreter. In the case of Versal devices, the ChipScoPy API can also control the registers.
An embedded logic analyzer for Intel/Altera FPGAs, provided as part of the Quartus development suite. Communication between the host and FPGA is accomplished with JTAG, and a programmable interface is provided via Tcl. Signal Tap is notable for providing a significant amount of configurability in the trigger conditions, and provides a small scripting language called Trigger Flow that allows users to define triggers as state machines. Signal Tap also allows for Simulation-Aware nodes, which allows for running simulations with data captured from the real world. At the time of writing, this feature is only available in the most recent and full-featured version of the Quartus suite, Quartus Prime Pro Edition 22.4.
A tool for reading and writing to individual registers on the FPGA, provided for Intel/Altera FPGAs as part of the Quartus development suite. Just like Signal Tap, communication between the host and FPGA is accomplished with JTAG.
An embedded logic analyzer for Lattice FPGAs, provided as part of the Diamond development suite. Communication between the host and FPGA is accomplished with JTAG. Reveal is notable for providing a significant amount of configurability in the trigger conditions, and supports trigger conditions formed with a mix of combinational and sequential logic. Reveal also provides special support for Wishbone buses, and for controlling SERDES modules on ECP5 devices.
Unlike other entries in this list, Opal Kelly's FrontPanel SDK is not marketed as a debugger (although it can be used as such). Instead, it's designed to provide a host computer with a real time interface to FPGA signals, and present them on a graphical \u201cfront panel\". These front panels exist as a GUI window on the host, and contain buttons, knobs, and indicators, much like a LabVIEW virtual instrument. Communication between the host and FPGA is accomplished with either USB or PCIe. Bindings for hosts running Windows, macOS, and Linux are provided, and target C, C++, C#, Python, Java, Ruby, and MATLAB. The FrontPanel SDK differs from other debuggers in that it provides a skeleton module into which the user logic is instantiated, instead of being instantiated inside the user's logic.
An embedded logic analyzer for Xilinx and Altera FPGAs, provided as part of MATLAB. Communication between the host and FPGA is accomplished with JTAG, but Ethernet is supported for Xilinx FPGAs. Notably, this tool allows for data to be captured and used directly inside MATLAB, which also includes a framework for FPGA-in-the-loop testing. It also provides an AXI manager IP block that allows for reads and writes to an AXI memory map from MATLAB. This IP supports PCI Express on Xilinx FPGAs, in addition to JTAG and Ethernet.
The whole system looks like the following:
Manta consists of two parts - a Python API running on a host machine, and an autogenerated block of RTL that's included with your logic on a FPGA. Either UART or Ethernet is used for communication, which allows the host machine to operate the debugging cores on the FPGA.
The logic Manta places on the FPGA consists of a series of cores connected in a chain along a common bus. Each core provides a unique method for interacting with the user\u2019s logic, which it connects to by routing signals (called probes) between the user\u2019s logic and the cores that interface with it.
These probes are presented as addressable memory, and are be controlled by reading and writing to their corresponding memory - not unlike registers on a microcontroller. Each core is allotted a section of address space at compile time, and operations addressed to a core\u2019s address space control the behavior of the core. These cores are then daisy-chained along an internal bus, which permits a chain arbitrarily many cores to be placed on the bus.
At the beginning of this chain is a module called a receive bridge, which converts incoming UART/Ethernet communication from the host into read and write requests, which are placed on the bus. These are called bus transactions, and once placed on the bus, they travel through each core before reaching the transmit bridge at the end of the chain. This module places the result of the bus transaction back on the UART/Ethernet interface, and sends it back to the host. This produces a request-response style of communication between the host machine and the FPGA.
The data bus is designed for simplicity, and consists of five signals used to perform reads and writes on memory:
addr [15:0]
data [15:0]
rw
valid
Each core has a bus input and output port, so that cores can be daisy-chained together. When it receives an incoming bus transaction (signalled by valid), the core checks the address on the wire against its own memory space. If the address lies within the core, the core will perform the requested operation against its own memory space. In the case of a read, it places the data at that address on data, and in the case of a write, it copies the value of data to the specified location in memory. However, if the address lies outside of the memory of the core, then no operations are performed.
Ethernet and UART both allow a stream of bytes to be sent between the host and FPGA, but since they're just interfaces, they don't define how these bytes are structured. As a result, Manta implements its own messaging format, with the following structure:
Each of these messages is a string of ASCII characters consisting of a preamble, optional address and data fields, and an End of Line (EOL). The preamble denotes the type of operation, R for a read and W for a write. The address and data fields are encoded as hexadecimal digits, represented with the characters 0-9 and A-F in ASCII. As a result, four characters are needed to encode a 16-bit address or 16-bits of data. If the message specifies a write request, then it will contain a data field after the address field. Both request types will conclude with an End of Line, which consists of the two ASCII characters indicating a Carriage Return (CR) and a Line Feed (LF).
These requests are sent by the host machine to the FPGA, which reads them from the rx line on the interface transceiver. This is handled by the receive bridge, which parses incoming messages, and generates bus transactions from them. Once this transaction runs through every core in the chain, it arrives at the transmit bridge, which may send a response back to the host over the tx line.
rx
tx
If the request specified a read operation, then a response will be produced. These responses have the same structure as the read request itself, albeit with the data read from memory substituted in place of the address. This results in a message of the same length, just with the address swapped for data. If the request specified a write operation, then no response will be sent back to the host. Manta provides no report of if the write was successful - if a write operation must be verified, Manta will just perform a read operation on the same address location and check the value.
An example of some bus traffic is provided below:
When UART is used, these bytes are transmitted directly across the wire, but when Ethernet is used, they're packed into the packet's payload field.
This is done with the architecture shown below:
Each of the probes is mapped to a register of Manta's internal memory. Since Manta's internal registers are 16 bits wide, probes less than 16 bits are mapped to a single register, but probes wider than 16 bits require multiple.
Whatever the number of registers required, these are read from and written to by the host machine - but the connection to the user's logic isn't direct. The value of each probe is buffered, and only once the strobe register has been set to one will the buffers update. When this happens, output probes provide new values to user logic, and new values for input probes are read from user logic. This provides a convenient place to perform clock domain crossing, and also mitigates the possibility of an inconsistent system state. This is explained in more detail in Chapter 3.6 of the original thesis.
strobe
The Logic Analyzer Core's implementation on the FPGA consists of three primary components:
trig
Each Memory core is actually a set of 16-bit wide BRAMs with their ports concatenated together, with any spare bits masked off. Here's a diagram:
Since each \\(n\\)-bit wide block memory is actually \\(ceil(n/16)\\) BRAMs under the hood, addressing the BRAMs correctly from Manta's internal bus is important. BRAMs are organized such that each 16-bit slice of a \\(N\\)-bit word in the Block Memory core are placed next to each other in bus address space. For instance, a 34-bit wide block memory would exist on Manta's internal bus as:
...and so on.
The configuration of the Ethernet core is best shown by example:
ethernet:\n phy: LiteEthPHYRMII\n vendor: xilinx\n toolchain: vivado\n\n clk_freq: 50e6\n refclk_freq: 50e6\n\n fpga_ip_addr: \"192.168.0.110\"\n host_ip_addr: \"192.168.0.100\"\n
---\ncores:\n my_io_core:\n type: io\n\n inputs:\n probe_0_in: 6\n probe_1_in: 12\n\n outputs:\n probe_2_out: 20\n probe_3_out: 1\n\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n\nuart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n
manta manta_inst (\n .clk(clk),\n .rst(rst),\n .rx(rx),\n .tx(tx),\n .probe_0_in(probe_0_in),\n .probe_1_in(probe_1_in),\n .probe_2_out(probe_2_out),\n .probe_3_out(probe_3_out),\n .larry(larry),\n .curly(curly),\n .moe(moe));\n
pip install --upgrade pip\n
pip install --upgrade git+https://github.com/fischermoseley/manta.git\n
git clone https://github.com/fischermoseley/manta.git\ncd manta\npip install -e \".[dev]\"\n
ACTION==\"add\", ATTR{idVendor}==\"0403\", ATTR{idProduct}==\"6010\", MODE:=\"666\"\n
It's recommended to place Manta on your system path by adding export PATH=\"~/.local/bin:$PATH\" to your .bashrc or .zshrc. This isn't strictly necessary, but it means that Manta (and any other executable Python modules) can be run as just manta on the command line, instead of python3 -m manta. If you're on Windows, this location will likely be different.
export PATH=\"~/.local/bin:$PATH\"
---\nmy_io_core:\n type: io\n\n inputs:\n kermit: 3\n piggy: 1\n animal: 38\n scooter:\n width: 4\n initial_value: 13\n\n outputs:\n fozzy: 1\n gonzo: 3\n\n user_clock: True\n
>>> import Manta\n>>> m = Manta\n>>> m.my_io_core.set_probe(\"fozzy\", True)\n>>> m.my_io_core.get_probe(\"fozzy\")\nTrue\n>>> m.my_io_core.set_probe(\"gonzo\", 4)\n>>> m.my_io_core.get_probe(\"scooter\")\n5\n
---\ncores:\n my_logic_analyzer:\n type: logic_analyzer\n sample_depth: 4096\n trigger_location: 1000\n\n probes:\n larry: 1\n curly: 3\n moe: 9\n\n triggers:\n - moe RISING\n - curly FALLING\n
[probe] [operation] [argument]
[probe]
[operation]
[argument]
probe: The probe that the trigger applies to. Each probe only supports one trigger on it. For instance, in the example above we couldn't add a trigger for curly LEQ 4, since we've already assigned a trigger to curly.
curly LEQ 4
curly
operation: The logical operation to perform. Manta supports the following operations:
RISING
FALLING
CHANGING
These operations only compare a probe's value with itself, but sometimes it is useful to compare a probe's value to a constant. Manta provides a operations for doing such, including:
GT
LT
GEQ
LEQ
EQ
NEQ
These operations require a constant to compare against, referred to as an argument, which is described below:
Sometimes, you care more about what happens before a trigger is met than afterwards, or vice versa. To accommodate this, the logic analyzer has an optional Trigger Position parameter, which sets when probe data is captured relative to the trigger condition being met. This is specified with the trigger_position entry in the configuration file, which sets how many samples to save prior to the trigger condition occurring. This is similar to a \"holdoff\" option on a traditional oscilloscope or logic analyzer.
manta capture [config file] [LA core] [output filename] [[additional output filenames]...]\n
manta capture manta.yaml my_logic_analyzer capture.vcd\n
This will reset your logic analyzer, configure it with the triggers specified in manta.yaml, perform a capture, and create the file. Additional output files may be passed as well - Manta will detect the file format based on the extension (.vcd, .csv). Verilog (.v) files are also supported, and will follow the playback mechanism described below.
Manta has the ability to generate a module that plays back a set of data captured from a Logic Analyzer core. This module has a set of outputs matching the inputs of the Logic Analyzer, which when enabled, will take the exact values captured by the logic analyzer. This module is synthesizable, and can either be used in simulation or included in the FPGA design.
If the file manta.yaml contained the configuration above, then running:
manta capture manta.yaml my_logic_analyzer capture.v\n
Generates a Verilog module at capture.v which can then be instantiated in the testbench or FPGA design in which it is needed.
capture.v
This is useful for two situations in particular:
Input Verification. Designs will often work in simulation, but fail in hardware. In the absence of any build errors, this usually means that the inputs being applied to the logic in simulation don't accurately represent those being applied to the logic in the real world. Playing signals back in simulation allows for easy comparison between simulated and measured input, and provides a nice way to check that the logic downstream is behaves properly.
Sparse Sampling. Sometimes designs will have a small number of inputs, but a huge amount of internal state. In situations like these, it may be more efficient to sample the inputs and simulate the logic, instead of directly sampling the state. For instance, debugging a misbehaving branch predictor in a CPU can be done by recording activity on the address and data busses and playing them back in simulation - which would use less FPGA resources than sampling the entire pattern history table.
---\ncores:\n my_memory:\n type: memory\n mode: bidirectional\n width: 12\n depth: 16384\n
from manta import Manta\nm = manta('manta.yaml')\n\nm.my_memory.write(addr=38, data=600)\nm.my_memory.write(addr=0x1234, data = 0b100011101011)\nm.my_memory.write(0x0612, 0x2001)\n\nfoo = m.my_memory.write(addr=38)\nfoo = m.my_memory.write(addr=1234)\nfoo = m.my_memory.write(0x0612)\n
addrs = list(range(0, 1234))\ndatas = list(range(1234, 2468))\nm.my_memory.write(addrs, datas)\n\nfoo = m.my_memory.read(addrs)\n
Since Manta's data bus is only 16-bits wide, it's only possible to manipulate the Memory core in 16-bit increments. This means that if you have a RAM that's \u226416 bits wide, you'll only need to issue a single bus transaction to read/write one entry in the RAM. However, if you have a RAM that's \u226516 bits wide, you'll need to issue a bus transaction to update each 16-bit slice of it. For instance, updating a single entry in a 33-bit wide RAM would require sending 3 messages to the FPGA: one for bits 1-16, another for bits 17-32, and one for bit 33. If your application expects each RAM entry to update instantaneously, this could be problematic.
src/manta/
test/
pytest
doc/
examples/
.github/
Since Vivado is large and requires individual licenses, it is run on a private server, which is configured as a self-hosted runner in GitHub Actions. This is a virtual server hosted with KVM/QEMU and managed by libvirt, which is configured as transient so that it reloads its state from a snapshot periodically. A Nexys4 DDR and Icestick are connected to the physical machine and passthrough-ed to this VM so that continuous integration can check against real hardware.
Manta needs an interface to pass data between the host machine and FPGA, and UART is a convenient option. When configured to use UART, Manta will shuffle data back and forth using generic 8N1 serial with no flow control. This happens through a series of read and write transactions, which are specified using a messaging format described here.
The configuration of the UART interface is best shown by example:
uart:\n port: \"auto\"\n baudrate: 3000000\n clock_freq: 100000000\n
port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a USB device with the same VID and PID as a FT2232 - a USB/UART converter chip that's super popular on FPGA dev boards. This doesn't always work, but it's super convenient when it does. If your port isn't automatically detected, then just specify the port manually.
port
/dev/ttyUSBXX
/dev/tty.usbserialXXX
COMX
auto
baudrate (required): The baudrate of the serial port. Generally you want to configure this at the maximum speed of your USB/UART chip such that data transfers as fast as possible. The ubiquitous FT2232 supports up to 3Mbaud.
baudrate
clock_freq (required): The frequency of the clock being provided to the manta module, in Hertz (Hz). This speed doesn't matter much to the logic itself, it's only used to calculate the correct baud timing for the provided baudrate. However, this frequency does have to be fast enough to ensure a good agreement between the onboard prescaler and the requested baudrate - and Manta will throw an error during code generation if that is not the case.
clock_freq
This page contains a list of tools similar to Manta, either in function or intention. This is meant to provide a useful comparison to other tools that you may be more familiar with, in hopes of a smoother experience integrating Manta into your project.
That said, Manta is by no means feature-complete, and there may be an alternative tool listed on this page that better fits your use case. If that is the case, we encourage you to use that tool (although do consider filing a feature request - we’d love to hear about your workflow!). The goal of this project is to make working with FPGAs easier, and maintaining this list serves that end.
If you're aware of a tool missing from this list, please let us know!
The ubiquity and simplicity of UART makes it a convenient mechanism for sharing data between the host machine and FPGA. As a result, Manta provides the option to run over UART, where operating the cores can often take place over the same USB cable used to program the FPGA.
Both the Use Cases page and the repository's examples folder contain examples of the UART Interface for your reference.
The UART interface is used by adding a uart entry at the bottom of the configuration file. This is best shown by example:
uart: port: "auto" baudrate: 3000000 clock_freq: 100000000 + stall_interval: 16 + chunk_size: 256
port (required): The name of the serial port on the host machine that's connected to the FPGA. Depending on your platform, this could be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set to auto, then Manta will try to find the right serial port by looking for a connected FTDI chip. This doesn't always work, so if your port isn't automatically detected then just specify the port manually.
baudrate (required): The baudrate of the serial port. Generally, this should be set to the maximum baudrate supported by the USB/UART chip on your dev board for fastest operation. Manta will throw an error if this baudrate is not achievable with your FPGA's clock frequency.
clock_freq (required): The frequency of the clock provided to the manta module, in Hertz (Hz). This is used to calculate an appropriate prescaler onboard the FPGA to acheive the desired baudrate. Manta will throw an error if this clock frequency does not allow you to achieve your desired baudrate.
stall_interval (optional): The number of read requests to send before sending a stall byte. This prevents packets from being dropped if the FPGA's baudrate is less than the USB-Serial adapter's baudrate. This is usually caused by a mismatch between the clock frequency of the USB-Serial adapter and the FPGA fabric. See issue #18 on GitHub. Defaults to 16, reduce this if Manta reports that bytes are being dropped.
stall_interval
chunk_size (optional): The number of read requests to send at a time. Since the FPGA responds to read requests almost instantly, sending them in batches prevents the host machine's input buffer from overflowing. Defaults to 256, Reduce this if Manta reports that bytes are being dropped, and decreasing stall_interval did not work.
chunk_size
Since Amaranth modules are Python objects, the configuration of the IO Core is given by the arguments given during initialization. See the documentation for the UARTInterface class constructor below, as well as the Amaranth examples in the repo.
UARTInterface
UARTInterface(port, baudrate, clock_freq, stall_interval=16, chunk_size=256) +
A synthesizable module for UART communication between a host machine and +the FPGA.
This function is the main mechanism for configuring a UART Interface +in an Amaranth-native design.
The name of the serial port on the host machine that's +connected to the FPGA. Depending on your platform, this could +be /dev/ttyUSBXX, /dev/tty.usbserialXXX, or COMX. If set +to auto, then Manta will try to find the right serial port by +looking for a connected FTDI chip. This doesn't always work, so +if your port isn't automatically detected then just specify the +port manually.
float | int
The baudrate of the serial port. Generally, +this should be set to the maximum baudrate supported by the +USB/UART chip on your dev board for fastest operation.
The frequency of the clock provided to +this module, in Hertz (Hz). This is used to calculate an +appropriate prescaler onboard the FPGA to acheive the desired +baudrate.
16
The number of read requests to send +before sending a stall byte. This prevents packets from being +dropped if the FPGA's baudrate is less than the USB-Serial +adapter's baudrate. This is usually caused by a mismatch +between the clock frequency of the USB-Serial adapter and the +FPGA fabric. See issue #18 on GitHub. Reduce this if Manta +reports that bytes are being dropped.
256
The number of read requests to send at +a time. Since the FPGA responds to read requests almost +instantly, sending them in batches prevents the host machine's +input buffer from overflowing. Reduce this if Manta reports +that bytes are being dropped, and decreasing stall_interval +did not work.
The baudrate is not acheivable with the clock frequency +provided, or the clock frequency or baudrate is invalid.