diff --git a/sw/applications/example_dma/main.c b/sw/applications/example_dma/main.c index 02b31d39f..bbab36980 100644 --- a/sw/applications/example_dma/main.c +++ b/sw/applications/example_dma/main.c @@ -10,6 +10,7 @@ #include "core_v_mini_mcu.h" #include "x-heep.h" #include "csr.h" +#include "rv_plic.h" #define TEST_SINGULAR_MODE #define TEST_PENDING_TRANSACTION @@ -343,6 +344,9 @@ int main(int argc, char *argv[]) PRINTF(" TESTING WINDOW INTERRUPT "); PRINTF("\n\n\r===================================\n\n\r"); + plic_Init(); + plic_irq_set_priority( DMA_WINDOW_INTR, 1); + plic_irq_set_enabled( DMA_WINDOW_INTR, kPlicToggleEnabled); window_intr_flag = 0; diff --git a/sw/applications/example_gpio_intr/main.c b/sw/applications/example_gpio_intr/main.c index abcc91ddb..acad473a9 100644 --- a/sw/applications/example_gpio_intr/main.c +++ b/sw/applications/example_gpio_intr/main.c @@ -57,6 +57,20 @@ plic_result_t plic_res; +uint8_t gpio_intr_flag = 0; + +void handler_1() +{ + PRINTF("handler 1\n"); + gpio_intr_flag = 1; +} + +void handler_2() +{ + PRINTF("handler 2\n"); + gpio_intr_flag = 1; +} + int main(int argc, char *argv[]) { @@ -96,7 +110,6 @@ int main(int argc, char *argv[]) // Set mie.MEIE bit to one to enable machine-level external interrupts const uint32_t mask = 1 << 11; CSR_SET_BITS(CSR_REG_MIE, mask); - plic_intr_flag = 0; //gpio_reset_all(); gpio_result_t gpio_res; @@ -125,15 +138,32 @@ int main(int argc, char *argv[]) return -1; } + gpio_assign_irq_handler( GPIO_INTR, &handler_1 ); + gpio_intr_flag = 0; + + PRINTF("Write 1 to GPIO 30 and wait for interrupt...\n\r"); + while(gpio_intr_flag == 0) { + // disable_interrupts + // this does not prevent waking up the core as this is controlled by the MIP register + CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); + gpio_write(GPIO_TB_OUT, true); + // wait_for_interrupt(); + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + } + + gpio_assign_irq_handler( GPIO_INTR, &handler_2 ); + gpio_intr_flag = 0; + PRINTF("Write 1 to GPIO 30 and wait for interrupt...\n\r"); - while(plic_intr_flag==0) { + while(gpio_intr_flag == 0) { // disable_interrupts // this does not prevent waking up the core as this is controlled by the MIP register CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); gpio_write(GPIO_TB_OUT, true); - wait_for_interrupt(); + //wait_for_interrupt(); CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); } + PRINTF("Success\n\r"); return EXIT_SUCCESS; diff --git a/sw/applications/example_i2s/main.c b/sw/applications/example_i2s/main.c index 29847d9cc..dc8a5c1d0 100644 --- a/sw/applications/example_i2s/main.c +++ b/sw/applications/example_i2s/main.c @@ -28,6 +28,7 @@ #include #include "core_v_mini_mcu.h" + #include "x-heep.h" #include "i2s.h" #include "i2s_structs.h" diff --git a/sw/device/lib/drivers/dma/dma.c b/sw/device/lib/drivers/dma/dma.c index 581faddef..c30b6105a 100644 --- a/sw/device/lib/drivers/dma/dma.c +++ b/sw/device/lib/drivers/dma/dma.c @@ -1,18 +1,18 @@ /* ******************* ******************************* C SOURCE FILE ***************************** -** ******************* -** -** project : X-HEEP -** filename : dma.c -** version : 1 -** date : 13/02/23 -** +** ******************* +** +** project : X-HEEP +** filename : dma.c +** version : 1 +** date : 13/02/23 +** *************************************************************************** -** -** Copyright (c) EPFL contributors. -** All rights reserved. -** +** +** Copyright (c) EPFL contributors. +** All rights reserved. +** *************************************************************************** */ @@ -21,7 +21,7 @@ /** * @file dma.c * @date 13/02/23 -* @brief The Direct Memory Access (DMA) driver to set up and use the DMA +* @brief The Direct Memory Access (DMA) driver to set up and use the DMA * peripheral */ @@ -38,7 +38,6 @@ /* To manage interrupts. */ #include "fast_intr_ctrl.h" -#include "rv_plic.h" #include "csr.h" #include "stdasm.h" @@ -56,10 +55,10 @@ /** * Returns the mask to enable/disable DMA interrupts. */ -#define DMA_CSR_REG_MIE_MASK (( 1 << 19 ) | (1 << 11 ) ) // @ToDo Add definitions for this 19 and 11 +#define DMA_CSR_REG_MIE_MASK (( 1 << 19 ) | (1 << 11 ) ) // @ToDo Add definitions for this 19 and 11 /** - * Size of a register of 32 bits. + * Size of a register of 32 bits. */ #define DMA_REGISTER_SIZE_BYTES sizeof(int) @@ -74,18 +73,18 @@ #define DMA_HALF_WORD_ALIGN_MASK 1 /** - * Selection offset to be used as index for when a register is written from - * the beginning. + * Selection offset to be used as index for when a register is written from + * the beginning. */ #define DMA_SELECTION_OFFSET_START 0 /** - * A small window size might result in loss of syncronism. If the processing - * of the window takes longer than the time it takes to the DMA to finish the - * next window, the application will not be able to cope. Although "how small - * is too small" is highly dependent on the length of the processing, this - * flag will be raised when the transaction and window size ratio is smaller - * than this arbitrarily chosen ratio as a mere reminder. + * A small window size might result in loss of syncronism. If the processing + * of the window takes longer than the time it takes to the DMA to finish the + * next window, the application will not be able to cope. Although "how small + * is too small" is highly dependent on the length of the processing, this + * flag will be raised when the transaction and window size ratio is smaller + * than this arbitrarily chosen ratio as a mere reminder. */ #define DMA_DEFAULT_TRANS_TO_WIND_SIZE_RATIO_THRESHOLD 4 @@ -97,18 +96,18 @@ /****************************************************************************/ /** - * Interrupts must be enabled in the INTERRUPT register of the DMA. - * Only one at a time. In case more than one is interrupt is to be triggered, + * Interrupts must be enabled in the INTERRUPT register of the DMA. + * Only one at a time. In case more than one is interrupt is to be triggered, * at the same time (last byte of a transaction of size multiple of the window - * size) only the lowest ID is triggered. - */ + * size) only the lowest ID is triggered. + */ typedef enum { INTR_EN_NONE = 0x0, /*!< No interrupts should be triggered. */ - INTR_EN_TRANS_DONE = 0x1, /*!< The TRANS_DONE interrupt is a fast - interrupt that is triggered once the whole transaction has finished. */ - INTR_EN_WINDOW_DONE = 0x2, /*!< The WINDOW_DONE interrupt is a PLIC - interrupt that is triggered every given number of bytes (set in the + INTR_EN_TRANS_DONE = 0x1, /*!< The TRANS_DONE interrupt is a fast + interrupt that is triggered once the whole transaction has finished. */ + INTR_EN_WINDOW_DONE = 0x2, /*!< The WINDOW_DONE interrupt is a PLIC + interrupt that is triggered every given number of bytes (set in the transaction configuration as win_du). */ INTR_EN__size, } inter_en_t; @@ -120,81 +119,81 @@ typedef enum /****************************************************************************/ /** - * @brief Creates a target that can be used to perform transactions. + * @brief Creates a target that can be used to perform transactions. * @param p_tgt Pointer to the dma_target_t structure where configuration - * should be allocated. The content of this pointer must be a static variable. - * @return A configuration flags mask. Each individual flag can be accessed with + * should be allocated. The content of this pointer must be a static variable. + * @return A configuration flags mask. Each individual flag can be accessed with * a bitwise AND ( ret & DMA_CONFIG_* ). */ dma_config_flags_t validate_target( dma_target_t *p_tgt ); /** - * @brief Creates an environment where targets can be added. An environment - * is, for instance, a RAM block, or section of memory that can be used by - * the DMA. - * Properly defining an environment can prevent the DMA from accessing - * restricted memory regions. + * @brief Creates an environment where targets can be added. An environment + * is, for instance, a RAM block, or section of memory that can be used by + * the DMA. + * Properly defining an environment can prevent the DMA from accessing + * restricted memory regions. * Targets for peripherals do not need an environment. - * @param p_env Pointer to the dma_env_t structure where configuration - * should be allocated. The content of this pointer must be a static variable. - * @return A configuration flags mask. Each individual flag can be accessed - * with a bitwise AND ( ret & DMA_CONFIG_* ). It is not recommended to query - * the result from inside environment structure as an error could have + * @param p_env Pointer to the dma_env_t structure where configuration + * should be allocated. The content of this pointer must be a static variable. + * @return A configuration flags mask. Each individual flag can be accessed + * with a bitwise AND ( ret & DMA_CONFIG_* ). It is not recommended to query + * the result from inside environment structure as an error could have * appeared before the creation of the structure. */ dma_config_flags_t validate_environment( dma_env_t *p_env ); /** - * @brief Gets how misaligned a pointer is, taking into account the data type - * size. - * @param p_ptr The source or destination pointer. - * @return How misaligned the pointer is, in bytes. + * @brief Gets how misaligned a pointer is, taking into account the data type + * size. + * @param p_ptr The source or destination pointer. + * @return How misaligned the pointer is, in bytes. */ -static inline uint8_t get_misalignment_b( uint8_t *p_ptr, +static inline uint8_t get_misalignment_b( uint8_t *p_ptr, dma_data_type_t p_type ); /** - * @brief Determines whether a given region will fit before the end of an + * @brief Determines whether a given region will fit before the end of an * environment. * @param p_start Pointer to the beginning of the region. * @param p_end Pointer to the last byte of the environment. * @param p_type The data type to be transferred. - * @param p_size_du The number of data units to be transferred. Must be + * @param p_size_du The number of data units to be transferred. Must be * non-zero. - * @param p_inc_du The size in data units of each increment. + * @param p_inc_du The size in data units of each increment. * @retval 1 There is an outbound. - * @retval 0 There is NOT an outbound. + * @retval 0 There is NOT an outbound. */ -static inline uint8_t is_region_outbound( uint8_t *p_start, - uint8_t *p_end, - uint32_t p_type, - uint32_t p_size_du, +static inline uint8_t is_region_outbound( uint8_t *p_start, + uint8_t *p_end, + uint32_t p_type, + uint32_t p_size_du, uint32_t p_inc_du ); /** - * @brief Writes a given value into the specified register. Its operation + * @brief Writes a given value into the specified register. Its operation * mimics that of bitfield_field32_write(), but does not require the use of - * a field structure, that is not always provided in the _regs.h file. + * a field structure, that is not always provided in the _regs.h file. * @param p_val The value to be written. * @param p_offset The register's offset from the peripheral's base address * where the target register is located. * @param p_mask The variable's mask to only modify its bits inside the whole * register. * @param p_sel The selection index (i.e. From which bit inside the register - * the value is to be written). + * the value is to be written). */ -static inline void write_register( uint32_t p_val, - uint32_t p_offset, - uint32_t p_mask, +static inline void write_register( uint32_t p_val, + uint32_t p_offset, + uint32_t p_mask, uint8_t p_sel ); /** - * @brief Analyzes a target to determine the size of its increment (in bytes). - * @param p_tgt A pointer to the target to analyze. + * @brief Analyzes a target to determine the size of its increment (in bytes). + * @param p_tgt A pointer to the target to analyze. * @return The number of bytes of the increment. - */ + */ static inline uint32_t get_increment_b( dma_target_t * p_tgt ); @@ -211,18 +210,18 @@ static inline uint32_t get_increment_b( dma_target_t * p_tgt ); /****************************************************************************/ /** - * Control Block (CB) of the DMA peripheral. - * Has variables and constant necessary/useful for its control. + * Control Block (CB) of the DMA peripheral. + * Has variables and constant necessary/useful for its control. */ static struct { /** - * Pointer to the transaction to be performed. + * Pointer to the transaction to be performed. */ dma_trans_t* trans; - + /** - * Flag to lower as soon as a transaction is launched, and raised by the + * Flag to lower as soon as a transaction is launched, and raised by the * interrupt handler once it has finished. Only used when the end event is * set to INTR_WAIT. */ @@ -231,7 +230,7 @@ static struct /** * memory mapped structure of a DMA. */ - dma *peri; + dma *peri; }dma_cb; @@ -242,11 +241,32 @@ static struct /** **/ /****************************************************************************/ +void handler_irq_dma(uint32_t id) +{ + /* + * Call the weak implementation provided in this module, + * or the non-weak implementation. + */ + dma_intr_handler_window_done(); +} + +void fic_irq_dma(void) +{ + /* The flag is raised so the waiting loop can be broken.*/ + dma_cb.intrFlag = 1; + + /* + * Call the weak implementation provided in this module, + * or the non-weak implementation. + */ + dma_intr_handler_trans_done(); +} + void dma_init( dma *peri ) { - /* + /* * If a DMA peripheral was provided, use that one, otherwise use the - * integrated one. + * integrated one. */ dma_cb.peri = peri ? peri : dma_peri; @@ -258,14 +278,14 @@ void dma_init( dma *peri ) dma_cb.peri->SIZE = 0; dma_cb.peri->PTR_INC = 0; dma_cb.peri->SLOT = 0; - dma_cb.peri->DATA_TYPE = 0; + dma_cb.peri->DATA_TYPE = 0; dma_cb.peri->MODE = 0; dma_cb.peri->WINDOW_SIZE = 0; dma_cb.peri->INTERRUPT_EN = 0; } -dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, - dma_en_realign_t p_enRealign, +dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, + dma_en_realign_t p_enRealign, dma_perf_checks_t p_check ) { /* @@ -274,62 +294,62 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, /* Data type is not necessary. If something is provided anyways it should be valid.*/ - DMA_STATIC_ASSERT( p_trans->type < DMA_DATA_TYPE__size, + DMA_STATIC_ASSERT( p_trans->type < DMA_DATA_TYPE__size, "Data type not valid"); /* Transaction mode should be a valid mode. */ - DMA_STATIC_ASSERT( p_trans->mode < DMA_TRANS_MODE__size, + DMA_STATIC_ASSERT( p_trans->mode < DMA_TRANS_MODE__size, "Transaction mode not valid"); /* The end event should be a valid end event. */ - DMA_STATIC_ASSERT( p_trans->end < DMA_TRANS_END__size, + DMA_STATIC_ASSERT( p_trans->end < DMA_TRANS_END__size, "End event not valid"); /* The alignment permission should be a valid permission. */ - DMA_STATIC_ASSERT( p_enRealign < DMA_ENABLE_REALIGN__size, + DMA_STATIC_ASSERT( p_enRealign < DMA_ENABLE_REALIGN__size, "Alignment not valid"); /* The checks request should be a valid request. */ - DMA_STATIC_ASSERT( p_check < DMA_PERFORM_CHECKS__size, + DMA_STATIC_ASSERT( p_check < DMA_PERFORM_CHECKS__size, "Check request not valid"); /* * CHECK IF TARGETS HAVE ERRORS */ - /* + /* * The transaction is NOT created if the targets include errors. - * A successful target validation has to be done before loading it to the + * A successful target validation has to be done before loading it to the * DMA. */ uint8_t errorSrc = validate_target( p_trans->src ); uint8_t errorDst = validate_target( p_trans->dst ); - /* - * If there are any errors or warnings in the valdiation of the targets, + /* + * If there are any errors or warnings in the valdiation of the targets, * they are added to the transaction flags, adding the source/destination - * identifying flags as well. + * identifying flags as well. */ p_trans->flags |= errorSrc ? (errorSrc | DMA_CONFIG_SRC ) : DMA_CONFIG_OK; - p_trans->flags |= errorDst ? (errorDst | DMA_CONFIG_SRC ) : DMA_CONFIG_OK; + p_trans->flags |= errorDst ? (errorDst | DMA_CONFIG_SRC ) : DMA_CONFIG_OK; /* If a critical error was detected, no further action is performed. */ if( p_trans->flags & DMA_CONFIG_CRITICAL_ERROR ) { return p_trans->flags; } - + /* - * CHECK IF THERE ARE TRIGGER INCONSISTENCIES + * CHECK IF THERE ARE TRIGGER INCONSISTENCIES */ - /* - * The DMA can only handle one trigger at a time, therefore, if the two + /* + * The DMA can only handle one trigger at a time, therefore, if the two * targets require a trigger, the transaction has to be discarded. - * None of the two values can be taken by default because this - * inconsistency is probably a result of an error (likely wrong target + * None of the two values can be taken by default because this + * inconsistency is probably a result of an error (likely wrong target * selection). */ if( p_check ) { if( p_trans->src->trig != DMA_TRIG_MEMORY - && p_trans->dst->trig != DMA_TRIG_MEMORY ) + && p_trans->dst->trig != DMA_TRIG_MEMORY ) { p_trans->flags |= DMA_CONFIG_INCOMPATIBLE; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; @@ -338,56 +358,56 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, } /* - * CHECK IF THERE IS A MODE INCONSISTENCY + * CHECK IF THERE IS A MODE INCONSISTENCY */ - - /* + + /* * Memory to Memory in circular mode is not only of dubious usefulness, but also * risky. Depending on the MCU properties, the buffer size, the window size and * length of the ISR, the CPU could miss consecutive interrupts or even enter in - * an irreversible loop state. + * an irreversible loop state. * This issue is less likely to happen with a properly set Memory-to-peripheral * configuration, so circular mode is allowed. */ if( p_check ) { - if( p_trans->src->trig == DMA_TRIG_MEMORY - && p_trans->dst->trig == DMA_TRIG_MEMORY - && p_trans->mode == DMA_TRANS_MODE_CIRCULAR ) + if( p_trans->src->trig == DMA_TRIG_MEMORY + && p_trans->dst->trig == DMA_TRIG_MEMORY + && p_trans->mode == DMA_TRANS_MODE_CIRCULAR ) { p_trans->flags |= DMA_CONFIG_INCOMPATIBLE; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; return p_trans->flags; } - } + } /* - * SET UP THE DEFAULT CONFIGURATIONS + * SET UP THE DEFAULT CONFIGURATIONS */ /* The flags are cleaned in case the structure was used before.*/ p_trans->flags = DMA_CONFIG_OK; - /* The copy size of the source (in data units -of the source-) is + /* The copy size of the source (in data units -of the source-) is transformed to bytes, to be used as default size.*/ uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(p_trans->src->type); p_trans->size_b = p_trans->src->size_du * dataSize_b; /* By default, the source defines the data type.*/ p_trans->type = p_trans->src->type; - /* + /* * By default, the transaction increment is set to 0 and, if required, * it will be changed to 1 (in which case both src and dst will have an * increment of 1 data unit). */ p_trans->inc_b = 0; - + /* - * CHECK IF THERE ARE MISALIGNMENTS + * CHECK IF THERE ARE MISALIGNMENTS */ if( p_check ) { - /* - * The source and destination targets are analyzed. + /* + * The source and destination targets are analyzed. * If the target is a peripheral (i.e. uses one of trigger slots) * then the misalignment is not checked. */ @@ -398,13 +418,13 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, { misalignment = get_misalignment_b( p_trans->src->ptr, p_trans->type ); } - - if( p_trans->dst->trig == DMA_TRIG_MEMORY ) + + if( p_trans->dst->trig == DMA_TRIG_MEMORY ) { dstMisalignment = get_misalignment_b( p_trans->dst->ptr, p_trans->type ); } - p_trans->flags |= ( misalignment ? DMA_CONFIG_SRC : DMA_CONFIG_OK ); + p_trans->flags |= ( misalignment ? DMA_CONFIG_SRC : DMA_CONFIG_OK ); p_trans->flags |= ( dstMisalignment ? DMA_CONFIG_DST : DMA_CONFIG_OK ); /* Only the largest misalignment is preserved.*/ @@ -415,18 +435,18 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, if( misalignment != 0 ) { - /* - * Misalignment flags will only be stored in the transaction, as - * they are data type dependent, and could vary from transaction + /* + * Misalignment flags will only be stored in the transaction, as + * they are data type dependent, and could vary from transaction * to transaction, even with the same pair of targets. */ p_trans->flags |= DMA_CONFIG_MISALIGN; - /* - * If a misalignment is detected and realignment is not allowed, + /* + * If a misalignment is detected and realignment is not allowed, * an error is returned. No operation should be performed by the DMA. * No further operations are done to prevent corrupting information - * that could be useful for debugging purposes. + * that could be useful for debugging purposes. */ if( !p_enRealign) { @@ -434,171 +454,171 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, } /* - * CHECK IF THERE IS A DISCONTINUITY + * CHECK IF THERE IS A DISCONTINUITY */ - - /* - * If there is a misalignment AND the source or destination - * arrangements are discontinuous, it is not possible to use the + + /* + * If there is a misalignment AND the source or destination + * arrangements are discontinuous, it is not possible to use the * DMA. An error is returned. * A discontinuity in an arrangement is defined as the increment - * being larger than the data type size. - * e.g. - * |AAAA|AAAA|BBBB|BBBB| |____|AAAA|AAAA|____| - * 0 1 2 3 0 1 2 3 - * - * |CCCC|CCCC|____|____| ==> |____|BBBB|BBBB|____| - * 4 5 6 7 4 5 6 7 - * - * |____|____|____|____| |____|CCCC|CCCC|____| - * 8 9 10 11 8 9 10 11 - * + * being larger than the data type size. + * e.g. + * |AAAA|AAAA|BBBB|BBBB| |____|AAAA|AAAA|____| + * 0 1 2 3 0 1 2 3 + * + * |CCCC|CCCC|____|____| ==> |____|BBBB|BBBB|____| + * 4 5 6 7 4 5 6 7 + * + * |____|____|____|____| |____|CCCC|CCCC|____| + * 8 9 10 11 8 9 10 11 + * * In this case the source arrangement has 16bit HALF WORDs, with - * an increment of 1 data unit, so all data is continuous. - * The destination arrangement has also 16bit HALF WORDs, but - * misaligned and with 2 data units of increment. + * an increment of 1 data unit, so all data is continuous. + * The destination arrangement has also 16bit HALF WORDs, but + * misaligned and with 2 data units of increment. * To copy a misaligned arrangement of HALF WORDs, the DMA should - * use BYTEs. However, by using bytes it would not be able to - * write twice and skip twice, as it has a single skip increment. - * - * The misalignment and discontinuity can be found in the source - * and destination respectively and vice versa, and this - * limitation would still exist. - * - * The discontinuous flag is added (the misaligned one was + * use BYTEs. However, by using bytes it would not be able to + * write twice and skip twice, as it has a single skip increment. + * + * The misalignment and discontinuity can be found in the source + * and destination respectively and vice versa, and this + * limitation would still exist. + * + * The discontinuous flag is added (the misaligned one was * already there), and it is turned into a critical error.' - * + * * No further operations are done to prevent corrupting * information that could be useful for debugging purposes. */ - if( ( p_trans->src->inc_du > 1 ) + if( ( p_trans->src->inc_du > 1 ) || ( p_trans->dst->inc_du > 1 ) ) { p_trans->flags |= DMA_CONFIG_DISCONTINUOUS; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; - return p_trans->flags; + return p_trans->flags; } /* - * PERFORM THE REALIGNMENT + * PERFORM THE REALIGNMENT */ - - /* + + /* * If realignment is allowed and there are no discontinuities, - * a more granular data type is used according to the detected - * misalignment in order to overcome it. + * a more granular data type is used according to the detected + * misalignment in order to overcome it. */ p_trans->type += misalignment; - /* + /* * Source and destination increment should now be of the size * of the data. * As increments are given in bytes, in both cases should be the - * size of a data unit. + * size of a data unit. */ p_trans->inc_b = DMA_DATA_TYPE_2_SIZE( p_trans->type ); /* The copy size does not change, as it is already stored in bytes.*/ } - + /* * CHECK IF SOURCE HAS SIZE 0 */ /* - * No further operations are done to prevent corrupting information - * that could be useful for debugging purposes. + * No further operations are done to prevent corrupting information + * that could be useful for debugging purposes. */ - if( p_trans->src->size_du == 0 ) + if( p_trans->src->size_du == 0 ) { p_trans->flags |= DMA_CONFIG_SRC; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; - return p_trans->flags; + return p_trans->flags; } - + /* * CHECK IF THERE IS CROSS-OUTBOUND */ - /* - * As the source target does not know the constraints of the + /* + * As the source target does not know the constraints of the * destination target, it is possible that the copied information - * ends up beyond the bounds of the destination environment. + * ends up beyond the bounds of the destination environment. * This check is not performed if no destination environment was set. * - * e.g. + * e.g. * If 10 HALF WORDs are to be transferred with a HALF WORD in between * (increment of 2 data units: writes 2 bytes, skips 2 bytes, ...), * then ( 10 data units * 2 bytes ) + ( 9 data units * 2 bytes ) are - * traveled for each = 38 bytes + * traveled for each = 38 bytes * that need to be gone over in order to complete the transaction. - * Having the transaction size in bytes, they need to be converted + * Having the transaction size in bytes, they need to be converted * to data units: * size [data units] = size [bytes] / convertionRatio [bytes/data unit]. - * + * * The transaction can be performed if the whole affected area can fit * inside the destination environment * (i.e. The start pointer + the 38 bytes -in this case-, is smaller - * than the end pointer of the environment). - * + * than the end pointer of the environment). + * * No further operations are done to prevent corrupting information - * that could be useful for debugging purposes. - */ + * that could be useful for debugging purposes. + */ uint8_t isEnv = p_trans->dst->env; uint8_t isOutb = is_region_outbound( - p_trans->dst->ptr, + p_trans->dst->ptr, p_trans->dst->env->end, p_trans->type, p_trans->src->size_du, p_trans->dst->inc_du ); if( isEnv && isOutb ) { - p_trans->flags |= DMA_CONFIG_DST; - p_trans->flags |= DMA_CONFIG_OUTBOUNDS; + p_trans->flags |= DMA_CONFIG_DST; + p_trans->flags |= DMA_CONFIG_OUTBOUNDS; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; - return p_trans->flags; + return p_trans->flags; } - - // @ToDo: It should also be checked that the destination is behind the - // source if there will be overlap. - // @ToDo: Consider if (when a destination target has no environment) - // the destination size should be used as limit. + + // @ToDo: It should also be checked that the destination is behind the + // source if there will be overlap. + // @ToDo: Consider if (when a destination target has no environment) + // the destination size should be used as limit. /* * CHECK IF THE WINDOW SIZE IS ADEQUATE */ /* - * The window size cannot be larger than the transaction size. Although - * this would not cause any error, the transaction is rejected because - * it is likely a mistake. + * The window size cannot be larger than the transaction size. Although + * this would not cause any error, the transaction is rejected because + * it is likely a mistake. */ if( p_trans->win_du > p_trans->size_b ) { - p_trans->flags |= DMA_CONFIG_WINDOW_SIZE; + p_trans->flags |= DMA_CONFIG_WINDOW_SIZE; p_trans->flags |= DMA_CONFIG_CRITICAL_ERROR; - return p_trans->flags; + return p_trans->flags; } /* * If the ratio between the transaction size and the window size is too - * large, the window size might be too small for the application to - * properly handle. The threshold is set in a weak implementation that + * large, the window size might be too small for the application to + * properly handle. The threshold is set in a weak implementation that * can be overriden to adopt a custom value or set to return 0 to bypass - * this check. - * The window size can also be set to 0 to not enable this trigger. - * This check only raises a warning, not a critical error as there is no - * certainty that an real error will occur. + * this check. + * The window size can also be set to 0 to not enable this trigger. + * This check only raises a warning, not a critical error as there is no + * certainty that an real error will occur. */ uint32_t threshold = dma_window_ratio_warning_threshold(); uint32_t ratio = p_trans->size_b / p_trans->win_du; - if( p_trans->win_du - && threshold + if( p_trans->win_du + && threshold && ( ratio > threshold) ) { - p_trans->flags |= DMA_CONFIG_WINDOW_SIZE; + p_trans->flags |= DMA_CONFIG_WINDOW_SIZE; } - } - + } + return p_trans->flags; } @@ -608,42 +628,42 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * CHECK FOR CRITICAL ERRORS */ - /* + /* * The transaction is not allowed if it contain a critical error. * A successful transaction creation has to be done before loading it to * the DMA. */ if( p_trans->flags & DMA_CONFIG_CRITICAL_ERROR ) - { + { dma_cb.trans = NULL; return DMA_CONFIG_CRITICAL_ERROR; } - + /* * CHECK IF THERE IS A TRANSACTION RUNNING */ /* - * Modifying the DMA registers during the execution of a transaction can be - * dangerous. - * This is prevented by blocking any modification of the current transaction - * until it has ended. + * Modifying the DMA registers during the execution of a transaction can be + * dangerous. + * This is prevented by blocking any modification of the current transaction + * until it has ended. * Transactions can still be validated in the meantime. - */ + */ if( !dma_is_ready() ) { return DMA_CONFIG_TRANS_OVERRIDE; } /* Save the current transaction */ - dma_cb.trans = p_trans; - + dma_cb.trans = p_trans; + /* * ENABLE/DISABLE INTERRUPTS */ - /* - * If the selected en event is polling, interrupts are disabled. + /* + * If the selected en event is polling, interrupts are disabled. * Otherwise the mie.MEIE bit is set to one to enable machine-level * fast DMA interrupt. */ @@ -653,7 +673,7 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) if( dma_cb.trans->end != DMA_TRANS_END_POLLING ) { /* Enable global interrupt for machine-level interrupts. */ - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8 ); + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8 ); /* @ToDo: What does this do? */ CSR_SET_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK ); @@ -662,11 +682,8 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) /* Only if a window is used should the window interrupt be set. */ if( p_trans->win_du > 0 ) { - plic_Init(); - plic_irq_set_priority(DMA_WINDOW_INTR, 1); - plic_irq_set_enabled(DMA_WINDOW_INTR, kPlicToggleEnabled); dma_cb.peri->INTERRUPT_EN |= INTR_EN_WINDOW_DONE; - } + } } /* @@ -706,8 +723,8 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * SET THE INCREMENTS */ - /* - * The increments might have been changed (vs. the original value of + /* + * The increments might have been changed (vs. the original value of * the target) due to misalignment issues. If they have, use the changed * values, otherwise, use the target-specific ones. * Other reason to overwrite the target increment is if a trigger is used. @@ -716,12 +733,12 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * as the values read from the second port are instead used. */ - write_register( get_increment_b( dma_cb.trans->src ), - DMA_PTR_INC_REG_OFFSET, + write_register( get_increment_b( dma_cb.trans->src ), + DMA_PTR_INC_REG_OFFSET, DMA_PTR_INC_SRC_PTR_INC_MASK, DMA_PTR_INC_SRC_PTR_INC_OFFSET ); - - + + if(dma_cb.trans->mode != DMA_TRANS_MODE_ADDRESS) { @@ -739,40 +756,40 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) dma_cb.peri->MODE = dma_cb.trans->mode; /* The window size is set to the transaction size if it was set to 0 in order to disable the functionality (it will never be triggered). */ - - dma_cb.peri->WINDOW_SIZE = dma_cb.trans->win_du + + dma_cb.peri->WINDOW_SIZE = dma_cb.trans->win_du ? dma_cb.trans->win_du : dma_cb.trans->size_b; - + /* * SET TRIGGER SLOTS AND DATA TYPE - */ - write_register( dma_cb.trans->src->trig, - DMA_SLOT_REG_OFFSET, + */ + write_register( dma_cb.trans->src->trig, + DMA_SLOT_REG_OFFSET, DMA_SLOT_RX_TRIGGER_SLOT_MASK, DMA_SLOT_RX_TRIGGER_SLOT_OFFSET ); - write_register( dma_cb.trans->dst->trig, - DMA_SLOT_REG_OFFSET, + write_register( dma_cb.trans->dst->trig, + DMA_SLOT_REG_OFFSET, DMA_SLOT_TX_TRIGGER_SLOT_MASK, DMA_SLOT_TX_TRIGGER_SLOT_OFFSET ); - write_register( dma_cb.trans->type, - DMA_DATA_TYPE_REG_OFFSET, - DMA_DATA_TYPE_DATA_TYPE_MASK, + write_register( dma_cb.trans->type, + DMA_DATA_TYPE_REG_OFFSET, + DMA_DATA_TYPE_DATA_TYPE_MASK, DMA_SELECTION_OFFSET_START ); - + return DMA_CONFIG_OK; } dma_config_flags_t dma_launch( dma_trans_t *p_trans ) { /* - * Make sure that the loaded transaction is the intended transaction. - * If the loaded trans was NULL'd, then this the transaction is never + * Make sure that the loaded transaction is the intended transaction. + * If the loaded trans was NULL'd, then this the transaction is never * launched. */ - if( ( p_trans == NULL ) + if( ( p_trans == NULL ) || ( dma_cb.trans != p_trans ) ) // @ToDo: Check per-element. { return DMA_CONFIG_CRITICAL_ERROR; @@ -783,12 +800,12 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans ) */ /* - * Modifying the DMA registers during the execution of a transaction can be - * dangerous. - * This is prevented by blocking any modification of the current transaction - * until it has ended. + * Modifying the DMA registers during the execution of a transaction can be + * dangerous. + * This is prevented by blocking any modification of the current transaction + * until it has ended. * Transactions can still be validated in the meantime. - */ + */ if( !dma_is_ready() ) { return DMA_CONFIG_TRANS_OVERRIDE; @@ -803,11 +820,11 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans ) /* Load the size and start the transaction. */ dma_cb.peri->SIZE = dma_cb.trans->size_b; - /* + /* * If the end event was set to wait for the interrupt, the dma_launch - * will not return until the interrupt arrives. + * will not return until the interrupt arrives. */ - while( p_trans->end == DMA_TRANS_END_INTR_WAIT + while( p_trans->end == DMA_TRANS_END_INTR_WAIT && ( dma_cb.intrFlag != 0 ) ) { // @ToDo: add a label for this 0 wait_for_interrupt(); } @@ -817,20 +834,20 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans ) __attribute__((optimize("O0"))) uint32_t dma_is_ready(void) -{ - /* The transaction READY bit is read from the status register*/ +{ + /* The transaction READY bit is read from the status register*/ uint32_t ret = ( dma_cb.peri->STATUS & (1<DONE ); - * 2) Consider only the LSB == 1 to be a valid 1 using a BITWISE AND. + * 2) Consider only the LSB == 1 to be a valid 1 using a BITWISE AND. * return ( 1 & dma_cb.peri->DONE ); - * This would be fixed if the DONE register was a 1 bit field. - */ + * This would be fixed if the DONE register was a 1 bit field. + */ uint32_t dma_get_window_count() @@ -851,38 +868,38 @@ void dma_stop_circular() __attribute__((weak, optimize("O0"))) void dma_intr_handler_trans_done() { - /* + /* * The DMA transaction has finished! * This is a weak implementation. - * Create your own function called - * void dma_intr_handler_trans_done() - * to override this one. + * Create your own function called + * void dma_intr_handler_trans_done() + * to override this one. */ } __attribute__((weak, optimize("O0"))) void dma_intr_handler_window_done() { - /* + /* * The DMA has copied another window. * This is a weak implementation. - * Create your own function called - * void dma_intr_handler_window_done() - * to override this one. + * Create your own function called + * void dma_intr_handler_window_done() + * to override this one. */ } __attribute__((weak, optimize("O0"))) uint8_t dma_window_ratio_warning_threshold() { - /* + /* * This is a weak implementation. - * Create your own function called - * void dma_window_ratio_warning_threshold() - * to override this one. - * Make it return 0 to disable this warning. + * Create your own function called + * void dma_window_ratio_warning_threshold() + * to override this one. + * Make it return 0 to disable this warning. */ return DMA_DEFAULT_TRANS_TO_WIND_SIZE_RATIO_THRESHOLD; } - + /****************************************************************************/ /** **/ /* LOCAL FUNCTIONS */ @@ -898,40 +915,40 @@ dma_config_flags_t validate_target( dma_target_t *p_tgt ) */ /* Increment can be 0 when a trigger is used. */ - DMA_STATIC_ASSERT( p_tgt->inc_du >= 0 , "Increment not valid"); - /* The size could be 0 if the target is only going to be used as a + DMA_STATIC_ASSERT( p_tgt->inc_du >= 0 , "Increment not valid"); + /* The size could be 0 if the target is only going to be used as a destination. */ - DMA_STATIC_ASSERT( p_tgt->size_du >= 0 , "Size not valid"); + DMA_STATIC_ASSERT( p_tgt->size_du >= 0 , "Size not valid"); /* The data type must be a valid type */ DMA_STATIC_ASSERT( p_tgt->type < DMA_DATA_TYPE__size , "Type not valid"); /* The trigger must be among the valid trigger values. */ DMA_STATIC_ASSERT( p_tgt->trig < DMA_TRIG__size , "Trigger not valid"); - + /* * INTEGRITY CHECKS */ - - /* + + /* * Check if the copy will always be inside the target's environments * boundaries. * This check is only performed if an environment was set. */ if( p_tgt->env != NULL ) - { + { /* Check if the environment was properly formed.*/ flags |= validate_environment( p_tgt->env ); - /* + /* * Check if the target selected size goes beyond the boundaries of - * the environment. - * This is only analyzed if a size was defined. + * the environment. + * This is only analyzed if a size was defined. */ if( p_tgt->size_du != 0 ) { uint8_t isOutb = is_region_outbound( p_tgt->ptr, - p_tgt->env->end, - p_tgt->type, - p_tgt->size_du, + p_tgt->env->end, + p_tgt->type, + p_tgt->size_du, p_tgt->inc_du ); if( isOutb ) { @@ -949,7 +966,7 @@ dma_config_flags_t validate_target( dma_target_t *p_tgt ) } } - /* + /* * If there is a trigger, there should not be environments * nor increments (or it is an incompatible peripheral). * Otherwise, an increment is needed (or it is an incompatible @@ -958,14 +975,14 @@ dma_config_flags_t validate_target( dma_target_t *p_tgt ) if( p_tgt->trig == DMA_TRIG_MEMORY ){ /* If it is a memory region. */ /* It should have an increment. */ - if( ( p_tgt->inc_du == 0 ) ){ + if( ( p_tgt->inc_du == 0 ) ){ flags |= DMA_CONFIG_INCOMPATIBLE; } } else /* If it is a peripheral. */ - { + { /* It should not have neither an environment nor an increment. */ - if( ( (p_tgt->env != NULL) + if( ( (p_tgt->env != NULL) || ( p_tgt->inc_du != 0 ) ) ) { flags |= DMA_CONFIG_INCOMPATIBLE; @@ -973,11 +990,11 @@ dma_config_flags_t validate_target( dma_target_t *p_tgt ) } /* - * This is returned so this function can be called as: - * if( validate_target == DMA_CONFIG_OK ){ go ahead } + * This is returned so this function can be called as: + * if( validate_target == DMA_CONFIG_OK ){ go ahead } * or if( validate_target() ){ check for errors } */ - return flags; + return flags; } dma_config_flags_t validate_environment( dma_env_t *p_env ) @@ -985,24 +1002,24 @@ dma_config_flags_t validate_environment( dma_env_t *p_env ) /* * SANITY CHECKS */ - if( (uint8_t*)p_env->end < (uint8_t*)p_env->start ) + if( (uint8_t*)p_env->end < (uint8_t*)p_env->start ) { return DMA_CONFIG_INCOMPATIBLE; - } + } return DMA_CONFIG_OK; } /* @ToDo: Prevent validation of targets whose environment was not validated. */ -static inline uint8_t get_misalignment_b( uint8_t *p_ptr, +static inline uint8_t get_misalignment_b( uint8_t *p_ptr, dma_data_type_t p_type ) { /* - * Note: These checks only make sense when the target is memory. This is - * not performed when the target is a peripheral (i.e. uses a trigger). + * Note: These checks only make sense when the target is memory. This is + * not performed when the target is a peripheral (i.e. uses a trigger). * Check for word alignment: - * The 2 LSBs of the data type must coincide with the 2 LSBs of the SRC + * The 2 LSBs of the data type must coincide with the 2 LSBs of the SRC * pointer. - * This guarantees word alignment. + * This guarantees word alignment. * |____|____|____|____|____|____|____|____| Memory address 0x*******y * 0 1 2 3 4 5 6 7 = y (In bytes) * Byte words can start in any of these positions @@ -1010,30 +1027,30 @@ static inline uint8_t get_misalignment_b( uint8_t *p_ptr, * Words can only start on 0 or 4 * For example, if there was a Word starting on address ended in 2: * |____|____|\\\\|\\\\|\\\\|\\\\|____|____| - * 0 1 2 3 4 5 6 7 - * The DMA could only grab bytes 0-3 and 4-7, so it CANNOT copy into the + * 0 1 2 3 4 5 6 7 + * The DMA could only grab bytes 0-3 and 4-7, so it CANNOT copy into the * destination pointer (x) the desired Word as follows: * |\\\\|\\\\|\\\\|\\\\| - * x x+1 x+2 x+3 - * - * To overcome this, the ALLOW REALIGN flag is available in the DMA - * control block. - * If the user set the ALLOW REALIGN flag, WORD reading from misaligned + * x x+1 x+2 x+3 + * + * To overcome this, the ALLOW REALIGN flag is available in the DMA + * control block. + * If the user set the ALLOW REALIGN flag, WORD reading from misaligned * pointers will be converted to two HALF WORD readings with a HALF WORD - * increment on both source and destination. - * + * increment on both source and destination. + * * HALF WORD misalignment is solved through the same method using two WORD - * readings. - * - */ - + * readings. + * + */ + uint8_t misalignment = 0; - /* + /* * If the data type is WORD and the two LSBs of pointer are not 00, * there is a misalignment. */ - + if( p_type == DMA_DATA_TYPE_WORD ) { if( ( (uint32_t)p_ptr & DMA_WORD_ALIGN_MASK ) != 0 ) @@ -1041,9 +1058,9 @@ static inline uint8_t get_misalignment_b( uint8_t *p_ptr, misalignment++; } } - - /* - * If the data type is WORD or HALF WORD and the LSB of pointer + + /* + * If the data type is WORD or HALF WORD and the LSB of pointer * is not 0, there is a misalignment. * The inequality is of special importance because WORDs stored in odd * pointers need to turn into BYTE as well. @@ -1057,64 +1074,64 @@ static inline uint8_t get_misalignment_b( uint8_t *p_ptr, } } - /* - * These two operations will end up with: + /* + * These two operations will end up with: * misalignment == 0 if no realignment is needed. - * misalignment == 1 if realignment is needed, but switching to half + * misalignment == 1 if realignment is needed, but switching to half * the word size would fix it - * misalignment == 2 if a WORD is to be read from an odd pointer, so - * BYTE data type is needed necessarily. + * misalignment == 2 if a WORD is to be read from an odd pointer, so + * BYTE data type is needed necessarily. */ return misalignment; } -static inline uint8_t is_region_outbound( uint8_t *p_start, - uint8_t *p_end, - uint32_t p_type, - uint32_t p_size_du, +static inline uint8_t is_region_outbound( uint8_t *p_start, + uint8_t *p_end, + uint32_t p_type, + uint32_t p_size_du, uint32_t p_inc_du ) { /* 000 = A data unit to be copied * xxx = A data unit to be skipped - * + * * v The start /------------\ The size of each increment - * |OOOO|xxxx|xxxx|OOOO|xxxx|xxxx| . . . |OOOO|xxxx|xxxx|OOOO|xxxx|xxxx| - * \--/ The size of a type + * |OOOO|xxxx|xxxx|OOOO|xxxx|xxxx| . . . |OOOO|xxxx|xxxx|OOOO|xxxx|xxxx| + * \--/ The size of a type * \------------------- Each increment n-1 times ------/ - * + 1 word (w/o increment) \--/ - * \------ All the affected region (rangeSize) ------------/ - * The last affected byte ^ - * + * + 1 word (w/o increment) \--/ + * \------ All the affected region (rangeSize) ------------/ + * The last affected byte ^ + * * If the environment ends before the last affected byte, then there is - * outbound writing and the function returns 1. + * outbound writing and the function returns 1. */ uint32_t affectedUnits = ( p_size_du - 1 ) * p_inc_du + 1; uint32_t rangeSize = DMA_DATA_TYPE_2_SIZE(p_type) * affectedUnits; uint32_t lasByteInsideRange = p_start + rangeSize -1; return ( p_end < lasByteInsideRange ); - // Size is be guaranteed to be non-zero before calling this function. + // Size is be guaranteed to be non-zero before calling this function. } -/* @ToDo: Consider changing the "mask" parameter for a bitfield definition +/* @ToDo: Consider changing the "mask" parameter for a bitfield definition (see dma_regs.h) */ -static inline void write_register( uint32_t p_val, - uint32_t p_offset, +static inline void write_register( uint32_t p_val, + uint32_t p_offset, uint32_t p_mask, uint8_t p_sel ) { - /* + /* * The index is computed to avoid needing to access the structure - * as a structure. + * as a structure. */ uint8_t index = p_offset / DMA_REGISTER_SIZE_BYTES; - /* - * An intermediate variable "value" is used to prevent writing twice into + /* + * An intermediate variable "value" is used to prevent writing twice into * the register. */ uint32_t value = (( uint32_t * ) dma_cb.peri ) [ index ]; value &= ~( p_mask << p_sel ); value |= (p_val & p_mask) << p_sel; - (( uint32_t * ) dma_cb.peri ) [ index ] = value; + (( uint32_t * ) dma_cb.peri ) [ index ] = value; // @ToDo: mmio_region_write32(dma->base_addr, (ptrdiff_t)(DMA_SLOT_REG_OFFSET), (tx_slot_mask << DMA_SLOT_TX_TRIGGER_SLOT_OFFSET) + rx_slot_mask) @@ -1124,10 +1141,10 @@ static inline uint32_t get_increment_b( dma_target_t * p_tgt ) { uint32_t inc_b = 0; /* If the target uses a trigger, the increment remains 0. */ - if( p_tgt->trig == DMA_TRIG_MEMORY ) - { + if( p_tgt->trig == DMA_TRIG_MEMORY ) + { /* - * If the transaction increment has been overriden (due to + * If the transaction increment has been overriden (due to * misalignments), then that value is used (it's always set to 1). */ inc_b = dma_cb.trans->inc_b; @@ -1145,34 +1162,6 @@ static inline uint32_t get_increment_b( dma_target_t * p_tgt ) return inc_b; } - -/** - * This is a non-weak implementation of the function declared in fast_intr_ctrl.c - */ -void fic_irq_dma(void) -{ - /* The flag is raised so the waiting loop can be broken.*/ - dma_cb.intrFlag = 1; - - /* - * Call the weak implementation provided in this module, - * or the non-weak implementation. - */ - dma_intr_handler_trans_done(); -} - -/** - * This is a non-weak implementation of the function declared in rv_plic.c - */ -void handler_irq_dma(uint32_t id) -{ - /* - * Call the weak implementation provided in this module, - * or the non-weak implementation. - */ - dma_intr_handler_window_done(); -} - /****************************************************************************/ /** **/ /* EOF */ diff --git a/sw/device/lib/drivers/dma/dma.h b/sw/device/lib/drivers/dma/dma.h index 505e43643..1956f9e8b 100644 --- a/sw/device/lib/drivers/dma/dma.h +++ b/sw/device/lib/drivers/dma/dma.h @@ -1,18 +1,18 @@ /* ******************* ******************************* H HEADER FILE ***************************** -** ******************* -** -** project : X-HEEP -** filename : dma.h -** version : 1 -** date : 13/02/23 -** +** ******************* +** +** project : X-HEEP +** filename : dma.h +** version : 1 +** date : 13/02/23 +** *************************************************************************** -** -** Copyright (c) EPFL contributors. -** All rights reserved. -** +** +** Copyright (c) EPFL contributors. +** All rights reserved. +** *************************************************************************** */ @@ -22,7 +22,7 @@ /** * @file dma.h * @date 13/02/23 -* @brief The Direct Memory Access (DMA) driver to set up and use the DMA +* @brief The Direct Memory Access (DMA) driver to set up and use the DMA * peripheral */ @@ -41,7 +41,8 @@ #include "dma_structs.h" // Generated #include "dma_regs.h" // Generated -#include "core_v_mini_mcu.h" +#include "core_v_mini_mcu.h" + #include "hart.h" // Wait for interrupt /****************************************************************************/ @@ -72,9 +73,9 @@ extern "C" { #endif /** - * Returns the size in bytes of a certain datatype, as a sizeof(type) would. + * Returns the size in bytes of a certain datatype, as a sizeof(type) would. */ -#define DMA_DATA_TYPE_2_SIZE(type) (0b00000100 >> (type) ) +#define DMA_DATA_TYPE_2_SIZE(type) (0b00000100 >> (type) ) /****************************************************************************/ /** **/ @@ -82,85 +83,85 @@ extern "C" { /** **/ /****************************************************************************/ -/** - * SLOT_1~4 are the available slots for adding triggers. - * These are defined in hardware, so it should be consistent with the +/** + * SLOT_1~4 are the available slots for adding triggers. + * These are defined in hardware, so it should be consistent with the * registers' assigned values. - * It was considered during design time that slots could be masked, in case a - * peripheral decided to use two or more slots for Tx or Rx. This is not the - * case in the present moment, anyways. - * @ToDo: What is connected to each slot should not be defined in the DMA HAL. + * It was considered during design time that slots could be masked, in case a + * peripheral decided to use two or more slots for Tx or Rx. This is not the + * case in the present moment, anyways. + * @ToDo: What is connected to each slot should not be defined in the DMA HAL. * It is system-architecture-dependent, but not DMA-architecture-dependent. * As there is currently no way of including these definitions anywhere else, - * they will be left here for the moment. + * they will be left here for the moment. */ typedef enum { - DMA_TRIG_MEMORY = 0, /*!< Reads from memory or writes in + DMA_TRIG_MEMORY = 0, /*!< Reads from memory or writes in memory. */ - DMA_TRIG_SLOT_SPI_RX = 1, /*!< Slot 1 (MEM < SPI). */ + DMA_TRIG_SLOT_SPI_RX = 1, /*!< Slot 1 (MEM < SPI). */ DMA_TRIG_SLOT_SPI_TX = 2, /*!< Slot 2 (MEM > SPI). */ - DMA_TRIG_SLOT_SPI_FLASH_RX = 4, /*!< Slot 3 (MEM < SPI FLASH). */ + DMA_TRIG_SLOT_SPI_FLASH_RX = 4, /*!< Slot 3 (MEM < SPI FLASH). */ DMA_TRIG_SLOT_SPI_FLASH_TX = 8, /*!< Slot 4 (MEM > SPI FLASH). */ DMA_TRIG_SLOT_I2S = 16,/*!< Slot 5 (I2S). */ DMA_TRIG__size, /*!< Not used, only for sanity checks. */ DMA_TRIG__undef, /*!< DMA will not be used. */ -} dma_trigger_slot_mask_t; +} dma_trigger_slot_mask_t; /** * All the valid data types for the DMA transfer. - * + * * Half Word = 2 bytes = 16 bits - * Byte = 1 byte = 8 bits + * Byte = 1 byte = 8 bits */ typedef enum -{ - DMA_DATA_TYPE_WORD = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_32BIT_WORD,/*!< +{ + DMA_DATA_TYPE_WORD = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_32BIT_WORD,/*!< Word = 4 bytes = 32 bits */ - DMA_DATA_TYPE_HALF_WORD = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_16BIT_WORD,/*!< - Half Word = 2 bytes = 16 bits */ + DMA_DATA_TYPE_HALF_WORD = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_16BIT_WORD,/*!< + Half Word = 2 bytes = 16 bits */ DMA_DATA_TYPE_BYTE = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_8BIT_WORD,/*!< Byte = 1 byte = 8 bits */ - /* DMA_DATA_TYPE_BYTE_alt = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_8BIT_WORD_2, - * BYTE and BYTE_alt are interchangeable in hw, but we advice against + /* DMA_DATA_TYPE_BYTE_alt = DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_8BIT_WORD_2, + * BYTE and BYTE_alt are interchangeable in hw, but we advice against * the use of BYTE_alt. - * By using the alternative, some functions/macros like - * DATA_TYPE_2_SIZE would brake. - */ - DMA_DATA_TYPE__size, /*!< Not used, only for sanity checks. */ + * By using the alternative, some functions/macros like + * DATA_TYPE_2_SIZE would brake. + */ + DMA_DATA_TYPE__size, /*!< Not used, only for sanity checks. */ DMA_DATA_TYPE__undef, /*!< DMA will not be used. */ } dma_data_type_t; /** - * It is possible to choose the level of safety with which the DMA operation - * should be configured. - * Not performing checks reduces the DMA usage overhead, but may result in a - * faulty operation, especially if the configurations set to the DMA are not + * It is possible to choose the level of safety with which the DMA operation + * should be configured. + * Not performing checks reduces the DMA usage overhead, but may result in a + * faulty operation, especially if the configurations set to the DMA are not * fixed but rather depend on the circumstance. * e.g. The source pointer is obtained as a result of a loop. It could happen - * the pointer ends up pointing outside the memory range, or that the pointer - * is close enough the the memory end to cause an overflow during reading. + * the pointer ends up pointing outside the memory range, or that the pointer + * is close enough the the memory end to cause an overflow during reading. */ typedef enum{ - DMA_PERFORM_CHECKS_ONLY_SANITY = 0, /*!< No checks will be performed. - Only sanity checks will be performed that no values are off-limits or + DMA_PERFORM_CHECKS_ONLY_SANITY = 0, /*!< No checks will be performed. + Only sanity checks will be performed that no values are off-limits or containing errors. */ DMA_PERFORM_CHECKS_INTEGRITY = 1, /*!< Sanity AND integrity of the - parameters is checked to make sure there are no inconsistencies. + parameters is checked to make sure there are no inconsistencies. Not using this flag is only recommended when parameters are constant and the proper operation has been previously tested. */ DMA_PERFORM_CHECKS__size, /*!< Not used, only for sanity checks. */ -} dma_perf_checks_t; +} dma_perf_checks_t; /** * In some cases the DMA can overcome a misalignment issue if the data type is * set to a smaller size. - * This action can be performed by the dma_configure() function if allowed by - * the user. + * This action can be performed by the dma_configure() function if allowed by + * the user. */ typedef enum { - DMA_DO_NOT_ENABLE_REALIGN = 0, /*!< If a misalignment is detected, it will + DMA_DO_NOT_ENABLE_REALIGN = 0, /*!< If a misalignment is detected, it will be treated as an error. */ DMA_ENABLE_REALIGN = 1, /*!< If a misalignment is detected, the DMA HAL will try to overcome it. */ @@ -169,56 +170,56 @@ typedef enum /** * The mode of operation of the DMA. It determines what the DMA does when the - * end of the transaction is reached. + * end of the transaction is reached. */ typedef enum { DMA_TRANS_MODE_SINGLE = DMA_MODE_MODE_VALUE_LINEAR_MODE, /*!< Only one transaction will be performed.*/ DMA_TRANS_MODE_CIRCULAR = DMA_MODE_MODE_VALUE_CIRCULAR_MODE, /*!< Once the transaction is finished, it is - re-loaded automatically (no need to call dma_trans_load), with the same + re-loaded automatically (no need to call dma_trans_load), with the same parameters. This generates a circular mode in the source and/or destination - pointing to memory. */ + pointing to memory. */ DMA_TRANS_MODE_ADDRESS = DMA_MODE_MODE_VALUE_ADDRESS_MODE, /*!< In this mode, the destination address is read from the address port! */ DMA_TRANS_MODE__size, /*!< Not used, only for sanity checks. */ } dma_trans_mode_t; /** - * Different possible actions that determine the end of the DMA transaction. - * This choice does not affect the transaction, but only the way the - * application is notified of its finalization. + * Different possible actions that determine the end of the DMA transaction. + * This choice does not affect the transaction, but only the way the + * application is notified of its finalization. * Consider that for INTR and INTR_WAIT global interrupts must be enabled with: - * CSR_SET_BITS(CSR_REG_MSTATUS, 0x8 ) (Or something of the sort). - * e.g. For SPI transmission, the application may consider the transfer has - * finished once the DMA has transferred all its data to the SPI buffer (Use - * any en event), or might prefer to wait until the SPI has finished sending + * CSR_SET_BITS(CSR_REG_MSTATUS, 0x8 ) (Or something of the sort). + * e.g. For SPI transmission, the application may consider the transfer has + * finished once the DMA has transferred all its data to the SPI buffer (Use + * any en event), or might prefer to wait until the SPI has finished sending * all the data in its buffer (in which case POLLING could be chosen to disable * DMA interrupts simply expect the SPI interrupt). */ typedef enum { - DMA_TRANS_END_POLLING, /*!< Interrupt for the DMA will be disabled. The + DMA_TRANS_END_POLLING, /*!< Interrupt for the DMA will be disabled. The application will be in charge of monitoring the end of the transaction.*/ DMA_TRANS_END_INTR, /*!< Interrupt for the DMA will be enabled. After - launching the transaction, the dma_launch function will exit. */ + launching the transaction, the dma_launch function will exit. */ DMA_TRANS_END_INTR_WAIT, /*!< Interrupt for the DMA will be enabled. After - launching the transaction, the dma_launch function will wait in a - wait_for_interrupt (wfi) state. */ + launching the transaction, the dma_launch function will wait in a + wait_for_interrupt (wfi) state. */ DMA_TRANS_END__size, /*!< Not used, only for sanity checks. */ } dma_trans_end_evt_t; /** * Possible returns of the dma_configure() function. - * Some of these issues or not a problem per se, yet a combination of them - * might be. - * For this reason, each error has only one high bit. This way they can be - * masked together using the bitwise OR operator: + * Some of these issues or not a problem per se, yet a combination of them + * might be. + * For this reason, each error has only one high bit. This way they can be + * masked together using the bitwise OR operator: * ( DMA_CONFIG_x | DMA_CONFIG_y | DMA_CONFIG_z ). * The *_SRC and *_DST labels identify in which arrangements issues were * encountered. - * - * A flag can be unset using the bitwise AND and NOT operators: - * x &= ~DMA_CONFIG_* + * + * A flag can be unset using the bitwise AND and NOT operators: + * x &= ~DMA_CONFIG_* */ typedef enum { @@ -231,28 +232,28 @@ typedef enum DMA_CONFIG_MISALIGN = 0x0004, /*!< An arrangement is misaligned. */ DMA_CONFIG_OVERLAP = 0x0008, /*!< The increment is smaller than the data type size. */ - DMA_CONFIG_DISCONTINUOUS = 0x0010, /*!< The increment is larger than the + DMA_CONFIG_DISCONTINUOUS = 0x0010, /*!< The increment is larger than the data type size. */ - DMA_CONFIG_OUTBOUNDS = 0x0020, /*!< The operation goes beyond the + DMA_CONFIG_OUTBOUNDS = 0x0020, /*!< The operation goes beyond the memory boundaries. */ - DMA_CONFIG_INCOMPATIBLE = 0x0040, /*!< Different arguments result in + DMA_CONFIG_INCOMPATIBLE = 0x0040, /*!< Different arguments result in incompatible requests. */ - DMA_CONFIG_WINDOW_SIZE = 0x0080, /*!< A small window size might result - in loss of syncronism. If the processing of the window takes longer than the - time it takes to the DMA to finish the next window, the application will not - be able to cope. Although "how small is too small" is highly dependent on + DMA_CONFIG_WINDOW_SIZE = 0x0080, /*!< A small window size might result + in loss of syncronism. If the processing of the window takes longer than the + time it takes to the DMA to finish the next window, the application will not + be able to cope. Although "how small is too small" is highly dependent on the length of the processing, this flag will be raised when the transaction and window size ratio is smaller than an arbitrarily chosen ratio as a mere - reminder. This value can be overriden buy means of defining a non-weak + reminder. This value can be overriden buy means of defining a non-weak implementation of the dma_window_ratio_warning_threshold function. */ - DMA_CONFIG_TRANS_OVERRIDE = 0x0100, /*!< A transaction is running. Its + DMA_CONFIG_TRANS_OVERRIDE = 0x0100, /*!< A transaction is running. Its values cannot be modified, nor can it be re-launched. */ DMA_CONFIG_CRITICAL_ERROR = 0x0200, /*!< This flag determines the function will return without the DMA performing any actions. */ } dma_config_flags_t; /** - * An environment is a region of memory defined by its start and end pointers. + * An environment is a region of memory defined by its start and end pointers. * The sole purpose of creating environments is preventing the DMA from writing * on restricted memory regions (outside the environment). */ @@ -263,57 +264,57 @@ typedef struct } dma_env_t; /** - * A target is a region of memory from/to which the DMA can copy data. - * It is defined by its start pointer and the size of the data that can be - * copied. Furthermore, control parameters can be added to prevent the DMA - * from reading/writing outside the boundaries of the target. + * A target is a region of memory from/to which the DMA can copy data. + * It is defined by its start pointer and the size of the data that can be + * copied. Furthermore, control parameters can be added to prevent the DMA + * from reading/writing outside the boundaries of the target. */ typedef struct { - dma_env_t* env; /*!< The environment to which this + dma_env_t* env; /*!< The environment to which this target belongs. It may be null (no checks will be performed to guarantee - the write operations are not performed beyond limits). This is always null - if the target is a peripheral. */ - uint8_t* ptr; /*!< Pointer to the start address from/to + the write operations are not performed beyond limits). This is always null + if the target is a peripheral. */ + uint8_t* ptr; /*!< Pointer to the start address from/to where data will be copied/pasted. */ - uint16_t inc_du; /*!< How much the pointer will increase - every time a read/write operation is done. It is a multiple of the data units. + uint16_t inc_du; /*!< How much the pointer will increase + every time a read/write operation is done. It is a multiple of the data units. Can be left blank if the target is a peripheral. */ uint32_t size_du; /*!< The size (in data units) of the data to be copied. Can be left blank if the target will only be used as destination.*/ - dma_data_type_t type; /*!< The type of data to be transferred. + dma_data_type_t type; /*!< The type of data to be transferred. Can be left blank if the target will only be used as destination. */ - dma_trigger_slot_mask_t trig; /*!< If the target is a peripheral, a + dma_trigger_slot_mask_t trig; /*!< If the target is a peripheral, a trigger can be set to control the data flow. */ -} dma_target_t; +} dma_target_t; /** - * A transaction is an agreed transfer of data from one target to another. - * It needs a source target and a destination target. - * It also includes control parameters to override the targets' specific ones + * A transaction is an agreed transfer of data from one target to another. + * It needs a source target and a destination target. + * It also includes control parameters to override the targets' specific ones * if needed. */ typedef struct { - dma_target_t* src; /*!< Target from where the data will be + dma_target_t* src; /*!< Target from where the data will be copied. */ - dma_target_t* dst; /*!< Target to where the data will be + dma_target_t* dst; /*!< Target to where the data will be copied. */ dma_target_t* src_addr; /*!< Target from where the dst address will be copied. - only valid in address mode */ - uint16_t inc_b; /*!< A common increment in case both targets + uint16_t inc_b; /*!< A common increment in case both targets need to use one same increment. */ - uint32_t size_b; /*!< The size of the transfer, in bytes (in - contrast, the size stored in the targets is in data units). */ - dma_data_type_t type; /*!< The data type to use. One is chosen among + uint32_t size_b; /*!< The size of the transfer, in bytes (in + contrast, the size stored in the targets is in data units). */ + dma_data_type_t type; /*!< The data type to use. One is chosen among the targets. */ dma_trans_mode_t mode; /*!< The copy mode to use. */ - uint32_t win_du; /*!< The amount of data units every which the - WINDOW_DONE flag is raised and its corresponding interrupt triggered. It - can be set to 0 to disable this functionality. */ - dma_trans_end_evt_t end; /*!< What should happen after the transaction + uint32_t win_du; /*!< The amount of data units every which the + WINDOW_DONE flag is raised and its corresponding interrupt triggered. It + can be set to 0 to disable this functionality. */ + dma_trans_end_evt_t end; /*!< What should happen after the transaction is launched. */ - dma_config_flags_t flags; /*!< A mask with possible issues aroused from + dma_config_flags_t flags; /*!< A mask with possible issues aroused from the creation of the transaction. */ } dma_trans_t; @@ -330,95 +331,106 @@ typedef struct /****************************************************************************/ /** - *@brief Takes all DMA configurations to a state where no accidental - * transaction can be performed. - * It can be called anytime to reset the DMA control block. - * @param peri Pointer to a register address following the dma structure. By - * default (peri == NULL), the integrated DMA will be used. + * @brief Attends the plic interrupt. + */ +void handler_irq_dma( uint32_t id ); + +/** + * @brief This is a non-weak implementation of the function declared in + * fast_intr_ctrl.c + */ +void fic_irq_dma(void); + +/** + *@brief Takes all DMA configurations to a state where no accidental + * transaction can be performed. + * It can be called anytime to reset the DMA control block. + * @param peri Pointer to a register address following the dma structure. By + * default (peri == NULL), the integrated DMA will be used. */ void dma_init( dma *peri ); /** * @brief Creates a transaction that can be loaded into the DMA. * @param p_trans Pointer to the dma_transaction_t structure where configuration - * should be allocated. The content of this pointer must be a static variable. + * should be allocated. The content of this pointer must be a static variable. * @note Variables size_b, inc_b and type will be set by this function. It is * not necessary to set them externally before calling it. - * @param p_enRealign Whether to allow the DMA to take a smaller data type - * in order to counter misalignments between the selected data type and the + * @param p_enRealign Whether to allow the DMA to take a smaller data type + * in order to counter misalignments between the selected data type and the * start pointer. - * @param p_check Whether integrity checks should be performed. - * @retval DMA_CONFIG_CRITICAL_ERROR if an error was detected in the transaction + * @param p_check Whether integrity checks should be performed. + * @retval DMA_CONFIG_CRITICAL_ERROR if an error was detected in the transaction * to be loaded. - * @retval DMA_CONFIG_OK == 0 otherwise. + * @retval DMA_CONFIG_OK == 0 otherwise. */ -dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, - dma_en_realign_t p_enRealign, +dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, + dma_en_realign_t p_enRealign, dma_perf_checks_t p_check ); /** - * @brief The transaction configuration (that has been previously validated - * through the creation functions) is effectively transferred into the DMA - * registers. - * @param p_trans Pointer to the transaction struct to be loaded into the DMA. - * The content of this pointer must be a static variable. - * @return A configuration flags mask. Each individual flag can be accessed - * with a bitwise AND ( ret & DMA_CONFIG_* ). It is not recommended to query - * the result from inside target structure as an error could have appeared + * @brief The transaction configuration (that has been previously validated + * through the creation functions) is effectively transferred into the DMA + * registers. + * @param p_trans Pointer to the transaction struct to be loaded into the DMA. + * The content of this pointer must be a static variable. + * @return A configuration flags mask. Each individual flag can be accessed + * with a bitwise AND ( ret & DMA_CONFIG_* ). It is not recommended to query + * the result from inside target structure as an error could have appeared * before the creation of the structure. */ dma_config_flags_t dma_load_transaction( dma_trans_t* p_trans ); /** - * @brief Launches the loaded transaction. - * @param p_trans A pointer to the desired transaction. This is only used to - * double check that the loaded transaction is the desired one. + * @brief Launches the loaded transaction. + * @param p_trans A pointer to the desired transaction. This is only used to + * double check that the loaded transaction is the desired one. * This check can be avoided by passing a NULL pointer. - * @retval DMA_CONFIG_CRITICAL_ERROR if the passed pointer does not correspond - * itself with the loaded transaction (i.e. it is likely the transaction to be + * @retval DMA_CONFIG_CRITICAL_ERROR if the passed pointer does not correspond + * itself with the loaded transaction (i.e. it is likely the transaction to be * loaded is not the desired one). * @retval DMA_CONFIG_OK == 0 otherwise. */ dma_config_flags_t dma_launch( dma_trans_t* p_trans ); /** - * @brief Read from the done register of the DMA. Additionally decreases the - * count of simultaneously-launched transactions. Be careful when calling this - * function * after it has returned 1, unless there is another transaction + * @brief Read from the done register of the DMA. Additionally decreases the + * count of simultaneously-launched transactions. Be careful when calling this + * function * after it has returned 1, unless there is another transaction * running or a new transaction was launched. - * Be careful when calling this function if interrupts were chosen as the end - * event. - * @return Whether the DMA is working or not. It starts returning 0 as soon as - * the dma_launch function has returned. - * @retval 0 - DMA is working. - * @retval 1 - DMA has finished the transmission. DMA is idle. + * Be careful when calling this function if interrupts were chosen as the end + * event. + * @return Whether the DMA is working or not. It starts returning 0 as soon as + * the dma_launch function has returned. + * @retval 0 - DMA is working. + * @retval 1 - DMA has finished the transmission. DMA is idle. */ uint32_t dma_is_ready(void); /** - * @brief Get the number of windows that have already been written. Resets on + * @brief Get the number of windows that have already been written. Resets on * the start of each transaction. * @return The number of windows that have been written from this transaction. */ uint32_t dma_get_window_count(void); /** - * @brief Prevent the DMA from relaunching the transaction automatically after - * finishing the current one. It does not affect the currently running - * transaction. It has no effect if the DMA is operating in SINGULAR + * @brief Prevent the DMA from relaunching the transaction automatically after + * finishing the current one. It does not affect the currently running + * transaction. It has no effect if the DMA is operating in SINGULAR * transaction mode. */ void dma_stop_circular(void); /** -* @brief DMA interrupt handler. +* @brief DMA interrupt handler. * `dma.c` provides a weak definition of this symbol, which can be overridden * at link-time by providing an additional non-weak definition. */ void dma_intr_handler_trans_done(void); /** -* @brief DMA interrupt handler. +* @brief DMA interrupt handler. * `dma.c` provides a weak definition of this symbol, which can be overridden * at link-time by providing an additional non-weak definition. */ @@ -427,12 +439,12 @@ void dma_intr_handler_window_done(void); /** * @brief This weak implementation allows the user to override the threshold * in which a warning is raised for a transaction to window size ratio that - * could cause a loss of syncronism. + * could cause a loss of syncronism. * Crete this override with caution. For small transaction sizes, even a large * window size might cause loss of syncronism (e.g. If the DMA is copying 10 - * bytes and the one interrupt is received every 5 bytes, there is a large - * chance that the DMA will finish copying the remaining 5 bytes before the - * CPU managed to process the previous 5 bytes. + * bytes and the one interrupt is received every 5 bytes, there is a large + * chance that the DMA will finish copying the remaining 5 bytes before the + * CPU managed to process the previous 5 bytes. * During the non-weak implementation, return 0 to disable this check. */ uint8_t dma_window_ratio_warning_threshold(void); diff --git a/sw/device/lib/drivers/gpio/gpio.c b/sw/device/lib/drivers/gpio/gpio.c index 80e051d20..8da1a2ec8 100644 --- a/sw/device/lib/drivers/gpio/gpio.c +++ b/sw/device/lib/drivers/gpio/gpio.c @@ -89,13 +89,71 @@ */ #define GPIO_MODE_NUM_PINS 16 +/** + * The first interrupt ID for the GPIOS + */ +#define GPIO_INTR_START GPIO_INTR_8 + +/** + * The last interrupt ID for the GPIOS + */ +#define GPIO_INTR_END GPIO_INTR_31 + +/** + * The number of GPIO interrupt IDs + */ +#define GPIO_INTR_QTY ( GPIO_INTR_END - GPIO_INTR_START + 1 ) + +/** + * Array of handlers for the different GPIO interrupts. + */ +void (*gpio_handlers[ GPIO_INTR_QTY ])( void ); + + +/****************************************************************************/ +/** **/ +/* PROTOTYPES OF LOCAL FUNCTIONS */ +/** **/ +/****************************************************************************/ + +/** + * A dummy function to prevent unassigned irq to access a null pointer. + */ +__attribute__((optimize("O0"))) static void gpio_handler_irq_dummy( uint32_t dummy ); + + /****************************************************************************/ /** **/ /* EXPORTED FUNCTIONS */ /** **/ /****************************************************************************/ -gpio_result_t gpio_config (gpio_cfg_t cfg){ +gpio_result_t gpio_assign_irq_handler( uint32_t intr_id, + void *handler() ) +{ + if( intr_id >= GPIO_INTR_START && intr_id <= GPIO_INTR_END ) + { + gpio_handlers[ intr_id - GPIO_INTR_START ] = handler; + return GpioOk; + } + return GpioError; +} + +void gpio_reset_handlers_list( ) +{ + for( uint8_t i = 0; i < GPIO_INTR_QTY; i++ ) + { + gpio_handlers[ i ] = &gpio_handler_irq_dummy; + } +} + +void handler_irq_gpio( uint32_t id ) +{ + gpio_handlers[ id - GPIO_INTR_START ](); +} + +gpio_result_t gpio_config (gpio_cfg_t cfg) +{ /* check that pin is in acceptable range. */ if (cfg.pin > (MAX_PIN-1) || cfg.pin < 0) return GpioPinNotAcceptable; @@ -178,6 +236,8 @@ gpio_result_t gpio_reset_all (void) gpio_peri->INTRPT_LVL_HIGH_EN0 = 0; gpio_peri->INTRPT_LVL_LOW_EN0 = 0; gpio_peri->INTRPT_STATUS0 = 0xFFFFFFFF; + + gpio_reset_handlers_list( ); } gpio_result_t gpio_read (gpio_pin_number_t pin, bool *val) @@ -437,6 +497,17 @@ void gpio_intr_set_mode (gpio_intr_general_mode_t mode) gpio_peri->CFG, BIT_MASK_1, GPIO_CFG_INTR_MODE_INDEX, mode); } +/****************************************************************************/ +/** **/ +/* LOCAL FUNCTIONS */ +/** **/ +/****************************************************************************/ + +__attribute__((optimize("O0"))) static void gpio_handler_irq_dummy( uint32_t dummy ) +{ + return; +} + /****************************************************************************/ /** **/ /* EOF */ diff --git a/sw/device/lib/drivers/gpio/gpio.h b/sw/device/lib/drivers/gpio/gpio.h index 8389cfe43..b3cba09cd 100644 --- a/sw/device/lib/drivers/gpio/gpio.h +++ b/sw/device/lib/drivers/gpio/gpio.h @@ -131,6 +131,25 @@ typedef struct gpio_cfg /** **/ /****************************************************************************/ +/** + * @brief Adds a handler function for a gpio interrupt to the handlers list. + * @param intr_id The interrupt ID of a gpio interrupt (from core_v_mini_mcu.h) + * @param handler A pointer to a function that will be called upon interrupt. + * @return The result of the operation + */ +gpio_result_t gpio_assign_irq_handler( uint32_t intr_id, + void *handler() ); + +/** + * @brief Resets all handlers to the dummy handler. + */ +void gpio_reset_handlers_list( ); + +/** + * @brief Attends the plic interrupt. + */ +void handler_irq_gpio( uint32_t id ); + /** * @brief gpio configuration. It first reset the pin configuration. * @param gpio_struct_t contatining pin, mode, en_input_sampling, en_intr, diff --git a/sw/device/lib/drivers/i2c/i2c.c b/sw/device/lib/drivers/i2c/i2c.c index 7a760764a..ef41d320a 100644 --- a/sw/device/lib/drivers/i2c/i2c.c +++ b/sw/device/lib/drivers/i2c/i2c.c @@ -625,3 +625,8 @@ i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, return i2c_write_byte_raw(i2c, byte, flags); } + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c(uint32_t id) +{ + // Replace this function with a non-weak implementation +} \ No newline at end of file diff --git a/sw/device/lib/drivers/i2c/i2c.h b/sw/device/lib/drivers/i2c/i2c.h index cf150c947..6c1706d9f 100644 --- a/sw/device/lib/drivers/i2c/i2c.h +++ b/sw/device/lib/drivers/i2c/i2c.h @@ -603,6 +603,12 @@ i2c_result_t i2c_write_byte_raw(const i2c_t *i2c, uint8_t byte, i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, i2c_fmt_t code, bool suppress_nak_irq); + +/** + * @brief Attends the plic interrupt. + */ +__attribute__((weak, optimize("O0"))) void handler_irq_i2c(uint32_t id); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/sw/device/lib/drivers/i2s/i2s.c b/sw/device/lib/drivers/i2s/i2s.c index 9e89d9f39..2c9975ef3 100644 --- a/sw/device/lib/drivers/i2s/i2s.c +++ b/sw/device/lib/drivers/i2s/i2s.c @@ -45,6 +45,10 @@ /** **/ /****************************************************************************/ +__attribute__((weak, optimize("O0"))) void handler_irq_i2s(uint32_t id) +{ + // Replace this function with a non-weak implementation +} // i2s base functions i2s_result_t i2s_init(uint16_t div_value, i2s_word_length_t word_length) @@ -70,7 +74,7 @@ i2s_result_t i2s_init(uint16_t div_value, i2s_word_length_t word_length) i2s_peri->CONTROL = control; // wait for I2S clock domain to acknowledge startup - while (! i2s_is_running()) ; + while (! i2s_is_running()) ; control |= (1 << I2S_CONTROL_EN_WS_BIT); // enable WS gen i2s_peri->CONTROL = control; @@ -134,7 +138,7 @@ i2s_result_t i2s_rx_stop(void) return kI2sErrUninit; } - bool overflow = i2s_rx_overflow(); + bool overflow = i2s_rx_overflow(); // disable rx channel uint32_t control = i2s_peri->CONTROL; @@ -196,7 +200,7 @@ void i2s_rx_enable_watermark(uint16_t watermark, bool interrupt_en) // enable/disable interrupt control = bitfield_bit32_write(control, I2S_CONTROL_INTR_EN_BIT, interrupt_en); // enable counter - control |= (1 << I2S_CONTROL_EN_WATERMARK_BIT); + control |= (1 << I2S_CONTROL_EN_WATERMARK_BIT); i2s_peri->CONTROL = control; } @@ -204,7 +208,7 @@ void i2s_rx_disable_watermark(void) { // disable interrupt and disable watermark counter i2s_peri->CONTROL &= ~( - (1 << I2S_CONTROL_INTR_EN_BIT) + (1 << I2S_CONTROL_INTR_EN_BIT) + (1 << I2S_CONTROL_EN_WATERMARK_BIT) ); } diff --git a/sw/device/lib/drivers/i2s/i2s.h b/sw/device/lib/drivers/i2s/i2s.h index bcee15360..4dcfbf0e6 100644 --- a/sw/device/lib/drivers/i2s/i2s.h +++ b/sw/device/lib/drivers/i2s/i2s.h @@ -49,6 +49,7 @@ #include "bitfield.h" #include "i2s_regs.h" + #ifdef __cplusplus extern "C" { #endif @@ -106,20 +107,25 @@ typedef enum i2s_channel_sel { /** **/ /****************************************************************************/ +/** + * @brief Attends the plic interrupt. + */ +__attribute__((weak, optimize("O0"))) void handler_irq_i2s(uint32_t id); + /** * Initialize I2S peripheral * Starts devices connected on the I2S bus * This function has to be called before any other function of the I2S hal. - * - * Generates SCK and WS + * + * Generates SCK and WS * (with the given parameters frequency and word length) - * - * @note to change clock freq or word length call `i2s_terminate` first - * - * @param div_value Divider value = src_clk_freq / gen_clk_freq + * + * @note to change clock freq or word length call `i2s_terminate` first + * + * @param div_value Divider value = src_clk_freq / gen_clk_freq * (odd values are allowed, for 0 and 1 the src clock is used) * @param word_length (see i2s_word_length_t) - * @return kI2sOk initialized successful + * @return kI2sOk initialized successful * @return kI2sError if peripheral was already running */ i2s_result_t i2s_init(uint16_t div_value, i2s_word_length_t word_length); @@ -127,7 +133,7 @@ i2s_result_t i2s_init(uint16_t div_value, i2s_word_length_t word_length); /** * Terminate I2S peripheral * Afterwards the init funciton has to be called to reinit the peripheral - * + * * Stops SCK and WS */ void i2s_terminate(void); @@ -135,7 +141,7 @@ void i2s_terminate(void); /** * check if i2s peripheral has been initialized - * + * * @return true if i2s peripheral enable */ bool i2s_is_running(void); @@ -143,17 +149,17 @@ bool i2s_is_running(void); // // RX Channel -// +// /** - * I2S start rx channels - * + * I2S start rx channels + * * (Start the DMA before) - * + * * @param channels to be enabled (see i2s_channel_sel_t) (I2S_DISABLE calls i2s_rx_stop()) - * - * @return kI2sOk success - * @return kI2sError RX already started + * + * @return kI2sOk success + * @return kI2sError RX already started * @return kI2sErrUninit error peripheral was not initialized * @return kI2sOverflow indicates overflow. (to clear call i2s_rx_stop()) */ @@ -163,7 +169,7 @@ i2s_result_t i2s_rx_start(i2s_channel_sel_t channels); * I2S stop rx channels and cleans overflow * * (DMA must not be reading from I2S RX data) - * + * * @return kI2sOk success * @return kI2sErrUninit error peripheral was not initialized * @return kI2sOverflow the RX-FIFO overflowed since the RX has been started. @@ -173,29 +179,29 @@ i2s_result_t i2s_rx_stop(void); /** * I2S check RX data availability - * - * @return true if RX data is available + * + * @return true if RX data is available */ bool i2s_rx_data_available(void); /** * I2S read RX word - * + * * @note The MSBs outside of word_length are 0. * - * @return uint32_t RX word + * @return uint32_t RX word */ uint32_t i2s_rx_read_data(void); /** * I2S check RX FIFO overflow - * + * * To clear overflow: * 1. stop DMA - * 2. call i2s_rx_stop() + * 2. call i2s_rx_stop() * 3. start DMA * 4. call i2s_rx_start() - * + * * @return true if RX FIFO overflowed * @return false */ @@ -207,8 +213,8 @@ bool i2s_rx_overflow(void); /** * I2S enable and configure watermark counter - * - * @param watermark number to trigger interrupt + * + * @param watermark number to trigger interrupt * @param interrupt_en enable/disable interrupt */ void i2s_rx_enable_watermark(uint16_t watermark, bool interrupt_en); @@ -221,7 +227,7 @@ void i2s_rx_disable_watermark(void); /** * I2S read value of watermark counter - * + * * @return uint16_t current counter value */ uint16_t i2s_rx_read_waterlevel(void); @@ -229,7 +235,7 @@ uint16_t i2s_rx_read_waterlevel(void); /** * I2S reset RX Watermark counter to 0 - * + * */ void i2s_rx_reset_waterlevel(void); diff --git a/sw/device/lib/drivers/rv_plic/rv_plic.c b/sw/device/lib/drivers/rv_plic/rv_plic.c index 0a4e03092..7b6ba5f44 100644 --- a/sw/device/lib/drivers/rv_plic/rv_plic.c +++ b/sw/device/lib/drivers/rv_plic/rv_plic.c @@ -37,18 +37,19 @@ #include "rv_plic.h" #include "rv_plic_structs.h" - -#include #include -#include - #include "bitfield.h" -#include "mmio.h" - #include "rv_plic_regs.h" // Generated. - #include "handler.h" +// Peripheral modules from where to obtain the irq handlers +#include "uart.h" +#include "gpio.h" +#include "i2c.h" +#include "i2s.h" +#include "dma.h" +#include "spi_host.h" + /****************************************************************************/ /** **/ /* DEFINITIONS AND MACROS */ @@ -61,27 +62,17 @@ const uint32_t plicMinPriority = 0; const uint32_t plicMaxPriority = RV_PLIC_PRIO0_PRIO0_MASK; - -/** - * Array of handler functions. Each entry is a callable handler function. - * When an interrupt is serviced, its ID is detected and basing on it an - * index is generated. This index will be used to address the proper - * handler function inside this array. -*/ -handler_funct_t handlers[] = {&handler_irq_uart, - &handler_irq_gpio, - &handler_irq_i2c, - &handler_irq_spi, - &handler_irq_i2s, - &handler_irq_dma, - &handler_irq_ext}; - /****************************************************************************/ /** **/ /* TYPEDEFS AND STRUCTURES */ /** **/ /****************************************************************************/ +/** + * Pointer used to dynamically access the different interrupt handlers. +*/ +typedef void (*handler_funct_t)(uint32_t); + /****************************************************************************/ /** **/ /* PROTOTYPES OF LOCAL FUNCTIONS */ @@ -89,29 +80,34 @@ handler_funct_t handlers[] = {&handler_irq_uart, /****************************************************************************/ /** - * Returns the irq_sources_t source type of a given irq source ID -*/ -static irq_sources_t plic_get_irq_src_type(uint32_t irq_id); - -/** - * Get an IE, IP or LE register offset (IE0_0, IE01, ...) from an IRQ source ID. + * @brief Get an IE, IP or LE register offset (e.g. IE0_0) from an IRQ ID. * * With more than 32 IRQ sources, there is a multiple of these registers to * accommodate all the bits (1 bit per IRQ source). This function calculates * the offset for a specific IRQ source ID (ID 32 would be IE01, ...). + * + * @param irq An interrupt source identification */ -static ptrdiff_t plic_offset_from_reg0(uint32_t irq); +static ptrdiff_t plic_offset_from_reg0( uint32_t irq); /** - * - * Get an IE, IP, LE register bit index from an IRQ source ID. + * + * @brief Get an IE, IP, LE register bit index from an IRQ source ID. * * With more than 32 IRQ sources, there is a multiple of these registers to * accommodate all the bits (1 bit per IRQ source). This function calculates * the bit position within a register for a specific IRQ source ID (ID 32 would * be bit 0). + * + * @param irq An interrupt source identification + */ +static uint8_t plic_irq_bit_index( uint32_t irq); + +/** + * @brief A dummy function to prevent unassigned irq to access a null pointer. */ -static uint8_t plic_irq_bit_index(uint32_t irq); +__attribute__((optimize("O0"))) static void handler_irq_dummy( uint32_t dummy ); + /****************************************************************************/ /** **/ @@ -125,13 +121,13 @@ static uint8_t plic_irq_bit_index(uint32_t irq); /** **/ /****************************************************************************/ -/* - Flag to handle the wait for interrupt. - Set to 1 after an interrupt occours and the core is in wait for interrupt - so the execution of the program can continue. +/** + * Array for the ISRs. Length automatically generated when compiling and + * assigned to QTY_INTR. + * Each element will be initialized to be the address of the handler function + * relative to its index. So each element will be a callable function. */ -uint8_t plic_intr_flag = 0; - +handler_funct_t handlers[QTY_INTR]; /****************************************************************************/ /** **/ @@ -139,68 +135,33 @@ uint8_t plic_intr_flag = 0; /** **/ /****************************************************************************/ -__attribute__((weak, optimize("O0"))) void handler_irq_uart(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_gpio(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_i2c(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_spi(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_i2s(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_dma(uint32_t id) { - -} - -__attribute__((weak, optimize("O0"))) void handler_irq_ext(uint32_t id) { - -} - void handler_irq_external(void) { - uint32_t int_id = 0; + uint32_t int_id = NULL_INTR; plic_result_t res = plic_irq_claim(&int_id); - irq_sources_t type = plic_get_irq_src_type(int_id); - if(type != IRQ_BAD) - { // Calls the proper handler - handlers[type](int_id); - plic_intr_flag = 1; - + handlers[int_id](int_id); plic_irq_complete(&int_id); - } } - /*! - Resets relevant registers of the PLIC (Level/Edge, + Resets relevant registers of the PLIC (Level/Edge, priority, target, threshold, interrupts). It writes the default value 0 in them and read back the value into the proper register struct */ plic_result_t plic_Init(void) { - - /* Clears all the Level/Edge registers */ + + /* Clears all the Level/Edge registers */ for(uint8_t i=0; iLE0)[i] = 0; } - /* Clears all the priority registers */ + /* Clears all the priority registers */ for(uint8_t i=0; iPRIO0)[i] = 0; @@ -211,7 +172,7 @@ plic_result_t plic_Init(void) { (&rv_plic_peri->IE00)[i] = 0; } - + /* Clears all the threshold registers */ rv_plic_peri->THRESHOLD0 = 0; if(rv_plic_peri->THRESHOLD0 != 0) @@ -219,7 +180,6 @@ plic_result_t plic_Init(void) return kPlicError; } - /* clears software interrupts registers */ rv_plic_peri->MSIP0 = 0; if(rv_plic_peri->MSIP0 != 0) @@ -227,13 +187,15 @@ plic_result_t plic_Init(void) return kPlicError; } - return kPlicOk; + /* Fill the handlers array with the fixed peripherals. */ + plic_reset_handlers_list(); + return kPlicOk; } -plic_result_t plic_irq_set_enabled(uint32_t irq, - plic_toggle_t state) +plic_result_t plic_irq_set_enabled( uint32_t irq, + plic_toggle_t state) { if(irq >= RV_PLIC_PARAM_NUM_SRC) { @@ -246,7 +208,7 @@ plic_result_t plic_irq_set_enabled(uint32_t irq, return kPlicBadArg; } - // Get the offset of the register in which to write given the irq ID + // Get the offset of the register in which to write given the irq ID ptrdiff_t offset = plic_offset_from_reg0(irq); // Get the destination bit in which to write @@ -254,30 +216,30 @@ plic_result_t plic_irq_set_enabled(uint32_t irq, // Writes `state` in the amount of bits defined by the mask // at position `bit_index` inside the proper Interrupt Enable register - (&rv_plic_peri->IE00)[offset] = bitfield_write((&rv_plic_peri->IE00)[offset], - BIT_MASK_1, - bit_index, + (&rv_plic_peri->IE00)[offset] = bitfield_write((&rv_plic_peri->IE00)[offset], + BIT_MASK_1, + bit_index, state); return kPlicOk; } -plic_result_t plic_irq_get_enabled(uint32_t irq, - plic_toggle_t *state) +plic_result_t plic_irq_get_enabled( uint32_t irq, + plic_toggle_t *state) { if(irq >= RV_PLIC_PARAM_NUM_SRC) { return kPlicBadArg; } - - // Get the destination register + + // Get the destination register ptrdiff_t offset = plic_offset_from_reg0(irq); // Get the destination bit in which to write uint16_t bit_index = plic_irq_bit_index(irq); - // Reads the enabled/disabled bit + // Reads the enabled/disabled bit *state = bitfield_read((&rv_plic_peri->IE00)[offset], BIT_MASK_1, bit_index); return kPlicOk; @@ -285,7 +247,7 @@ plic_result_t plic_irq_get_enabled(uint32_t irq, } -plic_result_t plic_irq_set_trigger(uint32_t irq, +plic_result_t plic_irq_set_trigger( uint32_t irq, plic_irq_trigger_t trigger) { if(irq >= RV_PLIC_PARAM_NUM_SRC) @@ -293,7 +255,7 @@ plic_result_t plic_irq_set_trigger(uint32_t irq, return kPlicBadArg; } - // Get the destination register + // Get the destination register ptrdiff_t offset = plic_offset_from_reg0(irq); // Get the destination bit in which to write @@ -310,7 +272,7 @@ plic_result_t plic_irq_set_trigger(uint32_t irq, } -plic_result_t plic_irq_set_priority(uint32_t irq, uint32_t priority) +plic_result_t plic_irq_set_priority( uint32_t irq, uint32_t priority) { if(irq >= RV_PLIC_PARAM_NUM_SRC || priority > plicMaxPriority) { @@ -338,7 +300,7 @@ plic_result_t plic_target_set_threshold(uint32_t threshold) } -plic_result_t plic_irq_is_pending(uint32_t irq, +plic_result_t plic_irq_is_pending( uint32_t irq, bool *is_pending) { if(irq >= RV_PLIC_PARAM_NUM_SRC || is_pending == NULL) @@ -346,7 +308,7 @@ plic_result_t plic_irq_is_pending(uint32_t irq, return kPlicBadArg; } - // Get the destination register + // Get the destination register ptrdiff_t offset = plic_offset_from_reg0(irq); // Get the destination bit in which to write @@ -358,22 +320,22 @@ plic_result_t plic_irq_is_pending(uint32_t irq, } -plic_result_t plic_irq_claim(uint32_t *claim_data) +plic_result_t plic_irq_claim( uint32_t *claim_data) { - if (claim_data == NULL) + if (claim_data == NULL) { return kPlicBadArg; } - + *claim_data = rv_plic_peri->CC0; return kPlicOk; } -plic_result_t plic_irq_complete(const uint32_t *complete_data) +plic_result_t plic_irq_complete(const uint32_t *complete_data) { - if (complete_data == NULL) + if (complete_data == NULL) { return kPlicBadArg; } @@ -404,55 +366,70 @@ plic_result_t plic_software_irq_is_pending(void) } +plic_result_t plic_assign_external_irq_handler( uint32_t id, + void *handler ) +{ + if( id >= EXT_IRQ_START && id <= QTY_INTR ) + { + handlers[ id ] = (handler_funct_t*) handler; + return kPlicOk; + } + return kPlicBadArg; +} + +void plic_reset_handlers_list(void) +{ + handlers[NULL_INTR] = &handler_irq_dummy; + + for( uint8_t i = NULL_INTR +1; i < QTY_INTR; i++ ) + { + if ( i <= UART_ID_END) + { + handlers[i] = &handler_irq_uart; + } + else if ( i <= GPIO_ID_END) + { + handlers[i] = &handler_irq_gpio; + } + else if ( i <= I2C_ID_END) + { + handlers[i] = &handler_irq_i2c; + } + else if ( i == SPI_ID) + { + handlers[i] = &handler_irq_spi; + } + else if ( i == I2S_ID) + { + handlers[i] = &handler_irq_i2s; + } + else if ( i == DMA_ID) + { + handlers[i] = &handler_irq_dma; + } + else + { + handlers[i] = &handler_irq_dummy; + } + } +} + /****************************************************************************/ /** **/ /* LOCAL FUNCTIONS */ /** **/ /****************************************************************************/ -static irq_sources_t plic_get_irq_src_type(uint32_t irq_id) +__attribute__((optimize("O0"))) static void handler_irq_dummy( uint32_t dummy ) { - if (irq_id < UART_ID_START || irq_id > EXT_IRQ_END) - { - return IRQ_BAD; - } - else if (irq_id <= UART_ID_END) - { - return IRQ_UART_SRC; - } - else if (irq_id <= GPIO_ID_END) - { - return IRQ_GPIO_SRC; - } - else if (irq_id <= I2C_ID_END) - { - return IRQ_I2C_SRC; - } - else if (irq_id == SPI_ID) - { - return IRQ_SPI_SRC; - } - else if (irq_id == I2S_ID) - { - return IRQ_I2S_SRC; - } - else if (irq_id == DMA_ID) - { - return IRQ_DMA_SRC; - } - else - { - return IRQ_EXT_SRC; - } - } -static ptrdiff_t plic_offset_from_reg0(uint32_t irq) +static ptrdiff_t plic_offset_from_reg0( uint32_t irq) { return irq / RV_PLIC_PARAM_REG_WIDTH; } -static uint8_t plic_irq_bit_index(uint32_t irq) +static uint8_t plic_irq_bit_index( uint32_t irq) { return irq % RV_PLIC_PARAM_REG_WIDTH; } diff --git a/sw/device/lib/drivers/rv_plic/rv_plic.h b/sw/device/lib/drivers/rv_plic/rv_plic.h index 37253bc51..ed2753e12 100644 --- a/sw/device/lib/drivers/rv_plic/rv_plic.h +++ b/sw/device/lib/drivers/rv_plic/rv_plic.h @@ -44,6 +44,7 @@ #include "mmio.h" #include "macros.h" + /****************************************************************************/ /** **/ /* DEFINITIONS AND MACROS */ @@ -53,19 +54,16 @@ /** * Start and end ID of the UART interrupt request lines */ -#define UART_ID_START UART_INTR_TX_WATERMARK #define UART_ID_END UART_INTR_RX_PARITY_ERR /** * Start and end ID of the GPIO interrupt request lines */ -#define GPIO_ID_START GPIO_INTR_8 #define GPIO_ID_END GPIO_INTR_31 /** * Start and end ID of the I2C interrupt request lines */ -#define I2C_ID_START INTR_FMT_WATERMARK #define I2C_ID_END INTR_HOST_TIMEOUT /** @@ -86,9 +84,7 @@ /** * ID of the external interrupt request lines */ - -#define EXT_IRQ_START EXT_INTR_0 -#define EXT_IRQ_END EXT_INTR_11 +#define EXT_IRQ_START EXT_INTR_0 /****************************************************************************/ /** **/ @@ -96,14 +92,6 @@ /** **/ /****************************************************************************/ -/** - * Pointer used to dynamically access the different interrupt handlers. - * Each element will be initialized to be the address of the handler function - * relative to its index. So each element will be a callable function. -*/ -typedef void (*handler_funct_t)(uint32_t); - - /** * A PLIC interrupt target. * @@ -172,87 +160,19 @@ typedef enum plic_irq_trigger { } plic_irq_trigger_t; -/** - * Enum for describing all the different types of interrupt - * sources that the rv_plic handles -*/ -typedef enum irq_sources -{ - IRQ_UART_SRC, // from 1 to 8 - IRQ_GPIO_SRC, // from 9 to 32 - IRQ_I2C_SRC, // from 33 to 48 - IRQ_SPI_SRC, // line 49 - IRQ_I2S_SRC, // line 50 - IRQ_DMA_SRC, // line 51 - IRQ_EXT_SRC, // from 52 to 63 - IRQ_BAD = -1 // default failure case -} irq_sources_t; - - -/****************************************************************************/ -/** **/ -/* EXPORTED VARIABLES */ -/** **/ -/****************************************************************************/ - -/** - * Flag used to handle the wait for interrupt. - * The core can test this variable to check if it has to wait - * for an interrupt coming from the RV_PLIC. - * When an interrupt occurs, this flag is set to 1 by the plic in order - * for the core to continue with the execution of the program. -*/ -extern uint8_t plic_intr_flag; - /****************************************************************************/ /** **/ /* EXPORTED FUNCTIONS */ /** **/ /****************************************************************************/ -/** - * IRQ handler for UART -*/ -void handler_irq_uart(uint32_t id); - -/** - * IRQ handler for GPIO -*/ -void handler_irq_gpio(uint32_t id); - -/** - * IRQ handler for I2C -*/ -void handler_irq_i2c(uint32_t id); - -/** - * IRQ handler for SPI -*/ -void handler_irq_spi(uint32_t id); - -/** - * IRQ handler for I2S -*/ -void handler_irq_i2s(uint32_t id); - -/** - * IRQ handler for DMA window -*/ -void handler_irq_dma(uint32_t id); - -/** - * IRQ handler for external interrupts sources -*/ -void handler_irq_ext(uint32_t id); - - /** * Generic handler for the interrupts in inputs to RV_PLIC. * Its basic purpose is to understand which source generated * the interrupt and call the proper specific handler. The source * is detected by reading the CC0 register (claim interrupt), containing * the ID of the source. - * Once the interrupt routine is finished, this function sets to 1 the + * Once the interrupt routine is finished, this function sets to 1 the * external_intr_flag and calls plic_irq_complete() function to conclude * the handling. */ @@ -269,70 +189,71 @@ plic_result_t plic_Init(void); /** * Sets wherher a particular interrupt is curently enabled or disabled. * This is done by setting a specific bit in the Interrupt Enable registers. - * + * * For a specific target, each interrupt source has a dedicated enable/disable bit * inside the relative Interrupt Enable registers, basing on the interrupt * source id. - * + * * This function sets that bit to 0 or 1 depending on the state that it is specified - * + * * @param irq An interrupt source identification * @param state The new toggle state for the interrupt * @return The result of the operation */ -plic_result_t plic_irq_set_enabled(uint32_t irq, - plic_toggle_t state); +plic_result_t plic_irq_set_enabled( uint32_t irq, + plic_toggle_t state); /** * Reads a specific bit of the Interrupt Enable registers to understand * if the corresponding interrupt is enabled or disabled. - * + * * For a specific target, each interrupt source has a dedicated enable/disable bit * inside the relative Interrupt Enable registers, basing on the interrupt * source id. - * + * * The resulting bit is saved inside the state variable passed as parameter - * + * * @param irq An interrupt source identification * @param state The toggle state of the interrupt, as read from the IE registers * @return The result of the operation */ -plic_result_t plic_irq_get_enabled(uint32_t irq, - plic_toggle_t *state); +plic_result_t plic_irq_get_enabled( uint32_t irq, + plic_toggle_t *state); /** * Sets the interrupt request trigger type. - * + * * For a specific interrupt line, identified by irq, sets if its trigger * type has to be edge or level. * Edge means that the interrupt is triggered when the source passes from low to high. * Level means that the interrupt is triggered when the source stays at a high level. - * + * * @param irq An interrupt source identification * @param triggger The trigger state for the interrupt * @result The result of the operation - * + * */ -plic_result_t plic_irq_set_trigger(uint32_t irq, - plic_irq_trigger_t trigger); +plic_result_t plic_irq_set_trigger( uint32_t irq, + plic_irq_trigger_t trigger); /** * Sets a priority value for a specific interrupt source - * + * * @param irq An interrupt source identification * @param priority A priority value to set * @return The result of the operation */ -plic_result_t plic_irq_set_priority(uint32_t irq, uint32_t priority); +plic_result_t plic_irq_set_priority( uint32_t irq, + uint32_t priority); /** * Sets the priority threshold. - * + * * PLIC will only interrupt a target when * IRQ source priority is set higher than the priority threshold for the * corresponding target. - * + * * @param threshold The threshold value to be set * @return The result of the operation */ @@ -340,12 +261,12 @@ plic_result_t plic_target_set_threshold(uint32_t threshold); /** * Returns whether a particular interrupt is currently pending. - * + * * @param irq An interrupt source identification - * @param[out] is_pending Boolean flagcorresponding to whether an interrupt is pending or not + * @param[out] is_pending Boolean flagcorresponding to whether an interrupt is pending or not */ -plic_result_t plic_irq_is_pending(uint32_t irq, - bool *is_pending); +plic_result_t plic_irq_is_pending( uint32_t irq, + bool *is_pending); /** * Claims an IRQ and gets the information about the source. @@ -363,27 +284,26 @@ plic_result_t plic_irq_is_pending(uint32_t irq, * interrupts should be Completed in the reverse order of when they were * Claimed. * - * @param target Target that claimed the IRQ. * @param[out] claim_data Data that describes the origin of the IRQ. * @return The result of the operation. */ -plic_result_t plic_irq_claim(uint32_t *claim_data); +plic_result_t plic_irq_claim( uint32_t *claim_data); /** * Completes the claimed interrupt request. - * + * * After an interrupt request is served, the core writes the interrupt source * ID into the Claim/Complete register. - * - * This function must be called after plic_irq_claim(), when the core is + * + * This function must be called after plic_irq_claim(), when the core is * ready to service others interrupts with the same ID. If this function * is not called, future claimed interrupts will not have the same ID. - * + * * @param complete_data Previously claimed IRQ data that is used to signal * PLIC of the IRQ servicing completion. * @return The result of the operation */ -plic_result_t plic_irq_complete(const uint32_t *complete_data); +plic_result_t plic_irq_complete(const uint32_t *complete_data ); /** @@ -413,11 +333,26 @@ void plic_software_irq_acknowledge(void); /** * Returns software interrupt pending state - * + * * @return The result of the operation */ plic_result_t plic_software_irq_is_pending(void); +/** + * Adds a handler function for an external interrupt to the handlers list. + * @param id The interrupt ID of an external interrupt (from core_v_mini_mcu.h) + * @param handler A pointer to a function that will be called upon interrupt. + * @return The result of the operation +*/ +plic_result_t plic_assign_external_irq_handler( uint32_t id, + void *handler ); + +/** + * Resets all peripheral handlers to their pre-set ones. All external handlers + * are re-set to the dummy handler. + */ +void plic_reset_handlers_list(void); + #endif /* _RV_PLIC_H_ */ /****************************************************************************/ diff --git a/sw/device/lib/drivers/spi_host/spi_host.c b/sw/device/lib/drivers/spi_host/spi_host.c index c53adf681..3312b8cd2 100644 --- a/sw/device/lib/drivers/spi_host/spi_host.c +++ b/sw/device/lib/drivers/spi_host/spi_host.c @@ -120,3 +120,8 @@ void spi_output_enable(const spi_host_t *spi, bool enable){ output_enable_reg = bitfield_bit32_write(output_enable_reg, SPI_HOST_CONTROL_OUTPUT_EN_BIT, enable); mmio_region_write32(spi->base_addr, SPI_HOST_CONTROL_REG_OFFSET, output_enable_reg); } + +__attribute__((weak, optimize("O0"))) void handler_irq_spi(uint32_t id) +{ + // Replace this function with a non-weak implementation +} \ No newline at end of file diff --git a/sw/device/lib/drivers/spi_host/spi_host.h b/sw/device/lib/drivers/spi_host/spi_host.h index c6ae3a965..b5db646d2 100644 --- a/sw/device/lib/drivers/spi_host/spi_host.h +++ b/sw/device/lib/drivers/spi_host/spi_host.h @@ -342,6 +342,12 @@ static inline __attribute__((always_inline)) __attribute__((const)) uint32_t spi return cmd_reg; } +/** + * @brief Attends the plic interrupt. + */ +__attribute__((weak, optimize("O0"))) void handler_irq_spi(uint32_t id); + + #ifdef __cplusplus } #endif diff --git a/sw/device/lib/drivers/uart/uart.c b/sw/device/lib/drivers/uart/uart.c index d98b7dab7..08a32fa7f 100644 --- a/sw/device/lib/drivers/uart/uart.c +++ b/sw/device/lib/drivers/uart/uart.c @@ -143,3 +143,8 @@ size_t uart_read(const uart_t *uart, const uint8_t *data, size_t len) { size_t uart_sink(void *uart, const char *data, size_t len) { return uart_write((const uart_t *)uart, (const uint8_t *)data, len); } + +__attribute__((weak, optimize("O0"))) void handler_irq_uart(uint32_t id) +{ + // Replace this function with a non-weak implementation +} \ No newline at end of file diff --git a/sw/device/lib/drivers/uart/uart.h b/sw/device/lib/drivers/uart/uart.h index 9bd2449d6..c780ca731 100644 --- a/sw/device/lib/drivers/uart/uart.h +++ b/sw/device/lib/drivers/uart/uart.h @@ -83,6 +83,12 @@ size_t uart_read(const uart_t *uart, const uint8_t *data, size_t len); size_t uart_sink(void *uart, const char *data, size_t len); + +/** + * @brief Attends the plic interrupt. + */ +__attribute__((weak, optimize("O0"))) void handler_irq_uart(uint32_t id); + #ifdef __cplusplus } #endif diff --git a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl index fefa059fa..0dc114f5f 100644 --- a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl +++ b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl @@ -54,6 +54,7 @@ extern "C" { #define FLASH_MEM_SIZE 0x${flash_mem_size_address} #define FLASH_MEM_END_ADDRESS (FLASH_MEM_START_ADDRESS + FLASH_MEM_SIZE) +#define QTY_INTR ${len(interrupts)} % for key, value in interrupts.items(): #define ${key.upper()} ${value} % endfor