cube2rust/
generate.rs

1use crate::db::*;
2use crate::gpio::*;
3use crate::i2c::*;
4use crate::rcc::*;
5use crate::spi::*;
6use crate::usart::*;
7use crate::utils::*;
8use crate::{Config, MCUFamily};
9
10pub fn generate_main(config: &Config) -> anyhow::Result<String> {
11    let hal = match config.mcu_family {
12        MCUFamily::STM32F0 => "stm32f0xx_hal",
13        _ => todo!("Only STM32F0 supported for now"),
14    };
15
16    let mut imports = GeneratedString::new();
17
18    imports.line("#![no_std]");
19    imports.line("#![no_main]");
20    imports.empty_line();
21    imports.line("use crate::hal::{prelude::*, stm32};");
22    imports.line("use cortex_m::interrupt;");
23    imports.line("use cortex_m_rt::entry;");
24    imports.line("use panic_halt as _;");
25    imports.line(f!("use {hal} as hal;"));
26    imports.empty_line();
27
28    let mut main_func = GeneratedString::new();
29
30    main_func.line("#[entry]");
31    main_func.line("fn main() -> ! {");
32    main_func.indent_right();
33
34    main_func.line("let mut p = stm32::Peripherals::take().unwrap();");
35
36    add_rcc(&mut main_func, &config);
37
38    add_ports(&mut main_func, &config);
39
40    add_gpios(&mut main_func, &config);
41
42    for spi in config.spis.iter() {
43        add_spi(&mut main_func, &mut imports, spi);
44    }
45
46    for usart in config.usarts.iter() {
47        add_usart(&mut main_func, &mut imports, usart);
48    }
49
50    for i2c in config.i2cs.iter() {
51        add_i2c(&mut main_func, &mut imports, i2c);
52    }
53
54    main_func.line("loop {}");
55
56    main_func.indent_left();
57    main_func.line("}");
58
59    Ok(imports.string + "\n" + &main_func.string)
60}
61
62fn add_rcc(string: &mut GeneratedString, config: &Config) {
63    string.line("let mut rcc = p");
64    string.indent_right();
65    string.line(".RCC");
66    string.line(".configure()");
67    match config.rcc.clock_source {
68        ClockSource::HSI => {}
69        ClockSource::HSI48 => string.line(".hsi48()"),
70        ClockSource::HSE(HSEMode::NotBypassed(freq)) => string.line(f!(
71            ".hse({freq}.hz(), hal::rcc::HSEBypassMode::NotBypassed)"
72        )),
73        ClockSource::HSE(HSEMode::Bypassed(freq)) => {
74            string.line(f!(".hse({freq}.hz(), hal::rcc::HSEBypassMode::Bypassed)"))
75        }
76    }
77
78    if let Some(sysclk_freq) = config.rcc.sysclk_freq {
79        string.line(f!(".sysclk({sysclk_freq}.hz())"));
80    }
81    if let Some(hclk_freq) = config.rcc.hclk_freq {
82        string.line(f!(".hclk({hclk_freq}.hz())"));
83    }
84    if let Some(apb1_freq) = config.rcc.apb1_freq {
85        string.line(f!(".pclk({apb1_freq}.hz())"));
86    }
87    string.line(".freeze(&mut p.FLASH);");
88    string.indent_left();
89    string.empty_line();
90}
91
92fn add_ports(string: &mut GeneratedString, config: &Config) {
93    for port in config.ports.iter() {
94        let port_lower = port.to_ascii_lowercase();
95
96        let gpio_names: Vec<_> = config
97            .gpios
98            .iter()
99            .filter(|gpio| gpio.port.ends_with(*port))
100            .map(|gpio| gpio.register.clone())
101            .collect();
102
103        let registers: Vec<_> = gpio_names
104            .iter()
105            .map(|name| f!("_{port_lower}.{name}"))
106            .collect();
107
108        let gpio_names = gpio_names.join(", ");
109        let registers = registers.join(", ");
110
111        string.line(f!("let _{port_lower} = p.GPIO{port}.split(&mut rcc);"));
112        string.line(f!("let ({gpio_names}) = ({registers});"));
113    }
114    string.empty_line();
115}
116
117fn add_gpios(string: &mut GeneratedString, config: &Config) {
118    for gpio in config.gpios.iter() {
119        let pin_name = gpio.get_name();
120        let pin_configuration = configure_gpio(gpio, config.mcu_family);
121
122        let mutable = if !matches!(&gpio.signal, SignalType::Peripheral(_)) {
123            "mut "
124        } else {
125            ""
126        };
127
128        string.line(f!("let {mutable}{pin_name} = {pin_configuration};"));
129    }
130
131    string.empty_line();
132}
133
134fn configure_gpio(gpio: &GpioPin, mcu_family: MCUFamily) -> String {
135    let name = gpio.get_name();
136
137    let func = match gpio.signal {
138        SignalType::AdcInput => f!("into_analog"),
139        SignalType::GpioInput => match gpio.pu_pd.unwrap_or_default() {
140            PullType::GPIO_NOPULL => f!("into_floating_input"),
141            PullType::GPIO_PULLUP => f!("into_pull_up_input"),
142            PullType::GPIO_PULLDOWN => f!("into_pull_down_input"),
143        },
144        SignalType::GpioOutput => match gpio.mode_default_output_pp.unwrap_or_default() {
145            ModeOutputType::GPIO_MODE_OUTPUT_OD => match gpio.speed.unwrap_or_default() {
146                SpeedType::GPIO_SPEED_FREQ_LOW => f!("into_open_drain_output"),
147                _ => todo!("{} higher speeds for", name),
148            },
149            ModeOutputType::GPIO_MODE_OUTPUT_PP => match gpio.speed.unwrap_or_default() {
150                SpeedType::GPIO_SPEED_FREQ_LOW => f!("into_push_pull_output"),
151                SpeedType::GPIO_SPEED_FREQ_MEDIUM => f!("into_push_pull_output_hs"),
152
153                _ => todo!("{} higher speeds for", name),
154            },
155        },
156        SignalType::Peripheral(ref name) => {
157            let af = get_alternate_function(mcu_family, gpio, name);
158            f!("into_alternate_af{af}")
159        }
160    };
161    f!("interrupt::free(|cs| {gpio.register}.{func}(cs))")
162}
163
164fn add_spi(main_func: &mut GeneratedString, imports: &mut GeneratedString, spi: &SPI) {
165    let polarity = match spi.polarity.unwrap_or_default() {
166        CLKPolarity::SPI_POLARITY_LOW => "IdleLow",
167        CLKPolarity::SPI_POLARITY_HIGH => "IdleHigh",
168    };
169
170    let phase = match spi.phase.unwrap_or_default() {
171        CLKPhase::SPI_PHASE_1EDGE => "CaptureOnFirstTransition",
172        CLKPhase::SPI_PHASE_2EDGE => "CaptureOnSecondTransition",
173    };
174
175    imports.line("use hal::spi::{Spi, Mode, Phase, Polarity};");
176
177    main_func.line(f!("let mut {spi.name_lower} = Spi::{spi.name_lower}("));
178    main_func.indent_right();
179    main_func.line(f!("p.{spi.name_upper},"));
180    main_func.line(f!(
181        "({spi.name_lower}_sck, {spi.name_lower}_miso, {spi.name_lower}_mosi),"
182    ));
183    main_func.line("Mode {");
184    main_func.indent_right();
185    main_func.line(f!("polarity: Polarity::{polarity},"));
186    main_func.line(f!("phase: Phase::{phase},"));
187    main_func.indent_left();
188    main_func.line("},");
189    main_func.line(f!("{spi.baudrate.0}.hz(),"));
190    main_func.line("&mut rcc");
191    main_func.indent_left();
192    main_func.line(");");
193    main_func.empty_line();
194}
195
196fn add_usart(main_func: &mut GeneratedString, imports: &mut GeneratedString, usart: &USART) {
197    let baudrate = usart.baudrate.unwrap_or(38400);
198
199    imports.line("use hal::serial::Serial;");
200
201    main_func.line(f!(
202        "let mut {usart.name_lower} = Serial::{usart.name_lower}("
203    ));
204    main_func.indent_right();
205    main_func.line(f!("p.{usart.name_upper},"));
206    main_func.line(f!("({usart.name_lower}_tx, {usart.name_lower}_rx),"));
207    main_func.line(f!("{baudrate}.bps(),"));
208    main_func.line("&mut rcc");
209    main_func.indent_left();
210    main_func.line(");");
211    main_func.empty_line();
212}
213
214fn add_i2c(main_func: &mut GeneratedString, imports: &mut GeneratedString, i2c: &I2C) {
215    imports.line("use hal::i2c::I2c;");
216
217    let speed: u32 = match i2c.mode.unwrap_or_default() {
218        Mode::I2C_Standard => 100,
219        Mode::I2C_Fast => 400,
220        Mode::I2C_Fast_Plus => 1000,
221    };
222
223    main_func.line(f!("let mut {i2c.name_lower} = I2c::{i2c.name_lower}("));
224    main_func.indent_right();
225    main_func.line(f!("p.{i2c.name_upper},"));
226    main_func.line(f!("({i2c.name_lower}_scl, {i2c.name_lower}_sda),"));
227    main_func.line(f!("{speed}.khz(),"));
228    main_func.line("&mut rcc");
229    main_func.indent_left();
230    main_func.line(");");
231    main_func.empty_line();
232}
233
234pub fn generate_cargo_config(config: &Config) -> String {
235    let mut file_content = String::from(
236        r#"[target.'cfg(all(target_arch = "arm", target_os = "none"))']
237# uncomment ONE of these three option to make `cargo run` start a GDB session
238# which option to pick depends on your system
239# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
240# runner = "gdb-multiarch -q -x openocd.gdb"
241# runner = "gdb -q -x openocd.gdb"
242
243rustflags = [
244  # LLD (shipped with the Rust toolchain) is used as the default linker
245  "-C", "link-arg=-Tlink.x",
246
247  # if you run into problems with LLD switch to the GNU linker by commenting out
248  # this line
249  # "-C", "linker=arm-none-eabi-ld",
250
251  # if you need to link to pre-compiled C libraries provided by a C toolchain
252  # use GCC as the linker by commenting out both lines above and then
253  # uncommenting the three lines below
254  # "-C", "linker=arm-none-eabi-gcc",
255  # "-C", "link-arg=-Wl,-Tlink.x",
256  # "-C", "link-arg=-nostartfiles",
257]
258
259[build]
260"#,
261    );
262
263    let target = match config.mcu_family {
264        MCUFamily::STM32F0 | MCUFamily::STM32L0 | MCUFamily::STM32G0 => "thumbv6m-none-eabi",
265        MCUFamily::STM32F1 | MCUFamily::STM32F2 | MCUFamily::STM32L1 => "thumbv7m-none-eabi",
266        MCUFamily::STM32F3 | MCUFamily::STM32F4 => "thumbv7em-none-eabihf",
267        _ => todo!("find out if it has FPU"),
268    };
269
270    file_content.push_str(&f!("target = \"{target}\"\n"));
271    file_content
272}
273
274pub fn generate_dependencies(config: &Config) -> anyhow::Result<String> {
275    let hal_crate = match config.mcu_family {
276        MCUFamily::STM32F0 => "stm32f0xx-hal",
277        _ => todo!("other hal crates"),
278    };
279
280    let feature = get_feature(config)?;
281
282    let mut filecontent =
283        f!("{hal_crate} = {{version = \"*\", features = [\"{feature}\", \"rt\"]}}");
284
285    filecontent.push_str(
286        r#"
287cortex-m = "*"
288cortex-m-rt = "*"
289panic-halt = "*"
290
291[profile.dev.package."*"]
292# opt-level = "z"
293
294[profile.release]
295# lto = true
296"#,
297    );
298
299    Ok(filecontent)
300}
301
302pub fn generate_memory_x(config: &Config) -> anyhow::Result<String> {
303    let mem_size = get_mem_size(config);
304
305    Ok(f!("\
306MEMORY
307{{
308  FLASH : ORIGIN = 0x00000000, LENGTH = {mem_size.flash}K
309  RAM : ORIGIN = 0x20000000, LENGTH = {mem_size.ram}K
310}}
311"))
312}