Syl is hardware description with compile-time checks.
The compiler catches width mismatches, driver conflicts, and clock domain violations before simulation.
Chisel silently resolves conflicts with last-connect semantics. Verilog gives you X in simulation. Syl rejects it at compile time.
arbiter.syl
cell Arbiter(
req_a: in Bit,
req_b: in Bit,
) -> grant: Bit {
grant := req_a
grant := req_b
}sylc
error[E_DRIVER_OVERLAP]: multiple drivers for signal 'grant' ┌─ arbiter.syl:5:5 │ 5 │ grant := req_a │ ----- first driver here 6 │ grant := req_b │ ----- second driver here
cross_domain.syl
cell Sync<A: Domain, B: Domain>(
clk_a: in Clock<A>,
clk_b: in Clock<B>,
d: in Bit,
) -> q: Bit {
reg r1: Bit reset(clk_b, 0)
next r1 := d
reg r2: Bit reset(clk_a, 0)
next r2 := r1
q := r2
}sylc
error[E_DOMAIN]: clock domain mismatch ┌─ cross_domain.syl:6:30 │ 6 │ reg r1: Bit reset(clk_b, 0) │ ^^^^^ domain B 7 │ next r1 := d │ ^ signal 'd' belongs to domain A │ = use an explicit synchronizer primitive to cross domains
mux.syl
cell Mux4(
sel: in UInt<2>,
a: in UInt<8>,
b: in UInt<8>,
c: in UInt<8>,
d: in UInt<16>,
) -> out: UInt<8> {
out := select {
sel eq 0 => a,
sel eq 1 => b,
sel eq 2 => c,
default => d,
}
}sylc
error[E_TYPE_MISMATCH]: branch type mismatch in select ┌─ mux.syl:12:21 │ 12│ default => d, │ ^ expected UInt<8>, found UInt<16> │ = all branches must have the same type
Terminal
cargo install sylcRequires Rust toolchain