litex/
led_controller.rs

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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! LiteX led controller (`LedChaser` core)
//!
//! Hardware source and documentation available at
//! [`litex/soc/cores/led.py`](https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/led.py).

use core::cell::Cell;
use core::mem;
use kernel::hil;
use kernel::utilities::StaticRef;

use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};

// TODO: Make the register width adaptable, perhaps by another trait
// with the integer type as an associated type?

/// [`LiteXLedController`] register layout
#[repr(C)]
pub struct LiteXLedRegisters<R: LiteXSoCRegisterConfiguration> {
    leds_out: R::ReadWrite8,
}

/// LiteX led controller core
pub struct LiteXLedController<R: LiteXSoCRegisterConfiguration> {
    regs: StaticRef<LiteXLedRegisters<R>>,
    led_count: usize,
    led_references: Cell<u8>,
}

impl<R: LiteXSoCRegisterConfiguration> LiteXLedController<R> {
    pub fn new(base: StaticRef<LiteXLedRegisters<R>>, led_count: usize) -> LiteXLedController<R> {
        // The number of leds may not be larger than the bit width of
        // the supplied register layout
        //
        // TODO: Automatically determine based on the type
        assert!(
            led_count <= 8,
            "LiteXLedController register width insufficient to support the requested LED count"
        );

        LiteXLedController {
            regs: base,
            led_count,
            led_references: Cell::new(0),
        }
    }

    /// Initialize the [`LiteXLedController`]
    ///
    /// This will turn all LEDs off, thus disabling the *LED Chaser*
    /// hardware-pattern of the LiteX core and switching to explicit
    /// software control.
    pub fn initialize(&self) {
        self.regs.leds_out.set(0);
    }

    /// Returns the number of LEDs managed by the
    /// [`LiteXLedController`]
    pub fn led_count(&self) -> usize {
        self.led_count
    }

    /// Create a [`LiteXLed`] instance
    ///
    /// To avoid duplicate use of a LED, this will return `None` if an
    /// instance for the requested LED already exists. Call
    /// [`LiteXLed::destroy`] (or drop the [`LiteXLed`]) to be create
    /// a new instance for this LED.
    pub fn get_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
        if index < self.led_count() && (self.led_references.get() & (1 << index)) == 0 {
            self.led_references
                .set(self.led_references.get() | (1 << index));
            Some(LiteXLed::new(self, index))
        } else {
            None
        }
    }

    /// Create a [`LiteXLed`] without checking for duplicates
    ///
    /// This function must only be used in a panic handler, if no
    /// other code will be running afterwards, in order to guarantee
    /// consistency between ownership of the LiteXLed instance and
    /// control over the LED state
    ///
    /// This function only checks whether the requested LEDs is within
    /// the controller's range of available LEDs, but *NOT* whether
    /// there already is a different reference to the same LED.
    pub unsafe fn panic_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
        if index < self.led_count() {
            Some(LiteXLed::new(self, index))
        } else {
            None
        }
    }

    /// Internal method to mark a [`LiteXLed`] instance as destroyed
    pub(self) fn destroy_led(&self, index: usize) {
        self.led_references
            .set(self.led_references.get() & !(1 << index));
    }

    /// Internal method to set a LED output
    pub(self) fn set_led(&self, index: usize, val: bool) {
        if val {
            self.regs
                .leds_out
                .set(self.regs.leds_out.get() | (1 << index));
        } else {
            self.regs
                .leds_out
                .set(self.regs.leds_out.get() & !(1 << index));
        }
    }

    /// Internal method to read the current state of a LED
    pub(self) fn read_led(&self, index: usize) -> bool {
        (self.regs.leds_out.get() & (1 << index)) != 0
    }
}

/// Single LED of a [`LiteXLedController`]
///
/// Can be obtained by calling [`LiteXLedController::get_led`].
///
/// Only one [`LiteXLed`] instance may exist per LED. To deregister
/// this instance, call [`LiteXLed::destroy`] (or drop it).
pub struct LiteXLed<'a, R: LiteXSoCRegisterConfiguration> {
    controller: &'a LiteXLedController<R>,
    index: usize,
}

impl<'a, R: LiteXSoCRegisterConfiguration> LiteXLed<'a, R> {
    fn new(controller: &'a LiteXLedController<R>, index: usize) -> LiteXLed<'a, R> {
        LiteXLed { controller, index }
    }

    /// Index of this LED in the [`LiteXLedController`] LED array
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns a reference to the [`LiteXLedController`] of this LED
    pub fn controller(&self) -> &'a LiteXLedController<R> {
        self.controller
    }

    /// Destroy (deregister & consume) the [`LiteXLed`]
    pub fn destroy(self) {
        mem::drop(self);
    }
}

impl<R: LiteXSoCRegisterConfiguration> hil::led::Led for LiteXLed<'_, R> {
    fn init(&self) {
        self.controller.set_led(self.index, false);
    }

    fn on(&self) {
        self.controller.set_led(self.index, true);
    }

    fn off(&self) {
        self.controller.set_led(self.index, false);
    }

    fn toggle(&self) {
        self.controller
            .set_led(self.index, !self.controller.read_led(self.index));
    }

    fn read(&self) -> bool {
        self.controller.read_led(self.index)
    }
}

impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXLed<'_, R> {
    /// Deregister the LED with the controller
    fn drop(&mut self) {
        self.controller.destroy_led(self.index);
    }
}