// implementing ieee 1481-2009 SPEF.
// hchar, special_chars refer to the ieee documentation.
WHITESPACE = _{ " " | "\t" | NEWLINE | ("//" ~ (!NEWLINE ~ ANY)* ~ (NEWLINE | &EOI)) }
// COMMENT_STR = @{"//" ~ (!NEWLINE ~ ANY)* ~ (NEWLINE | &EOI)}
// COMMENT = _{ COMMENT_STR }
main = {
SOI ~
header ~ // pushes DIVIDER, DELIMITER, BUS_DELIMITER*2 onto the stack.
name_map? ~ top_ports? ~ parasitic_net* ~
DROP ~ DROP ~ DROP ~ DROP ~ &EOI
}
str = @{ "\"" ~ ((!"\"" ~ !"\\" ~ ANY) | ("\\" ~ ANY))* ~ "\"" }
float = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT*)? ~
("e" ~ ("+" | "-") ~ ASCII_DIGIT+)?}
int = @{ ASCII_DIGIT+ }
par_value = ${ float ~ (":" ~ float ~ ":" ~ float)? }
hchar = { "." | "/" | ":" | "|" }
prefix_bus_delim_char = { "[" | "{" | "(" | "<" } // ":" | "." omitted.
suffix_bus_delim_char = { ">" | ")" | "}" | "]" }
special_char = {
"!" | "#" | "$" | "%" | "&" | "'" | "(" | ")" | "*" | "+"
| "," | "-" | "." | "/" | ":" | ";" | "<" | "=" | ">" | "?"
| "@" | "[" | "\\" | "]" | "^" | "`" | "{" | "|" | "}" | "~" }
header = {
"*SPEF" ~ str ~
"*DESIGN" ~ str ~
"*DATE" ~ str ~
"*VENDOR" ~ str ~
"*PROGRAM" ~ str ~
"*VERSION" ~ str ~
"*DESIGN_FLOW" ~ header_design_flow ~
"*DIVIDER" ~ PUSH(hchar) ~
"*DELIMITER" ~ PUSH(hchar) ~
"*BUS_DELIMITER" ~ PUSH(prefix_bus_delim_char) ~ PUSH(suffix_bus_delim_char) ~
"*T_UNIT" ~ float ~ unit_type ~
"*C_UNIT" ~ float ~ unit_type ~
"*R_UNIT" ~ float ~ unit_type ~
"*L_UNIT" ~ float ~ unit_type
}
header_design_flow = { str+ }
hier_delim = _{ &hchar ~ PEEK[0..1] }
pin_delim = _{ &hchar ~ PEEK[1..2] }
prefix_bus_delim = _{ &prefix_bus_delim_char ~ PEEK[2..3] }
suffix_bus_delim = _{ &suffix_bus_delim_char ~ PEEK[3..4] }
ident = @{ (ASCII_ALPHANUMERIC | "_" | ("\\" ~ ANY))+ }
bit_id = _{ prefix_bus_delim ~ int ~ suffix_bus_delim } // flattened to int
ident_path = {
hier_delim? ~ ident ~ (hier_delim ~ ident)* ~
bit_id?
} // contains hierarchy and bit. e.g., [/]top/hier1/u3[0]. NO macro pin `:a`.
unit_type = {
^"NS" | ^"PS"
| ^"PF" | ^"FF"
| ^"OHM" | ^"KOHM"
| ^"HENRY" | ^"MH" | ^"UH"
}
name_map = { "*NAME_MAP" ~ name_map_entry* }
name_map_entry = {
("*" ~ int) ~ ident_path
}
// with bit_id: net or port names. w/o bit_id: can be pin prefix.
// `physical_name` omitted.
ident_path_m = ${ ("*" ~ int) | ident_path }
direction = { "O" | "I" | "B" }
top_ports = { "*PORTS" ~ top_ports_entry* }
top_ports_entry = { ident_path_m ~ direction ~ conn_attr }
conn_attr = { (ca_coords | ca_cap_load | ca_slew | ca_driving_cell)* }
ca_coords = ${ "*C" ~ WHITESPACE+ ~ float ~ WHITESPACE+ ~ float }
ca_cap_load = ${ "*L" ~ WHITESPACE+ ~ par_value }
ca_slew = ${ "*S" ~ WHITESPACE+ ~ par_value ~ WHITESPACE+ ~ par_value }
ca_driving_cell = ${ "*D" ~ WHITESPACE+ ~ ident }
parasitic_net = {
"*D_NET" ~ ident_path_m ~ float ~
"*CONN" ~ net_conn* ~
"*CAP" ~ net_cap* ~
"*RES" ~ net_res* ~
"*END"
}
net_conn = {
(net_conn_port | net_conn_intr) ~ direction ~ conn_attr
}
net_conn_port = { "*P" ~ ident_path_m }
net_conn_intr = { "*I" ~ ident_path_m ~ pin_delim ~ ident ~ bit_id? }
net_name_ref = { ident_path_m ~ (pin_delim ~ ident ~ bit_id?)? }
net_cap = { int ~ net_name_ref ~ (par_value | net_name_ref ~ par_value) }
// we must try matching a value first, as some values can be parsed
// as idents as well... e.g., 20-aes256.spef:L393759:
// ```
// 2 *83544:DIODE 0
// 3 *375415:A 5.75436e-05
// ```
// sensitivity omitted
net_res = { int ~ net_name_ref ~ net_name_ref ~ par_value }
// sensitivity omitted