litex/
led_controller.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! LiteX led controller (`LedChaser` core)
6//!
7//! Hardware source and documentation available at
8//! [`litex/soc/cores/led.py`](https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/led.py).
9
10use core::cell::Cell;
11use core::mem;
12use kernel::hil;
13use kernel::utilities::StaticRef;
14
15use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
16
17// TODO: Make the register width adaptable, perhaps by another trait
18// with the integer type as an associated type?
19
20/// [`LiteXLedController`] register layout
21#[repr(C)]
22pub struct LiteXLedRegisters<R: LiteXSoCRegisterConfiguration> {
23    leds_out: R::ReadWrite8,
24}
25
26/// LiteX led controller core
27pub struct LiteXLedController<R: LiteXSoCRegisterConfiguration> {
28    regs: StaticRef<LiteXLedRegisters<R>>,
29    led_count: usize,
30    led_references: Cell<u8>,
31}
32
33impl<R: LiteXSoCRegisterConfiguration> LiteXLedController<R> {
34    pub fn new(base: StaticRef<LiteXLedRegisters<R>>, led_count: usize) -> LiteXLedController<R> {
35        // The number of leds may not be larger than the bit width of
36        // the supplied register layout
37        //
38        // TODO: Automatically determine based on the type
39        assert!(
40            led_count <= 8,
41            "LiteXLedController register width insufficient to support the requested LED count"
42        );
43
44        LiteXLedController {
45            regs: base,
46            led_count,
47            led_references: Cell::new(0),
48        }
49    }
50
51    /// Initialize the [`LiteXLedController`]
52    ///
53    /// This will turn all LEDs off, thus disabling the *LED Chaser*
54    /// hardware-pattern of the LiteX core and switching to explicit
55    /// software control.
56    pub fn initialize(&self) {
57        self.regs.leds_out.set(0);
58    }
59
60    /// Returns the number of LEDs managed by the
61    /// [`LiteXLedController`]
62    pub fn led_count(&self) -> usize {
63        self.led_count
64    }
65
66    /// Create a [`LiteXLed`] instance
67    ///
68    /// To avoid duplicate use of a LED, this will return `None` if an
69    /// instance for the requested LED already exists. Call
70    /// [`LiteXLed::destroy`] (or drop the [`LiteXLed`]) to be create
71    /// a new instance for this LED.
72    pub fn get_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
73        if index < self.led_count() && (self.led_references.get() & (1 << index)) == 0 {
74            self.led_references
75                .set(self.led_references.get() | (1 << index));
76            Some(LiteXLed::new(self, index))
77        } else {
78            None
79        }
80    }
81
82    /// Create a [`LiteXLed`] without checking for duplicates
83    ///
84    /// This function must only be used in a panic handler, if no
85    /// other code will be running afterwards, in order to guarantee
86    /// consistency between ownership of the LiteXLed instance and
87    /// control over the LED state
88    ///
89    /// This function only checks whether the requested LEDs is within
90    /// the controller's range of available LEDs, but *NOT* whether
91    /// there already is a different reference to the same LED.
92    pub unsafe fn panic_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
93        if index < self.led_count() {
94            Some(LiteXLed::new(self, index))
95        } else {
96            None
97        }
98    }
99
100    /// Internal method to mark a [`LiteXLed`] instance as destroyed
101    pub(self) fn destroy_led(&self, index: usize) {
102        self.led_references
103            .set(self.led_references.get() & !(1 << index));
104    }
105
106    /// Internal method to set a LED output
107    pub(self) fn set_led(&self, index: usize, val: bool) {
108        if val {
109            self.regs
110                .leds_out
111                .set(self.regs.leds_out.get() | (1 << index));
112        } else {
113            self.regs
114                .leds_out
115                .set(self.regs.leds_out.get() & !(1 << index));
116        }
117    }
118
119    /// Internal method to read the current state of a LED
120    pub(self) fn read_led(&self, index: usize) -> bool {
121        (self.regs.leds_out.get() & (1 << index)) != 0
122    }
123}
124
125/// Single LED of a [`LiteXLedController`]
126///
127/// Can be obtained by calling [`LiteXLedController::get_led`].
128///
129/// Only one [`LiteXLed`] instance may exist per LED. To deregister
130/// this instance, call [`LiteXLed::destroy`] (or drop it).
131pub struct LiteXLed<'a, R: LiteXSoCRegisterConfiguration> {
132    controller: &'a LiteXLedController<R>,
133    index: usize,
134}
135
136impl<'a, R: LiteXSoCRegisterConfiguration> LiteXLed<'a, R> {
137    fn new(controller: &'a LiteXLedController<R>, index: usize) -> LiteXLed<'a, R> {
138        LiteXLed { controller, index }
139    }
140
141    /// Index of this LED in the [`LiteXLedController`] LED array
142    pub fn index(&self) -> usize {
143        self.index
144    }
145
146    /// Returns a reference to the [`LiteXLedController`] of this LED
147    pub fn controller(&self) -> &'a LiteXLedController<R> {
148        self.controller
149    }
150
151    /// Destroy (deregister & consume) the [`LiteXLed`]
152    pub fn destroy(self) {
153        mem::drop(self);
154    }
155}
156
157impl<R: LiteXSoCRegisterConfiguration> hil::led::Led for LiteXLed<'_, R> {
158    fn init(&self) {
159        self.controller.set_led(self.index, false);
160    }
161
162    fn on(&self) {
163        self.controller.set_led(self.index, true);
164    }
165
166    fn off(&self) {
167        self.controller.set_led(self.index, false);
168    }
169
170    fn toggle(&self) {
171        self.controller
172            .set_led(self.index, !self.controller.read_led(self.index));
173    }
174
175    fn read(&self) -> bool {
176        self.controller.read_led(self.index)
177    }
178}
179
180impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXLed<'_, R> {
181    /// Deregister the LED with the controller
182    fn drop(&mut self) {
183        self.controller.destroy_led(self.index);
184    }
185}