teensycore 0.1.0

A kernel for the teensy4.0 microcontroller
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
#![allow(dead_code)]

use core::arch::asm;
use crate::phys::addrs;
use crate::{phys::*, wait_exact_ns};

const CTRL_BASE_REG: u32 = 0x18;
const DATA_BASE_REG: u32 = 0x1C;
const FIFO_BASE_REG: u32 = 0x28;
const WATERMARK_BASE_REG: u32 = 0x2C;

// Parity Type
pub const CTRL_PT: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1 };
// Parity Enable
pub const CTRL_PE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<1 };
// Idle Line Type Select
pub const CTRL_ILT: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<2 };
// 9-Bit or 8-Bit Mode Select
pub const CTRL_M: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<4 };
// Receiver Source Select 
pub const CTRL_RSRC: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<5 };
// Doze Enable
pub const CTRL_DOZEEN: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<6 };
// Loop Mode Select 
pub const CTRL_LOOPS: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<7 };
// Idle Configuration 
pub const CTRL_IDLECFG: Reg = Reg { base: CTRL_BASE_REG, mask: 0x7<<8 };
// 7-Bit Mode Select
pub const CTRL_M7: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<11 };
// Send Break 
pub const CTRL_SBK: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<16 };
// Receiver Enabled 
pub const CTRL_RE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<18 };
// Transmitter Enabled 
pub const CTRL_TE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<19 };
// Idle Line Interrupt Enabled
pub const CTRL_ILIE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<20 };
// Receiver Interrupt Enabled 
pub const CTRL_RIE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<21 };
// Transmission Complete Interrupt Enabled
pub const CTRL_TCIE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<22 };
// Transmit Interrupt Enabled
pub const CTRL_TIE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<23 };
// Receive FIFO Enable
pub const FIFO_RXFE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<3 };
// Transmit FIFO Enable
pub const FIFO_TXFE: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<7 };
// Receive FIFO Flush
pub const FIFO_RXFLUSH: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<14 };
// Transmit FIFO Flush
pub const FIFO_TXFLUSH: Reg = Reg { base: CTRL_BASE_REG, mask: 0x1<<15 };

// Input trigger mode (Controlled by XBAR, usually)
pub enum InputTrigger {
    Disabled,
    Rxd, // Input trigger modulates RXD
    CtsB, // Input trigger controls Clear-To-Send
    Txd, // Input trigger modulates TXD
}

pub enum Baud {
    Rate300 = 300,
    Rate2400 = 2400,
    Rate9600 = 9600,
}

pub enum ParityType {
    Even,
    Odd,
}

pub enum BitMode {
    NineBits,
    EightBits,
}

#[derive(Clone, Copy)]
pub enum IdleConfiguration {
    Idle1Char = 0x0,
    Idle2Char = 0x1,
    Idle4Char = 0x2,
    Idle8Char = 0x3,
    Idle16Char = 0x4,
    Idle32Char = 0x5,
    Idle64Char = 0x6,
    Idle128Char = 0x7,
}

#[derive(Copy, Clone)]
pub enum Device {
    Uart1,
    Uart2,
    Uart3,
    Uart4,
    Uart5,
    Uart6,
    Uart7,
    Uart8,
}

pub struct FifoConfig {
    pub tx_fifo_underflow_flag: bool,
    pub rx_fifo_underflow_flag: bool,
    pub tx_flush: bool,
    pub rx_flush: bool,
    // Receiver idle empty not supported currently
    pub tx_fifo_overflow_irq_en: bool,
    pub rx_fifo_underflow_irq_en: bool,
    pub tx_fifo_en: bool,
    pub rx_fifo_en: bool,
}

pub fn uart_clear_idle(device: Device) {
    let addr = get_addr(device) + 0x14;
    assign(addr, read_word(addr) | (0x1 << 20));
}

pub fn uart_or_reg(device: Device, register: &Reg, value: u32) {
    let addr = get_addr(device) + register.base;
    let val = read_word(addr) | value;
    assign(addr, val);
}

pub fn uart_and_reg(device: Device, register: &Reg, value: u32) {
    let addr = get_addr(device) + register.base;
    let val = read_word(addr) & value;
    assign(addr, val);
}

pub fn uart_set_reg(device: Device, register: &Reg) {
    let addr = get_addr(device) + register.base;
    let val = read_word(addr) | register.mask;
    assign(addr, val);
}

pub fn uart_clear_reg(device: Device, register: &Reg) {
    let addr = get_addr(device) + register.base;
    let val = read_word(addr) & !register.mask;
    assign(addr, val);
}

pub fn uart_invert_tx(device: Device, inverted: bool) {
    let addr = get_addr(device) + 0x18;
    let original = read_word(addr) ;
    let val = match inverted {
        true => original | 0x1 << 28,
        false => original & !(0x1 << 28),
    };

    assign(addr, val);
}

fn fifo_config_to_u32(config: &FifoConfig, baseline: u32) -> u32 {
    let mut result: u32 = baseline;   
    // Clear The rx_fifo_depth
    
    // Read Only
    // result = result & !0x7;
    // result = result & (config.rx_fifo_depth as u32);

    result = set_bit_from_bool(result, 3, config.rx_fifo_en);

    // Clear the tx_fifo_depth
    // Read Only
    // result = result & !(0x7 << 4);
    // result = result & (config.tx_fifo_depth as u32) << 4;

    result = set_bit_from_bool(result, 7, config.tx_fifo_en);
    result = set_bit_from_bool(result, 8, config.rx_fifo_underflow_irq_en);
    result = set_bit_from_bool(result, 9, config.tx_fifo_overflow_irq_en);
    result = set_bit_from_bool(result, 14, config.rx_flush);
    result = set_bit_from_bool(result, 15, config.tx_flush);
    result = set_bit_from_bool(result, 16, config.rx_fifo_underflow_flag);
    result = set_bit_from_bool(result, 17, config.tx_fifo_underflow_flag);
    return result;
}

pub struct UartConfig {
    // R8T9 not supported
    // R9T8 not supported
    // TXDIR not supported currently
    pub r9t8: bool,
    pub invert_transmission_polarity: bool,
    pub overrun_irq_en: bool,
    pub noise_error_irq_en: bool,
    pub framing_error_irq_en: bool,
    pub parity_error_irq_en: bool,
    pub tx_irq_en: bool,
    pub tx_complete_irq_en: bool,
    pub rx_irq_en: bool,
    pub idle_line_irq_en: bool,
    pub tx_en: bool,
    pub rx_en: bool,
    // Receiver wakeup control not supported
    // SBK not currently supported
    pub match1_irq_en: bool,
    pub match2_irq_en: bool,
    // 7-bit mode not supported
    pub idle_config: IdleConfiguration,
    // Loops not supported
    pub doze_en: bool,
    // RSRC not supported
    pub bit_mode: BitMode,
    // Received wakeup not supported
    // Line idle type not supported
    pub parity_en: bool,
    pub parity_type: ParityType,
}

#[inline]
fn set_bit_from_bool_without_clear(baseline: u32, bit: u8, value: bool) -> u32 {
    if value {
        return set_bit(baseline, bit);
    } else {
        return baseline;
    }
}

fn set_bit_from_bool(baseline: u32, bit: u8, value: bool) -> u32 {
    if value {
        return set_bit(baseline, bit);
    } else {
        return clear_bit(baseline, bit);
    }
}

fn config_to_u32(config: &UartConfig, baseline: u32) -> u32 {
    let mut result: u32 = baseline;
    
    match config.parity_type {
        ParityType::Even => {
            result = clear_bit(result, 0);
        },
        ParityType::Odd => {
            result = set_bit(result, 0);
        }
    }
    
    result = set_bit_from_bool(result, 1, config.parity_en);

    match config.bit_mode {
        BitMode::NineBits => {
            result = set_bit(result, 4);
        },
        BitMode::EightBits => {
            result = clear_bit(result, 4);
        }
    }

    result = set_bit_from_bool(result, 6, config.doze_en);

    // Clear idle config from original result
    result = result & !(0x7 << 8);
    result = result | (config.idle_config as u32) << 8;

    result = set_bit_from_bool(result, 14, config.match2_irq_en);
    result = set_bit_from_bool(result, 15, config.match1_irq_en);
    result = set_bit_from_bool(result, 18, config.rx_en);
    result = set_bit_from_bool(result, 19, config.tx_en);
    result = set_bit_from_bool(result, 20, config.idle_line_irq_en);
    result = set_bit_from_bool(result, 21, config.rx_irq_en);
    result = set_bit_from_bool(result, 22, config.tx_complete_irq_en);
    result = set_bit_from_bool(result, 23, config.tx_irq_en);
    result = set_bit_from_bool(result, 24, config.parity_error_irq_en);
    result = set_bit_from_bool(result, 25, config.framing_error_irq_en);
    result = set_bit_from_bool(result, 26, config.noise_error_irq_en);
    result = set_bit_from_bool(result, 27, config.overrun_irq_en);
    result = set_bit_from_bool(result, 28, config.invert_transmission_polarity);
    result = set_bit_from_bool(result, 30, config.r9t8);

    return result;
}

pub fn uart_start_clock() {
    // First, select the oscillator clock so all the math works
    assign(0x400F_C024, read_word(0x400F_C024) & !0x1F & !(0x1 << 6));
    assign(0x400FC07C, read_word(0x400FC07C) | (0x3 << 24));
    assign(0x400F_C074, read_word(0x400F_C074) | (0x3 << 2) | (0x3 << 6));
    assign(0x400F_C06C, read_word(0x400F_C06C) | (0x3 << 24));
    assign(0x400F_C068, read_word(0x400F_C068) | (0x3 << 12) | (0x3 << 28));
    assign(0x400F_C07C, read_word(0x400F_C07C) | (0x3 << 26));
}

pub fn get_addr(device: Device) -> u32 {
    return match device {
        Device::Uart1 => addrs::UART1,
        Device::Uart2 => addrs::UART2,
        Device::Uart3 => addrs::UART3,
        Device::Uart4 => addrs::UART4,
        Device::Uart5 => addrs::UART5,
        Device::Uart6 => addrs::UART6,
        Device::Uart7 => addrs::UART7,
        Device::Uart8 => addrs::UART8,
    };
}

// Set the software reset pin on or off
pub fn uart_sw_reset(device: Device, sw_reset: bool) {
    let value = match sw_reset {
        true => 0x2,
        false => 0x0,
    };

    assign(get_addr(device) + 0x8, value);

    // Solves what I believe is a timing issue.
    wait_exact_ns(1);
}

pub fn uart_configure(device: Device, configuration: UartConfig) {
    let addr = get_addr(device) + 0x18;
    assign(addr, config_to_u32(&configuration, 0x0));
}

pub fn uart_set_tie(device: Device, en: bool) {
    let addr = get_addr(device) + 0x18;
    let origin = read_word(addr);

    let val = match en {
        true => origin | CTRL_TIE.mask,
        false => origin & !CTRL_TIE.mask,
    };

    assign(addr, val);
}

pub fn uart_configure_fifo(device: Device, configuration: FifoConfig) {
    let addr = get_addr(device) + 0x28;
    assign(addr, fifo_config_to_u32(&configuration, 0x0));
}

pub fn uart_set_pin_config(device: Device, mode: InputTrigger) {
    let addr = get_addr(device) + 0xC;
    match mode {
        InputTrigger::Disabled => { 
            assign(addr, 0x00);
        },
        InputTrigger::Rxd => { 
            assign(addr, 0x01);
        },
        InputTrigger::Txd => { 
            assign(addr, 0x03);
        },
        InputTrigger::CtsB => { 
            assign(addr, 0x02);
        },
    }
}

pub fn uart_enable(device: Device) {
    let addr = get_addr(device) + 0x18;
    let baseline = read_word(addr);
    assign(addr, baseline | (0x1 << 19) | (0x1 << 18));
    unsafe { asm!("nop"); }
}

pub fn uart_disable(device: Device) {
    let addr = get_addr(device) + 0x18;
    let baseline = read_word(addr);
    assign(addr, baseline & !((0x1 << 19) | (0x1 << 18)));
    unsafe { asm!("nop"); }
}

pub fn uart_write_fifo(device: Device, byte: u8) {
    let addr = get_addr(device) + 0x1C;
    assign_8(addr, byte as u8);
}

pub fn uart_queue_preamble(device: Device) {
    uart_write_fifo(device, 0x00);
}

pub fn uart_read_fifo(device: Device) -> u8 {
    let addr = get_addr(device) + 0x1c;
    return (read_word(addr) & 0x3ff) as u8;
}

/// Returns the depth of the transmit buffer
pub fn uart_get_tx_size(device: Device) -> u32 {
    let addr = get_addr(device) + 0x28;
    let config = read_word(addr) & 0x7;
    return match config {
        0x0 => 1,
        0x1 => 4,
        0x2 => 8,
        0x3 => 16,
        0x4 => 32,
        0x5 => 64,
        0x6 => 128,
        0x7 => 256,
        _ => 4,
    };
}

/// Returns how many bytes are in the tx fifo
pub fn uart_get_tx_count(device: Device) -> u32 {
    let addr = get_addr(device) + 0x2C;
    return (read_word(addr) & 0x700) >> 8;
}

pub fn uart_get_receive_count(device: Device) -> u32 {
    let addr = get_addr(device) + 0x2C;
    return (read_word(addr) & 7000000) >> 24;
}

pub fn uart_has_data(device: Device) -> bool {
    let addr = get_addr(device) + 0x1C;
    return (read_word(addr) & (0x1 << 12)) == 0;
}

pub fn uart_baud_rate(device: Device, rate: u32) {
    // TODO: Explain why this works (if it works)
    let baud_clock = 80000000; // MHz
    let sbr = baud_clock / (rate * 16);
    uart_disable(device);
    let addr = get_addr(device) + 0x10;
    let value = (read_word(addr) & !(0x1 << 13) & !(0x1FFF)) | (0x00 << 24) | (0x1 << 14) | (0x1 << 17)  | (0x1 << 18) | sbr;
    assign(addr, value);
    uart_enable(device);
}

pub fn uart_enable_dma(device: Device) {
    let addr = get_addr(device) + 0x10;
    assign(addr, read_word(addr) | (0x1 << 21) | (0x1 << 23));
}

pub fn uart_disable_dma(device: Device) {
    let addr = get_addr(device) + 0x10;
    assign(addr, read_word(addr) & !(0x1 << 21) & !(0x1 << 23));
}

pub fn uart_flush(device: Device) {
    let addr = get_addr(device) + 0x1C;
    let original = read_word(addr);
    assign(addr, original | (0x1<<15));
}

pub fn uart_sbk(device: Device) {
    let addr = get_addr(device) + 0x1C;
    let original = read_word(addr);
    assign(addr, original & !(0xFF) | (0x1 << 13));
}

pub fn uart_watermark(device: Device, val: u32) {
    let addr = get_addr(device) + 0x2C;
    assign(addr, (val & 0x3) | ((val & 0x3) << 16));
}

pub fn uart_enable_fifo(device: Device) {
    let addr = get_addr(device) + 0x28;
    assign(addr, read_word(addr) | (0x1 << 7));
}   

pub fn uart_disable_fifo(device: Device) {
    let addr = get_addr(device) + 0x28;
    assign(addr, read_word(addr) & !(0x1 << 7));
}

pub fn uart_get_irq_statuses(device: Device) -> u32 {
    return read_word(get_addr(device) + 0x14);
}

pub struct UartClearIrqConfig {
    pub rx_overrun: bool,
    pub rx_idle: bool,
    pub rx_data_full: bool,
    pub rx_line_break: bool,
    pub rx_pin_active: bool,
    pub rx_set_data_inverted: bool, // This is not an irq, but it lives in the irq register
    pub tx_complete: bool,
    pub tx_empty: bool,
}

pub const RX_LINEBREAK_INT: u32 = 1 << 31;
pub const RX_PIN_ACTIVE_INT: u32 = 1 << 30;
pub const RX_SET_DATA_INVERTED: u32 = 1 << 28;
pub const RX_DATA_FULL_INT: u32 = 1 << 21;
pub const RX_IDLE_INT: u32 = 1 << 20;
pub const RX_OVERRUN_INT: u32 = 1 << 19;
pub const NOISE_FLAG_INT: u32 = 1 << 18;
pub const TX_EMPTY_INT: u32 = 1 << 23;
pub const TX_COMPLETE_INT: u32 = 1 << 22;

pub fn uart_clear_irq(device: Device) {
    let addr = get_addr(device) + 0x14;
    assign(addr, RX_LINEBREAK_INT | RX_PIN_ACTIVE_INT | RX_DATA_FULL_INT | RX_IDLE_INT | RX_OVERRUN_INT | NOISE_FLAG_INT | TX_EMPTY_INT | TX_COMPLETE_INT);
}