From d8dc704518e4ccd96c47c14f2ad07ff28b58f31b Mon Sep 17 00:00:00 2001 From: Manuel Eggimann Date: Wed, 7 Dec 2022 21:35:03 +0100 Subject: [PATCH] Add first driver draft for new gpio peripheral --- drivers/gpio/gpio.c | 41 +++ drivers/gpio/include/gpio.h | 64 +++++ drivers/gpio/include/gpio_hal.h | 86 ++++++ include/archi/chips/pulpissimo/memory_map.h | 7 +- include/archi/chips/pulpissimo/properties.h | 5 + include/common/bitfield.h | 285 ++++++++++++++++++++ 6 files changed, 485 insertions(+), 3 deletions(-) create mode 100644 drivers/gpio/gpio.c create mode 100644 drivers/gpio/include/gpio.h create mode 100644 drivers/gpio/include/gpio_hal.h create mode 100644 include/common/bitfield.h diff --git a/drivers/gpio/gpio.c b/drivers/gpio/gpio.c new file mode 100644 index 0000000..7cb7aa8 --- /dev/null +++ b/drivers/gpio/gpio.c @@ -0,0 +1,41 @@ +#include "gpio.h" +#include "common/bitfield.h" +#include "gpio_hal.h" +#include "pulp.h" + + +void gpio_configure(uint32_t number, uint32_t direction){ + // Now configure the GPIO peripheral + uint32_t addr = ARCHI_GPIO_ADDR + GPIO_MODE_REG_OFFSET_BASE + number/16*4; + uint32_t reg = archi_read32(addr); + bitfield_field32_t field; + field.mask = 0x3; + field.index = number%16*2; + reg = bitfield_field32_write(reg, field, (direction == GPIO_DIRECTION_INPUT)? GPIO_MODE_REG_VALUE_INPUT_ONLY: GPIO_MODE_REG_VALUE_OUTPUT_ACTIVE); + archi_write32(addr, reg); + // Enable/Disable input sampling + addr = ARCHI_GPIO_ADDR + GPIO_EN_INPUT_REG_OFFSET_BASE + number/32*4; + reg = archi_read32(addr); + reg = bitfield_bit32_write(reg, number%32, direction == SIRACUSA_GPIO_DIRECTION_INPUT); + archi_write32(addr, reg); + hal_compiler_barrier(); +} + + +void gpio_set(uint32_t number, uint32_t value){ + uint32_t addr; + if (value == 1) { + addr = ARCHI_GPIO_ADDR + GPIO_SET_REG_OFFSET_BASE + number/32*4; + } else { + addr = SIRACUSA_GPIO_BASE_ADDR + GPIO_CLEAR_REG_OFFSET_BASE + number/32*4; + } + archi_write32(addr, 1<<(number%32)); + hal_compiler_barrier(); +} + +uint32_t gpio_get(uint32_t number){ + uint32_t addr = SIRACUSA_GPIO_BASE_ADDR + GPIO_GPIO_IN_0_REG_OFFSET + number/32*4; + uint32_t reg = archi_read32(addr); + return bitfield_bit32_read(reg, number%32); + hal_compiler_barrier(); +} diff --git a/drivers/gpio/include/gpio.h b/drivers/gpio/include/gpio.h new file mode 100644 index 0000000..e30ed08 --- /dev/null +++ b/drivers/gpio/include/gpio.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 ETH Zurich, University of Bologna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Title: gpio.h + * Author: Manuel Eggimann + * Date: 09.03.2022 + * + * Description: Driver for the PULP GPIO peripheral + */ +#ifndef GPIO_H +#define GPIO_H + +#include + +#define GPIO_DIRECTION_OUTPUT 1 +#define GPIO_DIRECTION_INPUT 0 + + +/** + * Configure the provided GPIO as either an input or output. + * + * It is the user's responsibility to ensure that the GPIO to be configures is + * actually exposed to its corresponding IO pad. I.e. if your SoC contains an IO + * multiplexer you have to make sure you configure it apropriately to expose the + * GPIO on the pad. + * + * @param number The GPIO number to confgiure. You can either use directly an integer number or on of the PAD_GPIOXX macros. + * @param direction Either GPIO_DIRECTION_INPUT (0) or GPIO_DIRECTION_OUTPUT + */ +void gpio_configure(uint32_t number, uint32_t direction); + +/** + * Get the current value of the GPIO. + * + * The GPIO must have been configured as an input using configure_gpio(). + * + * @param number The GPIO number to confgiure. You can either use directly an integer number or on of the PAD_GPIOXX macros. + * @returns 0 if the gpio is currently at logic level LOW or 1 if it is at logic level HIGH. + */ +uint32_t gpio_get(uint32_t number); + +/** + * Drive the GPIO to the provided value. + * + * The corresponding GPIO must have been configured as an output using configure_gpio(). + * + * @param number The GPIO number to confgiure. You can either use directly an integer number or on of the PAD_GPIOXX macros. + * @param value Either 0 or 1. + */ +void gpio_set(uint32_t number, uint32_t value); + +#endif /* GPIO_H */ diff --git a/drivers/gpio/include/gpio_hal.h b/drivers/gpio/include/gpio_hal.h new file mode 100644 index 0000000..a54a0fa --- /dev/null +++ b/drivers/gpio/include/gpio_hal.h @@ -0,0 +1,86 @@ +// Generic Hardware Abstraction Layer for the GPIO peripheral + +#ifndef _GPIO_REG_DEFS_ +#define _GPIO_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Info register that contains information about this peripheral. +#define GPIO_INFO_REG_OFFSET 0x0 +#define GPIO_INFO_REG_GPIO_CNT_MASK 0x3ff +#define GPIO_INFO_REG_GPIO_CNT_OFFSET 0 +#define GPIO_INFO_REG_GPIO_CNT_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_INFO_GPIO_CNT_MASK, .index = GPIO_INFO_GPIO_CNT_OFFSET }) +#define GPIO_INFO_REG_VERSION_MASK 0x3ff +#define GPIO_INFO_REG_VERSION_OFFSET 10 +#define GPIO_INFO_REG_VERSION_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_INFO_VERSION_MASK, .index = GPIO_INFO_VERSION_OFFSET }) + +// Global configuration register for the peripheral +#define GPIO_CFG_REG_OFFSET 0x4 +#define GPIO_CFG_INTRPT_MODE_BIT 0 + +// Set the IO Mode of the GPIO. (common parameters) +#define GPIO_MODE_REG_OFFSET_BASE 0x8 +#define GPIO_MODE_REG_VALUE_INPUT_ONLY 0x0 +#define GPIO_MODE_REG_VALUE_OUTPUT_ACTIVE 0x1 +#define GPIO_MODE_REG_VALUE_OPEN_DRAIN0 0x2 +#define GPIO_MODE_REG_VALUE_OPEN_DRAIN1 0x3 + +// Enable sampling on the corresponding GPIO (common parameters) +#define GPIO_EN_INPUT_REG_OFFSET_BASE 0x80 + +// Read the current input values of all GPIOs. +#define GPIO_INPUT_REG_OFFSET_BASE 0x100 + +// Set the output value of the corresponding GPIOs. +#define GPIO_OUTPUT_REG_OFFSET_BASE 0x180 + +// For each asserted bit in this register, set the corresponding bit in the +// padout register. +#define GPIO_SET_REG_OFFSET_BASE 0x200 + +// For each asserted bit in this register, clear the corresponding bit in the +// padout register. +#define GPIO_CLEAR_REG_OFFSET_BASE 0X280 + +// For each asserted bit in this register, toggle the corresponding bit in +// the padout register. +#define GPIO_TOGGLE_REG_OFFSET_BASE 0X300 + +// Enable Interrupts on rising edges for the corresponding GPIO +#define GPIO_INTRPT_RISE_EN_REG_OFFSET_BASE 0x380 + +// Enable Interrupts on falling edges for the corresponding GPIO +#define GPIO_INTRPT_FALL_EN_REG_OFFSET_BASE 0x400 + +// Enable logic high level-sensitive Interrupts on the corresponding GPIO +#define GPIO_INTRPT_HIGH_EN_REG_OFFSET_BASE 0x480 + +// Enable logic low level-sensitive Interrupts on the corresponding GPIO +#define GPIO_INTRPT_LOW_EN_REG_OFFSET_BASE 0x500 + +// Asserted if there is any pending interrupts on corresponding GPIOs. +#define GPIO_INTRPT_STATUS_REG_OFFSET_BASE 0x580 + +// Asserted if there is a pending rise interrupts on corresponding GPIOs. +#define GPIO_INTRPT_RISE_STATUS_REG_OFFSET_BASE 0x600 + +// Asserted if there is any pending fall interrupts on corresponding GPIOs. +#define GPIO_INTRPT_FALL_STATUS_REG_OFFSET_BASE 0x680 + +// Asserted if there is any pending high-level interrupts on corresponding +// GPIOs. +#define GPIO_INTRPT_LVL_HIGH_STATUS_REG_OFFSET_BASE 0x700 + +// Asserted if there is any pending low-level interrupts on corresponding +// GPIOs. +#define GPIO_INTRPT_LVL_LOW_STATUS_REG_OFFSET_BASE 0x780 + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _GPIO_REG_DEFS_ +// End generated register defines for gpio diff --git a/include/archi/chips/pulpissimo/memory_map.h b/include/archi/chips/pulpissimo/memory_map.h index 4e226bb..8425b4b 100644 --- a/include/archi/chips/pulpissimo/memory_map.h +++ b/include/archi/chips/pulpissimo/memory_map.h @@ -26,9 +26,8 @@ #define ARCHI_SOC_PERIPHERALS_ADDR 0x1A100000 -#define ARCHI_FC_TIMER_SIZE 0x00000800 +#define ARCHI_FC_TIMER_SIZE 0x00000800 -#define ARCHI_FLL_OFFSET 0x00000000 #define ARCHI_GPIO_OFFSET 0x00001000 #define ARCHI_UDMA_OFFSET 0x00002000 #define ARCHI_APB_SOC_CTRL_OFFSET 0x00004000 @@ -37,7 +36,9 @@ #define ARCHI_FC_TIMER_OFFSET 0x0000B000 #define ARCHI_FC_HWPE_OFFSET 0x0000C000 #define ARCHI_STDOUT_OFFSET 0x0000F000 - +// Chip control port +#define ARCHI_FLL_OFFSET 0x00020000 +#define ARCHI_PAD_CFG_OFFSET 0x00021000 #define ARCHI_GPIO_ADDR ( ARCHI_SOC_PERIPHERALS_ADDR + ARCHI_GPIO_OFFSET ) diff --git a/include/archi/chips/pulpissimo/properties.h b/include/archi/chips/pulpissimo/properties.h index dcb9f47..9c31c15 100644 --- a/include/archi/chips/pulpissimo/properties.h +++ b/include/archi/chips/pulpissimo/properties.h @@ -243,4 +243,9 @@ #define ARCHI_FC_EVT_QUEUE_ERROR 29 +/* + * GPIO Count + */ +#define GPIOCount 32 + #endif diff --git a/include/common/bitfield.h b/include/common/bitfield.h new file mode 100644 index 0000000..3177f14 --- /dev/null +++ b/include/common/bitfield.h @@ -0,0 +1,285 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_LIB_BASE_BITFIELD_H_ +#define OPENTITAN_SW_DEVICE_LIB_BASE_BITFIELD_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * @file + * @brief Bitfield Manipulation Functions + */ + +/** + * All the bitfield functions are pure (they do not modify their arguments), so + * the result must be used. We enable warnings to ensure this happens. + */ +#define BITFIELD_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +/** + * A field of a 32-bit bitfield. + * + * The following field definition: `{ .mask = 0b11, .index = 12 }` + * + * Denotes the X-marked bits in the following 32-bit bitfield: + * + * field: 0b--------'--------'--XX----'-------- + * index: 31 0 + * + * Restrictions: The index plus the width of the mask must not be greater than + * 31. + */ +typedef struct bitfield_field32 { + /** The field mask. Usually all ones. */ + uint32_t mask; + /** The field position in the bitfield, counting from the zero-bit. */ + uint32_t index; +} bitfield_field32_t; + +/** + * Reads a value from `field` in `bitfield`. + * + * This function uses the `field` parameter to read the value from `bitfield`. + * The resulting value will be shifted right and zero-extended so the field's + * zero-bit is the return value's zero-bit. + * + * @param bitfield Bitfield to get the field from. + * @param field Field to read out from. + * @return Zero-extended `field` from `bitfield`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline uint32_t bitfield_field32_read(uint32_t bitfield, + bitfield_field32_t field) { + return (bitfield >> field.index) & field.mask; +} + +/** + * Writes `value` to `field` in `bitfield`. + * + * This function uses the `field` parameter to set specific bits in `bitfield`. + * The relevant portion of `bitfield` is zeroed before the bits are set to + * `value`. + * + * @param bitfield Bitfield to set the field in. + * @param field Field within bitfield to be set. + * @param value Value for the new field. + * @return `bitfield` with `field` set to `value`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline uint32_t bitfield_field32_write(uint32_t bitfield, + bitfield_field32_t field, + uint32_t value) { + bitfield &= ~(field.mask << field.index); + bitfield |= (value & field.mask) << field.index; + return bitfield; +} + +/** + * A single bit in a 32-bit bitfield. + * + * This denotes the position of a single bit, counting from the zero-bit. + * + * For instance, `(bitfield_bit_index_t)4` denotes the X-marked bit in the + * following 32-bit bitfield: + * + * field: 0b--------'--------'--------'---X---- + * index: 31 0 + * + * Restrictions: The value must not be greater than 31. + */ +typedef uint32_t bitfield_bit32_index_t; + +/** + * Turns a `bitfield_bit32_index_t` into a `bitfield_field32_t` (which is more + * general). + * + * @param bit_index The corresponding single bit to turn into a field. + * @return A 1-bit field that corresponds to `bit_index`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline bitfield_field32_t bitfield_bit32_to_field32( + bitfield_bit32_index_t bit_index) { + return (bitfield_field32_t){ + .mask = 0x1, .index = bit_index, + }; +} + +/** + * Reads the `bit_index`th bit in `bitfield`. + * + * @param bitfield Bitfield to get the bit from. + * @param bit_index Bit to read. + * @return `true` if the bit was one, `false` otherwise. + */ +BITFIELD_WARN_UNUSED_RESULT +inline bool bitfield_bit32_read(uint32_t bitfield, + bitfield_bit32_index_t bit_index) { + return bitfield_field32_read(bitfield, + bitfield_bit32_to_field32(bit_index)) == 0x1u; +} + +/** + * Writes `value` to the `bit_index`th bit in `bitfield`. + * + * @param bitfield Bitfield to update the bit in. + * @param bit_index Bit to update. + * @param value Bit value to write to `bitfield`. + * @return `bitfield` with the `bit_index`th bit set to `value`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline uint32_t bitfield_bit32_write(uint32_t bitfield, + bitfield_bit32_index_t bit_index, + bool value) { + return bitfield_field32_write(bitfield, bitfield_bit32_to_field32(bit_index), + value ? 0x1u : 0x0u); +} + +/** + * Find First Set Bit + * + * Returns one plus the index of the least-significant 1-bit of a 32-bit word. + * + * For instance, `bitfield_find_first_set32(field)` of the below 32-bit value + * returns `5`. + * + * field: 0b00000000'00000000'11111111'00010000 + * index: 31 0 + * + * This is the canonical definition for the GCC/Clang builtin `__builtin_ffs`, + * and hence takes and returns a signed integer. + * + * @param bitfield Bitfield to find the first set bit in. + * @return One plus the index of the least-significant 1-bit of `bitfield`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline int32_t bitfield_find_first_set32(int32_t bitfield) { + return __builtin_ffs(bitfield); +} + +/** + * Count Leading Zeroes + * + * Returns the number of leading 0-bits in `bitfield`, starting at the most + * significant bit position. If `bitfield` is 0, the result is 32, to match the + * RISC-V B Extension. + * + * For instance, `bitfield_count_leading_zeroes32(field)` of the below 32-bit + * value returns `16`. + * + * field: 0b00000000'00000000'11111111'00010000 + * index: 31 0 + * + * This is the canonical definition for the GCC/Clang builtin `__builtin_clz`, + * and hence returns a signed integer. + * + * @param bitfield Bitfield to count leading 0-bits from. + * @return The number of leading 0-bits in `bitfield`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline int32_t bitfield_count_leading_zeroes32(uint32_t bitfield) { + return (bitfield != 0) ? __builtin_clz(bitfield) : 32; +} + +/** + * Count Trailing Zeroes + * + * Returns the number of trailing 0-bits in `bitfield`, starting at the least + * significant bit position. If `bitfield` is 0, the result is 32, to match the + * RISC-V B Extension. + * + * For instance, `bitfield_count_trailing_zeroes32(field)` of the below 32-bit + * value returns `4`. + * + * field: 0b00000000'00000000'11111111'00010000 + * index: 31 0 + * + * This is the canonical definition for the GCC/Clang builtin `__builtin_ctz`, + * and hence returns a signed integer. + * + * @param bitfield Bitfield to count trailing 0-bits from. + * @return The number of trailing 0-bits in `bitfield`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline int32_t bitfield_count_trailing_zeroes32(uint32_t bitfield) { + return (bitfield != 0) ? __builtin_ctz(bitfield) : 32; +} + +/** + * Count Set Bits + * + * Returns the number of 1-bits in `bitfield`. + * + * For instance, `bitfield_popcount32(field)` of the below 32-bit value returns + * `9`. + * + * field: 0b00000000'00000000'11111111'00010000 + * index: 31 0 + * + * This is the canonical definition for the GCC/Clang builtin + * `__builtin_popcount`, and hence returns a signed integer. + * + * @param bitfield Bitfield to count 1-bits from. + * @return The number of 1-bits in `bitfield`. + */ +BITFIELD_WARN_UNUSED_RESULT +inline int32_t bitfield_popcount32(uint32_t bitfield) { + return __builtin_popcount(bitfield); +} + +/** + * Parity + * + * Returns the number of 1-bits in `bitfield`, modulo 2. + * + * For instance, `bitfield_parity32(field)` of the below 32-bit value returns + * `1`. + * + * field: 0b00000000'00000000'11111111'00010000 + * index: 31 0 + * + * This is the canonical definition for the GCC/Clang builtin + * `__builtin_parity`, and hence returns a signed integer. + * + * @param bitfield Bitfield to count 1-bits from. + * @return The number of 1-bits in `bitfield`, modulo 2. + */ +BITFIELD_WARN_UNUSED_RESULT +inline int32_t bitfield_parity32(uint32_t bitfield) { + return __builtin_parity(bitfield); +} + +/** + * Byte Swap + * + * Returns `field` with the order of the bytes reversed. Bytes here always means + * exactly 8 bits. + * + * For instance, `byteswap(field)` of the below 32-bit value returns `1`. + * + * field: 0bAAAAAAAA'BBBBBBBB'CCCCCCCC'DDDDDDDD + * index: 31 0 + * returns: 0bDDDDDDDD'CCCCCCCC'BBBBBBBB'AAAAAAAA + * + * This is the canonical definition for the GCC/Clang builtin + * `__builtin_bswap32`. + * + * @param bitfield Bitfield to reverse bytes of. + * @return `bitfield` with the order of bytes reversed. + */ +BITFIELD_WARN_UNUSED_RESULT +inline uint32_t bitfield_byteswap32(uint32_t bitfield) { + return __builtin_bswap32(bitfield); +} + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // OPENTITAN_SW_DEVICE_LIB_BASE_BITFIELD_H_