Initial Release
This commit is contained in:
28
src/host/CMakeLists.txt
Normal file
28
src/host/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
pico_add_subdirectory(hardware_divider)
|
||||
pico_add_subdirectory(hardware_gpio)
|
||||
pico_add_subdirectory(hardware_sync)
|
||||
pico_add_subdirectory(hardware_timer)
|
||||
pico_add_subdirectory(hardware_uart)
|
||||
pico_add_subdirectory(pico_bit_ops)
|
||||
pico_add_subdirectory(pico_divider)
|
||||
pico_add_subdirectory(pico_multicore)
|
||||
pico_add_subdirectory(pico_platform)
|
||||
pico_add_subdirectory(pico_printf)
|
||||
pico_add_subdirectory(pico_stdio)
|
||||
pico_add_subdirectory(pico_stdlib)
|
||||
|
||||
pico_add_doxygen(${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
macro(pico_set_float_implementation TARGET IMPL)
|
||||
endmacro()
|
||||
|
||||
macro(pico_set_double_implementation TARGET IMPL)
|
||||
endmacro()
|
||||
|
||||
macro(pico_set_boot_stage2 TARGET IMPL)
|
||||
endmacro()
|
||||
|
||||
set(PICO_HOST_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "")
|
||||
function(pico_define_boot_stage2 NAME)
|
||||
add_executable(${NAME} ${PICO_HOST_DIR}/boot_stage2.c)
|
||||
endfunction()
|
14
src/host/README.md
Normal file
14
src/host/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
This is a basic set of replacement library implementations sufficient to get simple applications
|
||||
running on your computer (Raspberry Pi OS, Linux, macOS or Windows using Cygwin or Windows Subsystem for Linux).
|
||||
It is selected by `PICO_PLATFORM=host` in your CMake build
|
||||
|
||||
This can be extremely useful for testing and debugging higher level application code, or porting code which is not yet small enough
|
||||
to run on the RP2040 device itself.
|
||||
|
||||
This base level host library provides a minimal environment to compile programs, but is likely sufficient for programs
|
||||
that don't access hardware directly.
|
||||
|
||||
It is possible however to inject additional SDK library implementations/simulations to provide
|
||||
more complete functionality. For an example of this see the [pico-host-sdl](https://github.com/raspberrypi/pico-host-sdl)
|
||||
which uses the SDL2 library to add additional library support for pico_multicore, timers/alarms in pico-time and
|
||||
pico-audio/pico-scanvideo from [pico-extras](https://github.com/raspberrypi/pico-extras)
|
1
src/host/boot_stage2.c
Normal file
1
src/host/boot_stage2.c
Normal file
@ -0,0 +1 @@
|
||||
// empty
|
1
src/host/hardware_divider/CMakeLists.txt
Normal file
1
src/host/hardware_divider/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
pico_simple_hardware_target(divider)
|
9
src/host/hardware_divider/divider.c
Normal file
9
src/host/hardware_divider/divider.c
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "hardware/divider.h"
|
||||
|
||||
__thread uint64_t hw_divider_result_threadlocal;
|
122
src/host/hardware_divider/include/hardware/divider.h
Normal file
122
src/host/hardware_divider/include/hardware/divider.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_DIVIDER_H
|
||||
#define _HARDWARE_DIVIDER_H
|
||||
|
||||
#include "pico/types.h"
|
||||
|
||||
typedef uint64_t divmod_result_t;
|
||||
|
||||
static inline int __sign_of(int32_t v) {
|
||||
return v > 0 ? 1 : (v < 0 ? -1 : 0);
|
||||
}
|
||||
|
||||
// divides unsigned values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
|
||||
static inline uint64_t hw_divider_divmod_u32(uint32_t a, uint32_t b) {
|
||||
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-1); // todo check this
|
||||
return (((uint64_t)(a%b))<<32u) | (a/b);
|
||||
}
|
||||
|
||||
// divides signed values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
|
||||
static inline uint64_t hw_divider_divmod_s32(int32_t a, int32_t b) {
|
||||
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-__sign_of(a));
|
||||
return (((uint64_t)(a%b))<<32u) | (uint32_t)(a/b);
|
||||
}
|
||||
|
||||
extern __thread divmod_result_t hw_divider_result_threadlocal;
|
||||
|
||||
static inline void hw_divider_divmod_s32_start(int32_t a, int32_t b) {
|
||||
hw_divider_result_threadlocal = hw_divider_divmod_s32(a, b);
|
||||
}
|
||||
|
||||
static inline void hw_divider_divmod_u32_start(uint32_t a, uint32_t b) {
|
||||
hw_divider_result_threadlocal = hw_divider_divmod_u32(a, b);
|
||||
}
|
||||
|
||||
static inline divmod_result_t hw_divider_result_wait() {
|
||||
return hw_divider_result_threadlocal;
|
||||
}
|
||||
|
||||
static inline uint64_t hw_divider_result_nowait() {
|
||||
return hw_divider_result_threadlocal;
|
||||
}
|
||||
|
||||
inline static uint32_t to_quotient_u32(unsigned long long int r) {
|
||||
return (uint32_t) r;
|
||||
}
|
||||
|
||||
inline static int32_t to_quotient_s32(unsigned long long int r) {
|
||||
return (int32_t)(uint32_t)r;
|
||||
}
|
||||
|
||||
inline static uint32_t to_remainder_u32(unsigned long long int r) {
|
||||
return (uint32_t)(r >> 32u);
|
||||
}
|
||||
|
||||
inline static int32_t to_remainder_s32(unsigned long long int r) {
|
||||
return (int32_t)(r >> 32u);
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_quotient_wait() {
|
||||
return to_quotient_u32(hw_divider_result_wait());
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_remainder_wait() {
|
||||
return to_remainder_u32(hw_divider_result_wait());
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_quotient_wait() {
|
||||
return to_quotient_s32(hw_divider_result_wait());
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_remainder_wait() {
|
||||
return to_remainder_s32(hw_divider_result_wait());
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b) {
|
||||
return b ? (a / b) : -1;
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b) {
|
||||
return b ? (a % b) : a;
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_quotient(int32_t a, int32_t b) {
|
||||
return b ? (a / b) : -__sign_of(a);
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_remainder(int32_t a, int32_t b) {
|
||||
return b ? (a % b) : a;
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b) {
|
||||
return hw_divider_u32_quotient(a,b);
|
||||
}
|
||||
|
||||
static inline uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b) {
|
||||
return hw_divider_u32_remainder(a,b);
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b) {
|
||||
return hw_divider_s32_quotient(a,b);
|
||||
}
|
||||
|
||||
static inline int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b) {
|
||||
return hw_divider_s32_remainder(a,b);
|
||||
}
|
||||
|
||||
typedef uint64_t hw_divider_state_t;
|
||||
|
||||
static inline void hw_divider_save_state(hw_divider_state_t *dest) {
|
||||
*dest = hw_divider_result_threadlocal;
|
||||
}
|
||||
|
||||
static inline void hw_divider_restore_state(hw_divider_state_t *src) {
|
||||
hw_divider_result_threadlocal = *src;
|
||||
}
|
||||
|
||||
#endif // _HARDWARE_DIVIDER_H
|
1
src/host/hardware_gpio/CMakeLists.txt
Normal file
1
src/host/hardware_gpio/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
pico_simple_hardware_target(gpio)
|
118
src/host/hardware_gpio/gpio.c
Normal file
118
src/host/hardware_gpio/gpio.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
// todo weak or replace? probably weak
|
||||
void gpio_set_function(uint gpio, enum gpio_function fn) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_pull_up(uint gpio) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_pull_down(uint gpio) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_disable_pulls(uint gpio) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_pulls(uint gpio, bool up, bool down) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_outover(uint gpio, uint value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_inover(uint gpio, uint value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_oeover(uint gpio, uint value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enable) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_acknowledge_irq(uint gpio, uint32_t events) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_init(uint gpio) {
|
||||
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(gpio_get)
|
||||
|
||||
bool PICO_WEAK_FUNCTION_IMPL_NAME(gpio_get)(uint gpio) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t gpio_get_all() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_set_mask(uint32_t mask) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_clr_mask(uint32_t mask) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_xor_mask(uint32_t mask) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_put_masked(uint32_t mask, uint32_t value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_put_all(uint32_t value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_put(uint gpio, int value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_dir_out_masked(uint32_t mask) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_dir_in_masked(uint32_t mask) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_dir_masked(uint32_t mask, uint32_t value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_dir_all_bits(uint32_t value) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_dir(uint gpio, bool out) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_debug_pins_init() {
|
||||
|
||||
}
|
||||
|
||||
void gpio_set_input_enabled(uint gpio, bool enable) {
|
||||
|
||||
}
|
||||
|
||||
void gpio_init_mask(uint gpio_mask) {
|
||||
|
||||
}
|
147
src/host/hardware_gpio/include/hardware/gpio.h
Normal file
147
src/host/hardware_gpio/include/hardware/gpio.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_GPIO_H_
|
||||
#define _HARDWARE_GPIO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
enum gpio_function {
|
||||
GPIO_FUNC_XIP = 0,
|
||||
GPIO_FUNC_SPI = 1,
|
||||
GPIO_FUNC_UART = 2,
|
||||
GPIO_FUNC_I2C = 3,
|
||||
GPIO_FUNC_PWM = 4,
|
||||
GPIO_FUNC_SIO = 5,
|
||||
GPIO_FUNC_PIO0 = 6,
|
||||
GPIO_FUNC_PIO1 = 7,
|
||||
GPIO_FUNC_GPCK = 8,
|
||||
GPIO_FUNC_USB = 9,
|
||||
GPIO_FUNC_NULL = 0xf,
|
||||
};
|
||||
|
||||
|
||||
#define GPIO_OUT 1
|
||||
#define GPIO_IN 0
|
||||
|
||||
#define N_GPIOS 30
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Pad Controls + IO Muxing
|
||||
// ----------------------------------------------------------------------------
|
||||
// Declarations for gpio.c
|
||||
|
||||
void gpio_set_function(uint gpio, enum gpio_function fn);
|
||||
|
||||
enum gpio_function gpio_get_function(uint gpio);
|
||||
|
||||
void gpio_pull_up(uint gpio);
|
||||
|
||||
void gpio_pull_down(uint gpio);
|
||||
|
||||
void gpio_disable_pulls(uint gpio);
|
||||
|
||||
void gpio_set_pulls(uint gpio, bool up, bool down);
|
||||
|
||||
void gpio_set_outover(uint gpio, uint value);
|
||||
|
||||
void gpio_set_inover(uint gpio, uint value);
|
||||
|
||||
void gpio_set_oeover(uint gpio, uint value);
|
||||
|
||||
void gpio_set_input_enabled(uint gpio, bool enable);
|
||||
|
||||
// Configure a GPIO for direct input/output from software
|
||||
void gpio_init(uint gpio);
|
||||
|
||||
void gpio_init_mask(uint gpio_mask);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Input
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Get the value of a single GPIO
|
||||
bool gpio_get(uint gpio);
|
||||
|
||||
// Get raw value of all
|
||||
uint32_t gpio_get_all();
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Output
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Drive high every GPIO appearing in mask
|
||||
void gpio_set_mask(uint32_t mask);
|
||||
|
||||
void gpio_clr_mask(uint32_t mask);
|
||||
|
||||
// Toggle every GPIO appearing in mask
|
||||
void gpio_xor_mask(uint32_t mask);
|
||||
|
||||
|
||||
// For each 1 bit in "mask", drive that pin to the value given by
|
||||
// corresponding bit in "value", leaving other pins unchanged.
|
||||
// Since this uses the TOGL alias, it is concurrency-safe with e.g. an IRQ
|
||||
// bashing different pins from the same core.
|
||||
void gpio_put_masked(uint32_t mask, uint32_t value);
|
||||
|
||||
// Drive all pins simultaneously
|
||||
void gpio_put_all(uint32_t value);
|
||||
|
||||
|
||||
// Drive a single GPIO high/low
|
||||
void gpio_put(uint gpio, int value);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Direction
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Switch all GPIOs in "mask" to output
|
||||
void gpio_set_dir_out_masked(uint32_t mask);
|
||||
|
||||
// Switch all GPIOs in "mask" to input
|
||||
void gpio_set_dir_in_masked(uint32_t mask);
|
||||
|
||||
// For each 1 bit in "mask", switch that pin to the direction given by
|
||||
// corresponding bit in "value", leaving other pins unchanged.
|
||||
// E.g. gpio_set_dir_masked(0x3, 0x2); -> set pin 0 to input, pin 1 to output,
|
||||
// simultaneously.
|
||||
void gpio_set_dir_masked(uint32_t mask, uint32_t value);
|
||||
|
||||
// Set direction of all pins simultaneously.
|
||||
// For each bit in value,
|
||||
// 1 = out
|
||||
// 0 = in
|
||||
void gpio_set_dir_all_bits(uint32_t value);
|
||||
|
||||
// Set a single GPIO to input/output.
|
||||
// true = out
|
||||
// 0 = in
|
||||
void gpio_set_dir(uint gpio, bool out);
|
||||
|
||||
// debugging
|
||||
#define PICO_DEBUG_PIN_BASE 19u
|
||||
|
||||
// note these two macros may only be used once per compilation unit
|
||||
#define CU_REGISTER_DEBUG_PINS(p, ...)
|
||||
#define CU_SELECT_DEBUG_PINS(x)
|
||||
#define DEBUG_PINS_ENABLED(p) false
|
||||
|
||||
#define DEBUG_PINS_SET(p, v) ((void)0)
|
||||
#define DEBUG_PINS_CLR(p, v) ((void)0)
|
||||
#define DEBUG_PINS_XOR(p, v) ((void)0)
|
||||
|
||||
void gpio_debug_pins_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
12
src/host/hardware_sync/CMakeLists.txt
Normal file
12
src/host/hardware_sync/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
pico_simple_hardware_headers_target(sync)
|
||||
|
||||
if (NOT TARGET hardware_sync)
|
||||
add_library(hardware_sync INTERFACE)
|
||||
|
||||
target_sources(hardware_sync INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/sync_core0_only.c
|
||||
)
|
||||
|
||||
target_link_libraries(hardware_sync INTERFACE hardware_sync_headers pico_platform)
|
||||
endif()
|
||||
|
106
src/host/hardware_sync/include/hardware/sync.h
Normal file
106
src/host/hardware_sync/include/hardware/sync.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_SYNC_H
|
||||
#define _HARDWARE_SYNC_H
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#if (__STDC_VERSION__ >= 201112L)
|
||||
#include <stdatomic.h>
|
||||
#else
|
||||
enum {
|
||||
memory_order_acquire, memory_order_release
|
||||
};
|
||||
static inline void atomic_thread_fence(uint x) {}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef PICO_SPINLOCK_ID_TIMER
|
||||
#define PICO_SPINLOCK_ID_TIMER 10
|
||||
#endif
|
||||
|
||||
#ifndef PICO_SPINLOCK_ID_STRIPED_FIRST
|
||||
#define PICO_SPINLOCK_ID_STRIPED_FIRST 16
|
||||
#endif
|
||||
|
||||
#ifndef PICO_SPINLOCK_ID_STRIPED_LAST
|
||||
#define PICO_SPINLOCK_ID_STRIPED_LAST 23
|
||||
#endif
|
||||
|
||||
typedef struct _spin_lock_t spin_lock_t;
|
||||
|
||||
inline static void __mem_fence_acquire() {
|
||||
#ifndef __cplusplus
|
||||
atomic_thread_fence(memory_order_acquire);
|
||||
#else
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static void __mem_fence_release() {
|
||||
#ifndef __cplusplus
|
||||
atomic_thread_fence(memory_order_release);
|
||||
#else
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void __sev();
|
||||
|
||||
void __wev();
|
||||
|
||||
void __wfi();
|
||||
|
||||
void __wfe();
|
||||
|
||||
uint32_t save_and_disable_interrupts();
|
||||
|
||||
void restore_interrupts(uint32_t status);
|
||||
|
||||
uint spin_lock_get_num(spin_lock_t *lock);
|
||||
|
||||
spin_lock_t *spin_lock_instance(uint lock_num);
|
||||
|
||||
void spin_lock_unsafe_blocking(spin_lock_t *lock);
|
||||
|
||||
void spin_unlock_unsafe(spin_lock_t *lock);
|
||||
|
||||
uint32_t spin_lock_blocking(spin_lock_t *lock);
|
||||
|
||||
bool is_spin_locked(const spin_lock_t *lock);
|
||||
|
||||
void spin_unlock(spin_lock_t *lock, uint32_t saved_irq);
|
||||
|
||||
uint get_core_num();
|
||||
|
||||
spin_lock_t *spin_lock_init(uint lock_num);
|
||||
|
||||
void clear_spin_locks(void);
|
||||
|
||||
uint next_striped_spin_lock_num();
|
||||
|
||||
void spin_lock_claim(uint lock_num);
|
||||
void spin_lock_claim_mask(uint32_t lock_num_mask);
|
||||
void spin_lock_unclaim(uint lock_num);
|
||||
int spin_lock_claim_unused(bool required);
|
||||
uint spin_lock_num(spin_lock_t *lock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
140
src/host/hardware_sync/sync_core0_only.c
Normal file
140
src/host/hardware_sync/sync_core0_only.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/platform_defs.h"
|
||||
|
||||
// This is a dummy implementation that is single threaded
|
||||
|
||||
static struct _spin_lock_t {
|
||||
bool locked;
|
||||
} _spinlocks[NUM_SPIN_LOCKS];
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(save_and_disable_interrupts)
|
||||
|
||||
//static uint8_t striped_spin_lock_num;
|
||||
|
||||
uint32_t PICO_WEAK_FUNCTION_IMPL_NAME(save_and_disable_interrupts)() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(restore_interrupts)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(restore_interrupts)(uint32_t status) {
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_instance)
|
||||
|
||||
spin_lock_t *PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_instance)(uint lock_num) {
|
||||
assert(lock_num < NUM_SPIN_LOCKS);
|
||||
return &_spinlocks[lock_num];
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_get_num)
|
||||
|
||||
uint PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_get_num)(spin_lock_t *lock) {
|
||||
return lock - _spinlocks;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_init)
|
||||
|
||||
spin_lock_t *PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_init)(uint lock_num) {
|
||||
spin_lock_t *lock = spin_lock_instance(lock_num);
|
||||
spin_unlock_unsafe(lock);
|
||||
return lock;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_unsafe_blocking)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_unsafe_blocking)(spin_lock_t *lock) {
|
||||
lock->locked = true;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_blocking)
|
||||
|
||||
uint32_t PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_blocking)(spin_lock_t *lock) {
|
||||
spin_lock_unsafe_blocking(lock);
|
||||
return 1; // todo wrong value
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(is_spin_locked)
|
||||
|
||||
bool PICO_WEAK_FUNCTION_IMPL_NAME(is_spin_locked)(const spin_lock_t *lock) {
|
||||
return lock->locked;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_unlock_unsafe)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_unlock_unsafe)(spin_lock_t *lock) {
|
||||
lock->locked = false;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_unlock)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_unlock)(spin_lock_t *lock, uint32_t saved_irq) {
|
||||
spin_unlock_unsafe(lock);
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(__sev)
|
||||
|
||||
volatile bool event_fired;
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(__sev)() {
|
||||
event_fired = true;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(__wfi)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(__wfi)() {
|
||||
panic("Can't wait on irq for host core0 only implementation");
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(__wfe)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(__wfe)() {
|
||||
while (!event_fired) tight_loop_contents();
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(get_core_num)
|
||||
|
||||
uint PICO_WEAK_FUNCTION_IMPL_NAME(get_core_num)() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(clear_spin_locks)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(clear_spin_locks)(void) {
|
||||
for (uint i = 0; i < NUM_SPIN_LOCKS; i++) {
|
||||
spin_unlock_unsafe(spin_lock_instance(i));
|
||||
}
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(next_striped_spin_lock_num)
|
||||
uint PICO_WEAK_FUNCTION_IMPL_NAME(next_striped_spin_lock_num)() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_claim)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_claim)(uint lock_num) {
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_claim_mask)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_claim_mask)(uint32_t mask) {
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_unclaim)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_unclaim)(uint lock_num) {
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_claim_unused)
|
||||
int PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_claim_unused)(bool required) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(spin_lock_num)
|
||||
uint PICO_WEAK_FUNCTION_IMPL_NAME(spin_lock_num)(spin_lock_t *lock) {
|
||||
return 0;
|
||||
}
|
16
src/host/hardware_timer/CMakeLists.txt
Normal file
16
src/host/hardware_timer/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
pico_simple_hardware_target(timer)
|
||||
|
||||
target_compile_definitions(hardware_timer INTERFACE
|
||||
PICO_HARDWARE_TIMER_RESOLUTION_US=1000 # to loosen tests a little
|
||||
)
|
||||
|
||||
if (NOT DEFINED PICO_TIME_NO_ALARM_SUPPORT)
|
||||
# we don't have alarm pools in the basic host support, though pico_host_sdl adds it
|
||||
set(PICO_TIME_NO_ALARM_SUPPORT "1" CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
if (PICO_TIME_NO_ALARM_SUPPORT)
|
||||
target_compile_definitions(hardware_timer INTERFACE
|
||||
PICO_TIME_DEFAULT_ALARM_POOL_DISABLED=1
|
||||
)
|
||||
endif()
|
42
src/host/hardware_timer/include/hardware/timer.h
Normal file
42
src/host/hardware_timer/include/hardware/timer.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_TIMER_H
|
||||
#define _HARDWARE_TIMER_H
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef PARAM_ASSERTIONS_ENABLED_TIMER
|
||||
#define PARAM_ASSERTIONS_ENABLED_TIMER 0
|
||||
#endif
|
||||
|
||||
static inline void check_hardware_alarm_num_param(uint alarm_num) {
|
||||
invalid_params_if(TIMER, alarm_num >= NUM_TIMERS);
|
||||
}
|
||||
|
||||
uint32_t time_us_32();
|
||||
uint64_t time_us_64();
|
||||
void busy_wait_us_32(uint32_t delay_us);
|
||||
void busy_wait_us(uint64_t delay_us);
|
||||
void busy_wait_until(absolute_time_t t);
|
||||
bool time_reached(absolute_time_t t);
|
||||
typedef void (*hardware_alarm_callback_t)(uint alarm_num);
|
||||
void hardware_alarm_claim(uint alarm_num);
|
||||
void hardware_alarm_unclaim(uint alarm_num);
|
||||
void hardware_alarm_set_callback(uint alarm_num, hardware_alarm_callback_t callback);
|
||||
bool hardware_alarm_set_target(uint alarm_num, absolute_time_t t);
|
||||
void hardware_alarm_cancel(uint alarm_num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
104
src/host/hardware_timer/timer.c
Normal file
104
src/host/hardware_timer/timer.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "hardware/timer.h"
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#endif
|
||||
|
||||
// in our case not a busy wait
|
||||
PICO_WEAK_FUNCTION_DEF(busy_wait_us)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(busy_wait_us_32)(uint32_t delay_us) {
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
usleep(delay_us);
|
||||
#else
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
PICO_WEAK_FUNCTION_DEF(busy_wait_us)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(busy_wait_us)(uint64_t delay_us) {
|
||||
absolute_time_t t;
|
||||
update_us_since_boot(&t, time_us_64() + delay_us);
|
||||
busy_wait_until(t);
|
||||
}
|
||||
|
||||
// this may or may not wrap
|
||||
PICO_WEAK_FUNCTION_DEF(time_us_64)
|
||||
uint64_t PICO_WEAK_FUNCTION_IMPL_NAME(time_us_64)() {
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
// struct timeval tv;
|
||||
// gettimeofday(&tv, NULL);
|
||||
// return tv.tv_sec * (uint64_t) 1000000 + tv.tv_usec;
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * (uint64_t) 1000000 + ts.tv_nsec / 1000;
|
||||
#else
|
||||
panic_unsupported();
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(timer_us_32)
|
||||
uint32_t PICO_WEAK_FUNCTION_IMPL_NAME(timer_us_32)() {
|
||||
return (uint32_t) time_us_64();
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(time_reached)
|
||||
bool PICO_WEAK_FUNCTION_IMPL_NAME(time_reached)(absolute_time_t t) {
|
||||
uint64_t target = to_us_since_boot(t);
|
||||
if (target > 0xffffffffu) return false;
|
||||
return time_us_64() >= target;
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(busy_wait_until)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(busy_wait_until)(absolute_time_t target) {
|
||||
#if defined(__unix__)
|
||||
struct timespec tspec;
|
||||
tspec.tv_sec = to_us_since_boot(target) / 1000000;
|
||||
tspec.tv_nsec = (to_us_since_boot(target) % 1000000) * 1000;
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tspec, NULL);
|
||||
#else
|
||||
const int chunk = 1u<<30u;
|
||||
uint64_t target_us = to_us_since_boot(target);
|
||||
uint64_t time_us = time_us_64();
|
||||
while (target_us - time_us >= chunk) {
|
||||
busy_wait_us_32(chunk);
|
||||
time_us = time_us_64();
|
||||
}
|
||||
if (target_us != time_us) {
|
||||
busy_wait_us_32(target_us - chunk);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t claimed_alarms;
|
||||
|
||||
void hardware_alarm_claim(uint alarm_num) {
|
||||
assert(!(claimed_alarms & (1u << alarm_num)));
|
||||
claimed_alarms |= 1u <<alarm_num;
|
||||
}
|
||||
|
||||
void hardware_alarm_unclaim(uint alarm_num) {
|
||||
assert(claimed_alarms & (1u << alarm_num));
|
||||
claimed_alarms &= ~(1u <<alarm_num);
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(hardware_alarm_set_callback)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(hardware_alarm_set_callback)(uint alarm_num, hardware_alarm_callback_t callback) {
|
||||
panic_unsupported();
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(hardware_alarm_set_target)
|
||||
bool PICO_WEAK_FUNCTION_IMPL_NAME(hardware_alarm_set_target)(uint alarm_num, absolute_time_t target) {
|
||||
panic_unsupported();
|
||||
}
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(hardware_alarm_cancel)
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(hardware_alarm_cancel)(uint alarm_num) {
|
||||
panic_unsupported();
|
||||
}
|
1
src/host/hardware_uart/CMakeLists.txt
Normal file
1
src/host/hardware_uart/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
pico_simple_hardware_target(uart)
|
91
src/host/hardware_uart/include/hardware/uart.h
Normal file
91
src/host/hardware_uart/include/hardware/uart.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_UART_H
|
||||
#define _HARDWARE_UART_H
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
#ifndef PARAM_ASSERTIONS_ENABLED_UART
|
||||
#define PARAM_ASSERTIONS_ENABLED_UART 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uart_inst uart_inst_t;
|
||||
|
||||
extern uart_inst_t * const uart0;
|
||||
extern uart_inst_t * const uart1;
|
||||
#define uart_default uart0
|
||||
|
||||
typedef enum {
|
||||
UART_PARITY_NONE,
|
||||
UART_PARITY_EVEN,
|
||||
UART_PARITY_ODD
|
||||
} uart_parity_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Setup
|
||||
|
||||
// Put the UART into a known state, and enable it. Must be called before other
|
||||
// functions.
|
||||
uint uart_init(uart_inst_t *uart, uint baudrate);
|
||||
|
||||
// Disable the UART if it is no longer used. Must be reinitialised before
|
||||
// being used again.
|
||||
void uart_deinit(uart_inst_t *uart);
|
||||
|
||||
// Set baud rate as close as possible to requested, and return actual rate.
|
||||
uint uart_set_baudrate(uart_inst_t *uart, uint baudrate);
|
||||
|
||||
// cts: enable flow control of TX by clear-to-send input
|
||||
// rts: enable assertion of request-to-send output by RX flow control
|
||||
void uart_set_hw_flow(uart_inst_t *uart, bool cts, bool rts);
|
||||
|
||||
// Configure how the UART serialises and deserialises data on the wire
|
||||
void uart_set_format(uart_inst_t *uart, uint data_bits, uint stop_bits, uart_parity_t parity);
|
||||
|
||||
// Enable the UART's interrupt output. Need to install an interrupt handler first.
|
||||
void uart_set_irq_enables(uart_inst_t *uart, bool rx_has_data, bool tx_needs_data);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Generic input/output
|
||||
|
||||
// If returns 0, no space is available in the UART to write more data.
|
||||
// If returns nonzero, at least that many bytes can be written without blocking.
|
||||
size_t uart_is_writable(uart_inst_t *uart);
|
||||
|
||||
// If returns 0, no data is available to be read from UART.
|
||||
// If returns nonzero, at least that many bytes can be written without blocking.
|
||||
size_t uart_is_readable(uart_inst_t *uart);
|
||||
|
||||
// Write len bytes directly from src to the UART
|
||||
void uart_write_blocking(uart_inst_t *uart, const uint8_t *src, size_t len);
|
||||
|
||||
// Read len bytes directly from the UART to dst
|
||||
void uart_read_blocking(uart_inst_t *uart, uint8_t *dst, size_t len);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// UART-specific operations and aliases
|
||||
|
||||
void uart_putc(uart_inst_t *uart, char c);
|
||||
|
||||
void uart_puts(uart_inst_t *uart, const char *s);
|
||||
|
||||
char uart_getc(uart_inst_t *uart);
|
||||
|
||||
// en: assert break condition (TX held low) if true. Clear break condition if false.
|
||||
void uart_set_break(uart_inst_t *uart, bool en);
|
||||
|
||||
void uart_default_tx_wait_blocking();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
118
src/host/hardware_uart/uart.c
Normal file
118
src/host/hardware_uart/uart.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "hardware/uart.h"
|
||||
|
||||
#if defined(__unix) || defined(__APPLE__)
|
||||
#define _XOPEN_SOURCE 600 /* for ONLCR */
|
||||
#define __BSD_VISIBLE 1 /* for ONLCR in *BSD */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef FNONBLOCK
|
||||
#define FNONBLOCK O_NONBLOCK
|
||||
#endif
|
||||
|
||||
struct termios _tty;
|
||||
static tcflag_t _res_oflg = 0;
|
||||
static tcflag_t _res_lflg = 0;
|
||||
|
||||
void _resetty(void) {
|
||||
if (!isatty(STDIN_FILENO))
|
||||
return;
|
||||
|
||||
/* reset tty: */
|
||||
_tty.c_oflag = _res_oflg;
|
||||
_tty.c_lflag = _res_lflg;
|
||||
tcsetattr(STDIN_FILENO, TCSADRAIN, &_tty);
|
||||
}
|
||||
|
||||
void _inittty(void) {
|
||||
if (!isatty(STDIN_FILENO))
|
||||
return;
|
||||
|
||||
/* save tty: */
|
||||
tcgetattr(STDIN_FILENO, &_tty);
|
||||
_res_oflg = _tty.c_oflag;
|
||||
_res_lflg = _tty.c_lflag;
|
||||
|
||||
/* set raw: */
|
||||
_tty.c_lflag &= ~(ICANON | ICRNL);// | ISIG);
|
||||
//_tty.c_oflag &= ~ONLCR;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &_tty);
|
||||
|
||||
fcntl(STDIN_FILENO, F_SETFL, FNONBLOCK);
|
||||
atexit(_resetty);
|
||||
}
|
||||
|
||||
#else
|
||||
void _inittty() {}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool dummy;
|
||||
} uart_hw_t;
|
||||
|
||||
uart_inst_t *const uart0;
|
||||
uart_inst_t *const uart1;
|
||||
|
||||
static int _nextchar = EOF;
|
||||
|
||||
static bool _peekchar() {
|
||||
if (_nextchar == EOF) {
|
||||
_nextchar = getchar();
|
||||
}
|
||||
return _nextchar != EOF;
|
||||
}
|
||||
|
||||
uint uart_init(uart_inst_t *uart, uint baud_rate) {
|
||||
_inittty();
|
||||
return baud_rate;
|
||||
}
|
||||
|
||||
size_t uart_is_writable(uart_inst_t *uart) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If returns 0, no data is available to be read from UART.
|
||||
// If returns nonzero, at least that many bytes can be written without blocking.
|
||||
size_t uart_is_readable(uart_inst_t *uart) {
|
||||
return _peekchar() ? 1 : 0;
|
||||
}
|
||||
|
||||
// Write len bytes directly from src to the UART
|
||||
//void uart_write_blocking(uart_inst_t uart, const uint8_t *src, size_t len);
|
||||
|
||||
// Read len bytes directly from the UART to dst
|
||||
//void uart_read_blocking(uart_inst_t uart, uint8_t *dst, size_t len);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// UART-specific operations and aliases
|
||||
|
||||
void uart_putc(uart_inst_t *uart, char c) {
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
void uart_puts(uart_inst_t *uart, const char *s) {
|
||||
puts(s);
|
||||
}
|
||||
|
||||
char uart_getc(uart_inst_t *uart) {
|
||||
while (!_peekchar()) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
char rc = (char) _nextchar;
|
||||
_nextchar = EOF;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void uart_default_tx_wait_blocking() {
|
||||
|
||||
}
|
9
src/host/pico_bit_ops/CMakeLists.txt
Normal file
9
src/host/pico_bit_ops/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
add_library(pico_bit_ops INTERFACE)
|
||||
|
||||
target_sources(pico_bit_ops INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/bit_ops.c)
|
||||
|
||||
target_link_libraries(pico_bit_ops INTERFACE pico_bit_ops_headers)
|
||||
|
||||
macro(pico_set_bit_ops_implementation TARGET IMPL)
|
||||
endmacro()
|
22
src/host/pico_bit_ops/bit_ops.c
Normal file
22
src/host/pico_bit_ops/bit_ops.c
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/bit_ops.h"
|
||||
|
||||
uint32_t __rev(uint32_t v) {
|
||||
v = ((v & 0x55555555u) << 1u) | ((v >> 1u) & 0x55555555u);
|
||||
v = ((v & 0x33333333u) << 2u) | ((v >> 2u) & 0x33333333u);
|
||||
v = ((v & 0x0f0f0f0fu) << 4u) | ((v >> 4u) & 0x0f0f0f0fu);
|
||||
return (v << 24u) | ((v & 0xff00u) << 8u) | ((v >> 8u) & 0xff00u) | (v >> 24u);
|
||||
}
|
||||
|
||||
uint64_t __revll(uint64_t v) {
|
||||
v = ((v & 0x5555555555555555u) << 1u) | ((v >> 1u) & 0x5555555555555555u);
|
||||
v = ((v & 0x3333333333333333u) << 2u) | ((v >> 2u) & 0x3333333333333333u);
|
||||
v = ((v & 0x0f0f0f0f0f0f0f0fu) << 4u) | ((v >> 4u) & 0x0f0f0f0f0f0f0f0fu);
|
||||
v = ((v & 0x00ff00ff00ff00ffu) << 8u) | ((v >> 8u) & 0x00ff00ff00ff00ffu);
|
||||
return (v << 48u) | ((v & 0xffff0000u) << 16u) | ((v >> 16u) & 0xffff0000u) | (v >> 48u);
|
||||
}
|
9
src/host/pico_divider/CMakeLists.txt
Normal file
9
src/host/pico_divider/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
add_library(pico_divider INTERFACE)
|
||||
|
||||
target_sources(pico_divider INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/divider.c)
|
||||
|
||||
target_link_libraries(pico_divider INTERFACE pico_divider_headers)
|
||||
|
||||
macro(pico_set_divider_implementation TARGET IMPL)
|
||||
endmacro()
|
114
src/host/pico_divider/divider.c
Normal file
114
src/host/pico_divider/divider.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/divider.h"
|
||||
|
||||
// These functions save/restore divider state, so are safe to call from interrupts
|
||||
int32_t div_s32s32(int32_t a, int32_t b) {
|
||||
return hw_divider_s32_quotient(a, b);
|
||||
}
|
||||
|
||||
divmod_result_t divmod_s32s32(int32_t a, int32_t b) {
|
||||
return hw_divider_divmod_s32(a, b);
|
||||
}
|
||||
|
||||
uint32_t div_u32u32(uint32_t a, uint32_t b) {
|
||||
return hw_divider_u32_quotient(a, b);
|
||||
}
|
||||
|
||||
divmod_result_t divmod_u32u32(uint32_t a, uint32_t b) {
|
||||
return hw_divider_divmod_u32(a, b);
|
||||
}
|
||||
|
||||
static inline int __sign_of_64(int32_t v) {
|
||||
return v > 0 ? 1 : (v < 0 ? -1 : 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint64_t quotient;
|
||||
uint64_t remainder;
|
||||
} qr_u64;
|
||||
|
||||
typedef struct {
|
||||
int64_t quotient;
|
||||
int64_t remainder;
|
||||
} qr_s64;
|
||||
|
||||
// divides unsigned values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
|
||||
static inline qr_u64 udiv64(uint64_t a, uint64_t b) {
|
||||
qr_u64 rc;
|
||||
if (!b) {
|
||||
rc.quotient = (uint64_t)-1; // todo check this
|
||||
rc.remainder = a;
|
||||
} else {
|
||||
rc.quotient = a/b;
|
||||
rc.remainder = a%b;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// divides signed values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
|
||||
static inline qr_s64 div64(int64_t a, int64_t b) {
|
||||
qr_s64 rc;
|
||||
if (!b) {
|
||||
rc.quotient = (uint64_t)(-__sign_of_64(a));
|
||||
rc.remainder = a;
|
||||
} else {
|
||||
rc.quotient = a/b;
|
||||
rc.remainder = a%b;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int64_t div_s64s64(int64_t a, int64_t b) {
|
||||
qr_s64 qr = div64(a, b);
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
int64_t divmod_s64s64_rem(int64_t a, int64_t b, int64_t *rem) {
|
||||
qr_s64 qr = div64(a, b);
|
||||
*rem = qr.remainder;
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
int64_t divmod_s64s64(int64_t a, int64_t b) {
|
||||
qr_s64 qr = div64(a, b);
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
uint64_t div_u64u64(uint64_t a, uint64_t b) {
|
||||
qr_u64 qr = udiv64(a, b);
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
uint64_t divmod_u64u64_rem(uint64_t a, uint64_t b, uint64_t *rem) {
|
||||
qr_u64 qr = udiv64(a, b);
|
||||
*rem = qr.remainder;
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
uint64_t divmod_u64u64(uint64_t a, uint64_t b) {
|
||||
qr_u64 qr = udiv64(a, b);
|
||||
return qr.quotient;
|
||||
}
|
||||
|
||||
// these functions are slightly faster, but unsafe the divider state, so are not generally safe to be called from interrupts
|
||||
|
||||
int32_t div_s32s32_unsafe(int32_t a, int32_t b) { return div_s32s32(a,b); }
|
||||
int32_t divmod_s32s32_rem_unsafe(int32_t a, int32_t b, int32_t *rem) { return divmod_s32s32_rem(a, b, rem); }
|
||||
int64_t divmod_s32s32_unsafe(int32_t a, int32_t b) { return divmod_s32s32(a, b); }
|
||||
|
||||
uint32_t div_u32u32_unsafe(uint32_t a, uint32_t b) { return div_u32u32(a, b); }
|
||||
uint32_t divmod_u32u32_rem_unsafe(uint32_t a, uint32_t b, uint32_t *rem) { return divmod_u32u32_rem(a, b, rem); }
|
||||
uint64_t divmod_u32u32_unsafe(uint32_t a, uint32_t b) { return divmod_u32u32(a, b); }
|
||||
|
||||
int64_t div_s64s64_unsafe(int64_t a, int64_t b) { return div_s64s64(a, b); }
|
||||
int64_t divmod_s64s64_rem_unsafe(int64_t a, int64_t b, int64_t *rem) { return divmod_s64s64_rem(a, b, rem); }
|
||||
int64_t divmod_s64s64_unsafe(int64_t a, int64_t b) { return divmod_s64s64(a, b); }
|
||||
|
||||
uint64_t div_u64u64_unsafe(uint64_t a, uint64_t b) { return div_u64u64(a, b); }
|
||||
uint64_t divmod_u64u64_rem_unsafe(uint64_t a, uint64_t b, uint64_t *rem) { return divmod_u64u64_rem(a, b, rem); }
|
||||
uint64_t divmod_u64u64_unsafe(uint64_t a, uint64_t b) { return divmod_u64u64(a, b); }
|
8
src/host/pico_multicore/CMakeLists.txt
Normal file
8
src/host/pico_multicore/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
if (NOT TARGET pico_multicore)
|
||||
add_library(pico_multicore INTERFACE)
|
||||
|
||||
target_include_directories(pico_multicore INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
endif()
|
||||
|
||||
|
||||
|
44
src/host/pico_multicore/include/pico/multicore.h
Normal file
44
src/host/pico_multicore/include/pico/multicore.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_MULTICORE_H
|
||||
#define _PICO_MULTICORE_H
|
||||
|
||||
#include "pico/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void multicore_reset_core1();
|
||||
void multicore_launch_core1(void (*entry)(void));
|
||||
void multicore_launch_core1_with_stack(void (*entry)(void), uint32_t *stack_bottom, size_t stack_size_bytes);
|
||||
void multicore_sleep_core1();
|
||||
void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vector_table);
|
||||
|
||||
bool multicore_fifo_rvalid();
|
||||
bool multicore_fifo_wready();
|
||||
void multicore_fifo_push(uint32_t data);
|
||||
uint32_t multicore_fifo_pop_blocking();
|
||||
void multicore_fifo_drain();
|
||||
void multicore_fifo_clear_irq();
|
||||
int32_t multicore_fifo_get_status();
|
||||
|
||||
// call this from the lockout victim thread
|
||||
void multicore_lockout_victim_init();
|
||||
|
||||
// start locking out the other core (it will be
|
||||
bool multicore_lockout_start_timeout_us(uint64_t timeout_us);
|
||||
void multicore_lockout_start_blocking();
|
||||
|
||||
bool multicore_lockout_end_timeout_us(uint64_t timeout_us);
|
||||
void multicore_lockout_end_blocking();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
25
src/host/pico_platform/CMakeLists.txt
Normal file
25
src/host/pico_platform/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
if (NOT TARGET pico_platform_headers)
|
||||
add_library(pico_platform_headers INTERFACE)
|
||||
|
||||
target_compile_definitions(pico_platform_headers INTERFACE
|
||||
PICO_NO_HARDWARE=1
|
||||
PICO_ON_DEVICE=0
|
||||
PICO_BUILD=1
|
||||
)
|
||||
|
||||
target_include_directories(pico_platform_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET pico_platform)
|
||||
add_library(pico_platform INTERFACE)
|
||||
|
||||
target_sources(pico_platform INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/platform_base.c
|
||||
)
|
||||
|
||||
target_link_libraries(pico_platform INTERFACE pico_platform_headers pico_bit_ops ${PICO_PLATFORM_EXTRA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
function(pico_add_platform_library TARGET)
|
||||
target_link_libraries(pico_platform INTERFACE ${TARGET})
|
||||
endfunction()
|
24
src/host/pico_platform/include/hardware/platform_defs.h
Normal file
24
src/host/pico_platform/include/hardware/platform_defs.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_PLATFORM_DEFS_H
|
||||
#define _HARDWARE_PLATFORM_DEFS_H
|
||||
|
||||
#define NUM_CORES 2u
|
||||
|
||||
#define NUM_DMA_CHANNELS 12u
|
||||
|
||||
#define NUM_TIMERS 4u
|
||||
|
||||
#define NUM_IRQS 32u
|
||||
|
||||
#define NUM_SPIN_LOCKS 32u
|
||||
|
||||
#define XOSC_MHZ 12
|
||||
|
||||
#define NUM_SPIN_LOCKS 32u
|
||||
|
||||
#endif
|
137
src/host/pico_platform/include/pico/platform.h
Normal file
137
src/host/pico_platform/include/pico/platform.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_PLATFORM_H_
|
||||
#define _PICO_PLATFORM_H_
|
||||
|
||||
#include "hardware/platform_defs.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __unix__
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define __not_in_flash(grup)
|
||||
#define __not_in_flash_func(func) func
|
||||
#define __no_inline_not_in_flash_func(func)
|
||||
#define __in_flash(group)
|
||||
#define __scratch_x(group)
|
||||
#define __scratch_y(group)
|
||||
|
||||
#define __packed_aligned
|
||||
#define __packed
|
||||
|
||||
#define __time_critical_func(x) x
|
||||
#define __after_data(group)
|
||||
|
||||
//int running_on_fpga() { return false; }
|
||||
extern void tight_loop_contents();
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef __noreturn
|
||||
#define __noreturn __attribute((noreturn))
|
||||
#endif
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#ifndef __noinline
|
||||
#define __noinline __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#ifndef __aligned
|
||||
#define __aligned(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
||||
#define PICO_WEAK_FUNCTION_DEF(x) _Pragma(__STRING(weak x))
|
||||
#define PICO_WEAK_FUNCTION_IMPL_NAME(x) x
|
||||
|
||||
#else
|
||||
#ifndef __noreturn
|
||||
#define __noreturn __declspec(noreturn)
|
||||
#endif
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused
|
||||
#endif
|
||||
|
||||
#ifndef __noinline
|
||||
#define __noinline __declspec(noinline)
|
||||
#endif
|
||||
|
||||
#ifndef __aligned
|
||||
#define __aligned(x) __declspec(align(x))
|
||||
#endif
|
||||
|
||||
#ifndef __CONCAT
|
||||
#define __CONCAT(x,y) x ## y
|
||||
#endif
|
||||
|
||||
#ifndef __STRING
|
||||
#define __STRING(x) #x
|
||||
#endif()
|
||||
|
||||
#define __thread __declspec( thread )
|
||||
|
||||
#define PICO_WEAK_FUNCTION_DEF(x) __pragma(comment(linker, __STRING(/alternatename:_##x=_##x##__weak)));
|
||||
#define PICO_WEAK_FUNCTION_IMPL_NAME(x) x ## __weak
|
||||
|
||||
static __noreturn void __builtin_unreachable() {
|
||||
}
|
||||
|
||||
#include <intrin.h>
|
||||
#define __builtin_clz __lzcnt
|
||||
#endif
|
||||
|
||||
#ifndef count_of
|
||||
#define count_of(a) (sizeof(a)/sizeof((a)[0]))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((b)>(a)?(a):(b))
|
||||
#endif
|
||||
|
||||
// abort in our case
|
||||
void __noreturn __breakpoint();
|
||||
|
||||
void __noreturn panic_unsupported();
|
||||
|
||||
void __noreturn panic(const char *fmt, ...);
|
||||
|
||||
// arggggghhhh there is a weak function called sem_init used by SDL
|
||||
#define sem_init sem_init_alternative
|
||||
|
||||
extern uint32_t host_safe_hw_ptr_impl(uintptr_t x);
|
||||
// return a 32 bit handle for a raw ptr; DMA chaining for example embeds pointers in 32 bit values
|
||||
// which of course does not work if we're running the code natively on a 64 bit platforms. Therefore
|
||||
// we provide this macro which allows that code to provide a 64->32 bit mapping in host mode
|
||||
#define host_safe_hw_ptr(x) host_safe_hw_ptr_impl((uintptr_t)(x))
|
||||
void *decode_host_safe_hw_ptr(uint32_t ptr);
|
||||
|
||||
#define __fast_mul(a,b) ((a)*(b))
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
inline static int32_t __mul_instruction(int32_t a,int32_t b)
|
||||
{
|
||||
return a*b;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
46
src/host/pico_platform/platform_base.c
Normal file
46
src/host/pico_platform/platform_base.c
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
PICO_WEAK_FUNCTION_DEF(tight_loop_contents)
|
||||
|
||||
void PICO_WEAK_FUNCTION_IMPL_NAME(tight_loop_contents)() {
|
||||
|
||||
}
|
||||
|
||||
void __noreturn panic_unsupported() {
|
||||
panic("not supported");
|
||||
}
|
||||
|
||||
void hard_assertion_failure(void) {
|
||||
panic("Hard assert");
|
||||
}
|
||||
|
||||
void panic(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
puts("*** PANIC ***\n");
|
||||
if (fmt) {
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
puts("\n");
|
||||
|
||||
__breakpoint();
|
||||
}
|
||||
|
||||
void __breakpoint() {
|
||||
#ifdef _MSC_VER
|
||||
__debugbreak();
|
||||
#else
|
||||
__builtin_trap();
|
||||
#endif
|
||||
}
|
6
src/host/pico_printf/CMakeLists.txt
Normal file
6
src/host/pico_printf/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
if (NOT TARGET pico_printf)
|
||||
add_library(pico_printf INTERFACE)
|
||||
function(pico_set_printf_implementation)
|
||||
endfunction()
|
||||
endif()
|
||||
|
20
src/host/pico_stdio/CMakeLists.txt
Normal file
20
src/host/pico_stdio/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
if (NOT TARGET pico_stdio)
|
||||
add_library(pico_stdio INTERFACE)
|
||||
|
||||
target_include_directories(pico_stdio INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
target_sources(pico_stdio INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/stdio.c
|
||||
)
|
||||
add_library(pico_stdio_usb INTERFACE)
|
||||
add_library(pico_stdio_uart INTERFACE)
|
||||
add_library(pico_stdio_semihosting INTERFACE)
|
||||
|
||||
function(pico_enable_stdio_uart)
|
||||
endfunction()
|
||||
function(pico_enable_stdio_usb)
|
||||
endfunction()
|
||||
function(pico_enable_stdio_semihosting)
|
||||
endfunction()
|
||||
endif()
|
||||
|
21
src/host/pico_stdio/include/pico/stdio.h
Normal file
21
src/host/pico_stdio/include/pico/stdio.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef _PICO_STDIO_H
|
||||
#define _PICO_STDIO_H
|
||||
|
||||
typedef struct stdio_driver stdio_driver_t;
|
||||
|
||||
#define STDIO_ERROR -1
|
||||
#define STDIO_NO_INPUT -2
|
||||
|
||||
static inline void stdio_usb_init() {}
|
||||
void stdio_uart_init();
|
||||
static inline void stdio_init_all() { stdio_uart_init(); }
|
||||
static inline void stdio_filter_driver(stdio_driver_t *);
|
||||
static inline void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {}
|
||||
int getchar_timeout_us(uint32_t timeout_us);
|
||||
|
||||
#endif
|
23
src/host/pico_stdio/stdio.c
Normal file
23
src/host/pico_stdio/stdio.c
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/uart.h"
|
||||
|
||||
int getchar_timeout_us(uint32_t timeout_us) {
|
||||
absolute_time_t t = make_timeout_time_us(timeout_us);
|
||||
while (!uart_is_readable(uart_default)) {
|
||||
if (absolute_time_diff_us(t, get_absolute_time()) > 0) {
|
||||
return STDIO_NO_INPUT;
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
return uart_getc(uart_default);
|
||||
}
|
||||
|
||||
void stdio_uart_init() {
|
||||
uart_init(uart_default, 0);
|
||||
}
|
19
src/host/pico_stdlib/CMakeLists.txt
Normal file
19
src/host/pico_stdlib/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
if (NOT TARGET pico_stdlib)
|
||||
add_library(pico_stdlib INTERFACE)
|
||||
|
||||
target_sources(pico_stdlib INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/stdlib.c
|
||||
)
|
||||
|
||||
target_link_libraries(pico_stdlib INTERFACE
|
||||
pico_stdlib_headers
|
||||
pico_platform
|
||||
pico_time
|
||||
pico_divider
|
||||
pico_binary_info
|
||||
pico_printf
|
||||
pico_stdio
|
||||
hardware_gpio
|
||||
)
|
||||
endif()
|
||||
|
27
src/host/pico_stdlib/stdlib.c
Normal file
27
src/host/pico_stdlib/stdlib.c
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
void setup_default_uart() {
|
||||
|
||||
}
|
||||
|
||||
void set_sys_clock_48mhz() {
|
||||
|
||||
}
|
||||
|
||||
bool check_sys_clock_khz(uint32_t freq_khz, uint *vco_out, uint *postdiv1_out, uint *postdiv2_out) {
|
||||
*vco_out = 1000000;
|
||||
*postdiv1_out = 0;
|
||||
*postdiv2_out = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_sys_clock_pll(__unused uint32_t vco_freq, __unused uint post_div1, __unused uint post_div2) {
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user