litex_arty/
main.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//! Board file for a LiteX-built VexRiscv-based SoC synthesized for a
6//! Digilent Arty-A7 FPGA board
7
8#![no_std]
9#![no_main]
10
11use core::ptr::{addr_of, addr_of_mut};
12
13use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
14
15use kernel::capabilities;
16use kernel::component::Component;
17use kernel::hil::time::{Alarm, Timer};
18use kernel::platform::chip::InterruptService;
19use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
20use kernel::platform::{KernelResources, SyscallDriverLookup};
21use kernel::scheduler::mlfq::MLFQSched;
22use kernel::utilities::registers::interfaces::ReadWriteable;
23use kernel::utilities::StaticRef;
24use kernel::{create_capability, debug, static_init};
25use rv32i::csr;
26
27mod io;
28mod litex_generated_constants;
29
30// This module contains the LiteX SoC configuration options, register
31// positions, interrupt mappings and other implementation details of
32// the generated bitstream.
33//
34// Its values are used throughout the file, hence import it under a
35// short name.
36use litex_generated_constants as socc;
37
38/// Structure for dynamic interrupt mapping, depending on the SoC
39/// configuration
40///
41/// This struct is deliberately kept in the board crate. Because of
42/// the configurable nature of LiteX, it does not make sense to define
43/// a default interrupt mapping, as the interrupt numbers are
44/// generated sequentially for all softcores.
45struct LiteXArtyInterruptablePeripherals {
46    uart0: &'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>,
47    timer0: &'static litex_vexriscv::timer::LiteXTimer<
48        'static,
49        socc::SoCRegisterFmt,
50        socc::ClockFrequency,
51    >,
52    ethmac0: &'static litex_vexriscv::liteeth::LiteEth<
53        'static,
54        { socc::ETHMAC_TX_SLOTS },
55        socc::SoCRegisterFmt,
56    >,
57}
58
59impl LiteXArtyInterruptablePeripherals {
60    // Resolve any recursive dependencies and set up deferred calls:
61    pub fn init(&'static self) {
62        kernel::deferred_call::DeferredCallClient::register(self.uart0);
63    }
64}
65
66impl InterruptService for LiteXArtyInterruptablePeripherals {
67    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
68        match interrupt as usize {
69            socc::UART_INTERRUPT => {
70                self.uart0.service_interrupt();
71                true
72            }
73            socc::TIMER0_INTERRUPT => {
74                self.timer0.service_interrupt();
75                true
76            }
77            socc::ETHMAC_INTERRUPT => {
78                self.ethmac0.service_interrupt();
79                true
80            }
81            _ => false,
82        }
83    }
84}
85
86const NUM_PROCS: usize = 4;
87
88// Actual memory for holding the active process structures. Need an
89// empty list at least.
90static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
91    [None; NUM_PROCS];
92
93// Reference to the chip, led controller, UART hardware, and process printer for
94// panic dumps.
95struct LiteXArtyPanicReferences {
96    chip: Option<&'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>,
97    uart: Option<&'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>>,
98    led_controller:
99        Option<&'static litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>>,
100    process_printer: Option<&'static capsules_system::process_printer::ProcessPrinterText>,
101}
102static mut PANIC_REFERENCES: LiteXArtyPanicReferences = LiteXArtyPanicReferences {
103    chip: None,
104    uart: None,
105    led_controller: None,
106    process_printer: None,
107};
108
109// How should the kernel respond when a process faults.
110const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
111    capsules_system::process_policies::PanicFaultPolicy {};
112
113/// Dummy buffer that causes the linker to reserve enough space for the stack.
114#[no_mangle]
115#[link_section = ".stack_buffer"]
116pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
117
118/// A structure representing this platform that holds references to all
119/// capsules for this platform.
120struct LiteXArty {
121    led_driver: &'static capsules_core::led::LedDriver<
122        'static,
123        litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
124        4,
125    >,
126    console: &'static capsules_core::console::Console<'static>,
127    pconsole: &'static capsules_core::process_console::ProcessConsole<
128        'static,
129        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
130        VirtualMuxAlarm<
131            'static,
132            litex_vexriscv::timer::LiteXAlarm<
133                'static,
134                'static,
135                socc::SoCRegisterFmt,
136                socc::ClockFrequency,
137            >,
138        >,
139        components::process_console::Capability,
140    >,
141    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
142        'static,
143        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
144    >,
145    alarm: &'static capsules_core::alarm::AlarmDriver<
146        'static,
147        VirtualMuxAlarm<
148            'static,
149            litex_vexriscv::timer::LiteXAlarm<
150                'static,
151                'static,
152                socc::SoCRegisterFmt,
153                socc::ClockFrequency,
154            >,
155        >,
156    >,
157    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
158    scheduler: &'static MLFQSched<
159        'static,
160        VirtualMuxAlarm<
161            'static,
162            litex_vexriscv::timer::LiteXAlarm<
163                'static,
164                'static,
165                socc::SoCRegisterFmt,
166                socc::ClockFrequency,
167            >,
168        >,
169    >,
170    scheduler_timer: &'static VirtualSchedulerTimer<
171        VirtualMuxAlarm<
172            'static,
173            litex_vexriscv::timer::LiteXAlarm<
174                'static,
175                'static,
176                socc::SoCRegisterFmt,
177                socc::ClockFrequency,
178            >,
179        >,
180    >,
181}
182
183/// Mapping of integer syscalls to objects that implement syscalls
184impl SyscallDriverLookup for LiteXArty {
185    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
186    where
187        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
188    {
189        match driver_num {
190            capsules_core::led::DRIVER_NUM => f(Some(self.led_driver)),
191            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
192            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
193            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
194            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
195            _ => f(None),
196        }
197    }
198}
199
200impl KernelResources<litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>
201    for LiteXArty
202{
203    type SyscallDriverLookup = Self;
204    type SyscallFilter = ();
205    type ProcessFault = ();
206    type Scheduler = MLFQSched<
207        'static,
208        VirtualMuxAlarm<
209            'static,
210            litex_vexriscv::timer::LiteXAlarm<
211                'static,
212                'static,
213                socc::SoCRegisterFmt,
214                socc::ClockFrequency,
215            >,
216        >,
217    >;
218    type SchedulerTimer = VirtualSchedulerTimer<
219        VirtualMuxAlarm<
220            'static,
221            litex_vexriscv::timer::LiteXAlarm<
222                'static,
223                'static,
224                socc::SoCRegisterFmt,
225                socc::ClockFrequency,
226            >,
227        >,
228    >;
229    type WatchDog = ();
230    type ContextSwitchCallback = ();
231
232    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
233        self
234    }
235    fn syscall_filter(&self) -> &Self::SyscallFilter {
236        &()
237    }
238    fn process_fault(&self) -> &Self::ProcessFault {
239        &()
240    }
241    fn scheduler(&self) -> &Self::Scheduler {
242        self.scheduler
243    }
244    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
245        self.scheduler_timer
246    }
247    fn watchdog(&self) -> &Self::WatchDog {
248        &()
249    }
250    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
251        &()
252    }
253}
254
255/// This is in a separate, inline(never) function so that its stack frame is
256/// removed when this function returns. Otherwise, the stack space used for
257/// these static_inits is wasted.
258#[inline(never)]
259unsafe fn start() -> (
260    &'static kernel::Kernel,
261    LiteXArty,
262    &'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>,
263) {
264    // These symbols are defined in the linker script.
265    extern "C" {
266        /// Beginning of the ROM region containing app images.
267        static _sapps: u8;
268        /// End of the ROM region containing app images.
269        static _eapps: u8;
270        /// Beginning of the RAM region for app memory.
271        static mut _sappmem: u8;
272        /// End of the RAM region for app memory.
273        static _eappmem: u8;
274        /// The start of the kernel text (Included only for kernel PMP)
275        static _stext: u8;
276        /// The end of the kernel text (Included only for kernel PMP)
277        static _etext: u8;
278        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
279        static _sflash: u8;
280        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
281        static _eflash: u8;
282        /// The start of the kernel / app RAM (Included only for kernel PMP)
283        static _ssram: u8;
284        /// The end of the kernel / app RAM (Included only for kernel PMP)
285        static _esram: u8;
286    }
287
288    // ---------- BASIC INITIALIZATION ----------
289
290    // Basic setup of the riscv platform.
291    rv32i::configure_trap_handler();
292
293    // Set up memory protection immediately after setting the trap handler, to
294    // ensure that much of the board initialization routine runs with PMP kernel
295    // memory protection.
296    let pmp = rv32i::pmp::kernel_protection::KernelProtectionPMP::new(
297        rv32i::pmp::kernel_protection::FlashRegion(
298            rv32i::pmp::NAPOTRegionSpec::from_start_end(
299                core::ptr::addr_of!(_sflash),
300                core::ptr::addr_of!(_eflash),
301            )
302            .unwrap(),
303        ),
304        rv32i::pmp::kernel_protection::RAMRegion(
305            rv32i::pmp::NAPOTRegionSpec::from_start_end(
306                core::ptr::addr_of!(_ssram),
307                core::ptr::addr_of!(_esram),
308            )
309            .unwrap(),
310        ),
311        rv32i::pmp::kernel_protection::MMIORegion(
312            rv32i::pmp::NAPOTRegionSpec::from_start_size(
313                0xf0000000 as *const u8, // start
314                0x10000000,              // size
315            )
316            .unwrap(),
317        ),
318        rv32i::pmp::kernel_protection::KernelTextRegion(
319            rv32i::pmp::TORRegionSpec::from_start_end(
320                core::ptr::addr_of!(_stext),
321                core::ptr::addr_of!(_etext),
322            )
323            .unwrap(),
324        ),
325    )
326    .unwrap();
327
328    // initialize capabilities
329    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
330    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
331
332    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
333
334    // ---------- LED CONTROLLER HARDWARE ----------
335
336    // Initialize the LEDs, stopping any patterns from the bootloader
337    // / bios still running in HW and turn them all off
338    let led0 = static_init!(
339        litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>,
340        litex_vexriscv::led_controller::LiteXLedController::new(
341            StaticRef::new(
342                socc::CSR_LEDS_BASE
343                    as *const litex_vexriscv::led_controller::LiteXLedRegisters<
344                        socc::SoCRegisterFmt,
345                    >
346            ),
347            4, // 4 LEDs on this board
348        )
349    );
350    led0.initialize();
351
352    PANIC_REFERENCES.led_controller = Some(led0);
353
354    // --------- TIMER & UPTIME CORE; ALARM INITIALIZATION ----------
355
356    // Initialize the hardware timer
357    let timer0 = static_init!(
358        litex_vexriscv::timer::LiteXTimer<'static, socc::SoCRegisterFmt, socc::ClockFrequency>,
359        litex_vexriscv::timer::LiteXTimer::new(StaticRef::new(
360            socc::CSR_TIMER0_BASE
361                as *const litex_vexriscv::timer::LiteXTimerRegisters<socc::SoCRegisterFmt>
362        ),)
363    );
364
365    // The SoC is expected to feature the 64-bit uptime extension to the timer hardware
366    let timer0_uptime = static_init!(
367        litex_vexriscv::timer::LiteXTimerUptime<
368            'static,
369            socc::SoCRegisterFmt,
370            socc::ClockFrequency,
371        >,
372        litex_vexriscv::timer::LiteXTimerUptime::new(timer0)
373    );
374
375    // Create the LiteXAlarm based on the hardware LiteXTimer core and
376    // the uptime peripheral
377    let litex_alarm = static_init!(
378        litex_vexriscv::timer::LiteXAlarm<
379            'static,
380            'static,
381            socc::SoCRegisterFmt,
382            socc::ClockFrequency,
383        >,
384        litex_vexriscv::timer::LiteXAlarm::new(timer0_uptime, timer0)
385    );
386    timer0.set_timer_client(litex_alarm);
387    litex_alarm.initialize();
388
389    // Create a shared virtualization mux layer on top of a single hardware
390    // alarm.
391    let mux_alarm = static_init!(
392        MuxAlarm<
393            'static,
394            litex_vexriscv::timer::LiteXAlarm<
395                'static,
396                'static,
397                socc::SoCRegisterFmt,
398                socc::ClockFrequency,
399            >,
400        >,
401        MuxAlarm::new(litex_alarm)
402    );
403    litex_alarm.set_alarm_client(mux_alarm);
404
405    // Userspace alarm driver
406    let virtual_alarm_user = static_init!(
407        VirtualMuxAlarm<
408            'static,
409            litex_vexriscv::timer::LiteXAlarm<
410                'static,
411                'static,
412                socc::SoCRegisterFmt,
413                socc::ClockFrequency,
414            >,
415        >,
416        VirtualMuxAlarm::new(mux_alarm)
417    );
418    virtual_alarm_user.setup();
419
420    let alarm = static_init!(
421        capsules_core::alarm::AlarmDriver<
422            'static,
423            VirtualMuxAlarm<
424                'static,
425                litex_vexriscv::timer::LiteXAlarm<
426                    'static,
427                    'static,
428                    socc::SoCRegisterFmt,
429                    socc::ClockFrequency,
430                >,
431            >,
432        >,
433        capsules_core::alarm::AlarmDriver::new(
434            virtual_alarm_user,
435            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
436        )
437    );
438    virtual_alarm_user.set_alarm_client(alarm);
439
440    // Systick virtual alarm for scheduling
441    let systick_virtual_alarm = static_init!(
442        VirtualMuxAlarm<
443            'static,
444            litex_vexriscv::timer::LiteXAlarm<
445                'static,
446                'static,
447                socc::SoCRegisterFmt,
448                socc::ClockFrequency,
449            >,
450        >,
451        VirtualMuxAlarm::new(mux_alarm)
452    );
453    systick_virtual_alarm.setup();
454
455    let scheduler_timer = static_init!(
456        VirtualSchedulerTimer<
457            VirtualMuxAlarm<
458                'static,
459                litex_vexriscv::timer::LiteXAlarm<
460                    'static,
461                    'static,
462                    socc::SoCRegisterFmt,
463                    socc::ClockFrequency,
464                >,
465            >,
466        >,
467        VirtualSchedulerTimer::new(systick_virtual_alarm)
468    );
469
470    // ---------- UART ----------
471
472    // Initialize the HW UART
473    let uart0 = static_init!(
474        litex_vexriscv::uart::LiteXUart<socc::SoCRegisterFmt>,
475        litex_vexriscv::uart::LiteXUart::new(
476            StaticRef::new(
477                socc::CSR_UART_BASE
478                    as *const litex_vexriscv::uart::LiteXUartRegisters<socc::SoCRegisterFmt>,
479            ),
480            // No UART PHY CSR present, thus baudrate fixed in
481            // hardware. Change with --uart-baudrate during SoC
482            // generation. Fixed to 1MBd.
483            None,
484        )
485    );
486    uart0.initialize();
487
488    PANIC_REFERENCES.uart = Some(uart0);
489
490    // Create a shared UART channel for the console and for kernel debug.
491    let uart_mux = components::console::UartMuxComponent::new(uart0, socc::UART_BAUDRATE)
492        .finalize(components::uart_mux_component_static!());
493
494    // ---------- ETHERNET ----------
495
496    // ETHMAC peripheral
497    let ethmac0 = static_init!(
498        litex_vexriscv::liteeth::LiteEth<{socc::ETHMAC_TX_SLOTS}, socc::SoCRegisterFmt>,
499        litex_vexriscv::liteeth::LiteEth::new(
500            StaticRef::new(
501                socc::CSR_ETHMAC_BASE
502                    as *const litex_vexriscv::liteeth::LiteEthMacRegisters<socc::SoCRegisterFmt>,
503            ),
504            socc::MEM_ETHMAC_BASE,
505            socc::MEM_ETHMAC_SIZE,
506            socc::ETHMAC_SLOT_SIZE,
507            socc::ETHMAC_RX_SLOTS,
508            socc::ETHMAC_TX_SLOTS,
509        )
510    );
511
512    // Initialize the ETHMAC controller
513    ethmac0.initialize();
514
515    // ---------- LED DRIVER ----------
516
517    // LEDs
518    let led_driver =
519        components::led::LedsComponent::new().finalize(components::led_component_static!(
520            litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
521            led0.get_led(0).unwrap(),
522            led0.get_led(1).unwrap(),
523            led0.get_led(2).unwrap(),
524            led0.get_led(3).unwrap(),
525        ));
526
527    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ----------
528
529    let interrupt_service = static_init!(
530        LiteXArtyInterruptablePeripherals,
531        LiteXArtyInterruptablePeripherals {
532            uart0,
533            timer0,
534            ethmac0,
535        }
536    );
537    interrupt_service.init();
538
539    let chip = static_init!(
540        litex_vexriscv::chip::LiteXVexRiscv<
541            LiteXArtyInterruptablePeripherals,
542        >,
543        litex_vexriscv::chip::LiteXVexRiscv::new(
544            "LiteX on Arty A7",
545            interrupt_service,
546            pmp,
547        )
548    );
549
550    PANIC_REFERENCES.chip = Some(chip);
551
552    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
553        .finalize(components::process_printer_text_component_static!());
554
555    PANIC_REFERENCES.process_printer = Some(process_printer);
556
557    // Enable RISC-V interrupts globally
558    csr::CSR
559        .mie
560        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET);
561    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
562
563    // Unmask all interrupt sources in the interrupt controller
564    chip.unmask_interrupts();
565
566    // Setup the process console.
567    let pconsole = components::process_console::ProcessConsoleComponent::new(
568        board_kernel,
569        uart_mux,
570        mux_alarm,
571        process_printer,
572        None,
573    )
574    .finalize(components::process_console_component_static!(
575        litex_vexriscv::timer::LiteXAlarm<
576            'static,
577            'static,
578            socc::SoCRegisterFmt,
579            socc::ClockFrequency,
580        >
581    ));
582
583    // Setup the console.
584    let console = components::console::ConsoleComponent::new(
585        board_kernel,
586        capsules_core::console::DRIVER_NUM,
587        uart_mux,
588    )
589    .finalize(components::console_component_static!());
590
591    // Create the debugger object that handles calls to `debug!()`.
592    components::debug_writer::DebugWriterComponent::new(
593        uart_mux,
594        create_capability!(capabilities::SetDebugWriterCapability),
595    )
596    .finalize(components::debug_writer_component_static!());
597
598    let lldb = components::lldb::LowLevelDebugComponent::new(
599        board_kernel,
600        capsules_core::low_level_debug::DRIVER_NUM,
601        uart_mux,
602    )
603    .finalize(components::low_level_debug_component_static!());
604
605    let scheduler = components::sched::mlfq::MLFQComponent::new(mux_alarm, &*addr_of!(PROCESSES))
606        .finalize(components::mlfq_component_static!(
607            litex_vexriscv::timer::LiteXAlarm<
608                'static,
609                'static,
610                socc::SoCRegisterFmt,
611                socc::ClockFrequency,
612            >,
613            NUM_PROCS
614        ));
615
616    let litex_arty = LiteXArty {
617        console,
618        pconsole,
619        alarm,
620        lldb,
621        led_driver,
622        scheduler,
623        scheduler_timer,
624        ipc: kernel::ipc::IPC::new(
625            board_kernel,
626            kernel::ipc::DRIVER_NUM,
627            &memory_allocation_cap,
628        ),
629    };
630
631    debug!("LiteX+VexRiscv on ArtyA7: initialization complete, entering main loop.");
632    let _ = litex_arty.pconsole.start();
633
634    kernel::process::load_processes(
635        board_kernel,
636        chip,
637        core::slice::from_raw_parts(
638            core::ptr::addr_of!(_sapps),
639            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
640        ),
641        core::slice::from_raw_parts_mut(
642            core::ptr::addr_of_mut!(_sappmem),
643            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
644        ),
645        &mut *addr_of_mut!(PROCESSES),
646        &FAULT_RESPONSE,
647        &process_mgmt_cap,
648    )
649    .unwrap_or_else(|err| {
650        debug!("Error loading processes!");
651        debug!("{:?}", err);
652    });
653
654    (board_kernel, litex_arty, chip)
655}
656
657/// Main function called after RAM initialized.
658#[no_mangle]
659pub unsafe fn main() {
660    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
661
662    let (board_kernel, board, chip) = start();
663    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
664}