2. The ato
compiler¶
The ato
command line interface is the main way to interact with atopile.
The CLI has tools to:
- Build your code to update your PCB
- Test your design
- Generate files to order your PCBs from manufacturers
- Install and manage dependencies
- Create new projects, components or build-targets
There's a semi-defacto-standard format to running applications from the terminal, including ato
.
application
is the name of the application you're running.command
is the command, think of it as a "menu option" the application provides.options
are the options for the command, typically zero-to-many of--some-option option-value
arguments
are just like options, but they only take a value. Their position tells the application which argument they are.
Add --help
to the application/command/subcommand to get more information at any point.
Tip
Some things might have multiple commands/subcommands. Think of it like a menu where, from left to right you're choosing deeper and deeper into the menu.
If an upper command/application accepts options, those options should go right after the application/command, rather than at the end after subcommands.
For example, -v
is a special type of option, called a "flag", which doesn't take a value. It's presence or absence is what matters. It increases the amount of information the compiler will print -> increase it's verbosity.
The -v
flag is an application option, so to use it, place it right after the application name.
Build Process¶
As a rough overview, here's what happens when you run ato build
:
graph TD
A[Run <tt>ato build</tt>] --> B[Compiler looks for your <tt>ato.yaml</tt>]
B --> C["Compile the code, starting from the 'entry point'"]
code1[<tt>src/</tt>] -->|some_code.ato| C
code3[Dependencies] -->|dependency_code.ato| C
C -->|App - the model of your design| D[Solve equations]
D --> E[Pick components]
cloud[atopile server recommends components] --> E
E --> D
E -->|Picked all components| F[Run tests]
F --> G[Update PCB File]
G --> H[Generate Manufacturing Data]
G --> I[Generate Reports]
G --> J[Generate ... Target]
Explaining the hello-world project¶
Important
This tutorial continues from the quickstart guide.
If you haven't already, complete it, and then come back here.
With the additional explanation of ato
code and the build process, it should be a little more clear what's happening now in the quickstart project.
import Resistor
module App:
r1 = new Resistor # Create a new resistor
r1.resistance = 100ohm +/- 10% # Set the resistor value
Let's break it down:
1. Import a Resistor
¶
2. Define a new module
, named App
¶
This is the entry point of the build (see your ato.yaml
).
3. Create an instance of the Resistor
¶
4. Set the resistor's value
attribute¶
This constrains what components can be picked for this resistor.
For picking, the rated resistance must be wholely within the value
attribute. The precise way to say this in more CS terms / what atopile says internally is that the picked resistor's resistance must be a subset of the quantity interval defined by the value attribute.
Extending it¶
Let's say our circuit needs a simple voltage divider - and the standard library doesn't exist containing one pre-made and tested for you.
We can easily create our own!
import Resistor
module VoltageDivider:
r_top = new Resistor
r_bottom = new Resistor
r_top.p2 ~ r_bottom.p1
module App:
my_vdiv = new VoltageDivider
my_vdiv.r_top.resistance = 10kohm +/- 10%
my_vdiv.r_bottom.resistance = 4.7kohm +/- 10%
Now, this is, technically, a voltage divider. And the compiler will happily build it grabbing the right components etc...
But, it's not a good voltage divider.
- There's no clear interface showing how it should be connected to
- The point of a voltage divider is to scale voltage, but we're left in the dark about what it's scaling actually is
- There is no documentation on what's going on
Let's fix that.
import Resistor, ElectricPower, ElectricSignal
module VoltageDivider:
"""
A voltage divider using two resistors.
Connect to the `power` and `output` interfaces
Configure via:
- `power.voltage`
- `output.reference.voltage`
- `max_current`
"""
# External interfaces
power = new ElectricPower
output = new ElectricSignal
# Components
r_bottom = new Resistor
r_top = new Resistor
# Variables
v_in: voltage
v_out: voltage
max_current: current
total_resistance: resistance
ratio: dimensionless
r_total: resistance
# Connections
power.hv ~ r_top.p1; r_top.p2 ~ output.line
output.line ~ r_bottom.p1; r_bottom.p2 ~ power.lv
# Link interface voltages
assert v_out is output.reference.voltage
assert v_in is power.voltage
# Equations - rearranged a few ways to simplify for the solver
assert r_top.resistance is (v_in / max_current) - r_bottom.resistance
assert r_bottom.resistance is (v_in / max_current) - r_top.resistance
assert r_top.resistance is (v_in - v_out) / max_current
assert r_bottom.resistance is v_out / max_current
assert r_bottom.resistance is r_total * ratio
assert r_top.resistance is r_total * (1 - ratio)
# Calculate outputs
assert r_total is r_top.resistance + r_bottom.resistance
assert v_out is v_in * r_bottom.resistance / r_total
assert v_out is v_in * ratio
assert max_current is v_in / r_total
assert ratio is r_bottom.resistance / r_total
module App:
my_vdiv = new VoltageDivider
assert my_vdiv.power.voltage is 10V +/- 1%
assert my_vdiv.output.reference.voltage within 3.3V +/- 10%
assert my_vdiv.max_current within 10uA to 100uA
Ooooo yeah! Now we're talking.
Let's break it down.
More imports¶
When possible, import from the standard library.
Define a new module
, named VoltageDivider
¶
This means we trivially have as many VoltageDivider
's as we want, all configured properly with all the same interfaces.
Document with """
¶
A voltage divider using two resistors.
Connect to the `power` and `output` interfaces
Configure via:
- `power.voltage`
- `output.reference.voltage`
- `max_current`
"""
Place external interfaces somewhere obvious¶
... and use sensible types, like Power
to expose them.
Embed the relationships between parameters with assert
¶
Read these statements as "make sure the following is true".
- The output voltage will be within the range of the voltage divider ratio equation
- The input voltage must be greater than the output voltage
- The current through the bottom resistor must be within the allowed range of the maximum quiescent current