# Amaranth
> [[FPGA]], [[Open source MOC|open source]], [[ICE40 Overview]]
See: https://amaranth-lang.org/docs/amaranth/latest/
Tutorial: https://github.com/robertbaruch/amaranth-tutorial
Learning exercises: https://github.com/robertbaruch/amaranth-exercises
[[amaranth Finite State Machines]]
[[Simulating in Amaranth]]
## Background
Amaranth consists of:
- Language
- Standard Library
- Simulator
- Buildsystem
### Amaranth Lanuguage
- Python library
- Python code is used to construct a netlist of a digital circuit design
- Amaranth is made to work with other python tools
- Amaranth tries to be hard to miss-use
- Usability issues are considered reportable bugs
### Amaranth standard library
- Collection of essentila digital design componets and interfaces such as [[FIFO First In First Out Buffer]]
- Using an amaranth component helps minimized the amount of vendor specific code that you need to write.
### Simulator
- You can use python to write simulator test benches for your design to interface with
Running an amarath `.py` file -> generate a verilog '.v'' file and a simulation `.vcd` file which you can view in [[GTKWave]]
## Installation
### Mac
1. Install [[Homebrew]] if not already installed
2. Install Amaranth
- I am installing the development snapshot here, last release in main was a year ago
- `pip install --user --upgrade 'amaranth[builtin-yosys]'`
3. Install board definitions?
- no instructions for this yet apparently
## Language guide
- Values
- A binary number computed or stored anywhere in the design
- Has width
- number of bits used to represent value
- Has [[signedness]]
- With and signedness together are called shape
- Constant
- Simplest value
- Called as either
- Const(...)
- C(...)
- Shape
- Shape is an object with 2 attributes: .width and .signed
- Shapes of values
- All values have a shape
- .shape() can compute a values shape
- Example
- The width of a value v can be retrived with either:
- v.shape().width
- len(v)
- Shape casting
- Shapes can be *cast* from other objects
- Called shape-castable
- So if you have a list of data, and you want to make sure your shape can contain all of the potential values, you could cast the shape to that list.
- So if you have some class defining a state machine, casting would let you reliably have the shape for the output value of the state machine fully contain the output value
- This seems to be inline with Amaranth being a language focusing on usability.
- Value casting
- Values may be cast from other objects
- Casting can be done explicitly with value.cast
- BUT casting is also implicit, since objects that can be cast as values are accepted in the same places that values are
- Signals
- a signal is a value representing a potentially varying number
- Signals can be assigned in [[combinatorial domain]] or [[synchronous domain]]
- generated as [[wires - FPGA]] and [[registers - FPGA]] respectively
- This makes sense as the combinatorial domain is where functions of the design that remain fixed get defined, and synchronous domain is where memory like functionality can be created
- This video from the [[FPGAs for Beginners Youtube Channel]] covers the theory of these different domains: https://www.youtube.com/watch?v=oROOR6ylVF4
- signals can not be undefined of uniniotialized
- They are going to be implemented in the FPGA as actual hardware, so yes you do have to fully define hardware
- signal can be created with an explicitly defined shape
- Signal Name
- each signal has a name, which gets used in:
- waveform viewer
- diagnostic messages
- Verilog output
- etc.
- Most cases signal name is inferred from the name of the variable or attribute the signal is placed into
- Can be explicitly defined with the name= paramater
- Initial signal value
- Each signal and an initial value
- Specified with the reset= parameter
- If no initial value is specified, defaults to 0
- Signals assigned in a [[combinatorial domain]] assume their initial value when none of the assignments are active
- Signals assigned in the [[synchronous domain]] assume initial value after power on reset and under explicit reset
- Signals used but never assigned are equivalent to the constants of the initial value
- Reset-less signals can be defined
## Operators
- Values can be combined with each other or other value-castable objects with:
- arithmetic operations
- bitwise operations
- logical operations
- bit sequence operations
- other operators
- the resulting form is an experession
- *which is also a value*
- Performing or describing computations?
- Code written in python computes concrete objects with the goal of outputting a concrete result
- Amaranth code ***Describes*** computations on many potential abstract objects
- Goal is to generate the described circuit which can then do the operation when simulated or deployed on the FPGA
- amaranth expressions don't compute the results in python
- they instead are python objects that describe a circuit you could synthesis that would carry out the operation
- This means that amaranth values exist on a higher level of abstraction than the python that describes it
- So if you want to create an if statement that runs on the FPGA, you cant just use python if
- You have to use python to describe the resulting circuit of an if statement
- Amaranth has control structures for this
- Width Extension
- Many operations will automatically zero extend or sign extend values as necessary to complete operation
- So if you use an add operator, and the result could have a wider shape than the original values, then the widh will be increased
- Arithmetic operators
### Assigning the value of a signal
```python
s = Signal()
s.eq(1)
```
### Multiplexing signals
```python
y.eq(Mux(cond,x1,x2))
```
If `cond` is true, then `y` is `x1`, otherwise, it is `x2`
## Modules
`comb` = [[combinatorial domain]] - static lookup tables
`sync` = [[synchronous domain]] - updates with clock cycle
Adding assignments to a domain:
```python
a = Signal()
b = Signal()
c = Signal()
m.d.comb += a.eq(1)
m.d.sync += [
b.eq(c),
c.eq(b),
]
```
## Interfacing with pads
https://github.com/RobertBaruch/amaranth-tutorial/blob/main/9_synthesis.md
### Direction types:
- `i`: input only. Signal for the pin is `.i`
- `o`: output only. Signal for the pin is `.o`
- `io`: bidirectional. Signals for the pin are `.i` for the input, `.o` for the output, and `.oe` is the direction for the pin: 0 for input, 1 for output
- `oe`: tristate. Signals for the pin are `.o` for the output, and `.oe` to enable output: 0 for disable, 1 for enable
### Resources
A resource configures pins on the device, and allows you to request those pins in an `elaborate` function. A `Pin` has `Signal`s associated with it, including `i` and `o` (input/output)
A resource includes: name, number, and items to configure
Ex: `Resource("abc",0,Pins("J3",dir="i")`
To get the input Signal from "abc": `platform.request("abc").i`
### Resource configuration items
- `Pins`
- `PinsN` - all pins are active low
- `DiffPairs`
- ![[Screenshot 2023-05-16 at 5.27.49 PM.jpg]]
- `Clock` - a clock with a given frequency in Hz
- `Attrs` - "platform-specific attributes such as voltage standard to select"
#### Example:
```python
Resource("clk", 0, Pins("J3", dir="i"), Clock(12e6), Attrs(GLOBAL=True, IO_STANDARD="SB_LVCMOS"))
```
> This says that the `clk` resource is at pin `J3` on the FPGA, has a frequency of 12MHz, is a "global" signal, and uses the LVCMOS voltage standard. Without knowing about the toolchain for the platform, you will not know what attributes are required.
```python
Resource("sata", 0,
Subsignal("tx", DiffPairs("AD16", "AD17", dir="o")),
Subsignal("rx", DiffPairs("AF15", "AF16", dir="i")),
Attrs(IO_TYPE="==LVDS==")
),
```
#### Known Lattice ICE40 Attributes
- `GLOBAL`: bool. If True, the pin is global. Global pins are designated `GBIN` in the datasheet pin listing.
- `IO_STANDARD`: string:
- `SB_LVCMOS`: for all single-ended pins and differential output pins
- `SB_LVDS_INPUT`: for differential input pins
### Connectors
Are a different thing
### Reqesting numbered resoruces
- There are 5 LEDs on glasgow
- If you want to request say the 2nd LED, you would
- `LED_2 = platform.request("led",1)`
## Defining a board
Properties of a board definition:
- `device`
- `package`
- `resources`
- `default_clk`
- `default_rst`
- `connectors`
[[Glasgow board definition in Amaranth]]