A Compiler Infrastructure for Accelerator Generators (original) (raw)
Calyx is a compiler infrastructure for languages that target hardware accelerators. Calyx’s control language simplifies encoding of high-level semantics. The Calyx compiler automatically optimizes and lowers programs into synthesizable hardware designs. Calyx has been integrated with the LLVM CIRCT infrastructure and is available as a dialect within it.
If you’re interested in building on Calyx, please join our Zulip chat or start a discussion and let’s chat!
Frontends
CIRCT is an LLVM incubator project building an open-source hardware toolchain. You can try compiling the Calyx dialect in your web browser withCompiler Explorer!
Dahlia is an imperative, loop-based programming language for generating hardware accelerators.
Systolic arrays are the class of architectures that power AI chips like Google's TPUs.
TVM is a compiler for machine learning frameworks that can optimize and target kernels to several different backends.
Language
Components
Components are the basic building block of Calyx programs. A component has three sections: cells, wires, and control. An empty component does nothing.
component main() -> () {
cells { }
wires { }
control { }
}
Cells
The cells section instantiates sub-components like adders and registers. These sub-components can be used in this component to define behaviour.
component main() -> () {
cells {
// A 32-bit register
c = std_reg(32);
}
wires { }
control { }
}
Assignments & Guards
The wires section specifies connections to cells. They are continuously active by default and work like non-blocking assignments in RTL.
Guarded assignments have a boolean expression that controls when the assignment should be active.
component main() -> () {
cells {
// A 1-bit register
b = std_reg(1);
// A 32-bit register
c = std_reg(32);
}
wires {
// write a 1-bit constant to b
b.in = 1'd1;
b.write_en = 1'd1;
// write 1 when b.out == 1
c.in = b.out ? 32'd1;
// write 2 when b.out == 0
c.in = !b.out ? 32'd2;
}
control { }
}
Groups
A group is a collection of assignments that are only active when the group is run from the control program. The write_c[done]
port is a special port defined on groups that signify when the group has finished.
component main() -> () {
cells {
// A 32-bit register
c = std_reg(32);
}
wires {
group write_c {
c.in = 32'd1;
c.write_en = 1'd1;
// group is done when reg is done
write_c[done] = c.done;
}
}
control { }
}
Control
The control language specifies the order to run groups. The language has the normal imperative control flow operators: seq
, if
, and while
. It also has par
to express parallel composition.
component main() -> () {
cells {
a = std_reg(32);
b = std_reg(32);
}
wires {
group write_a { ... }
group read_a { ... }
group write_b { ... }
}
control {
seq {
write_a;
par {
read_a;
write_b;
}
}
}
}
Authors
Calyx's development is led byRachit Nigam,Griffin Berlstein,Chris Gyurgyik,Samuel Thomas, andAdrian Sampson. An ever growingset of excellent folks have contributed code and ideas to Calyx. If you're interested on using or contributing to Calyx, pleaselet us know!