├── .gitignore ├── example ├── .cargo │ └── config.toml ├── Cargo.toml └── src │ └── main.rs ├── Cargo.toml ├── ulp_macro ├── Cargo.toml └── src │ ├── lib.rs │ ├── parser.rs │ └── codegen.rs ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.xtensa-esp32-none-elf] 2 | runner = "espflash flash --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "xtensa-esp32-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ulp_macro = { path = "../ulp_macro" } 8 | panic-halt = "0.2" 9 | esp32-hal = "0.13.0" 10 | esp32 = "0.24.0" 11 | esp-println = { version = "0.5.0", features = ["esp32"] } 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ulp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [workspace] 8 | members = [ 9 | "example", 10 | "ulp_macro" 11 | ] 12 | 13 | [[bin]] 14 | name = "example" 15 | path = "main.rs" 16 | 17 | [dependencies] 18 | ulp_macro = { path = "ulp_macro" } 19 | -------------------------------------------------------------------------------- /ulp_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ulp_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | quote = "1.0" 11 | bitfield = "0.14.0" 12 | proc-macro2 = "1.0" 13 | litrs = "0.4.0" 14 | peg = "0.8.1" 15 | 16 | [dev-dependencies] 17 | ariadne = "0.2.0" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 FSM-ULP Rust Macro 2 | 3 | This is a Rust macro which enables writing ULP assembly in right in your Rust source files. 4 | 5 | Example 6 | 7 | ```Rust 8 | let ulp_code = ulp_asm!( 9 | " 10 | MOVE R0, data 11 | entry: 12 | LD R1, R0, 0 13 | ADD R1, R1, 1 14 | ST R1, R0, 0 15 | WAIT 0xffff 16 | JUMP entry 17 | 18 | data: 19 | .long 0 20 | " 21 | ); 22 | ``` 23 | 24 | Have a look at the `example` folder. 25 | -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate ulp_macro; 5 | use esp32_hal as hal; 6 | use esp_println::println; 7 | use hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, Delay}; 8 | use panic_halt as _; 9 | use ulp_macro::ulp_asm; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | let peripherals = Peripherals::take(); 14 | let system = peripherals.DPORT.split(); 15 | let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); 16 | 17 | let ulp_code = ulp_asm!( 18 | " 19 | MOVE R0, data 20 | entry: 21 | LD R1, R0, 0 22 | ADD R1, R1, 1 23 | ST R1, R0, 0 24 | WAIT 0xffff 25 | JUMP entry 26 | 27 | data: 28 | .long 0 29 | " 30 | ); 31 | 32 | unsafe { 33 | // ideally this should be in the HAL 34 | let sens = &*esp32::SENS::PTR; 35 | let rtc = &*esp32::RTC_CNTL::PTR; 36 | 37 | rtc.state0.write(|w| w.ulp_cp_slp_timer_en().clear_bit()); 38 | sens.sar_start_force.write(|w| w.pc_init().bits(0)); 39 | sens.sar_start_force 40 | .write(|w| w.ulp_cp_force_start_top().clear_bit()); 41 | rtc.timer5.write(|w| w.min_slp_val().bits(2)); 42 | } 43 | 44 | // load code to RTC ram 45 | ulp_code.load(); 46 | 47 | println!("Hello world! Hello ULP!"); 48 | 49 | // getter and setters are named as the label with a prefix 50 | println!("data is {}", ulp_code.get_data()); 51 | unsafe { 52 | // ideally this should be in the HAL 53 | let rtc = &*esp32::RTC_CNTL::PTR; 54 | rtc.state0.write(|w| w.ulp_cp_slp_timer_en().set_bit()); 55 | } 56 | 57 | let mut delay = Delay::new(&clocks); 58 | loop { 59 | delay.delay_ms(500u32); 60 | println!("data is {}", ulp_code.get_data()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ulp_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_span)] 2 | 3 | use litrs::StringLit; 4 | use proc_macro::{self, TokenStream}; 5 | use quote::format_ident; 6 | use quote::quote; 7 | 8 | mod parser; 9 | use parser::parse; 10 | use quote::quote_spanned; 11 | 12 | mod codegen; 13 | use crate::codegen::create_codegen; 14 | use crate::codegen::CodeGen; 15 | 16 | #[proc_macro] 17 | pub fn ulp_asm(input: TokenStream) -> TokenStream { 18 | let first_token = input.into_iter().next().expect("no input"); 19 | let arg = StringLit::try_from(&first_token).expect("Expecting a string argument"); 20 | let src = arg.value(); 21 | 22 | let ast = parse(src); 23 | match ast { 24 | Ok(ast) => { 25 | let code = create_codegen().generate(ast); 26 | 27 | match code { 28 | Ok((code, labels)) => { 29 | let code_len = code.len(); 30 | 31 | let mut accessors = Vec::new(); 32 | for lbl in labels { 33 | let getter_name = format_ident!("get_{}", lbl.name); 34 | let setter_name = format_ident!("set_{}", lbl.name); 35 | let address = lbl.address; 36 | 37 | accessors.push(quote! { 38 | fn #getter_name(&self) -> u16 { 39 | unsafe {(((#address + 0x5000_0000) as *const u32).read_volatile() & 0xffff) as u16} 40 | } 41 | 42 | fn #setter_name(&self, value: u16) { 43 | unsafe {((#address + 0x5000_0000) as *mut u32).write_volatile(value as u32)} 44 | } 45 | 46 | }); 47 | } 48 | 49 | let tokens = quote! { 50 | { 51 | struct _Ulp { 52 | code: [u8; #code_len], 53 | } 54 | 55 | impl _Ulp { 56 | #(#accessors)* 57 | 58 | fn load(&self) { 59 | unsafe { 60 | core::ptr::copy_nonoverlapping( 61 | self.code.as_ptr() as *const u8, 62 | 0x5000_0000 as *mut u8, 63 | #code_len as usize 64 | ); 65 | } 66 | } 67 | } 68 | 69 | _Ulp { 70 | code: [ #(#code),* ] 71 | } 72 | } 73 | }; 74 | 75 | tokens.into() 76 | } 77 | Err(error) => { 78 | let error_msg = format!("{:?}", error); 79 | let span = first_token.span().into(); 80 | let tokens = quote_spanned! {span=> compile_error!(#error_msg)}; 81 | tokens.into() 82 | } 83 | } 84 | } 85 | Err(error) => { 86 | let error_msg = error.to_string(); 87 | let span = match first_token { 88 | proc_macro::TokenTree::Group(_) => todo!(), 89 | proc_macro::TokenTree::Ident(_) => todo!(), 90 | proc_macro::TokenTree::Punct(_) => todo!(), 91 | proc_macro::TokenTree::Literal(ref lit) => lit 92 | .subspan(error.location.offset..(error.location.offset + 1)) 93 | .unwrap_or_else(|| first_token.span()), 94 | } 95 | .into(); 96 | 97 | let tokens = quote_spanned! {span=> compile_error!(#error_msg)}; 98 | tokens.into() 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.70" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" 19 | 20 | [[package]] 21 | name = "ariadne" 22 | version = "0.2.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702" 25 | dependencies = [ 26 | "unicode-width", 27 | "yansi", 28 | ] 29 | 30 | [[package]] 31 | name = "autocfg" 32 | version = "1.1.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 35 | 36 | [[package]] 37 | name = "bare-metal" 38 | version = "1.0.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 41 | 42 | [[package]] 43 | name = "basic-toml" 44 | version = "0.1.2" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" 47 | dependencies = [ 48 | "serde", 49 | ] 50 | 51 | [[package]] 52 | name = "bitfield" 53 | version = "0.14.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" 56 | 57 | [[package]] 58 | name = "bitflags" 59 | version = "2.3.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" 62 | 63 | [[package]] 64 | name = "cfg-if" 65 | version = "1.0.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 68 | 69 | [[package]] 70 | name = "core-isa-parser" 71 | version = "0.2.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "23ec98e54b735872e54b2335c2e5a5c7fa7d9c3bfd45500f75280f84089a0083" 74 | dependencies = [ 75 | "anyhow", 76 | "enum-as-inner", 77 | "regex", 78 | "strum", 79 | "strum_macros", 80 | ] 81 | 82 | [[package]] 83 | name = "critical-section" 84 | version = "1.1.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" 87 | 88 | [[package]] 89 | name = "darling" 90 | version = "0.20.3" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" 93 | dependencies = [ 94 | "darling_core", 95 | "darling_macro", 96 | ] 97 | 98 | [[package]] 99 | name = "darling_core" 100 | version = "0.20.3" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" 103 | dependencies = [ 104 | "fnv", 105 | "ident_case", 106 | "proc-macro2", 107 | "quote", 108 | "strsim", 109 | "syn 2.0.26", 110 | ] 111 | 112 | [[package]] 113 | name = "darling_macro" 114 | version = "0.20.3" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" 117 | dependencies = [ 118 | "darling_core", 119 | "quote", 120 | "syn 2.0.26", 121 | ] 122 | 123 | [[package]] 124 | name = "embedded-dma" 125 | version = "0.2.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 128 | dependencies = [ 129 | "stable_deref_trait", 130 | ] 131 | 132 | [[package]] 133 | name = "embedded-hal" 134 | version = "0.2.7" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 137 | dependencies = [ 138 | "nb 0.1.3", 139 | "void", 140 | ] 141 | 142 | [[package]] 143 | name = "enum-as-inner" 144 | version = "0.4.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" 147 | dependencies = [ 148 | "heck", 149 | "proc-macro2", 150 | "quote", 151 | "syn 1.0.109", 152 | ] 153 | 154 | [[package]] 155 | name = "esp-hal-common" 156 | version = "0.10.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "05d4498ddbbbf9a21e64f9269d2b4dd4059c34863ff54c702fb99e435f967767" 159 | dependencies = [ 160 | "basic-toml", 161 | "bitflags", 162 | "cfg-if", 163 | "critical-section", 164 | "embedded-dma", 165 | "embedded-hal", 166 | "esp-hal-procmacros", 167 | "esp32", 168 | "fugit", 169 | "lock_api", 170 | "log", 171 | "nb 1.1.0", 172 | "paste", 173 | "serde", 174 | "strum", 175 | "void", 176 | "xtensa-lx", 177 | "xtensa-lx-rt", 178 | ] 179 | 180 | [[package]] 181 | name = "esp-hal-procmacros" 182 | version = "0.6.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "042d5a1ef0e01d6de045972779e4aced3a10e6170169a5cb2de7bef31802e28a" 185 | dependencies = [ 186 | "darling", 187 | "proc-macro-crate", 188 | "proc-macro-error", 189 | "proc-macro2", 190 | "quote", 191 | "syn 2.0.26", 192 | ] 193 | 194 | [[package]] 195 | name = "esp-println" 196 | version = "0.5.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "af6a511d37dba5fb8f01bf5485bc619a1a1959e1aaf666a7597df8fe615a0816" 199 | dependencies = [ 200 | "critical-section", 201 | ] 202 | 203 | [[package]] 204 | name = "esp32" 205 | version = "0.24.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "4e0a6cd6fdb2f8fd98e65d12c1ccc24d3410def1a8a9b660a64d15dfe70b6857" 208 | dependencies = [ 209 | "critical-section", 210 | "vcell", 211 | "xtensa-lx", 212 | ] 213 | 214 | [[package]] 215 | name = "esp32-hal" 216 | version = "0.13.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "769fa34985651980dd337a5784285a24cd7f122700f3d5c4a027d4401b028aa2" 219 | dependencies = [ 220 | "embedded-hal", 221 | "esp-hal-common", 222 | ] 223 | 224 | [[package]] 225 | name = "example" 226 | version = "0.1.0" 227 | dependencies = [ 228 | "esp-println", 229 | "esp32", 230 | "esp32-hal", 231 | "panic-halt", 232 | "ulp_macro", 233 | ] 234 | 235 | [[package]] 236 | name = "fnv" 237 | version = "1.0.7" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 240 | 241 | [[package]] 242 | name = "fugit" 243 | version = "0.3.7" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 246 | dependencies = [ 247 | "gcd", 248 | ] 249 | 250 | [[package]] 251 | name = "gcd" 252 | version = "2.3.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 255 | 256 | [[package]] 257 | name = "hashbrown" 258 | version = "0.12.3" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 261 | 262 | [[package]] 263 | name = "heck" 264 | version = "0.4.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 267 | 268 | [[package]] 269 | name = "ident_case" 270 | version = "1.0.1" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 273 | 274 | [[package]] 275 | name = "indexmap" 276 | version = "1.9.3" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 279 | dependencies = [ 280 | "autocfg", 281 | "hashbrown", 282 | ] 283 | 284 | [[package]] 285 | name = "litrs" 286 | version = "0.4.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "4f17c3668f3cc1132437cdadc93dab05e52d592f06948d3f64828430c36e4a70" 289 | dependencies = [ 290 | "proc-macro2", 291 | ] 292 | 293 | [[package]] 294 | name = "lock_api" 295 | version = "0.4.10" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 298 | dependencies = [ 299 | "autocfg", 300 | "scopeguard", 301 | ] 302 | 303 | [[package]] 304 | name = "log" 305 | version = "0.4.18" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" 308 | 309 | [[package]] 310 | name = "memchr" 311 | version = "2.5.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 314 | 315 | [[package]] 316 | name = "minijinja" 317 | version = "0.15.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "359c4820413be7706e93999171652e140578384f85faac14cb22d350bd0fbabf" 320 | dependencies = [ 321 | "serde", 322 | ] 323 | 324 | [[package]] 325 | name = "mutex-trait" 326 | version = "0.2.0" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" 329 | 330 | [[package]] 331 | name = "nb" 332 | version = "0.1.3" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 335 | dependencies = [ 336 | "nb 1.1.0", 337 | ] 338 | 339 | [[package]] 340 | name = "nb" 341 | version = "1.1.0" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 344 | 345 | [[package]] 346 | name = "once_cell" 347 | version = "1.17.1" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 350 | 351 | [[package]] 352 | name = "panic-halt" 353 | version = "0.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" 356 | 357 | [[package]] 358 | name = "paste" 359 | version = "1.0.12" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 362 | 363 | [[package]] 364 | name = "peg" 365 | version = "0.8.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554" 368 | dependencies = [ 369 | "peg-macros", 370 | "peg-runtime", 371 | ] 372 | 373 | [[package]] 374 | name = "peg-macros" 375 | version = "0.8.1" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b" 378 | dependencies = [ 379 | "peg-runtime", 380 | "proc-macro2", 381 | "quote", 382 | ] 383 | 384 | [[package]] 385 | name = "peg-runtime" 386 | version = "0.8.1" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739" 389 | 390 | [[package]] 391 | name = "proc-macro-crate" 392 | version = "1.3.1" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 395 | dependencies = [ 396 | "once_cell", 397 | "toml_edit", 398 | ] 399 | 400 | [[package]] 401 | name = "proc-macro-error" 402 | version = "1.0.4" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 405 | dependencies = [ 406 | "proc-macro-error-attr", 407 | "proc-macro2", 408 | "quote", 409 | "syn 1.0.109", 410 | "version_check", 411 | ] 412 | 413 | [[package]] 414 | name = "proc-macro-error-attr" 415 | version = "1.0.4" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 418 | dependencies = [ 419 | "proc-macro2", 420 | "quote", 421 | "version_check", 422 | ] 423 | 424 | [[package]] 425 | name = "proc-macro2" 426 | version = "1.0.66" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 429 | dependencies = [ 430 | "unicode-ident", 431 | ] 432 | 433 | [[package]] 434 | name = "quote" 435 | version = "1.0.31" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" 438 | dependencies = [ 439 | "proc-macro2", 440 | ] 441 | 442 | [[package]] 443 | name = "r0" 444 | version = "1.0.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" 447 | 448 | [[package]] 449 | name = "regex" 450 | version = "1.7.3" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" 453 | dependencies = [ 454 | "aho-corasick", 455 | "memchr", 456 | "regex-syntax", 457 | ] 458 | 459 | [[package]] 460 | name = "regex-syntax" 461 | version = "0.6.29" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 464 | 465 | [[package]] 466 | name = "rustversion" 467 | version = "1.0.12" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" 470 | 471 | [[package]] 472 | name = "scopeguard" 473 | version = "1.1.0" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 476 | 477 | [[package]] 478 | name = "serde" 479 | version = "1.0.173" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" 482 | dependencies = [ 483 | "serde_derive", 484 | ] 485 | 486 | [[package]] 487 | name = "serde_derive" 488 | version = "1.0.173" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49" 491 | dependencies = [ 492 | "proc-macro2", 493 | "quote", 494 | "syn 2.0.26", 495 | ] 496 | 497 | [[package]] 498 | name = "spin" 499 | version = "0.9.8" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 502 | dependencies = [ 503 | "lock_api", 504 | ] 505 | 506 | [[package]] 507 | name = "stable_deref_trait" 508 | version = "1.2.0" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 511 | 512 | [[package]] 513 | name = "strsim" 514 | version = "0.10.0" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 517 | 518 | [[package]] 519 | name = "strum" 520 | version = "0.24.1" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 523 | dependencies = [ 524 | "strum_macros", 525 | ] 526 | 527 | [[package]] 528 | name = "strum_macros" 529 | version = "0.24.3" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 532 | dependencies = [ 533 | "heck", 534 | "proc-macro2", 535 | "quote", 536 | "rustversion", 537 | "syn 1.0.109", 538 | ] 539 | 540 | [[package]] 541 | name = "syn" 542 | version = "1.0.109" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 545 | dependencies = [ 546 | "proc-macro2", 547 | "quote", 548 | "unicode-ident", 549 | ] 550 | 551 | [[package]] 552 | name = "syn" 553 | version = "2.0.26" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" 556 | dependencies = [ 557 | "proc-macro2", 558 | "quote", 559 | "unicode-ident", 560 | ] 561 | 562 | [[package]] 563 | name = "toml_datetime" 564 | version = "0.6.1" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" 567 | 568 | [[package]] 569 | name = "toml_edit" 570 | version = "0.19.8" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" 573 | dependencies = [ 574 | "indexmap", 575 | "toml_datetime", 576 | "winnow", 577 | ] 578 | 579 | [[package]] 580 | name = "ulp" 581 | version = "0.1.0" 582 | dependencies = [ 583 | "ulp_macro", 584 | ] 585 | 586 | [[package]] 587 | name = "ulp_macro" 588 | version = "0.1.0" 589 | dependencies = [ 590 | "ariadne", 591 | "bitfield", 592 | "litrs", 593 | "peg", 594 | "proc-macro2", 595 | "quote", 596 | ] 597 | 598 | [[package]] 599 | name = "unicode-ident" 600 | version = "1.0.8" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 603 | 604 | [[package]] 605 | name = "unicode-width" 606 | version = "0.1.10" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 609 | 610 | [[package]] 611 | name = "vcell" 612 | version = "0.1.3" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 615 | 616 | [[package]] 617 | name = "version_check" 618 | version = "0.9.4" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 621 | 622 | [[package]] 623 | name = "void" 624 | version = "1.0.2" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 627 | 628 | [[package]] 629 | name = "winnow" 630 | version = "0.4.1" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" 633 | dependencies = [ 634 | "memchr", 635 | ] 636 | 637 | [[package]] 638 | name = "xtensa-lx" 639 | version = "0.8.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "9490addc0a1edd86e571a9ed8063f33d8224f981e61bbf72279671ed0cb4bb7c" 642 | dependencies = [ 643 | "bare-metal", 644 | "mutex-trait", 645 | "spin", 646 | ] 647 | 648 | [[package]] 649 | name = "xtensa-lx-rt" 650 | version = "0.15.0" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "f68fea36eb8e2d5ff4c99d367527f4a56e484b16c5e84abf4195988bd845a3fc" 653 | dependencies = [ 654 | "bare-metal", 655 | "core-isa-parser", 656 | "minijinja", 657 | "r0", 658 | "xtensa-lx-rt-proc-macros", 659 | ] 660 | 661 | [[package]] 662 | name = "xtensa-lx-rt-proc-macros" 663 | version = "0.2.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "3283815e334d3e5d21868dc3c7140720b2a784238289b1127b67908c89404b79" 666 | dependencies = [ 667 | "proc-macro2", 668 | "quote", 669 | "syn 1.0.109", 670 | ] 671 | 672 | [[package]] 673 | name = "yansi" 674 | version = "0.5.1" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" 677 | -------------------------------------------------------------------------------- /ulp_macro/src/parser.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | use ariadne::{Label, Report, ReportKind, Source}; 3 | 4 | use peg::str::LineCol; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq)] 7 | pub(crate) enum Reg { 8 | R0 = 0, 9 | R1 = 1, 10 | R2 = 2, 11 | R3 = 3, 12 | } 13 | 14 | #[derive(Debug, Clone)] 15 | pub(crate) enum Value { 16 | Number(u32), 17 | Identifier(String), 18 | } 19 | 20 | #[derive(Debug, PartialEq, Clone, Copy)] 21 | pub(crate) enum Condition { 22 | Eq, 23 | Lt, 24 | Le, 25 | Gt, 26 | Ge, 27 | Ov, 28 | } 29 | 30 | #[derive(Debug, Clone)] 31 | pub(crate) enum Instruction { 32 | Move(Reg, Reg), 33 | MoveImmediate(Reg, Value), 34 | Store(Reg, Reg, Value), 35 | Load(Reg, Reg, Value), 36 | Add(Reg, Reg, Reg), 37 | AddImmediate(Reg, Reg, Value), 38 | Nop, 39 | Sub(Reg, Reg, Reg), 40 | SubImmediate(Reg, Reg, Value), 41 | And(Reg, Reg, Reg), 42 | AndImmediate(Reg, Reg, Value), 43 | Or(Reg, Reg, Reg), 44 | OrImmediate(Reg, Reg, Value), 45 | Lsh(Reg, Reg, Reg), 46 | LshImmediate(Reg, Reg, Value), 47 | Rsh(Reg, Reg, Reg), 48 | RshImmediate(Reg, Reg, Value), 49 | Jump(Reg), 50 | JumpImmediate(Value), 51 | JumpConditional(Reg, Condition), 52 | JumpConditionalImmediate(Value, Condition), 53 | JumpR(Value, Value, Condition), 54 | JumpS(Value, Value, Condition), 55 | StageRst, 56 | StageInc(Value), 57 | StageDec(Value), 58 | Halt, 59 | Wake, 60 | Sleep(Value), 61 | Wait(Value), 62 | Tsens(Reg, Value), 63 | Adc(Reg, Value, Value), 64 | I2cRd(Value, Value, Value, Value), 65 | I2cWr(Value, Value, Value, Value, Value), 66 | RegRd(Value, Value, Value), 67 | RegWr(Value, Value, Value, Value), 68 | Comment, 69 | Label(String), 70 | Set(String, Value), 71 | Long(Value), 72 | Global(String), 73 | } 74 | 75 | peg::parser! { 76 | grammar pparser() for str { 77 | rule i(literal: &'static str) 78 | = input:$([_]*<{literal.len()}>) 79 | {? if input.eq_ignore_ascii_case(literal) { Ok(()) } else { Err(literal) } } 80 | 81 | rule keyword_chars() = ['A'..='Z'|'0'..='9'] 82 | 83 | rule ws() = [' '|'\r'|'\n'] 84 | 85 | rule space() = [' '| '\t'] 86 | 87 | rule newline() = ['\r'|'\n'] 88 | 89 | rule eof() = ![_] 90 | 91 | rule any() = [' '] / ['\t'] / ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['.'] / ['ö'] / ['ä'] / [','] 92 | / ['ü'] / ['Ö'] / ['Ä'] / ['Ü'] / ['-'] / ['_'] / ['('] / [')'] / [','] / ['['] / [']'] / ['+'] / ['='] 93 | / ['&'] / ['|'] / ['<'] / ['>'] 94 | 95 | rule identifier_char() = ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['_'] 96 | 97 | rule register() -> Reg 98 | = s:$(i("R0") / i("R1") / i("R2") / i("R3")) { 99 | match s.to_uppercase().as_str() { 100 | "R0" => Reg::R0, 101 | "R1" => Reg::R1, 102 | "R2" => Reg::R2, 103 | "R3" => Reg::R3, 104 | _ => panic!() 105 | } 106 | } 107 | 108 | rule condition() -> Condition 109 | = s:$(i("EQ") / i("LT") / i("LE") / i("GT") / i("GE") / i("OV")) { 110 | match s.to_uppercase().as_str() { 111 | "EQ" => Condition::Eq, 112 | "LT" => Condition::Lt, 113 | "LE" => Condition::Le, 114 | "GT" => Condition::Gt, 115 | "GE" => Condition::Ge, 116 | "OV" => Condition::Ov, 117 | _ => panic!() 118 | } 119 | } 120 | 121 | rule number_hex() -> Value = "0x" s:$(['0'..='9'|'a'..='f'|'A'..='F']+) { 122 | Value::Number(u32::from_str_radix(s, 16).unwrap()) 123 | } 124 | 125 | rule number_bin() -> Value = "0b" s:$(['0'..='1']+) { 126 | Value::Number(u32::from_str_radix(s, 2).unwrap()) 127 | } 128 | 129 | rule number() -> Value = s:$(['0'..='9']+) { 130 | Value::Number(u32::from_str_radix(s, 10).unwrap()) 131 | } 132 | 133 | rule identifier() -> Value 134 | = s:$(identifier_char()*) { Value::Identifier(s.to_string()) } 135 | 136 | rule number_or_symbol() -> Value 137 | = v:(number_hex() / number_bin() / number() / identifier()) { 138 | v 139 | } 140 | 141 | rule move_instr() -> Instruction 142 | = i("MOVE")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())* { 143 | Instruction::Move(r0, r1) 144 | } 145 | 146 | rule move_immediate_instr() -> Instruction 147 | = i("MOVE")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 148 | Instruction::MoveImmediate(r0, l) 149 | } 150 | 151 | rule store_instr() -> Instruction 152 | = i("ST")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 153 | Instruction::Store(r0, r1, l) 154 | } 155 | 156 | rule load_instr() -> Instruction 157 | = i("LD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 158 | Instruction::Load(r0, r1, l) 159 | } 160 | 161 | rule add_immediate_instr() -> Instruction 162 | = i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 163 | Instruction::AddImmediate(r0, r1, l) 164 | } 165 | 166 | rule add_instr() -> Instruction 167 | = i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 168 | Instruction::Add(r0, r1, r3) 169 | } 170 | 171 | rule nop_instr() -> Instruction 172 | = i("NOP")(space())* { 173 | Instruction::Nop 174 | } 175 | 176 | rule sub_immediate_instr() -> Instruction 177 | = i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 178 | Instruction::SubImmediate(r0, r1, l) 179 | } 180 | 181 | rule sub_instr() -> Instruction 182 | = i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 183 | Instruction::Sub(r0, r1, r3) 184 | } 185 | 186 | rule and_immediate_instr() -> Instruction 187 | = i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 188 | Instruction::AndImmediate(r0, r1, l) 189 | } 190 | 191 | rule and_instr() -> Instruction 192 | = i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 193 | Instruction::And(r0, r1, r3) 194 | } 195 | 196 | rule or_immediate_instr() -> Instruction 197 | = i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 198 | Instruction::OrImmediate(r0, r1, l) 199 | } 200 | 201 | rule or_instr() -> Instruction 202 | = i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 203 | Instruction::Or(r0, r1, r3) 204 | } 205 | 206 | rule lsh_immediate_instr() -> Instruction 207 | = i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 208 | Instruction::LshImmediate(r0, r1, l) 209 | } 210 | 211 | rule lsh_instr() -> Instruction 212 | = i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 213 | Instruction::Lsh(r0, r1, r3) 214 | } 215 | 216 | rule rsh_immediate_instr() -> Instruction 217 | = i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 218 | Instruction::RshImmediate(r0, r1, l) 219 | } 220 | 221 | rule rsh_instr() -> Instruction 222 | = i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* { 223 | Instruction::Rsh(r0, r1, r3) 224 | } 225 | 226 | rule jump_instr() -> Instruction 227 | = i("JUMP")(space())+r0:(register())(space())* { 228 | Instruction::Jump(r0) 229 | } 230 | 231 | rule jump_immediate_instr() -> Instruction 232 | = i("JUMP")(space())+v:(number_or_symbol())(space())* { 233 | Instruction::JumpImmediate(v) 234 | } 235 | 236 | rule jump_conditional_instr() -> Instruction 237 | = i("JUMP")(space())+r0:(register())(space())*[','](space())*c:(condition())(space())* { 238 | Instruction::JumpConditional(r0, c) 239 | } 240 | 241 | rule jump_conditional_immediate_instr() -> Instruction 242 | = i("JUMP")(space())+v:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* { 243 | Instruction::JumpConditionalImmediate(v, c) 244 | } 245 | 246 | rule jumpr_conditional_instr() -> Instruction 247 | = i("JUMPR")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* { 248 | Instruction::JumpR(step, thr, c) 249 | } 250 | 251 | rule jumps_conditional_instr() -> Instruction 252 | = i("JUMPS")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* { 253 | Instruction::JumpS(step, thr, c) 254 | } 255 | 256 | rule stage_rst_instr() -> Instruction 257 | = i("STAGE_RST")(space())* { 258 | Instruction::StageRst 259 | } 260 | 261 | rule stage_inc_instr() -> Instruction 262 | = i("STAGE_INC")(space())+value:(number_or_symbol())(space())* { 263 | Instruction::StageInc(value) 264 | } 265 | 266 | rule stage_dec_instr() -> Instruction 267 | = i("STAGE_DEC")(space())+value:(number_or_symbol())(space())* { 268 | Instruction::StageDec(value) 269 | } 270 | 271 | rule halt_instr() -> Instruction 272 | = i("HALT")(space())* { 273 | Instruction::Halt 274 | } 275 | 276 | rule wake_instr() -> Instruction 277 | = i("WAKE")(space())* { 278 | Instruction::Wake 279 | } 280 | 281 | rule sleep_instr() -> Instruction 282 | = i("SLEEP")(space())+v:(number_or_symbol())(space())* { 283 | Instruction::Sleep(v) 284 | } 285 | 286 | rule wait_instr() -> Instruction 287 | = i("WAIT")(space())+v:(number_or_symbol())(space())* { 288 | Instruction::Wait(v) 289 | } 290 | 291 | rule tsens_instr() -> Instruction 292 | = i("TSENS")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* { 293 | Instruction::Tsens(r0, l) 294 | } 295 | 296 | rule adc_instr() -> Instruction 297 | = i("ADC")(space())+r0:(register())(space())*[','](space())*l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())* { 298 | Instruction::Adc(r0, l0, l1) 299 | } 300 | 301 | rule i2c_rd_instr() -> Instruction 302 | = i("I2C_RD")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space())* { 303 | Instruction::I2cRd(l0, l1, l2, l3) 304 | } 305 | 306 | rule i2c_wr_instr() -> Instruction 307 | = i("I2C_WR")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space())*[','](space())*l4:(number_or_symbol())(space())* { 308 | Instruction::I2cWr(l0, l1, l2, l3, l4) 309 | } 310 | 311 | rule reg_rd_instr() -> Instruction 312 | = i("REG_RD")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())* { 313 | Instruction::RegRd(l0, l1, l2) 314 | } 315 | 316 | rule reg_wr_instr() -> Instruction 317 | = i("REG_WR")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space()*) { 318 | Instruction::RegWr(l0, l1, l2, l3) 319 | } 320 | 321 | rule comment() -> Instruction = "//" any()* { Instruction::Comment } 322 | 323 | rule block_comment() -> Instruction = "/*" (any() / newline())* "*/" { Instruction::Comment } 324 | 325 | rule label() -> Instruction 326 | = s:$(identifier_char()*) ":"(space())* { 327 | Instruction::Label(s[..s.len()].to_string()) 328 | } 329 | 330 | rule set_pseudo_instr() -> Instruction 331 | = i(".set")(space())+label:(identifier())(space())*[','](space())*l:(number_or_symbol())(space())* { 332 | if let Value::Identifier(ident) = label { Instruction::Set(ident, l) } else { panic!() } 333 | } 334 | 335 | rule long_pseudo_instr() -> Instruction 336 | = i(".long")(space())+l:(number_or_symbol())(space())* { 337 | Instruction::Long(l) 338 | } 339 | 340 | rule global_pseudo_instr() -> Instruction 341 | = i(".global")(space())+label:(identifier())(space())* { 342 | if let Value::Identifier(ident) = label { Instruction::Global(ident) } else { panic!() } 343 | } 344 | 345 | rule instr() -> Instruction 346 | = ((space() / newline())*) c:( 347 | move_instr() / 348 | move_immediate_instr() / 349 | store_instr() / 350 | load_instr() / 351 | add_instr() / 352 | add_immediate_instr() / 353 | nop_instr() / 354 | sub_instr() / 355 | sub_immediate_instr() / 356 | and_instr() / 357 | and_immediate_instr() / 358 | or_instr() / 359 | or_immediate_instr() / 360 | lsh_instr() / 361 | lsh_immediate_instr() / 362 | rsh_instr() / 363 | rsh_immediate_instr() / 364 | jump_conditional_instr() / 365 | jump_conditional_immediate_instr() / 366 | jump_instr() / 367 | jump_immediate_instr() / 368 | jumpr_conditional_instr() / 369 | jumps_conditional_instr() / 370 | stage_rst_instr() / 371 | stage_inc_instr() / 372 | stage_dec_instr() / 373 | halt_instr() / 374 | wake_instr() / 375 | sleep_instr() / 376 | wait_instr() / 377 | tsens_instr() / 378 | adc_instr() / 379 | i2c_rd_instr() / 380 | i2c_wr_instr() / 381 | reg_rd_instr() / 382 | reg_wr_instr() / 383 | comment() / 384 | block_comment() / 385 | label() / 386 | set_pseudo_instr() / 387 | long_pseudo_instr() / 388 | global_pseudo_instr() 389 | ) ((space() / newline())*) { c } 390 | 391 | pub(crate) rule parse() -> Vec 392 | = ((newline()*) (ws()*)) c:(instr()) ** (newline()*) { 393 | c 394 | } 395 | } 396 | } 397 | 398 | pub(crate) fn parse(src: &str) -> Result, peg::error::ParseError> { 399 | pparser::parse(src) 400 | } 401 | 402 | #[cfg(test)] 403 | pub(crate) fn print_error(src: &str, error: &peg::error::ParseError) { 404 | Report::build(ReportKind::Error, (), 34) 405 | .with_message(error.to_string()) 406 | .with_label( 407 | Label::new(error.location.offset..(error.location.offset + 1)) 408 | .with_message("Error here"), 409 | ) 410 | .finish() 411 | .print(Source::from(src)) 412 | .unwrap(); 413 | } 414 | 415 | #[cfg(test)] 416 | fn parse_and_print_error(src: &str) -> Result, peg::error::ParseError> { 417 | let res = parse(src); 418 | 419 | match &res { 420 | Ok(_) => (), 421 | Err(err) => print_error(src, &err), 422 | } 423 | 424 | println!("{:?}", res); 425 | res 426 | } 427 | 428 | #[test] 429 | fn test_simple() { 430 | let res = parse_and_print_error("\r\n MOVE R0, R1\r\n ADD R3, R3, 1 //Zeiger auf nächsten 32-Bit-Speicherplatz\n MOVE R1,R2 "); 431 | assert!(res.is_ok()) 432 | } 433 | 434 | #[test] 435 | fn test_simple2() { 436 | let res = parse_and_print_error( 437 | "ST R0, R3, 0 //2.Element über Offset adressieren\nADD R3, R3, 1 ", 438 | ); 439 | assert!(res.is_ok()) 440 | } 441 | 442 | #[test] 443 | fn test_simple2_identifier() { 444 | let res = 445 | parse("ST R0, R3, myvalue //2.Element über Offset adressieren\nADD R3, R3, VALUE "); 446 | assert!(res.is_ok()) 447 | } 448 | 449 | #[test] 450 | fn test_numbers() { 451 | let res = parse_and_print_error( 452 | "ST R0, R3, 0 \n ST R0, R3, 0xabcd \n ST R0, R3, 0b1010111011 \n \n", 453 | ); 454 | assert!(res.is_ok()) 455 | } 456 | 457 | #[test] 458 | fn test_comments() { 459 | let res = parse_and_print_error("move R0, R1 // test\n\nMove R2,R3\n"); 460 | assert!(res.is_ok()) 461 | } 462 | 463 | #[test] 464 | fn test2() { 465 | let res = parse_and_print_error( 466 | " 467 | label: 468 | MOVE R3, 555 // Some Comment 469 | MOVE R0, 1234 470 | ST R0, R3, 0 471 | 472 | MOVE R0, 5678 473 | 474 | ST R0, R3, 4 //2.Element über Offset adressieren 475 | //oder 476 | ADD R3, R3, 1 //Zeiger auf nächsten 32-Bit-Speicherplatz 477 | ST R0, R3, 0 //2.Element mit Nulloffset adressieren 478 | ST R0, R3, 0 //2.Element mit Nulloffset adressieren 479 | JUMP label 480 | ", 481 | ); 482 | 483 | assert!(res.is_ok()) 484 | } 485 | 486 | #[test] 487 | fn parse_complex_thing() { 488 | let res = parse_and_print_error( 489 | "is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit 490 | AND r0, r0, 1 491 | JUMP is_rdy_for_wakeup, eq // Retry until the bit is set 492 | WAKE // Trigger wake up 493 | REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN) 494 | HALT // Stop the ULP program 495 | // After these instructions, SoC will wake up, 496 | // and ULP will not run again until started by the main program.", 497 | ); 498 | 499 | assert!(res.is_ok()) 500 | } 501 | 502 | #[test] 503 | fn parse_others() { 504 | let res = parse_and_print_error( 505 | "is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit 506 | .set foo, 0x234 507 | .set foo, 234 508 | .set foo2, foo 509 | .global my_label 510 | // After these instructions, SoC will wake up, 511 | // and ULP will not run again until started by the main program. 512 | 513 | JUMPR label, 1, GE 514 | JUMPS label, 1, GE 515 | 516 | some_more_stuff: 517 | move r1, r0 518 | jump p1_status_changed, eq 519 | 520 | move r3, p1_status 521 | rsh r0, r1, 7 522 | and r0, r0, 1 523 | st r0, r3, 0 524 | 525 | move r3, p2_status 526 | rsh r0, r1, 8 527 | and r0, r0, 1 528 | st r0, r3, 0 529 | 530 | move r3, p3_status 531 | rsh r0, r1, 9 532 | and r0, r0, 1 533 | st r0, r3, 0 534 | // check if p1 status changed 535 | rsh r0, r1, 7 536 | and r0, r0, 1 537 | move r3, p1_status_next 538 | ld r3, r3, 0 539 | add r3, r0, r3 540 | and r3, r3, 1 541 | jump p1_status_changed, eq 542 | // check if p2 status changed 543 | rsh r0, r1, 8 544 | and r0, r0, 1 545 | move r3, p2_status_next 546 | ld r3, r3, 0 547 | add r3, r0, r3 548 | and r3, r3, 1 549 | jump p2_status_changed, eq 550 | 551 | /* check if p3 status changed */ 552 | rsh r0, r1, 9 553 | and r0, r0, 1 554 | move r3, p3_status_next 555 | ld r3, r3, 0 556 | add r3, r0, r3 557 | and r3, r3, 1 558 | jump p3_status_changed, eq 559 | 560 | /* 561 | multi line comments 562 | */ 563 | 564 | halt 565 | 566 | ADC R1, 0, 1 567 | TSENS R1, 1000 568 | 569 | REG_RD 0x120, 7, 4 570 | REG_WR 0x120, 7, 0, 0x10 571 | I2C_WR 0x20, 0x33, 7, 0, 1 572 | I2C_RD 0x10, 7, 0, 0 573 | 574 | WAIT 10 575 | SLEEP 1 576 | 577 | label: 578 | .long 0 579 | ", 580 | ); 581 | 582 | assert!(res.is_ok()) 583 | } 584 | -------------------------------------------------------------------------------- /ulp_macro/src/codegen.rs: -------------------------------------------------------------------------------- 1 | use bitfield::bitfield; 2 | use std::collections::HashMap; 3 | 4 | use crate::parser::{Condition, Instruction, Reg, Value}; 5 | 6 | #[derive(Debug)] 7 | pub(crate) enum CodeGenError { 8 | GenericError(String), 9 | } 10 | 11 | #[derive(Debug, Clone)] 12 | pub(crate) struct Label { 13 | pub(crate) name: String, 14 | pub(crate) address: u32, 15 | } 16 | 17 | pub(crate) trait CodeGen { 18 | fn generate(&self, ast: Vec) -> Result<(Vec, Vec