Skip to content

Repository files navigation

Argon

License

Argon is a programming language for writing constraint-based integrated circuit layout generators. Argon's primary feature is bidirectional editing between Neovim and a custom GUI. Simpler geometric constraints can be entered visually in the GUI, while more complex logic can be implemented in code.

Argon's syntax and type system is inspired by Rust. Unlike Rust, Argon is not intended to be a fully featured general-purpose programming language. The main goal of Argon is to allow interoperability with the GUI, enable the creation of most practical parametric cells, and allow for performance optimizations such as caching and incremental compilation.

Installation

To use Argon, you will need:

Install Argon from source:

cargo install --git https://github.com/ucb-substrate/argon --locked argon

To install from a local clone, you can run:

cargo install --locked --path crates/argon

Command-line compilation

Use arc from an Argon library containing lib.ar and Argon.toml. The manifest names the library and can set its Argon technology file and path dependencies and GDS cell imports:

name = "my-library"
tech = "tech.toml"

[dependencies]
pdk = "../pdk"

[gds]
ring_osc = "~/Downloads/ring_osc.gds"
"macros::sram" = "layout/sram.gds"

From the library directory, check the source or run a cell:

arc check
arc run --cell 'top(10., 20.)'
arc run --cell 'top()' --gds

arc check checks the library without executing a cell. arc run writes the result to target/argon.bin; pass --gds to also write target/argon.gds. Dependency cells use their dependency name, for example arc run --cell 'pdk::fet1v8(true, 150., 5)'.

Cell arguments are ordinary Argon expressions, evaluated by the compiler in the library's scope, so they may call functions and build sequences:

arc run --cell 'top(pitch * 4., -width / 2.)'
arc run --cell 'array(cons(250., cons(350., [])), Mode::Fast)'

GDS imports are zero-argument cells. A module-qualified entry such as "macros::sram" can be referenced as lib::macros::sram() or imported with use lib::macros::sram;. Paths in the manifest are relative to Argon.toml, and a leading ~/ is expanded to the user's home directory. When invoking argonc directly, pass the technology file with --tech tech.toml and the same mapping as --gds-import 'macros::sram=layout/sram.gds'.

The technology file is TOML. dbu is meters per GDS database unit and can instead use "m", "mm", "um", "nm", or "pm". Every other length is an integer multiple of the DBU. display_unit is also the coordinate unit used in Argon source, while grid controls solver and GUI snapping:

dbu = "nm"       # physical size of one GDS database unit
display_unit = 1 # one source/display unit is one DBU
grid = 1         # snap grid is one DBU

[[layers]]
name = "met1.pin"
gds = [68, 16]
fill = "#0000ff"
border = "#0000ff"

[layers.style]
expanded = false       # whether a layer group starts expanded
frame_brightness = 0   # -100 black, 0 unchanged, 100 white (border)
fill_brightness = 0    # -100 black, 0 unchanged, 100 white (fill)
dither_pattern = "I0" # I0 solid, I1 clear, other Ix built-in, Cx custom
line_style = "I0"     # empty/I0 solid, other Ix built-in, Cx custom
valid = true           # false: display shapes but do not allow selection
visible = true         # initial visibility in the layer list
transparent = false    # background-dependent transparent composition
width = 1              # border width in screen pixels
marked = false         # draw small crosses over the layer
xfill = false          # draw a diagonal X through boxes
animation = 0          # 0 none, 1 scrolling, 2 blinking, 3 inverse blinking

[[layers]]
name = "met1.label"
gds = [68, 5]
fill = "#0000ff"
border = "#0000ff"

[pin_layers]
"met1.pin" = "met1.label"

For example, with dbu = "nm", display_unit = 1000, and grid = 5, Argon coordinates are expressed in microns and snap to a 5 nm grid.

Each layer maps its Argon name to a GDS layer/datatype pair. A pin_layers entry maps a pin-shape layer to the text layer that names contained pins. GDS coordinates are transformed between database and source/display units during import and export.

Polygons normally take a layer and a point count. Each generated point has independent solver coordinates, addressable either as polygon.x0, polygon.y0, and so on, or through polygon.points[0].x and .y:

let outline = polygon("met1", 3,
    x0=0., y0=0.,
    x1=100., y1=0.,
    y2=100.,
);
eq(outline.x2, 50.);

The GUI polygon tool (toolbar button or p) places vertices in click order; press Enter after the final vertex to close and insert the polygon. It writes editable fallback coordinates (x0i, y0i, x1i, y1i, and so on), so vertex drags persist. Add hard x0/y0 kwargs or eq constraints later when coordinates should become fixed. Handwritten geometry does not need fallback kwargs up front: the first drag inserts any missing *i coordinates into polygon, rectangle, or instance constructors. Escape clears an in-progress polygon. Polygon edges touching a point with any unconstrained coordinate are dashed; a point constrained in only one axis remains draggable along its free axis. Polygon fills use the layer's solid or stippled fill style just like rectangles.

Imported rectangular geometry can be used by GUI dimensions. Unlabeled shapes receive stable fields such as gds_rect_12; a shape on a configured pin layer uses text from the corresponding contained label layer as its field name. Repeated pin names are arrays (inst.VDD[0], inst.VDD[1]). When an instance is collapsed in the GUI, its displayed bounding-box edges are available through bbox(inst).

IDE

Install the Neovim plugin with the built-in vim.pack package manager by adding this to your init.lua:

vim.pack.add({
    'https://github.com/ucb-substrate/argon',
})

The plugin detects .ar files and starts argon-analyzer from your PATH.

From an Argon project directory, start Neovim and the GUI together:

argone pdks/sky130

From within the GUI, hit the o hotkey, type inv(1200., 2000., 4) after the prefilled :Argon openCell command, and press Enter to open the inv cell. You should now be able to edit layouts in both Neovim and the GUI.

Parametric Cell Tutorial

Create a new Argon library with the following command:

arc new tutorial

Your library directory should look like this:

tutorial
├── Argon.toml
├── tech.toml
└── lib.ar

The generated lib.ar contains a top() cell with a “Hello world!” text label. The manifest points to the generated default technology file, so the workspace is ready to open immediately:

argone tutorial

With the layout canvas focused, press o and enter top() after the prefilled :Argon openCell command to see the starter label.

For the rest of the tutorial, replace the generated top() cell in lib.ar with:

cell inset_rect() {
}

With the layout canvas focused, press o, type inset_rect() after the prefilled :Argon openCell command, and press Enter. Click the met2 layer in the right sidebar to select it. Press r to activate the Rectangle tool, then click two points on the canvas to draw your first rectangle. You should see a rectangle appear in the GUI and code editor.

Select the met1 layer and draw another rectangle that surrounds the first. Press Esc to leave the Rectangle tool.

Let us now dimension the rectangles such that the met2 rectangle is inset by 50. relative to the met1 rectangle. Press d to activate the Dimension tool and click the top edge of each rectangle. Click elsewhere to place the dimension label. The dimension should be highlighted yellow while it is being edited. Type 50. and press Enter to set its value. The decimal point is important because 50 is an integer literal, while the dimension requires a float. To edit an existing dimension later, press s, select its label, and press q.

Tip

If you make a mistake, you can undo and redo changes from the GUI using u and Ctrl-R, respectively, or manually modify the code in the text editor if needed.

Repeat for the other 3 sides of the rectangle.

Now, let's parametrize the width and height of the outer rectangle. In the code editor, add a width and height parameter to your cell:

cell inset_rect(w: Float, h: Float) {
    // ...
}

Once you save, you may notice that an error popped up saying that the open cell is invalid. This is because we opened the cell with no arguments, but the cell now requires us to specify w and h. To resolve this, focus the canvas, press o, enter inset_rect(200., 200.), and press Enter.

You can now press d and dimension the width of the met1 rectangle by selecting the top edge, then clicking above the rectangle to place the label. Enter the dimension as w. Dimension the right edge to h. You can press f to fit the layout to your screen.

You may notice that none of the rectangles have a solid boundary, indicating that they are not fully constrained. In order to constrain the edges to absolute coordinates, you can dimension the left and bottom edges of the met1 rectangle relative to the origin. If the origin is not in view, you can also add the following lines to your code (make sure to save in order to have your changes reflected in the GUI):

cell inset_rect(w: Float, h: Float) {
    // ...
    eq(rect1.x0, 0.);
    eq(rect1.y0, 0.);
}

You can also define a hierarchical cell in your code editor as follows:

cell triple_rect() {
    let cell1 = inset_rect(200., 200.);
    let inst1 = inst(cell1);
    let inst2 = inst(cell1, xi=300.);
    let inst3 = inst(inset_rect(300., 400.), xi=600.);
}

After saving, focus the canvas, press o, enter triple_rect(), and press Enter. You should be able to constrain the instances relative to one another based on their constituent rectangles.

You can also add an instance from the GUI. Select the destination scope in the hierarchy sidebar, press i, enter a cell invocation such as inset_rect(150., 150.), and press Enter. Move the instance outline to the desired location and click to insert it. The placement tool remains active so you can click again to insert more copies; press Esc when finished.

Configuration

Argon's configuration file is ~/.config/argon/config.toml, or $XDG_CONFIG_HOME/argon/config.toml when XDG_CONFIG_HOME is set. The GUI's font and icon sizes can be overridden in logical pixels:

[gui]
font_size = 14
icon_size = 18

Both values are optional and must be between 1 and 256. Omit them to use the built-in sizes. Run :Argon reload after editing the file.

Logs

The analyzer and Argone write to one shared log at ~/.local/state/argon/argon.log. If XDG_STATE_HOME is set, the log is written to $XDG_STATE_HOME/argon/argon.log instead. While the analyzer is running, open it with :Argon log.

Configure the log level in ~/.config/argon/config.toml (or $XDG_CONFIG_HOME/argon/config.toml):

[log]
level = "debug"

The level follows RUST_LOG filter syntax. It defaults to error; warn or error is recommended unless you are troubleshooting.

Contributing

If you'd like to contribute to Argon, please let us know. You can:

  • Ping us in the #substrate channel in the Berkeley Architecture Research Slack workspace.
  • Open an issue and/or PR.
  • Email rahulkumar -AT- berkeley -DOT- edu and rohankumar -AT- berkeley -DOT- edu.

Documentation updates, tests, and bugfixes are always welcome. For larger feature additions, please discuss your ideas with us before implementing them.

Contributions can be submitted by opening a pull request against the main branch of this repository. Developer documentation can be found in the docs/ folder.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed under the BSD 3-Clause license, without any additional terms or conditions.

About

A programming language for writing constraint-based integrated circuit layout generators

Resources

Stars

17 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages