Initial Commit Test 1
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
103
Cargo.lock
generated
Normal file
103
Cargo.lock
generated
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ariadne"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peg"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477"
|
||||||
|
dependencies = [
|
||||||
|
"peg-macros",
|
||||||
|
"peg-runtime",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peg-macros"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71"
|
||||||
|
dependencies = [
|
||||||
|
"peg-runtime",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peg-runtime"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ulp_macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ariadne",
|
||||||
|
"bitfield",
|
||||||
|
"litrs",
|
||||||
|
"peg",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "ulp_macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0"
|
||||||
|
bitfield = "0.14.0"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
litrs = "0.4.0"
|
||||||
|
peg = "0.8.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
ariadne = "0.2.0"
|
||||||
1504
src/codegen.rs
Normal file
1504
src/codegen.rs
Normal file
File diff suppressed because it is too large
Load Diff
101
src/lib.rs
Normal file
101
src/lib.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#![feature(proc_macro_span)]
|
||||||
|
|
||||||
|
use litrs::StringLit;
|
||||||
|
use proc_macro::{self, TokenStream};
|
||||||
|
use quote::format_ident;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
mod parser;
|
||||||
|
use parser::parse;
|
||||||
|
use quote::quote_spanned;
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
|
use crate::codegen::create_codegen;
|
||||||
|
use crate::codegen::CodeGen;
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn ulp_asm(input: TokenStream) -> TokenStream {
|
||||||
|
let first_token = input.into_iter().next().expect("no input");
|
||||||
|
let arg = StringLit::try_from(&first_token).expect("Expecting a string argument");
|
||||||
|
let src = arg.value();
|
||||||
|
|
||||||
|
let ast = parse(src);
|
||||||
|
match ast {
|
||||||
|
Ok(ast) => {
|
||||||
|
let code = create_codegen().generate(ast);
|
||||||
|
|
||||||
|
match code {
|
||||||
|
Ok((code, labels)) => {
|
||||||
|
let code_len = code.len();
|
||||||
|
|
||||||
|
let mut accessors = Vec::new();
|
||||||
|
for lbl in labels {
|
||||||
|
let getter_name = format_ident!("get_{}", lbl.name);
|
||||||
|
let setter_name = format_ident!("set_{}", lbl.name);
|
||||||
|
let address = lbl.address;
|
||||||
|
|
||||||
|
accessors.push(quote! {
|
||||||
|
fn #getter_name(&self) -> u16 {
|
||||||
|
unsafe {(((#address + 0x5000_0000) as *const u32).read_volatile() & 0xffff) as u16}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn #setter_name(&self, value: u16) {
|
||||||
|
unsafe {((#address + 0x5000_0000) as *mut u32).write_volatile(value as u32)}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = quote! {
|
||||||
|
{
|
||||||
|
struct _Ulp {
|
||||||
|
code: [u8; #code_len],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl _Ulp {
|
||||||
|
#(#accessors)*
|
||||||
|
|
||||||
|
fn load(&self) {
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
self.code.as_ptr() as *const u8,
|
||||||
|
0x5000_0000 as *mut u8,
|
||||||
|
#code_len as usize
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_Ulp {
|
||||||
|
code: [ #(#code),* ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.into()
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
let error_msg = format!("{:?}", error);
|
||||||
|
let span = first_token.span().into();
|
||||||
|
let tokens = quote_spanned! {span=> compile_error!(#error_msg)};
|
||||||
|
tokens.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
let error_msg = error.to_string();
|
||||||
|
let span = match first_token {
|
||||||
|
proc_macro::TokenTree::Group(_) => todo!(),
|
||||||
|
proc_macro::TokenTree::Ident(_) => todo!(),
|
||||||
|
proc_macro::TokenTree::Punct(_) => todo!(),
|
||||||
|
proc_macro::TokenTree::Literal(ref lit) => lit
|
||||||
|
.subspan(error.location.offset..(error.location.offset + 1))
|
||||||
|
.unwrap_or_else(|| first_token.span()),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let tokens = quote_spanned! {span=> compile_error!(#error_msg)};
|
||||||
|
tokens.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
583
src/parser.rs
Normal file
583
src/parser.rs
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
use ariadne::{Label, Report, ReportKind, Source};
|
||||||
|
|
||||||
|
use peg::str::LineCol;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub(crate) enum Reg {
|
||||||
|
R0 = 0,
|
||||||
|
R1 = 1,
|
||||||
|
R2 = 2,
|
||||||
|
R3 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum Value {
|
||||||
|
Number(u32),
|
||||||
|
Identifier(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub(crate) enum Condition {
|
||||||
|
Eq,
|
||||||
|
Lt,
|
||||||
|
Le,
|
||||||
|
Gt,
|
||||||
|
Ge,
|
||||||
|
Ov,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum Instruction {
|
||||||
|
Move(Reg, Reg),
|
||||||
|
MoveImmediate(Reg, Value),
|
||||||
|
Store(Reg, Reg, Value),
|
||||||
|
Load(Reg, Reg, Value),
|
||||||
|
Add(Reg, Reg, Reg),
|
||||||
|
AddImmediate(Reg, Reg, Value),
|
||||||
|
Nop,
|
||||||
|
Sub(Reg, Reg, Reg),
|
||||||
|
SubImmediate(Reg, Reg, Value),
|
||||||
|
And(Reg, Reg, Reg),
|
||||||
|
AndImmediate(Reg, Reg, Value),
|
||||||
|
Or(Reg, Reg, Reg),
|
||||||
|
OrImmediate(Reg, Reg, Value),
|
||||||
|
Lsh(Reg, Reg, Reg),
|
||||||
|
LshImmediate(Reg, Reg, Value),
|
||||||
|
Rsh(Reg, Reg, Reg),
|
||||||
|
RshImmediate(Reg, Reg, Value),
|
||||||
|
Jump(Reg),
|
||||||
|
JumpImmediate(Value),
|
||||||
|
JumpConditional(Reg, Condition),
|
||||||
|
JumpConditionalImmediate(Value, Condition),
|
||||||
|
JumpR(Value, Value, Condition),
|
||||||
|
JumpS(Value, Value, Condition),
|
||||||
|
StageRst,
|
||||||
|
StageInc(Value),
|
||||||
|
StageDec(Value),
|
||||||
|
Halt,
|
||||||
|
Wake,
|
||||||
|
Sleep(Value),
|
||||||
|
Wait(Value),
|
||||||
|
Tsens(Reg, Value),
|
||||||
|
Adc(Reg, Value, Value),
|
||||||
|
I2cRd(Value, Value, Value, Value),
|
||||||
|
I2cWr(Value, Value, Value, Value, Value),
|
||||||
|
RegRd(Value, Value, Value),
|
||||||
|
RegWr(Value, Value, Value, Value),
|
||||||
|
Comment,
|
||||||
|
Label(String),
|
||||||
|
Set(String, Value),
|
||||||
|
Long(Value),
|
||||||
|
Global(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
peg::parser! {
|
||||||
|
grammar pparser() for str {
|
||||||
|
rule i(literal: &'static str)
|
||||||
|
= input:$([_]*<{literal.len()}>)
|
||||||
|
{? if input.eq_ignore_ascii_case(literal) { Ok(()) } else { Err(literal) } }
|
||||||
|
|
||||||
|
rule keyword_chars() = ['A'..='Z'|'0'..='9']
|
||||||
|
|
||||||
|
rule ws() = [' '|'\r'|'\n']
|
||||||
|
|
||||||
|
rule space() = [' '| '\t']
|
||||||
|
|
||||||
|
rule newline() = ['\r'|'\n']
|
||||||
|
|
||||||
|
rule eof() = ![_]
|
||||||
|
|
||||||
|
rule any() = [' '] / ['\t'] / ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['.'] / ['ö'] / ['ä'] / [',']
|
||||||
|
/ ['ü'] / ['Ö'] / ['Ä'] / ['Ü'] / ['-'] / ['_'] / ['('] / [')'] / [','] / ['['] / [']'] / ['+'] / ['=']
|
||||||
|
/ ['&'] / ['|'] / ['<'] / ['>']
|
||||||
|
|
||||||
|
rule identifier_char() = ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['_']
|
||||||
|
|
||||||
|
rule register() -> Reg
|
||||||
|
= s:$(i("R0") / i("R1") / i("R2") / i("R3")) {
|
||||||
|
match s.to_uppercase().as_str() {
|
||||||
|
"R0" => Reg::R0,
|
||||||
|
"R1" => Reg::R1,
|
||||||
|
"R2" => Reg::R2,
|
||||||
|
"R3" => Reg::R3,
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule condition() -> Condition
|
||||||
|
= s:$(i("EQ") / i("LT") / i("LE") / i("GT") / i("GE") / i("OV")) {
|
||||||
|
match s.to_uppercase().as_str() {
|
||||||
|
"EQ" => Condition::Eq,
|
||||||
|
"LT" => Condition::Lt,
|
||||||
|
"LE" => Condition::Le,
|
||||||
|
"GT" => Condition::Gt,
|
||||||
|
"GE" => Condition::Ge,
|
||||||
|
"OV" => Condition::Ov,
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule number_hex() -> Value = "0x" s:$(['0'..='9'|'a'..='f'|'A'..='F']+) {
|
||||||
|
Value::Number(u32::from_str_radix(s, 16).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
rule number_bin() -> Value = "0b" s:$(['0'..='1']+) {
|
||||||
|
Value::Number(u32::from_str_radix(s, 2).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
rule number() -> Value = s:$(['0'..='9']+) {
|
||||||
|
Value::Number(u32::from_str_radix(s, 10).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
rule identifier() -> Value
|
||||||
|
= s:$(identifier_char()*) { Value::Identifier(s.to_string()) }
|
||||||
|
|
||||||
|
rule number_or_symbol() -> Value
|
||||||
|
= v:(number_hex() / number_bin() / number() / identifier()) {
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
rule move_instr() -> Instruction
|
||||||
|
= i("MOVE")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())* {
|
||||||
|
Instruction::Move(r0, r1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule move_immediate_instr() -> Instruction
|
||||||
|
= i("MOVE")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::MoveImmediate(r0, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule store_instr() -> Instruction
|
||||||
|
= i("ST")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Store(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule load_instr() -> Instruction
|
||||||
|
= i("LD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Load(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule add_immediate_instr() -> Instruction
|
||||||
|
= i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::AddImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule add_instr() -> Instruction
|
||||||
|
= i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::Add(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule nop_instr() -> Instruction
|
||||||
|
= i("NOP")(space())* {
|
||||||
|
Instruction::Nop
|
||||||
|
}
|
||||||
|
|
||||||
|
rule sub_immediate_instr() -> Instruction
|
||||||
|
= i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::SubImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule sub_instr() -> Instruction
|
||||||
|
= i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::Sub(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule and_immediate_instr() -> Instruction
|
||||||
|
= i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::AndImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule and_instr() -> Instruction
|
||||||
|
= i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::And(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule or_immediate_instr() -> Instruction
|
||||||
|
= i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::OrImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule or_instr() -> Instruction
|
||||||
|
= i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::Or(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule lsh_immediate_instr() -> Instruction
|
||||||
|
= i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::LshImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule lsh_instr() -> Instruction
|
||||||
|
= i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::Lsh(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule rsh_immediate_instr() -> Instruction
|
||||||
|
= i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::RshImmediate(r0, r1, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule rsh_instr() -> Instruction
|
||||||
|
= i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
|
||||||
|
Instruction::Rsh(r0, r1, r3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jump_instr() -> Instruction
|
||||||
|
= i("JUMP")(space())+r0:(register())(space())* {
|
||||||
|
Instruction::Jump(r0)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jump_immediate_instr() -> Instruction
|
||||||
|
= i("JUMP")(space())+v:(number_or_symbol())(space())* {
|
||||||
|
Instruction::JumpImmediate(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jump_conditional_instr() -> Instruction
|
||||||
|
= i("JUMP")(space())+r0:(register())(space())*[','](space())*c:(condition())(space())* {
|
||||||
|
Instruction::JumpConditional(r0, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jump_conditional_immediate_instr() -> Instruction
|
||||||
|
= i("JUMP")(space())+v:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
|
||||||
|
Instruction::JumpConditionalImmediate(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jumpr_conditional_instr() -> Instruction
|
||||||
|
= i("JUMPR")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
|
||||||
|
Instruction::JumpR(step, thr, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule jumps_conditional_instr() -> Instruction
|
||||||
|
= i("JUMPS")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
|
||||||
|
Instruction::JumpS(step, thr, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule stage_rst_instr() -> Instruction
|
||||||
|
= i("STAGE_RST")(space())* {
|
||||||
|
Instruction::StageRst
|
||||||
|
}
|
||||||
|
|
||||||
|
rule stage_inc_instr() -> Instruction
|
||||||
|
= i("STAGE_INC")(space())+value:(number_or_symbol())(space())* {
|
||||||
|
Instruction::StageInc(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule stage_dec_instr() -> Instruction
|
||||||
|
= i("STAGE_DEC")(space())+value:(number_or_symbol())(space())* {
|
||||||
|
Instruction::StageDec(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule halt_instr() -> Instruction
|
||||||
|
= i("HALT")(space())* {
|
||||||
|
Instruction::Halt
|
||||||
|
}
|
||||||
|
|
||||||
|
rule wake_instr() -> Instruction
|
||||||
|
= i("WAKE")(space())* {
|
||||||
|
Instruction::Wake
|
||||||
|
}
|
||||||
|
|
||||||
|
rule sleep_instr() -> Instruction
|
||||||
|
= i("SLEEP")(space())+v:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Sleep(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule wait_instr() -> Instruction
|
||||||
|
= i("WAIT")(space())+v:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Wait(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule tsens_instr() -> Instruction
|
||||||
|
= i("TSENS")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Tsens(r0, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule adc_instr() -> Instruction
|
||||||
|
= i("ADC")(space())+r0:(register())(space())*[','](space())*l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Adc(r0, l0, l1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule i2c_rd_instr() -> Instruction
|
||||||
|
= 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())* {
|
||||||
|
Instruction::I2cRd(l0, l1, l2, l3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule i2c_wr_instr() -> Instruction
|
||||||
|
= 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())* {
|
||||||
|
Instruction::I2cWr(l0, l1, l2, l3, l4)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule reg_rd_instr() -> Instruction
|
||||||
|
= i("REG_RD")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())* {
|
||||||
|
Instruction::RegRd(l0, l1, l2)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule reg_wr_instr() -> Instruction
|
||||||
|
= 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()*) {
|
||||||
|
Instruction::RegWr(l0, l1, l2, l3)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule comment() -> Instruction = "//" any()* { Instruction::Comment }
|
||||||
|
|
||||||
|
rule block_comment() -> Instruction = "/*" (any() / newline())* "*/" { Instruction::Comment }
|
||||||
|
|
||||||
|
rule label() -> Instruction
|
||||||
|
= s:$(identifier_char()*) ":"(space())* {
|
||||||
|
Instruction::Label(s[..s.len()].to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
rule set_pseudo_instr() -> Instruction
|
||||||
|
= i(".set")(space())+label:(identifier())(space())*[','](space())*l:(number_or_symbol())(space())* {
|
||||||
|
if let Value::Identifier(ident) = label { Instruction::Set(ident, l) } else { panic!() }
|
||||||
|
}
|
||||||
|
|
||||||
|
rule long_pseudo_instr() -> Instruction
|
||||||
|
= i(".long")(space())+l:(number_or_symbol())(space())* {
|
||||||
|
Instruction::Long(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule global_pseudo_instr() -> Instruction
|
||||||
|
= i(".global")(space())+label:(identifier())(space())* {
|
||||||
|
if let Value::Identifier(ident) = label { Instruction::Global(ident) } else { panic!() }
|
||||||
|
}
|
||||||
|
|
||||||
|
rule instr() -> Instruction
|
||||||
|
= ((space() / newline())*) c:(
|
||||||
|
move_instr() /
|
||||||
|
move_immediate_instr() /
|
||||||
|
store_instr() /
|
||||||
|
load_instr() /
|
||||||
|
add_instr() /
|
||||||
|
add_immediate_instr() /
|
||||||
|
nop_instr() /
|
||||||
|
sub_instr() /
|
||||||
|
sub_immediate_instr() /
|
||||||
|
and_instr() /
|
||||||
|
and_immediate_instr() /
|
||||||
|
or_instr() /
|
||||||
|
or_immediate_instr() /
|
||||||
|
lsh_instr() /
|
||||||
|
lsh_immediate_instr() /
|
||||||
|
rsh_instr() /
|
||||||
|
rsh_immediate_instr() /
|
||||||
|
jump_conditional_instr() /
|
||||||
|
jump_conditional_immediate_instr() /
|
||||||
|
jump_instr() /
|
||||||
|
jump_immediate_instr() /
|
||||||
|
jumpr_conditional_instr() /
|
||||||
|
jumps_conditional_instr() /
|
||||||
|
stage_rst_instr() /
|
||||||
|
stage_inc_instr() /
|
||||||
|
stage_dec_instr() /
|
||||||
|
halt_instr() /
|
||||||
|
wake_instr() /
|
||||||
|
sleep_instr() /
|
||||||
|
wait_instr() /
|
||||||
|
tsens_instr() /
|
||||||
|
adc_instr() /
|
||||||
|
i2c_rd_instr() /
|
||||||
|
i2c_wr_instr() /
|
||||||
|
reg_rd_instr() /
|
||||||
|
reg_wr_instr() /
|
||||||
|
comment() /
|
||||||
|
block_comment() /
|
||||||
|
label() /
|
||||||
|
set_pseudo_instr() /
|
||||||
|
long_pseudo_instr() /
|
||||||
|
global_pseudo_instr()
|
||||||
|
) ((space() / newline())*) { c }
|
||||||
|
|
||||||
|
pub(crate) rule parse() -> Vec<Instruction>
|
||||||
|
= ((newline()*) (ws()*)) c:(instr()) ** (newline()*) {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse(src: &str) -> Result<Vec<Instruction>, peg::error::ParseError<LineCol>> {
|
||||||
|
pparser::parse(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn print_error(src: &str, error: &peg::error::ParseError<LineCol>) {
|
||||||
|
Report::build(ReportKind::Error, (), 34)
|
||||||
|
.with_message(error.to_string())
|
||||||
|
.with_label(
|
||||||
|
Label::new(error.location.offset..(error.location.offset + 1))
|
||||||
|
.with_message("Error here"),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.print(Source::from(src))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn parse_and_print_error(src: &str) -> Result<Vec<Instruction>, peg::error::ParseError<LineCol>> {
|
||||||
|
let res = parse(src);
|
||||||
|
|
||||||
|
match &res {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => print_error(src, &err),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple() {
|
||||||
|
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 ");
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple2() {
|
||||||
|
let res = parse_and_print_error(
|
||||||
|
"ST R0, R3, 0 //2.Element über Offset adressieren\nADD R3, R3, 1 ",
|
||||||
|
);
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple2_identifier() {
|
||||||
|
let res =
|
||||||
|
parse("ST R0, R3, myvalue //2.Element über Offset adressieren\nADD R3, R3, VALUE ");
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_numbers() {
|
||||||
|
let res = parse_and_print_error(
|
||||||
|
"ST R0, R3, 0 \n ST R0, R3, 0xabcd \n ST R0, R3, 0b1010111011 \n \n",
|
||||||
|
);
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comments() {
|
||||||
|
let res = parse_and_print_error("move R0, R1 // test\n\nMove R2,R3\n");
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test2() {
|
||||||
|
let res = parse_and_print_error(
|
||||||
|
"
|
||||||
|
label:
|
||||||
|
MOVE R3, 555 // Some Comment
|
||||||
|
MOVE R0, 1234
|
||||||
|
ST R0, R3, 0
|
||||||
|
|
||||||
|
MOVE R0, 5678
|
||||||
|
|
||||||
|
ST R0, R3, 4 //2.Element über Offset adressieren
|
||||||
|
//oder
|
||||||
|
ADD R3, R3, 1 //Zeiger auf nächsten 32-Bit-Speicherplatz
|
||||||
|
ST R0, R3, 0 //2.Element mit Nulloffset adressieren
|
||||||
|
ST R0, R3, 0 //2.Element mit Nulloffset adressieren
|
||||||
|
JUMP label
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_complex_thing() {
|
||||||
|
let res = parse_and_print_error(
|
||||||
|
"is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
|
||||||
|
AND r0, r0, 1
|
||||||
|
JUMP is_rdy_for_wakeup, eq // Retry until the bit is set
|
||||||
|
WAKE // Trigger wake up
|
||||||
|
REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
|
||||||
|
HALT // Stop the ULP program
|
||||||
|
// After these instructions, SoC will wake up,
|
||||||
|
// and ULP will not run again until started by the main program.",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_others() {
|
||||||
|
let res = parse_and_print_error(
|
||||||
|
"is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
|
||||||
|
.set foo, 0x234
|
||||||
|
.set foo, 234
|
||||||
|
.set foo2, foo
|
||||||
|
.global my_label
|
||||||
|
// After these instructions, SoC will wake up,
|
||||||
|
// and ULP will not run again until started by the main program.
|
||||||
|
|
||||||
|
JUMPR label, 1, GE
|
||||||
|
JUMPS label, 1, GE
|
||||||
|
|
||||||
|
some_more_stuff:
|
||||||
|
move r1, r0
|
||||||
|
jump p1_status_changed, eq
|
||||||
|
|
||||||
|
move r3, p1_status
|
||||||
|
rsh r0, r1, 7
|
||||||
|
and r0, r0, 1
|
||||||
|
st r0, r3, 0
|
||||||
|
|
||||||
|
move r3, p2_status
|
||||||
|
rsh r0, r1, 8
|
||||||
|
and r0, r0, 1
|
||||||
|
st r0, r3, 0
|
||||||
|
|
||||||
|
move r3, p3_status
|
||||||
|
rsh r0, r1, 9
|
||||||
|
and r0, r0, 1
|
||||||
|
st r0, r3, 0
|
||||||
|
// check if p1 status changed
|
||||||
|
rsh r0, r1, 7
|
||||||
|
and r0, r0, 1
|
||||||
|
move r3, p1_status_next
|
||||||
|
ld r3, r3, 0
|
||||||
|
add r3, r0, r3
|
||||||
|
and r3, r3, 1
|
||||||
|
jump p1_status_changed, eq
|
||||||
|
// check if p2 status changed
|
||||||
|
rsh r0, r1, 8
|
||||||
|
and r0, r0, 1
|
||||||
|
move r3, p2_status_next
|
||||||
|
ld r3, r3, 0
|
||||||
|
add r3, r0, r3
|
||||||
|
and r3, r3, 1
|
||||||
|
jump p2_status_changed, eq
|
||||||
|
|
||||||
|
/* check if p3 status changed */
|
||||||
|
rsh r0, r1, 9
|
||||||
|
and r0, r0, 1
|
||||||
|
move r3, p3_status_next
|
||||||
|
ld r3, r3, 0
|
||||||
|
add r3, r0, r3
|
||||||
|
and r3, r3, 1
|
||||||
|
jump p3_status_changed, eq
|
||||||
|
|
||||||
|
/*
|
||||||
|
multi line comments
|
||||||
|
*/
|
||||||
|
|
||||||
|
halt
|
||||||
|
|
||||||
|
ADC R1, 0, 1
|
||||||
|
TSENS R1, 1000
|
||||||
|
|
||||||
|
REG_RD 0x120, 7, 4
|
||||||
|
REG_WR 0x120, 7, 0, 0x10
|
||||||
|
I2C_WR 0x20, 0x33, 7, 0, 1
|
||||||
|
I2C_RD 0x10, 7, 0, 0
|
||||||
|
|
||||||
|
WAIT 10
|
||||||
|
SLEEP 1
|
||||||
|
|
||||||
|
label:
|
||||||
|
.long 0
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.is_ok())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user